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:
authorJulian Eisel <julian@blender.org>2020-07-01 18:25:04 +0300
committerJulian Eisel <julian@blender.org>2020-07-01 18:25:04 +0300
commit889c4ca9c26d770af02cedef972d65eb173bdb5e (patch)
tree6f46156146db1b3f09d92c6eaa1f44ba8a584ed4
parent87df15190210eb84ef52e5dccc2932918f912da5 (diff)
parent0829cebeb024095c268f190c34daa8ae9a5a224c (diff)
Merge branch 'asset-uuid--archived' into asset-engine--archivedasset-engine--archived
-rw-r--r--CMakeLists.txt24
-rw-r--r--GNUmakefile4
-rw-r--r--build_files/build_environment/CMakeLists.txt13
-rw-r--r--build_files/build_environment/cmake/harvest.cmake2
-rw-r--r--build_files/build_environment/cmake/ispc.cmake63
-rw-r--r--build_files/build_environment/cmake/openimagedenoise.cmake25
-rw-r--r--build_files/build_environment/cmake/options.cmake23
-rw-r--r--build_files/build_environment/cmake/osl.cmake1
-rw-r--r--build_files/build_environment/cmake/setup_mingw32.cmake227
-rw-r--r--build_files/build_environment/cmake/setup_mingw64.cmake10
-rw-r--r--build_files/build_environment/cmake/ssl.conf2
-rw-r--r--build_files/build_environment/cmake/tbb.cmake7
-rw-r--r--build_files/build_environment/cmake/usd.cmake3
-rw-r--r--build_files/build_environment/cmake/versions.cmake45
-rwxr-xr-xbuild_files/build_environment/install_deps.sh2
-rw-r--r--build_files/build_environment/patches/cmakelists_hidapi.txt20
-rw-r--r--build_files/build_environment/patches/hidapi.diff15
-rw-r--r--build_files/build_environment/patches/ispc.diff36
-rw-r--r--build_files/build_environment/patches/openimagedenoise.diff120
-rw-r--r--build_files/build_environment/patches/usd.diff44
-rw-r--r--build_files/buildbot/README.md2
-rw-r--r--build_files/buildbot/buildbot_utils.py4
-rw-r--r--build_files/buildbot/codesign/base_code_signer.py150
-rw-r--r--build_files/buildbot/slave_rsync.py37
-rwxr-xr-xbuild_files/buildbot/worker_bundle_dmg.py (renamed from build_files/buildbot/slave_bundle_dmg.py)2
-rw-r--r--build_files/buildbot/worker_codesign.cmake (renamed from build_files/buildbot/slave_codesign.cmake)2
-rwxr-xr-xbuild_files/buildbot/worker_codesign.py (renamed from build_files/buildbot/slave_codesign.py)0
-rw-r--r--build_files/buildbot/worker_compile.py (renamed from build_files/buildbot/slave_compile.py)4
-rw-r--r--build_files/buildbot/worker_pack.py (renamed from build_files/buildbot/slave_pack.py)4
-rw-r--r--build_files/buildbot/worker_test.py (renamed from build_files/buildbot/slave_test.py)0
-rw-r--r--build_files/buildbot/worker_update.py (renamed from build_files/buildbot/slave_update.py)0
-rw-r--r--build_files/cmake/Modules/FindOpenImageDenoise.cmake7
-rw-r--r--build_files/cmake/config/blender_full.cmake1
-rw-r--r--build_files/cmake/config/blender_lite.cmake1
-rw-r--r--build_files/cmake/config/blender_release.cmake1
-rw-r--r--build_files/cmake/platform/platform_win32.cmake4
-rw-r--r--doc/python_api/requirements.txt4
-rw-r--r--doc/python_api/rst/bgl.rst2
-rw-r--r--doc/python_api/rst/include__bmesh.rst88
-rw-r--r--doc/python_api/rst/info_api_reference.rst245
-rw-r--r--doc/python_api/rst/info_best_practice.rst243
-rw-r--r--doc/python_api/rst/info_gotcha.rst360
-rw-r--r--doc/python_api/rst/info_overview.rst118
-rw-r--r--doc/python_api/rst/info_quickstart.rst198
-rw-r--r--doc/python_api/rst/info_tips_and_tricks.rst130
-rw-r--r--doc/python_api/sphinx_doc_gen.py50
-rwxr-xr-xdoc/python_api/sphinx_doc_update.py27
-rw-r--r--extern/ceres/CMakeLists.txt576
-rw-r--r--extern/ceres/ChangeLog839
-rwxr-xr-xextern/ceres/bundle.sh9
-rw-r--r--extern/ceres/files.txt81
-rw-r--r--extern/ceres/include/ceres/autodiff_cost_function.h89
-rw-r--r--extern/ceres/include/ceres/autodiff_first_order_function.h151
-rw-r--r--extern/ceres/include/ceres/autodiff_local_parameterization.h40
-rw-r--r--extern/ceres/include/ceres/c_api.h4
-rw-r--r--extern/ceres/include/ceres/ceres.h9
-rw-r--r--extern/ceres/include/ceres/conditioned_cost_function.h16
-rw-r--r--extern/ceres/include/ceres/context.h56
-rw-r--r--extern/ceres/include/ceres/cost_function.h27
-rw-r--r--extern/ceres/include/ceres/cost_function_to_functor.h612
-rw-r--r--extern/ceres/include/ceres/covariance.h79
-rw-r--r--extern/ceres/include/ceres/crs_matrix.h5
-rw-r--r--extern/ceres/include/ceres/cubic_interpolation.h436
-rw-r--r--extern/ceres/include/ceres/dynamic_autodiff_cost_function.h95
-rw-r--r--extern/ceres/include/ceres/dynamic_cost_function.h56
-rw-r--r--extern/ceres/include/ceres/dynamic_cost_function_to_functor.h62
-rw-r--r--extern/ceres/include/ceres/dynamic_numeric_diff_cost_function.h125
-rw-r--r--extern/ceres/include/ceres/evaluation_callback.h80
-rw-r--r--extern/ceres/include/ceres/first_order_function.h54
-rw-r--r--extern/ceres/include/ceres/gradient_checker.h23
-rw-r--r--extern/ceres/include/ceres/gradient_problem.h25
-rw-r--r--extern/ceres/include/ceres/gradient_problem_solver.h128
-rw-r--r--extern/ceres/include/ceres/internal/array_selector.h95
-rw-r--r--extern/ceres/include/ceres/internal/autodiff.h291
-rw-r--r--extern/ceres/include/ceres/internal/disable_warnings.h2
-rw-r--r--extern/ceres/include/ceres/internal/eigen.h41
-rw-r--r--extern/ceres/include/ceres/internal/fixed_array.h573
-rw-r--r--extern/ceres/include/ceres/internal/householder_vector.h (renamed from extern/ceres/internal/ceres/householder_vector.h)19
-rw-r--r--extern/ceres/include/ceres/internal/integer_sequence_algorithm.h165
-rw-r--r--extern/ceres/include/ceres/internal/line_parameterization.h183
-rw-r--r--extern/ceres/include/ceres/internal/macros.h170
-rw-r--r--extern/ceres/include/ceres/internal/manual_constructor.h208
-rw-r--r--extern/ceres/include/ceres/internal/memory.h90
-rw-r--r--extern/ceres/include/ceres/internal/numeric_diff.h198
-rw-r--r--extern/ceres/include/ceres/internal/parameter_dims.h124
-rw-r--r--extern/ceres/include/ceres/internal/port.h62
-rw-r--r--extern/ceres/include/ceres/internal/scoped_ptr.h310
-rw-r--r--extern/ceres/include/ceres/internal/variadic_evaluate.h210
-rw-r--r--extern/ceres/include/ceres/iteration_callback.h64
-rw-r--r--extern/ceres/include/ceres/jet.h693
-rw-r--r--extern/ceres/include/ceres/local_parameterization.h186
-rw-r--r--extern/ceres/include/ceres/loss_function.h85
-rw-r--r--extern/ceres/include/ceres/normal_prior.h12
-rw-r--r--extern/ceres/include/ceres/numeric_diff_cost_function.h147
-rw-r--r--extern/ceres/include/ceres/numeric_diff_options.h22
-rw-r--r--extern/ceres/include/ceres/ordered_groups.h47
-rw-r--r--extern/ceres/include/ceres/problem.h235
-rw-r--r--extern/ceres/include/ceres/rotation.h255
-rw-r--r--extern/ceres/include/ceres/sized_cost_function.h51
-rw-r--r--extern/ceres/include/ceres/solver.h494
-rw-r--r--extern/ceres/include/ceres/tiny_solver.h368
-rw-r--r--extern/ceres/include/ceres/tiny_solver_autodiff_function.h206
-rw-r--r--extern/ceres/include/ceres/tiny_solver_cost_function_adapter.h142
-rw-r--r--extern/ceres/include/ceres/types.h71
-rw-r--r--extern/ceres/include/ceres/version.h6
-rw-r--r--extern/ceres/internal/ceres/accelerate_sparse.cc289
-rw-r--r--extern/ceres/internal/ceres/accelerate_sparse.h147
-rw-r--r--extern/ceres/internal/ceres/array_utils.cc14
-rw-r--r--extern/ceres/internal/ceres/array_utils.h4
-rw-r--r--extern/ceres/internal/ceres/block_jacobi_preconditioner.cc1
-rw-r--r--extern/ceres/internal/ceres/block_jacobi_preconditioner.h18
-rw-r--r--extern/ceres/internal/ceres/block_jacobian_writer.cc3
-rw-r--r--extern/ceres/internal/ceres/block_jacobian_writer.h1
-rw-r--r--extern/ceres/internal/ceres/block_random_access_dense_matrix.cc1
-rw-r--r--extern/ceres/internal/ceres/block_random_access_dense_matrix.h29
-rw-r--r--extern/ceres/internal/ceres/block_random_access_diagonal_matrix.cc6
-rw-r--r--extern/ceres/internal/ceres/block_random_access_diagonal_matrix.h34
-rw-r--r--extern/ceres/internal/ceres/block_random_access_matrix.h17
-rw-r--r--extern/ceres/internal/ceres/block_random_access_sparse_matrix.cc53
-rw-r--r--extern/ceres/internal/ceres/block_random_access_sparse_matrix.h46
-rw-r--r--extern/ceres/internal/ceres/block_sparse_matrix.cc182
-rw-r--r--extern/ceres/internal/ceres/block_sparse_matrix.h102
-rw-r--r--extern/ceres/internal/ceres/block_structure.h9
-rw-r--r--extern/ceres/internal/ceres/c_api.cc6
-rw-r--r--extern/ceres/internal/ceres/callbacks.cc24
-rw-r--r--extern/ceres/internal/ceres/callbacks.h19
-rw-r--r--extern/ceres/internal/ceres/canonical_views_clustering.cc232
-rw-r--r--extern/ceres/internal/ceres/canonical_views_clustering.h124
-rw-r--r--extern/ceres/internal/ceres/cgnr_linear_operator.h12
-rw-r--r--extern/ceres/internal/ceres/cgnr_solver.cc39
-rw-r--r--extern/ceres/internal/ceres/cgnr_solver.h13
-rw-r--r--extern/ceres/internal/ceres/collections_port.h196
-rw-r--r--extern/ceres/internal/ceres/compressed_col_sparse_matrix_utils.cc6
-rw-r--r--extern/ceres/internal/ceres/compressed_row_jacobian_writer.cc6
-rw-r--r--extern/ceres/internal/ceres/compressed_row_jacobian_writer.h2
-rw-r--r--extern/ceres/internal/ceres/compressed_row_sparse_matrix.cc682
-rw-r--r--extern/ceres/internal/ceres/compressed_row_sparse_matrix.h173
-rw-r--r--extern/ceres/internal/ceres/concurrent_queue.h159
-rw-r--r--extern/ceres/internal/ceres/conditioned_cost_function.cc2
-rw-r--r--extern/ceres/internal/ceres/conjugate_gradients_solver.cc13
-rw-r--r--extern/ceres/internal/ceres/conjugate_gradients_solver.h10
-rw-r--r--extern/ceres/internal/ceres/context.cc41
-rw-r--r--extern/ceres/internal/ceres/context_impl.cc43
-rw-r--r--extern/ceres/internal/ceres/context_impl.h67
-rw-r--r--extern/ceres/internal/ceres/coordinate_descent_minimizer.cc137
-rw-r--r--extern/ceres/internal/ceres/coordinate_descent_minimizer.h14
-rw-r--r--extern/ceres/internal/ceres/corrector.cc4
-rw-r--r--extern/ceres/internal/ceres/corrector.h2
-rw-r--r--extern/ceres/internal/ceres/covariance.cc10
-rw-r--r--extern/ceres/internal/ceres/covariance_impl.cc257
-rw-r--r--extern/ceres/internal/ceres/covariance_impl.h8
-rw-r--r--extern/ceres/internal/ceres/cxsparse.cc284
-rw-r--r--extern/ceres/internal/ceres/cxsparse.h59
-rw-r--r--extern/ceres/internal/ceres/dense_normal_cholesky_solver.cc1
-rw-r--r--extern/ceres/internal/ceres/dense_normal_cholesky_solver.h6
-rw-r--r--extern/ceres/internal/ceres/dense_qr_solver.cc2
-rw-r--r--extern/ceres/internal/ceres/dense_qr_solver.h6
-rw-r--r--extern/ceres/internal/ceres/dense_sparse_matrix.cc2
-rw-r--r--extern/ceres/internal/ceres/dense_sparse_matrix.h28
-rw-r--r--extern/ceres/internal/ceres/dogleg_strategy.cc12
-rw-r--r--extern/ceres/internal/ceres/dogleg_strategy.h15
-rw-r--r--extern/ceres/internal/ceres/dynamic_compressed_row_jacobian_writer.cc41
-rw-r--r--extern/ceres/internal/ceres/dynamic_compressed_row_sparse_matrix.h4
-rw-r--r--extern/ceres/internal/ceres/dynamic_sparse_normal_cholesky_solver.cc286
-rw-r--r--extern/ceres/internal/ceres/dynamic_sparse_normal_cholesky_solver.h86
-rw-r--r--extern/ceres/internal/ceres/eigensparse.cc190
-rw-r--r--extern/ceres/internal/ceres/eigensparse.h90
-rw-r--r--extern/ceres/internal/ceres/evaluator.cc8
-rw-r--r--extern/ceres/internal/ceres/evaluator.h61
-rw-r--r--extern/ceres/internal/ceres/execution_summary.h38
-rw-r--r--extern/ceres/internal/ceres/float_cxsparse.cc47
-rw-r--r--extern/ceres/internal/ceres/float_cxsparse.h58
-rw-r--r--extern/ceres/internal/ceres/float_suitesparse.cc47
-rw-r--r--extern/ceres/internal/ceres/float_suitesparse.h58
-rw-r--r--extern/ceres/internal/ceres/function_sample.cc (renamed from extern/ceres/internal/ceres/integral_types.h)86
-rw-r--r--extern/ceres/internal/ceres/function_sample.h94
-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.cc5
-rw-r--r--extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_2_3.cc5
-rw-r--r--extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_2_4.cc5
-rw-r--r--extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_2_d.cc5
-rw-r--r--extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_3.cc5
-rw-r--r--extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_4.cc5
-rw-r--r--extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_6.cc5
-rw-r--r--extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_9.cc5
-rw-r--r--extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_d.cc5
-rw-r--r--extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_3.cc5
-rw-r--r--extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_4.cc5
-rw-r--r--extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_6.cc58
-rw-r--r--extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_8.cc5
-rw-r--r--extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_9.cc5
-rw-r--r--extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_d.cc5
-rw-r--r--extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_d_d.cc5
-rw-r--r--extern/ceres/internal/ceres/generated/partitioned_matrix_view_3_3_3.cc58
-rw-r--r--extern/ceres/internal/ceres/generated/partitioned_matrix_view_4_4_2.cc5
-rw-r--r--extern/ceres/internal/ceres/generated/partitioned_matrix_view_4_4_3.cc5
-rw-r--r--extern/ceres/internal/ceres/generated/partitioned_matrix_view_4_4_4.cc5
-rw-r--r--extern/ceres/internal/ceres/generated/partitioned_matrix_view_4_4_d.cc5
-rw-r--r--extern/ceres/internal/ceres/generated/partitioned_matrix_view_d_d_d.cc5
-rw-r--r--extern/ceres/internal/ceres/generated/schur_eliminator_2_2_2.cc5
-rw-r--r--extern/ceres/internal/ceres/generated/schur_eliminator_2_2_3.cc5
-rw-r--r--extern/ceres/internal/ceres/generated/schur_eliminator_2_2_4.cc5
-rw-r--r--extern/ceres/internal/ceres/generated/schur_eliminator_2_2_d.cc5
-rw-r--r--extern/ceres/internal/ceres/generated/schur_eliminator_2_3_3.cc5
-rw-r--r--extern/ceres/internal/ceres/generated/schur_eliminator_2_3_4.cc5
-rw-r--r--extern/ceres/internal/ceres/generated/schur_eliminator_2_3_6.cc5
-rw-r--r--extern/ceres/internal/ceres/generated/schur_eliminator_2_3_9.cc5
-rw-r--r--extern/ceres/internal/ceres/generated/schur_eliminator_2_3_d.cc5
-rw-r--r--extern/ceres/internal/ceres/generated/schur_eliminator_2_4_3.cc5
-rw-r--r--extern/ceres/internal/ceres/generated/schur_eliminator_2_4_4.cc5
-rw-r--r--extern/ceres/internal/ceres/generated/schur_eliminator_2_4_6.cc58
-rw-r--r--extern/ceres/internal/ceres/generated/schur_eliminator_2_4_8.cc5
-rw-r--r--extern/ceres/internal/ceres/generated/schur_eliminator_2_4_9.cc5
-rw-r--r--extern/ceres/internal/ceres/generated/schur_eliminator_2_4_d.cc5
-rw-r--r--extern/ceres/internal/ceres/generated/schur_eliminator_2_d_d.cc5
-rw-r--r--extern/ceres/internal/ceres/generated/schur_eliminator_3_3_3.cc58
-rw-r--r--extern/ceres/internal/ceres/generated/schur_eliminator_4_4_2.cc5
-rw-r--r--extern/ceres/internal/ceres/generated/schur_eliminator_4_4_3.cc5
-rw-r--r--extern/ceres/internal/ceres/generated/schur_eliminator_4_4_4.cc5
-rw-r--r--extern/ceres/internal/ceres/generated/schur_eliminator_4_4_d.cc5
-rw-r--r--extern/ceres/internal/ceres/generated/schur_eliminator_d_d_d.cc5
-rw-r--r--extern/ceres/internal/ceres/gradient_checker.cc19
-rw-r--r--extern/ceres/internal/ceres/gradient_checking_cost_function.cc34
-rw-r--r--extern/ceres/internal/ceres/gradient_checking_cost_function.h7
-rw-r--r--extern/ceres/internal/ceres/gradient_problem_evaluator.h43
-rw-r--r--extern/ceres/internal/ceres/gradient_problem_solver.cc89
-rw-r--r--extern/ceres/internal/ceres/graph.h55
-rw-r--r--extern/ceres/internal/ceres/graph_algorithms.h84
-rw-r--r--extern/ceres/internal/ceres/implicit_schur_complement.cc1
-rw-r--r--extern/ceres/internal/ceres/implicit_schur_complement.h16
-rw-r--r--extern/ceres/internal/ceres/inner_product_computer.cc330
-rw-r--r--extern/ceres/internal/ceres/inner_product_computer.h157
-rw-r--r--extern/ceres/internal/ceres/invert_psd_matrix.h79
-rw-r--r--extern/ceres/internal/ceres/iterative_refiner.cc74
-rw-r--r--extern/ceres/internal/ceres/iterative_refiner.h93
-rw-r--r--extern/ceres/internal/ceres/iterative_schur_complement_solver.cc108
-rw-r--r--extern/ceres/internal/ceres/iterative_schur_complement_solver.h16
-rw-r--r--extern/ceres/internal/ceres/lapack.cc3
-rw-r--r--extern/ceres/internal/ceres/levenberg_marquardt_strategy.cc12
-rw-r--r--extern/ceres/internal/ceres/levenberg_marquardt_strategy.h12
-rw-r--r--extern/ceres/internal/ceres/line_search.cc219
-rw-r--r--extern/ceres/internal/ceres/line_search.h98
-rw-r--r--extern/ceres/internal/ceres/line_search_minimizer.cc122
-rw-r--r--extern/ceres/internal/ceres/line_search_minimizer.h6
-rw-r--r--extern/ceres/internal/ceres/line_search_preprocessor.cc7
-rw-r--r--extern/ceres/internal/ceres/line_search_preprocessor.h6
-rw-r--r--extern/ceres/internal/ceres/linear_least_squares_problems.cc19
-rw-r--r--extern/ceres/internal/ceres/linear_least_squares_problems.h15
-rw-r--r--extern/ceres/internal/ceres/linear_solver.cc15
-rw-r--r--extern/ceres/internal/ceres/linear_solver.h122
-rw-r--r--extern/ceres/internal/ceres/local_parameterization.cc154
-rw-r--r--extern/ceres/internal/ceres/loss_function.cc19
-rw-r--r--extern/ceres/internal/ceres/low_rank_inverse_hessian.cc10
-rw-r--r--extern/ceres/internal/ceres/low_rank_inverse_hessian.h10
-rw-r--r--extern/ceres/internal/ceres/minimizer.h9
-rw-r--r--extern/ceres/internal/ceres/mutex.h329
-rw-r--r--extern/ceres/internal/ceres/normal_prior.cc1
-rw-r--r--extern/ceres/internal/ceres/pair_hash.h112
-rw-r--r--extern/ceres/internal/ceres/parallel_for.h67
-rw-r--r--extern/ceres/internal/ceres/parallel_for_cxx.cc247
-rw-r--r--extern/ceres/internal/ceres/parallel_for_nothreads.cc78
-rw-r--r--extern/ceres/internal/ceres/parallel_for_openmp.cc88
-rw-r--r--extern/ceres/internal/ceres/parallel_utils.cc90
-rw-r--r--extern/ceres/internal/ceres/parallel_utils.h67
-rw-r--r--extern/ceres/internal/ceres/parameter_block.h217
-rw-r--r--extern/ceres/internal/ceres/parameter_block_ordering.cc35
-rw-r--r--extern/ceres/internal/ceres/partitioned_matrix_view.cc224
-rw-r--r--extern/ceres/internal/ceres/partitioned_matrix_view.h28
-rw-r--r--extern/ceres/internal/ceres/partitioned_matrix_view_impl.h2
-rw-r--r--extern/ceres/internal/ceres/partitioned_matrix_view_template.py (renamed from extern/ceres/internal/ceres/generate_partitioned_matrix_view_specializations.py)86
-rw-r--r--extern/ceres/internal/ceres/polynomial.cc18
-rw-r--r--extern/ceres/internal/ceres/polynomial.h24
-rw-r--r--extern/ceres/internal/ceres/preconditioner.cc3
-rw-r--r--extern/ceres/internal/ceres/preconditioner.h54
-rw-r--r--extern/ceres/internal/ceres/preprocessor.cc25
-rw-r--r--extern/ceres/internal/ceres/preprocessor.h21
-rw-r--r--extern/ceres/internal/ceres/problem.cc207
-rw-r--r--extern/ceres/internal/ceres/problem_impl.cc629
-rw-r--r--extern/ceres/internal/ceres/problem_impl.h121
-rw-r--r--extern/ceres/internal/ceres/program.cc153
-rw-r--r--extern/ceres/internal/ceres/program.h15
-rw-r--r--extern/ceres/internal/ceres/program_evaluator.h252
-rw-r--r--extern/ceres/internal/ceres/random.h6
-rw-r--r--extern/ceres/internal/ceres/reorder_program.cc87
-rw-r--r--extern/ceres/internal/ceres/reorder_program.h17
-rw-r--r--extern/ceres/internal/ceres/residual_block.cc48
-rw-r--r--extern/ceres/internal/ceres/residual_block.h11
-rw-r--r--extern/ceres/internal/ceres/residual_block_utils.cc4
-rw-r--r--extern/ceres/internal/ceres/schur_complement_solver.cc442
-rw-r--r--extern/ceres/internal/ceres/schur_complement_solver.h102
-rw-r--r--extern/ceres/internal/ceres/schur_eliminator.cc204
-rw-r--r--extern/ceres/internal/ceres/schur_eliminator.h352
-rw-r--r--extern/ceres/internal/ceres/schur_eliminator_impl.h336
-rw-r--r--extern/ceres/internal/ceres/schur_eliminator_template.py (renamed from extern/ceres/internal/ceres/generate_eliminator_specialization.py)86
-rw-r--r--extern/ceres/internal/ceres/schur_jacobi_preconditioner.cc35
-rw-r--r--extern/ceres/internal/ceres/schur_jacobi_preconditioner.h21
-rw-r--r--extern/ceres/internal/ceres/schur_templates.cc227
-rw-r--r--extern/ceres/internal/ceres/schur_templates.h46
-rw-r--r--extern/ceres/internal/ceres/scoped_thread_token.h61
-rw-r--r--extern/ceres/internal/ceres/scratch_evaluate_preparer.h4
-rw-r--r--extern/ceres/internal/ceres/single_linkage_clustering.cc94
-rw-r--r--extern/ceres/internal/ceres/single_linkage_clustering.h (renamed from extern/ceres/include/ceres/fpclassify.h)60
-rw-r--r--extern/ceres/internal/ceres/small_blas.h244
-rw-r--r--extern/ceres/internal/ceres/small_blas_generic.h315
-rw-r--r--extern/ceres/internal/ceres/solver.cc398
-rw-r--r--extern/ceres/internal/ceres/solver_utils.cc6
-rw-r--r--extern/ceres/internal/ceres/sparse_cholesky.cc163
-rw-r--r--extern/ceres/internal/ceres/sparse_cholesky.h138
-rw-r--r--extern/ceres/internal/ceres/sparse_matrix.h2
-rw-r--r--extern/ceres/internal/ceres/sparse_normal_cholesky_solver.cc467
-rw-r--r--extern/ceres/internal/ceres/sparse_normal_cholesky_solver.h83
-rw-r--r--extern/ceres/internal/ceres/split.cc2
-rw-r--r--extern/ceres/internal/ceres/stringprintf.cc22
-rw-r--r--extern/ceres/internal/ceres/subset_preconditioner.cc117
-rw-r--r--extern/ceres/internal/ceres/subset_preconditioner.h91
-rw-r--r--extern/ceres/internal/ceres/suitesparse.cc430
-rw-r--r--extern/ceres/internal/ceres/suitesparse.h44
-rw-r--r--extern/ceres/internal/ceres/thread_pool.cc116
-rw-r--r--extern/ceres/internal/ceres/thread_pool.h120
-rw-r--r--extern/ceres/internal/ceres/thread_token_provider.cc76
-rw-r--r--extern/ceres/internal/ceres/thread_token_provider.h97
-rw-r--r--extern/ceres/internal/ceres/triplet_sparse_matrix.cc77
-rw-r--r--extern/ceres/internal/ceres/triplet_sparse_matrix.h62
-rw-r--r--extern/ceres/internal/ceres/trust_region_minimizer.cc73
-rw-r--r--extern/ceres/internal/ceres/trust_region_minimizer.h12
-rw-r--r--extern/ceres/internal/ceres/trust_region_preprocessor.cc173
-rw-r--r--extern/ceres/internal/ceres/trust_region_preprocessor.h6
-rw-r--r--extern/ceres/internal/ceres/trust_region_step_evaluator.cc10
-rw-r--r--extern/ceres/internal/ceres/trust_region_step_evaluator.h4
-rw-r--r--extern/ceres/internal/ceres/trust_region_strategy.h54
-rw-r--r--extern/ceres/internal/ceres/types.cc51
-rw-r--r--extern/ceres/internal/ceres/visibility.cc152
-rw-r--r--extern/ceres/internal/ceres/visibility.h78
-rw-r--r--extern/ceres/internal/ceres/visibility_based_preconditioner.cc585
-rw-r--r--extern/ceres/internal/ceres/visibility_based_preconditioner.h82
-rw-r--r--extern/ceres/internal/ceres/wall_time.cc31
-rw-r--r--extern/ceres/internal/ceres/wall_time.h2
-rw-r--r--extern/ceres/patches/series2
-rw-r--r--extern/ceres/patches/unused_parameter.patch13
-rw-r--r--extern/ceres/patches/unused_variable.patch12
-rw-r--r--extern/draco/CMakeLists.txt2
-rw-r--r--extern/glog/AUTHORS3
-rw-r--r--extern/glog/CMakeLists.txt13
-rw-r--r--extern/glog/ChangeLog15
-rw-r--r--extern/glog/README.blender2
-rw-r--r--extern/glog/include/glog/logging.h97
-rw-r--r--extern/glog/include/glog/raw_logging.h7
-rw-r--r--extern/glog/src/config.h4
-rw-r--r--extern/glog/src/config_mac.h6
-rw-r--r--extern/glog/src/demangle.cc64
-rw-r--r--extern/glog/src/logging.cc130
-rw-r--r--extern/glog/src/raw_logging.cc16
-rw-r--r--extern/glog/src/signalhandler.cc44
-rw-r--r--extern/glog/src/stacktrace.h3
-rw-r--r--extern/glog/src/stacktrace_windows-inl.h50
-rw-r--r--extern/glog/src/stacktrace_x86-inl.h15
-rw-r--r--extern/glog/src/symbolize.cc249
-rw-r--r--extern/glog/src/symbolize.h15
-rw-r--r--extern/glog/src/utilities.cc42
-rw-r--r--extern/glog/src/utilities.h16
-rw-r--r--extern/glog/src/vlog_is_on.cc6
-rw-r--r--extern/glog/src/windows/glog/logging.h35
-rw-r--r--extern/glog/src/windows/glog/raw_logging.h5
-rw-r--r--extern/glog/src/windows/port.cc13
-rw-r--r--extern/glog/src/windows/port.h23
-rwxr-xr-xextern/glog/src/windows/preprocess.sh2
-rw-r--r--extern/gmock/CHANGES126
-rw-r--r--extern/gmock/CMakeLists.txt11
-rw-r--r--extern/gmock/README.blender6
-rw-r--r--extern/gmock/README.md361
-rw-r--r--extern/gmock/include/gmock/gmock-actions.h555
-rw-r--r--extern/gmock/include/gmock/gmock-cardinalities.h32
-rw-r--r--extern/gmock/include/gmock/gmock-function-mocker.h253
-rw-r--r--extern/gmock/include/gmock/gmock-generated-actions.h1109
-rw-r--r--extern/gmock/include/gmock/gmock-generated-function-mockers.h1003
-rw-r--r--extern/gmock/include/gmock/gmock-generated-matchers.h1672
-rw-r--r--extern/gmock/include/gmock/gmock-generated-nice-strict.h397
-rw-r--r--extern/gmock/include/gmock/gmock-matchers.h2701
-rw-r--r--extern/gmock/include/gmock/gmock-more-actions.h110
-rw-r--r--extern/gmock/include/gmock/gmock-more-matchers.h44
-rw-r--r--extern/gmock/include/gmock/gmock-nice-strict.h215
-rw-r--r--extern/gmock/include/gmock/gmock-spec-builders.h636
-rw-r--r--extern/gmock/include/gmock/gmock.h17
-rw-r--r--extern/gmock/include/gmock/internal/custom/README.md16
-rw-r--r--extern/gmock/include/gmock/internal/custom/gmock-generated-actions.h2
-rw-r--r--extern/gmock/include/gmock/internal/custom/gmock-matchers.h13
-rw-r--r--extern/gmock/include/gmock/internal/custom/gmock-port.h13
-rw-r--r--extern/gmock/include/gmock/internal/gmock-generated-internal-utils.h279
-rw-r--r--extern/gmock/include/gmock/internal/gmock-internal-utils.h248
-rw-r--r--extern/gmock/include/gmock/internal/gmock-port.h30
-rw-r--r--extern/gmock/include/gmock/internal/gmock-pp.h317
-rw-r--r--extern/gmock/src/gmock-all.cc3
-rw-r--r--extern/gmock/src/gmock-cardinalities.cc15
-rw-r--r--extern/gmock/src/gmock-internal-utils.cc56
-rw-r--r--extern/gmock/src/gmock-matchers.cc290
-rw-r--r--extern/gmock/src/gmock-spec-builders.cc249
-rw-r--r--extern/gmock/src/gmock.cc56
-rw-r--r--extern/gmock/src/gmock_main.cc17
-rw-r--r--extern/gtest/CHANGES157
-rw-r--r--extern/gtest/CMakeLists.txt19
-rw-r--r--extern/gtest/CONTRIBUTORS37
-rw-r--r--extern/gtest/README.blender6
-rw-r--r--extern/gtest/README.md60
-rw-r--r--extern/gtest/include/gtest/gtest-death-test.h71
-rw-r--r--extern/gtest/include/gtest/gtest-matchers.h750
-rw-r--r--extern/gtest/include/gtest/gtest-message.h56
-rw-r--r--extern/gtest/include/gtest/gtest-param-test.h1225
-rw-r--r--extern/gtest/include/gtest/gtest-printers.h473
-rw-r--r--extern/gtest/include/gtest/gtest-spi.h20
-rw-r--r--extern/gtest/include/gtest/gtest-test-part.h43
-rw-r--r--extern/gtest/include/gtest/gtest-typed-test.h255
-rw-r--r--extern/gtest/include/gtest/gtest.h852
-rw-r--r--extern/gtest/include/gtest/gtest_pred_impl.h81
-rw-r--r--extern/gtest/include/gtest/gtest_prod.h17
-rw-r--r--extern/gtest/include/gtest/internal/custom/README.md56
-rw-r--r--extern/gtest/include/gtest/internal/custom/gtest-port.h34
-rw-r--r--extern/gtest/include/gtest/internal/custom/gtest-printers.h4
-rw-r--r--extern/gtest/include/gtest/internal/custom/gtest.h6
-rw-r--r--extern/gtest/include/gtest/internal/gtest-death-test-internal.h175
-rw-r--r--extern/gtest/include/gtest/internal/gtest-filepath.h13
-rw-r--r--extern/gtest/include/gtest/internal/gtest-internal.h720
-rw-r--r--extern/gtest/include/gtest/internal/gtest-linked_ptr.h243
-rw-r--r--extern/gtest/include/gtest/internal/gtest-param-util-generated.h5146
-rw-r--r--extern/gtest/include/gtest/internal/gtest-param-util.h502
-rw-r--r--extern/gtest/include/gtest/internal/gtest-port-arch.h24
-rw-r--r--extern/gtest/include/gtest/internal/gtest-port.h895
-rw-r--r--extern/gtest/include/gtest/internal/gtest-string.h30
-rw-r--r--extern/gtest/include/gtest/internal/gtest-tuple.h1020
-rw-r--r--extern/gtest/include/gtest/internal/gtest-type-util.h42
-rw-r--r--extern/gtest/src/gtest-all.cc6
-rw-r--r--extern/gtest/src/gtest-death-test.cc567
-rw-r--r--extern/gtest/src/gtest-filepath.cc40
-rw-r--r--extern/gtest/src/gtest-internal-inl.h304
-rw-r--r--extern/gtest/src/gtest-matchers.cc97
-rw-r--r--extern/gtest/src/gtest-port.cc452
-rw-r--r--extern/gtest/src/gtest-printers.cc123
-rw-r--r--extern/gtest/src/gtest-test-part.cc34
-rw-r--r--extern/gtest/src/gtest-typed-test.cc10
-rw-r--r--extern/gtest/src/gtest.cc2487
-rw-r--r--extern/gtest/src/gtest_main.cc15
-rw-r--r--extern/mantaflow/CMakeLists.txt8
-rw-r--r--extern/mantaflow/helper/pwrapper/pconvert.cpp93
-rw-r--r--extern/mantaflow/helper/pwrapper/pconvert.h11
-rw-r--r--extern/mantaflow/helper/util/randomstream.h4
-rw-r--r--extern/mantaflow/preprocessed/fileio/iogrids.cpp565
-rw-r--r--extern/mantaflow/preprocessed/fileio/iomeshes.cpp74
-rw-r--r--extern/mantaflow/preprocessed/fileio/ioparticles.cpp49
-rw-r--r--extern/mantaflow/preprocessed/fileio/ioutil.cpp56
-rw-r--r--extern/mantaflow/preprocessed/fileio/iovdb.cpp618
-rw-r--r--extern/mantaflow/preprocessed/fileio/mantaio.cpp144
-rw-r--r--extern/mantaflow/preprocessed/fileio/mantaio.h110
-rw-r--r--extern/mantaflow/preprocessed/gitinfo.h2
-rw-r--r--extern/mantaflow/preprocessed/grid.cpp42
-rw-r--r--extern/mantaflow/preprocessed/grid.h16
-rw-r--r--extern/mantaflow/preprocessed/grid4d.cpp14
-rw-r--r--extern/mantaflow/preprocessed/grid4d.h10
-rw-r--r--extern/mantaflow/preprocessed/particle.cpp50
-rw-r--r--extern/mantaflow/preprocessed/particle.h33
-rw-r--r--extern/mantaflow/preprocessed/plugin/secondaryparticles.cpp4
-rw-r--r--extern/mantaflow/preprocessed/python/defines.py.reg.cpp3
-rw-r--r--extern/mantaflow/preprocessed/registration.cpp4
-rw-r--r--intern/cycles/CMakeLists.txt1
-rw-r--r--intern/cycles/blender/CMakeLists.txt11
-rw-r--r--intern/cycles/blender/addon/engine.py3
-rw-r--r--intern/cycles/blender/addon/properties.py125
-rw-r--r--intern/cycles/blender/addon/ui.py132
-rw-r--r--intern/cycles/blender/blender_camera.cpp9
-rw-r--r--intern/cycles/blender/blender_curves.cpp681
-rw-r--r--intern/cycles/blender/blender_device.cpp50
-rw-r--r--intern/cycles/blender/blender_geometry.cpp24
-rw-r--r--intern/cycles/blender/blender_mesh.cpp76
-rw-r--r--intern/cycles/blender/blender_object.cpp4
-rw-r--r--intern/cycles/blender/blender_python.cpp11
-rw-r--r--intern/cycles/blender/blender_session.cpp75
-rw-r--r--intern/cycles/blender/blender_shader.cpp8
-rw-r--r--intern/cycles/blender/blender_sync.cpp141
-rw-r--r--intern/cycles/blender/blender_sync.h33
-rw-r--r--intern/cycles/blender/blender_viewport.cpp16
-rw-r--r--intern/cycles/blender/blender_viewport.h4
-rw-r--r--intern/cycles/blender/blender_volume.cpp57
-rw-r--r--intern/cycles/bvh/CMakeLists.txt4
-rw-r--r--intern/cycles/bvh/bvh.cpp72
-rw-r--r--intern/cycles/bvh/bvh.h2
-rw-r--r--intern/cycles/bvh/bvh4.cpp447
-rw-r--r--intern/cycles/bvh/bvh4.h88
-rw-r--r--intern/cycles/bvh/bvh8.cpp541
-rw-r--r--intern/cycles/bvh/bvh8.h99
-rw-r--r--intern/cycles/bvh/bvh_build.cpp169
-rw-r--r--intern/cycles/bvh/bvh_build.h15
-rw-r--r--intern/cycles/bvh/bvh_embree.cpp161
-rw-r--r--intern/cycles/bvh/bvh_embree.h2
-rw-r--r--intern/cycles/bvh/bvh_optix.cpp13
-rw-r--r--intern/cycles/bvh/bvh_params.h2
-rw-r--r--intern/cycles/bvh/bvh_sort.cpp15
-rw-r--r--intern/cycles/bvh/bvh_split.cpp10
-rw-r--r--intern/cycles/bvh/bvh_split.h10
-rw-r--r--intern/cycles/bvh/bvh_unaligned.cpp6
-rw-r--r--intern/cycles/device/CMakeLists.txt12
-rw-r--r--intern/cycles/device/cuda/device_cuda.h33
-rw-r--r--intern/cycles/device/cuda/device_cuda_impl.cpp73
-rw-r--r--intern/cycles/device/device.cpp55
-rw-r--r--intern/cycles/device/device.h11
-rw-r--r--intern/cycles/device/device_cpu.cpp177
-rw-r--r--intern/cycles/device/device_cuda.cpp1
-rw-r--r--intern/cycles/device/device_denoising.cpp10
-rw-r--r--intern/cycles/device/device_denoising.h2
-rw-r--r--intern/cycles/device/device_multi.cpp4
-rw-r--r--intern/cycles/device/device_network.cpp1
-rw-r--r--intern/cycles/device/device_opencl.cpp1
-rw-r--r--intern/cycles/device/device_optix.cpp51
-rw-r--r--intern/cycles/device/device_split_kernel.cpp18
-rw-r--r--intern/cycles/device/device_split_kernel.h4
-rw-r--r--intern/cycles/device/device_task.cpp4
-rw-r--r--intern/cycles/device/device_task.h54
-rw-r--r--intern/cycles/device/opencl/device_opencl.h18
-rw-r--r--intern/cycles/device/opencl/device_opencl_impl.cpp103
-rw-r--r--intern/cycles/kernel/CMakeLists.txt14
-rw-r--r--intern/cycles/kernel/bvh/bvh.h98
-rw-r--r--intern/cycles/kernel/bvh/bvh_local.h63
-rw-r--r--intern/cycles/kernel/bvh/bvh_nodes.h145
-rw-r--r--intern/cycles/kernel/bvh/bvh_shadow_all.h146
-rw-r--r--intern/cycles/kernel/bvh/bvh_traversal.h154
-rw-r--r--intern/cycles/kernel/bvh/bvh_types.h7
-rw-r--r--intern/cycles/kernel/bvh/bvh_volume.h108
-rw-r--r--intern/cycles/kernel/bvh/bvh_volume_all.h136
-rw-r--r--intern/cycles/kernel/bvh/obvh_local.h398
-rw-r--r--intern/cycles/kernel/bvh/obvh_nodes.h410
-rw-r--r--intern/cycles/kernel/bvh/obvh_shadow_all.h664
-rw-r--r--intern/cycles/kernel/bvh/obvh_traversal.h557
-rw-r--r--intern/cycles/kernel/bvh/obvh_volume.h480
-rw-r--r--intern/cycles/kernel/bvh/obvh_volume_all.h551
-rw-r--r--intern/cycles/kernel/bvh/qbvh_local.h291
-rw-r--r--intern/cycles/kernel/bvh/qbvh_nodes.h329
-rw-r--r--intern/cycles/kernel/bvh/qbvh_shadow_all.h453
-rw-r--r--intern/cycles/kernel/bvh/qbvh_traversal.h420
-rw-r--r--intern/cycles/kernel/bvh/qbvh_volume.h367
-rw-r--r--intern/cycles/kernel/bvh/qbvh_volume_all.h444
-rw-r--r--intern/cycles/kernel/closure/bsdf.h48
-rw-r--r--intern/cycles/kernel/closure/bsdf_hair_principled.h7
-rw-r--r--intern/cycles/kernel/geom/geom_curve.h91
-rw-r--r--intern/cycles/kernel/geom/geom_curve_intersect.h1354
-rw-r--r--intern/cycles/kernel/geom/geom_motion_curve.h112
-rw-r--r--intern/cycles/kernel/geom/geom_object.h80
-rw-r--r--intern/cycles/kernel/geom/geom_primitive.h5
-rw-r--r--intern/cycles/kernel/geom/geom_triangle.h21
-rw-r--r--intern/cycles/kernel/geom/geom_triangle_intersect.h427
-rw-r--r--intern/cycles/kernel/kernel_emission.h4
-rw-r--r--intern/cycles/kernel/kernel_light.h508
-rw-r--r--intern/cycles/kernel/kernel_light_background.h448
-rw-r--r--intern/cycles/kernel/kernel_light_common.h159
-rw-r--r--intern/cycles/kernel/kernel_montecarlo.h10
-rw-r--r--intern/cycles/kernel/kernel_shader.h41
-rw-r--r--intern/cycles/kernel/kernel_types.h87
-rw-r--r--intern/cycles/kernel/kernels/cpu/kernel.cpp10
-rw-r--r--intern/cycles/kernel/kernels/optix/kernel_optix.cu26
-rw-r--r--intern/cycles/kernel/osl/CMakeLists.txt9
-rw-r--r--intern/cycles/kernel/osl/osl_closures.cpp79
-rw-r--r--intern/cycles/kernel/osl/osl_closures.h2
-rw-r--r--intern/cycles/kernel/shaders/node_sky_texture.osl123
-rw-r--r--intern/cycles/kernel/svm/svm_closure.h50
-rw-r--r--intern/cycles/kernel/svm/svm_geometry.h2
-rw-r--r--intern/cycles/kernel/svm/svm_noise.h4
-rw-r--r--intern/cycles/kernel/svm/svm_sky.h301
-rw-r--r--intern/cycles/kernel/svm/svm_types.h2
-rw-r--r--intern/cycles/render/CMakeLists.txt2
-rw-r--r--intern/cycles/render/curves.cpp110
-rw-r--r--intern/cycles/render/curves.h62
-rw-r--r--intern/cycles/render/denoising.cpp6
-rw-r--r--intern/cycles/render/geometry.cpp41
-rw-r--r--intern/cycles/render/hair.cpp1
-rw-r--r--intern/cycles/render/hair.h1
-rw-r--r--intern/cycles/render/image.cpp1
-rw-r--r--intern/cycles/render/image.h2
-rw-r--r--intern/cycles/render/image_sky.cpp91
-rw-r--r--intern/cycles/render/image_sky.h49
-rw-r--r--intern/cycles/render/integrator.cpp1
-rw-r--r--intern/cycles/render/light.cpp140
-rw-r--r--intern/cycles/render/nodes.cpp199
-rw-r--r--intern/cycles/render/nodes.h9
-rw-r--r--intern/cycles/render/object.cpp82
-rw-r--r--intern/cycles/render/scene.cpp13
-rw-r--r--intern/cycles/render/scene.h12
-rw-r--r--intern/cycles/render/session.cpp46
-rw-r--r--intern/cycles/render/session.h14
-rw-r--r--intern/cycles/render/shader.cpp1
-rw-r--r--intern/cycles/render/svm.cpp3
-rw-r--r--intern/cycles/test/render_graph_finalize_test.cpp4
-rw-r--r--intern/cycles/util/CMakeLists.txt5
-rw-r--r--intern/cycles/util/util_debug.cpp15
-rw-r--r--intern/cycles/util/util_debug.h6
-rw-r--r--intern/cycles/util/util_math_fast.h8
-rw-r--r--intern/cycles/util/util_openimagedenoise.h39
-rw-r--r--intern/cycles/util/util_sky_model.h24
-rw-r--r--intern/cycles/util/util_sky_nishita.cpp371
-rw-r--r--intern/cycles/util/util_task.cpp430
-rw-r--r--intern/cycles/util/util_task.h102
-rw-r--r--intern/cycles/util/util_tbb.h39
-rw-r--r--intern/cycles/util/util_version.h2
-rw-r--r--intern/ghost/intern/GHOST_IXrGraphicsBinding.h3
-rw-r--r--intern/ghost/intern/GHOST_XrGraphicsBinding.cpp35
-rw-r--r--intern/ghost/intern/GHOST_XrSession.cpp11
-rw-r--r--intern/ghost/intern/GHOST_XrSwapchain.cpp7
-rw-r--r--intern/ghost/intern/GHOST_XrSwapchain.h3
-rw-r--r--intern/guardedalloc/MEM_guardedalloc.h3
-rw-r--r--intern/libmv/libmv/multiview/projection_test.cc8
-rw-r--r--intern/libmv/libmv/simple_pipeline/bundle.cc17
-rw-r--r--intern/mantaflow/extern/manta_fluid_API.h24
-rw-r--r--intern/mantaflow/intern/MANTA_main.cpp1609
-rw-r--r--intern/mantaflow/intern/MANTA_main.h29
-rw-r--r--intern/mantaflow/intern/manta_fluid_API.cpp47
-rw-r--r--intern/mantaflow/intern/strings/fluid_script.h155
-rw-r--r--intern/mantaflow/intern/strings/liquid_script.h109
-rw-r--r--intern/mantaflow/intern/strings/smoke_script.h45
-rw-r--r--intern/quadriflow/quadriflow_capi.cpp2
-rw-r--r--intern/rigidbody/RBI_api.h2
-rwxr-xr-xrelease/datafiles/blender_icons_geom_update.py4
-rw-r--r--release/datafiles/icons/ops.mesh.extrude_manifold.datbin0 -> 494 bytes
m---------release/datafiles/locale0
m---------release/scripts/addons0
m---------release/scripts/addons_contrib0
-rw-r--r--release/scripts/modules/addon_utils.py21
-rw-r--r--release/scripts/modules/bl_previews_utils/bl_previews_render.py4
-rw-r--r--release/scripts/modules/bl_ui_utils/bug_report_url.py6
-rw-r--r--release/scripts/modules/bpy/ops.py2
-rw-r--r--release/scripts/modules/bpy/utils/previews.py7
-rw-r--r--release/scripts/modules/rna_keymap_ui.py3
-rw-r--r--release/scripts/modules/rna_prop_ui.py2
-rw-r--r--release/scripts/presets/keyconfig/keymap_data/blender_default.py62
-rw-r--r--release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py2
-rw-r--r--release/scripts/startup/bl_operators/__init__.py1
-rw-r--r--release/scripts/startup/bl_operators/gpencil_mesh_bake.py162
-rw-r--r--release/scripts/startup/bl_operators/node.py9
-rw-r--r--release/scripts/startup/bl_operators/object.py5
-rw-r--r--release/scripts/startup/bl_operators/object_quick_effects.py7
-rw-r--r--release/scripts/startup/bl_operators/presets.py8
-rw-r--r--release/scripts/startup/bl_operators/screen_play_rendered_anim.py34
-rw-r--r--release/scripts/startup/bl_operators/sequencer.py12
-rw-r--r--release/scripts/startup/bl_operators/vertexpaint_dirt.py30
-rw-r--r--release/scripts/startup/bl_operators/view3d.py2
-rw-r--r--release/scripts/startup/bl_operators/wm.py12
-rw-r--r--release/scripts/startup/bl_ui/properties_constraint.py2097
-rw-r--r--release/scripts/startup/bl_ui/properties_data_camera.py1
-rw-r--r--release/scripts/startup/bl_ui/properties_data_gpencil.py4
-rw-r--r--release/scripts/startup/bl_ui/properties_data_mesh.py28
-rw-r--r--release/scripts/startup/bl_ui/properties_data_modifier.py484
-rw-r--r--release/scripts/startup/bl_ui/properties_data_shaderfx.py118
-rw-r--r--release/scripts/startup/bl_ui/properties_output.py2
-rw-r--r--release/scripts/startup/bl_ui/properties_paint_common.py25
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_common.py1
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_fluid.py69
-rw-r--r--release/scripts/startup/bl_ui/properties_render.py4
-rw-r--r--release/scripts/startup/bl_ui/properties_scene.py13
-rw-r--r--release/scripts/startup/bl_ui/space_outliner.py6
-rw-r--r--release/scripts/startup/bl_ui/space_sequencer.py12
-rw-r--r--release/scripts/startup/bl_ui/space_text.py5
-rw-r--r--release/scripts/startup/bl_ui/space_time.py4
-rw-r--r--release/scripts/startup/bl_ui/space_toolsystem_common.py4
-rw-r--r--release/scripts/startup/bl_ui/space_toolsystem_toolbar.py75
-rw-r--r--release/scripts/startup/bl_ui/space_userpref.py47
-rw-r--r--release/scripts/startup/bl_ui/space_view3d.py27
-rw-r--r--release/scripts/startup/bl_ui/space_view3d_toolbar.py14
-rw-r--r--source/blender/blenkernel/BKE_blender_version.h2
-rw-r--r--source/blender/blenkernel/BKE_blendfile.h1
-rw-r--r--source/blender/blenkernel/BKE_collection.h11
-rw-r--r--source/blender/blenkernel/BKE_colortools.h7
-rw-r--r--source/blender/blenkernel/BKE_constraint.h5
-rw-r--r--source/blender/blenkernel/BKE_curveprofile.h22
-rw-r--r--source/blender/blenkernel/BKE_derived_node_tree.hh69
-rw-r--r--source/blender/blenkernel/BKE_effect.h1
-rw-r--r--source/blender/blenkernel/BKE_global.h15
-rw-r--r--source/blender/blenkernel/BKE_gpencil.h1
-rw-r--r--source/blender/blenkernel/BKE_gpencil_geom.h13
-rw-r--r--source/blender/blenkernel/BKE_gpencil_modifier.h10
-rw-r--r--source/blender/blenkernel/BKE_idprop.h4
-rw-r--r--source/blender/blenkernel/BKE_lib_id.h15
-rw-r--r--source/blender/blenkernel/BKE_lib_override.h4
-rw-r--r--source/blender/blenkernel/BKE_main.h1
-rw-r--r--source/blender/blenkernel/BKE_mesh.h1
-rw-r--r--source/blender/blenkernel/BKE_mesh_remesh_voxel.h1
-rw-r--r--source/blender/blenkernel/BKE_mesh_runtime.h1
-rw-r--r--source/blender/blenkernel/BKE_modifier.h96
-rw-r--r--source/blender/blenkernel/BKE_node.h2
-rw-r--r--source/blender/blenkernel/BKE_node_tree_ref.hh37
-rw-r--r--source/blender/blenkernel/BKE_object.h6
-rw-r--r--source/blender/blenkernel/BKE_paint.h44
-rw-r--r--source/blender/blenkernel/BKE_pbvh.h15
-rw-r--r--source/blender/blenkernel/BKE_pointcache.h2
-rw-r--r--source/blender/blenkernel/BKE_screen.h4
-rw-r--r--source/blender/blenkernel/BKE_shader_fx.h7
-rw-r--r--source/blender/blenkernel/BKE_subdiv_ccg.h15
-rw-r--r--source/blender/blenkernel/BKE_undo_system.h4
-rw-r--r--source/blender/blenkernel/intern/DerivedMesh.c4
-rw-r--r--source/blender/blenkernel/intern/action.c2
-rw-r--r--source/blender/blenkernel/intern/anim_data.c2
-rw-r--r--source/blender/blenkernel/intern/armature_deform.c3
-rw-r--r--source/blender/blenkernel/intern/blender_copybuffer.c9
-rw-r--r--source/blender/blenkernel/intern/blender_undo.c4
-rw-r--r--source/blender/blenkernel/intern/blendfile.c25
-rw-r--r--source/blender/blenkernel/intern/boids.c5
-rw-r--r--source/blender/blenkernel/intern/bpath.c18
-rw-r--r--source/blender/blenkernel/intern/brush.c56
-rw-r--r--source/blender/blenkernel/intern/collection.c101
-rw-r--r--source/blender/blenkernel/intern/colortools.c28
-rw-r--r--source/blender/blenkernel/intern/constraint.c54
-rw-r--r--source/blender/blenkernel/intern/crazyspace.c8
-rw-r--r--source/blender/blenkernel/intern/curve.c6
-rw-r--r--source/blender/blenkernel/intern/curveprofile.c287
-rw-r--r--source/blender/blenkernel/intern/customdata.c54
-rw-r--r--source/blender/blenkernel/intern/derived_node_tree.cc55
-rw-r--r--source/blender/blenkernel/intern/dynamicpaint.c2
-rw-r--r--source/blender/blenkernel/intern/editmesh.c11
-rw-r--r--source/blender/blenkernel/intern/effect.c42
-rw-r--r--source/blender/blenkernel/intern/fcurve.c6
-rw-r--r--source/blender/blenkernel/intern/fcurve_driver.c14
-rw-r--r--source/blender/blenkernel/intern/fluid.c100
-rw-r--r--source/blender/blenkernel/intern/font.c16
-rw-r--r--source/blender/blenkernel/intern/gpencil.c66
-rw-r--r--source/blender/blenkernel/intern/gpencil_curve.c3
-rw-r--r--source/blender/blenkernel/intern/gpencil_geom.c406
-rw-r--r--source/blender/blenkernel/intern/gpencil_modifier.c60
-rw-r--r--source/blender/blenkernel/intern/idprop.c4
-rw-r--r--source/blender/blenkernel/intern/image.c46
-rw-r--r--source/blender/blenkernel/intern/image_save.c6
-rw-r--r--source/blender/blenkernel/intern/lib_id.c73
-rw-r--r--source/blender/blenkernel/intern/lib_id_delete.c9
-rw-r--r--source/blender/blenkernel/intern/lib_override.c215
-rw-r--r--source/blender/blenkernel/intern/lib_remap.c5
-rw-r--r--source/blender/blenkernel/intern/library.c18
-rw-r--r--source/blender/blenkernel/intern/main.c23
-rw-r--r--source/blender/blenkernel/intern/mesh_evaluate.c80
-rw-r--r--source/blender/blenkernel/intern/mesh_remesh_voxel.c31
-rw-r--r--source/blender/blenkernel/intern/mesh_runtime.c31
-rw-r--r--source/blender/blenkernel/intern/modifier.c2
-rw-r--r--source/blender/blenkernel/intern/movieclip.c26
-rw-r--r--source/blender/blenkernel/intern/node.c4
-rw-r--r--source/blender/blenkernel/intern/node_tree_ref.cc25
-rw-r--r--source/blender/blenkernel/intern/object.c388
-rw-r--r--source/blender/blenkernel/intern/object_update.c2
-rw-r--r--source/blender/blenkernel/intern/packedFile.c30
-rw-r--r--source/blender/blenkernel/intern/paint.c36
-rw-r--r--source/blender/blenkernel/intern/particle.c3
-rw-r--r--source/blender/blenkernel/intern/particle_system.c9
-rw-r--r--source/blender/blenkernel/intern/pbvh.c31
-rw-r--r--source/blender/blenkernel/intern/pbvh_intern.h3
-rw-r--r--source/blender/blenkernel/intern/pointcache.c35
-rw-r--r--source/blender/blenkernel/intern/rigidbody.c7
-rw-r--r--source/blender/blenkernel/intern/scene.c71
-rw-r--r--source/blender/blenkernel/intern/screen.c26
-rw-r--r--source/blender/blenkernel/intern/seqeffects.c8
-rw-r--r--source/blender/blenkernel/intern/seqmodifier.c2
-rw-r--r--source/blender/blenkernel/intern/sequencer.c631
-rw-r--r--source/blender/blenkernel/intern/shader_fx.c20
-rw-r--r--source/blender/blenkernel/intern/simulation.cc17
-rw-r--r--source/blender/blenkernel/intern/softbody.c5
-rw-r--r--source/blender/blenkernel/intern/sound.c8
-rw-r--r--source/blender/blenkernel/intern/studiolight.c6
-rw-r--r--source/blender/blenkernel/intern/subdiv_ccg.c38
-rw-r--r--source/blender/blenkernel/intern/text.c71
-rw-r--r--source/blender/blenlib/BLI_array.hh21
-rw-r--r--source/blender/blenlib/BLI_color.hh4
-rw-r--r--source/blender/blenlib/BLI_dot_export.hh10
-rw-r--r--source/blender/blenlib/BLI_dot_export_attribute_enums.hh4
-rw-r--r--source/blender/blenlib/BLI_hash.hh26
-rw-r--r--source/blender/blenlib/BLI_hash_tables.hh8
-rw-r--r--source/blender/blenlib/BLI_index_range.hh2
-rw-r--r--source/blender/blenlib/BLI_listbase_wrapper.hh4
-rw-r--r--source/blender/blenlib/BLI_map.hh15
-rw-r--r--source/blender/blenlib/BLI_math_geom.h4
-rw-r--r--source/blender/blenlib/BLI_memory_utils.hh15
-rw-r--r--source/blender/blenlib/BLI_optional.hh189
-rw-r--r--source/blender/blenlib/BLI_set.hh16
-rw-r--r--source/blender/blenlib/BLI_string_ref.hh2
-rw-r--r--source/blender/blenlib/BLI_vector.hh6
-rw-r--r--source/blender/blenlib/CMakeLists.txt1
-rw-r--r--source/blender/blenlib/intern/BLI_kdopbvh.c4
-rw-r--r--source/blender/blenlib/intern/delaunay_2d.c37
-rw-r--r--source/blender/blenlib/intern/dot_export.cc6
-rw-r--r--source/blender/blenlib/intern/edgehash.c2
-rw-r--r--source/blender/blenlib/intern/math_bits_inline.c2
-rw-r--r--source/blender/blenlib/intern/math_geom.c26
-rw-r--r--source/blender/blenlib/intern/math_matrix.c16
-rw-r--r--source/blender/blenlib/intern/math_rotation.c6
-rw-r--r--source/blender/blenlib/intern/storage.c2
-rw-r--r--source/blender/blenloader/BLO_readfile.h26
-rw-r--r--source/blender/blenloader/BLO_writefile.h40
-rw-r--r--source/blender/blenloader/intern/blend_validate.c14
-rw-r--r--source/blender/blenloader/intern/readfile.c1787
-rw-r--r--source/blender/blenloader/intern/versioning_250.c4
-rw-r--r--source/blender/blenloader/intern/versioning_270.c2
-rw-r--r--source/blender/blenloader/intern/versioning_280.c37
-rw-r--r--source/blender/blenloader/intern/versioning_290.c73
-rw-r--r--source/blender/blenloader/intern/versioning_cycles.c78
-rw-r--r--source/blender/blenloader/intern/versioning_defaults.c16
-rw-r--r--source/blender/blenloader/intern/versioning_legacy.c8
-rw-r--r--source/blender/blenloader/intern/writefile.c289
-rw-r--r--source/blender/bmesh/intern/bmesh_interp.c18
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh_convert.c10
-rw-r--r--source/blender/bmesh/intern/bmesh_opdefines.c15
-rw-r--r--source/blender/bmesh/intern/bmesh_operators.h9
-rw-r--r--source/blender/bmesh/intern/bmesh_polygon_edgenet.c3
-rw-r--r--source/blender/bmesh/operators/bmo_bevel.c4
-rw-r--r--source/blender/bmesh/operators/bmo_connect.c49
-rw-r--r--source/blender/bmesh/tools/bmesh_bevel.c577
-rw-r--r--source/blender/bmesh/tools/bmesh_bevel.h2
-rw-r--r--source/blender/compositor/intern/COM_NodeOperation.h2
-rw-r--r--source/blender/compositor/intern/COM_NodeOperationBuilder.cpp4
-rw-r--r--source/blender/compositor/nodes/COM_FlipNode.cpp4
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder.cc6
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder.h6
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_cache.cc19
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_cache.h12
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_cycle.cc6
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_cycle.h6
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_map.cc29
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_map.h9
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes.cc25
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes.h11
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc13
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes_scene.cc6
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc6
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_pchanmap.cc6
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_pchanmap.h6
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations.cc98
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations.h10
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations_impl.h6
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations_keys.cc6
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc8
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations_scene.cc8
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc10
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_remove_noop.cc6
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_remove_noop.h6
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_rna.cc8
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_rna.h6
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_transitive.cc6
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_transitive.h6
-rw-r--r--source/blender/depsgraph/intern/debug/deg_debug.cc6
-rw-r--r--source/blender/depsgraph/intern/debug/deg_debug.h6
-rw-r--r--source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc42
-rw-r--r--source/blender/depsgraph/intern/debug/deg_debug_stats_gnuplot.cc14
-rw-r--r--source/blender/depsgraph/intern/debug/deg_time_average.h6
-rw-r--r--source/blender/depsgraph/intern/depsgraph.cc34
-rw-r--r--source/blender/depsgraph/intern/depsgraph.h6
-rw-r--r--source/blender/depsgraph/intern/depsgraph_build.cc144
-rw-r--r--source/blender/depsgraph/intern/depsgraph_debug.cc94
-rw-r--r--source/blender/depsgraph/intern/depsgraph_eval.cc20
-rw-r--r--source/blender/depsgraph/intern/depsgraph_physics.cc16
-rw-r--r--source/blender/depsgraph/intern/depsgraph_physics.h6
-rw-r--r--source/blender/depsgraph/intern/depsgraph_query.cc36
-rw-r--r--source/blender/depsgraph/intern/depsgraph_query_foreach.cc18
-rw-r--r--source/blender/depsgraph/intern/depsgraph_query_iter.cc32
-rw-r--r--source/blender/depsgraph/intern/depsgraph_registry.cc6
-rw-r--r--source/blender/depsgraph/intern/depsgraph_registry.h6
-rw-r--r--source/blender/depsgraph/intern/depsgraph_relation.cc6
-rw-r--r--source/blender/depsgraph/intern/depsgraph_relation.h6
-rw-r--r--source/blender/depsgraph/intern/depsgraph_tag.cc38
-rw-r--r--source/blender/depsgraph/intern/depsgraph_tag.h6
-rw-r--r--source/blender/depsgraph/intern/depsgraph_type.cc10
-rw-r--r--source/blender/depsgraph/intern/depsgraph_type.h16
-rw-r--r--source/blender/depsgraph/intern/depsgraph_update.cc12
-rw-r--r--source/blender/depsgraph/intern/depsgraph_update.h6
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval.cc6
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval.h6
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc12
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h6
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_flush.cc41
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_flush.h6
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc6
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.h6
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.cc6
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.h6
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_modifier.cc22
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_modifier.h12
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_movieclip.cc6
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_movieclip.h6
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc43
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.h8
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_pose.cc6
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_pose.h11
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_scene.cc6
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_scene.h6
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequence.cc6
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequence.h6
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequencer.cc19
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequencer.h9
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sound.cc6
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sound.h6
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_volume.cc6
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_volume.h6
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_stats.cc6
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_stats.h6
-rw-r--r--source/blender/depsgraph/intern/node/deg_node.cc6
-rw-r--r--source/blender/depsgraph/intern/node/deg_node.h6
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_component.cc6
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_component.h15
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_factory.cc6
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_factory.h6
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_factory_impl.h6
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_id.cc6
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_id.h6
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_operation.cc10
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_operation.h8
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_time.cc6
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_time.h6
-rw-r--r--source/blender/draw/CMakeLists.txt4
-rw-r--r--source/blender/draw/DRW_select_buffer.h14
-rw-r--r--source/blender/draw/engines/basic/basic_engine.c2
-rw-r--r--source/blender/draw/engines/eevee/eevee_data.c144
-rw-r--r--source/blender/draw/engines/eevee/eevee_effects.c29
-rw-r--r--source/blender/draw/engines/eevee/eevee_engine.c137
-rw-r--r--source/blender/draw/engines/eevee/eevee_lightcache.c1
-rw-r--r--source/blender/draw/engines/eevee/eevee_lightprobes.c4
-rw-r--r--source/blender/draw/engines/eevee/eevee_materials.c16
-rw-r--r--source/blender/draw/engines/eevee/eevee_mist.c2
-rw-r--r--source/blender/draw/engines/eevee/eevee_motion_blur.c598
-rw-r--r--source/blender/draw/engines/eevee/eevee_occlusion.c2
-rw-r--r--source/blender/draw/engines/eevee/eevee_private.h117
-rw-r--r--source/blender/draw/engines/eevee/eevee_render.c81
-rw-r--r--source/blender/draw/engines/eevee/eevee_renderpasses.c49
-rw-r--r--source/blender/draw/engines/eevee/eevee_screen_raytrace.c2
-rw-r--r--source/blender/draw/engines/eevee/eevee_shadows.c2
-rw-r--r--source/blender/draw/engines/eevee/eevee_subsurface.c25
-rw-r--r--source/blender/draw/engines/eevee/eevee_temporal_sampling.c41
-rw-r--r--source/blender/draw/engines/eevee/eevee_volumes.c2
-rw-r--r--source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl15
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_motion_blur_frag.glsl247
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_temporal_aa.glsl19
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_velocity_resolve_frag.glsl18
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_velocity_tile_frag.glsl151
-rw-r--r--source/blender/draw/engines/eevee/shaders/object_motion_frag.glsl27
-rw-r--r--source/blender/draw/engines/eevee/shaders/object_motion_vert.glsl54
-rw-r--r--source/blender/draw/engines/external/external_engine.c2
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_cache_utils.c8
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_engine.c21
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_engine.h2
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_shader_fx.c8
-rw-r--r--source/blender/draw/engines/overlay/overlay_armature.c6
-rw-r--r--source/blender/draw/engines/overlay/overlay_edit_text.c27
-rw-r--r--source/blender/draw/engines/overlay/overlay_engine.c2
-rw-r--r--source/blender/draw/engines/overlay/overlay_extra.c2
-rw-r--r--source/blender/draw/engines/overlay/overlay_grid.c4
-rw-r--r--source/blender/draw/engines/overlay/overlay_private.h8
-rw-r--r--source/blender/draw/engines/overlay/overlay_shader.c33
-rw-r--r--source/blender/draw/engines/overlay/overlay_wireframe.c3
-rw-r--r--source/blender/draw/engines/overlay/shaders/armature_dof_solid_frag.glsl11
-rw-r--r--source/blender/draw/engines/overlay/shaders/armature_dof_vert.glsl4
-rw-r--r--source/blender/draw/engines/overlay/shaders/edit_mesh_vert.glsl2
-rw-r--r--source/blender/draw/engines/overlay/shaders/grid_frag.glsl7
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_cavity_lib.glsl2
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl3
-rw-r--r--source/blender/draw/engines/workbench/workbench_engine.c37
-rw-r--r--source/blender/draw/engines/workbench/workbench_materials.c2
-rw-r--r--source/blender/draw/engines/workbench/workbench_private.h2
-rw-r--r--source/blender/draw/engines/workbench/workbench_transparent.c6
-rw-r--r--source/blender/draw/intern/DRW_render.h8
-rw-r--r--source/blender/draw/intern/draw_cache.c32
-rw-r--r--source/blender/draw/intern/draw_cache.h3
-rw-r--r--source/blender/draw/intern/draw_cache_extract.h3
-rw-r--r--source/blender/draw/intern/draw_cache_extract_mesh.c3415
-rw-r--r--source/blender/draw/intern/draw_cache_impl.h6
-rw-r--r--source/blender/draw/intern/draw_cache_impl_curve.c10
-rw-r--r--source/blender/draw/intern/draw_cache_impl_gpencil.c21
-rw-r--r--source/blender/draw/intern/draw_cache_impl_mesh.c98
-rw-r--r--source/blender/draw/intern/draw_cache_impl_metaball.c12
-rw-r--r--source/blender/draw/intern/draw_cache_inline.h24
-rw-r--r--source/blender/draw/intern/draw_common.h7
-rw-r--r--source/blender/draw/intern/draw_hair.c157
-rw-r--r--source/blender/draw/intern/draw_manager.c49
-rw-r--r--source/blender/draw/intern/draw_manager_data.c37
-rw-r--r--source/blender/draw/intern/draw_select_buffer.c7
-rw-r--r--source/blender/editors/animation/anim_filter.c9
-rw-r--r--source/blender/editors/animation/time_scrub_ui.c44
-rw-r--r--source/blender/editors/armature/armature_edit.c20
-rw-r--r--source/blender/editors/armature/armature_relations.c30
-rw-r--r--source/blender/editors/armature/armature_select.c6
-rw-r--r--source/blender/editors/armature/armature_skinning.c4
-rw-r--r--source/blender/editors/armature/meshlaplacian.c5
-rw-r--r--source/blender/editors/curve/editcurve.c4
-rw-r--r--source/blender/editors/curve/editcurve_select.c4
-rw-r--r--source/blender/editors/curve/editfont.c8
-rw-r--r--source/blender/editors/datafiles/CMakeLists.txt1
-rw-r--r--source/blender/editors/gpencil/CMakeLists.txt1
-rw-r--r--source/blender/editors/gpencil/annotate_draw.c47
-rw-r--r--source/blender/editors/gpencil/annotate_paint.c480
-rw-r--r--source/blender/editors/gpencil/drawgpencil.c45
-rw-r--r--source/blender/editors/gpencil/editaction_gpencil.c130
-rw-r--r--source/blender/editors/gpencil/gpencil_add_stroke.c17
-rw-r--r--source/blender/editors/gpencil/gpencil_convert.c444
-rw-r--r--source/blender/editors/gpencil/gpencil_data.c208
-rw-r--r--source/blender/editors/gpencil/gpencil_edit.c374
-rw-r--r--source/blender/editors/gpencil/gpencil_fill.c134
-rw-r--r--source/blender/editors/gpencil/gpencil_intern.h104
-rw-r--r--source/blender/editors/gpencil/gpencil_interpolate.c62
-rw-r--r--source/blender/editors/gpencil/gpencil_merge.c20
-rw-r--r--source/blender/editors/gpencil/gpencil_mesh.c404
-rw-r--r--source/blender/editors/gpencil/gpencil_ops.c143
-rw-r--r--source/blender/editors/gpencil/gpencil_paint.c348
-rw-r--r--source/blender/editors/gpencil/gpencil_primitive.c132
-rw-r--r--source/blender/editors/gpencil/gpencil_sculpt_paint.c367
-rw-r--r--source/blender/editors/gpencil/gpencil_select.c236
-rw-r--r--source/blender/editors/gpencil/gpencil_utils.c267
-rw-r--r--source/blender/editors/gpencil/gpencil_uv.c4
-rw-r--r--source/blender/editors/gpencil/gpencil_vertex_ops.c58
-rw-r--r--source/blender/editors/gpencil/gpencil_vertex_paint.c134
-rw-r--r--source/blender/editors/gpencil/gpencil_weight_paint.c112
-rw-r--r--source/blender/editors/include/ED_anim_api.h25
-rw-r--r--source/blender/editors/include/ED_armature.h2
-rw-r--r--source/blender/editors/include/ED_curve.h2
-rw-r--r--source/blender/editors/include/ED_gpencil.h88
-rw-r--r--source/blender/editors/include/ED_mball.h8
-rw-r--r--source/blender/editors/include/ED_mesh.h13
-rw-r--r--source/blender/editors/include/ED_object.h10
-rw-r--r--source/blender/editors/include/ED_render.h2
-rw-r--r--source/blender/editors/include/ED_screen_types.h2
-rw-r--r--source/blender/editors/include/ED_time_scrub_ui.h5
-rw-r--r--source/blender/editors/include/UI_interface.h15
-rw-r--r--source/blender/editors/include/UI_interface_icons.h1
-rw-r--r--source/blender/editors/include/UI_view2d.h7
-rw-r--r--source/blender/editors/interface/interface.c4
-rw-r--r--source/blender/editors/interface/interface_context_menu.c2
-rw-r--r--source/blender/editors/interface/interface_draw.c278
-rw-r--r--source/blender/editors/interface/interface_handlers.c162
-rw-r--r--source/blender/editors/interface/interface_icons.c20
-rw-r--r--source/blender/editors/interface/interface_intern.h5
-rw-r--r--source/blender/editors/interface/interface_layout.c1
-rw-r--r--source/blender/editors/interface/interface_ops.c10
-rw-r--r--source/blender/editors/interface/interface_panel.c188
-rw-r--r--source/blender/editors/interface/interface_region_menu_popup.c14
-rw-r--r--source/blender/editors/interface/interface_region_search.c11
-rw-r--r--source/blender/editors/interface/interface_region_tooltip.c7
-rw-r--r--source/blender/editors/interface/interface_templates.c813
-rw-r--r--source/blender/editors/interface/interface_utils.c23
-rw-r--r--source/blender/editors/interface/interface_widgets.c250
-rw-r--r--source/blender/editors/interface/view2d.c94
-rw-r--r--source/blender/editors/interface/view2d_ops.c24
-rw-r--r--source/blender/editors/io/io_alembic.c8
-rw-r--r--source/blender/editors/io/io_collada.c1
-rw-r--r--source/blender/editors/mesh/editmesh_bevel.c109
-rw-r--r--source/blender/editors/mesh/editmesh_knife.c10
-rw-r--r--source/blender/editors/mesh/editmesh_tools.c11
-rw-r--r--source/blender/editors/mesh/mesh_data.c178
-rw-r--r--source/blender/editors/mesh/mesh_intern.h2
-rw-r--r--source/blender/editors/mesh/mesh_ops.c6
-rw-r--r--source/blender/editors/mesh/meshtools.c10
-rw-r--r--source/blender/editors/metaball/mball_edit.c115
-rw-r--r--source/blender/editors/object/CMakeLists.txt4
-rw-r--r--source/blender/editors/object/object_add.c237
-rw-r--r--source/blender/editors/object/object_constraint.c71
-rw-r--r--source/blender/editors/object/object_data_transfer.c12
-rw-r--r--source/blender/editors/object/object_edit.c33
-rw-r--r--source/blender/editors/object/object_facemap_ops.c7
-rw-r--r--source/blender/editors/object/object_gpencil_modifier.c230
-rw-r--r--source/blender/editors/object/object_intern.h8
-rw-r--r--source/blender/editors/object/object_modes.c2
-rw-r--r--source/blender/editors/object/object_modifier.c166
-rw-r--r--source/blender/editors/object/object_ops.c5
-rw-r--r--source/blender/editors/object/object_relations.c292
-rw-r--r--source/blender/editors/object/object_remesh.c20
-rw-r--r--source/blender/editors/object/object_shader_fx.c98
-rw-r--r--source/blender/editors/object/object_shapekey.c32
-rw-r--r--source/blender/editors/object/object_transform.c50
-rw-r--r--source/blender/editors/object/object_vgroup.c255
-rw-r--r--source/blender/editors/physics/particle_edit.c2
-rw-r--r--source/blender/editors/physics/physics_fluid.c26
-rw-r--r--source/blender/editors/physics/physics_pointcache.c2
-rw-r--r--source/blender/editors/render/render_update.c4
-rw-r--r--source/blender/editors/scene/scene_edit.c2
-rw-r--r--source/blender/editors/screen/area.c33
-rw-r--r--source/blender/editors/screen/screen_edit.c1
-rw-r--r--source/blender/editors/screen/screen_ops.c129
-rw-r--r--source/blender/editors/screen/workspace_layout_edit.c2
-rw-r--r--source/blender/editors/sculpt_paint/CMakeLists.txt2
-rw-r--r--source/blender/editors/sculpt_paint/paint_cursor.c2
-rw-r--r--source/blender/editors/sculpt_paint/paint_image.c2
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_proj.c4
-rw-r--r--source/blender/editors/sculpt_paint/paint_mask.c13
-rw-r--r--source/blender/editors/sculpt_paint/paint_ops.c20
-rw-r--r--source/blender/editors/sculpt_paint/paint_stroke.c5
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex.c12
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c687
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_cloth.c40
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_detail.c2
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_face_set.c10
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_color.c323
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_mask.c10
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_mesh.c17
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_intern.h40
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_mask_expand.c10
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c4
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_paint_color.c477
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_pose.c26
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_smooth.c50
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_transform.c11
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_undo.c68
-rw-r--r--source/blender/editors/sound/sound_ops.c5
-rw-r--r--source/blender/editors/space_action/action_edit.c22
-rw-r--r--source/blender/editors/space_action/action_select.c18
-rw-r--r--source/blender/editors/space_action/space_action.c30
-rw-r--r--source/blender/editors/space_buttons/CMakeLists.txt4
-rw-r--r--source/blender/editors/space_buttons/buttons_context.c6
-rw-r--r--source/blender/editors/space_buttons/space_buttons.c19
-rw-r--r--source/blender/editors/space_clip/clip_dopesheet_draw.c4
-rw-r--r--source/blender/editors/space_clip/clip_ops.c2
-rw-r--r--source/blender/editors/space_clip/space_clip.c12
-rw-r--r--source/blender/editors/space_console/space_console.c7
-rw-r--r--source/blender/editors/space_file/filelist.c2
-rw-r--r--source/blender/editors/space_file/space_file.c5
-rw-r--r--source/blender/editors/space_graph/space_graph.c32
-rw-r--r--source/blender/editors/space_image/image_edit.c2
-rw-r--r--source/blender/editors/space_image/image_ops.c30
-rw-r--r--source/blender/editors/space_info/space_info.c7
-rw-r--r--source/blender/editors/space_nla/nla_buttons.c4
-rw-r--r--source/blender/editors/space_nla/space_nla.c27
-rw-r--r--source/blender/editors/space_node/CMakeLists.txt4
-rw-r--r--source/blender/editors/space_node/drawnode.c38
-rw-r--r--source/blender/editors/space_node/node_draw.c7
-rw-r--r--source/blender/editors/space_node/node_edit.c6
-rw-r--r--source/blender/editors/space_node/space_node.c5
-rw-r--r--source/blender/editors/space_outliner/outliner_collections.c3
-rw-r--r--source/blender/editors/space_outliner/outliner_dragdrop.c3
-rw-r--r--source/blender/editors/space_outliner/outliner_draw.c59
-rw-r--r--source/blender/editors/space_outliner/outliner_edit.c30
-rw-r--r--source/blender/editors/space_outliner/outliner_intern.h29
-rw-r--r--source/blender/editors/space_outliner/outliner_select.c305
-rw-r--r--source/blender/editors/space_outliner/outliner_sync.c29
-rw-r--r--source/blender/editors/space_outliner/outliner_tools.c216
-rw-r--r--source/blender/editors/space_outliner/outliner_tree.c30
-rw-r--r--source/blender/editors/space_outliner/outliner_utils.c41
-rw-r--r--source/blender/editors/space_outliner/space_outliner.c9
-rw-r--r--source/blender/editors/space_sequencer/sequencer_draw.c20
-rw-r--r--source/blender/editors/space_sequencer/sequencer_edit.c26
-rw-r--r--source/blender/editors/space_sequencer/sequencer_intern.h1
-rw-r--r--source/blender/editors/space_sequencer/sequencer_select.c22
-rw-r--r--source/blender/editors/space_sequencer/space_sequencer.c7
-rw-r--r--source/blender/editors/space_text/text_ops.c85
-rw-r--r--source/blender/editors/space_view3d/view3d_camera_control.c2
-rw-r--r--source/blender/editors/space_view3d/view3d_draw.c3
-rw-r--r--source/blender/editors/space_view3d/view3d_edit.c2
-rw-r--r--source/blender/editors/space_view3d/view3d_fly.c30
-rw-r--r--source/blender/editors/space_view3d/view3d_header.c2
-rw-r--r--source/blender/editors/space_view3d/view3d_placement.c2
-rw-r--r--source/blender/editors/space_view3d/view3d_project.c6
-rw-r--r--source/blender/editors/space_view3d/view3d_select.c2
-rw-r--r--source/blender/editors/space_view3d/view3d_snap.c8
-rw-r--r--source/blender/editors/space_view3d/view3d_utils.c2
-rw-r--r--source/blender/editors/space_view3d/view3d_walk.c31
-rw-r--r--source/blender/editors/transform/transform.c63
-rw-r--r--source/blender/editors/transform/transform_constraints.c164
-rw-r--r--source/blender/editors/transform/transform_constraints.h6
-rw-r--r--source/blender/editors/transform/transform_convert.c45
-rw-r--r--source/blender/editors/transform/transform_convert.h2
-rw-r--r--source/blender/editors/transform/transform_convert_armature.c33
-rw-r--r--source/blender/editors/transform/transform_convert_curve.c2
-rw-r--r--source/blender/editors/transform/transform_convert_mask.c2
-rw-r--r--source/blender/editors/transform/transform_convert_mesh.c929
-rw-r--r--source/blender/editors/transform/transform_data.h4
-rw-r--r--source/blender/editors/transform/transform_generics.c64
-rw-r--r--source/blender/editors/transform/transform_mode.c40
-rw-r--r--source/blender/editors/transform/transform_mode.h4
-rw-r--r--source/blender/editors/transform/transform_mode_bbone_resize.c5
-rw-r--r--source/blender/editors/transform/transform_mode_boneenvelope.c1
-rw-r--r--source/blender/editors/transform/transform_mode_edge_slide.c323
-rw-r--r--source/blender/editors/transform/transform_mode_resize.c35
-rw-r--r--source/blender/editors/transform/transform_mode_rotate.c63
-rw-r--r--source/blender/editors/transform/transform_mode_translate.c31
-rw-r--r--source/blender/editors/transform/transform_mode_vert_slide.c38
-rw-r--r--source/blender/editors/transform/transform_ops.c4
-rw-r--r--source/blender/editors/transform/transform_snap.c259
-rw-r--r--source/blender/editors/transform/transform_snap.h2
-rw-r--r--source/blender/editors/transform/transform_snap_object.c114
-rw-r--r--source/blender/editors/uvedit/uvedit_draw.c98
-rw-r--r--source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp2
-rw-r--r--source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp4
-rw-r--r--source/blender/freestyle/intern/python/BPy_Freestyle.cpp2
-rw-r--r--source/blender/freestyle/intern/view_map/AutoPtrHelper.h21
-rw-r--r--source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp8
-rw-r--r--source/blender/functions/CMakeLists.txt16
-rw-r--r--source/blender/functions/FN_array_spans.hh209
-rw-r--r--source/blender/functions/FN_attributes_ref.hh248
-rw-r--r--source/blender/functions/FN_cpp_type.hh73
-rw-r--r--source/blender/functions/FN_cpp_types.hh4
-rw-r--r--source/blender/functions/FN_generic_vector_array.hh208
-rw-r--r--source/blender/functions/FN_multi_function.hh108
-rw-r--r--source/blender/functions/FN_multi_function_builder.hh232
-rw-r--r--source/blender/functions/FN_multi_function_context.hh48
-rw-r--r--source/blender/functions/FN_multi_function_data_type.hh127
-rw-r--r--source/blender/functions/FN_multi_function_network.hh495
-rw-r--r--source/blender/functions/FN_multi_function_network_evaluation.hh66
-rw-r--r--source/blender/functions/FN_multi_function_param_type.hh165
-rw-r--r--source/blender/functions/FN_multi_function_params.hh240
-rw-r--r--source/blender/functions/FN_multi_function_signature.hh166
-rw-r--r--source/blender/functions/FN_spans.hh404
-rw-r--r--source/blender/functions/intern/attributes_ref.cc72
-rw-r--r--source/blender/functions/intern/cpp_types.cc4
-rw-r--r--source/blender/functions/intern/multi_function.cc (renamed from source/blender/io/alembic/intern/abc_writer_nurbs.h)40
-rw-r--r--source/blender/functions/intern/multi_function_network.cc278
-rw-r--r--source/blender/functions/intern/multi_function_network_evaluation.cc1063
-rw-r--r--source/blender/gpencil_modifiers/CMakeLists.txt5
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c458
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.h66
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilarmature.c45
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c152
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c94
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c48
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c87
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c60
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c46
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilmultiply.c76
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c78
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c41
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c86
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c54
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c53
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c40
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpenciltexture.c62
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c52
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpenciltime.c97
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c62
-rw-r--r--source/blender/gpu/CMakeLists.txt1
-rw-r--r--source/blender/gpu/GPU_buffers.h2
-rw-r--r--source/blender/gpu/GPU_draw.h2
-rw-r--r--source/blender/gpu/GPU_vertex_buffer.h2
-rw-r--r--source/blender/gpu/GPU_vertex_format.h4
-rw-r--r--source/blender/gpu/intern/gpu_buffers.c38
-rw-r--r--source/blender/gpu/intern/gpu_draw.c75
-rw-r--r--source/blender/gpu/intern/gpu_draw_smoke.c3
-rw-r--r--source/blender/gpu/intern/gpu_extensions.c23
-rw-r--r--source/blender/gpu/intern/gpu_platform.c3
-rw-r--r--source/blender/gpu/intern/gpu_shader.c5
-rw-r--r--source/blender/gpu/intern/gpu_state.c2
-rw-r--r--source/blender/gpu/intern/gpu_texture.c13
-rw-r--r--source/blender/gpu/intern/gpu_vertex_buffer.c29
-rw-r--r--source/blender/gpu/intern/gpu_vertex_format.c14
-rw-r--r--source/blender/gpu/shaders/gpu_shader_2D_edituvs_edges_frag.glsl30
-rw-r--r--source/blender/gpu/shaders/gpu_shader_2D_edituvs_edges_vert.glsl6
-rw-r--r--source/blender/gpu/shaders/gpu_shader_2D_widget_base_frag.glsl86
-rw-r--r--source/blender/gpu/shaders/gpu_shader_2D_widget_base_vert.glsl311
-rw-r--r--source/blender/gpu/shaders/gpu_shader_point_varying_color_varying_outline_aa_frag.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_ambient_occlusion.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_anisotropic.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_diffuse.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_eevee_specular.glsl3
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_glass.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_glossy.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl14
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_refraction.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_toon.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_translucent.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_transparent.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_velvet.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_wireframe.glsl4
-rw-r--r--source/blender/io/CMakeLists.txt2
-rw-r--r--source/blender/io/alembic/ABC_alembic.h6
-rw-r--r--source/blender/io/alembic/CMakeLists.txt52
-rw-r--r--source/blender/io/alembic/exporter/abc_archive.cc265
-rw-r--r--source/blender/io/alembic/exporter/abc_archive.h87
-rw-r--r--source/blender/io/alembic/exporter/abc_export_capi.cc220
-rw-r--r--source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc261
-rw-r--r--source/blender/io/alembic/exporter/abc_hierarchy_iterator.h90
-rw-r--r--source/blender/io/alembic/exporter/abc_subdiv_disabler.cc107
-rw-r--r--source/blender/io/alembic/exporter/abc_subdiv_disabler.h55
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_abstract.cc101
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_abstract.h77
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_camera.cc110
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_camera.h52
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_curves.cc (renamed from source/blender/io/alembic/intern/abc_writer_curves.cc)89
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_curves.h (renamed from source/blender/io/alembic/intern/abc_writer_curves.h)44
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_hair.cc (renamed from source/blender/io/alembic/intern/abc_writer_hair.cc)125
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_hair.h (renamed from source/blender/io/alembic/intern/abc_writer_hair.h)48
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_mball.cc90
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_mball.h (renamed from source/blender/io/alembic/intern/abc_writer_camera.h)36
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_mesh.cc (renamed from source/blender/io/alembic/intern/abc_writer_mesh.cc)588
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_mesh.h95
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_nurbs.cc (renamed from source/blender/io/alembic/intern/abc_writer_nurbs.cc)96
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_nurbs.h57
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_points.cc148
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_points.h (renamed from source/blender/io/alembic/intern/abc_writer_archive.h)38
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_transform.cc115
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_transform.h (renamed from source/blender/io/alembic/intern/abc_writer_points.h)40
-rw-r--r--source/blender/io/alembic/intern/abc_axis_conversion.cc15
-rw-r--r--source/blender/io/alembic/intern/abc_axis_conversion.h22
-rw-r--r--source/blender/io/alembic/intern/abc_customdata.cc7
-rw-r--r--source/blender/io/alembic/intern/abc_customdata.h11
-rw-r--r--source/blender/io/alembic/intern/abc_exporter.cc673
-rw-r--r--source/blender/io/alembic/intern/abc_exporter.h127
-rw-r--r--source/blender/io/alembic/intern/abc_reader_archive.cc8
-rw-r--r--source/blender/io/alembic/intern/abc_reader_archive.h12
-rw-r--r--source/blender/io/alembic/intern/abc_reader_camera.cc8
-rw-r--r--source/blender/io/alembic/intern/abc_reader_camera.h12
-rw-r--r--source/blender/io/alembic/intern/abc_reader_curves.cc8
-rw-r--r--source/blender/io/alembic/intern/abc_reader_curves.h12
-rw-r--r--source/blender/io/alembic/intern/abc_reader_mesh.cc13
-rw-r--r--source/blender/io/alembic/intern/abc_reader_mesh.h12
-rw-r--r--source/blender/io/alembic/intern/abc_reader_nurbs.cc8
-rw-r--r--source/blender/io/alembic/intern/abc_reader_nurbs.h12
-rw-r--r--source/blender/io/alembic/intern/abc_reader_object.cc8
-rw-r--r--source/blender/io/alembic/intern/abc_reader_object.h12
-rw-r--r--source/blender/io/alembic/intern/abc_reader_points.cc8
-rw-r--r--source/blender/io/alembic/intern/abc_reader_points.h12
-rw-r--r--source/blender/io/alembic/intern/abc_reader_transform.cc8
-rw-r--r--source/blender/io/alembic/intern/abc_reader_transform.h12
-rw-r--r--source/blender/io/alembic/intern/abc_util.cc22
-rw-r--r--source/blender/io/alembic/intern/abc_util.h25
-rw-r--r--source/blender/io/alembic/intern/abc_writer_archive.cc98
-rw-r--r--source/blender/io/alembic/intern/abc_writer_camera.cc79
-rw-r--r--source/blender/io/alembic/intern/abc_writer_mball.cc95
-rw-r--r--source/blender/io/alembic/intern/abc_writer_mball.h56
-rw-r--r--source/blender/io/alembic/intern/abc_writer_mesh.h91
-rw-r--r--source/blender/io/alembic/intern/abc_writer_object.cc77
-rw-r--r--source/blender/io/alembic/intern/abc_writer_object.h69
-rw-r--r--source/blender/io/alembic/intern/abc_writer_points.cc121
-rw-r--r--source/blender/io/alembic/intern/abc_writer_transform.cc119
-rw-r--r--source/blender/io/alembic/intern/abc_writer_transform.h60
-rw-r--r--source/blender/io/alembic/intern/alembic_capi.cc234
-rw-r--r--source/blender/io/collada/AnimationImporter.cpp6
-rw-r--r--source/blender/io/collada/BCMath.cpp2
-rw-r--r--source/blender/io/collada/ImageExporter.cpp4
-rw-r--r--source/blender/io/common/CMakeLists.txt (renamed from build_files/build_environment/cmake/hidapi.cmake)34
-rw-r--r--source/blender/io/common/IO_abstract_hierarchy_iterator.h (renamed from source/blender/io/usd/intern/abstract_hierarchy_iterator.h)80
-rw-r--r--source/blender/io/common/intern/abstract_hierarchy_iterator.cc (renamed from source/blender/io/usd/intern/abstract_hierarchy_iterator.cc)123
-rw-r--r--source/blender/io/usd/CMakeLists.txt10
-rw-r--r--source/blender/io/usd/intern/usd_capi.cc22
-rw-r--r--source/blender/io/usd/intern/usd_exporter_context.h8
-rw-r--r--source/blender/io/usd/intern/usd_hierarchy_iterator.cc8
-rw-r--r--source/blender/io/usd/intern/usd_hierarchy_iterator.h14
-rw-r--r--source/blender/io/usd/intern/usd_writer_abstract.cc8
-rw-r--r--source/blender/io/usd/intern/usd_writer_abstract.h13
-rw-r--r--source/blender/io/usd/intern/usd_writer_camera.cc8
-rw-r--r--source/blender/io/usd/intern/usd_writer_camera.h8
-rw-r--r--source/blender/io/usd/intern/usd_writer_hair.cc8
-rw-r--r--source/blender/io/usd/intern/usd_writer_hair.h8
-rw-r--r--source/blender/io/usd/intern/usd_writer_light.cc8
-rw-r--r--source/blender/io/usd/intern/usd_writer_light.h8
-rw-r--r--source/blender/io/usd/intern/usd_writer_mesh.cc8
-rw-r--r--source/blender/io/usd/intern/usd_writer_mesh.h8
-rw-r--r--source/blender/io/usd/intern/usd_writer_metaball.cc8
-rw-r--r--source/blender/io/usd/intern/usd_writer_metaball.h8
-rw-r--r--source/blender/io/usd/intern/usd_writer_transform.cc8
-rw-r--r--source/blender/io/usd/intern/usd_writer_transform.h8
-rw-r--r--source/blender/makesdna/DNA_ID.h47
-rw-r--r--source/blender/makesdna/DNA_action_types.h3
-rw-r--r--source/blender/makesdna/DNA_brush_defaults.h1
-rw-r--r--source/blender/makesdna/DNA_brush_types.h28
-rw-r--r--source/blender/makesdna/DNA_constraint_types.h7
-rw-r--r--source/blender/makesdna/DNA_curveprofile_types.h20
-rw-r--r--source/blender/makesdna/DNA_customdata_types.h5
-rw-r--r--source/blender/makesdna/DNA_fluid_types.h145
-rw-r--r--source/blender/makesdna/DNA_gpencil_modifier_types.h4
-rw-r--r--source/blender/makesdna/DNA_gpencil_types.h4
-rw-r--r--source/blender/makesdna/DNA_image_types.h2
-rw-r--r--source/blender/makesdna/DNA_key_types.h2
-rw-r--r--source/blender/makesdna/DNA_material_types.h2
-rw-r--r--source/blender/makesdna/DNA_mesh_types.h7
-rw-r--r--source/blender/makesdna/DNA_meshdata_types.h2
-rw-r--r--source/blender/makesdna/DNA_modifier_types.h26
-rw-r--r--source/blender/makesdna/DNA_movieclip_types.h2
-rw-r--r--source/blender/makesdna/DNA_node_types.h14
-rw-r--r--source/blender/makesdna/DNA_object_force_types.h4
-rw-r--r--source/blender/makesdna/DNA_outliner_types.h3
-rw-r--r--source/blender/makesdna/DNA_scene_defaults.h6
-rw-r--r--source/blender/makesdna/DNA_scene_types.h14
-rw-r--r--source/blender/makesdna/DNA_screen_types.h16
-rw-r--r--source/blender/makesdna/DNA_shader_fx_types.h9
-rw-r--r--source/blender/makesdna/DNA_sound_types.h2
-rw-r--r--source/blender/makesdna/DNA_space_types.h6
-rw-r--r--source/blender/makesdna/DNA_text_types.h22
-rw-r--r--source/blender/makesdna/DNA_userdef_types.h16
-rw-r--r--source/blender/makesdna/DNA_vfont_types.h2
-rw-r--r--source/blender/makesdna/intern/CMakeLists.txt1
-rw-r--r--source/blender/makesdna/intern/dna_genfile.c14
-rw-r--r--source/blender/makesdna/intern/dna_rename_defs.h6
-rw-r--r--source/blender/makesdna/intern/makesdna.c2
-rw-r--r--source/blender/makesrna/RNA_access.h18
-rw-r--r--source/blender/makesrna/intern/CMakeLists.txt29
-rw-r--r--source/blender/makesrna/intern/makesrna.c12
-rw-r--r--source/blender/makesrna/intern/rna_ID.c25
-rw-r--r--source/blender/makesrna/intern/rna_access.c90
-rw-r--r--source/blender/makesrna/intern/rna_access_compare_override.c147
-rw-r--r--source/blender/makesrna/intern/rna_action.c2
-rw-r--r--source/blender/makesrna/intern/rna_brush.c65
-rw-r--r--source/blender/makesrna/intern/rna_cachefile.c9
-rw-r--r--source/blender/makesrna/intern/rna_constraint.c10
-rw-r--r--source/blender/makesrna/intern/rna_curve.c5
-rw-r--r--source/blender/makesrna/intern/rna_curveprofile.c40
-rw-r--r--source/blender/makesrna/intern/rna_define.c5
-rw-r--r--source/blender/makesrna/intern/rna_fcurve.c4
-rw-r--r--source/blender/makesrna/intern/rna_fluid.c58
-rw-r--r--source/blender/makesrna/intern/rna_gpencil.c6
-rw-r--r--source/blender/makesrna/intern/rna_gpencil_modifier.c15
-rw-r--r--source/blender/makesrna/intern/rna_image.c4
-rw-r--r--source/blender/makesrna/intern/rna_image_api.c4
-rw-r--r--source/blender/makesrna/intern/rna_internal.h1
-rw-r--r--source/blender/makesrna/intern/rna_internal_types.h4
-rw-r--r--source/blender/makesrna/intern/rna_key.c5
-rw-r--r--source/blender/makesrna/intern/rna_lattice.c2
-rw-r--r--source/blender/makesrna/intern/rna_light.c1
-rw-r--r--source/blender/makesrna/intern/rna_linestyle.c1
-rw-r--r--source/blender/makesrna/intern/rna_main.c12
-rw-r--r--source/blender/makesrna/intern/rna_main_api.c8
-rw-r--r--source/blender/makesrna/intern/rna_material.c3
-rw-r--r--source/blender/makesrna/intern/rna_mesh.c242
-rw-r--r--source/blender/makesrna/intern/rna_modifier.c47
-rw-r--r--source/blender/makesrna/intern/rna_movieclip.c2
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c65
-rw-r--r--source/blender/makesrna/intern/rna_object.c109
-rw-r--r--source/blender/makesrna/intern/rna_object_force.c9
-rw-r--r--source/blender/makesrna/intern/rna_pose.c30
-rw-r--r--source/blender/makesrna/intern/rna_render.c2
-rw-r--r--source/blender/makesrna/intern/rna_rna.c92
-rw-r--r--source/blender/makesrna/intern/rna_scene.c44
-rw-r--r--source/blender/makesrna/intern/rna_sculpt_paint.c6
-rw-r--r--source/blender/makesrna/intern/rna_sequencer.c108
-rw-r--r--source/blender/makesrna/intern/rna_sequencer_api.c34
-rw-r--r--source/blender/makesrna/intern/rna_shader_fx.c14
-rw-r--r--source/blender/makesrna/intern/rna_simulation.c1
-rw-r--r--source/blender/makesrna/intern/rna_sound.c2
-rw-r--r--source/blender/makesrna/intern/rna_sound_api.c3
-rw-r--r--source/blender/makesrna/intern/rna_space.c12
-rw-r--r--source/blender/makesrna/intern/rna_text.c14
-rw-r--r--source/blender/makesrna/intern/rna_texture.c1
-rw-r--r--source/blender/makesrna/intern/rna_ui_api.c82
-rw-r--r--source/blender/makesrna/intern/rna_userdef.c24
-rw-r--r--source/blender/makesrna/intern/rna_vfont.c2
-rw-r--r--source/blender/makesrna/intern/rna_vfont_api.c3
-rw-r--r--source/blender/makesrna/intern/rna_wm.c2
-rw-r--r--source/blender/makesrna/intern/rna_world.c1
-rw-r--r--source/blender/modifiers/CMakeLists.txt4
-rw-r--r--source/blender/modifiers/intern/MOD_armature.c13
-rw-r--r--source/blender/modifiers/intern/MOD_bevel.c78
-rw-r--r--source/blender/modifiers/intern/MOD_cast.c2
-rw-r--r--source/blender/modifiers/intern/MOD_collision.c32
-rw-r--r--source/blender/modifiers/intern/MOD_correctivesmooth.c28
-rw-r--r--source/blender/modifiers/intern/MOD_decimate.c4
-rw-r--r--source/blender/modifiers/intern/MOD_displace.c4
-rw-r--r--source/blender/modifiers/intern/MOD_explode.c11
-rw-r--r--source/blender/modifiers/intern/MOD_hook.c29
-rw-r--r--source/blender/modifiers/intern/MOD_meshdeform.c35
-rw-r--r--source/blender/modifiers/intern/MOD_meshsequencecache.c11
-rw-r--r--source/blender/modifiers/intern/MOD_mirror.c13
-rw-r--r--source/blender/modifiers/intern/MOD_multires.c8
-rw-r--r--source/blender/modifiers/intern/MOD_ocean.c11
-rw-r--r--source/blender/modifiers/intern/MOD_particlesystem.c16
-rw-r--r--source/blender/modifiers/intern/MOD_remesh.c4
-rw-r--r--source/blender/modifiers/intern/MOD_solidify.c2
-rw-r--r--source/blender/modifiers/intern/MOD_solidify_extrude.c2
-rw-r--r--source/blender/modifiers/intern/MOD_subsurf.c13
-rw-r--r--source/blender/modifiers/intern/MOD_surface.c15
-rw-r--r--source/blender/modifiers/intern/MOD_surfacedeform.c64
-rw-r--r--source/blender/modifiers/intern/MOD_ui_common.c127
-rw-r--r--source/blender/modifiers/intern/MOD_warp.c25
-rw-r--r--source/blender/modifiers/intern/MOD_wave.c2
-rw-r--r--source/blender/modifiers/intern/MOD_weightvgedit.c25
-rw-r--r--source/blender/nodes/CMakeLists.txt2
-rw-r--r--source/blender/nodes/NOD_composite.h8
-rw-r--r--source/blender/nodes/NOD_shader.h8
-rw-r--r--source/blender/nodes/NOD_socket.h8
-rw-r--r--source/blender/nodes/NOD_texture.h8
-rw-r--r--source/blender/nodes/composite/node_composite_util.h8
-rw-r--r--source/blender/nodes/intern/node_common.c2
-rw-r--r--source/blender/nodes/intern/node_exec.h8
-rw-r--r--source/blender/nodes/intern/node_socket.cc (renamed from source/blender/nodes/intern/node_socket.c)109
-rw-r--r--source/blender/nodes/shader/node_shader_util.h8
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_sky.c11
-rw-r--r--source/blender/nodes/texture/node_texture_util.h8
-rw-r--r--source/blender/physics/intern/BPH_mass_spring.cpp60
-rw-r--r--source/blender/physics/intern/implicit.h5
-rw-r--r--source/blender/physics/intern/implicit_blender.c63
-rw-r--r--source/blender/python/bmesh/bmesh_py_types_customdata.c3
-rw-r--r--source/blender/python/intern/bpy_library_write.c41
-rw-r--r--source/blender/python/intern/bpy_msgbus.c4
-rw-r--r--source/blender/python/intern/bpy_props.c72
-rw-r--r--source/blender/python/intern/bpy_rna.c2
-rw-r--r--source/blender/python/mathutils/mathutils_Matrix.c37
-rw-r--r--source/blender/python/mathutils/mathutils_Quaternion.c14
-rw-r--r--source/blender/python/mathutils/mathutils_Vector.c6
-rw-r--r--source/blender/python/mathutils/mathutils_noise.c2
-rw-r--r--source/blender/render/intern/source/external_engine.c2
-rw-r--r--source/blender/render/intern/source/pipeline.c2
-rw-r--r--source/blender/render/intern/source/render_texture.c2
-rw-r--r--source/blender/shader_fx/CMakeLists.txt5
-rw-r--r--source/blender/shader_fx/intern/FX_shader_blur.c40
-rw-r--r--source/blender/shader_fx/intern/FX_shader_colorize.c47
-rw-r--r--source/blender/shader_fx/intern/FX_shader_flip.c36
-rw-r--r--source/blender/shader_fx/intern/FX_shader_glow.c48
-rw-r--r--source/blender/shader_fx/intern/FX_shader_light.c10
-rw-r--r--source/blender/shader_fx/intern/FX_shader_pixel.c40
-rw-r--r--source/blender/shader_fx/intern/FX_shader_rim.c61
-rw-r--r--source/blender/shader_fx/intern/FX_shader_shadow.c94
-rw-r--r--source/blender/shader_fx/intern/FX_shader_swirl.c32
-rw-r--r--source/blender/shader_fx/intern/FX_shader_wave.c35
-rw-r--r--source/blender/shader_fx/intern/FX_ui_common.c272
-rw-r--r--source/blender/shader_fx/intern/FX_ui_common.h56
-rw-r--r--source/blender/windowmanager/gizmo/intern/wm_gizmo.c2
-rw-r--r--source/blender/windowmanager/intern/wm.c4
-rw-r--r--source/blender/windowmanager/intern/wm_draw.c61
-rw-r--r--source/blender/windowmanager/intern/wm_files.c107
-rw-r--r--source/blender/windowmanager/intern/wm_files_link.c20
-rw-r--r--source/blender/windowmanager/intern/wm_gesture_ops.c2
-rw-r--r--source/blender/windowmanager/intern/wm_init_exit.c8
-rw-r--r--source/blender/windowmanager/intern/wm_operator_props.c4
-rw-r--r--source/blender/windowmanager/intern/wm_operators.c2
-rw-r--r--source/blender/windowmanager/intern/wm_surface.c14
-rw-r--r--source/blender/windowmanager/intern/wm_window.c1
-rw-r--r--source/blender/windowmanager/wm_surface.h5
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_session.c6
-rw-r--r--source/creator/creator.c26
-rw-r--r--tests/gtests/alembic/CMakeLists.txt2
-rw-r--r--tests/gtests/alembic/abc_export_test.cc213
-rw-r--r--tests/gtests/alembic/abc_matrix_test.cc8
-rw-r--r--tests/gtests/blenlib/BLI_array_test.cc69
-rw-r--r--tests/gtests/blenlib/BLI_edgehash_test.cc3
-rw-r--r--tests/gtests/blenlib/BLI_index_mask_test.cc4
-rw-r--r--tests/gtests/blenlib/BLI_index_range_test.cc74
-rw-r--r--tests/gtests/blenlib/BLI_linear_allocator_test.cc16
-rw-r--r--tests/gtests/blenlib/BLI_map_test.cc93
-rw-r--r--tests/gtests/blenlib/BLI_math_matrix_test.cc99
-rw-r--r--tests/gtests/blenlib/BLI_optional_test.cc61
-rw-r--r--tests/gtests/blenlib/BLI_set_test.cc42
-rw-r--r--tests/gtests/blenlib/BLI_span_test.cc64
-rw-r--r--tests/gtests/blenlib/BLI_stack_cxx_test.cc44
-rw-r--r--tests/gtests/blenlib/BLI_string_ref_test.cc38
-rw-r--r--tests/gtests/blenlib/BLI_vector_set_test.cc60
-rw-r--r--tests/gtests/blenlib/BLI_vector_test.cc122
-rw-r--r--tests/gtests/blenlib/CMakeLists.txt2
-rw-r--r--tests/gtests/functions/CMakeLists.txt6
-rw-r--r--tests/gtests/functions/FN_array_spans_test.cc149
-rw-r--r--tests/gtests/functions/FN_attributes_ref_test.cc114
-rw-r--r--tests/gtests/functions/FN_cpp_type_test.cc11
-rw-r--r--tests/gtests/functions/FN_generic_vector_array_test.cc118
-rw-r--r--tests/gtests/functions/FN_multi_function_network_test.cc270
-rw-r--r--tests/gtests/functions/FN_multi_function_test.cc334
-rw-r--r--tests/gtests/functions/FN_spans_test.cc231
-rw-r--r--tests/gtests/usd/CMakeLists.txt2
-rw-r--r--tests/gtests/usd/abstract_hierarchy_iterator_test.cc146
-rw-r--r--tests/gtests/usd/hierarchy_context_order_test.cc4
-rw-r--r--tests/python/alembic_tests.py42
-rw-r--r--tests/python/bl_pyapi_idprop_datablock.py6
1537 files changed, 70061 insertions, 59305 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6b80db402ef..60c6f638370 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -321,14 +321,6 @@ mark_as_advanced(WITH_SYSTEM_GLOG)
# Freestyle
option(WITH_FREESTYLE "Enable Freestyle (advanced edges rendering)" ON)
-# New object types
-option(WITH_NEW_OBJECT_TYPES "Enable new hair and pointcloud objects (use for development only, don't save in files)" OFF)
-mark_as_advanced(WITH_NEW_OBJECT_TYPES)
-
-# New simulation data block
-option(WITH_NEW_SIMULATION_TYPE "Enable simulation data block (use for development only, don't save in files)" OFF)
-mark_as_advanced(WITH_NEW_SIMULATION_TYPE)
-
# Misc
if(WIN32)
option(WITH_INPUT_IME "Enable Input Method Editor (IME) for complex Asian character input" ON)
@@ -370,7 +362,7 @@ option(WITH_CYCLES "Enable Cycles Render Engine" ON)
option(WITH_CYCLES_STANDALONE "Build Cycles standalone application" OFF)
option(WITH_CYCLES_STANDALONE_GUI "Build Cycles standalone with GUI" OFF)
option(WITH_CYCLES_OSL "Build Cycles with OSL support" ON)
-option(WITH_CYCLES_EMBREE "Build Cycles with Embree support" OFF)
+option(WITH_CYCLES_EMBREE "Build Cycles with Embree support" ON)
option(WITH_CYCLES_CUDA_BINARIES "Build Cycles CUDA binaries" OFF)
option(WITH_CYCLES_CUBIN_COMPILER "Build cubins with nvrtc based compiler instead of nvcc" OFF)
option(WITH_CYCLES_CUDA_BUILD_SERIAL "Build cubins one after another (useful on machines with limited RAM)" OFF)
@@ -654,6 +646,7 @@ if(WITH_BOOST AND NOT (WITH_CYCLES OR WITH_OPENIMAGEIO OR WITH_INTERNATIONAL OR
set(WITH_BOOST OFF)
endif()
+set_and_warn_dependency(WITH_TBB WITH_CYCLES OFF)
set_and_warn_dependency(WITH_TBB WITH_USD OFF)
set_and_warn_dependency(WITH_TBB WITH_OPENIMAGEDENOISE OFF)
set_and_warn_dependency(WITH_TBB WITH_OPENVDB OFF)
@@ -1245,7 +1238,7 @@ endif()
if(WITH_LIBMV)
# We always have C++11 which includes unordered_map.
- set(CERES_DEFINES -DCERES_STD_UNORDERED_MAP)
+ set(CERES_DEFINES "-DCERES_STD_UNORDERED_MAP;-DCERES_USE_CXX_THREADS")
endif()
#-----------------------------------------------------------------------------
@@ -1561,20 +1554,15 @@ if(WITH_PYTHON)
endif()
if(MSVC)
- # MSVC needs to be tested first, since clang on windows will
- # match the compiler test below but clang-cl does not accept -std=c++11
- # since it is on by default and cannot be turned off.
- #
- # Nothing special is needed, C++11 features are available by default.
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++17")
elseif(
CMAKE_COMPILER_IS_GNUCC OR
CMAKE_C_COMPILER_ID MATCHES "Clang" OR
CMAKE_C_COMPILER_ID MATCHES "Intel"
)
- # TODO(sergey): Do we want c++11 or gnu-c++11 here?
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17")
else()
- message(FATAL_ERROR "Unknown compiler ${CMAKE_C_COMPILER_ID}, can't enable C++11 build")
+ message(FATAL_ERROR "Unknown compiler ${CMAKE_C_COMPILER_ID}, can't enable C++17 build")
endif()
# Visual Studio has all standards it supports available by default
diff --git a/GNUmakefile b/GNUmakefile
index 93e2164dee8..9a5164cd722 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -142,6 +142,10 @@ Information
endef
# HELP_TEXT (end)
+# This makefile is not meant for Windows
+ifeq ($(OS),Windows_NT)
+ $(error On Windows, use "cmd //c make.bat" instead of "make")
+endif
# System Vars
OS:=$(shell uname -s)
diff --git a/build_files/build_environment/CMakeLists.txt b/build_files/build_environment/CMakeLists.txt
index dd90eb9adde..3c228fd9f7d 100644
--- a/build_files/build_environment/CMakeLists.txt
+++ b/build_files/build_environment/CMakeLists.txt
@@ -48,11 +48,7 @@ include(cmake/options.cmake)
include(cmake/versions.cmake)
if(ENABLE_MINGW64)
- if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
- include(cmake/setup_mingw64.cmake)
- else()
- include(cmake/setup_mingw32.cmake)
- endif()
+ include(cmake/setup_mingw64.cmake)
else()
set(mingw_LIBDIR ${LIBDIR})
endif()
@@ -97,17 +93,18 @@ if(UNIX)
else()
include(cmake/pugixml.cmake)
endif()
+include(cmake/ispc.cmake)
include(cmake/openimagedenoise.cmake)
include(cmake/embree.cmake)
-include(cmake/xr_openxr.cmake)
+if(NOT APPLE)
+ include(cmake/xr_openxr.cmake)
+endif()
if(WITH_WEBP)
include(cmake/webp.cmake)
endif()
if(WIN32)
- # HMD branch deps
- include(cmake/hidapi.cmake)
# OCIO deps
include(cmake/tinyxml.cmake)
include(cmake/yamlcpp.cmake)
diff --git a/build_files/build_environment/cmake/harvest.cmake b/build_files/build_environment/cmake/harvest.cmake
index a7685f87c8d..33b7f9db192 100644
--- a/build_files/build_environment/cmake/harvest.cmake
+++ b/build_files/build_environment/cmake/harvest.cmake
@@ -43,8 +43,6 @@ if(BUILD_MODE STREQUAL Release)
# tiff
${CMAKE_COMMAND} -E copy ${LIBDIR}/tiff/lib/tiff.lib ${HARVEST_TARGET}/tiff/lib/libtiff.lib &&
${CMAKE_COMMAND} -E copy_directory ${LIBDIR}/tiff/include/ ${HARVEST_TARGET}/tiff/include/ &&
- # hidapi
- ${CMAKE_COMMAND} -E copy_directory ${LIBDIR}/hidapi/ ${HARVEST_TARGET}/hidapi/
DEPENDS
)
endif()
diff --git a/build_files/build_environment/cmake/ispc.cmake b/build_files/build_environment/cmake/ispc.cmake
new file mode 100644
index 00000000000..0bb5db82aea
--- /dev/null
+++ b/build_files/build_environment/cmake/ispc.cmake
@@ -0,0 +1,63 @@
+# ***** 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.
+#
+# ***** END GPL LICENSE BLOCK *****
+
+if(WIN32)
+ set(ISPC_EXTRA_ARGS_WIN
+ -DFLEX_EXECUTABLE=${LIBDIR}/flexbison/win_flex.exe
+ -DBISON_EXECUTABLE=${LIBDIR}/flexbison/win_bison.exe
+ -DM4_EXECUTABLE=${DOWNLOAD_DIR}/mingw/mingw64/msys/1.0/bin/m4.exe
+ )
+endif()
+
+set(ISPC_EXTRA_ARGS
+ -DARM_ENABLED=Off
+ -DISPC_NO_DUMPS=On
+ -DISPC_INCLUDE_EXAMPLES=Off
+ -DISPC_INCLUDE_TESTS=Off
+ -DLLVM_ROOT=${LIBDIR}/llvm/lib/cmake/llvm
+ -DLLVM_LIBRARY_DIR=${LIBDIR}/llvm/lib
+ -DCLANG_EXECUTABLE=${LIBDIR}/clang/bin/clang
+ -DISPC_INCLUDE_TESTS=Off
+ -DCLANG_LIBRARY_DIR=${LIBDIR}/clang/lib
+ -DCLANG_INCLUDE_DIRS=${LIBDIR}/clang/include
+ ${ISPC_EXTRA_ARGS_WIN}
+)
+
+ExternalProject_Add(external_ispc
+ URL ${ISPC_URI}
+ DOWNLOAD_DIR ${DOWNLOAD_DIR}
+ URL_HASH MD5=${ISPC_HASH}
+ PREFIX ${BUILD_DIR}/ispc
+ PATCH_COMMAND ${PATCH_CMD} -p 1 -d ${BUILD_DIR}/ispc/src/external_ispc < ${PATCH_DIR}/ispc.diff
+ CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${LIBDIR}/ispc -Wno-dev ${DEFAULT_CMAKE_FLAGS} ${ISPC_EXTRA_ARGS} ${BUILD_DIR}/ispc/src/external_ispc
+ INSTALL_DIR ${LIBDIR}/ispc
+)
+
+add_dependencies(
+ external_ispc
+ ll
+ external_clang
+)
+
+if(WIN32)
+ add_dependencies(
+ external_ispc
+ external_flexbison
+ )
+endif()
+
diff --git a/build_files/build_environment/cmake/openimagedenoise.cmake b/build_files/build_environment/cmake/openimagedenoise.cmake
index 1332a38fea6..65f12199952 100644
--- a/build_files/build_environment/cmake/openimagedenoise.cmake
+++ b/build_files/build_environment/cmake/openimagedenoise.cmake
@@ -18,26 +18,41 @@
set(OIDN_EXTRA_ARGS
- -DWITH_EXAMPLE=OFF
- -DWITH_TEST=OFF
+ -DOIDN_APPS=OFF
-DTBB_ROOT=${LIBDIR}/tbb
-DTBB_STATIC_LIB=${TBB_STATIC_LIBRARY}
-DOIDN_STATIC_LIB=ON
+ -DOIDN_STATIC_RUNTIME=OFF
+ -DISPC_EXECUTABLE=${LIBDIR}/ispc/bin/ispc
)
+if(WIN32)
+ set(OIDN_EXTRA_ARGS
+ ${OIDN_EXTRA_ARGS}
+ -DTBB_DEBUG_LIBRARY=${LIBDIR}/tbb/lib/tbb.lib
+ -DTBB_DEBUG_LIBRARY_MALLOC=${LIBDIR}/tbb/lib/tbbmalloc.lib
+ )
+else()
+ set(OIDN_EXTRA_ARGS
+ ${OIDN_EXTRA_ARGS}
+ -Dtbb_LIBRARY_RELEASE=${LIBDIR}/tbb/lib/tbb_static.a
+ -Dtbbmalloc_LIBRARY_RELEASE=${LIBDIR}/tbb/lib/tbbmalloc_static.a
+ )
+endif()
+
ExternalProject_Add(external_openimagedenoise
URL ${OIDN_URI}
DOWNLOAD_DIR ${DOWNLOAD_DIR}
URL_HASH MD5=${OIDN_HASH}
PREFIX ${BUILD_DIR}/openimagedenoise
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${LIBDIR}/openimagedenoise ${DEFAULT_CMAKE_FLAGS} ${OIDN_EXTRA_ARGS}
- PATCH_COMMAND ${PATCH_CMD} --verbose -p 1 -N -d ${BUILD_DIR}/openimagedenoise/src/external_openimagedenoise < ${PATCH_DIR}/openimagedenoise.diff
INSTALL_DIR ${LIBDIR}/openimagedenoise
)
add_dependencies(
external_openimagedenoise
external_tbb
+ external_ispc
)
if(WIN32)
@@ -46,7 +61,7 @@ if(WIN32)
COMMAND ${CMAKE_COMMAND} -E copy_directory ${LIBDIR}/openimagedenoise/include ${HARVEST_TARGET}/openimagedenoise/include
COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/openimagedenoise/lib/openimagedenoise.lib ${HARVEST_TARGET}/openimagedenoise/lib/openimagedenoise.lib
COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/openimagedenoise/lib/common.lib ${HARVEST_TARGET}/openimagedenoise/lib/common.lib
- COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/openimagedenoise/lib/mkldnn.lib ${HARVEST_TARGET}/openimagedenoise/lib/mkldnn.lib
+ COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/openimagedenoise/lib/dnnl.lib ${HARVEST_TARGET}/openimagedenoise/lib/dnnl.lib
DEPENDEES install
)
endif()
@@ -54,7 +69,7 @@ if(WIN32)
ExternalProject_Add_Step(external_openimagedenoise after_install
COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/openimagedenoise/lib/openimagedenoise.lib ${HARVEST_TARGET}/openimagedenoise/lib/openimagedenoise_d.lib
COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/openimagedenoise/lib/common.lib ${HARVEST_TARGET}/openimagedenoise/lib/common_d.lib
- COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/openimagedenoise/lib/mkldnn.lib ${HARVEST_TARGET}/openimagedenoise/lib/mkldnn_d.lib
+ COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/openimagedenoise/lib/dnnl.lib ${HARVEST_TARGET}/openimagedenoise/lib/dnnl_d.lib
DEPENDEES install
)
endif()
diff --git a/build_files/build_environment/cmake/options.cmake b/build_files/build_environment/cmake/options.cmake
index ed82f1fd9ee..4b973067020 100644
--- a/build_files/build_environment/cmake/options.cmake
+++ b/build_files/build_environment/cmake/options.cmake
@@ -17,7 +17,7 @@
# ***** END GPL LICENSE BLOCK *****
if(WIN32)
- option(ENABLE_MINGW64 "Enable building of ffmpeg/iconv/libsndfile/lapack/fftw3 by installing mingw64" ON)
+ option(ENABLE_MINGW64 "Enable building of ffmpeg/iconv/libsndfile/fftw3 by installing mingw64" ON)
endif()
option(WITH_WEBP "Enable building of oiio with webp support" OFF)
set(MAKE_THREADS 1 CACHE STRING "Number of threads to run make with")
@@ -45,11 +45,7 @@ message("PATCH_DIR = ${PATCH_DIR}")
message("BUILD_DIR = ${BUILD_DIR}")
if(WIN32)
- if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
- set(PATCH_CMD ${DOWNLOAD_DIR}/mingw/mingw64/msys/1.0/bin/patch.exe)
- else()
- set(PATCH_CMD ${DOWNLOAD_DIR}/mingw/mingw32/msys/1.0/bin/patch.exe)
- endif()
+ set(PATCH_CMD ${DOWNLOAD_DIR}/mingw/mingw64/msys/1.0/bin/patch.exe)
set(LIBEXT ".lib")
set(LIBPREFIX "")
@@ -82,17 +78,10 @@ if(WIN32)
set(PLATFORM_CXX_FLAGS)
set(PLATFORM_CMAKE_FLAGS)
- if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
- set(MINGW_PATH ${DOWNLOAD_DIR}/mingw/mingw64)
- set(MINGW_SHELL ming64sh.cmd)
- set(PERL_SHELL ${DOWNLOAD_DIR}/perl/portableshell.bat)
- set(MINGW_HOST x86_64-w64-mingw32)
- else()
- set(MINGW_PATH ${DOWNLOAD_DIR}/mingw/mingw32)
- set(MINGW_SHELL ming32sh.cmd)
- set(PERL_SHELL ${DOWNLOAD_DIR}/perl32/portableshell.bat)
- set(MINGW_HOST i686-w64-mingw32)
- endif()
+ set(MINGW_PATH ${DOWNLOAD_DIR}/mingw/mingw64)
+ set(MINGW_SHELL ming64sh.cmd)
+ set(PERL_SHELL ${DOWNLOAD_DIR}/perl/portableshell.bat)
+ set(MINGW_HOST x86_64-w64-mingw32)
set(CONFIGURE_ENV
cd ${MINGW_PATH} &&
diff --git a/build_files/build_environment/cmake/osl.cmake b/build_files/build_environment/cmake/osl.cmake
index 3532eec144e..db70b2937b8 100644
--- a/build_files/build_environment/cmake/osl.cmake
+++ b/build_files/build_environment/cmake/osl.cmake
@@ -75,6 +75,7 @@ set(OSL_EXTRA_ARGS
-DUSE_LLVM_BITCODE=OFF
-DUSE_PARTIO=OFF
-DUSE_QT=OFF
+ -DINSTALL_DOCS=OFF
${OSL_SIMD_FLAGS}
-DPARTIO_LIBRARIES=
)
diff --git a/build_files/build_environment/cmake/setup_mingw32.cmake b/build_files/build_environment/cmake/setup_mingw32.cmake
deleted file mode 100644
index c9fbe301ac7..00000000000
--- a/build_files/build_environment/cmake/setup_mingw32.cmake
+++ /dev/null
@@ -1,227 +0,0 @@
-# ***** 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.
-#
-# ***** END GPL LICENSE BLOCK *****
-
-####################################################################################################################
-# Mingw32 Builds
-####################################################################################################################
-# This installs mingw32+msys to compile ffmpeg/iconv/libsndfile/lapack/fftw3
-####################################################################################################################
-
-message("LIBDIR = ${LIBDIR}")
-macro(cmake_to_msys_path MsysPath ResultingPath)
- string(REPLACE ":" "" TmpPath "${MsysPath}")
- string(SUBSTRING ${TmpPath} 0 1 Drive)
- string(SUBSTRING ${TmpPath} 1 255 PathPart)
- string(TOLOWER ${Drive} LowerDrive)
- string(CONCAT ${ResultingPath} "/" ${LowerDrive} ${PathPart})
-endmacro()
-cmake_to_msys_path(${LIBDIR} mingw_LIBDIR)
-message("mingw_LIBDIR = ${mingw_LIBDIR}")
-
-message("Checking for mingw32")
-# download mingw32
-if(NOT EXISTS "${DOWNLOAD_DIR}/i686-4.9.4-release-win32-sjlj-rt_v5-rev0.7z")
- message("Downloading mingw32")
- file(DOWNLOAD "https://astuteinternet.dl.sourceforge.net/project/mingw-w64/Toolchains%20targetting%20Win32/Personal%20Builds/mingw-builds/4.9.4/threads-win32/sjlj/i686-4.9.4-release-win32-sjlj-rt_v5-rev0.7z" "${DOWNLOAD_DIR}/i686-4.9.4-release-win32-sjlj-rt_v5-rev0.7z")
-endif()
-
-# make mingw root directory
-if(NOT EXISTS "${DOWNLOAD_DIR}/mingw")
- execute_process(
- COMMAND ${CMAKE_COMMAND} -E make_directory ${DOWNLOAD_DIR}/mingw
- WORKING_DIRECTORY ${DOWNLOAD_DIR}
- )
-endif()
-
-# extract mingw32
-if((NOT EXISTS "${DOWNLOAD_DIR}/mingw/mingw32/ming32sh.cmd") AND (EXISTS "${DOWNLOAD_DIR}/i686-4.9.4-release-win32-sjlj-rt_v5-rev0.7z"))
- message("Extracting mingw32")
- execute_process(
- COMMAND ${CMAKE_COMMAND} -E tar jxf ${DOWNLOAD_DIR}/i686-4.9.4-release-win32-sjlj-rt_v5-rev0.7z
- WORKING_DIRECTORY ${DOWNLOAD_DIR}/mingw
- )
-endif()
-
-message("Checking for pkg-config")
-if(NOT EXISTS "${DOWNLOAD_DIR}/pkg-config-lite-0.28-1_bin-win32.zip")
- message("Downloading pkg-config")
- file(DOWNLOAD "https://nchc.dl.sourceforge.net/project/pkgconfiglite/0.28-1/pkg-config-lite-0.28-1_bin-win32.zip" "${DOWNLOAD_DIR}/pkg-config-lite-0.28-1_bin-win32.zip")
-endif()
-
-# extract pkgconfig
-if((NOT EXISTS "${DOWNLOAD_DIR}/mingw/mingw32/bin/pkg-config.exe") AND (EXISTS "${DOWNLOAD_DIR}/pkg-config-lite-0.28-1_bin-win32.zip"))
- message("Extracting pkg-config")
- execute_process(
- COMMAND ${CMAKE_COMMAND} -E tar jxf "${DOWNLOAD_DIR}/pkg-config-lite-0.28-1_bin-win32.zip"
- WORKING_DIRECTORY ${DOWNLOAD_DIR}/
- )
-
- execute_process(
- COMMAND ${CMAKE_COMMAND} -E copy "${DOWNLOAD_DIR}/pkg-config-lite-0.28-1/bin/pkg-config.exe" "${DOWNLOAD_DIR}/mingw/mingw32/bin/pkg-config.exe"
- )
-
-endif()
-
-message("Checking for nasm")
-if(NOT EXISTS "${DOWNLOAD_DIR}/nasm-2.13.02-win32.zip")
- message("Downloading nasm")
- file(DOWNLOAD "http://www.nasm.us/pub/nasm/releasebuilds/2.13.02/win32/nasm-2.13.02-win32.zip" "${DOWNLOAD_DIR}/nasm-2.13.02-win32.zip")
-endif()
-
-# extract nasm
-if((NOT EXISTS "${DOWNLOAD_DIR}/mingw/mingw32/bin/nasm.exe") AND (EXISTS "${DOWNLOAD_DIR}/nasm-2.13.02-win32.zip"))
- message("Extracting nasm")
- execute_process(
- COMMAND ${CMAKE_COMMAND} -E tar jxf "${DOWNLOAD_DIR}/nasm-2.13.02-win32.zip"
- WORKING_DIRECTORY ${DOWNLOAD_DIR}/
- )
- execute_process(
- COMMAND ${CMAKE_COMMAND} -E copy "${DOWNLOAD_DIR}/nasm-2.13.02/nasm.exe" "${DOWNLOAD_DIR}/mingw/mingw32/bin/nasm.exe"
- )
-
-endif()
-SET(NASM_PATH ${DOWNLOAD_DIR}/mingw/mingw32/bin/nasm.exe)
-message("Checking for mingwGet")
-if(NOT EXISTS "${DOWNLOAD_DIR}/mingw-get-0.6.2-mingw32-beta-20131004-1-bin.zip")
- message("Downloading mingw-get")
- file(DOWNLOAD "https://nchc.dl.sourceforge.net/project/mingw/Installer/mingw-get/mingw-get-0.6.2-beta-20131004-1/mingw-get-0.6.2-mingw32-beta-20131004-1-bin.zip" "${DOWNLOAD_DIR}/mingw-get-0.6.2-mingw32-beta-20131004-1-bin.zip")
-endif()
-
-# extract mingw_get
-if((NOT EXISTS "${DOWNLOAD_DIR}/mingw/mingw32/bin/mingw-get.exe") AND (EXISTS "${DOWNLOAD_DIR}/mingw-get-0.6.2-mingw32-beta-20131004-1-bin.zip"))
- message("Extracting mingw-get")
- execute_process(
- COMMAND ${CMAKE_COMMAND} -E tar jxf "${DOWNLOAD_DIR}/mingw-get-0.6.2-mingw32-beta-20131004-1-bin.zip"
- WORKING_DIRECTORY ${DOWNLOAD_DIR}/mingw/mingw32/
- )
-endif()
-
-if((EXISTS "${DOWNLOAD_DIR}/mingw/mingw32/bin/mingw-get.exe") AND (NOT EXISTS "${DOWNLOAD_DIR}/mingw/mingw32/msys/1.0/bin/make.exe"))
- message("Installing MSYS")
- execute_process(
- COMMAND ${DOWNLOAD_DIR}/mingw/mingw32/bin/mingw-get install msys msys-patch
- WORKING_DIRECTORY ${DOWNLOAD_DIR}/mingw/mingw32/bin/
- )
-endif()
-
-if((EXISTS "${DOWNLOAD_DIR}/mingw/mingw32/bin/mingw-get.exe") AND (NOT EXISTS "${DOWNLOAD_DIR}/mingw/mingw32/msys/1.0/bin/mktemp.exe"))
- message("Installing mktemp")
- execute_process(
- COMMAND ${DOWNLOAD_DIR}/mingw/mingw32/bin/mingw-get install msys msys-mktemp
- WORKING_DIRECTORY ${DOWNLOAD_DIR}/mingw/mingw32/bin/
- )
-endif()
-
-
-message("Checking for CoreUtils")
-# download old core_utils for pr.exe (ffmpeg needs it to build)
-if(NOT EXISTS "${DOWNLOAD_DIR}/coreutils-5.97-MSYS-1.0.11-snapshot.tar.bz2")
- message("Downloading CoreUtils 5.97")
- file(DOWNLOAD "https://nchc.dl.sourceforge.net/project/mingw/MSYS/Base/msys-core/_obsolete/coreutils-5.97-MSYS-1.0.11-2/coreutils-5.97-MSYS-1.0.11-snapshot.tar.bz2" "${DOWNLOAD_DIR}/coreutils-5.97-MSYS-1.0.11-snapshot.tar.bz2")
-endif()
-
-if((EXISTS "${DOWNLOAD_DIR}/coreutils-5.97-MSYS-1.0.11-snapshot.tar.bz2") AND (NOT EXISTS "${DOWNLOAD_DIR}/mingw/mingw32/msys/1.0/bin/pr.exe"))
- message("Installing pr from CoreUtils 5.97")
- execute_process(
- COMMAND ${CMAKE_COMMAND} -E make_directory ${DOWNLOAD_DIR}/tmp_coreutils
- WORKING_DIRECTORY ${DOWNLOAD_DIR}
- )
-
- execute_process(
- COMMAND ${CMAKE_COMMAND} -E tar jxf ${DOWNLOAD_DIR}/coreutils-5.97-MSYS-1.0.11-snapshot.tar.bz2
- WORKING_DIRECTORY ${DOWNLOAD_DIR}/tmp_coreutils/
- )
-
- execute_process(
- COMMAND ${CMAKE_COMMAND} -E copy ${DOWNLOAD_DIR}/tmp_coreutils/coreutils-5.97/bin/pr.exe "${DOWNLOAD_DIR}/mingw/mingw32/msys/1.0/bin/pr.exe"
- WORKING_DIRECTORY ${DOWNLOAD_DIR}/tmp_coreutils/
- )
-endif()
-
-if(NOT EXISTS "${DOWNLOAD_DIR}/mingw/mingw32/ming32sh.cmd")
- message("Installing ming32sh.cmd")
- execute_process(
- COMMAND ${CMAKE_COMMAND} -E copy ${PATCH_DIR}/ming32sh.cmd ${DOWNLOAD_DIR}/mingw/mingw32/ming32sh.cmd
- )
-endif()
-
-message("Checking for perl")
-# download perl for libvpx
-if(NOT EXISTS "${DOWNLOAD_DIR}/strawberry-perl-5.22.1.3-32bit-portable.zip")
- message("Downloading perl")
- file(DOWNLOAD "http://strawberryperl.com/download/5.22.1.3/strawberry-perl-5.22.1.3-32bit-portable.zip" "${DOWNLOAD_DIR}/strawberry-perl-5.22.1.3-32bit-portable.zip")
-endif()
-
-# make perl root directory
-if(NOT EXISTS "${DOWNLOAD_DIR}/perl32")
- execute_process(
- COMMAND ${CMAKE_COMMAND} -E make_directory ${DOWNLOAD_DIR}/perl32
- WORKING_DIRECTORY ${DOWNLOAD_DIR}
- )
-endif()
-
-# extract perl
-if((NOT EXISTS "${DOWNLOAD_DIR}/perl32/portable.perl") AND (EXISTS "${DOWNLOAD_DIR}/strawberry-perl-5.22.1.3-32bit-portable.zip"))
- message("Extracting perl")
- execute_process(
- COMMAND ${CMAKE_COMMAND} -E tar jxf ${DOWNLOAD_DIR}/strawberry-perl-5.22.1.3-32bit-portable.zip
- WORKING_DIRECTORY ${DOWNLOAD_DIR}/perl32
- )
-endif()
-
-# get yasm for vpx
-if(NOT EXISTS "${DOWNLOAD_DIR}/mingw/mingw32/bin/yasm.exe")
- message("Downloading yasm")
- file(DOWNLOAD "http://www.tortall.net/projects/yasm/releases/yasm-1.3.0-win32.exe" "${DOWNLOAD_DIR}/mingw/mingw32/bin/yasm.exe")
-endif()
-
-message("checking i686-w64-mingw32-strings")
-# copy strings.exe to i686-w64-mingw32-strings for x264
-if(NOT EXISTS "${DOWNLOAD_DIR}/mingw/mingw32/bin/i686-w64-mingw32-strings.exe")
- message("fixing i686-w64-mingw32-strings.exe")
- execute_process(
- COMMAND ${CMAKE_COMMAND} -E copy "${DOWNLOAD_DIR}/mingw/mingw32/bin/strings.exe" "${DOWNLOAD_DIR}/mingw/mingw32/bin/i686-w64-mingw32-strings.exe"
- )
-endif()
-
-message("checking i686-w64-mingw32-ar.exe")
-# copy ar.exe to i686-w64-mingw32-ar.exe for x264
-if(NOT EXISTS "${DOWNLOAD_DIR}/mingw/mingw32/bin/i686-w64-mingw32-ar.exe")
- message("fixing i686-w64-mingw32-ar.exe")
- execute_process(
- COMMAND ${CMAKE_COMMAND} -E copy "${DOWNLOAD_DIR}/mingw/mingw32/bin/ar.exe" "${DOWNLOAD_DIR}/mingw/mingw32/bin/i686-w64-mingw32-ar.exe"
- )
-endif()
-
-message("checking i686-w64-mingw32-strip.exe")
-# copy strip.exe to i686-w64-mingw32-strip.exe for x264
-if(NOT EXISTS "${DOWNLOAD_DIR}/mingw/mingw32/bin/i686-w64-mingw32-strip.exe")
- message("fixing i686-w64-mingw32-strip.exe")
- execute_process(
- COMMAND ${CMAKE_COMMAND} -E copy "${DOWNLOAD_DIR}/mingw/mingw32/bin/strip.exe" "${DOWNLOAD_DIR}/mingw/mingw32/bin/i686-w64-mingw32-strip.exe"
- )
-endif()
-
-message("checking i686-w64-mingw32-ranlib.exe")
-# copy ranlib.exe to i686-w64-mingw32-ranlib.exe for x264
-if(NOT EXISTS "${DOWNLOAD_DIR}/mingw/mingw32/bin/i686-w64-mingw32-ranlib.exe")
- message("fixing i686-w64-mingw32-ranlib.exe")
- execute_process(
- COMMAND ${CMAKE_COMMAND} -E copy "${DOWNLOAD_DIR}/mingw/mingw32/bin/ranlib.exe" "${DOWNLOAD_DIR}/mingw/mingw32/bin/i686-w64-mingw32-ranlib.exe"
- )
-endif()
diff --git a/build_files/build_environment/cmake/setup_mingw64.cmake b/build_files/build_environment/cmake/setup_mingw64.cmake
index 5ac9722d23d..e370da049d5 100644
--- a/build_files/build_environment/cmake/setup_mingw64.cmake
+++ b/build_files/build_environment/cmake/setup_mingw64.cmake
@@ -19,7 +19,7 @@
####################################################################################################################
# Mingw64 Builds
####################################################################################################################
-# This installs mingw64+msys to compile ffmpeg/iconv/libsndfile/lapack/fftw3
+# This installs mingw64+msys to compile ffmpeg/iconv/libsndfile/fftw3
####################################################################################################################
message("LIBDIR = ${LIBDIR}")
@@ -128,6 +128,14 @@ if((EXISTS "${DOWNLOAD_DIR}/mingw/mingw64/bin/mingw-get.exe") AND (NOT EXISTS "$
)
endif()
+if((EXISTS "${DOWNLOAD_DIR}/mingw/mingw64/bin/mingw-get.exe") AND (NOT EXISTS "${DOWNLOAD_DIR}/mingw/mingw64/msys/1.0/bin/m4.exe"))
+ message("Installing m4")
+ execute_process(
+ COMMAND ${DOWNLOAD_DIR}/mingw/mingw64/bin/mingw-get install msys msys-m4
+ WORKING_DIRECTORY ${DOWNLOAD_DIR}/mingw/mingw64/bin/
+ )
+endif()
+
message("Checking for CoreUtils")
# download old core_utils for pr.exe (ffmpeg needs it to build)
if(NOT EXISTS "${DOWNLOAD_DIR}/coreutils-5.97-MSYS-1.0.11-snapshot.tar.bz2")
diff --git a/build_files/build_environment/cmake/ssl.conf b/build_files/build_environment/cmake/ssl.conf
index 91f4357f8d8..818e5a625ab 100644
--- a/build_files/build_environment/cmake/ssl.conf
+++ b/build_files/build_environment/cmake/ssl.conf
@@ -1,4 +1,4 @@
-%targets = (
+my %targets = (
"blender-linux-x86" => {
inherit_from => [ "linux-x86" ],
diff --git a/build_files/build_environment/cmake/tbb.cmake b/build_files/build_environment/cmake/tbb.cmake
index 1cb852fb5d1..82cd9291ddf 100644
--- a/build_files/build_environment/cmake/tbb.cmake
+++ b/build_files/build_environment/cmake/tbb.cmake
@@ -50,6 +50,13 @@ ExternalProject_Add(external_tbb
if(WIN32)
if(BUILD_MODE STREQUAL Release)
ExternalProject_Add_Step(external_tbb after_install
+ # findtbb.cmake in some deps *NEEDS* to find tbb_debug.lib even if they are not going to use it
+ # to make that test pass, we place a copy with the right name in the lib folder.
+ COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbb.lib ${HARVEST_TARGET}/tbb/lib/tbb_debug.lib
+ COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbbmalloc.lib ${HARVEST_TARGET}/tbb/lib/tbbmalloc_debug.lib
+ COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbb.dll ${HARVEST_TARGET}/tbb/lib/tbb_debug.dll
+ COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbbmalloc.dll ${HARVEST_TARGET}/tbb/lib/tbbmalloc_debug.dll
+ # Normal collection of build artifacts
COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbb.lib ${HARVEST_TARGET}/tbb/lib/tbb.lib
COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbb.dll ${HARVEST_TARGET}/tbb/lib/tbb.dll
COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbbmalloc.lib ${HARVEST_TARGET}/tbb/lib/tbbmalloc.lib
diff --git a/build_files/build_environment/cmake/usd.cmake b/build_files/build_environment/cmake/usd.cmake
index c98a9cae959..5fa1872cabc 100644
--- a/build_files/build_environment/cmake/usd.cmake
+++ b/build_files/build_environment/cmake/usd.cmake
@@ -27,6 +27,9 @@ set(USD_EXTRA_ARGS
-DTBB_INCLUDE_DIRS=${LIBDIR}/tbb/include
-DTBB_LIBRARIES=${LIBDIR}/tbb/lib/${LIBPREFIX}${TBB_LIBRARY}${LIBEXT}
-DTbb_TBB_LIBRARY=${LIBDIR}/tbb/lib/${LIBPREFIX}${TBB_LIBRARY}${LIBEXT}
+ # USD wants the tbb debug lib set even when you are doing a release build
+ # Otherwise it will error out during the cmake configure phase.
+ -DTBB_LIBRARIES_DEBUG=${LIBDIR}/tbb/lib/${LIBPREFIX}${TBB_LIBRARY}${LIBEXT}
# This is a preventative measure that avoids possible conflicts when add-ons
# try to load another USD library into the same process space.
diff --git a/build_files/build_environment/cmake/versions.cmake b/build_files/build_environment/cmake/versions.cmake
index 4882ca2a724..5ec5553079c 100644
--- a/build_files/build_environment/cmake/versions.cmake
+++ b/build_files/build_environment/cmake/versions.cmake
@@ -212,10 +212,6 @@ set(ICONV_VERSION 1.16)
set(ICONV_URI http://ftp.gnu.org/pub/gnu/libiconv/libiconv-${ICONV_VERSION}.tar.gz)
set(ICONV_HASH 7d2a800b952942bb2880efb00cfd524c)
-set(LAPACK_VERSION 3.6.0)
-set(LAPACK_URI http://www.netlib.org/lapack/lapack-${LAPACK_VERSION}.tgz)
-set(LAPACK_HASH f2f6c67134e851fe189bb3ca1fbb5101)
-
set(SNDFILE_VERSION 1.0.28)
set(SNDFILE_URI http://www.mega-nerd.com/libsndfile/files/libsndfile-${SNDFILE_VERSION}.tar.gz)
set(SNDFILE_HASH 646b5f98ce89ac60cdb060fcd398247c)
@@ -236,13 +232,13 @@ set(SPNAV_VERSION 0.2.3)
set(SPNAV_URI http://downloads.sourceforge.net/project/spacenav/spacenav%20library%20%28SDK%29/libspnav%20${SPNAV_VERSION}/libspnav-${SPNAV_VERSION}.tar.gz)
set(SPNAV_HASH 44d840540d53326d4a119c0f1aa7bf0a)
-set(JEMALLOC_VERSION 5.0.1)
+set(JEMALLOC_VERSION 5.2.1)
set(JEMALLOC_URI https://github.com/jemalloc/jemalloc/releases/download/${JEMALLOC_VERSION}/jemalloc-${JEMALLOC_VERSION}.tar.bz2)
-set(JEMALLOC_HASH 507f7b6b882d868730d644510491d18f)
+set(JEMALLOC_HASH 3d41fbf006e6ebffd489bdb304d009ae)
-set(XML2_VERSION 2.9.4)
+set(XML2_VERSION 2.9.10)
set(XML2_URI http://xmlsoft.org/sources/libxml2-${XML2_VERSION}.tar.gz)
-set(XML2_HASH ae249165c173b1ff386ee8ad676815f5)
+set(XML2_HASH 10942a1dc23137a8aa07f0639cbfece5)
set(TINYXML_VERSION 2_6_2)
set(TINYXML_VERSION_DOTS 2.6.2)
@@ -269,23 +265,23 @@ set(FLEXBISON_HASH d87a3938194520d904013abef3df10ce)
# NOTE: bzip.org domain does no longer belong to BZip 2 project, so we download
# sources from Debian packaging.
-set(BZIP2_VERSION 1.0.6)
-set(BZIP2_URI http://http.debian.net/debian/pool/main/b/bzip2/bzip2_${BZIP2_VERSION}.orig.tar.bz2)
-set(BZIP2_HASH d70a9ccd8bdf47e302d96c69fecd54925f45d9c7b966bb4ef5f56b770960afa7)
+set(BZIP2_VERSION 1.0.8)
+set(BZIP2_URI http://http.debian.net/debian/pool/main/b/bzip2/bzip2_${BZIP2_VERSION}.orig.tar.gz)
+set(BZIP2_HASH ab5a03176ee106d3f0fa90e381da478ddae405918153cca248e682cd0c4a2269)
-set(FFI_VERSION 3.2.1)
+set(FFI_VERSION 3.3)
set(FFI_URI https://sourceware.org/pub/libffi/libffi-${FFI_VERSION}.tar.gz)
-set(FFI_HASH d06ebb8e1d9a22d19e38d63fdb83954253f39bedc5d46232a05645685722ca37)
+set(FFI_HASH 72fba7922703ddfa7a028d513ac15a85c8d54c8d67f55fa5a4802885dc652056)
-set(LZMA_VERSION 5.2.4)
+set(LZMA_VERSION 5.2.5)
set(LZMA_URI https://tukaani.org/xz/xz-${LZMA_VERSION}.tar.bz2)
-set(LZMA_HASH 3313fd2a95f43d88e44264e6b015e7d03053e681860b0d5d3f9baca79c57b7bf)
+set(LZMA_HASH 5117f930900b341493827d63aa910ff5e011e0b994197c3b71c08a20228a42df)
-set(SSL_VERSION 1.1.0i)
+set(SSL_VERSION 1.1.1g)
set(SSL_URI https://www.openssl.org/source/openssl-${SSL_VERSION}.tar.gz)
-set(SSL_HASH ebbfc844a8c8cc0ea5dc10b86c9ce97f401837f3fa08c17b2cdadc118253cf99)
+set(SSL_HASH ddb04774f1e32f0c49751e21b67216ac87852ceb056b75209af2443400636d46)
-set(SQLITE_VERSION 3.24.0)
+set(SQLITE_VERSION 3.31.1)
set(SQLITE_URI https://www.sqlite.org/2018/sqlite-src-3240000.zip)
set(SQLITE_HASH fb558c49ee21a837713c4f1e7e413309aabdd9c7)
@@ -293,13 +289,13 @@ set(EMBREE_VERSION 3.10.0)
set(EMBREE_URI https://github.com/embree/embree/archive/v${EMBREE_VERSION}.zip)
set(EMBREE_HASH 4bbe29e7eaa46417efc75fc5f1e8eb87)
-set(USD_VERSION 20.02)
+set(USD_VERSION 20.05)
set(USD_URI https://github.com/PixarAnimationStudios/USD/archive/v${USD_VERSION}.tar.gz)
-set(USD_HASH 8901815f01469a7f4496bd60315a9d10)
+set(USD_HASH 6d679e739e7f65725d9c029e37dda9fc)
-set(OIDN_VERSION 1.0.0)
-set(OIDN_URI https://github.com/OpenImageDenoise/oidn/releases/download/v${OIDN_VERSION}/oidn-${OIDN_VERSION}.src.zip)
-set(OIDN_HASH 19fe67b0164e8f020ac8a4f520defe60)
+set(OIDN_VERSION 1.2.1)
+set(OIDN_URI https://github.com/OpenImageDenoise/oidn/releases/download/v${OIDN_VERSION}/oidn-${OIDN_VERSION}.src.tar.gz)
+set(OIDN_HASH cbebc1a25eb6de62af3a59e943063608)
set(LIBGLU_VERSION 9.0.1)
set(LIBGLU_URI ftp://ftp.freedesktop.org/pub/mesa/glu/glu-${LIBGLU_VERSION}.tar.xz)
@@ -312,3 +308,6 @@ set(MESA_HASH d60828056d77bfdbae0970f9b15fb1be)
set(XR_OPENXR_SDK_VERSION 1.0.8)
set(XR_OPENXR_SDK_URI https://github.com/KhronosGroup/OpenXR-SDK/archive/release-${XR_OPENXR_SDK_VERSION}.tar.gz)
set(XR_OPENXR_SDK_HASH c6de63d2e0f9029aa58dfa97cad8ce07)
+set(ISPC_VERSION v1.13.0)
+set(ISPC_URI https://github.com/ispc/ispc/archive/${ISPC_VERSION}.tar.gz)
+set(ISPC_HASH 4bf5e8d0020c4b9980faa702c1a6f25f)
diff --git a/build_files/build_environment/install_deps.sh b/build_files/build_environment/install_deps.sh
index b3d300565e2..49d7b8209ee 100755
--- a/build_files/build_environment/install_deps.sh
+++ b/build_files/build_environment/install_deps.sh
@@ -459,7 +459,7 @@ ALEMBIC_FORCE_BUILD=false
ALEMBIC_FORCE_REBUILD=false
ALEMBIC_SKIP=false
-USD_VERSION="20.02"
+USD_VERSION="20.05"
USD_FORCE_BUILD=false
USD_FORCE_REBUILD=false
USD_SKIP=false
diff --git a/build_files/build_environment/patches/cmakelists_hidapi.txt b/build_files/build_environment/patches/cmakelists_hidapi.txt
deleted file mode 100644
index 239b9d88b16..00000000000
--- a/build_files/build_environment/patches/cmakelists_hidapi.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-cmake_minimum_required(VERSION 2.8)
-project(hidapi)
-
-set(SRC_FILES
- windows/hid.c
-)
-
-set(HEADER_FILES
- hidapi/hidapi.h
-)
-include_directories(hidapi)
-add_definitions(-DHID_API_STATIC)
-add_library(hidapi STATIC ${SRC_FILES} ${HEADER_FILES})
-
-install(TARGETS hidapi DESTINATION lib)
-
-INSTALL(FILES hidapi/hidapi.h
- DESTINATION "include"
- )
-
diff --git a/build_files/build_environment/patches/hidapi.diff b/build_files/build_environment/patches/hidapi.diff
deleted file mode 100644
index 720a8ae5ae9..00000000000
--- a/build_files/build_environment/patches/hidapi.diff
+++ /dev/null
@@ -1,15 +0,0 @@
---- hidapi/hidapi.h 2011-10-25 20:58:16 -0600
-+++ hidapi/hidapi.h 2016-11-01 12:05:58 -0600
-@@ -30,7 +30,11 @@
- #include <wchar.h>
-
- #ifdef _WIN32
-- #define HID_API_EXPORT __declspec(dllexport)
-+ #ifdef HID_API_STATIC
-+ #define HID_API_EXPORT
-+ #else
-+ #define HID_API_EXPORT __declspec(dllexport)
-+ #endif
- #define HID_API_CALL
- #else
- #define HID_API_EXPORT /**< API export macro */
diff --git a/build_files/build_environment/patches/ispc.diff b/build_files/build_environment/patches/ispc.diff
new file mode 100644
index 00000000000..710bfc7a448
--- /dev/null
+++ b/build_files/build_environment/patches/ispc.diff
@@ -0,0 +1,36 @@
+diff -Naur external_ispc/CMakeLists.txt external_ispc_fixed/CMakeLists.txt
+--- external_ispc/CMakeLists.txt 2020-04-23 17:29:06 -0600
++++ external_ispc_fixed/CMakeLists.txt 2020-05-05 09:01:09 -0600
+@@ -389,7 +389,7 @@
+
+ # Link against Clang libraries
+ foreach(clangLib ${CLANG_LIBRARY_LIST})
+- find_library(${clangLib}Path NAMES ${clangLib} HINTS ${LLVM_LIBRARY_DIRS})
++ find_library(${clangLib}Path NAMES ${clangLib} HINTS ${LLVM_LIBRARY_DIRS} ${CLANG_LIBRARY_DIR})
+ list(APPEND CLANG_LIBRARY_FULL_PATH_LIST ${${clangLib}Path})
+ endforeach()
+ target_link_libraries(${PROJECT_NAME} ${CLANG_LIBRARY_FULL_PATH_LIST})
+diff -Naur orig/CMakeLists.txt external_ispc/CMakeLists.txt
+--- orig/CMakeLists.txt 2020-05-05 09:19:11 -0600
++++ external_ispc/CMakeLists.txt 2020-05-05 09:26:44 -0600
+@@ -333,7 +333,7 @@
+
+ # Include directories
+ target_include_directories(${PROJECT_NAME} PRIVATE
+- ${LLVM_INCLUDE_DIRS}
++ ${LLVM_INCLUDE_DIRS} ${CLANG_INCLUDE_DIRS}
+ ${CMAKE_CURRENT_SOURCE_DIR}/src
+ ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR})
+ # Compile options
+diff -Naur orig/cmake/GenerateBuiltins.cmake.txt external_ispc/cmake/GenerateBuiltins.cmake.txt
++++ orig/cmake/GenerateBuiltins.cmake 2020-05-25 13:32:40.830803821 +0200
++++ external_ispc/cmake/GenerateBuiltins.cmake 2020-05-25 13:32:40.830803821 +0200
+@@ -97,6 +97,8 @@
+
+ if ("${bit}" STREQUAL "32" AND ${arch} STREQUAL "x86")
+ set(target_arch "i386")
++ # Blender: disable 32bit due to build issues on Linux and being unnecessary.
++ set(SKIP ON)
+ elseif ("${bit}" STREQUAL "64" AND ${arch} STREQUAL "x86")
+ set(target_arch "x86_64")
+ elseif ("${bit}" STREQUAL "32" AND ${arch} STREQUAL "arm")
diff --git a/build_files/build_environment/patches/openimagedenoise.diff b/build_files/build_environment/patches/openimagedenoise.diff
deleted file mode 100644
index 7bfc3aa2eba..00000000000
--- a/build_files/build_environment/patches/openimagedenoise.diff
+++ /dev/null
@@ -1,120 +0,0 @@
-diff --git a/CMakeLists.txt b/CMakeLists.txt
-index 70ec895..e616b63 100644
---- a/CMakeLists.txt
-+++ b/CMakeLists.txt
-@@ -178,7 +178,9 @@ set_property(TARGET ${PROJECT_NAME} PROPERTY SOVERSION "0")
- ## Open Image Denoise examples
- ## ----------------------------------------------------------------------------
-
--add_subdirectory(examples)
-+if(WITH_EXAMPLE)
-+ add_subdirectory(examples)
-+endif()
-
- ## ----------------------------------------------------------------------------
- ## Open Image Denoise install and packaging
-Submodule mkl-dnn contains modified content
-diff --git a/mkl-dnn/cmake/TBB.cmake b/mkl-dnn/cmake/TBB.cmake
-index 0711e699..c14210b6 100644
---- a/mkl-dnn/cmake/TBB.cmake
-+++ b/mkl-dnn/cmake/TBB.cmake
-@@ -138,13 +138,13 @@ else()
- set(TBB_LIBRARY_MALLOC TBB_LIBRARY_MALLOC-NOTFOUND)
- if(APPLE)
- find_path(TBB_INCLUDE_DIR tbb/task_scheduler_init.h PATHS ${TBB_ROOT}/include NO_DEFAULT_PATH)
-- find_library(TBB_LIBRARY tbb PATHS ${TBB_ROOT}/lib NO_DEFAULT_PATH)
-- find_library(TBB_LIBRARY_MALLOC tbbmalloc PATHS ${TBB_ROOT}/lib NO_DEFAULT_PATH)
-+ find_library(TBB_LIBRARY tbb_static PATHS ${TBB_ROOT}/lib NO_DEFAULT_PATH)
-+ find_library(TBB_LIBRARY_MALLOC tbbmalloc_static PATHS ${TBB_ROOT}/lib NO_DEFAULT_PATH)
- else()
- find_path(TBB_INCLUDE_DIR tbb/task_scheduler_init.h PATHS ${TBB_ROOT}/include NO_DEFAULT_PATH)
- set(TBB_HINTS HINTS ${TBB_ROOT}/lib/intel64/gcc4.4 ${TBB_ROOT}/lib ${TBB_ROOT}/lib64 PATHS /usr/libx86_64-linux-gnu/)
-- find_library(TBB_LIBRARY tbb ${TBB_HINTS})
-- find_library(TBB_LIBRARY_MALLOC tbbmalloc ${TBB_HINTS})
-+ find_library(TBB_LIBRARY tbb_static ${TBB_HINTS})
-+ find_library(TBB_LIBRARY_MALLOC tbbmalloc_static ${TBB_HINTS})
- endif()
- endif()
-
-diff '--ignore-matching-lines=:' -ur '--exclude=*.svn*' -u -r
---- a/cmake/install.cmake 2019-08-12 18:02:20.794402575 +0200
-+++ b/cmake/install.cmake 2019-08-12 18:06:07.470045703 +0200
-@@ -18,6 +18,13 @@
- ## Install library
- ## ----------------------------------------------------------------------------
-
-+if(UNIX)
-+install(FILES
-+ ${CMAKE_BINARY_DIR}/libOpenImageDenoise.a
-+ ${CMAKE_BINARY_DIR}/libmkldnn.a
-+ ${CMAKE_BINARY_DIR}/libcommon.a
-+ DESTINATION ${CMAKE_INSTALL_LIBDIR})
-+else()
- install(TARGETS ${PROJECT_NAME}
- EXPORT
- ${PROJECT_NAME}_Export
-@@ -38,6 +45,7 @@
- DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT devel
- )
- endif()
-+endif()
-
- ## ----------------------------------------------------------------------------
- ## Install headers
-@@ -78,6 +86,7 @@
- ## Install CMake configuration files
- ## ----------------------------------------------------------------------------
-
-+if(NOT UNIX)
- install(EXPORT ${PROJECT_NAME}_Export
- DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}
- #NAMESPACE ${PROJECT_NAME}::
-@@ -92,3 +101,4 @@
- DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}
- COMPONENT devel
- )
-+endif()
-diff '--ignore-matching-lines=:' -ur '--exclude=*.svn*' -u -r
---- a/CMakeLists.txt 2019-08-12 14:22:00.974078598 +0200
-+++ b/CMakeLists.txt 2019-08-12 18:05:05.949057375 +0200
-@@ -14,7 +14,11 @@
- ## limitations under the License. ##
- ## ======================================================================== ##
-
--cmake_minimum_required(VERSION 3.1)
-+if(UNIX)
-+ cmake_minimum_required(VERSION 3.1)
-+else()
-+ cmake_minimum_required(VERSION 3.13)
-+endif()
-
- set(OIDN_VERSION_MAJOR 1)
- set(OIDN_VERSION_MINOR 0)
-@@ -32,13 +36,8 @@
- set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake")
-
- # Build as shared or static library
--if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.13.0")
-- option(OIDN_STATIC_LIB "Build Open Image Denoise as a static library.")
-- mark_as_advanced(CLEAR OIDN_STATIC_LIB)
--else()
-- set(OIDN_STATIC_LIB OFF CACHE BOOL "Build Open Image Denoise as a static library." FORCE)
-- mark_as_advanced(OIDN_STATIC_LIB)
--endif()
-+option(OIDN_STATIC_LIB "Build Open Image Denoise as a static library.")
-+mark_as_advanced(CLEAR OIDN_STATIC_LIB)
- if(OIDN_STATIC_LIB)
- set(OIDN_LIB_TYPE STATIC)
- else()
-diff -Naur orig/core/api.cpp external_openimagedenoise/core/api.cpp
---- orig/core/api.cpp 2019-07-19 08:37:04 -0600
-+++ external_openimagedenoise/core/api.cpp 2020-01-21 15:10:56 -0700
-@@ -15,7 +15,7 @@
- // ======================================================================== //
-
- #ifdef _WIN32
--# define OIDN_API extern "C" __declspec(dllexport)
-+# define OIDN_API extern "C"
- #else
- # define OIDN_API extern "C" __attribute__ ((visibility ("default")))
- #endif
diff --git a/build_files/build_environment/patches/usd.diff b/build_files/build_environment/patches/usd.diff
index adee3d2e458..8f457271a5c 100644
--- a/build_files/build_environment/patches/usd.diff
+++ b/build_files/build_environment/patches/usd.diff
@@ -11,10 +11,10 @@ diff -x .git -ur usd.orig/cmake/defaults/Packages.cmake external_usd/cmake/defau
# --math
diff -x .git -ur usd.orig/pxr/base/plug/initConfig.cpp external_usd/pxr/base/plug/initConfig.cpp
---- usd.orig/pxr/base/plug/initConfig.cpp 2019-10-24 22:39:53.000000000 +0200
-+++ external_usd/pxr/base/plug/initConfig.cpp 2019-12-11 11:00:37.643323127 +0100
-@@ -69,8 +69,38 @@
-
+--- usd.orig/pxr/base/plug/initConfig.cpp.orig 2020-06-12 17:20:07.478199779 +0200
++++ external_usd/pxr/base/plug/initConfig.cpp 2020-06-12 17:25:28.648588552 +0200
+@@ -69,10 +69,40 @@
+
ARCH_CONSTRUCTOR(Plug_InitConfig, 2, void)
{
+ /* The contents of this constructor have been moved to usd_initialise_plugin_path(...) */
@@ -39,7 +39,9 @@ diff -x .git -ur usd.orig/pxr/base/plug/initConfig.cpp external_usd/pxr/base/plu
+usd_initialise_plugin_path(const char *datafiles_usd_path)
+{
std::vector<std::string> result;
-
+
+ std::vector<std::string> debugMessages;
+
+ // Add Blender-specific paths. They MUST end in a slash, or symlinks will not be treated as directory.
+ if (datafiles_usd_path != NULL && datafiles_usd_path[0] != '\0') {
+ std::string datafiles_usd_path_str(datafiles_usd_path);
@@ -49,14 +51,14 @@ diff -x .git -ur usd.orig/pxr/base/plug/initConfig.cpp external_usd/pxr/base/plu
+ result.push_back(datafiles_usd_path_str);
+ }
+
- // Determine the absolute path to the Plug shared library.
- // Any relative paths specified in the plugin search path will be
- // anchored to this directory, to allow for relocatability.
-@@ -94,9 +124,24 @@
- _AppendPathList(&result, installLocation, sharedLibPath);
+ // Determine the absolute path to the Plug shared library. Any relative
+ // paths specified in the plugin search path will be anchored to this
+ // directory, to allow for relocatability. Note that this can fail when pxr
+@@ -114,9 +144,24 @@
+ _AppendPathList(&result, installLocation, binaryPath);
#endif // PXR_INSTALL_LOCATION
-
-- Plug_SetPaths(result);
+
+- Plug_SetPaths(result, debugMessages);
-}
+ if (!TfGetenv("PXR_PATH_DEBUG").empty()) {
+ printf("USD Plugin paths: (%zu in total):\n", result.size());
@@ -64,10 +66,10 @@ diff -x .git -ur usd.orig/pxr/base/plug/initConfig.cpp external_usd/pxr/base/plu
+ printf(" %s\n", path.c_str());
+ }
+ }
-
-+ Plug_SetPaths(result);
+
++ Plug_SetPaths(result, debugMessages);
}
-
+
PXR_NAMESPACE_CLOSE_SCOPE
+
+/* Workaround to make it possible to pass a path at runtime to USD. */
@@ -79,18 +81,6 @@ diff -x .git -ur usd.orig/pxr/base/plug/initConfig.cpp external_usd/pxr/base/plu
+ PXR_NS::usd_initialise_plugin_path(datafiles_usd_path);
+}
+}
-diff -Naur external_usd_orig/pxr/base/tf/preprocessorUtils.h external_usd/pxr/base/tf/preprocessorUtils.h
---- external_usd_orig/pxr/base/tf/preprocessorUtils.h 2019-10-24 14:39:53 -0600
-+++ external_usd/pxr/base/tf/preprocessorUtils.h 2020-01-14 09:30:18 -0700
-@@ -189,7 +189,7 @@
- /// Exapnds to 1 if the argument is a tuple, and 0 otherwise.
- /// \ingroup group_tf_Preprocessor
- /// \hideinitializer
--#if defined(ARCH_OS_WINDOWS)
-+#if defined(ARCH_COMPILER_MSVC)
- #define TF_PP_IS_TUPLE(sequence) \
- BOOST_VMD_IS_TUPLE(sequence)
- #else
diff -Naur external_usd_base/cmake/macros/Public.cmake external_usd/cmake/macros/Public.cmake
--- external_usd_base/cmake/macros/Public.cmake 2019-10-24 14:39:53 -0600
+++ external_usd/cmake/macros/Public.cmake 2020-01-11 13:33:29 -0700
diff --git a/build_files/buildbot/README.md b/build_files/buildbot/README.md
index cf129f83b39..06733c9a42d 100644
--- a/build_files/buildbot/README.md
+++ b/build_files/buildbot/README.md
@@ -8,7 +8,7 @@ Code signing is done as part of INSTALL target, which makes it possible to sign
files which are aimed into a bundle and coming from a non-signed source (such as
libraries SVN).
-This is achieved by specifying `slave_codesign.cmake` as a post-install script
+This is achieved by specifying `worker_codesign.cmake` as a post-install script
run by CMake. This CMake script simply involves an utility script written in
Python which takes care of an actual signing.
diff --git a/build_files/buildbot/buildbot_utils.py b/build_files/buildbot/buildbot_utils.py
index 39a00980cc8..e8adf5ba810 100644
--- a/build_files/buildbot/buildbot_utils.py
+++ b/build_files/buildbot/buildbot_utils.py
@@ -40,8 +40,8 @@ class Builder:
# Buildbot runs from build/ directory
self.blender_dir = os.path.abspath(os.path.join('..', 'blender.git'))
- self.build_dir = os.path.abspath(os.path.join('..', 'build', name))
- self.install_dir = os.path.abspath(os.path.join('..', 'install', name))
+ self.build_dir = os.path.abspath(os.path.join('..', 'build'))
+ self.install_dir = os.path.abspath(os.path.join('..', 'install'))
self.upload_dir = os.path.abspath(os.path.join('..', 'install'))
# Detect platform
diff --git a/build_files/buildbot/codesign/base_code_signer.py b/build_files/buildbot/codesign/base_code_signer.py
index 2f86531a4d0..dca771cdbaf 100644
--- a/build_files/buildbot/codesign/base_code_signer.py
+++ b/build_files/buildbot/codesign/base_code_signer.py
@@ -48,6 +48,7 @@ import shutil
import subprocess
import time
import tarfile
+import uuid
from pathlib import Path
from tempfile import TemporaryDirectory
@@ -121,21 +122,10 @@ class BaseCodeSigner(metaclass=abc.ABCMeta):
# Consider this an input of the code signing server.
unsigned_storage_dir: Path
- # Information about archive which contains files which are to be signed.
- #
- # This archive is created by the buildbot worked and acts as an input for
- # the code signing server.
- unsigned_archive_info: ArchiveWithIndicator
-
# Storage where signed files are stored.
# Consider this an output of the code signer server.
signed_storage_dir: Path
- # Information about archive which contains signed files.
- #
- # This archive is created by the code signing server.
- signed_archive_info: ArchiveWithIndicator
-
# Platform the code is currently executing on.
platform: util.Platform
@@ -146,50 +136,44 @@ class BaseCodeSigner(metaclass=abc.ABCMeta):
# Unsigned (signing server input) configuration.
self.unsigned_storage_dir = absolute_shared_storage_dir / 'unsigned'
- self.unsigned_archive_info = ArchiveWithIndicator(
- self.unsigned_storage_dir, 'unsigned_files.tar', 'ready.stamp')
# Signed (signing server output) configuration.
self.signed_storage_dir = absolute_shared_storage_dir / 'signed'
- self.signed_archive_info = ArchiveWithIndicator(
- self.signed_storage_dir, 'signed_files.tar', 'ready.stamp')
self.platform = util.get_current_platform()
- """
- General note on cleanup environment functions.
-
- It is expected that there is only one instance of the code signer server
- running for a given input/output directory, and that it serves a single
- buildbot worker.
- By its nature, a buildbot worker only produces one build at a time and
- never performs concurrent builds.
- This leads to a conclusion that when starting in a clean environment
- there shouldn't be any archives remaining from a previous build.
-
- However, it is possible to have various failure scenarios which might
- leave the environment in a non-clean state:
+ def cleanup_environment_for_builder(self) -> None:
+ # TODO(sergey): Revisit need of cleaning up the existing files.
+ # In practice it wasn't so helpful, and with multiple clients
+ # talking to the same server it becomes even mor etricky.
+ pass
- - Network hiccup which makes buildbot worker to stop current build
- and re-start it after connection to server is re-established.
+ def cleanup_environment_for_signing_server(self) -> None:
+ # TODO(sergey): Revisit need of cleaning up the existing files.
+ # In practice it wasn't so helpful, and with multiple clients
+ # talking to the same server it becomes even mor etricky.
+ pass
- Note, this could also happen during buildbot server maintenance.
+ def generate_request_id(self) -> str:
+ """
+ Generate an unique identifier for code signing request.
+ """
+ return str(uuid.uuid4())
- - Signing server might get restarted due to updates or other reasons.
+ def archive_info_for_request_id(
+ self, path: Path, request_id: str) -> ArchiveWithIndicator:
+ return ArchiveWithIndicator(
+ path, f'{request_id}.tar', f'{request_id}.ready')
- Requiring manual interaction in such cases is not something good to
- require, so here we simply assume that the system is used the way it is
- intended to and restore environment to a prestine clean state.
- """
+ def signed_archive_info_for_request_id(
+ self, request_id: str) -> ArchiveWithIndicator:
+ return self.archive_info_for_request_id(
+ self.signed_storage_dir, request_id)
- def cleanup_environment_for_builder(self) -> None:
- self.unsigned_archive_info.clean()
- self.signed_archive_info.clean()
-
- def cleanup_environment_for_signing_server(self) -> None:
- # Don't clear the requested to-be-signed archive since we might be
- # restarting signing machine while the buildbot is busy.
- self.signed_archive_info.clean()
+ def unsigned_archive_info_for_request_id(
+ self, request_id: str) -> ArchiveWithIndicator:
+ return self.archive_info_for_request_id(
+ self.unsigned_storage_dir, request_id)
############################################################################
# Buildbot worker side helpers.
@@ -232,7 +216,7 @@ class BaseCodeSigner(metaclass=abc.ABCMeta):
if self.check_file_is_to_be_signed(file)]
return files_to_be_signed
- def wait_for_signed_archive_or_die(self) -> None:
+ def wait_for_signed_archive_or_die(self, request_id) -> None:
"""
Wait until archive with signed files is available.
@@ -240,13 +224,19 @@ class BaseCodeSigner(metaclass=abc.ABCMeta):
is still no responce from the signing server the application will exit
with a non-zero exit code.
"""
+
+ signed_archive_info = self.signed_archive_info_for_request_id(
+ request_id)
+ unsigned_archive_info = self.unsigned_archive_info_for_request_id(
+ request_id)
+
timeout_in_seconds = self.config.TIMEOUT_IN_SECONDS
time_start = time.monotonic()
- while not self.signed_archive_info.is_ready():
+ while not signed_archive_info.is_ready():
time.sleep(1)
time_slept_in_seconds = time.monotonic() - time_start
if time_slept_in_seconds > timeout_in_seconds:
- self.unsigned_archive_info.clean()
+ unsigned_archive_info.clean()
raise SystemExit("Signing server didn't finish signing in "
f"{timeout_in_seconds} seconds, dying :(")
@@ -303,13 +293,19 @@ class BaseCodeSigner(metaclass=abc.ABCMeta):
return
logger_builder.info('Found %d files to sign.', len(files))
+ request_id = self.generate_request_id()
+ signed_archive_info = self.signed_archive_info_for_request_id(
+ request_id)
+ unsigned_archive_info = self.unsigned_archive_info_for_request_id(
+ request_id)
+
pack_files(files=files,
- archive_filepath=self.unsigned_archive_info.archive_filepath)
- self.unsigned_archive_info.tag_ready()
+ archive_filepath=unsigned_archive_info.archive_filepath)
+ unsigned_archive_info.tag_ready()
# Wait for the signing server to finish signing.
logger_builder.info('Waiting signing server to sign the files...')
- self.wait_for_signed_archive_or_die()
+ self.wait_for_signed_archive_or_die(request_id)
# Extract signed files from archive and move files to final location.
with TemporaryDirectory(prefix='blender-buildbot-') as temp_dir_str:
@@ -317,7 +313,7 @@ class BaseCodeSigner(metaclass=abc.ABCMeta):
logger_builder.info('Extracting signed files from archive...')
extract_files(
- archive_filepath=self.signed_archive_info.archive_filepath,
+ archive_filepath=signed_archive_info.archive_filepath,
extraction_dir=unpacked_signed_files_dir)
destination_dir = path
@@ -327,19 +323,44 @@ class BaseCodeSigner(metaclass=abc.ABCMeta):
unpacked_signed_files_dir, destination_dir)
logger_builder.info('Removing archive with signed files...')
- self.signed_archive_info.clean()
+ signed_archive_info.clean()
############################################################################
# Signing server side helpers.
- def wait_for_sign_request(self) -> None:
+ def wait_for_sign_request(self) -> str:
"""
Wait for the buildbot to request signing of an archive.
+
+ Returns an identifier of signing request.
"""
+
# TOOD(sergey): Support graceful shutdown on Ctrl-C.
- while not self.unsigned_archive_info.is_ready():
+
+ logger_server.info(
+ f'Waiting for a request directory {self.unsigned_storage_dir} to appear.')
+ while not self.unsigned_storage_dir.exists():
+ time.sleep(1)
+
+ logger_server.info(
+ 'Waiting for a READY indicator of any signing request.')
+ request_id = None
+ while request_id is None:
+ for file in self.unsigned_storage_dir.iterdir():
+ if file.suffix != '.ready':
+ continue
+ request_id = file.stem
+ logger_server.info(f'Found READY for request ID {request_id}.')
+ if request_id is None:
+ time.sleep(1)
+
+ unsigned_archive_info = self.unsigned_archive_info_for_request_id(
+ request_id)
+ while not unsigned_archive_info.is_ready():
time.sleep(1)
+ return request_id
+
@abc.abstractmethod
def sign_all_files(self, files: List[AbsoluteAndRelativeFileName]) -> None:
"""
@@ -348,7 +369,7 @@ class BaseCodeSigner(metaclass=abc.ABCMeta):
NOTE: Signing should happen in-place.
"""
- def run_signing_pipeline(self):
+ def run_signing_pipeline(self, request_id: str):
"""
Run the full signing pipeline starting from the point when buildbot
worker have requested signing.
@@ -360,9 +381,14 @@ class BaseCodeSigner(metaclass=abc.ABCMeta):
with TemporaryDirectory(prefix='blender-codesign-') as temp_dir_str:
temp_dir = Path(temp_dir_str)
+ signed_archive_info = self.signed_archive_info_for_request_id(
+ request_id)
+ unsigned_archive_info = self.unsigned_archive_info_for_request_id(
+ request_id)
+
logger_server.info('Extracting unsigned files from archive...')
extract_files(
- archive_filepath=self.unsigned_archive_info.archive_filepath,
+ archive_filepath=unsigned_archive_info.archive_filepath,
extraction_dir=temp_dir)
logger_server.info('Collecting all files which needs signing...')
@@ -374,11 +400,11 @@ class BaseCodeSigner(metaclass=abc.ABCMeta):
logger_server.info('Packing signed files...')
pack_files(files=files,
- archive_filepath=self.signed_archive_info.archive_filepath)
- self.signed_archive_info.tag_ready()
+ archive_filepath=signed_archive_info.archive_filepath)
+ signed_archive_info.tag_ready()
logger_server.info('Removing signing request...')
- self.unsigned_archive_info.clean()
+ unsigned_archive_info.clean()
logger_server.info('Signing is complete.')
@@ -389,11 +415,11 @@ class BaseCodeSigner(metaclass=abc.ABCMeta):
while True:
logger_server.info('Waiting for the signing request in %s...',
self.unsigned_storage_dir)
- self.wait_for_sign_request()
+ request_id = self.wait_for_sign_request()
logger_server.info(
- 'Got signing request, beging signign procedure.')
- self.run_signing_pipeline()
+ f'Beging signign procedure for request ID {request_id}.')
+ self.run_signing_pipeline(request_id)
############################################################################
# Command executing.
diff --git a/build_files/buildbot/slave_rsync.py b/build_files/buildbot/slave_rsync.py
deleted file mode 100644
index 19f1e67408d..00000000000
--- a/build_files/buildbot/slave_rsync.py
+++ /dev/null
@@ -1,37 +0,0 @@
-# ##### 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.
-#
-# ##### END GPL LICENSE BLOCK #####
-
-# <pep8 compliant>
-
-# Runs on buildbot slave, rsync zip directly to buildbot server rather
-# than using upload which is much slower
-
-import buildbot_utils
-import os
-import sys
-
-if __name__ == "__main__":
- builder = buildbot_utils.create_builder_from_arguments()
-
- # rsync, this assumes ssh keys are setup so no password is needed
- local_zip = "buildbot_upload.zip"
- remote_folder = "builder.blender.org:/data/buildbot-master/uploaded/"
- remote_zip = remote_folder + "buildbot_upload_" + builder.name + ".zip"
-
- command = ["rsync", "-avz", local_zip, remote_zip]
- buildbot_utils.call(command)
diff --git a/build_files/buildbot/slave_bundle_dmg.py b/build_files/buildbot/worker_bundle_dmg.py
index 11d2c3cb602..cd3da85e12a 100755
--- a/build_files/buildbot/slave_bundle_dmg.py
+++ b/build_files/buildbot/worker_bundle_dmg.py
@@ -30,7 +30,7 @@ from tempfile import TemporaryDirectory, NamedTemporaryFile
from typing import List
BUILDBOT_DIRECTORY = Path(__file__).absolute().parent
-CODESIGN_SCRIPT = BUILDBOT_DIRECTORY / 'slave_codesign.py'
+CODESIGN_SCRIPT = BUILDBOT_DIRECTORY / 'worker_codesign.py'
BLENDER_GIT_ROOT_DIRECTORY = BUILDBOT_DIRECTORY.parent.parent
DARWIN_DIRECTORY = BLENDER_GIT_ROOT_DIRECTORY / 'release' / 'darwin'
diff --git a/build_files/buildbot/slave_codesign.cmake b/build_files/buildbot/worker_codesign.cmake
index fd2beae11a0..f37feaef407 100644
--- a/build_files/buildbot/slave_codesign.cmake
+++ b/build_files/buildbot/worker_codesign.cmake
@@ -33,7 +33,7 @@ else()
endif()
execute_process(
- COMMAND ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_LIST_DIR}/slave_codesign.py"
+ COMMAND ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_LIST_DIR}/worker_codesign.py"
"${CMAKE_INSTALL_PREFIX}"
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
RESULT_VARIABLE exit_code
diff --git a/build_files/buildbot/slave_codesign.py b/build_files/buildbot/worker_codesign.py
index a82ee98b1b5..a82ee98b1b5 100755
--- a/build_files/buildbot/slave_codesign.py
+++ b/build_files/buildbot/worker_codesign.py
diff --git a/build_files/buildbot/slave_compile.py b/build_files/buildbot/worker_compile.py
index 65cadea587b..f1357e1864f 100644
--- a/build_files/buildbot/slave_compile.py
+++ b/build_files/buildbot/worker_compile.py
@@ -25,7 +25,7 @@ import buildbot_utils
def get_cmake_options(builder):
post_install_script = os.path.join(
- builder.blender_dir, 'build_files', 'buildbot', 'slave_codesign.cmake')
+ builder.blender_dir, 'build_files', 'buildbot', 'worker_codesign.cmake')
config_file = "build_files/cmake/config/blender_release.cmake"
options = ['-DCMAKE_BUILD_TYPE:STRING=Release',
@@ -35,7 +35,7 @@ def get_cmake_options(builder):
options.append('-DCMAKE_OSX_ARCHITECTURES:STRING=x86_64')
options.append('-DCMAKE_OSX_DEPLOYMENT_TARGET=10.9')
elif builder.platform == 'win':
- options.extend(['-G', 'Visual Studio 15 2017 Win64'])
+ options.extend(['-G', 'Visual Studio 16 2019', '-A', 'x64'])
options.extend(['-DPOSTINSTALL_SCRIPT:PATH=' + post_install_script])
elif builder.platform == 'linux':
config_file = "build_files/buildbot/config/blender_linux.cmake"
diff --git a/build_files/buildbot/slave_pack.py b/build_files/buildbot/worker_pack.py
index 8549a7881e6..87ee49c87d8 100644
--- a/build_files/buildbot/slave_pack.py
+++ b/build_files/buildbot/worker_pack.py
@@ -18,7 +18,7 @@
# <pep8 compliant>
-# Runs on buildbot slave, creating a release package using the build
+# Runs on buildbot worker, creating a release package using the build
# system and zipping it into buildbot_upload.zip. This is then uploaded
# to the master in the next buildbot step.
@@ -110,7 +110,7 @@ def pack_mac(builder):
release_dir = os.path.join(builder.blender_dir, 'release', 'darwin')
buildbot_dir = os.path.join(builder.blender_dir, 'build_files', 'buildbot')
- bundle_script = os.path.join(buildbot_dir, 'slave_bundle_dmg.py')
+ bundle_script = os.path.join(buildbot_dir, 'worker_bundle_dmg.py')
command = [bundle_script]
command += ['--dmg', package_filepath]
diff --git a/build_files/buildbot/slave_test.py b/build_files/buildbot/worker_test.py
index b959568a5c6..b959568a5c6 100644
--- a/build_files/buildbot/slave_test.py
+++ b/build_files/buildbot/worker_test.py
diff --git a/build_files/buildbot/slave_update.py b/build_files/buildbot/worker_update.py
index 36a7ae31c84..36a7ae31c84 100644
--- a/build_files/buildbot/slave_update.py
+++ b/build_files/buildbot/worker_update.py
diff --git a/build_files/cmake/Modules/FindOpenImageDenoise.cmake b/build_files/cmake/Modules/FindOpenImageDenoise.cmake
index b3d0dab3289..c7215d30e8a 100644
--- a/build_files/cmake/Modules/FindOpenImageDenoise.cmake
+++ b/build_files/cmake/Modules/FindOpenImageDenoise.cmake
@@ -48,7 +48,14 @@ SET(_openimagedenoise_FIND_COMPONENTS
# These are needed when building statically
SET(_openimagedenoise_FIND_STATIC_COMPONENTS
common
+
+ # These additional library names change between versions, we list all of them
+ # so builds work with multiple versions. Missing libraries are skipped.
+ dnnl_cpu
+ dnnl_common
+ dnnl_cpu # Second time because of circular dependency
mkldnn
+ dnnl
)
SET(_openimagedenoise_LIBRARIES)
diff --git a/build_files/cmake/config/blender_full.cmake b/build_files/cmake/config/blender_full.cmake
index a997d7c0e68..41bee263e22 100644
--- a/build_files/cmake/config/blender_full.cmake
+++ b/build_files/cmake/config/blender_full.cmake
@@ -11,6 +11,7 @@ set(WITH_CODEC_AVI ON CACHE BOOL "" FORCE)
set(WITH_CODEC_FFMPEG ON CACHE BOOL "" FORCE)
set(WITH_CODEC_SNDFILE ON CACHE BOOL "" FORCE)
set(WITH_CYCLES ON CACHE BOOL "" FORCE)
+set(WITH_CYCLES_EMBREE ON CACHE BOOL "" FORCE)
set(WITH_CYCLES_OSL ON CACHE BOOL "" FORCE)
set(WITH_DRACO ON CACHE BOOL "" FORCE)
set(WITH_FFTW3 ON CACHE BOOL "" FORCE)
diff --git a/build_files/cmake/config/blender_lite.cmake b/build_files/cmake/config/blender_lite.cmake
index f3a6d4608fe..68b9bd1d94d 100644
--- a/build_files/cmake/config/blender_lite.cmake
+++ b/build_files/cmake/config/blender_lite.cmake
@@ -15,6 +15,7 @@ set(WITH_CODEC_AVI OFF CACHE BOOL "" FORCE)
set(WITH_CODEC_FFMPEG OFF CACHE BOOL "" FORCE)
set(WITH_CODEC_SNDFILE OFF CACHE BOOL "" FORCE)
set(WITH_CYCLES OFF CACHE BOOL "" FORCE)
+set(WITH_CYCLES_EMBREE OFF CACHE BOOL "" FORCE)
set(WITH_CYCLES_OSL OFF CACHE BOOL "" FORCE)
set(WITH_CYCLES_DEVICE_OPTIX OFF CACHE BOOL "" FORCE)
set(WITH_DRACO OFF CACHE BOOL "" FORCE)
diff --git a/build_files/cmake/config/blender_release.cmake b/build_files/cmake/config/blender_release.cmake
index 01a59e451aa..5fce64ce719 100644
--- a/build_files/cmake/config/blender_release.cmake
+++ b/build_files/cmake/config/blender_release.cmake
@@ -12,6 +12,7 @@ set(WITH_CODEC_AVI ON CACHE BOOL "" FORCE)
set(WITH_CODEC_FFMPEG ON CACHE BOOL "" FORCE)
set(WITH_CODEC_SNDFILE ON CACHE BOOL "" FORCE)
set(WITH_CYCLES ON CACHE BOOL "" FORCE)
+set(WITH_CYCLES_EMBREE ON CACHE BOOL "" FORCE)
set(WITH_CYCLES_OSL ON CACHE BOOL "" FORCE)
set(WITH_DRACO ON CACHE BOOL "" FORCE)
set(WITH_FFTW3 ON CACHE BOOL "" FORCE)
diff --git a/build_files/cmake/platform/platform_win32.cmake b/build_files/cmake/platform/platform_win32.cmake
index db939b1d00c..01d48364435 100644
--- a/build_files/cmake/platform/platform_win32.cmake
+++ b/build_files/cmake/platform/platform_win32.cmake
@@ -539,10 +539,10 @@ if(WITH_OPENIMAGEDENOISE)
set(OPENIMAGEDENOISE_LIBRARIES
optimized ${OPENIMAGEDENOISE_LIBPATH}/OpenImageDenoise.lib
optimized ${OPENIMAGEDENOISE_LIBPATH}/common.lib
- optimized ${OPENIMAGEDENOISE_LIBPATH}/mkldnn.lib
+ optimized ${OPENIMAGEDENOISE_LIBPATH}/dnnl.lib
debug ${OPENIMAGEDENOISE_LIBPATH}/OpenImageDenoise_d.lib
debug ${OPENIMAGEDENOISE_LIBPATH}/common_d.lib
- debug ${OPENIMAGEDENOISE_LIBPATH}/mkldnn_d.lib)
+ debug ${OPENIMAGEDENOISE_LIBPATH}/dnnl_d.lib)
set(OPENIMAGEDENOISE_DEFINITIONS)
endif()
diff --git a/doc/python_api/requirements.txt b/doc/python_api/requirements.txt
index 263b12eb604..1e538f83b31 100644
--- a/doc/python_api/requirements.txt
+++ b/doc/python_api/requirements.txt
@@ -1,2 +1,2 @@
-Sphinx==3.0.3
-sphinx_rtd_theme==0.5.0rc1
+Sphinx==3.1.1
+sphinx_rtd_theme==0.5.0
diff --git a/doc/python_api/rst/bgl.rst b/doc/python_api/rst/bgl.rst
index 746e5d1d674..0c40f2092a8 100644
--- a/doc/python_api/rst/bgl.rst
+++ b/doc/python_api/rst/bgl.rst
@@ -427,7 +427,7 @@ offers a set of extensive examples, including advanced features.
Return evaluator parameters
- .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glGetMap.xml>`_
+ .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glGetMap.xml>`__
:type target: Enumerated constant
:arg target: Specifies the symbolic name of a map.
diff --git a/doc/python_api/rst/include__bmesh.rst b/doc/python_api/rst/include__bmesh.rst
index d1356a5e5d9..a2f38fc53bf 100644
--- a/doc/python_api/rst/include__bmesh.rst
+++ b/doc/python_api/rst/include__bmesh.rst
@@ -1,5 +1,5 @@
..
- This document is appended to the auto generated bmesh api doc to avoid clogging up the C files with details.
+ This document is appended to the auto generated BMesh API doc to avoid clogging up the C files with details.
to test this run:
./blender.bin -b -noaudio -P doc/python_api/sphinx_doc_gen.py -- \
--partial bmesh* ; cd doc/python_api ; sphinx-build sphinx-in sphinx-out ; cd ../../
@@ -19,25 +19,24 @@ Submodules:
Introduction
------------
-This API gives access the blenders internal mesh editing api, featuring geometry connectivity data and
+This API gives access the Blender's internal mesh editing API, featuring geometry connectivity data and
access to editing operations such as split, separate, collapse and dissolve.
-
The features exposed closely follow the C API,
-giving python access to the functions used by blenders own mesh editing tools.
+giving Python access to the functions used by Blender's own mesh editing tools.
For an overview of BMesh data types and how they reference each other see:
-`BMesh Design Document <https://wiki.blender.org/index.php/Dev:Source/Modeling/BMesh/Design>`_ .
+`BMesh Design Document <https://wiki.blender.org/index.php/Dev:Source/Modeling/BMesh/Design>`__.
.. note::
- **Disk** and **Radial** data is not exposed by the python api since this is for internal use only.
+ **Disk** and **Radial** data is not exposed by the Python API since this is for internal use only.
.. warning:: TODO items are...
- * add access to BMesh **walkers**
- * add custom-data manipulation functions add/remove/rename.
+ - add access to BMesh **walkers**.
+ - add custom-data manipulation functions add, remove or rename.
Example Script
@@ -46,55 +45,52 @@ Example Script
.. literalinclude:: __/__/__/release/scripts/templates_py/bmesh_simple.py
-Stand-Alone Module
-^^^^^^^^^^^^^^^^^^
+Standalone Module
+^^^^^^^^^^^^^^^^^
-The bmesh module is written to be standalone except for :mod:`mathutils`
+The BMesh module is written to be standalone except for :mod:`mathutils`
which is used for vertex locations and normals.
-
The only other exception to this are when converting mesh data to and from :class:`bpy.types.Mesh`.
Mesh Access
-----------
-There are 2 ways to access BMesh data, you can create a new BMesh by converting a mesh from
-:class:`bpy.types.BlendData.meshes` or by accessing the current edit mode mesh.
-see: :class:`bmesh.types.BMesh.from_mesh` and :mod:`bmesh.from_edit_mesh` respectively.
+There are two ways to access BMesh data, you can create a new BMesh by converting a mesh from
+:class:`bpy.types.BlendData.meshes` or by accessing the current Edit-Mode mesh.
+See: :class:`bmesh.types.BMesh.from_mesh` and :mod:`bmesh.from_edit_mesh` respectively.
-When explicitly converting from mesh data python **owns** the data, that is to say -
-that the mesh only exists while python holds a reference to it,
-and the script is responsible for putting it back into a mesh data-block when the edits are done.
+When explicitly converting from mesh data Python **owns** the data, that means that
+the mesh only exists while Python holds a reference to it.
+The script is responsible for putting it back into a mesh data-block when the edits are done.
-Note that unlike :mod:`bpy`, a BMesh does not necessarily correspond to data in the currently open blend file,
+Note that unlike :mod:`bpy`, a BMesh does not necessarily correspond to data in the currently open blend-file,
a BMesh can be created, edited and freed without the user ever seeing or having access to it.
-Unlike edit mode, the bmesh module can use multiple BMesh instances at once.
-
-Take care when dealing with multiple BMesh instances since the mesh data can use a lot of memory, while a mesh that
-python owns will be freed in when the script holds no references to it,
-its good practice to call :class:`bmesh.types.BMesh.free` which will remove all the mesh data immediately and disable
-further access.
+Unlike Edit-Mode, the BMesh module can use multiple BMesh instances at once.
+Take care when dealing with multiple BMesh instances since the mesh data can use a lot of memory.
+While a mesh that the Python script owns will be freed when the script holds no references to it,
+it's good practice to call :class:`bmesh.types.BMesh.free` which will remove all the mesh data immediately
+and disable further access.
-EditMode Tessellation
-^^^^^^^^^^^^^^^^^^^^^
-When writing scripts that operate on editmode data you will normally want to re-calculate the tessellation after
-running the script, this needs to be called explicitly.
+Edit-Mode Tessellation
+^^^^^^^^^^^^^^^^^^^^^^
-The BMesh its self does not store the triangulated faces, they are stored in the :class:`bpy.types.Mesh`,
+When writing scripts that operate on Edit-Mode data you will normally want to re-calculate the tessellation after
+running the script, this needs to be called explicitly.
+The BMesh itself does not store the triangulated faces, instead they are stored in the :class:`bpy.types.Mesh`,
to refresh tessellation triangles call :class:`bpy.types.Mesh.calc_loop_triangles`.
CustomData Access
-----------------
-BMesh has a unified way to access mesh attributes such as UV's vertex colors, shape keys, edge crease etc.
+BMesh has a unified way to access mesh attributes such as UVs, vertex colors, shape keys, edge crease, etc.
+This works by having a **layers** property on BMesh data sequences to access the custom data layers
+which can then be used to access the actual data on each vert, edge, face or loop.
-This works by having a **layers** property on bmesh data sequences to access the custom data layers which can then be
-used to access the actual data on each vert/edge/face/loop.
-
-Here are some examples ...
+Here are some examples:
.. code-block:: python
@@ -139,27 +135,27 @@ Here are some examples ...
Keeping a Correct State
-----------------------
-When modeling in blender there are certain assumptions made about the state of the mesh.
+When modeling in Blender there are certain assumptions made about the state of the mesh:
-* hidden geometry isn't selected.
-* when an edge is selected, its vertices are selected too.
-* when a face is selected, its edges and vertices are selected.
-* duplicate edges / faces don't exist.
-* faces have at least 3 vertices.
+- Hidden geometry isn't selected.
+- When an edge is selected, its vertices are selected too.
+- When a face is selected, its edges and vertices are selected.
+- Duplicate edges / faces don't exist.
+- Faces have at least three vertices.
To give developers flexibility these conventions are not enforced,
-however tools must leave the mesh in a valid state else other tools may behave incorrectly.
-
+yet tools must leave the mesh in a valid state or else other tools may behave incorrectly.
Any errors that arise from not following these conventions is considered a bug in the script,
-not a bug in blender.
+not a bug in Blender.
Selection / Flushing
^^^^^^^^^^^^^^^^^^^^
As mentioned above, it is possible to create an invalid selection state
-(by selecting a state and then de-selecting one of its vertices's for example), mostly the best way to solve this is to
-flush the selection after performing a series of edits. this validates the selection state.
+(by selecting a state and then deselecting one of its vertices for example),
+mostly the best way to solve this is to flush the selection
+after performing a series of edits. This validates the selection state.
Module Functions
diff --git a/doc/python_api/rst/info_api_reference.rst b/doc/python_api/rst/info_api_reference.rst
index 7d8eaade0f2..f62b81f43f2 100644
--- a/doc/python_api/rst/info_api_reference.rst
+++ b/doc/python_api/rst/info_api_reference.rst
@@ -3,94 +3,84 @@
Reference API Usage
*******************
-Blender has many interlinking data types which have an auto-generated reference api which often has the information
+Blender has many interlinking data types which have an auto-generated reference API which often has the information
you need to write a script, but can be difficult to use.
-
-This document is designed to help you understand how to use the reference api.
+This document is designed to help you understand how to use the reference API.
Reference API Scope
===================
-The reference API covers :mod:`bpy.types`, which stores types accessed via :mod:`bpy.context` - *The user context*
-or :mod:`bpy.data` - *Blend file data*.
+The reference API covers :mod:`bpy.types`, which stores types accessed via :mod:`bpy.context` -- *the user context*
+or :mod:`bpy.data` -- *blend-file data*.
-Other modules such as :mod:`bmesh` and :mod:`aud` are not using Blenders data API
+Other modules such as :mod:`bmesh` and :mod:`aud` are not using Blender's data API
so this document doesn't apply to those modules.
Data Access
===========
-The most common case for using the reference API is to find out how to access data in the blend file.
-
-Before going any further its best to be aware of ID Data-Blocks in Blender since you will often find properties
+The most common case for using the reference API is to find out how to access data in the blend-file.
+Before going any further its best to be aware of ID data-blocks in Blender since you will often find properties
relative to them.
ID Data
-------
-ID Data-Blocks are used in Blender as top-level data containers.
-
-From the user interface this isn't so obvious, but when developing you need to know about ID Data-Blocks.
-
-ID data types include Scene, Group, Object, Mesh, Screen, World, Armature, Image and Texture.
-for a full list see the sub-classes of :class:`bpy.types.ID`
+ID data-blocks are used in Blender as top-level data containers.
+From the user interface this isn't so obvious, but when developing you need to know about ID data-blocks.
+ID data types include Scene, Group, Object, Mesh, Workspace, World, Armature, Image and Texture.
+For a full list see the subclasses of :class:`bpy.types.ID`.
-Here are some characteristics ID Data-Blocks share.
+Here are some characteristics ID data-blocks share:
-- ID's are blend file data, so loading a new blend file reloads an entire new set of Data-Blocks.
-- ID's can be accessed in Python from ``bpy.data.*``
+- IDs are blend-file data, so loading a new blend-file reloads an entire new set of data-blocks.
+- IDs can be accessed in Python from ``bpy.data.*``.
- Each data-block has a unique ``.name`` attribute, displayed in the interface.
-- Animation data is stored in ID's ``.animation_data``.
-- ID's are the only data types that can be linked between blend files.
-- ID's can be added/copied and removed via Python.
-- ID's have their own garbage-collection system which frees unused ID's when saving.
-- When a data-block has a reference to some external data, this is typically an ID Data-Block.
+- Animation data is stored in IDs ``.animation_data``.
+- IDs are the only data types that can be linked between blend-files.
+- IDs can be added/copied and removed via Python.
+- IDs have their own garbage-collection system which frees unused IDs when saving.
+- When a data-block has a reference to some external data, this is typically an ID data-block.
Simple Data Access
------------------
-Lets start with a simple case, say you want a python script to adjust the object's location.
+In this simple case a Python script is used to adjust the object's location.
+Start by collecting the information where the data is located.
-Start by finding this setting in the interface ``Properties Window -> Object -> Transform -> Location``
-
-From the button you can right click and select **Online Python Reference**, this will link you to:
-:class:`bpy.types.Object.location`
-
-Being an API reference, this link often gives little more information then the tool-tip, though some of the pages
+First find this setting in the interface ``Properties editor -> Object -> Transform -> Location``.
+From the button context menu select *Online Python Reference*, this will link you to:
+:class:`bpy.types.Object.location`.
+Being an API reference, this link often gives little more information then the tooltip, though some of the pages
include examples (normally at the top of the page).
+But you now know that you have to use ``.location`` and that its an array of three floats.
-At this point you may say *Now what?* - you know that you have to use ``.location`` and that its an array of 3 floats
-but you're still left wondering how to access this in a script.
-
-So the next step is to find out where to access objects, go down to the bottom of the page to the **References**
-section, for objects there are many references, but one of the most common places to access objects is via the context.
-
-It's easy to be overwhelmed at this point since there ``Object`` get referenced in so many places - modifiers,
-functions, textures and constraints.
-
+So the next step is to find out where to access objects, go down to the bottom of the page to the references section,
+for objects there are many references, but one of the most common places to access objects is via the context.
+It's easy to be overwhelmed at this point since there ``Object`` get referenced in so many places:
+modifiers, functions, textures and constraints.
But if you want to access any data the user has selected
you typically only need to check the :mod:`bpy.context` references.
-Even then, in this case there are quite a few though if you read over these - most are mode specific.
-If you happen to be writing a tool that only runs in weight paint mode, then using ``weight_paint_object``
-would be appropriate.
-However to access an item the user last selected, look for the ``active`` members,
-Having access to a single active member the user selects is a convention in Blender: eg. ``active_bone``,
-``active_pose_bone``, ``active_node`` ... and in this case we can use - ``active_object``.
+Even then, in this case there are quite a few though
+if you read over these you'll notice that most are mode specific.
+If you happen to be writing a tool that only runs in Weight Paint Mode,
+then using ``weight_paint_object`` would be appropriate.
+However, to access an item the user last selected, look for the ``active`` members,
+Having access to a single active member the user selects is a convention in Blender:
+e.g. ``active_bone``, ``active_pose_bone``, ``active_node``, etc. and in this case you can use ``active_object``.
-
-So now we have enough information to find the location of the active object.
+So now you have enough information to find the location of the active object.
.. code-block:: python
bpy.context.active_object.location
-You can type this into the python console to see the result.
-
+You can type this into the Python console to see the result.
The other common place to access objects in the reference is :class:`bpy.types.BlendData.objects`.
.. note::
@@ -100,7 +90,7 @@ The other common place to access objects in the reference is :class:`bpy.types.B
so the documentation points there.
-With :mod:`bpy.data.objects`, this is a collection of objects so you need to access one of its members.
+With :mod:`bpy.data.objects`, this is a collection of objects so you need to access one of its members:
.. code-block:: python
@@ -117,37 +107,34 @@ Here are some more complex examples:
.. code-block:: python
- # access a render layers samples
- bpy.context.scene.render.layers["RenderLayer"].samples
+ # Access the number of samples for the Cycles render engine.
+ bpy.context.scene.cycles.samples
- # access to the current weight paint brush size
+ # Access to the current weight paint brush size.
bpy.context.tool_settings.weight_paint.brush.size
- # check if the window is fullscreen
+ # Check if the window is full-screen.
bpy.context.window.screen.show_fullscreen
As you can see there are times when you want to access data which is nested
in a way that causes you to go through a few indirections.
+The properties are arranged to match how data is stored internally (in Blender's C code) which is often logical
+but not always quite what you would expect from using Blender.
+So this takes some time to learn, it helps you understand how data fits together in Blender
+which is important to know when writing scripts.
-The properties are arranged to match how data is stored internally (in blenders C code) which is often logical but
-not always quite what you would expect from using Blender.
-
-So this takes some time to learn, it helps you understand how data fits together in Blender which is important
-to know when writing scripts.
-
-
-When starting out scripting you will often run into the problem where you're not sure how to access the data you want.
-
-There are a few ways to do this.
+When starting out scripting you will often run into the problem
+where you're not sure how to access the data you want.
+There are a few ways to do this:
- Use the Python console's auto-complete to inspect properties.
*This can be hit-and-miss but has the advantage
that you can easily see the values of properties and assign them to interactively see the results.*
-- Copy the Data-Path from the user interface.
- *Explained further in :ref:`Copy Data Path <info_data_path_copy>`*
+- Copy the data path from the user interface.
+ *Explained further in* :ref:`Copy Data Path <info_data_path_copy>`.
- Using the documentation to follow references.
- *Explained further in :ref:`Indirect Data Access <info_data_path_indirect>`*
+ *Explained further in* :ref:`Indirect Data Access <info_data_path_indirect>`.
.. _info_data_path_copy:
@@ -155,42 +142,36 @@ There are a few ways to do this.
Copy Data Path
--------------
-Blender can compute the Python string to a property which is shown in the tool-tip, on the line below ``Python: ...``,
-This saves having to use the API reference to click back up the references to find where data is accessed from.
-
-There is a user-interface feature to copy the data-path which gives the path from an :class:`bpy.types.ID` data-block,
+Blender can compute the Python string to a property which is shown in the tooltip,
+on the line below ``Python: ...``. This saves having to open the API references to find where data is accessed from.
+In the context menu is a copy data-path tool which gives the path from an :class:`bpy.types.ID` data-block,
to its property.
-To see how this works we'll get the path to the Subdivision-Surface modifiers subdivision setting.
-
-Start with the default scene and select the **Modifiers** tab, then add a **Subdivision-Surface** modifier to the cube.
-
-Now hover your mouse over the button labeled **View**, The tool-tip includes :class:`bpy.types.SubsurfModifier.levels`
-but we want the path from the object to this property.
+To see how this works you'll get the path to the Subdivision Surface modifiers *Levels* setting.
+Start with the default scene and select the Modifiers tab, then add a Subdivision Surface modifier to the cube.
+Now hover your mouse over the button labeled *Levels Viewport*,
+The tooltip includes :class:`bpy.types.SubsurfModifier.levels` but you want the path from the object to this property.
Note that the text copied won't include the ``bpy.data.collection["name"].`` component since its assumed that
you won't be doing collection look-ups on every access and typically you'll want to use the context rather
then access each :class:`bpy.types.ID` instance by name.
-
Type in the ID path into a Python console :mod:`bpy.context.active_object`.
-Include the trailing dot and don't hit "enter", yet.
-
-Now right-click on the button and select **Copy Data Path**, then paste the result into the console.
+Include the trailing dot and don't execute the code, yet.
-So now you should have the answer:
+Now in the button's context menu select *Copy Data Path*, then paste the result into the console:
.. code-block:: python
bpy.context.active_object.modifiers["Subsurf"].levels
-Hit "enter" and you'll get the current value of 1. Now try changing the value to 2:
+Press :kbd:`Return` and you'll get the current value of 1. Now try changing the value to 2:
.. code-block:: python
- bpy.context.active_object.modifiers["Subsurf"].levels = 2
+ bpy.context.active_object.modifiers["Subsurf"].levels = 2
-You can see the value update in the Subdivision-Surface modifier's UI as well as the cube.
+You can see the value update in the Subdivision Surface modifier's UI as well as the cube.
.. _info_data_path_indirect:
@@ -198,51 +179,45 @@ You can see the value update in the Subdivision-Surface modifier's UI as well as
Indirect Data Access
--------------------
-For this example we'll go over something more involved, showing the steps to access the active sculpt brushes texture.
-
-Lets say we want to access the texture of a brush via Python, to adjust its ``contrast`` for example.
+This more advanced example shows the steps to access the active sculpt brushes texture.
+For example, if you want to access the texture of a brush via Python to adjust its ``contrast``.
-- Start in the default scene and enable 'Sculpt' mode from the 3D-View header.
-- From the toolbar expand the **Texture** panel and add a new texture.
- *Notice the texture button its self doesn't have very useful links (you can check the tooltips).*
-- The contrast setting isn't exposed in the sculpt toolbar, so view the texture in the properties panel...
+#. Start in the default scene and enable Sculpt Mode from the 3D Viewport header.
+#. From the Sidebar expand the Brush Settings panel's *Texture* subpanel and add a new texture.
+ *Notice the texture data-block menu itself doesn't have very useful links (you can check the tooltips).*
+#. The contrast setting isn't exposed in the Sidebar, so view the texture in the properties editor:
- - In the properties button select the Texture context.
- - Select the Brush icon to show the brush texture.
- - Expand the *Colors* panel to locate the *Contrast* button.
-- Right click on the contrast button and select **Online Python Reference**
- This takes you to ``bpy.types.Texture.contrast``
-- Now we can see that ``contrast`` is a property of texture,
- so next we'll check on how to access the texture from the brush.
-- Check on the **References** at the bottom of the page, sometimes there are many references, and it may take
- some guess work to find the right one, but in this case its obviously ``Brush.texture``.
+ - In the properties editor select the Texture tab.
+ - Select brush texture.
+ - Expand the *Colors* panel to locate the *Contrast* number field.
+#. Open the context menu of the contrast field and select *Online Python Reference*.
+ This takes you to ``bpy.types.Texture.contrast``. Now you can see that ``contrast`` is a property of texture.
+#. To find out how to access the texture from the brush check on the references at the bottom of the page.
+ Sometimes there are many references, and it may take some guesswork to find the right one,
+ but in this case it's ``Brush.texture``.
- *Now we know that the texture can be accessed from* ``bpy.data.brushes["BrushName"].texture``
- *but normally you won't want to access the brush by name, so we'll see now to access the active brush instead.*
-- So the next step is to check on where brushes are accessed from via the **References**.
- In this case there is simply ``bpy.context.brush`` which is all we need.
+#. Now you know that the texture can be accessed from ``bpy.data.brushes["BrushName"].texture``
+ but normally you *won't* want to access the brush by name, instead you want to access the active brush.
+ So the next step is to check on where brushes are accessed from via the references.
+ In this case there it is simply ``bpy.context.brush``.
-Now you can use the Python console to form the nested properties needed to access brush textures contrast,
-logically we now know.
+Now you can use the Python console to form the nested properties needed to access brush textures contrast:
+*Context -> Brush -> Texture -> Contrast*.
-*Context -> Brush -> Texture -> Contrast*
-
-Since the attribute for each is given along the way we can compose the data path in the python console:
+Since the attribute for each is given along the way you can compose the data path in the Python console:
.. code-block:: python
bpy.context.brush.texture.contrast
-
There can be multiple ways to access the same data, which you choose often depends on the task.
-
-An alternate path to access the same setting is...
+An alternate path to access the same setting is:
.. code-block:: python
bpy.context.sculpt.brush.texture.contrast
-Or access the brush directly...
+Or access the brush directly:
.. code-block:: python
@@ -251,27 +226,24 @@ Or access the brush directly...
If you are writing a user tool normally you want to use the :mod:`bpy.context` since the user normally expects
the tool to operate on what they have selected.
-
-For automation you are more likely to use :mod:`bpy.data` since you want to be able to access specific data and manipulate
-it, no matter what the user currently has the view set at.
+For automation you are more likely to use :mod:`bpy.data` since you want to be able to access specific data and
+manipulate it, no matter what the user currently has the view set at.
Operators
=========
-Most key-strokes and buttons in Blender call an operator which is also exposed to python via :mod:`bpy.ops`,
+Most hotkeys and buttons in Blender call an operator which is also exposed to Python via :mod:`bpy.ops`.
-To see the Python equivalent hover your mouse over the button and see the tool-tip,
-eg ``Python: bpy.ops.render.render()``,
-If there is no tool-tip or the ``Python:`` line is missing then this button is not using an operator and
-can't be accessed from Python.
+To see the Python equivalent hover your mouse over the button and see the tooltip,
+e.g ``Python: bpy.ops.render.render()``,
+If there is no tooltip or the ``Python:`` line is missing then this button is not using an operator
+and can't be accessed from Python.
-
-If you want to use this in a script you can press :kbd:`Control-C` while your mouse is over the button to copy it to the
-clipboard.
-
-You can also right click on the button and view the **Online Python Reference**, this mainly shows arguments and
-their defaults however operators written in Python show their file and line number which may be useful if you
+If you want to use this in a script you can press :kbd:`Ctrl-C` while your mouse is over the button
+to copy it to the clipboard.
+You can also use button's context menu and view the *Online Python Reference*, this mainly shows arguments and
+their defaults, however, operators written in Python show their file and line number which may be useful if you
are interested to check on the source code.
.. note::
@@ -280,21 +252,18 @@ are interested to check on the source code.
for more on this see :ref:`using operators <using_operators>`.
-Info View
----------
-
-Blender records operators you run and displays them in the **Info** space.
-This is located above the file-menu which can be dragged down to display its contents.
+Info Editor
+-----------
-Select the **Script** screen that comes default with Blender to see its output.
-You can perform some actions and see them show up - delete a vertex for example.
+Blender records operators you run and displays them in the Info editor.
+Select the Scripting workspace that comes default with Blender to see its output.
+You can perform some actions and see them show up -- delete a vertex for example.
-Each entry can be selected (Right-Mouse-Button),
-then copied :kbd:`Control-C`, usually to paste in the text editor or python console.
+Each entry can be selected, then copied :kbd:`Ctrl-C`, usually to paste in the text editor or Python console.
.. note::
Not all operators get registered for display,
zooming the view for example isn't so useful to repeat so its excluded from the output.
- To display *every* operator that runs see :ref:`Show All Operators <info_show_all_operators>`
+ To display *every* operator that runs see :ref:`Show All Operators <info_show_all_operators>`.
diff --git a/doc/python_api/rst/info_best_practice.rst b/doc/python_api/rst/info_best_practice.rst
index 8b64bacd50e..2607d047997 100644
--- a/doc/python_api/rst/info_best_practice.rst
+++ b/doc/python_api/rst/info_best_practice.rst
@@ -3,38 +3,34 @@
Best Practice
*************
-When writing your own scripts python is great for new developers to pick up and become productive,
-but you can also pick up odd habits or at least write scripts that are not easy for others to understand.
-
+When writing your own scripts Python is great for new developers to pick up and become productive,
+but you can also pick up bad practices or at least write scripts that are not easy for others to understand.
For your own work this is of course fine,
-but if you want to collaborate with others or have your work included with blender there are practices we encourage.
+but if you want to collaborate with others or have your work included with Blender there are practices we encourage.
Style Conventions
=================
-For Blender/Python development we have chosen to follow python suggested style guide to avoid mixing styles
-amongst our own scripts and make it easier to use python scripts from other projects.
-
-Using our style guide for your own scripts makes it easier if you eventually want to contribute them to blender.
-
-This style guide is known as pep8 and can be found `here <https://www.python.org/dev/peps/pep-0008/>`_
-
-A brief listing of pep8 criteria.
+For Blender Python development we have chosen to follow Python suggested style guide to avoid mixing styles
+among our own scripts and make it easier to use Python scripts from other projects.
+Using our style guide for your own scripts makes it easier if you eventually want to contribute them to Blender.
-- camel caps for class names: MyClass
-- all lower case underscore separated module names: my_module
-- indentation of 4 spaces (no tabs)
-- spaces around operators. ``1 + 1``, not ``1+1``
-- only use explicit imports, (no importing ``*``)
-- don't use single line: ``if val: body``, separate onto 2 lines instead.
+This style guide is known as `pep8 <https://www.python.org/dev/peps/pep-0008/>`__
+and here is a brief listing of pep8 criteria:
+- Camel caps for class names: MyClass
+- All lower case underscore separated module names: my_module
+- Indentation of 4 spaces (no tabs)
+- Spaces around operators: ``1 + 1``, not ``1+1``
+- Only use explicit imports (no wildcard importing ``*``)
+- Don't use multiple statements on a single line: ``if val: body``, separate onto two lines instead.
-As well as pep8 we have other conventions used for blender python scripts.
+As well as pep8 we have additional conventions used for Blender Python scripts:
- Use single quotes for enums, and double quotes for strings.
- Both are of course strings, but in our internal API enums are unique items from a limited set. eg.
+ Both are of course strings, but in our internal API enums are unique items from a limited set, e.g:
.. code-block:: python
@@ -42,14 +38,14 @@ As well as pep8 we have other conventions used for blender python scripts.
bpy.context.scene.render.filepath = "//render_out"
- pep8 also defines that lines should not exceed 79 characters,
- we felt this is too restrictive so this is optional per script.
+ we have decided that this is too restrictive so it is optional per script.
-Periodically we run checks for pep8 compliance on blender scripts,
-for scripts to be included in this check add this line as a comment at the top of the script.
+Periodically we run checks for pep8 compliance on Blender scripts,
+for scripts to be included in this check add this line as a comment at the top of the script:
``# <pep8 compliant>``
-To enable line length checks use this instead.
+To enable line length checks use this instead:
``# <pep8-80 compliant>``
@@ -59,85 +55,79 @@ User Interface Layout
Some notes to keep in mind when writing UI layouts:
-- UI code is quite simple. Layout declarations are there to easily create a decent layout.
+UI code is quite simple. Layout declarations are there to easily create a decent layout.
+The general rule here is: If you need more code for the layout declaration,
+than for the actual properties, then you are doing it wrong.
- General rule here: If you need more code for the layout declaration,
- then for the actual properties, you do it wrong.
-Example layouts:
+.. rubric:: Example layouts:
-- layout()
+``layout()``
+ The basic layout is a simple top-to-bottom layout.
- The basic layout is a simple Top -> Bottom layout.
+ .. code-block:: python
- .. code-block:: python
+ layout.prop()
+ layout.prop()
- layout.prop()
- layout.prop()
+``layout.row()``
+ Use ``row()``, when you want more than one property in a single line.
-- layout.row()
+ .. code-block:: python
- Use row(), when you want more than 1 property in one line.
+ row = layout.row()
+ row.prop()
+ row.prop()
- .. code-block:: python
+``layout.column()``
+ Use ``column()``, when you want your properties in a column.
- row = layout.row()
- row.prop()
- row.prop()
+ .. code-block:: python
-- layout.column()
+ col = layout.column()
+ col.prop()
+ col.prop()
- Use column(), when you want your properties in a column.
+``layout.split()``
+ This can be used to create more complex layouts.
+ For example, you can split the layout and create two ``column()`` layouts next to each other.
+ Do not use split, when you simply want two properties in a row. Use ``row()`` instead.
- .. code-block:: python
+ .. code-block:: python
- col = layout.column()
- col.prop()
- col.prop()
+ split = layout.split()
-- layout.split()
+ col = split.column()
+ col.prop()
+ col.prop()
- This can be used to create more complex layouts.
- For example you can split the layout and create two column() layouts next to each other.
- Don't use split, when you simply want two properties in a row. Use row() for that.
+ col = split.column()
+ col.prop()
+ col.prop()
- .. code-block:: python
- split = layout.split()
-
- col = split.column()
- col.prop()
- col.prop()
-
- col = split.column()
- col.prop()
- col.prop()
-
-Declaration names:
+.. rubric:: Declaration names:
Try to only use these variable names for layout declarations:
-- row for a row() layout
-- col for a column() layout
-- split for a split() layout
-- flow for a column_flow() layout
-- sub for a sub layout (a column inside a column for example)
+:row: for a ``row()`` layout
+:col: for a ``column()`` layout
+:split: for a ``split()`` layout
+:flow: for a ``column_flow()`` layout
+:sub: for a sub layout (a column inside a column for example)
Script Efficiency
=================
-
List Manipulation (General Python Tips)
---------------------------------------
-
-Searching for list items
+Searching for List Items
^^^^^^^^^^^^^^^^^^^^^^^^
In Python there are some handy list functions that save you having to search through the list.
-
-Even though you are not looping on the list data **python is**,
+Even though you are not looping on the list data **Python is**,
so you need to be aware of functions that will slow down your script by searching the whole list.
.. code-block:: python
@@ -150,23 +140,21 @@ so you need to be aware of functions that will slow down your script by searchin
Modifying Lists
^^^^^^^^^^^^^^^
-In python we can add and remove from a list, this is slower when the list length is modified,
+
+In Python you can add and remove from a list, this is slower when the list length is modified,
especially at the start of the list, since all the data after the index of
-modification needs to be moved up or down 1 place.
+modification needs to be moved up or down one place.
-The most simple way to add onto the end of the list is to use
-``my_list.append(list_item)`` or ``my_list.extend(some_list)`` and the fastest way to
-remove an item is ``my_list.pop()`` or ``del my_list[-1]``.
+The fastest way to add onto the end of the list is to use
+``my_list.append(list_item)`` or ``my_list.extend(some_list)`` and
+to remove an item is ``my_list.pop()`` or ``del my_list[-1]``.
To use an index you can use ``my_list.insert(index, list_item)`` or ``list.pop(index)``
for list removal, but these are slower.
-Sometimes its faster (but more memory hungry) to just rebuild the list.
-
-
-Say you want to remove all triangular polygons in a list.
-
-Rather than...
+Sometimes it's faster (but less memory efficient) to just rebuild the list.
+For example if you want to remove all triangular polygons in a list.
+Rather than:
.. code-block:: python
@@ -179,7 +167,7 @@ Rather than...
polygons.pop(p_idx) # remove the triangle
-It's faster to build a new list with list comprehension.
+It's faster to build a new list with list comprehension:
.. code-block:: python
@@ -189,14 +177,14 @@ It's faster to build a new list with list comprehension.
Adding List Items
^^^^^^^^^^^^^^^^^
-If you have a list that you want to add onto another list, rather than...
+If you have a list that you want to add onto another list, rather than:
.. code-block:: python
for l in some_list:
my_list.append(l)
-Use...
+Use:
.. code-block:: python
@@ -205,9 +193,7 @@ Use...
Note that insert can be used when needed,
but it is slower than append especially when inserting at the start of a long list.
-
-This example shows a very sub-optimal way of making a reversed list.
-
+This example shows a very suboptimal way of making a reversed list:
.. code-block:: python
@@ -219,7 +205,6 @@ This example shows a very sub-optimal way of making a reversed list.
Python provides more convenient ways to reverse a list using the slice method,
but you may want to time this before relying on it too much:
-
.. code-block:: python
some_reversed_list = some_list[::-1]
@@ -228,12 +213,10 @@ but you may want to time this before relying on it too much:
Removing List Items
^^^^^^^^^^^^^^^^^^^
-Use ``my_list.pop(index)`` rather than ``my_list.remove(list_item)``
-
+Use ``my_list.pop(index)`` rather than ``my_list.remove(list_item)``.
This requires you to have the index of the list item but is faster since ``remove()`` will search the list.
-
-Here is an example of how to remove items in 1 loop,
-removing the last items first, which is faster (as explained above).
+Here is an example of how to remove items in one loop,
+removing the last items first, which is faster (as explained above):
.. code-block:: python
@@ -247,7 +230,7 @@ removing the last items first, which is faster (as explained above).
This example shows a fast way of removing items,
for use in cases where you can alter the list order without breaking the scripts functionality.
-This works by swapping 2 list items, so the item you remove is always last.
+This works by swapping two list items, so the item you remove is always last:
.. code-block:: python
@@ -260,64 +243,59 @@ This works by swapping 2 list items, so the item you remove is always last.
my_list.pop()
-When removing many items in a large list this can provide a good speedup.
+When removing many items in a large list this can provide a good speed-up.
Avoid Copying Lists
^^^^^^^^^^^^^^^^^^^
-When passing a list/dictionary to a function,
+When passing a list or dictionary to a function,
it is faster to have the function modify the list rather than returning
-a new list so python doesn't have to duplicate the list in memory.
+a new list so Python doesn't have to duplicate the list in memory.
Functions that modify a list in-place are more efficient than functions that create new lists.
-
-
-This is generally slower so only use for functions when it makes sense not to modify the list in place.
+This is generally slower so only use for functions when it makes sense not to modify the list in place:
>>> my_list = some_list_func(my_list)
-This is generally faster since there is no re-assignment and no list duplication.
+This is generally faster since there is no re-assignment and no list duplication:
>>> some_list_func(vec)
-Also note that passing a sliced list makes a copy of the list in python memory.
+Also note that, passing a sliced list makes a copy of the list in Python memory:
>>> foobar(my_list[:])
-If my_list was a large array containing 10000's of items, a copy could use a lot of extra memory.
+If my_list was a large array containing 10,000's of items, a copy could use a lot of extra memory.
Writing Strings to a File (Python General)
------------------------------------------
-Here are 3 ways of joining multiple strings into one string for writing.
-This also applies to any area of your code that involves a lot of string joining.
-
-
-``String addition`` -
-this is the slowest option, *don't use if you can help it, especially when writing data in a loop*.
+Here are three ways of joining multiple strings into one string for writing.
+This also applies to any area of your code that involves a lot of string joining:
->>> file.write(str1 + " " + str2 + " " + str3 + "\n")
+String concatenation
+ This is the slowest option, do **not** use if you can avoid it, especially when writing data in a loop.
+ >>> file.write(str1 + " " + str2 + " " + str3 + "\n")
-``String formatting`` -
-use this when you are writing string data from floats and ints.
+String formatting
+ Use this when you are writing string data from floats and ints.
->>> file.write("%s %s %s\n" % (str1, str2, str3))
+ >>> file.write("%s %s %s\n" % (str1, str2, str3))
+String joining
+ Use to join a list of strings (the list may be temporary). In the following example, the strings are joined with
+ a space " " in between, other examples are "" or ", ".
-``String join() function``
-use to join a list of strings (the list may be temporary). In the following example, the strings are joined with a space " " in between, other examples are "" or ", ".
+ >>> file.write(" ".join((str1, str2, str3, "\n")))
->>> file.write(" ".join([str1, str2, str3, "\n"]))
-
-Join is fastest on many strings,
-`string formatting <https://wiki.blender.org/index.php/Dev:Source/Modeling/BMesh/Design>`__
-is quite fast too (better for converting data types). String arithmetic is slowest.
+Join is fastest on many strings, string formatting is quite fast too (better for converting data types).
+String concatenation is the slowest.
Parsing Strings (Import/Exporting)
@@ -333,36 +311,35 @@ Parsing Numbers
^^^^^^^^^^^^^^^
Use ``float(string)`` rather than ``eval(string)``, if you know the value will be an int then ``int(string)``,
-float() will work for an int too but it is faster to read ints with int().
+``float()`` will work for an int too but it is faster to read ints with ``int()``.
Checking String Start/End
^^^^^^^^^^^^^^^^^^^^^^^^^
-If you are checking the start of a string for a keyword, rather than...
+If you are checking the start of a string for a keyword, rather than:
>>> if line[0:5] == "vert ": ...
-use...
+Use:
>>> if line.startswith("vert "):
-Using ``startswith()`` is slightly faster (approx 5%) and also avoids a possible
-error with the slice length not matching the string length.
+Using ``startswith()`` is slightly faster (around 5%) and also avoids a possible error
+with the slice length not matching the string length.
-my_string.endswith("foo_bar") can be used for line endings too.
+``my_string.endswith("foo_bar")`` can be used for line endings too.
-If you are unsure whether the text is upper or lower case, use the ``lower()`` or ``upper()`` string function.
+If you are unsure whether the text is upper or lower case, use the ``lower()`` or ``upper()`` string function:
>>> if line.lower().startswith("vert ")
-Use try/except Sparingly
-------------------------
+Error Handling
+--------------
The **try** statement is useful to save time writing error checking code.
-
-However **try** is significantly slower than an **if** since an exception has to be set each time,
+However, **try** is significantly slower than an **if** since an exception has to be set each time,
so avoid using **try** in areas of your code that execute in a loop and runs many times.
There are cases where using **try** is faster than checking whether the condition will raise an error,
@@ -382,7 +359,7 @@ In cases where you know you are checking for the same value which is referenced
Time Your Code
--------------
-While developing a script it is good to time it to be aware of any changes in performance, this can be done simply.
+While developing a script it is good to time it to be aware of any changes in performance, this can be done simply:
.. code-block:: python
diff --git a/doc/python_api/rst/info_gotcha.rst b/doc/python_api/rst/info_gotcha.rst
index 55898845f3e..0713ffa54a1 100644
--- a/doc/python_api/rst/info_gotcha.rst
+++ b/doc/python_api/rst/info_gotcha.rst
@@ -4,7 +4,7 @@ Gotchas
*******
This document attempts to help you work with the Blender API in areas
-that can be troublesome and avoid practices that are known to give instability.
+that can be troublesome and avoid practices that are known to cause instability.
.. _using_operators:
@@ -12,13 +12,13 @@ that can be troublesome and avoid practices that are known to give instability.
Using Operators
===============
-Blender's operators are tools for users to access, that Python can access them too is very useful
-nevertheless operators have limitations that can make them cumbersome to script.
+Blender's operators are tools for users to access, that can access with Python too which is very useful.
+Still operators have limitations that can make them cumbersome to script.
-Main limits are...
+The main limits are:
-- Can't pass data such as objects, meshes or materials to operate on (operators use the context instead)
-- The return value from calling an operator gives the success (if it finished or was canceled),
+- Can't pass data such as objects, meshes or materials to operate on (operators use the context instead).
+- The return value from calling an operator is the success (if it finished or was canceled),
in some cases it would be more logical from an API perspective to return the result of the operation.
- Operators poll function can fail where an API function would raise an exception giving details on exactly why.
@@ -34,26 +34,23 @@ When calling an operator gives an error like this:
Which raises the question as to what the correct context might be?
Typically operators check for the active area type, a selection or active object they can operate on,
-but some operators are more picky about when they run.
-
+but some operators are more strict when they run.
In most cases you can figure out what context an operator needs
-simply be seeing how it's used in Blender and thinking about what it does.
-
-Unfortunately if you're still stuck - the only way to **really** know
-what's going on is to read the source code for the poll function and see what its checking.
+by examining how it's used in Blender and thinking about what it does.
+If you're still stuck, unfortunately, the only way to eventually know what is causing the error is
+to read the source code for the poll function and see what it is checking.
For Python operators it's not so hard to find the source
-since it's included with Blender and the source file/line is included in the operator reference docs.
-
+since it's included with Blender and the source file and line is included in the operator reference docs.
Downloading and searching the C code isn't so simple,
-especially if you're not familiar with the C language but by searching the
-operator name or description you should be able to find the poll function with no knowledge of C.
+especially if you're not familiar with the C language but by searching the operator name or description
+you should be able to find the poll function with no knowledge of C.
.. note::
Blender does have the functionality for poll functions to describe why they fail,
- but its currently not used much, if you're interested to help improve our API
- feel free to add calls to ``CTX_wm_operator_poll_msg_set`` where its not obvious why poll fails.
+ but its currently not used much, if you're interested to help improve the API
+ feel free to add calls to ``CTX_wm_operator_poll_msg_set`` where its not obvious why poll fails, e.g:
>>> bpy.ops.gpencil.draw()
RuntimeError: Operator bpy.ops.gpencil.draw.poll() Failed to find Grease Pencil data to draw into
@@ -63,7 +60,7 @@ The operator still doesn't work!
--------------------------------
Certain operators in Blender are only intended for use in a specific context,
-some operators for example are only called from the properties window where they check the current material,
+some operators for example are only called from the properties editor where they check the current material,
modifier or constraint.
Examples of this are:
@@ -74,8 +71,8 @@ Examples of this are:
- :mod:`bpy.ops.buttons.file_browse`
Another possibility is that you are the first person to attempt to use this operator
-in a script and some modifications need to be made to the operator to run in a different context,
-if the operator should logically be able to run but fails when accessed from a script
+in a script and some modifications need to be made to the operator to run in a different context.
+If the operator should logically be able to run but fails when accessed from a script
it should be reported to the bug tracker.
@@ -85,22 +82,20 @@ Stale Data
No updates after setting values
-------------------------------
-Sometimes you want to modify values from Python and immediately access the updated values, eg:
-
+Sometimes you want to modify values from Python and immediately access the updated values, e.g:
Once changing the objects :class:`bpy.types.Object.location`
you may want to access its transformation right after from :class:`bpy.types.Object.matrix_world`,
but this doesn't work as you might expect.
-Consider the calculations that might go into working out the object's final transformation, this includes:
+Consider the calculations that might contribute to the object's final transformation, this includes:
-- animation function curves.
-- drivers and their Python expressions.
-- constraints
-- parent objects and all of their f-curves, constraints etc.
+- Animation function curves.
+- Drivers and their Python expressions.
+- Constraints
+- Parent objects and all of their F-curves, constraints, etc.
To avoid expensive recalculations every time a property is modified,
-Blender defers making the actual calculations until they are needed.
-
+Blender defers the evaluation until the results are needed.
However, while the script runs you may want to access the updated values.
In this case you need to call :class:`bpy.types.ViewLayer.update` after modifying values, for example:
@@ -110,44 +105,41 @@ In this case you need to call :class:`bpy.types.ViewLayer.update` after modifyin
bpy.context.view_layer.update()
-Now all dependent data (child objects, modifiers, drivers... etc)
+Now all dependent data (child objects, modifiers, drivers, etc.)
has been recalculated and is available to the script within active view layer.
-Can I redraw during the script?
--------------------------------
+Can I redraw during script execution?
+-------------------------------------
The official answer to this is no, or... *"You don't want to do that"*.
-
-To give some background on the topic...
+To give some background on the topic:
While a script executes Blender waits for it to finish and is effectively locked until its done,
while in this state Blender won't redraw or respond to user input.
Normally this is not such a problem because scripts distributed with Blender
tend not to run for an extended period of time,
-nevertheless scripts *can* take ages to execute and its nice to see what's going on in the view port.
+nevertheless scripts *can* take a long time to complete and it would be nice to see progress in the viewport.
-Tools that lock Blender in a loop and redraw are highly discouraged
-since they conflict with Blenders ability to run multiple operators
+When tools lock Blender in a loop redraw are highly discouraged
+since they conflict with Blender's ability to run multiple operators
at once and update different parts of the interface as the tool runs.
-So the solution here is to write a **modal** operator, that is - an operator which defines a modal() function,
-See the modal operator template in the text editor.
-
+So the solution here is to write a **modal** operator, which is an operator that defines a ``modal()`` function,
+See the modal operator template in the text editor.
Modal operators execute on user input or setup their own timers to run frequently,
they can handle the events or pass through to be handled by the keymap or other modal operators.
-
-Transform, Painting, Fly-Mode and File-Select are example of a modal operators.
+Examples of a modal operators are Transform, Painting, Fly Navigation and File Select.
Writing modal operators takes more effort than a simple ``for`` loop
-that happens to redraw but is more flexible and integrates better with Blenders design.
+that contains draw calls but is more flexible and integrates better with Blender's design.
-**Ok, Ok! I still want to draw from Python**
+.. rubric:: Ok, Ok! I still want to draw from Python
-If you insist - yes its possible, but scripts that use this hack won't be considered
-for inclusion in Blender and any issues with using it won't be considered bugs,
-this is also not guaranteed to work in future releases.
+If you insist -- yes it's possible, but scripts that use this hack will not be considered
+for inclusion in Blender and any issue with using it will not be considered a bug,
+there is also no guaranteed compatibility in future releases.
.. code-block:: python
@@ -157,18 +149,18 @@ this is also not guaranteed to work in future releases.
Modes and Mesh Access
=====================
-When working with mesh data you may run into the problem where a script fails to run as expected in edit-mode.
-This is caused by edit-mode having its own data which is only written back to the mesh when exiting edit-mode.
+When working with mesh data you may run into the problem where a script fails to run as expected in Edit-Mode.
+This is caused by Edit-Mode having its own data which is only written back to the mesh when exiting Edit-Mode.
A common example is that exporters may access a mesh through ``obj.data`` (a :class:`bpy.types.Mesh`)
-but the user is in edit-mode, where the mesh data is available but out of sync with the edit mesh.
+when the user is in Edit-Mode, where the mesh data is available but out of sync with the edit mesh.
In this situation you can...
-- Exit edit-mode before running the tool.
+- Exit Edit-Mode before running the tool.
- Explicitly update the mesh by calling :class:`bmesh.types.BMesh.to_mesh`.
- Modify the script to support working on the edit-mode data directly, see: :mod:`bmesh.from_edit_mesh`.
-- Report the context as incorrect and only allow the script to run outside edit-mode.
+- Report the context as incorrect and only allow the script to run outside Edit-Mode.
.. _info_gotcha_mesh_faces:
@@ -176,24 +168,24 @@ In this situation you can...
N-Gons and Tessellation
=======================
-Since 2.63 NGons are supported, this adds some complexity
+Since 2.63 n-gons are supported, this adds some complexity
since in some cases you need to access triangles still (some exporters for example).
-There are now 3 ways to access faces:
+There are now three ways to access faces:
-- :class:`bpy.types.MeshPolygon` -
- this is the data structure which now stores faces in object mode
+- :class:`bpy.types.MeshPolygon` --
+ this is the data structure which now stores faces in Object-Mode
(access as ``mesh.polygons`` rather than ``mesh.faces``).
-- :class:`bpy.types.MeshLoopTriangle` -
+- :class:`bpy.types.MeshLoopTriangle` --
the result of tessellating polygons into triangles
(access as ``mesh.loop_triangles``).
-- :class:`bmesh.types.BMFace` -
- the polygons as used in editmode.
+- :class:`bmesh.types.BMFace` --
+ the polygons as used in Edit-Mode.
For the purpose of the following documentation,
-these will be referred to as polygons, loop triangles and bmesh-faces respectively.
+these will be referred to as polygons, loop triangles and BMesh-faces respectively.
-5+ sided faces will be referred to as ``ngons``.
+Faces with five or more sides will be referred to as ``ngons``.
Support Overview
@@ -216,58 +208,58 @@ Support Overview
- Unusable *(read-only)*.
- Best
* - Export/Output
- - Good *(ngon support)*
- - Good *(When ngons can't be used)*
- - Good *(ngons, extra memory overhead)*
+ - Good *(n-gon support)*
+ - Good *(When n-gons cannot be used)*
+ - Good *(n-gons, extra memory overhead)*
.. note::
Using the :mod:`bmesh` API is completely separate API from :mod:`bpy`,
- typically you would would use one or the other based on the level of editing needed,
+ typically you would use one or the other based on the level of editing needed,
not simply for a different way to access faces.
Creating
--------
-All 3 datatypes can be used for face creation.
+All three data types can be used for face creation:
-- polygons are the most efficient way to create faces but the data structure is _very_ rigid and inflexible,
+- Polygons are the most efficient way to create faces but the data structure is *very* rigid and inflexible,
you must have all your vertices and faces ready and create them all at once.
- This is further complicated by the fact that each polygon does not store its own verts,
+ This is further complicated by the fact that each polygon does not store its own vertices,
rather they reference an index and size in :class:`bpy.types.Mesh.loops` which are a fixed array too.
-- bmesh-faces are most likely the easiest way for new scripts to create faces,
- since faces can be added one by one and the api has features intended for mesh manipulation.
+- BMesh-faces are most likely the easiest way to create faces in new scripts,
+ since faces can be added one by one and the API has features intended for mesh manipulation.
While :class:`bmesh.types.BMesh` uses more memory it can be managed by only operating on one mesh at a time.
Editing
-------
-Editing is where the 3 data types vary most.
+Editing is where the three data types vary most.
- Polygons are very limited for editing,
changing materials and options like smooth works but for anything else
they are too inflexible and are only intended for storage.
-- Tessfaces should not be used for editing geometry because doing so will cause existing ngons to be tessellated.
-- BMesh-Faces are by far the best way to manipulate geometry.
+- Tessfaces should not be used for editing geometry because doing so will cause existing n-gons to be tessellated.
+- BMesh-faces are by far the best way to manipulate geometry.
Exporting
---------
-All 3 data types can be used for exporting,
-the choice mostly depends on whether the target format supports ngons or not.
+All three data types can be used for exporting,
+the choice mostly depends on whether the target format supports n-gons or not.
-- Polygons are the most direct & efficient way to export providing they convert into the output format easily enough.
-- Tessfaces work well for exporting to formats which don't support ngons,
+- Polygons are the most direct and efficient way to export providing they convert into the output format easily enough.
+- Tessfaces work well for exporting to formats which don't support n-gons,
in fact this is the only place where their use is encouraged.
- BMesh-Faces can work for exporting too but may not be necessary if polygons can be used
- since using bmesh gives some overhead because its not the native storage format in object mode.
+ since using BMesh gives some overhead because its not the native storage format in Object-Mode.
-EditBones, PoseBones, Bone... Bones
-===================================
+Edit Bones, Pose Bones, Bone... Bones
+=====================================
Armature Bones in Blender have three distinct data structures that contain them.
If you are accessing the bones through one of them, you may not have access to the properties you really need.
@@ -280,43 +272,41 @@ If you are accessing the bones through one of them, you may not have access to t
Edit Bones
----------
-``bpy.context.object.data.edit_bones`` contains a editbones;
-to access them you must set the armature mode to edit mode first (editbones do not exist in object or pose mode).
+``bpy.context.object.data.edit_bones`` contains an edit bones;
+to access them you must set the armature mode to Edit-Mode first (edit bones do not exist in Object or Pose-Mode).
Use these to create new bones, set their head/tail or roll, change their parenting relationships to other bones, etc.
-Example using :class:`bpy.types.EditBone` in armature editmode:
-
-This is only possible in edit mode.
+Example using :class:`bpy.types.EditBone` in armature Edit-Mode
+which is only possible in Edit-Mode:
>>> bpy.context.object.data.edit_bones["Bone"].head = Vector((1.0, 2.0, 3.0))
-This will be empty outside of editmode.
+This will be empty outside of Edit-Mode:
>>> mybones = bpy.context.selected_editable_bones
-Returns an editbone only in edit mode.
+Returns an edit bone only in Edit-Mode:
>>> bpy.context.active_bone
-Bones (Object Mode)
+Bones (Object-Mode)
-------------------
``bpy.context.object.data.bones`` contains bones.
-These *live* in object mode, and have various properties you can change,
+These *live* in Object-Mode, and have various properties you can change,
note that the head and tail properties are read-only.
-Example using :class:`bpy.types.Bone` in object or pose mode:
-
-Returns a bone (not an editbone) outside of edit mode
+Example using :class:`bpy.types.Bone` in Object or Pose-Mode
+returning a bone (not an edit bone) outside of Edit-Mode:
>>> bpy.context.active_bone
-This works, as with blender the setting can be edited in any mode
+This works, as with Blender the setting can be edited in any mode:
>>> bpy.context.object.data.bones["Bone"].use_deform = True
-Accessible but read-only
+Accessible but read-only:
>>> tail = myobj.data.bones["Bone"].tail
@@ -326,42 +316,42 @@ Pose Bones
``bpy.context.object.pose.bones`` contains pose bones.
This is where animation data resides, i.e. animatable transformations
-are applied to pose bones, as are constraints and ik-settings.
+are applied to pose bones, as are constraints and IK-settings.
-Examples using :class:`bpy.types.PoseBone` in object or pose mode:
+Examples using :class:`bpy.types.PoseBone` in Object or Pose-Mode:
.. code-block:: python
# Gets the name of the first constraint (if it exists)
bpy.context.object.pose.bones["Bone"].constraints[0].name
- # Gets the last selected pose bone (pose mode only)
+ # Gets the last selected pose bone (Pose-Mode only)
bpy.context.active_pose_bone
.. note::
Notice the pose is accessed from the object rather than the object data,
- this is why blender can have 2 or more objects sharing the same armature in different poses.
+ this is why Blender can have two or more objects sharing the same armature in different poses.
.. note::
- Strictly speaking PoseBone's are not bones, they are just the state of the armature,
+ Strictly speaking pose bones are not bones, they are just the state of the armature,
stored in the :class:`bpy.types.Object` rather than the :class:`bpy.types.Armature`,
- the real bones are however accessible from the pose bones - :class:`bpy.types.PoseBone.bone`
+ yet the real bones are accessible from the pose bones via :class:`bpy.types.PoseBone.bone`.
Armature Mode Switching
-----------------------
While writing scripts that deal with armatures you may find you have to switch between modes,
-when doing so take care when switching out of edit-mode not to keep references
-to the edit-bones or their head/tail vectors.
-Further access to these will crash blender so its important the script
+when doing so take care when switching out of Edit-Mode not to keep references
+to the edit bones or their head/tail vectors.
+Further access to these will crash Blender so its important the script
clearly separates sections of the code which operate in different modes.
-This is mainly an issue with editmode since pose data can be manipulated without having to be in pose mode,
-however for operator access you may still need to enter pose mode.
+This is mainly an issue with Edit-Mode since pose data can be manipulated without having to be in Pose-Mode,
+yet for operator access you may still need to enter Pose-Mode.
Data Names
@@ -372,8 +362,7 @@ Naming Limitations
------------------
A common mistake is to assume newly created data is given the requested name.
-
-This can cause bugs when you add some data (normally imported) then reference it later by name.
+This can cause bugs when you add data (normally imported) then reference it later by name:
.. code-block:: python
@@ -383,7 +372,7 @@ This can cause bugs when you add some data (normally imported) then reference it
bpy.data.meshes[meshid]
-Or with name assignment...
+Or with name assignment:
.. code-block:: python
@@ -397,12 +386,12 @@ Data names may not match the assigned values if they exceed the maximum length,
Its better practice not to reference objects by names at all,
-once created you can store the data in a list, dictionary, on a class etc,
+once created you can store the data in a list, dictionary, on a class, etc;
there is rarely a reason to have to keep searching for the same data by name.
If you do need to use name references, its best to use a dictionary to maintain
a mapping between the names of the imported assets and the newly created data,
-this way you don't run this risk of referencing existing data from the blend file, or worse modifying it.
+this way you don't run this risk of referencing existing data from the blend-file, or worse modifying it.
.. code-block:: python
@@ -421,17 +410,14 @@ this way you don't run this risk of referencing existing data from the blend fil
Library Collisions
------------------
-Blender keeps data names unique - :class:`bpy.types.ID.name` so you can't name two objects,
-meshes, scenes etc the same thing by accident.
-
-However when linking in library data from another blend file naming collisions can occur,
+Blender keeps data names unique (:class:`bpy.types.ID.name`) so you can't name two objects,
+meshes, scenes, etc., the same by accident.
+However, when linking in library data from another blend-file naming collisions can occur,
so its best to avoid referencing data by name at all.
-This can be tricky at times and not even blender handles this correctly in some case
-(when selecting the modifier object for eg you can't select between multiple objects with the same name),
-but its still good to try avoid problems in this area.
-
-
+This can be tricky at times and not even Blender handles this correctly in some case
+(when selecting the modifier object for e.g. you can't select between multiple objects with the same name),
+but its still good to try avoiding these problems in this area.
If you need to select between local and library data, there is a feature in ``bpy.data`` members to allow for this.
.. code-block:: python
@@ -454,18 +440,17 @@ If you need to select between local and library data, there is a feature in ``bp
Relative File Paths
===================
-Blenders relative file paths are not compatible with standard Python modules such as ``sys`` and ``os``.
+Blender's relative file paths are not compatible with standard Python modules such as ``sys`` and ``os``.
+Built-in Python functions don't understand Blender's ``//`` prefix which denotes the blend-file path.
-Built in Python functions don't understand blenders ``//`` prefix which denotes the blend file path.
-
-A common case where you would run into this problem is when exporting a material with associated image paths.
+A common case where you would run into this problem is when exporting a material with associated image paths:
>>> bpy.path.abspath(image.filepath)
-When using blender data from linked libraries there is an unfortunate complication
-since the path will be relative to the library rather than the open blend file.
-When the data block may be from an external blend file pass the library argument from the :class:`bpy.types.ID`.
+When using Blender data from linked libraries there is an unfortunate complication
+since the path will be relative to the library rather than the open blend-file.
+When the data block may be from an external blend-file pass the library argument from the :class:`bpy.types.ID`.
>>> bpy.path.abspath(image.filepath, library=image.library)
@@ -478,19 +463,15 @@ Unicode Problems
Python supports many different encodings so there is nothing stopping you from
writing a script in ``latin1`` or ``iso-8859-15``.
+See `PEP 263 <https://www.python.org/dev/peps/pep-0263/>`__.
-See `pep-0263 <https://www.python.org/dev/peps/pep-0263/>`_
-
-However this complicates matters for Blender's Python API because ``.blend`` files don't have an explicit encoding.
-
-To avoid the problem for Python integration and script authors we have decided all strings in blend files
+However, this complicates matters for Blender's Python API because ``.blend`` files don't have an explicit encoding.
+To avoid the problem for Python integration and script authors we have decided all strings in blend-files
**must** be ``UTF-8``, ``ASCII`` compatible.
-
This means assigning strings with different encodings to an object names for instance will raise an error.
-Paths are an exception to this rule since we cannot ignore the existence of non ``UTF-8`` paths on users file-system.
-
-This means seemingly harmless expressions can raise errors, eg.
+Paths are an exception to this rule since the existence of non-UTF-8 paths on user's file system cannot be ignored.
+This means seemingly harmless expressions can raise errors, e.g:
>>> print(bpy.data.filepath)
UnicodeEncodeError: 'ascii' codec can't encode characters in position 10-21: ordinal not in range(128)
@@ -501,7 +482,7 @@ This means seemingly harmless expressions can raise errors, eg.
TypeError: bpy_struct: item.attr= val: Object.name expected a string type, not str
-Here are 2 ways around filesystem encoding issues:
+Here are two ways around file-system encoding issues:
>>> print(repr(bpy.data.filepath))
@@ -512,11 +493,11 @@ Here are 2 ways around filesystem encoding issues:
Unicode encoding/decoding is a big topic with comprehensive Python documentation,
-to avoid getting stuck too deep in encoding problems - here are some suggestions:
+to keep it short about encoding problems -- here are some suggestions:
-- Always use utf-8 encoding or convert to utf-8 where the input is unknown.
-- Avoid manipulating filepaths as strings directly, use ``os.path`` functions instead.
-- Use ``os.fsencode()`` / ``os.fsdecode()`` instead of built in string decoding functions when operating on paths.
+- Always use UTF-8 encoding or convert to UTF-8 where the input is unknown.
+- Avoid manipulating file paths as strings directly, use ``os.path`` functions instead.
+- Use ``os.fsencode()`` or ``os.fsdecode()`` instead of built-in string decoding functions when operating on paths.
- To print paths or to include them in the user interface use ``repr(path)`` first
or ``"%r" % path`` with string formatting.
@@ -528,11 +509,11 @@ to avoid getting stuck too deep in encoding problems - here are some suggestions
some importers do this.
-Strange errors using 'threading' module
-=======================================
+Strange Errors when Using the 'Threading' Module
+================================================
-Python threading with Blender only works properly when the threads finish up before the script does.
-By using ``threading.join()`` for example.
+Python threading with Blender only works properly when the threads finish up before the script does,
+for example by using ``threading.join()``.
Here is an example of threading supported by Blender:
@@ -571,8 +552,8 @@ Here is an example of threading supported by Blender:
t.join()
-This an example of a timer which runs many times a second and moves
-the default cube continuously while Blender runs **(Unsupported)**.
+This an example of a timer which runs many times a second
+and moves the default cube continuously while Blender runs **(Unsupported)**.
.. code-block:: python
@@ -592,33 +573,33 @@ the default cube continuously while Blender runs **(Unsupported)**.
Use cases like the one above which leave the thread running once the script finishes
may seem to work for a while but end up causing random crashes or errors in Blender's own drawing code.
-So far, no work has gone into making Blender's Python integration thread safe,
-so until its properly supported, best not make use of this.
+So far, no work has been done to make Blender's Python integration thread safe,
+so until it's properly supported, it's best not make use of this.
.. note::
- Pythons threads only allow co-currency and won't speed up your scripts on multi-processor systems,
- the ``subprocess`` and ``multiprocess`` modules can be used with Blender and make use of multiple CPU's too.
+ Python threads only allow concurrency and won't speed up your scripts on multiprocessor systems,
+ the ``subprocess`` and ``multiprocess`` modules can be used with Blender to make use of multiple CPUs too.
Help! My script crashes Blender
===============================
-**TL;DR:** Do not keep direct references to Blender data (of any kind) when modifying the container
-of that data, and/or when some undo/redo may happen (e.g. during modal operators execution...).
+:abbr:`TL;DR (Too long; didn't read.)` Do not keep direct references to Blender data (of any kind)
+when modifying the container of that data, and/or when some undo/redo may happen
+(e.g. during modal operators execution...).
Instead, use indices (or other data always stored by value in Python, like string keys...),
that allow you to get access to the desired data.
-Ideally it would be impossible to crash Blender from Python
-however there are some problems with the API where it can be made to crash.
-
+Ideally it would be impossible to crash Blender from Python,
+however, there are some problems with the API where it can be made to crash.
Strictly speaking this is a bug in the API but fixing it would mean adding memory verification
-on every access since most crashes are caused by the Python objects referencing Blenders memory directly,
+on every access since most crashes are caused by the Python objects referencing Blender's memory directly,
whenever the memory is freed or re-allocated, further Python access to it can crash the script.
But fixing this would make the scripts run very slow,
or writing a very different kind of API which doesn't reference the memory directly.
-Here are some general hints to avoid running into these problems.
+Here are some general hints to avoid running into these problems:
- Be aware of memory limits,
especially when working with large lists since Blender can crash simply by running out of memory.
@@ -631,16 +612,16 @@ Here are some general hints to avoid running into these problems.
- Modules or classes that remain active while Blender is used,
should not hold references to data the user may remove, instead,
fetch data from the context each time the script is activated.
-- Crashes may not happen every time, they may happen more on some configurations/operating-systems.
-- Be wary of recursive patterns, those are very efficient at hiding the issues described here.
-- See last sub-section about `Unfortunate Corner Cases`_ for some known breaking exceptions.
+- Crashes may not happen every time, they may happen more on some configurations or operating systems.
+- Be careful with recursive patterns, those are very efficient at hiding the issues described here.
+- See last subsection about `Unfortunate Corner Cases`_ for some known breaking exceptions.
.. note::
To find the line of your script that crashes you can use the ``faulthandler`` module.
- See the `faulthandler docs <https://docs.python.org/dev/library/faulthandler.html>`_.
+ See the `Faulthandler docs <https://docs.python.org/dev/library/faulthandler.html>`__.
- While the crash may be in Blenders C/C++ code,
+ While the crash may be in Blender's C/C++ code,
this can help a lot to track down the area of the script that causes the crash.
.. note::
@@ -654,7 +635,7 @@ Here are some general hints to avoid running into these problems.
in any possible way.
-**Don’t:**
+.. rubric:: Do not:
.. code-block:: python
@@ -673,7 +654,7 @@ Here are some general hints to avoid running into these problems.
first_item.name = "foobar"
-**Do:**
+.. rubric:: Do:
.. code-block:: python
@@ -696,33 +677,31 @@ Here are some general hints to avoid running into these problems.
Undo/Redo
---------
-Undo invalidates all :class:`bpy.types.ID` instances (Object, Scene, Mesh, Lamp... etc).
+Undo invalidates all :class:`bpy.types.ID` instances (Object, Scene, Mesh, Light, etc.).
-This example shows how you can tell undo changes the memory locations.
+This example shows how you can tell undo changes the memory locations:
>>> hash(bpy.context.object)
-9223372036849950810
>>> hash(bpy.context.object)
-9223372036849950810
- # ... move the active object, then undo
+Move the active object, then undo:
>>> hash(bpy.context.object)
-9223372036849951740
As suggested above, simply not holding references to data when Blender is used
-interactively by the user is the only way to ensure the script doesn't become unstable.
+interactively by the user is the only way to make sure that the script doesn't become unstable.
Undo & Library Data
^^^^^^^^^^^^^^^^^^^
-One of the advantages with Blenders library linking system that undo
+One of the advantages with Blender's library linking system that undo
can skip checking changes in library data since it is assumed to be static.
-
Tools in Blender are not allowed to modify library data.
-
-Python however does not enforce this restriction.
+But Python does not enforce this restriction.
This can be useful in some cases, using a script to adjust material values for example.
But its also possible to use a script to make library data point to newly created local data,
@@ -733,13 +712,13 @@ So it's best to consider modifying library data an advanced usage of the API
and only to use it when you know what you're doing.
-Edit Mode / Memory Access
+Edit-Mode / Memory Access
-------------------------
-Switching edit-mode ``bpy.ops.object.mode_set(mode='EDIT')`` / ``bpy.ops.object.mode_set(mode='OBJECT')``
+Switching mode ``bpy.ops.object.mode_set(mode='EDIT')`` or ``bpy.ops.object.mode_set(mode='OBJECT')``
will re-allocate objects data,
-any references to a meshes vertices/polygons/uvs, armatures bones,
-curves points etc cannot be accessed after switching edit-mode.
+any references to a meshes vertices/polygons/UVs, armatures bones,
+curves points, etc. cannot be accessed after switching mode.
Only the reference to the data its self can be re-accessed, the following example will crash.
@@ -754,7 +733,7 @@ Only the reference to the data its self can be re-accessed, the following exampl
print(polygons)
-So after switching edit-mode you need to re-access any object data variables,
+So after switching mode you need to re-access any object data variables,
the following example shows how to avoid the crash above.
.. code-block:: python
@@ -770,7 +749,7 @@ the following example shows how to avoid the crash above.
These kinds of problems can happen for any functions which re-allocate
-the object data but are most common when switching edit-mode.
+the object data but are most common when switching mode.
Array Re-Allocation
@@ -791,21 +770,20 @@ internally the array which stores this data is re-allocated.
This can be avoided by re-assigning the point variables after adding the new one or by storing
indices to the points rather than the points themselves.
-The best way is to sidestep the problem altogether add all the points to the curve at once.
-This means you don't have to worry about array re-allocation and its faster too
-since reallocating the entire array for every point added is inefficient.
+The best way is to sidestep the problem altogether by adding all the points to the curve at once.
+This means you don't have to worry about array re-allocation and it's faster too
+since re-allocating the entire array for every added point is inefficient.
Removing Data
-------------
**Any** data that you remove shouldn't be modified or accessed afterwards,
-this includes f-curves, drivers, render layers, timeline markers, modifiers, constraints
-along with objects, scenes, collections, bones.. etc.
-
-The ``remove()`` api calls will invalidate the data they free to prevent common mistakes.
+this includes: F-curves, drivers, render layers, timeline markers, modifiers, constraints
+along with objects, scenes, collections, bones, etc.
-The following example shows how this precaution works.
+The ``remove()`` API calls will invalidate the data they free to prevent common mistakes.
+The following example shows how this precaution works:
.. code-block:: python
@@ -818,7 +796,7 @@ The following example shows how this precaution works.
But take care because this is limited to scripts accessing the variable which is removed,
-the next example will still crash.
+the next example will still crash:
.. code-block:: python
@@ -835,8 +813,8 @@ Besides all expected cases listed above, there are a few others that should not
an issue but, due to internal implementation details, currently are:
- ``Object.hide_viewport``, ``Object.hide_select`` and ``Object.hide_render``:
- Setting any of those booleans will trigger a rebuild of Collection caches, hence breaking
- any current iteration over ``Collection.all_objects``.
+ Setting any of those Booleans will trigger a rebuild of Collection caches,
+ thus breaking any current iteration over ``Collection.all_objects``.
sys.exit
@@ -848,5 +826,5 @@ as if Blender is crashing since ``sys.exit()`` will close Blender immediately.
For example, the ``argparse`` module will print an error and exit if the arguments are invalid.
-An ugly way of troubleshooting this is to set ``sys.exit = None`` and see what line of Python code is quitting,
+An dirty way of troubleshooting this is to set ``sys.exit = None`` and see what line of Python code is quitting,
you could of course replace ``sys.exit`` with your own function but manipulating Python in this way is bad practice.
diff --git a/doc/python_api/rst/info_overview.rst b/doc/python_api/rst/info_overview.rst
index 562076c6c43..9676489950e 100644
--- a/doc/python_api/rst/info_overview.rst
+++ b/doc/python_api/rst/info_overview.rst
@@ -1,4 +1,3 @@
-
.. _info_overview:
*******************
@@ -6,24 +5,24 @@ Python API Overview
*******************
The purpose of this document is to explain how Python and Blender fit together,
-covering some of the functionality that may not be obvious from reading the API
-references and example scripts.
+covering some of the functionality that may not be obvious from reading the API references
+and example scripts.
Python in Blender
=================
-Blender has an embedded Python interpreter which is loaded when Blender is started and stays
-active while Blender is running. This interpreter runs scripts to draw the user interface
-and is used for some of Blender’s internal tools as well.
+Blender has an embedded Python interpreter which is loaded when Blender is started
+and stays active while Blender is running. This interpreter runs scripts to draw the user interface
+and is used for some of Blender's internal tools as well.
Blender's embedded interpreter provides a typical Python environment, so code from tutorials
-on how to write Python scripts can also be run with Blender’s interpreter. Blender provides its
+on how to write Python scripts can also be run with Blender's interpreter. Blender provides its
Python modules, such as :mod:`bpy` and :mod:`mathutils`, to the embedded interpreter so they can
-be imported into a script and give access to Blender's data, classes, and functions. Scripts that
-deal with Blender data will need to import the modules to work.
+be imported into a script and give access to Blender's data, classes, and functions.
+Scripts that deal with Blender data will need to import the modules to work.
-Here is a simple example which moves a vertex attached to an object named **Cube**:
+Here is a simple example which moves a vertex attached to an object named "Cube":
.. code-block:: python
@@ -31,7 +30,7 @@ Here is a simple example which moves a vertex attached to an object named **Cube
bpy.data.objects["Cube"].data.vertices[0].co.x += 1.0
This modifies Blender's internal data directly.
-When you run this in the interactive console you will see the 3D viewport update.
+When you run this in the interactive console you will see the 3D Viewport update.
The Default Environment
@@ -41,7 +40,7 @@ When developing your own scripts it may help to understand how Blender sets up i
Many Python scripts come bundled with Blender and can be used as a reference
because they use the same API that script authors write tools in.
Typical usage for scripts include: user interface, import/export,
-scene manipulation, automation, defining your own toolset and customization.
+scene manipulation, automation, defining your own tool set and customization.
On startup Blender scans the ``scripts/startup/`` directory for Python modules and imports them.
The exact location of this directory depends on your installation.
@@ -54,8 +53,8 @@ Script Loading
This may seem obvious, but it is important to note the difference between
executing a script directly and importing a script as a module.
-Extending Blender by executing a script directly means the classes that the script
-defines remain available inside Blender after the script finishes execution.
+Extending Blender by executing a script directly means the classes that the script defines
+remain available inside Blender after the script finishes execution.
Using scripts this way makes future access to their classes
(to unregister them for example) more difficult compared to importing the scripts as modules.
When a script is imported as a module, its class instances will remain
@@ -63,12 +62,11 @@ inside the module and can be accessed later on by importing that module again.
For this reason it is preferable to avoid directly executing scripts that extend Blender by registering classes.
+Here are some ways to run scripts directly in Blender:
-Here are some ways to run scripts directly in Blender.
-
-- Loaded in the text editor and press **Run Script**.
+- Loaded in the text editor and press *Run Script*.
- Typed or pasted into the interactive console.
-- Execute a Python file from the command line with Blender, eg:
+- Execute a Python file from the command line with Blender, e.g:
.. code-block:: sh
@@ -77,24 +75,22 @@ Here are some ways to run scripts directly in Blender.
To run as modules:
-- The obvious way, ``import some_module`` command from the text window or interactive console.
-- Open as a text block and tick "Register" option, this will load with the blend file.
-- copy into one of the directories ``scripts/startup``, where they will be automatically imported on startup.
-- define as an add-on, enabling the add-on will load it as a Python module.
+- The obvious way, ``import some_module`` command from the text editor or interactive console.
+- Open as a text data-block and check the *Register* option, this will load with the blend-file.
+- Copy into one of the directories ``scripts/startup``, where they will be automatically imported on startup.
+- Define as an add-on, enabling the add-on will load it as a Python module.
Add-ons
-------
-Some of Blenders functionality is best kept optional,
-alongside scripts loaded at startup we have add-ons which are kept in their own directory ``scripts/addons``,
-and only load on startup if selected from the user preferences.
-
-The only difference between add-ons and built-in Python modules is that add-ons must contain a ``bl_info``
-variable which Blender uses to read metadata such as name, author, category and URL.
-
-The User Preferences add-on listing uses **bl_info** to display information about each add-on.
+Some of Blender's functionality is best kept optional,
+alongside scripts loaded at startup there are add-ons which are kept in their own directory ``scripts/addons``,
+They are only loaded on startup if selected from the user preferences.
+The only difference between add-ons and built-in Python modules is that add-ons must contain a ``bl_info`` variable
+which Blender uses to read metadata such as name, author, category and project link.
+The User Preferences add-on listing uses ``bl_info`` to display information about each add-on.
`See Add-ons <https://wiki.blender.org/index.php/Dev:Py/Scripts/Guidelines/Addons>`__
for details on the ``bl_info`` dictionary.
@@ -105,7 +101,7 @@ Integration through Classes
Running Python scripts in the text editor is useful for testing but you'll
want to extend Blender to make tools accessible like other built-in functionality.
-The Blender Python api allows integration for:
+The Blender Python API allows integration for:
- :class:`bpy.types.Panel`
- :class:`bpy.types.Menu`
@@ -114,13 +110,12 @@ The Blender Python api allows integration for:
- :class:`bpy.types.KeyingSet`
- :class:`bpy.types.RenderEngine`
-
This is intentionally limited. Currently, for more advanced features such as mesh modifiers,
object types, or shader nodes, C/C++ must be used.
For Python integration Blender defines methods which are common to all types.
This works by creating a Python subclass of a Blender class which contains variables and functions
-specified by the parent class which are pre-defined to interface with Blender.
+specified by the parent class which are predefined to interface with Blender.
For example:
@@ -137,22 +132,20 @@ For example:
bpy.utils.register_class(SimpleOperator)
-First note that we subclass a member of :mod:`bpy.types`,
+First note that it defines a subclass as a member of :mod:`bpy.types`,
this is common for all classes which can be integrated with Blender and
-used so we know if this is an Operator and not a Panel when registering.
+is used to distinguish an Operator from a Panel when registering.
Both class properties start with a ``bl_`` prefix.
This is a convention used to distinguish Blender properties from those you add yourself.
-
Next see the execute function, which takes an instance of the operator and the current context.
A common prefix is not used for functions.
-
Lastly the register function is called, this takes the class and loads it into Blender. See `Class Registration`_.
Regarding inheritance, Blender doesn't impose restrictions on the kinds of class inheritance used,
the registration checks will use attributes and functions defined in parent classes.
-class mix-in example:
+Class mix-in example:
.. code-block:: python
@@ -173,8 +166,8 @@ While ``__init__()`` and ``__del__()`` will be called if defined,
the class instances lifetime only spans the execution.
So a panel for example will have a new instance for every redraw,
for this reason there is rarely a cause to store variables in the panel instance.
-Instead, persistent variables should be stored in Blenders
-ata so that the state can be restored when Blender is restarted.
+Instead, persistent variables should be stored in Blender's data
+so that the state can be restored when Blender is restarted.
.. note::
@@ -182,15 +175,14 @@ ata so that the state can be restored when Blender is restarted.
So once the class is registered with Blender, instancing the class and calling the functions is left up to Blender.
In fact you cannot instance these classes from the script as you would expect with most Python API's.
-
-To run operators you can call them through the operator api, eg:
+To run operators you can call them through the operator API, e.g:
.. code-block:: python
import bpy
bpy.ops.object.simple_operator()
-User interface classes are given a context in which to draw, buttons window, file header, toolbar etc,
+User interface classes are given a context in which to draw, buttons, window, file header, toolbar, etc.,
then they are drawn when that area is displayed so they are never called by Python scripts directly.
@@ -205,7 +197,7 @@ Module Registration
Blender modules loaded at startup require ``register()`` and ``unregister()`` functions.
These are the *only* functions that Blender calls from your code, which is otherwise a regular Python module.
-A simple Blender/Python module can look like this:
+A simple Blender Python module can look like this:
.. code-block:: python
@@ -225,16 +217,15 @@ A simple Blender/Python module can look like this:
These functions usually appear at the bottom of the script containing class registration sometimes adding menu items.
You can also use them for internal purposes setting up data for your own tools but take care
-since register won't re-run when a new blend file is loaded.
+since register won't re-run when a new blend-file is loaded.
The register/unregister calls are used so it's possible to toggle add-ons and reload scripts while Blender runs.
If the register calls were placed in the body of the script, registration would be called on import,
meaning there would be no distinction between importing a module or loading its classes into Blender.
-
This becomes problematic when a script imports classes from another module
making it difficult to manage which classes are being loaded and when.
-The last 2 lines are only for testing:
+The last two lines are only for testing:
.. code-block:: python
@@ -251,7 +242,6 @@ Class Registration
Registering a class with Blender results in the class definition being loaded into Blender,
where it becomes available alongside existing functionality.
-
Once this class is loaded you can access it from :mod:`bpy.types`,
using the ``bl_idname`` rather than the classes original name.
@@ -271,23 +261,23 @@ Using the function arguments ``def execute(self, context, spam)``, will raise an
``ValueError: expected Operator, SimpleOperator class "execute" function to have 2 args, found 3``
-Using ``bl_idname = 1`` will raise.
+Using ``bl_idname = 1`` will raise:
``TypeError: validating class error: Operator.bl_idname expected a string type, not int``
-Inter Classes Dependencies
-^^^^^^^^^^^^^^^^^^^^^^^^^^
+Inter-Class Dependencies
+^^^^^^^^^^^^^^^^^^^^^^^^
When customizing Blender you may want to group your own settings together,
after all, they will likely have to co-exist with other scripts.
To group these properties classes need to be defined,
for groups within groups or collections within groups
-you can find yourself having to deal with order of registration/unregistration.
+you can't avoid having to deal with the order of registration/unregistration.
Custom properties groups are themselves classes which need to be registered.
-Say you want to store material settings for a custom engine.
+For example, if you want to store material settings for a custom engine:
.. code-block:: python
@@ -311,7 +301,7 @@ Say you want to store material settings for a custom engine.
.. note::
- *The class must be registered before being used in a property, failing to do so will raise an error:*
+ The class **must be** registered before being used in a property, failing to do so will raise an error:
``ValueError: bpy_struct "Material" registration error: my_custom_props could not register``
@@ -341,17 +331,17 @@ Say you want to store material settings for a custom engine.
if __name__ == "__main__":
register()
-.. note::
+.. important::
- *The lower most class needs to be registered first and that unregister() is a mirror of register()*
+ The lower most class needs to be registered first and that ``unregister()`` is a mirror of ``register()``.
Manipulating Classes
^^^^^^^^^^^^^^^^^^^^
Properties can be added and removed as Blender runs,
-normally happens on register or unregister but for some
-special cases it may be useful to modify types as the script runs.
+normally done on register or unregister but for some special cases
+it may be useful to modify types as the script runs.
For example:
@@ -362,7 +352,7 @@ For example:
# remove
del bpy.types.Object.my_float
-This works just as well for PropertyGroup subclasses you define yourself.
+This works just as well for ``PropertyGroup`` subclasses you define yourself.
.. code-block:: python
@@ -370,7 +360,7 @@ This works just as well for PropertyGroup subclasses you define yourself.
pass
MyPropGroup.my_float: bpy.props.FloatProperty()
-...this is equivalent to:
+This is equivalent to:
.. code-block:: python
@@ -378,11 +368,11 @@ This works just as well for PropertyGroup subclasses you define yourself.
my_float: bpy.props.FloatProperty()
-Dynamic Defined-Classes (Advanced)
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Dynamic Class Definition (Advanced)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-In some cases the specifier for data may not be in Blender, renderman shader definitions
-for example, and it may be useful to define them as types and remove them on the fly.
+In some cases the specifier for data may not be in Blender, for example a external render engines shader definitions,
+and it may be useful to define them as types and remove them on the fly.
.. code-block:: python
diff --git a/doc/python_api/rst/info_quickstart.rst b/doc/python_api/rst/info_quickstart.rst
index 926a0fd9f53..ed529b0fb57 100644
--- a/doc/python_api/rst/info_quickstart.rst
+++ b/doc/python_api/rst/info_quickstart.rst
@@ -1,19 +1,16 @@
-
.. _info_quickstart:
-***********************
-Quickstart Introduction
-***********************
-
-Preface
-=======
+**********
+Quickstart
+**********
-This API is generally stable but some areas are still being added and improved.
+This :abbr:`API (Application Programming Interface)` is generally stable
+but some areas are still being extended and improved.
-The Blender/Python API can do the following:
+.. rubric:: Blender Python API features:
- Edit any data the user interface can (Scenes, Meshes, Particles etc.).
-- Modify user preferences, key-maps and themes.
+- Modify user preferences, keymaps and themes.
- Run tools with own settings.
- Create user interface elements such as menus, headers and panels.
- Create new tools.
@@ -21,10 +18,10 @@ The Blender/Python API can do the following:
- Create new rendering engines that integrate with Blender.
- Subscribe to changes to data and it's properties.
- Define new settings in existing Blender data.
-- Draw in the 3D view using Python.
+- Draw in the 3D Viewport using Python.
-The Blender/Python API **can't** (yet)...
+.. rubric:: (Still) missing features:
- Create new space types.
- Assign custom properties to every type.
@@ -33,22 +30,21 @@ The Blender/Python API **can't** (yet)...
Before Starting
===============
-This document isn't intended to fully cover each topic.
-Rather, its purpose is to familiarize you with Blender Python API.
-
+This document its intended to familiarize you with Blender Python API
+but not to fully cover each topic.
A quick list of helpful things to know before starting:
-- Blender uses Python 3.x; some online documentation still assumes 2.x.
+- Blender uses Python 3.x; some online documentation still assumes version 2.x.
- The interactive console is great for testing one-liners.
It also has autocompletion so you can inspect the API quickly.
-- Button tool tips show Python attributes and operator names.
-- Right clicking on buttons and menu items directly links to API documentation.
-- For more examples, the text menu has a templates section where some example operators can be found.
+- Button tooltips show Python attributes and operator names.
+- The context menu of buttons directly links to this API documentation.
+- More operator examples can be found in the text editor's template menu.
- To examine further scripts distributed with Blender, see:
- | ``scripts/startup/bl_ui`` for the user interface,
- | ``scripts/startup/bl_operators`` for operators.
+ - ``scripts/startup/bl_ui`` for the user interface.
+ - ``scripts/startup/bl_operators`` for operators.
Exact location depends on platform, see:
:ref:`directory layout docs <blender_manual:blender-directory-layout>`.
@@ -59,19 +55,14 @@ Running Scripts
The two most common ways to execute Python scripts are using the built-in
text editor or entering commands in the Python console.
-
-Both the *Text Editor* and *Python Console* are space types you can select from the view header.
-
+Both the *Text Editor* and *Python Console* are space types you can select from the header.
Rather than manually configuring your spaces for Python development,
-you may prefer to use the *Scripting* screen, included default with Blender,
-accessible from the top headers screen selector.
+you can use the *Scripting* workspace accessible from the Topbar tabs.
From the text editor you can open ``.py`` files or paste then from the clipboard, then test using *Run Script*.
-
The Python Console is typically used for typing in snippets and for testing to get immediate feedback,
but can also have entire scripts pasted into it.
-
-Scripts can also run from the command line with Blender but to learn Blender/Python this isn't essential.
+Scripts can also run from the command line with Blender but to learn scripting in Blender this isn't essential.
Key Concepts
@@ -80,14 +71,13 @@ Key Concepts
Data Access
-----------
-Accessing DataBlocks
-^^^^^^^^^^^^^^^^^^^^
-
-Python accesses Blender's data in the same way as the animation system and user interface;
-this implies that any setting that can be changed via a button can also be changed from Python.
+Accessing Data-Blocks
+^^^^^^^^^^^^^^^^^^^^^
-Accessing data from the currently loaded blend file is done with the module :mod:`bpy.data`.
-This gives access to library data. For example:
+You can access Blender's data with the Python API in the same way as the animation system or user interface;
+this implies that any setting that can be changed via a button can also be changed with Python.
+Accessing data from the currently loaded blend-file is done with the module :mod:`bpy.data`.
+It gives access to library data, for example:
>>> bpy.data.objects
<bpy_collection[3], BlendDataObjects>
@@ -99,12 +89,11 @@ This gives access to library data. For example:
<bpy_collection[1], BlendDataMaterials>
-About Collections
-^^^^^^^^^^^^^^^^^
-
-You'll notice that an index as well as a string can be used to access members of the collection.
+Accessing Collections
+^^^^^^^^^^^^^^^^^^^^^
-Unlike Python's dictionaries, both methods are acceptable;
+You will notice that an index as well as a string can be used to access members of the collection.
+Unlike Python dictionaries, both methods are available;
however, the index of a member may change while running Blender.
>>> list(bpy.data.objects)
@@ -120,7 +109,7 @@ however, the index of a member may change while running Blender.
Accessing Attributes
^^^^^^^^^^^^^^^^^^^^
-Once you have a data block, such as a material, object, collections etc.,
+Once you have a data-block, such as a material, object, collection, etc.,
its attributes can be accessed much like you would change a setting using the graphical interface.
In fact, the tooltip for each button also displays the Python attribute
which can help in finding what settings to change in a script.
@@ -135,8 +124,8 @@ which can help in finding what settings to change in a script.
bpy.data.materials['MyMaterial']
-For testing what data to access it's useful to use the "Console", which is its own space type.
-This supports auto-complete, giving you a fast way to dig into different data in your file.
+For testing what data to access it's useful to use the Python Console, which is its own space type.
+This supports auto-complete, giving you a fast way to explore the data in your file.
Example of a data path that can be quickly found via the console:
@@ -149,8 +138,8 @@ Example of a data path that can be quickly found via the console:
Data Creation/Removal
^^^^^^^^^^^^^^^^^^^^^
-Those of you familiar with other Python API's may be surprised that
-new data-blocks in the bpy API can't be created by calling the class:
+When you are familiar with other Python APIs you may be surprised that
+new data-blocks in the bpy API cannot be created by calling the class:
>>> bpy.types.Mesh()
Traceback (most recent call last):
@@ -159,10 +148,10 @@ new data-blocks in the bpy API can't be created by calling the class:
This is an intentional part of the API design.
-The Blender/Python API can't create Blender data that exists outside the main Blender database
-(accessed through :mod:`bpy.data`), because this data is managed by Blender (save/load/undo/append... etc).
+The Blender Python API can't create Blender data that exists outside the main Blender database
+(accessed through :mod:`bpy.data`), because this data is managed by Blender (save, load, undo, append, etc).
-Data is added and removed via methods on the collections in :mod:`bpy.data`, eg:
+Data is added and removed via methods on the collections in :mod:`bpy.data`, e.g:
>>> mesh = bpy.data.meshes.new(name="MyMesh")
>>> print(mesh)
@@ -174,14 +163,12 @@ Data is added and removed via methods on the collections in :mod:`bpy.data`, eg:
Custom Properties
^^^^^^^^^^^^^^^^^
-Python can access properties on any datablock that has an ID
-(data that can be linked in and accessed from :mod:`bpy.data`.
-When assigning a property, you can make up your own names,
-these will be created when needed or overwritten if they exist.
-
-This data is saved with the blend file and copied with objects.
+Python can access properties on any data-block that has an ID
+(data that can be linked in and accessed from :mod:`bpy.data`).
+When assigning a property, you can pick your own names,
+these will be created when needed or overwritten if they already exist.
-Example:
+This data is saved with the blend-file and copied with objects, for example:
.. code-block:: python
@@ -201,10 +188,10 @@ Example:
del collection["MySettings"]
-Note that these properties can only be assigned basic Python types.
+Note that these properties can only be assigned basic Python types:
- int, float, string
-- array of ints/floats
+- array of ints or floats
- dictionary (only string keys are supported, values must be basic types too)
These properties are valid outside of Python. They can be animated by curves or used in driver paths.
@@ -218,18 +205,16 @@ it's more common to operate on the user's selection.
The context is always available from ``bpy.context`` and can be used to get the active object, scene,
tool settings along with many other attributes.
-Common-use cases:
+Some common use cases are:
>>> bpy.context.object
>>> bpy.context.selected_objects
>>> bpy.context.visible_bones
-Note that the context is read-only.
-These values cannot be modified directly,
-though they may be changed by running API functions or by using the data API.
+Note that the context is read-only, which means that these values cannot be modified directly.
+But they can be changed by running API functions or by using the data API.
So ``bpy.context.active_object = obj`` will raise an error.
-
But ``bpy.context.view_layer.objects.active = obj`` works as expected.
The context attributes change depending on where they are accessed.
@@ -257,7 +242,7 @@ Examples:
.. tip::
- The :ref:`Operator Cheat Sheet <blender_manual:bpy.ops.wm.operator_cheat_sheet>`.
+ The :ref:`Operator Cheat Sheet <blender_manual:bpy.ops.wm.operator_cheat_sheet>`
gives a list of all operators and their default values in Python syntax, along with the generated docs.
This is a good way to get an overview of all Blender's operators.
@@ -265,8 +250,8 @@ Examples:
Operator Poll()
^^^^^^^^^^^^^^^
-Many operators have a "poll" function which may check that the cursor
-is in a valid area or that the object is in the correct mode (Edit Mode, Weight Paint etc).
+Many operators have a "poll" function which checks if the cursor
+is in a valid area or if the object is in the correct mode (Edit Mode, Weight Paint Mode, etc).
When an operator's poll function fails within Python, an exception is raised.
For example, calling ``bpy.ops.view3d.render_border()`` from the console raises the following error:
@@ -275,10 +260,10 @@ For example, calling ``bpy.ops.view3d.render_border()`` from the console raises
RuntimeError: Operator bpy.ops.view3d.render_border.poll() failed, context is incorrect
-In this case the context must be the 3d view with an active camera.
+In this case the context must be the 3D Viewport with an active camera.
-To avoid using try/except clauses wherever operators are called you can call the operators
-own ``poll()`` function to check if it can run in the current context.
+To avoid using try-except clauses wherever operators are called, you can call the operators
+own ``poll()`` function to check if it can run the operator in the current context.
.. code-block:: python
@@ -291,11 +276,10 @@ Integration
Python scripts can integrate with Blender in the following ways:
-- By defining a rendering engine.
+- By defining a render engine.
- By defining operators.
- By defining menus, headers and panels.
-- By inserting new buttons into existing menus, headers and panels
-
+- By inserting new buttons into existing menus, headers and panels.
In Python, this is done by defining a class, which is a subclass of an existing type.
@@ -306,56 +290,52 @@ Example Operator
.. literalinclude:: __/__/__/release/scripts/templates_py/operator_simple.py
Once this script runs, ``SimpleOperator`` is registered with Blender
-and can be called from the operator search popup or added to the toolbar.
+and can be called from Operator Search or added to the toolbar.
To run the script:
-#. Highlight the above code then press :kbd:`Ctrl-C` to copy it.
-#. Start Blender
-#. Press :kbd:`Ctrl-Right` twice to change to the Scripting layout.
-#. Click the button labeled ``New`` and the confirmation pop up in order to create a new text block.
-#. Press :kbd:`Ctrl-V` to paste the code into the text panel (the upper left frame).
-#. Click on the button **Run Script**.
+#. Start Blender and switch to the Scripting workspace.
+#. Click the *New* button in the text editor to create a new text data-block.
+#. Copy the code from above and paste it into the text editor.
+#. Click on the *Run Script* button.
#. Move your cursor into the 3D Viewport,
- open the :ref:`operator search menu <blender_manual:bpy.ops.wm.search_menu>`,
+ open the :ref:`Operator Search menu <blender_manual:bpy.ops.wm.search_menu>`,
and type "Simple".
#. Click on the "Simple Operator" item found in search.
+.. seealso::
-.. seealso:: The class members with the ``bl_`` prefix are documented in the API
- reference :class:`bpy.types.Operator`
+ The class members with the ``bl_`` prefix are documented in the API reference :class:`bpy.types.Operator`.
.. note::
The output from the ``main`` function is sent to the terminal;
in order to see this, be sure to :ref:`use the terminal <use_the_terminal>`.
+
Example Panel
-------------
-Panels register themselves as a class, like an operator.
+Panels are registered as a class, like an operator.
Notice the extra ``bl_`` variables used to set the context they display in.
.. literalinclude:: __/__/__/release/scripts/templates_py/ui_panel_simple.py
To run the script:
-#. Highlight the above code then press :kbd:`Ctrl-C` to copy it.
-#. Start Blender.
-#. Click on the tab for the *Scripting* workspace.
-#. Click the button labeled ``New`` to create a new text block.
-#. Press :kbd:`Ctrl-V` to paste the code into the text panel (the upper left frame).
-#. Click on the button **Run Script**.
-
+#. Start Blender and switch to the Scripting workspace.
+#. Click the *New* button in the text editor to create a new text data-block.
+#. Copy the code from above and paste it into the text editor.
+#. Click on the *Run Script* button.
To view the results:
-#. Select the the default cube.
+#. Select the default cube.
#. Click on the Object properties icon in the buttons panel (far right; appears as a tiny cube).
-#. Scroll down to see a panel named **Hello World Panel**.
-#. Changing the object name also updates **Hello World Panel's** Name: field.
+#. Scroll down to see a panel named "Hello World Panel".
+#. Changing the object name also updates *Hello World Panel's* name: field.
-Note the row distribution and the label and properties that are available through the code.
+Note the row distribution and the label and properties that are defined through the code.
.. seealso:: :class:`bpy.types.Panel`
@@ -364,8 +344,7 @@ Types
=====
Blender defines a number of Python types but also uses Python native types.
-
-Blender's Python API can be split up into 3 categories.
+Blender's Python API can be split up into three categories.
Native Types
@@ -374,7 +353,7 @@ Native Types
In simple cases returning a number or a string as a custom type would be cumbersome,
so these are accessed as normal Python types.
-- Blender float/int/boolean -> float/int/boolean
+- Blender float, int, boolean -> float, int, boolean
- Blender enumerator -> string
>>> C.object.rotation_mode = 'AXIS_ANGLE'
@@ -393,11 +372,10 @@ so these are accessed as normal Python types.
Internal Types
--------------
-Used for Blender data-blocks and collections: :class:`bpy.types.bpy_struct`
-
-For data that contains its own attributes collections/meshes/bones/scenes... etc.
+:class:`bpy.types.bpy_struct` is used for Blender data-blocks and collections.
+Also for data that contains its own attributes: collections, meshes, bones, scenes, etc.
-There are 2 main types that wrap Blenders data, one for data-blocks
+There are two main types that wrap Blender's data, one for data-blocks
(known internally as ``bpy_struct``), another for properties.
>>> bpy.context.object
@@ -406,14 +384,13 @@ There are 2 main types that wrap Blenders data, one for data-blocks
>>> C.scene.objects
bpy.data.scenes['Scene'].objects
-Note that these types reference Blender's data so modifying them is immediately visible.
+Note that these types reference Blender's data so modifying them is visible immediately.
Mathutils Types
---------------
-Used for vectors, quaternion, eulers, matrix and color types, accessible from :mod:`mathutils`
-
+Accessible from :mod:`mathutils` are vectors, quaternions, Euler angles, matrix and color types.
Some attributes such as :class:`bpy.types.Object.location`,
:class:`bpy.types.PoseBone.rotation_euler` and :class:`bpy.types.Scene.cursor_location`
can be accessed as special math types which can be used together and manipulated in various useful ways.
@@ -422,14 +399,13 @@ Example of a matrix, vector multiplication:
.. code-block:: python
- bpy.context.object.matrix_world * bpy.context.object.data.verts[0].co
+ bpy.context.object.matrix_world @ bpy.context.object.data.verts[0].co
.. note::
mathutils types keep a reference to Blender's internal data so changes can
be applied back.
-
Example:
.. code-block:: python
@@ -449,13 +425,11 @@ Example of a matrix, vector multiplication:
Animation
=========
-There are 2 ways to add keyframes through Python.
+There are two ways to add keyframes through Python.
-The first is through key properties directly, which is similar to inserting a keyframe from the button as a user.
+The first is through key properties directly, which is like inserting a keyframe from the button as a user.
You can also manually create the curves and keyframe data, then set the path to the property.
-Here are examples of both methods.
-
-Both examples insert a keyframe on the active object's Z axis.
+Here are examples of both methods. Both insert a keyframe on the active object's Z axis.
Simple example:
@@ -467,7 +441,7 @@ Simple example:
obj.location[2] = 1.0
obj.keyframe_insert(data_path="location", frame=20.0, index=2)
-Using Low-Level Functions:
+Using low-level functions:
.. code-block:: python
diff --git a/doc/python_api/rst/info_tips_and_tricks.rst b/doc/python_api/rst/info_tips_and_tricks.rst
index 7a2e8206c72..0b5e1910902 100644
--- a/doc/python_api/rst/info_tips_and_tricks.rst
+++ b/doc/python_api/rst/info_tips_and_tricks.rst
@@ -4,23 +4,22 @@ Tips and Tricks
***************
Here are various suggestions that you might find useful when writing scripts.
-
-Some of these are just Python features that scripters may not have thought to use with Blender,
-others are Blender specific.
+Some of these are just Python features that you may not have thought to use with Blender,
+others are Blender-specific.
.. _use_the_terminal:
-Use The Terminal
+Use the Terminal
================
When writing Python scripts, it's useful to have a terminal open,
this is not the built-in Python console but a terminal application which is used to start Blender.
-There are 3 main uses for the terminal, these are:
+The three main use cases for the terminal are:
- You can see the output of ``print()`` as your script runs, which is useful to view debug info.
-- The error trace-back is printed in full to the terminal which won't always generate an error popup in
+- The error traceback is printed in full to the terminal which won't always generate an report message in
Blender's user interface (depending on how the script is executed).
- If the script runs for too long or you accidentally enter an infinite loop,
:kbd:`Ctrl-C` in the terminal (:kbd:`Ctrl-Break` on Windows) will quit the script early.
@@ -28,26 +27,25 @@ There are 3 main uses for the terminal, these are:
.. note::
For Linux and macOS users this means starting the terminal first, then running Blender from within it.
- On Windows the terminal can be enabled from the help menu.
+ On Windows the terminal can be enabled from the Help menu.
Interface Tricks
================
-
Access Operator Commands
------------------------
-You may have noticed that the tooltip for menu items and buttons includes the ``bpy.ops.[...])`` command
+You may have noticed that the tooltip for menu items and buttons includes the ``bpy.ops.[...]`` command
to run that button, a handy (hidden) feature is that you can press :kbd:`Ctrl-C` over
-any menu item/button to copy this command into the clipboard.
+any menu item or button to copy this command into the clipboard.
Access Data Path
----------------
-To find the path from an :class:`ID` datablock to its setting isn't always so simple since it may be nested away.
-To get this quickly you can right click on the setting and select select **Copy Data Path**,
+To find the path from an :class:`ID` data-block to its setting isn't always so simple since it may be nested away.
+To get this quickly open the context menu of the setting and select *Copy Data Path*,
if this can't be generated, only the property name is copied.
.. note::
@@ -62,11 +60,10 @@ if this can't be generated, only the property name is copied.
Show All Operators
==================
-While Blender logs operators in the Info space,
-this only reports operators with the ``REGISTER`` option enabeld so as not to flood the *Info* view
+While Blender logs operators in the Info editor,
+this only reports operators with the ``REGISTER`` option enabled so as not to flood the *Info* view
with calls to ``bpy.ops.view3d.smoothview`` and ``bpy.ops.view3d.zoom``.
-
-However, for testing it can be useful to see **every** operator called in a terminal,
+Yet for testing it can be useful to see **every** operator called in a terminal,
do this by enabling the debug option either by passing the ``--debug-wm`` argument when starting Blender
or by setting :mod:`bpy.app.debug_wm` to ``True`` while Blender is running.
@@ -74,20 +71,18 @@ or by setting :mod:`bpy.app.debug_wm` to ``True`` while Blender is running.
Use an External Editor
======================
-Blenders text editor is fine for small changes and writing tests but its not full featured,
+Blender's text editor is fine for small changes and writing tests but its not full featured,
for larger projects you'll probably want to use a standalone editor or Python IDE.
-
-Editing a text file externally and having the same text open in Blender does work but isn't that optimal
-so here are 2 ways you can easily use an external file from Blender.
-
-Using the following examples you'll still need textblock in Blender to execute,
+Editing a text file externally and having the same text open in Blender does work
+but isn't that optimal so here are two ways you can use an external file from Blender.
+Using the following examples you'll still need text data-block in Blender to execute,
but reference an external file rather than including it directly.
Executing External Scripts
--------------------------
-This is the equivalent to running the script directly, referencing a scripts path from a 2 line text-block.
+This is the equivalent to running the script directly, referencing a scripts path from a two line code block.
.. code-block:: python
@@ -95,7 +90,7 @@ This is the equivalent to running the script directly, referencing a scripts pat
exec(compile(open(filename).read(), filename, 'exec'))
-You might want to reference a script relative to the blend file.
+You might want to reference a script relative to the blend-file.
.. code-block:: python
@@ -128,9 +123,10 @@ has to call a function in the module, in this case ``main()`` but it can be any
an advantage with this is you can pass arguments to the function from this
small script which is often useful for testing different settings quickly.
-The other issue with this is the script has to be in Pythons module search path.
-While this is not best practice - for testing you can extend the search path,
-this example adds the current blend files directory to the search path, then loads the script as a module.
+The other issue with this is the script has to be in Python's module search path.
+While this is not best practice -- for testing purposes you can extend the search path,
+this following example adds the current blend-files directory to the search path
+and then loads the script as a module.
.. code-block:: python
@@ -148,42 +144,40 @@ this example adds the current blend files directory to the search path, then loa
myscript.main()
-Don't Use Blender!
-==================
-
-While developing your own scripts Blenders interface can get in the way,
-manually reloading, running the scripts, opening file import etc. adds overhead.
+Use Blender without it's User Interface
+=======================================
+While developing your own scripts Blender's interface can get in the way,
+manually reloading, running the scripts, opening file import, etc. adds overhead.
For scripts that are not interactive it can end up being more efficient not to use
-Blenders interface at all and instead execute the script on the command line.
+Blender's interface at all and instead execute the script on the command line.
.. code-block:: sh
blender --background --python myscript.py
-You might want to run this with a blend file so the script has some data to operate on.
+You might want to run this with a blend-file so the script has some data to operate on.
.. code-block:: sh
blender myscene.blend --background --python myscript.py
-
.. note::
Depending on your setup you might have to enter the full path to the Blender executable.
Once the script is running properly in background mode, you'll want to check the output of the script,
-this depends completely on the task at hand however here are some suggestions.
+this depends completely on the task at hand, however, here are some suggestions:
-- render the output to an image, use an image viewer and keep writing over the same image each time.
-- save a new blend file, or export the file using one of Blenders exporters.
-- if the results can be displayed as text - print them or write them to a file.
+- Render the output to an image, use an image viewer and keep writing over the same image each time.
+- Save a new blend-file, or export the file using one of Blender's exporters.
+- If the results can be displayed as text then print them or write them to a file.
While this can take a little time to setup, it can be well worth the effort
-to reduce the time it takes to test changes - you can even have
+to reduce the time it takes to test changes. You can even have
Blender running the script every few seconds with a viewer updating the results,
so no need to leave your text editor to see changes.
@@ -200,7 +194,7 @@ but to quickly setup your own custom pipeline or writing one-off scripts this ca
Examples include:
-- Run The Gimp in batch mode to execute custom scripts for advanced image processing.
+- Run Gimp in batch mode to execute custom scripts for advanced image processing.
- Write out 3D models to use external mesh manipulation tools and read back in the results.
- Convert files into recognizable formats before reading.
@@ -209,15 +203,16 @@ Bundled Python & Extensions
===========================
The Blender releases distributed from blender.org include a complete Python installation on all platforms,
-this has the disadvantage that any extensions you have installed in your systems Python won't be found by Blender.
+this has the disadvantage that any extensions you have installed on your system's Python environment
+will not be found by Blender.
-There are 2 ways around this:
+There are two ways to work around this:
-- Remove Blender Python sub-directory, Blender will then fallback on the systems Python and use that instead.
+- Remove Blender Python subdirectory, Blender will then fallback on the system's Python and use that instead.
Depending on your platform,
- you may need to explicitly reference the location of your Python installation using the
- ``PYTHONPATH`` environment variable, eg:
+ you may need to explicitly reference the location of your Python installation using
+ the ``PYTHONPATH`` environment variable, e.g:
.. code-block:: sh
@@ -226,21 +221,21 @@ There are 2 ways around this:
.. warning::
The Python (major, minor) version must match the one that Blender comes with.
- Therefor can't use Python 3.6 with Blender built to use Python 3.7.
+ Therefor you can't use Python 3.6 with Blender built to use Python 3.7.
-- Copy or link the extensions into Blender's Python sub-directory so Blender can access them,
- you could also copy the entire Python installation into Blenders sub-directory,
+- Copy or link the extensions into Blender's Python subdirectory so Blender can access them,
+ you can also copy the entire Python installation into Blender's subdirectory,
replacing the one Blender comes with.
This works as long as the Python versions match and the paths are created in the same relative locations.
Doing this has the advantage that you can redistribute this bundle to others with Blender
including any extensions you rely on.
-Drop Into a Python Interpreter in Your Script
-=============================================
+Insert a Python Interpreter into your Script
+============================================
-In the middle of a script you may want to inspect some variables,
-run some function and generally dig about to see what's going on.
+In the middle of a script you may want to inspect variables,
+run functions and inspect the flow.
.. code-block:: python
@@ -248,7 +243,7 @@ run some function and generally dig about to see what's going on.
code.interact(local=locals())
-If you want to access both global and local variables do this...
+If you want to access both global and local variables run this:
.. code-block:: python
@@ -266,7 +261,7 @@ The next example is an equivalent single line version of the script above which
``code.interact`` can be added at any line in the script
-and will pause the script an launch an interactive interpreter in the terminal,
+and will pause the script to launch an interactive interpreter in the terminal,
when you're done you can quit the interpreter and the script will continue execution.
@@ -279,13 +274,14 @@ The IPython prompt has auto-complete and some useful features that the standard
IPython.embed()
-Admittedly this highlights the lack of any Python debugging support built into Blender, but its still handy to know.
+Admittedly this highlights the lack of any Python debugging support built into Blender,
+but its still a handy thing to know.
+
Advanced
========
-
-Blender as a module
+Blender as a Module
-------------------
From a Python perspective it's nicer to have everything as an extension
@@ -293,26 +289,22 @@ which lets the Python script combine many components.
Advantages include:
-- you can use external editors/IDE's with Blenders Python API and execute scripts within the IDE
+- You can use external editors or IDEs with Blender's Python API and execute scripts within the IDE
(step over code, inspect variables as the script runs).
-- editors/IDE's can auto complete Blender modules & variables.
-- existing scripts can import Blender API's without having to run inside Blender.
-
+- Editors or IDEs can auto-complete Blender modules and variables.
+- Existing scripts can import Blender APIs without having to be run inside of Blender.
This is marked advanced because to run Blender as a Python module requires a special build option.
-
For instructions on building see
-`Building Blender as a Python module <https://wiki.blender.org/wiki/Building_Blender/Other/BlenderAsPyModule>`_
+`Building Blender as a Python module <https://wiki.blender.org/wiki/Building_Blender/Other/BlenderAsPyModule>`__.
Python Safety (Build Option)
----------------------------
-Since it's possible to access data which has been removed (see Gotcha's),
-this can be hard to track down the cause of crashes.
-
+Since it's possible to access data which has been removed (see :doc:`Gotchas <info_gotcha>`),
+can make it hard to track down the cause of crashes.
To raise Python exceptions on accessing freed data (rather than crashing),
enable the CMake build option ``WITH_PYTHON_SAFETY``.
-
-This enables data tracking which makes data access about 2x slower
+This enables data tracking which makes data access about two times slower
which is why the option isn't enabled in release builds.
diff --git a/doc/python_api/sphinx_doc_gen.py b/doc/python_api/sphinx_doc_gen.py
index d4cc1d37262..1b2f22217ca 100644
--- a/doc/python_api/sphinx_doc_gen.py
+++ b/doc/python_api/sphinx_doc_gen.py
@@ -83,6 +83,8 @@ import inspect
import shutil
import logging
+from textwrap import indent
+
from platform import platform
PLATFORM = platform().split('-')[0].lower() # 'linux', 'darwin', 'windows'
@@ -357,7 +359,7 @@ INFO_DOCS = (
("info_tips_and_tricks.rst",
"Tips and Tricks: Hints to help you while writing scripts for Blender"),
("info_gotcha.rst",
- "Gotcha's: some of the problems you may come up against when writing scripts"),
+ "Gotcha's: some of the problems you may encounter when writing scripts"),
("change_log.rst", "List of changes since last Blender release"),
)
@@ -438,25 +440,30 @@ if ARGS.sphinx_build:
if ARGS.log:
SPHINX_BUILD_LOG = os.path.join(ARGS.output_dir, ".sphinx-build.log")
- SPHINX_BUILD = ["sphinx-build",
- "-w", SPHINX_BUILD_LOG,
- SPHINX_IN, SPHINX_OUT]
+ SPHINX_BUILD = [
+ "sphinx-build",
+ "-w", SPHINX_BUILD_LOG,
+ SPHINX_IN, SPHINX_OUT,
+ ]
# pdf build
if ARGS.sphinx_build_pdf:
SPHINX_OUT_PDF = os.path.join(ARGS.output_dir, "sphinx-out_pdf")
- SPHINX_BUILD_PDF = ["sphinx-build",
- "-b", "latex",
- SPHINX_IN, SPHINX_OUT_PDF]
+ SPHINX_BUILD_PDF = [
+ "sphinx-build",
+ "-b", "latex",
+ SPHINX_IN, SPHINX_OUT_PDF,
+ ]
SPHINX_MAKE_PDF = ["make", "-C", SPHINX_OUT_PDF]
SPHINX_MAKE_PDF_STDOUT = None
if ARGS.log:
SPHINX_BUILD_PDF_LOG = os.path.join(ARGS.output_dir, ".sphinx-build_pdf.log")
- SPHINX_BUILD_PDF = ["sphinx-build", "-b", "latex",
- "-w", SPHINX_BUILD_PDF_LOG,
- SPHINX_IN, SPHINX_OUT_PDF]
-
+ SPHINX_BUILD_PDF = [
+ "sphinx-build", "-b", "latex",
+ "-w", SPHINX_BUILD_PDF_LOG,
+ SPHINX_IN, SPHINX_OUT_PDF,
+ ]
sphinx_make_pdf_log = os.path.join(ARGS.output_dir, ".latex_make.log")
SPHINX_MAKE_PDF_STDOUT = open(sphinx_make_pdf_log, "w", encoding="utf-8")
@@ -1026,6 +1033,7 @@ context_type_map = {
"gpencil": ("GreasePencil", False),
"gpencil_data": ("GreasePencil", False),
"gpencil_data_owner": ("ID", False),
+ "hair": ("Hair", False),
"image_paint_object": ("Object", False),
"lattice": ("Lattice", False),
"light": ("Light", False),
@@ -1042,6 +1050,7 @@ context_type_map = {
"particle_settings": ("ParticleSettings", False),
"particle_system": ("ParticleSystem", False),
"particle_system_editable": ("ParticleSystem", False),
+ "pointcloud": ("PointCloud", False),
"pose_bone": ("PoseBone", False),
"pose_object": ("Object", False),
"scene": ("Scene", False),
@@ -1196,12 +1205,15 @@ def pyrna_enum2sphinx(prop, use_empty_descriptions=False):
break
if ok:
- return "".join(["* ``%s`` %s.\n" %
- (identifier,
- ", ".join(escape_rst(val) for val in (name, description) if val),
- )
- for identifier, name, description in prop.enum_items
- ])
+ return "".join([
+ "* ``%s``\n"
+ "%s.\n" % (
+ identifier,
+ # Account for multi-line enum descriptions, allowing this to be a block of text.
+ indent(", ".join(escape_rst(val) for val in (name, description) if val) or "Undocumented", " "),
+ )
+ for identifier, name, description in prop.enum_items
+ ])
else:
return ""
@@ -1268,7 +1280,7 @@ def pyrna2sphinx(basepath):
fw(ident + ":%s%s:\n\n" % (id_name, identifier))
if prop.name or prop.description:
- fw(ident + " " + ", ".join(val for val in (prop.name, prop.description) if val) + "\n\n")
+ fw(indent(", ".join(val for val in (prop.name, prop.description) if val), ident + " ") + "\n\n")
# special exception, can't use generic code here for enums
if enum_text:
@@ -1714,7 +1726,7 @@ class PatchedPythonDomain(PythonDomain):
# end workaround
fw("def setup(app):\n")
- fw(" app.add_stylesheet('css/theme_overrides.css')\n")
+ fw(" app.add_css_file('css/theme_overrides.css')\n")
fw(" app.add_domain(PatchedPythonDomain, override=True)\n\n")
file.close()
diff --git a/doc/python_api/sphinx_doc_update.py b/doc/python_api/sphinx_doc_update.py
index 4495fca9274..71b49d4949d 100755
--- a/doc/python_api/sphinx_doc_update.py
+++ b/doc/python_api/sphinx_doc_update.py
@@ -118,20 +118,19 @@ def main():
# III) Get Blender version info.
getver_file = os.path.join(tmp_dir, "blendver.txt")
- getver_script = (
- "import sys, bpy\n"
- "with open(sys.argv[-1], 'w') as f:\n"
- " is_release = bpy.app.version_cycle in {'rc', 'release'}\n"
- " is_beta = bpy.app.version_cycle in {'beta'}\n"
- " branch = bpy.app.build_branch.split()[0].decode()\n"
- " f.write('%d\\n' % is_release)\n"
- " f.write('%d\\n' % is_beta)\n"
- " f.write('%s\\n' % branch)\n"
- " f.write('%d.%d\\n' % (bpy.app.version[0], bpy.app.version[1]))\n"
- " f.write('%d.%d\\n' % (bpy.app.version[0], bpy.app.version[1])\n"
- " if (is_release or is_beta) else '%s\\n' % branch)\n"
- " f.write('%d_%d' % (bpy.app.version[0], bpy.app.version[1]))\n"
- )
+ getver_script = (r"""import sys, bpy
+with open(sys.argv[-1], 'w') as f:
+ is_release = bpy.app.version_cycle in {'rc', 'release'}
+ is_beta = bpy.app.version_cycle in {'beta'}
+ branch = bpy.app.build_branch.split()[0].decode()
+ f.write('%d\n' % is_release)
+ f.write('%d\n' % is_beta)
+ f.write('%s\n' % branch)
+ f.write('%d.%d\n' % (bpy.app.version[0], bpy.app.version[1]))
+ f.write('%d.%d\n' % (bpy.app.version[0], bpy.app.version[1])
+ if (is_release or is_beta) else '%s\n' % branch)
+ f.write('%d_%d' % (bpy.app.version[0], bpy.app.version[1]))
+""")
get_ver_cmd = (args.blender, "--background", "-noaudio", "--factory-startup", "--python-exit-code", "1",
"--python-expr", getver_script, "--", getver_file)
subprocess.run(get_ver_cmd)
diff --git a/extern/ceres/CMakeLists.txt b/extern/ceres/CMakeLists.txt
index 009445ea690..51cf9657319 100644
--- a/extern/ceres/CMakeLists.txt
+++ b/extern/ceres/CMakeLists.txt
@@ -37,223 +37,279 @@ set(INC_SYS
)
set(SRC
- 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/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/coordinate_descent_minimizer.cc
- internal/ceres/corrector.cc
- internal/ceres/covariance.cc
- internal/ceres/covariance_impl.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/evaluator.cc
- internal/ceres/file.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/is_close.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/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/scratch_evaluate_preparer.cc
- internal/ceres/solver.cc
- internal/ceres/solver_utils.cc
- internal/ceres/sparse_matrix.cc
- internal/ceres/sparse_normal_cholesky_solver.cc
- internal/ceres/split.cc
- internal/ceres/stringprintf.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/wall_time.cc
+ 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_local_parameterization.h
- include/ceres/c_api.h
- include/ceres/ceres.h
- include/ceres/conditioned_cost_function.h
- include/ceres/cost_function.h
- include/ceres/cost_function_to_functor.h
- include/ceres/covariance.h
- include/ceres/crs_matrix.h
- include/ceres/dynamic_autodiff_cost_function.h
- include/ceres/dynamic_cost_function_to_functor.h
- include/ceres/dynamic_numeric_diff_cost_function.h
- include/ceres/fpclassify.h
- include/ceres/gradient_checker.h
- include/ceres/gradient_problem.h
- include/ceres/gradient_problem_solver.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/macros.h
- include/ceres/internal/manual_constructor.h
- include/ceres/internal/numeric_diff.h
- include/ceres/internal/port.h
- include/ceres/internal/reenable_warnings.h
- include/ceres/internal/scoped_ptr.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/types.h
- include/ceres/version.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/casts.h
- internal/ceres/cgnr_linear_operator.h
- internal/ceres/cgnr_solver.h
- internal/ceres/collections_port.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/conjugate_gradients_solver.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/evaluator.h
- internal/ceres/execution_summary.h
- internal/ceres/file.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/householder_vector.h
- internal/ceres/implicit_schur_complement.h
- internal/ceres/integral_types.h
- internal/ceres/is_close.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/mutex.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/scratch_evaluate_preparer.h
- internal/ceres/small_blas.h
- internal/ceres/solver_utils.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/suitesparse.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/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
)
set(LIB
@@ -263,44 +319,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_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_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_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_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)
@@ -315,13 +375,9 @@ add_definitions(
-DCERES_NO_SUITESPARSE
-DCERES_NO_CXSPARSE
-DCERES_NO_LAPACK
+ -DCERES_NO_ACCELERATE_SPARSE
-DCERES_HAVE_RWLOCK
+ -DCERES_USE_CXX_THREADS
)
-if(WITH_OPENMP)
- add_definitions(
- -DCERES_USE_OPENMP
- )
-endif()
-
blender_add_lib(extern_ceres "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/extern/ceres/ChangeLog b/extern/ceres/ChangeLog
index ae8d42a7c95..64c75e572f4 100644
--- a/extern/ceres/ChangeLog
+++ b/extern/ceres/ChangeLog
@@ -1,588 +1,587 @@
-commit 8590e6e8e057adba4ec0083446d00268565bb444
-Author: Sameer Agarwal <sameeragarwal@google.com>
-Date: Thu Oct 27 12:29:37 2016 -0700
+commit e39d9ed1d60dfeb58dd2a0df4622c683f87b28e3
+Author: Carl Dehlin <carl@dehlin.com>
+Date: Tue Jun 16 09:02:05 2020 +0200
- Remove two checks from rotation.h
-
- This allows rotation.h to remove its dependency on glog.
+ Add a missing term and remove a superfluous word
- Change-Id: Ia6aede93ee51a4bd4039570dc8edd100a7045329
+ Change-Id: I25f40f0bf241302b975e6fc14690aa863c0728b0
-commit e892499e8d8977b9178a760348bdd201ec5f3489
-Author: Je Hyeong Hong <jhh37@outlook.com>
-Date: Tue Oct 18 22:49:11 2016 +0100
+commit 27cab77b699a1a2b5354820c57a91c92eaeb21e3
+Author: Carl Dehlin <carl@dehlin.com>
+Date: Mon Jun 15 20:01:18 2020 +0200
- Relax the tolerance in QuaternionParameterizationTestHelper.
-
- This commit relaxes the tolerance value for comparing between the actual
- local matrix and the expected local matrix. Without this fix,
- EigenQuaternionParameterization.ZeroTest could fail as the difference
- exactly matches the value of std::numeric_limits<double>::epsilon().
+ Reformulate some sentences
- Change-Id: Ic4d3f26c0acdf5f16fead80dfdc53df9e7dabbf9
+ Change-Id: I4841aa8e8522008dd816261d9ad98e5fb8ad1758
-commit 7ed9e2fb7f1dff264c5e4fbaa89ee1c4c99df269
-Author: Sameer Agarwal <sameeragarwal@google.com>
-Date: Wed Oct 19 04:45:23 2016 -0700
+commit 8ac6655ce85a4462f2882fcb9e9118a7057ebe09
+Author: Carl Dehlin <carl@dehlin.com>
+Date: Mon Jun 15 19:10:12 2020 +0200
- Occured -> Occurred.
-
- Thanks to Phillip Huebner for reporting this.
+ Fix documentation formatting issues
- Change-Id: I9cddfbb373aeb496961d08e434fe661bff4abd29
+ Change-Id: Iea3a6e75dc3a7376eda866ab24e535a6df84f8ea
-commit b82f97279682962d8c8ae1b6d9e801ba072a0ab1
-Author: Je Hyeong Hong <jhh37@outlook.com>
-Date: Tue Oct 18 21:18:32 2016 +0100
+commit 7ef83e07592ead74eeacc227b642df1959d2a246
+Author: Alex Stewart <alexs.mac@gmail.com>
+Date: Sat May 30 11:30:01 2020 +0100
- Fix a test error in autodiff_test.cc.
+ Update minimum required C++ version for Ceres to C++14
- Previously, the test for the projective camera model would fail as no
- tolerance is set in line 144. To resolve this, this commit changes
- assert_equal to assert_near.
+ - Removes all workarounds for pre-C++14 versions
+ - Removes '11' qualifier from C++ threading option and associated
+ defines.
+ - Fix missing inclusion of 'Multithreading' in reported Ceres components
+ when C++ threading model is enabled.
+ - Update Sphinx documentation to specify C++14 as minimum requirement.
- Change-Id: I6cd3379083b1a10c7cd0a9cc83fd6962bb993cc9
+ Change-Id: I706c8b367b3221e3c4d1a0aaf669a8f9c911e438
-commit 5690b447de5beed6bdda99b7f30f372283c2fb1a
+commit 1d75e7568172dc5a4dc97937dcf66e0f5d28272c
Author: Sameer Agarwal <sameeragarwal@google.com>
-Date: Thu Oct 13 09:52:02 2016 -0700
+Date: Mon May 25 18:09:50 2020 -0700
- Fix documentation source for templated functions in rotation.h
+ Improve documentation for LocalParameterization
- Change-Id: Ic1b2e6f0e6eb9914f419fd0bb5af77b66252e57c
+ Change-Id: I63fa81206e67bfac56cc42bf2bb4915a3a11332b
-commit 2f8f98f7e8940e465de126fb51282396f42bea20
+commit 763398ca4ed56952f48c48df6a98e277e3e05381
Author: Sameer Agarwal <sameeragarwal@google.com>
-Date: Thu Oct 13 09:35:18 2016 -0700
+Date: Mon May 25 12:12:03 2020 -0700
- Prepare for 1.12.0RC1
+ Update the section on Preconditioners
- Change-Id: I23eaf0b46117a01440143001b74dacfa5e57cbf0
-
-commit 55c12d2e9569fe4aeac3ba688ac36810935a37ba
-Author: Damon Kohler <damonkohler@google.com>
-Date: Wed Oct 5 16:30:31 2016 +0200
-
- Adds package.xml to support Catkin.
+ Re-organize the section, add some more references and details for
+ existing preconditioners and add documentation for the SUBSET
+ precondition.
- Change-Id: I8ad4d36a8b036417604a54644e0bb70dd1615feb
-
-commit 0bcce6565202f5476e40f12afc0a99eb44bd9dfb
-Author: Sameer Agarwal <sameeragarwal@google.com>
-Date: Mon Oct 10 23:30:42 2016 -0700
-
- Fix tabs in Android.mk
+ https://github.com/ceres-solver/ceres-solver/issues/490
- Change-Id: Ie5ab9a8ba2b727721565e1ded242609b6df5f8f5
+ Change-Id: I93d0af819c160f5e4ce48b18202f629ddb92ca7b
-commit e6ffe2667170d2fc435443685c0163396fc52d7b
+commit a614f788a34ea86dd9f679b779ffbf920db45aa6
Author: Sameer Agarwal <sameeragarwal@google.com>
-Date: Mon Oct 10 22:47:08 2016 -0700
+Date: Fri May 22 13:52:53 2020 -0700
- Update the version history.
+ Call EvaluationCallback before evaluating the fixed cost.
- Change-Id: I9a57b0541d6cebcb695ecb364a1d4ca04ea4e06c
-
-commit 0a4ccb7ee939ab35b22e26758401e039b033b176
-Author: David Gossow <dgossow@google.com>
-Date: Wed Sep 7 21:38:12 2016 +0200
-
- Relaxing Jacobian matching in Gradient Checker test.
+ Fixe a subtle bug in Program::RemoveFixedBlocks, where we call
+ ResidualBlock::Evaluate on residual blocks with all constant parameter
+ blocks without paying attention to the presence of an
+ EvaluationCallback.
+
+ In the process also run clang-format on some of the files touched by
+ this change.
- Any result of an arithmetic operation on floating-point matrices
- should never be checked for strict equality with some expected
- value, due to limited floating point precision on different machines.
- This fixes some occurences of exact checks in the gradient checker
- unit test that were causing problems on some platforms.
+ https://github.com/ceres-solver/ceres-solver/issues/482
- Change-Id: I48e804c9c705dc485ce74ddfe51037d4957c8fcb
+ Change-Id: I342b66f6f975fdee2eef139a31f24d4a3e568e84
-commit ee44fc91b59584921c1d1c8db153fda6d633b092
-Author: Je Hyeong Hong <jhh37@outlook.com>
-Date: Mon Oct 3 12:19:30 2016 +0100
+commit 70308f7bb9cac560db250262079c0f8b030b9d6b
+Author: Sameer Agarwal <sameeragarwal@google.com>
+Date: Tue May 26 06:12:13 2020 -0700
- Fix an Intel compiler error in covariance_impl.cc.
+ Simplify documentation generation.
- Intel C compiler strictly asks for parallel loops with collapse to be
- perfectly nested. Otherwise, compiling Ceres with ICC will throw an
- error at line 348 of covariance_impl.cc.
+ 1. The MathJax font configuration is moved into conf.py and removed
+ from make_docs.py along with better font sizing.
+ 2. Remove the bread crumb replacement as it is not working anymore.
+ 3. Fix a parsing error in nnls_modeling.rst which the new version of
+ sphinx barfed on.
- Change-Id: I1ecb68e89b7faf79e4153dfe6675c390d1780db4
+ Change-Id: Ia3c2e732323a8b5cabafe851ac5ca0f0c82da071
-commit 9026d69d1ce1e0bcd21debd54a38246d85c7c6e4
-Author: Sameer Agarwal <sameeragarwal@google.com>
-Date: Thu Sep 22 17:20:14 2016 -0700
-
- Allow SubsetParameterization to hold all parameters constant
-
- 1. SubsetParameterization can now be constructed such that all
- parameters are constant. This is required for it be used as part
- of a ProductParameterization to hold a part of parameter block
- constant. For example, a parameter block consisting of a rotation
- as a quaternion and a translation vector can now have a local
- parameterization where the translation part is constant and the
- quaternion part has a QuaternionParameterization associated with it.
-
- 2. The check for the tangent space of a parameterization being
- positive dimensional. We were not doing this check up till now
- and the user could accidentally create parameterizations like this
- and create a problem for themselves. This will ensure that even
- though one can construct a SubsetParameterization where all
- parameters are constant, you cannot actually use it as a local
- parameterization for an entire parameter block. Which is how
- it was before, but the check was inside the SubsetParameterization
- constructor.
-
- 3. Added more tests and refactored existing tests to be more
- granular.
-
- Change-Id: Ic0184a1f30e3bd8a416b02341781a9d98e855ff7
-
-commit a36693f83da7a3fd19dce473d060231d4cc97499
+commit e886d7e65368e73e9d35c2ead895d81ced677977
Author: Sameer Agarwal <sameeragarwal@google.com>
-Date: Sat Sep 17 16:31:41 2016 -0700
+Date: Mon May 25 13:09:39 2020 -0700
- Update version history
+ Reduce the number of minimizer iterations in evaluation_callback_test.cc
- Change-Id: Ib2f0138ed7a1879ca3b2173e54092f7ae8dd5c9d
+ This should reduce the probability of the test heuristic failing due
+ to floating point issues.
+
+ https://github.com/ceres-solver/ceres-solver/issues/562
+ https://github.com/ceres-solver/ceres-solver/issues/392
+
+ Change-Id: I8ccf4164a8d595f5930d378f464313d4a2cae419
-commit 01e23e3d33178fdd050973666505c1080cfe04c3
-Author: David Gossow <dgossow@google.com>
-Date: Thu Sep 8 12:22:28 2016 +0200
+commit 9483e6f2f57bf51bad7cefd155cd5b48ca672c63
+Author: Sameer Agarwal <sameeragarwal@google.com>
+Date: Tue May 12 05:16:32 2020 -0700
- Removing duplicate include directive.
+ Simplify DynamicCompressedRowJacobianWriter::Write
- Change-Id: I729ae6501497746d1bb615cb893ad592e16ddf3f
+ Change-Id: I67aa2959bd479909b5cada79359c5cfdb8a37ef7
-commit 99b8210cee92cb972267537fb44bebf56f812d52
+commit 323cc55bb92a513924e566f487b54556052a716f
Author: Sameer Agarwal <sameeragarwal@google.com>
-Date: Wed Sep 7 15:31:30 2016 -0700
+Date: Mon May 25 10:38:35 2020 -0700
- Update Android.mk to include new files.
+ Update the version in package.xml to 2.0.0.
- Change-Id: Id543ee7d2a65b65c868554a17f593c0a4958e873
+ Change-Id: Ibac053916520e8c597c875a8c7f5668bb35b6ba1
-commit 195d8d13a6a3962ac39ef7fcdcc6add0216eb8bc
-Author: Sameer Agarwal <sameeragarwal@google.com>
-Date: Tue Sep 6 07:12:23 2016 -0700
+commit 303b078b50bd3311a9c86fc256be3e9f2f334411
+Author: Bayes Nie <niebayes@gmail.com>
+Date: Sun May 24 16:08:52 2020 +0800
- Remove two DCHECKs from CubicHermiteSpline.
+ Fix few typos and alter a NULL to nullptr.
- They were present as debugging checks but were causing problems
- with the build on 32bit i386 due to numerical cancellation issues,
- where x ~ -epsilon.
+ Fix typos in docs/source/features.rst and examples/helloworld.cc. Alter a NULL to nullptr in include/ceres/autodiff_cost_function.h
- Removing these checks only changes the behaviour in Debug mode.
- We are already handling such small negative numbers in production
- if they occur. All that this change does is to remove the crash.
+ Change-Id: Ibcf00b6ef665ad6be9af14b3add2dd4f3852e7e6
+
+commit cca93fed63dd4117f3d6dd5339131fc7674e6e0a
+Author: Alex Stewart <alexs.mac@gmail.com>
+Date: Sun May 24 18:05:05 2020 +0100
+
+ Bypass Ceres' FindGlog.cmake in CeresConfig.cmake if possible
- https://github.com/ceres-solver/ceres-solver/issues/212
+ - If the version of glog detected and used to build Ceres was built with
+ CMake (i.e. Config mode) then we now use Config mode directly in
+ CeresConfig.cmake and do not install Ceres' FindGlog.cmake module.
+ - This has the benefit of removing any hard-coded paths from
+ CeresConfig.cmake provided that all dependencies were also built with
+ CMake.
- Thanks to @NeroBurner and @debalance for reporting this.
+ Change-Id: I85af8a953fd6d300e8bc0cdeb0b3636fec182f68
+
+commit 77fc1d0fc4159ebb3a0a84a16651564eb2ce3c9d
+Author: Alex Stewart <alexs.mac@gmail.com>
+Date: Sun May 24 19:07:26 2020 +0100
+
+ Use build_depend for private dependencies in Catkin package.xml
- Change-Id: I66480e86d4fa0a4b621204f2ff44cc3ff8d01c04
+ Change-Id: If0c0569e7ebbf37c0d8e8daaf7765e20a6282531
-commit 83041ac84f2d67c28559c67515e0e596a3f3aa20
-Author: Sameer Agarwal <sameeragarwal@google.com>
-Date: Fri Sep 2 19:10:35 2016 -0700
+commit a09682f00d8e50ada3c7ed16f8c48fa71a423f60
+Author: Alex Stewart <alexs.mac@gmail.com>
+Date: Sun May 24 16:49:28 2020 +0100
- Fix some compiler warnings.
+ Fix MSVC version check to support use of clang-cl front-end
- Reported by Richard Trieu.
+ - Raised as issue: #521
- Change-Id: I202b7a7df09cc19c92582d276ccf171edf88a9fb
+ Change-Id: Iaea6b43484b90ec8789bda0447c8a90759974ec1
-commit 8c4623c63a2676e79e7917bb0561f903760f19b9
-Author: Sameer Agarwal <sameeragarwal@google.com>
-Date: Thu Sep 1 00:05:09 2016 -0700
+commit b70687fcc86624c7d5520d25734938fa95d2af73
+Author: Alex Stewart <alexs.mac@gmail.com>
+Date: Sun May 24 20:28:12 2020 +0100
- Update ExpectArraysClose to use ExpectClose instead of EXPECT_NEAR
+ Add namespace qualified Ceres::ceres CMake target
- The documentation for ExpectArraysClose and its implementation
- did not match.
+ - This reflects modern CMake style, and also provides a measure of
+ protection against missing find_package() imports in downstream
+ clients resulting in linker errors when 'ceres' matches the compiled
+ library and not the imported target.
+ - The original 'ceres' target remains, as a local imported interface
+ target created by CeresConfig for backwards compatibility.
- This change makes the polynomial_test not fail on 64bit AMD builds.
+ Change-Id: Ie9ed8de9b7059bc0cae1ae5002bb94d8fe617188
+
+commit 99efa54bdb4e14c3f4382a166baf6772113f74a8
+Author: Alex Stewart <alexs.mac@gmail.com>
+Date: Sun May 24 19:18:38 2020 +0100
+
+ Replace type aliases deprecated/removed in C++17/C++20 from FixedArray
- Thanks to Phillip Huebner for reporting this.
+ - Raised as issue #551
+ - Also adds C++20 to the set of ALLOWED_CXX_STANDARDS, although this
+ will require a version of CMake >= 3.12.
- Change-Id: I503f2d3317a28d5885a34f8bdbccd49d20ae9ba2
+ Change-Id: I0f13c72e93a35391fd2d18590b4243a329a2322c
-commit 2fd39fcecb47eebce727081c9ffb8edf86c33669
+commit adb973e4a337c372aa81ca1a4f3bb704068c08b7
Author: Sameer Agarwal <sameeragarwal@google.com>
-Date: Thu Sep 1 16:05:06 2016 -0700
+Date: Thu May 21 14:45:28 2020 -0700
- FindWithDefault returns by value rather than reference.
-
- Returning by reference leads to lifetime issues with the default
- value which may go out of scope by the time it is used.
-
- Thanks to @Ardavel for reporting this, as this causes graph_test
- to fail on VS2015x64.
-
- https://github.com/ceres-solver/ceres-solver/issues/216
+ NULL -> nullptr
- Change-Id: I596481219cfbf7622d49a6511ea29193b82c8ba3
+ Change-Id: Iaeea2ef7912d328653a76b65976adc8025a5be35
-commit 716f049a7b91a8f3a4632c367d9534d1d9190a81
-Author: Mike Vitus <vitus@google.com>
-Date: Wed Aug 31 13:38:30 2016 -0700
+commit 27b717951b58c134b3a5a9f664a66c7480364d6c
+Author: Alastair Harrison <aharrison24@gmail.com>
+Date: Fri May 15 10:10:12 2020 +0100
- Convert pose graph 2D example to glog and gflags.
+ Respect FIND_QUIETLY flag in cmake config file
- Change-Id: I0ed75a60718ef95199bb36f33d9eb99157d11d40
+ Ensure that Ceres does not print any log messages when somebody has
+ used 'find_package(Ceres QUIET)' in their CMake project.
+
+ Change-Id: Id6b68859cc8a5857f3fa78f29736cb82fd5a0943
-commit 46c5ce89dda308088a5fdc238d0c126fdd2c2b58
-Author: David Gossow <dgossow@google.com>
-Date: Wed Aug 31 18:40:57 2016 +0200
+commit 646959ef118a1f10bf93741d97cf64265d42f8c6
+Author: huangqinjin <huangqinjin@gmail.com>
+Date: Sat Apr 25 02:03:11 2020 +0800
- Fix compiler errors on some systems
+ Do not export class template LineParameterization
- This fixes some signed-unsigned comparisons and a missing header
- include.
+ For MSVC, instantiating a dllimport class template will cause error C2491:
+ definition of dllimport function not allowed.
- Change-Id: Ieb2bf6e905faa74851bc4ac4658d2f1da24b6ecc
+ Change-Id: Icc7f7ea84598df0a5436f48ffc2bab5cfab93921
-commit b102d53e1dd7dab132e58411183b6fffc2090590
-Author: David Gossow <dgossow@google.com>
-Date: Wed Aug 31 10:21:20 2016 +0200
+commit 1f128d070a24224d12eb901bc74ba393ccdbd0c3
+Author: huangqinjin <huangqinjin@gmail.com>
+Date: Mon Mar 4 13:14:43 2019 +0800
- Gradient checker multithreading bugfix.
+ Change the type of parameter index/offset to match their getter/setter
- This is a follow-up on c/7470. GradientCheckingCostFunction calls
- callback_->SetGradientErrorDetected() in its Evaluate method,
- which will run in multiple threads simultaneously when enabling
- this option in the solver. Thus, the string append operation
- inside that method has to be protected by a mutex.
+ Change-Id: If28b795e792f39db9775ada105e9038570195329
+
+commit 072c8f070e16cb32f211473c40196c6b5618d5a9
+Author: huangqinjin <huangqinjin@gmail.com>
+Date: Sat Apr 25 00:04:58 2020 +0800
+
+ Initialize integer variables with integer instead of double
- Change-Id: I314ef1df2be52595370d9af05851bf6da39bb45e
+ Change-Id: I652aca4ceb3129706a5d5e38afe9f16b61200a5b
-commit 79a28d1e49af53f67af7f3387d07e7c9b7339433
-Author: Sameer Agarwal <sameeragarwal@google.com>
-Date: Wed Aug 31 06:47:45 2016 -0700
+commit 8c36bcc81fbd4f78a2faa2c914ef40af264f4c31
+Author: Alex Stewart <alexs.mac@gmail.com>
+Date: Mon Apr 27 18:33:25 2020 +0100
- Rename a confusingly named member of Solver::Options
+ Use inline & -inlinehint-threshold in auto-diff benchmarks
- Solver::Options::numeric_derivative_relative_step_size to
- Solver::Options::gradient_check_numeric_derivative_relative_step_size
+ - This results in the same performance as the original solution of
+ increasing -inline-threshold, but this approach is more viable to
+ incorporate in a large code base as its effects are more targeted.
- Change-Id: Ib89ae3f87e588d4aba2a75361770d2cec26f07aa
+ Change-Id: Id798dbca7d3050de0ea847a5ecc69484ac78a2cf
-commit 358ae741c8c4545b03d95c91fa546d9a36683677
+commit 57cf20aa5d3c1b2f25d255814f4fff5260db81c6
Author: Sameer Agarwal <sameeragarwal@google.com>
-Date: Wed Aug 31 06:58:41 2016 -0700
+Date: Tue Apr 21 10:10:01 2020 -0700
- Note that Problem::Evaluate cannot be called from an IterationCallback
+ static const -> static constexpr where we can.
- Change-Id: Ieabdc2d40715e6b547ab22156ba32e9c8444b7ed
+ Change-Id: I8a6d26a89c4377dd440fa6dcf23513b7556533fc
-commit 44044e25b14d7e623baae4505a17c913bdde59f8
+commit 40b27482a202c8b0a5f9e8f2b4be0192d34195f5
Author: Sameer Agarwal <sameeragarwal@google.com>
-Date: Wed Aug 31 05:50:58 2016 -0700
+Date: Tue Apr 21 09:35:30 2020 -0700
- Update the NumTraits for Jets
+ Add std::numeric_limit specialization for Jets
- 1. Use AVX if EIGEN_VECTORIZE_AVX is defined.
- 2. Make the cost of division same as the cost of multiplication.
+ This allows users to use std::numeric_limits on templated functors.
- These are updates to the original numtraits update needed for eigen 3.3
- that Shaheen Gandhi sent out.
-
- Change-Id: Ic1e3ed7d05a659c7badc79a894679b2dd61c51b9
+ Change-Id: I403cec5c9826033ce7dfd6047deb64f66c35f806
-commit 4b6ad5d88e45ce8638c882d3e8f08161089b6dba
-Author: Sameer Agarwal <sameeragarwal@google.com>
-Date: Sat Aug 27 23:21:55 2016 -0700
+commit e751d6e4f0daa9f691c5ed25ca8dc564875d8bef
+Author: Darius Rueckert <darius.rueckert@fau.de>
+Date: Wed Apr 8 10:43:53 2020 +0200
- Use ProductParameterization in bundle_adjuster.cc
-
- Previously, when using a quaternion to parameterize the camera
- orientation, the camera parameter block was split into two
- parameter blocks. One for the rotation and another for the
- translation and intrinsics. This was to enable the use of the
- Quaternion parameterization.
+ Remove AutodiffCodegen
- Now that we have a ProductParameterization which allows us
- to compose multiple parameterizations, this is no longer needed
- and we use a size 10 parameter block instead.
+ - Remove Codegen files
+ - Revert Jet and Rotation
- This leads to a more than 2x improvements in the linear solver time.
-
- Change-Id: I78b8f06696f81fee54cfe1a4ae193ee8a5f8e920
+ Change-Id: I005c5f98f2b6dfa5c7fd88d998b6aa83e47dab60
-commit bfc916cf1cf753b85c1e2ac037e2019ee891f6f9
-Author: Shaheen Gandhi <visigoth@gmail.com>
-Date: Thu Aug 4 12:10:14 2016 -0700
+commit e9eb76f8ef9835940659cfb3a312ed6822c48152
+Author: Darius Rueckert <darius.rueckert@fau.de>
+Date: Mon Apr 6 11:11:43 2020 +0200
- Allow ceres to be used with the latest version of Eigen
+ Remove AutodiffCodegen CMake integration
- Change-Id: Ief3b0f6b405484ec04ecd9ab6a1e1e5409a594c2
+ Change-Id: I403597540df8429378336626b8f748b7821fe6f5
-commit edbd48ab502aa418ad9700ee5c3ada5f9268b90a
-Author: Alex Stewart <alexs.mac@gmail.com>
-Date: Sun Jul 10 14:13:51 2016 +0100
+commit 9435e08a7a7c903897e18e1dc24d801caf4f62a4
+Author: Sameer Agarwal <sameeragarwal@google.com>
+Date: Fri Apr 3 11:50:09 2020 -0700
- Enable support for OpenMP in Clang if detected.
-
- - Previously we disabled OpenMP if Clang was detected, as it did not
- support it. However as of Clang 3.8 (and potentially Xcode 8) OpenMP
- is supported.
+ More clang-tidy and wjr@ comment fixes
- Change-Id: Ia39dac9fe746f1fc6310e08553f85f3c37349707
+ Change-Id: I5736ae482f736fc56c00d21c659b1f8d41da68e9
-commit f6df6c05dd83b19fa90044106ebaca40957998ae
-Author: Mike Vitus <vitus@google.com>
-Date: Thu Aug 18 19:27:43 2016 -0700
+commit d93fac4b7ab670a936ce821284a0b9d099b4688c
+Author: Darius Rueckert <darius.rueckert@fau.de>
+Date: Fri Apr 3 09:33:17 2020 +0200
- Add an example for modeling and solving a 3D pose graph SLAM problem.
+ Remove AutodiffCodegen Tests
- Change-Id: I750ca5f20c495edfee5f60ffedccc5bd8ba2bb37
+ Change-Id: Icd194db7b22add518844f1b507d0fdd3e0fe17fe
-commit ac3b8e82175122e38bafaaa9cd419ba3cee11087
-Author: David Gossow <dgossow@google.com>
-Date: Fri Apr 29 16:07:11 2016 +0200
+commit 2281c6ed24d2c12f133fa6039f224b3da18cebe3
+Author: Sameer Agarwal <sameeragarwal@google.com>
+Date: Thu Apr 2 16:43:42 2020 -0700
- Gradient checking cleanup and local parameterization bugfix
-
- Change the Ceres gradient checking API to make is useful for
- unit testing, clean up code duplication and fix interaction between
- gradient checking and local parameterizations.
-
- There were two gradient checking implementations, one being used
- when using the check_gradients flag in the Solver, the other
- being a standalone class. The standalone version was restricted
- to cost functions with fixed parameter sizes at compile time, which
- is being lifted here. This enables it to be used inside the
- GradientCheckingCostFunction as well.
+ Fixes for comments from William Rucklidge
- In addition, this installs new hooks in the Solver to ensure
- that Solve will fail if any incorrect gradients are detected. This
- way, you can set the check_gradient flags to true and detect
- errors in an automated way, instead of just printing error information
- to the log. The error log is now also returned in the Solver summary
- instead of being printed directly. The user can then decide what to
- do with it. The existing hooks for user callbacks are used for
- this purpose to keep the internal API changes minimal and non-invasive.
-
- The last and biggest change is the way the the interaction between
- local parameterizations and the gradient checker works. Before,
- local parameterizations would be ignored by the checker. However,
- if a cost function does not compute its Jacobian along the null
- space of the local parameterization, this wil not have any effect
- on the solver, but would result in a gradient checker error.
- With this change, the Jacobians are multiplied by the Jacobians
- of the respective local parameterization and thus being compared
- in the tangent space only.
-
- The typical use case for this are quaternion parameters, where
- a cost function will typically assume that the quaternion is
- always normalized, skipping the correct computation of the Jacobian
- along the normal to save computation cost.
-
- Change-Id: I5e1bb97b8a899436cea25101efe5011b0bb13282
+ Change-Id: I64fcc25532cc66dc4cb7e2ea7ccfb220b0cb7e1f
-commit d4264ec10d9a270b53b5db86c0245ae8cbd2cf18
-Author: Mike Vitus <vitus@google.com>
-Date: Wed Aug 17 13:39:05 2016 -0700
+commit d797a87a4091af6ae0063e3c8291429c15318bdc
+Author: Sameer Agarwal <sameeragarwal@google.com>
+Date: Thu Apr 2 13:57:56 2020 -0700
- Add a quaternion local parameterization for Eigen's quaternion element convention.
+ Use Ridders' method in GradientChecker.
- Change-Id: I7046e8b24805313c5fb6a767de581d0054fcdb83
-
-commit fd7cab65ef30fbc33612220abed52dd5073413c4
-Author: Mike Vitus <vitus@google.com>
-Date: Wed Aug 10 09:29:12 2016 -0700
-
- Fix typos in the pose graph 2D example.
+ Using Ridders' method gives orders of magnitude more accuracy compared
+ to central differences. This will make things slower, but this is
+ primarily a testing/debugging feature and the speed hit is not a
+ concern. This should also reduce the false positive rates when users
+ enable check_gradients. This is reflected the increased sensitivity of
+ the tests for GradientChecker.
+
+ https://github.com/ceres-solver/ceres-solver/issues/554
- Change-Id: Ie024ff6b6cab9f2e8011d21121a91931bd987bd1
+ Change-Id: I6b871c72df55be1c31175ba062cf3c1e94e4b662
-commit 375dc348745081f89693607142d8b6744a7fb6b4
-Author: Mike Vitus <vitus@google.com>
-Date: Wed Aug 3 18:51:16 2016 -0700
+commit 41675682dc9df836bf15845064cfe1087619c79d
+Author: Sameer Agarwal <sameeragarwal@google.com>
+Date: Thu Apr 2 07:28:38 2020 -0700
- Remove duplicate entry for the NIST example in the docs.
+ Fix a MSVC type deduction bug in ComputeHouseholderVector
- Change-Id: Ic4e8f9b68b77b5235b5c96fe588cc56866dab759
+ A recent change made this function templated and MSVC 16 has trouble
+ doing automatic argument deduction, so the type of the template is
+ simplified and all callsites are explicitly annotated with the type
+ of the arguments.
+
+ Change-Id: I83cd0269e6e82c4a8f4e391f5fc03b92c942f74d
-commit f554681bf22d769abc12dd6d346ef65f9bb22431
-Author: Mike Vitus <vitus@google.com>
-Date: Mon Jul 25 18:30:48 2016 -0700
+commit 947ec0c1fa0f67c89e21daaf8d1648822ae5293a
+Author: Darius Rueckert <darius.rueckert@fau.de>
+Date: Thu Apr 2 09:52:53 2020 +0200
- Add an example for modeling and solving a 2D pose graph SLAM problem.
+ Remove AutodiffCodegen autodiff benchmarks
- Change-Id: Ia89b12af7afa33e7b1b9a68d69cf2a0b53416737
+ Change-Id: If1eaad31710cc91d40323ea6cae7cabe6fa64b1f
-commit e1bcc6e0f51512f43aa7bfb7b0d62f7ac1d0cd4b
+commit 27183d661ecae246dbce6d03cacf84f39fba1f1e
Author: Sameer Agarwal <sameeragarwal@google.com>
-Date: Wed May 18 07:52:48 2016 -0700
+Date: Thu Jul 11 16:30:59 2019 +0200
- Add additional logging for analyzing orderings
+ Allow LocalParameterizations to have zero local size.
+
+ Local parameterizations with zero tangent/local size will cause the
+ corresponding parameter block to be treated as constant.
- Change-Id: Ic68d2959db35254e2895f11294fb25de4d4b8a81
+ https://github.com/ceres-solver/ceres-solver/issues/347
+
+ Change-Id: I554a2acc420f5dd9d0cc7f97b691877eb057b2c0
-commit 16980b4fec846f86910c18772b8145bcb55f4728
-Author: Mike Vitus <vitus@google.com>
-Date: Fri Jul 15 13:37:49 2016 -0700
+commit 7ac7d79dca2ac6b482da50fd9ad0227ba8d6c632
+Author: Darius Rueckert <darius.rueckert@fau.de>
+Date: Wed Apr 1 14:51:12 2020 +0200
- Delete the remove_definitons command from sampled_functions
- CMakeLists.txt because it will be inherited from the top level examples
- CMakeLists.txt.
+ Remove HelloWorldCodegen example
- Change-Id: I25593587df0ae84fd8ddddc589bc2a13f3777427
+ Change-Id: I2584f41d591a5d648b4832385c2a779bb25fc04d
-commit a04490be97800e78e59db5eb67fa46226738ffba
-Author: Mike Vitus <vitus@google.com>
-Date: Thu Jul 14 10:10:13 2016 -0700
+commit 8c8738bf832f0fc27f0d4a9585fc59b2eaa6a828
+Author: Nikolaus Demmel <nikolaus@nikolaus-demmel.de>
+Date: Sun Mar 29 13:29:02 2020 +0200
- Add readme for the sampled_function example.
+ Add photometric and relative-pose residuals to autodiff benchmarks
- Change-Id: I9468b6a7b9f2ffdd2bf9f0dd1f4e1d5f894e540c
+ Change-Id: Id100ff2656ab63bb4fd19a51b95e78281cfd8b4a
-commit ff11d0e63d4678188e8cabd40a532ba06912fe5a
-Author: Alex Stewart <alexs.mac@gmail.com>
-Date: Wed Jun 29 09:31:45 2016 +0100
+commit 9f7fb66d62014ed62ba6aa617364e8591211c797
+Author: Darius Rueckert <darius.rueckert@fau.de>
+Date: Wed Mar 25 11:41:39 2020 +0100
- Use _j[0,1,n]() Bessel functions on MSVC to avoid deprecation errors.
+ Add a constant cost function to the autodiff benchmarks
+
+ The constant cost function is run with a variable number of
+ parameters to test at which point, different compilers fail
+ to optimize the autodiff code.
- - Microsoft deprecated the POSIX Bessel functions: j[0,1,n]() in favour
- of _j[0,1,n](), it appears since at least MSVC 2005:
- https://msdn.microsoft.com/en-us/library/ms235384(v=vs.100).aspx.
- - As this occurs in jet.h (templated public header), although Ceres
- suppresses the warning when it itself is built (to suppress a warning
- about the insecurity of using std::copy), it will crop up again in
- client code (without this fix) unless it is explicitly suppressed
- there also.
- - Raised as Issue #190:
- https://github.com/ceres-solver/ceres-solver/issues/190.
+ Clang achieves expected performance which fails at >50 parameters.
+ G++ fails already at 20 parameters
- Change-Id: If7ac5dbb856748f9900be93ec0452a40c0b00524
+ Change-Id: I75d8c683ef0011d813ec6d966d7ad58f86530f44
-commit 8ea86e1614cf77644ce782e43cde6565a54444f5
-Author: Nicolai Wojke <nwojke@uni-koblenz.de>
-Date: Mon Apr 25 14:24:41 2016 +0200
+commit ab0d373e465f46ce483db640d0fb2f244f48702d
+Author: Sameer Agarwal <sameeragarwal@google.com>
+Date: Tue Mar 24 12:30:46 2020 -0700
- Fix: Copy minimizer option 'is_silent' to LinSearchDirection::Options
+ Fix a comment in autodiff.h
- Change-Id: I23b4c3383cad30033c539ac93883d77c8dd4ba1a
+ Change-Id: I613e537c834e3f29cd92808c65ddb74f112974cc
-commit 080ca4c5f2ac42620971a07f06d2d13deb7befa8
-Author: Sameer Agarwal <sameeragarwal@google.com>
-Date: Sun Apr 24 22:46:54 2016 -0700
+commit 27bb997144d00dd4494d440627f1e782bf4adf43
+Author: Johannes Beck <Jodebo_Beck@gmx.de>
+Date: Tue Mar 24 08:05:43 2020 +0100
- Fix typos in users.rst
+ Change SVD algorithm in covariance computation.
- Change-Id: Ifdc67638a39403354bc9589f42a1b42cb9984dd2
+ Switch from JacobiSVD to BDCSVD in
+ ComputeCovarianceValuesUsingDenseSVD. This should increase
+ the performance for larger covariance matrices. See
+ https://eigen.tuxfamily.org/dox/classEigen_1_1BDCSVD.html
+
+ Change-Id: Icde4dec89f506b638b0f9f1aee3b7cfc9e4d72fc
-commit 21ab397dc55335c147fdd795899b1f8981037b09
-Author: Sameer Agarwal <sameeragarwal@google.com>
-Date: Sun Apr 24 21:13:00 2016 -0700
+commit 84fdac38e033c8f9a63c6e6fca7b44219110f7df
+Author: Johannes Beck <Jodebo_Beck@gmx.de>
+Date: Tue Mar 24 08:02:21 2020 +0100
- Make some Jet comparisons exact.
+ Add const to GetCovarianceMatrix*
+
+ This CL adds const to the functions GetCovarianceMatrix and
+ GetCovarianceMatrixInTangentSpace.
- Change-Id: Ia08c72f3b8779df96f5c0d5a954b2c0a1dd3a061
+ Change-Id: Ibe2cafebede47977a9aabcac8d245f30af184fd1
-commit ee40f954cf464087eb8943abf4d9db8917a33fbe
-Author: Sameer Agarwal <sameeragarwal@google.com>
-Date: Sun Apr 24 07:49:55 2016 -0700
+commit 6bde61d6be9d81a2cd759a6bbb4a8cd3c24a529c
+Author: Johannes Beck <Jodebo_Beck@gmx.de>
+Date: Sat Dec 28 13:29:19 2019 +0100
- Add colmap to users.rst
+ Add line local parameterization.
+
+ This CL adds a local parameterization for a n-dimensional
+ line, which is represented as an origin point and a direction.
+ The line direction is updated in the same way as a
+ homogeneous vector and the origin point is updated
+ perpendicular to the line direction.
- Change-Id: I452a8c1dc6a3bc55734b2fc3a4002ff7939ba863
+ Change-Id: I733f395e5cc4250abf9778c26fe0a5ae1de6b624
-commit 9665e099022bd06e53b0779550e9aebded7f274d
+commit 2c1c0932e9d3f91691e5c5fce46b4440e181a8bc
Author: Sameer Agarwal <sameeragarwal@google.com>
-Date: Mon Apr 18 06:00:58 2016 -0700
+Date: Mon Mar 23 11:15:32 2020 -0700
- Fix step norm evaluation in LineSearchMinimizer
+ Update documentation in autodiff.h
- TrustRegionMinimizer evaluates the size of the step
- taken in the ambient space, where as the LineSearchMinimizer
- was using the norm in the tangent space. This change fixes
- this discrepancy.
-
- Change-Id: I9fef64cbb5622c9769c0413003cfb1dc6e89cfa3
+ Change-Id: Icc2753b4f5be95022ffd92e479cdd3d9d7959d4c
-commit 620ca9d0668cd4a00402264fddca3cf6bd2e7265
-Author: Alex Stewart <alexs.mac@gmail.com>
-Date: Mon Apr 18 15:14:11 2016 +0100
+commit 8904fa4887ed7b3e6d110ad5a98efbc2df48595e
+Author: Darius Rueckert <darius.rueckert@fau.de>
+Date: Mon Mar 23 14:59:26 2020 +0100
- Remove use of -Werror when compiling Ceres.
+ Inline Jet initialization in Autodiff
+
+ Inlining the Jet initialzation is mandatory for good performance
+ in autodiff, because all the constants in the dual part can be
+ propagated into the cost functor.
- - As noted in Issue #193 (in that case for GCC 6), Ceres' use of -Werror
- when compiling on *nix can prevent compilation on new compilers that
- add new warnings and there is an inevitable delay between new compiler
- versions and Ceres versions.
- - Removing the explicit use of -Werror, and relying on indirect
- verification by maintainers should fix build issues for Ceres releases
- on newer compilers.
+ This patch unrolls the initialization loop with templates and adds
+ EIGEN_ALWAYS_INLINE to the constructors.
- Change-Id: I38e9ade28d4a90e53dcd918a7d470f1a1debd7b4
+ Change-Id: Ic89d645984f3e1df6c63948236da823ba60d9620
-commit 0c63bd3efbf1d41151c9fab41d4b77dc64c572c8
-Author: Mike Vitus <vitus@google.com>
-Date: Thu Apr 14 10:25:52 2016 -0700
+commit 18a464d4e566e17930005876af19e32cc8796fa3
+Author: Sameer Agarwal <sameeragarwal@google.com>
+Date: Mon Mar 23 07:12:12 2020 -0700
- Add floor and ceil functions to the Jet implementation.
+ Remove an errant CR from local_parameterization.cc
- Change-Id: I72ebfb0e9ade2964dbf3a014225ead345d5ae352
+ Change-Id: Iff98a96f06de5755062a1c79523604dca78b298e
-commit 9843f3280356c158d23c06a16085c6c5ba35e053
-Author: Alex Stewart <alexs.mac@gmail.com>
-Date: Mon Mar 7 21:24:32 2016 +0000
+commit 5c85f21799804d39cbfd20ec451aa219511e4212
+Author: Darius Rueckert <darius.rueckert@fau.de>
+Date: Mon Mar 23 10:12:00 2020 +0100
- Report Ceres compile options as components in find_package().
+ Use ArraySelector in Autodiff
- - Users can now specify particular components from Ceres, such as
- SuiteSparse support) that must be present in a detected version of
- Ceres in order for it to be reported as found by find_package().
- - This allows users to specify for example that they require a version
- of Ceres with SuiteSparse support at configure time, rather than
- finding out only at run time that Ceres was not compiled with the
- options they require.
- - The list of available components are built directly from the Ceres
- compile options.
- - The meta-module SparseLinearAlgebraLibrary is present if at least
- one sparse linear algebra backend is available.
+ The class ArraySelector is now used in autodiff to store the
+ parameters and residuals. This reduces overhead of FixedArray
+ for fixed-sized residuals and allows more optimizations due
+ to inlining and unrolling.
- Change-Id: I65f1ddfd7697e6dd25bb4ac7e54f5097d3ca6266
+ Change-Id: Ibadc5644e64d672f7a555e250fb1f8da262f9d4f
-commit e4d4d88bbe51b9cc0f7450171511abbea0779790
-Author: Timer <linyicx@126.com>
-Date: Fri Apr 8 15:42:18 2016 +0800
+commit 80477ff073ab7af03cfb248cab4ef41a87f913d0
+Author: Darius Rueckert <darius.rueckert@fau.de>
+Date: Fri Mar 20 16:26:55 2020 +0100
- Fix a spelling error in nnls_modeling.rst
+ Add class ArraySelector
- Change-Id: I341d901d3df993bc5397ed15e6cb330b0c38fd72
+ The ArraySelector selects the best array implementation based on
+ template arguments.
+
+ Change-Id: I93c6db1a638e924b85292e63bca9525610ec2e2f
-commit 5512f58536e1be0d92010d8325b606e7b4733a08
-Author: Keir Mierle <mierle@gmail.com>
-Date: Thu Apr 7 12:03:16 2016 -0700
+commit e7a30359ee754057f9bd7b349c98c291138d91f4
+Author: Darius Rueckert <darius.rueckert@fau.de>
+Date: Fri Mar 20 15:50:37 2020 +0100
- Only use collapse() directive with OpenMP 3.0 or higher
+ Pass kNumResiduals to Autodiff
+
+ The compile-time constant kNumResiduals is now passed to the
+ autodiff functions as a template parameter. This will be used
+ by future patches to optimize autodiff performance.
- Change-Id: Icba544c0494763c57eb6dc61e98379312ca15972
+ Change-Id: Ia2b2cc99b88752e8f12f4ce2542b1963bda552f5
-commit d61e94da5225217cab7b4f93b72f97055094681f
-Author: Thomas Schneider <schneith@ethz.ch>
-Date: Wed Apr 6 10:40:29 2016 +0200
+commit f339d71dd64e4d871cc883f278a153f212f0d1f0
+Author: Sameer Agarwal <sameeragarwal@google.com>
+Date: Thu Mar 19 12:08:28 2020 -0700
+
+ Refactor the automatic differentiation benchmarks.
+
+ 1. Merge them into a single binary.
+ 2. All benchmarks now do the Residual and the Residual + Jacobian
+ evaluation.
+ 3. Re-organize and simplify the CMake file in this directory.
+ 4. Fix a bug in the file where the Clang compiler was not being matched.
+
+ autodiff_benchmarks
+ ---------------------------------------------------------------------------
+ Benchmark Time CPU Iterations
+ ---------------------------------------------------------------------------
+ BM_Linear1CodeGen/0 3.02 ns 3.01 ns 233870456
+ BM_Linear1CodeGen/1 3.02 ns 3.01 ns 233059100
+ BM_Linear1AutoDiff/0 3.78 ns 3.77 ns 185791712
+ BM_Linear1AutoDiff/1 14.0 ns 13.8 ns 53927875
+ BM_Linear10CodeGen/0 5.10 ns 5.10 ns 126745007
+ BM_Linear10CodeGen/1 29.1 ns 29.1 ns 23949310
+ BM_Linear10AutoDiff/0 6.50 ns 6.49 ns 107516972
+ BM_Linear10AutoDiff/1 169 ns 169 ns 4153218
+ BM_Rat43AutoDiff/0 52.7 ns 51.2 ns 16444586
+ BM_Rat43AutoDiff/1 91.8 ns 91.5 ns 7302316
+ BM_SnavelyReprojectionCodeGen/0 38.0 ns 36.2 ns 21131501
+ BM_SnavelyReprojectionCodeGen/1 113 ns 112 ns 5627779
+ BM_SnavelyReprojectionAutoDiff/0 34.4 ns 34.3 ns 20476937
+ BM_SnavelyReprojectionAutoDiff/1 242 ns 240 ns 2930611
+ BM_BrdfCodeGen/0 53.9 ns 53.7 ns 11950083
+ BM_BrdfCodeGen/1 507 ns 505 ns 1396732
+ BM_BrdfAutoDiff/0 58.3 ns 57.8 ns 12220670
+ BM_BrdfAutoDiff/1 2034 ns 1999 ns 257003
+
+ autodiff_benchmarks_fast_math
+ ---------------------------------------------------------------------------
+ Benchmark Time CPU Iterations
+ ---------------------------------------------------------------------------
+ BM_Linear1CodeGen/0 3.19 ns 3.16 ns 215313065
+ BM_Linear1CodeGen/1 2.78 ns 2.76 ns 201497994
+ BM_Linear1AutoDiff/0 3.27 ns 3.26 ns 206154598
+ BM_Linear1AutoDiff/1 13.2 ns 13.1 ns 57257840
+ BM_Linear10CodeGen/0 5.70 ns 5.51 ns 121849325
+ BM_Linear10CodeGen/1 33.9 ns 33.3 ns 21829295
+ BM_Linear10AutoDiff/0 6.85 ns 6.78 ns 106813153
+ BM_Linear10AutoDiff/1 173 ns 171 ns 3849877
+ BM_Rat43AutoDiff/0 44.8 ns 44.2 ns 15577017
+ BM_Rat43AutoDiff/1 96.2 ns 94.6 ns 7374864
+ BM_SnavelyReprojectionCodeGen/0 33.9 ns 33.5 ns 20508373
+ BM_SnavelyReprojectionCodeGen/1 89.7 ns 88.4 ns 7620624
+ BM_SnavelyReprojectionAutoDiff/0 36.5 ns 35.8 ns 20546176
+ BM_SnavelyReprojectionAutoDiff/1 257 ns 252 ns 3044325
+ BM_BrdfCodeGen/0 61.1 ns 58.5 ns 11334013
+ BM_BrdfCodeGen/1 265 ns 265 ns 2625459
+ BM_BrdfAutoDiff/0 52.5 ns 52.5 ns 12938763
+ BM_BrdfAutoDiff/1 1560 ns 1560 ns 440909
+
+ Change-Id: I2d1a4293d3245a50f73af6cf5e5138084321ae6f
+
+commit d37b4cb150c4af65268f9ce5739d1c67e73cb358
+Author: Sameer Agarwal <sameeragarwal@google.com>
+Date: Thu Mar 19 07:36:58 2020 -0700
- Add IsParameterBlockConstant to the ceres::Problem class.
+ Fix some include headers in codegen/test_utils.cc/h
- Change-Id: I7d0e828e81324443209c17fa54dd1d37605e5bfe
+ Change-Id: I769029ce2797eba0de6c7baeb76dc3f2782b6305
-commit 77d94b34741574e958a417561702d6093fba87fb
-Author: Alex Stewart <alexs.mac@gmail.com>
-Date: Sun Feb 14 16:54:03 2016 +0000
+commit 550766e6da49dca895a6e2056b0872c557157c5b
+Author: Darius Rueckert <darius.rueckert@fau.de>
+Date: Wed Mar 18 20:09:20 2020 +0100
- Fix install path for CeresConfig.cmake to be architecture-aware.
+ Add Autodiff Brdf Benchmark
- - Previously we were auto-detecting a "64" suffix for the install path
- for the Ceres library on non-Debian/Arch Linux distributions, but
- we were installing CeresConfig.cmake to an architecture independent
- location.
- - We now install CeresConfig.cmake to lib${LIB_SUFFIX}/cmake/Ceres.
- - Also make LIB_SUFFIX visible to the user in the CMake GUI s/t they can
- easily override the auto-detected value if desired.
- - Reported by jpgr87@gmail.com as Issue #194.
+ The disney brdf is a good benchmark cost functor, because it has
+ - 28 parameters in 7 blocks
+ - 3 residuals
+ - Lots of low-level arithmetic
- Change-Id: If126260d7af685779487c01220ae178ac31f7aea
+ Change-Id: I62c8a717d0aecb64639158f971bdccf6afdfae36
diff --git a/extern/ceres/bundle.sh b/extern/ceres/bundle.sh
index 02af59faf28..573db5a0a2f 100755
--- a/extern/ceres/bundle.sh
+++ b/extern/ceres/bundle.sh
@@ -9,7 +9,6 @@ fi
repo="https://ceres-solver.googlesource.com/ceres-solver"
branch="master"
-#tag="1.4.0"
tag=""
tmp=`mktemp -d`
checkout="$tmp/ceres"
@@ -157,14 +156,10 @@ add_definitions(
-DCERES_NO_SUITESPARSE
-DCERES_NO_CXSPARSE
-DCERES_NO_LAPACK
+ -DCERES_NO_ACCELERATE_SPARSE
-DCERES_HAVE_RWLOCK
+ -DCERES_USE_CXX_THREADS
)
-if(WITH_OPENMP)
- add_definitions(
- -DCERES_USE_OPENMP
- )
-endif()
-
blender_add_lib(extern_ceres "\${SRC}" "\${INC}" "\${INC_SYS}" "\${LIB}")
EOF
diff --git a/extern/ceres/files.txt b/extern/ceres/files.txt
index 4d973bbcdc2..bfbd57090c9 100644
--- a/extern/ceres/files.txt
+++ b/extern/ceres/files.txt
@@ -1,29 +1,37 @@
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/fpclassify.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/macros.h
-include/ceres/internal/manual_constructor.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/scoped_ptr.h
include/ceres/internal/variadic_evaluate.h
include/ceres/iteration_callback.h
include/ceres/jet.h
@@ -37,8 +45,13 @@ 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
@@ -63,21 +76,26 @@ 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/collections_port.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
@@ -85,6 +103,7 @@ 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
@@ -102,11 +121,21 @@ 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
@@ -118,10 +147,12 @@ 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
@@ -138,17 +169,18 @@ 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_eliminator_specialization.py
-internal/ceres/generate_partitioned_matrix_view_specializations.py
+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
@@ -157,12 +189,15 @@ internal/ceres/gradient_problem_evaluator.h
internal/ceres/gradient_problem_solver.cc
internal/ceres/graph_algorithms.h
internal/ceres/graph.h
-internal/ceres/householder_vector.h
internal/ceres/implicit_schur_complement.cc
internal/ceres/implicit_schur_complement.h
-internal/ceres/integral_types.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
@@ -190,14 +225,21 @@ internal/ceres/low_rank_inverse_hessian.h
internal/ceres/map_util.h
internal/ceres/minimizer.cc
internal/ceres/minimizer.h
-internal/ceres/mutex.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
@@ -222,14 +264,23 @@ 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
@@ -239,7 +290,14 @@ 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
@@ -251,7 +309,10 @@ 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 e7893e4828e..5e6e9c55db5 100644
--- a/extern/ceres/include/ceres/autodiff_cost_function.h
+++ b/extern/ceres/include/ceres/autodiff_cost_function.h
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2019 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -30,7 +30,7 @@
//
// Create CostFunctions as needed by the least squares framework, with
// Jacobians computed via automatic differentiation. For more
-// information on automatic differentation, see the wikipedia article
+// information on automatic differentiation, see the wikipedia article
// at http://en.wikipedia.org/wiki/Automatic_differentiation
//
// To get an auto differentiated cost function, you must define a class with a
@@ -54,7 +54,7 @@
// for a series of measurements, where there is an instance of the cost function
// for each measurement k.
//
-// The actual cost added to the total problem is e^2, or (k - x'k)^2; however,
+// The actual cost added to the total problem is e^2, or (k - x'y)^2; however,
// the squaring is implicitly done by the optimization framework.
//
// To write an auto-differentiable cost function for the above model, first
@@ -90,7 +90,7 @@
// Dimension of x ---------------+ |
// Dimension of y ------------------+
//
-// In this example, there is usually an instance for each measumerent of k.
+// In this example, there is usually an instance for each measurement of k.
//
// In the instantiation above, the template parameters following
// "MyScalarCostFunctor", "1, 2, 2", describe the functor as computing a
@@ -110,12 +110,8 @@
// Dimension of x ------------------------------------+ |
// Dimension of y ---------------------------------------+
//
-// The framework can currently accommodate cost functions of up to 10
-// independent variables, and there is no limit on the dimensionality
-// of each of them.
-//
// WARNING #1: Since the functor will get instantiated with different types for
-// T, you must to convert from other numeric types to T before mixing
+// T, you must convert from other numeric types to T before mixing
// computations with other variables of type T. In the example above, this is
// seen where instead of using k_ directly, k_ is wrapped with T(k_).
//
@@ -129,8 +125,9 @@
#ifndef CERES_PUBLIC_AUTODIFF_COST_FUNCTION_H_
#define CERES_PUBLIC_AUTODIFF_COST_FUNCTION_H_
+#include <memory>
+
#include "ceres/internal/autodiff.h"
-#include "ceres/internal/scoped_ptr.h"
#include "ceres/sized_cost_function.h"
#include "ceres/types.h"
#include "glog/logging.h"
@@ -138,7 +135,7 @@
namespace ceres {
// A cost function which computes the derivative of the cost with respect to
-// the parameters (a.k.a. the jacobian) using an autodifferentiation framework.
+// the parameters (a.k.a. the jacobian) using an auto differentiation framework.
// The first template argument is the functor object, described in the header
// comment. The second argument is the dimension of the residual (or
// ceres::DYNAMIC to indicate it will be set at runtime), and subsequent
@@ -153,27 +150,15 @@ namespace ceres {
// of residuals for a single autodiff cost function at runtime.
template <typename CostFunctor,
int kNumResiduals, // Number of residuals, or ceres::DYNAMIC.
- int N0, // Number of parameters in block 0.
- int N1 = 0, // Number of parameters in block 1.
- int N2 = 0, // Number of parameters in block 2.
- int N3 = 0, // Number of parameters in block 3.
- int N4 = 0, // Number of parameters in block 4.
- int N5 = 0, // Number of parameters in block 5.
- int N6 = 0, // Number of parameters in block 6.
- int N7 = 0, // Number of parameters in block 7.
- int N8 = 0, // Number of parameters in block 8.
- int N9 = 0> // Number of parameters in block 9.
-class AutoDiffCostFunction : public SizedCostFunction<kNumResiduals,
- N0, N1, N2, N3, N4,
- N5, N6, N7, N8, N9> {
+ int... Ns> // Number of parameters in each parameter block.
+class AutoDiffCostFunction : public SizedCostFunction<kNumResiduals, Ns...> {
public:
// Takes ownership of functor. Uses the template-provided value for the
// number of residuals ("kNumResiduals").
- explicit AutoDiffCostFunction(CostFunctor* functor)
- : functor_(functor) {
- CHECK_NE(kNumResiduals, DYNAMIC)
- << "Can't run the fixed-size constructor if the "
- << "number of residuals is set to ceres::DYNAMIC.";
+ explicit AutoDiffCostFunction(CostFunctor* functor) : functor_(functor) {
+ static_assert(kNumResiduals != DYNAMIC,
+ "Can't run the fixed-size constructor if the number of "
+ "residuals is set to ceres::DYNAMIC.");
}
// Takes ownership of functor. Ignores the template-provided
@@ -183,13 +168,10 @@ class AutoDiffCostFunction : public SizedCostFunction<kNumResiduals,
// numbers of residuals at runtime.
AutoDiffCostFunction(CostFunctor* functor, int num_residuals)
: functor_(functor) {
- CHECK_EQ(kNumResiduals, DYNAMIC)
- << "Can't run the dynamic-size constructor if the "
- << "number of residuals is not ceres::DYNAMIC.";
- SizedCostFunction<kNumResiduals,
- N0, N1, N2, N3, N4,
- N5, N6, N7, N8, N9>
- ::set_num_residuals(num_residuals);
+ static_assert(kNumResiduals == DYNAMIC,
+ "Can't run the dynamic-size constructor if the number of "
+ "residuals is not ceres::DYNAMIC.");
+ SizedCostFunction<kNumResiduals, Ns...>::set_num_residuals(num_residuals);
}
virtual ~AutoDiffCostFunction() {}
@@ -197,29 +179,28 @@ class AutoDiffCostFunction : public SizedCostFunction<kNumResiduals,
// Implementation details follow; clients of the autodiff cost function should
// not have to examine below here.
//
- // To handle varardic cost functions, some template magic is needed. It's
+ // To handle variadic cost functions, some template magic is needed. It's
// mostly hidden inside autodiff.h.
- virtual bool Evaluate(double const* const* parameters,
- double* residuals,
- double** jacobians) const {
+ bool Evaluate(double const* const* parameters,
+ double* residuals,
+ double** jacobians) const override {
+ using ParameterDims =
+ typename SizedCostFunction<kNumResiduals, Ns...>::ParameterDims;
+
if (!jacobians) {
- return internal::VariadicEvaluate<
- CostFunctor, double, N0, N1, N2, N3, N4, N5, N6, N7, N8, N9>
- ::Call(*functor_, parameters, residuals);
+ return internal::VariadicEvaluate<ParameterDims>(
+ *functor_, parameters, residuals);
}
- return internal::AutoDiff<CostFunctor, double,
- N0, N1, N2, N3, N4, N5, N6, N7, N8, N9>::Differentiate(
- *functor_,
- parameters,
- SizedCostFunction<kNumResiduals,
- N0, N1, N2, N3, N4,
- N5, N6, N7, N8, N9>::num_residuals(),
- residuals,
- jacobians);
- }
+ return internal::AutoDifferentiate<kNumResiduals, ParameterDims>(
+ *functor_,
+ parameters,
+ SizedCostFunction<kNumResiduals, Ns...>::num_residuals(),
+ residuals,
+ jacobians);
+ };
private:
- internal::scoped_ptr<CostFunctor> functor_;
+ std::unique_ptr<CostFunctor> functor_;
};
} // namespace ceres
diff --git a/extern/ceres/include/ceres/autodiff_first_order_function.h b/extern/ceres/include/ceres/autodiff_first_order_function.h
new file mode 100644
index 00000000000..b98d845655b
--- /dev/null
+++ b/extern/ceres/include/ceres/autodiff_first_order_function.h
@@ -0,0 +1,151 @@
+// 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_AUTODIFF_FIRST_ORDER_FUNCTION_H_
+#define CERES_PUBLIC_AUTODIFF_FIRST_ORDER_FUNCTION_H_
+
+#include <memory>
+
+#include "ceres/first_order_function.h"
+#include "ceres/internal/eigen.h"
+#include "ceres/internal/fixed_array.h"
+#include "ceres/jet.h"
+#include "ceres/types.h"
+
+namespace ceres {
+
+// Create FirstOrderFunctions as needed by the GradientProblem
+// framework, with gradients computed via automatic
+// differentiation. For more information on automatic differentiation,
+// see the wikipedia article at
+// http://en.wikipedia.org/wiki/Automatic_differentiation
+//
+// To get an auto differentiated function, you must define a class
+// with a templated operator() (a functor) that computes the cost
+// function in terms of the template parameter T. The autodiff
+// framework substitutes appropriate "jet" objects for T in order to
+// compute the derivative when necessary, but this is hidden, and you
+// should write the function as if T were a scalar type (e.g. a
+// double-precision floating point number).
+//
+// 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 auto-differentiable FirstOrderFunction for the above model, first
+// define the object
+//
+// class QuadraticCostFunctor {
+// public:
+// explicit QuadraticCostFunctor(double a) : a_(a) {}
+// template <typename T>
+// bool operator()(const T* const xy, T* cost) const {
+// const T* const x = xy;
+// const T* const y = xy + 2;
+// *cost = x[0] * y[0] + x[1] * y[1] - T(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 arrays of T. The
+// output is the last parameter.
+//
+// Then given this class definition, the auto differentiated FirstOrderFunction
+// for it can be constructed as follows.
+//
+// FirstOrderFunction* function =
+// new AutoDiffFirstOrderFunction<QuadraticCostFunctor, 4>(
+// new QuadraticCostFunctor(1.0)));
+//
+// In the instantiation above, the template parameters following
+// "QuadraticCostFunctor", "4", describe the functor as computing a
+// 1-dimensional output from a four dimensional vector.
+//
+// WARNING: Since the functor will get instantiated with different types for
+// T, you must convert from other numeric types to T before mixing
+// computations with other variables of type T. In the example above, this is
+// seen where instead of using a_ directly, a_ is wrapped with T(a_).
+
+template <typename FirstOrderFunctor, int kNumParameters>
+class AutoDiffFirstOrderFunction : public FirstOrderFunction {
+ public:
+ // Takes ownership of functor.
+ explicit AutoDiffFirstOrderFunction(FirstOrderFunctor* functor)
+ : functor_(functor) {
+ static_assert(kNumParameters > 0, "kNumParameters must be positive");
+ }
+
+ virtual ~AutoDiffFirstOrderFunction() {}
+
+ bool Evaluate(const double* const parameters,
+ double* cost,
+ double* gradient) const override {
+ if (gradient == nullptr) {
+ return (*functor_)(parameters, cost);
+ }
+
+ typedef Jet<double, kNumParameters> JetT;
+ internal::FixedArray<JetT, (256 * 7) / sizeof(JetT)> x(kNumParameters);
+ for (int i = 0; i < kNumParameters; ++i) {
+ x[i].a = parameters[i];
+ x[i].v.setZero();
+ x[i].v[i] = 1.0;
+ }
+
+ JetT output;
+ output.a = kImpossibleValue;
+ output.v.setConstant(kImpossibleValue);
+
+ if (!(*functor_)(x.data(), &output)) {
+ return false;
+ }
+
+ *cost = output.a;
+ VectorRef(gradient, kNumParameters) = output.v;
+ return true;
+ }
+
+ int NumParameters() const override { return kNumParameters; }
+
+ private:
+ std::unique_ptr<FirstOrderFunctor> functor_;
+};
+
+} // namespace ceres
+
+#endif // CERES_PUBLIC_AUTODIFF_FIRST_ORDER_FUNCTION_H_
diff --git a/extern/ceres/include/ceres/autodiff_local_parameterization.h b/extern/ceres/include/ceres/autodiff_local_parameterization.h
index 27397e20d3b..d694376fdd1 100644
--- a/extern/ceres/include/ceres/autodiff_local_parameterization.h
+++ b/extern/ceres/include/ceres/autodiff_local_parameterization.h
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2019 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -33,9 +33,10 @@
#ifndef CERES_PUBLIC_AUTODIFF_LOCAL_PARAMETERIZATION_H_
#define CERES_PUBLIC_AUTODIFF_LOCAL_PARAMETERIZATION_H_
-#include "ceres/local_parameterization.h"
+#include <memory>
+
#include "ceres/internal/autodiff.h"
-#include "ceres/internal/scoped_ptr.h"
+#include "ceres/local_parameterization.h"
namespace ceres {
@@ -107,21 +108,20 @@ namespace ceres {
template <typename Functor, int kGlobalSize, int kLocalSize>
class AutoDiffLocalParameterization : public LocalParameterization {
public:
- AutoDiffLocalParameterization() :
- functor_(new Functor()) {}
+ AutoDiffLocalParameterization() : functor_(new Functor()) {}
// Takes ownership of functor.
- explicit AutoDiffLocalParameterization(Functor* functor) :
- functor_(functor) {}
+ explicit AutoDiffLocalParameterization(Functor* functor)
+ : functor_(functor) {}
virtual ~AutoDiffLocalParameterization() {}
- virtual bool Plus(const double* x,
- const double* delta,
- double* x_plus_delta) const {
+ bool Plus(const double* x,
+ const double* delta,
+ double* x_plus_delta) const override {
return (*functor_)(x, delta, x_plus_delta);
}
- virtual bool ComputeJacobian(const double* x, double* jacobian) const {
+ bool ComputeJacobian(const double* x, double* jacobian) const override {
double zero_delta[kLocalSize];
for (int i = 0; i < kLocalSize; ++i) {
zero_delta[i] = 0.0;
@@ -133,20 +133,18 @@ class AutoDiffLocalParameterization : public LocalParameterization {
}
const double* parameter_ptrs[2] = {x, zero_delta};
- double* jacobian_ptrs[2] = { NULL, jacobian };
- return internal::AutoDiff<Functor, double, kGlobalSize, kLocalSize>
- ::Differentiate(*functor_,
- parameter_ptrs,
- kGlobalSize,
- x_plus_delta,
- jacobian_ptrs);
+ double* jacobian_ptrs[2] = {NULL, jacobian};
+ return internal::AutoDifferentiate<
+ kGlobalSize,
+ internal::StaticParameterDims<kGlobalSize, kLocalSize>>(
+ *functor_, parameter_ptrs, kGlobalSize, x_plus_delta, jacobian_ptrs);
}
- virtual int GlobalSize() const { return kGlobalSize; }
- virtual int LocalSize() const { return kLocalSize; }
+ int GlobalSize() const override { return kGlobalSize; }
+ int LocalSize() const override { return kLocalSize; }
private:
- internal::scoped_ptr<Functor> functor_;
+ std::unique_ptr<Functor> functor_;
};
} // namespace ceres
diff --git a/extern/ceres/include/ceres/c_api.h b/extern/ceres/include/ceres/c_api.h
index df7c9b6d671..0e6e590d0f5 100644
--- a/extern/ceres/include/ceres/c_api.h
+++ b/extern/ceres/include/ceres/c_api.h
@@ -1,5 +1,5 @@
/* Ceres Solver - A fast non-linear least squares minimizer
- * Copyright 2015 Google Inc. All rights reserved.
+ * Copyright 2019 Google Inc. All rights reserved.
* http://ceres-solver.org/
*
* Redistribution and use in source and binary forms, with or without
@@ -143,4 +143,4 @@ CERES_EXPORT void ceres_solve(ceres_problem_t* problem);
#include "ceres/internal/reenable_warnings.h"
-#endif /* CERES_PUBLIC_C_API_H_ */
+#endif /* CERES_PUBLIC_C_API_H_ */
diff --git a/extern/ceres/include/ceres/ceres.h b/extern/ceres/include/ceres/ceres.h
index 64ffb99798a..d249351694c 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 2015 Google Inc. All rights reserved.
+// Copyright 2019 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -36,12 +36,18 @@
#include "ceres/autodiff_cost_function.h"
#include "ceres/autodiff_local_parameterization.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/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/gradient_checker.h"
#include "ceres/gradient_problem.h"
#include "ceres/gradient_problem_solver.h"
#include "ceres/iteration_callback.h"
@@ -49,6 +55,7 @@
#include "ceres/local_parameterization.h"
#include "ceres/loss_function.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/sized_cost_function.h"
diff --git a/extern/ceres/include/ceres/conditioned_cost_function.h b/extern/ceres/include/ceres/conditioned_cost_function.h
index 29597d935cb..a57ee209b80 100644
--- a/extern/ceres/include/ceres/conditioned_cost_function.h
+++ b/extern/ceres/include/ceres/conditioned_cost_function.h
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2019 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -34,12 +34,12 @@
#ifndef CERES_PUBLIC_CONDITIONED_COST_FUNCTION_H_
#define CERES_PUBLIC_CONDITIONED_COST_FUNCTION_H_
+#include <memory>
#include <vector>
#include "ceres/cost_function.h"
-#include "ceres/internal/scoped_ptr.h"
-#include "ceres/types.h"
#include "ceres/internal/disable_warnings.h"
+#include "ceres/types.h"
namespace ceres {
@@ -77,17 +77,19 @@ class CERES_EXPORT ConditionedCostFunction : public CostFunction {
// 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.
+ //
+ // The conditioners can repeat.
ConditionedCostFunction(CostFunction* wrapped_cost_function,
const std::vector<CostFunction*>& conditioners,
Ownership ownership);
virtual ~ConditionedCostFunction();
- virtual bool Evaluate(double const* const* parameters,
- double* residuals,
- double** jacobians) const;
+ bool Evaluate(double const* const* parameters,
+ double* residuals,
+ double** jacobians) const override;
private:
- internal::scoped_ptr<CostFunction> wrapped_cost_function_;
+ std::unique_ptr<CostFunction> wrapped_cost_function_;
std::vector<CostFunction*> conditioners_;
Ownership ownership_;
};
diff --git a/extern/ceres/include/ceres/context.h b/extern/ceres/include/ceres/context.h
new file mode 100644
index 00000000000..d08e32b31a8
--- /dev/null
+++ b/extern/ceres/include/ceres/context.h
@@ -0,0 +1,56 @@
+// 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: vitus@google.com (Michael Vitus)
+
+#ifndef CERES_PUBLIC_CONTEXT_H_
+#define CERES_PUBLIC_CONTEXT_H_
+
+namespace ceres {
+
+// A global context for processing data in Ceres. This provides a mechanism to
+// allow Ceres to reuse items that are expensive to create between multiple
+// calls; for example, thread pools. The same Context can be used on multiple
+// 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 {
+ public:
+ Context() {}
+ Context(const Context&) = delete;
+ void operator=(const Context&) = delete;
+
+ virtual ~Context() {}
+
+ // Creates a context object and the caller takes ownership.
+ static Context* Create();
+};
+
+} // namespace ceres
+
+#endif // CERES_PUBLIC_CONTEXT_H_
diff --git a/extern/ceres/include/ceres/cost_function.h b/extern/ceres/include/ceres/cost_function.h
index f051a897c0d..d1550c119e8 100644
--- a/extern/ceres/include/ceres/cost_function.h
+++ b/extern/ceres/include/ceres/cost_function.h
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2019 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -44,18 +44,18 @@
#ifndef CERES_PUBLIC_COST_FUNCTION_H_
#define CERES_PUBLIC_COST_FUNCTION_H_
+#include <cstdint>
#include <vector>
-#include "ceres/internal/macros.h"
-#include "ceres/internal/port.h"
-#include "ceres/types.h"
+
#include "ceres/internal/disable_warnings.h"
+#include "ceres/internal/port.h"
namespace ceres {
// This class implements the computation of the cost (a.k.a. residual) terms as
// a function of the input (control) variables, and is the interface for users
// to describe their least squares problem to Ceres. In other words, this is the
-// modelling layer between users and the Ceres optimizer. The signature of the
+// modeling layer between users and the Ceres optimizer. The signature of the
// function (number and sizes of input parameter blocks and number of outputs)
// is stored in parameter_block_sizes_ and num_residuals_ respectively. User
// code inheriting from this class is expected to set these two members with the
@@ -64,6 +64,8 @@ namespace ceres {
class CERES_EXPORT CostFunction {
public:
CostFunction() : num_residuals_(0) {}
+ CostFunction(const CostFunction&) = delete;
+ void operator=(const CostFunction&) = delete;
virtual ~CostFunction() {}
@@ -115,29 +117,24 @@ class CERES_EXPORT CostFunction {
double* residuals,
double** jacobians) const = 0;
- const std::vector<int32>& parameter_block_sizes() const {
+ const std::vector<int32_t>& parameter_block_sizes() const {
return parameter_block_sizes_;
}
- int num_residuals() const {
- return num_residuals_;
- }
+ int num_residuals() const { return num_residuals_; }
protected:
- std::vector<int32>* mutable_parameter_block_sizes() {
+ std::vector<int32_t>* mutable_parameter_block_sizes() {
return &parameter_block_sizes_;
}
- void set_num_residuals(int num_residuals) {
- num_residuals_ = num_residuals;
- }
+ void set_num_residuals(int num_residuals) { num_residuals_ = num_residuals; }
private:
// Cost function signature metadata: number of inputs & their sizes,
// number of outputs (residuals).
- std::vector<int32> parameter_block_sizes_;
+ std::vector<int32_t> parameter_block_sizes_;
int num_residuals_;
- CERES_DISALLOW_COPY_AND_ASSIGN(CostFunction);
};
} // namespace ceres
diff --git a/extern/ceres/include/ceres/cost_function_to_functor.h b/extern/ceres/include/ceres/cost_function_to_functor.h
index d2dc94725e4..1beeb906063 100644
--- a/extern/ceres/include/ceres/cost_function_to_functor.h
+++ b/extern/ceres/include/ceres/cost_function_to_functor.h
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2019 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -30,7 +30,7 @@
//
// CostFunctionToFunctor is an adapter class that allows users to use
// SizedCostFunction objects in templated functors which are to be used for
-// automatic differentiation. This allows the user to seamlessly mix
+// automatic differentiation. This allows the user to seamlessly mix
// analytic, numeric and automatic differentiation.
//
// For example, let us assume that
@@ -38,16 +38,15 @@
// class IntrinsicProjection : public SizedCostFunction<2, 5, 3> {
// public:
// IntrinsicProjection(const double* observation);
-// virtual bool Evaluate(double const* const* parameters,
-// double* residuals,
-// double** jacobians) const;
+// bool Evaluate(double const* const* parameters,
+// double* residuals,
+// double** jacobians) const override;
// };
//
// is a cost function that implements the projection of a point in its
// local coordinate system onto its image plane and subtracts it from
// the observed point projection. It can compute its residual and
-// either via analytic or numerical differentiation can compute its
-// jacobians.
+// jacobians either via analytic or numerical differentiation.
//
// Now we would like to compose the action of this CostFunction with
// the action of camera extrinsics, i.e., rotation and
@@ -87,594 +86,83 @@
#ifndef CERES_PUBLIC_COST_FUNCTION_TO_FUNCTOR_H_
#define CERES_PUBLIC_COST_FUNCTION_TO_FUNCTOR_H_
+#include <cstdint>
#include <numeric>
+#include <tuple>
+#include <utility>
#include <vector>
#include "ceres/cost_function.h"
#include "ceres/dynamic_cost_function_to_functor.h"
#include "ceres/internal/fixed_array.h"
+#include "ceres/internal/parameter_dims.h"
#include "ceres/internal/port.h"
-#include "ceres/internal/scoped_ptr.h"
+#include "ceres/types.h"
namespace ceres {
-template <int kNumResiduals,
- int N0, int N1 = 0, int N2 = 0, int N3 = 0, int N4 = 0,
- int N5 = 0, int N6 = 0, int N7 = 0, int N8 = 0, int N9 = 0>
+template <int kNumResiduals, int... Ns>
class CostFunctionToFunctor {
public:
// Takes ownership of cost_function.
explicit CostFunctionToFunctor(CostFunction* cost_function)
: cost_functor_(cost_function) {
- CHECK_NOTNULL(cost_function);
+ CHECK(cost_function != nullptr);
CHECK(kNumResiduals > 0 || kNumResiduals == DYNAMIC);
- // This block breaks the 80 column rule to keep it somewhat readable.
- CHECK((!N1 && !N2 && !N3 && !N4 && !N5 && !N6 && !N7 && !N8 && !N9) ||
- ((N1 > 0) && !N2 && !N3 && !N4 && !N5 && !N6 && !N7 && !N8 && !N9) ||
- ((N1 > 0) && (N2 > 0) && !N3 && !N4 && !N5 && !N6 && !N7 && !N8 && !N9) || // NOLINT
- ((N1 > 0) && (N2 > 0) && (N3 > 0) && !N4 && !N5 && !N6 && !N7 && !N8 && !N9) || // NOLINT
- ((N1 > 0) && (N2 > 0) && (N3 > 0) && (N4 > 0) && !N5 && !N6 && !N7 && !N8 && !N9) || // NOLINT
- ((N1 > 0) && (N2 > 0) && (N3 > 0) && (N4 > 0) && (N5 > 0) && !N6 && !N7 && !N8 && !N9) || // NOLINT
- ((N1 > 0) && (N2 > 0) && (N3 > 0) && (N4 > 0) && (N5 > 0) && (N6 > 0) && !N7 && !N8 && !N9) || // NOLINT
- ((N1 > 0) && (N2 > 0) && (N3 > 0) && (N4 > 0) && (N5 > 0) && (N6 > 0) && (N7 > 0) && !N8 && !N9) || // NOLINT
- ((N1 > 0) && (N2 > 0) && (N3 > 0) && (N4 > 0) && (N5 > 0) && (N6 > 0) && (N7 > 0) && (N8 > 0) && !N9) || // NOLINT
- ((N1 > 0) && (N2 > 0) && (N3 > 0) && (N4 > 0) && (N5 > 0) && (N6 > 0) && (N7 > 0) && (N8 > 0) && (N9 > 0))) // NOLINT
- << "Zero block cannot precede a non-zero block. Block sizes are "
- << "(ignore trailing 0s): " << N0 << ", " << N1 << ", " << N2 << ", "
- << N3 << ", " << N4 << ", " << N5 << ", " << N6 << ", " << N7 << ", "
- << N8 << ", " << N9;
-
- const std::vector<int32>& parameter_block_sizes =
+ const std::vector<int32_t>& parameter_block_sizes =
cost_function->parameter_block_sizes();
- const int num_parameter_blocks =
- (N0 > 0) + (N1 > 0) + (N2 > 0) + (N3 > 0) + (N4 > 0) +
- (N5 > 0) + (N6 > 0) + (N7 > 0) + (N8 > 0) + (N9 > 0);
+ const int num_parameter_blocks = ParameterDims::kNumParameterBlocks;
CHECK_EQ(static_cast<int>(parameter_block_sizes.size()),
num_parameter_blocks);
- CHECK_EQ(N0, parameter_block_sizes[0]);
- if (parameter_block_sizes.size() > 1) CHECK_EQ(N1, parameter_block_sizes[1]); // NOLINT
- if (parameter_block_sizes.size() > 2) CHECK_EQ(N2, parameter_block_sizes[2]); // NOLINT
- if (parameter_block_sizes.size() > 3) CHECK_EQ(N3, parameter_block_sizes[3]); // NOLINT
- if (parameter_block_sizes.size() > 4) CHECK_EQ(N4, parameter_block_sizes[4]); // NOLINT
- if (parameter_block_sizes.size() > 5) CHECK_EQ(N5, parameter_block_sizes[5]); // NOLINT
- if (parameter_block_sizes.size() > 6) CHECK_EQ(N6, parameter_block_sizes[6]); // NOLINT
- if (parameter_block_sizes.size() > 7) CHECK_EQ(N7, parameter_block_sizes[7]); // NOLINT
- if (parameter_block_sizes.size() > 8) CHECK_EQ(N8, parameter_block_sizes[8]); // NOLINT
- if (parameter_block_sizes.size() > 9) CHECK_EQ(N9, parameter_block_sizes[9]); // NOLINT
-
- CHECK_EQ(accumulate(parameter_block_sizes.begin(),
- parameter_block_sizes.end(), 0),
- N0 + N1 + N2 + N3 + N4 + N5 + N6 + N7 + N8 + N9);
- }
-
- bool operator()(const double* x0, double* residuals) const {
- CHECK_NE(N0, 0);
- CHECK_EQ(N1, 0);
- CHECK_EQ(N2, 0);
- CHECK_EQ(N3, 0);
- CHECK_EQ(N4, 0);
- CHECK_EQ(N5, 0);
- CHECK_EQ(N6, 0);
- CHECK_EQ(N7, 0);
- CHECK_EQ(N8, 0);
- CHECK_EQ(N9, 0);
-
- return cost_functor_(&x0, residuals);
- }
-
- bool operator()(const double* x0,
- const double* x1,
- double* residuals) const {
- CHECK_NE(N0, 0);
- CHECK_NE(N1, 0);
- CHECK_EQ(N2, 0);
- CHECK_EQ(N3, 0);
- CHECK_EQ(N4, 0);
- CHECK_EQ(N5, 0);
- CHECK_EQ(N6, 0);
- CHECK_EQ(N7, 0);
- CHECK_EQ(N8, 0);
- CHECK_EQ(N9, 0);
- internal::FixedArray<const double*> parameter_blocks(2);
- parameter_blocks[0] = x0;
- parameter_blocks[1] = x1;
- return cost_functor_(parameter_blocks.get(), residuals);
- }
-
- bool operator()(const double* x0,
- const double* x1,
- const double* x2,
- double* residuals) const {
- CHECK_NE(N0, 0);
- CHECK_NE(N1, 0);
- CHECK_NE(N2, 0);
- CHECK_EQ(N3, 0);
- CHECK_EQ(N4, 0);
- CHECK_EQ(N5, 0);
- CHECK_EQ(N6, 0);
- CHECK_EQ(N7, 0);
- CHECK_EQ(N8, 0);
- CHECK_EQ(N9, 0);
- internal::FixedArray<const double*> parameter_blocks(3);
- parameter_blocks[0] = x0;
- parameter_blocks[1] = x1;
- parameter_blocks[2] = x2;
- return cost_functor_(parameter_blocks.get(), residuals);
- }
+ if (parameter_block_sizes.size() == num_parameter_blocks) {
+ for (int block = 0; block < num_parameter_blocks; ++block) {
+ CHECK_EQ(ParameterDims::GetDim(block), parameter_block_sizes[block])
+ << "Parameter block size missmatch. The specified static parameter "
+ "block dimension does not match the one from the cost function.";
+ }
+ }
- bool operator()(const double* x0,
- const double* x1,
- const double* x2,
- const double* x3,
- double* residuals) const {
- CHECK_NE(N0, 0);
- CHECK_NE(N1, 0);
- CHECK_NE(N2, 0);
- CHECK_NE(N3, 0);
- CHECK_EQ(N4, 0);
- CHECK_EQ(N5, 0);
- CHECK_EQ(N6, 0);
- CHECK_EQ(N7, 0);
- CHECK_EQ(N8, 0);
- CHECK_EQ(N9, 0);
- internal::FixedArray<const double*> parameter_blocks(4);
- parameter_blocks[0] = x0;
- parameter_blocks[1] = x1;
- parameter_blocks[2] = x2;
- parameter_blocks[3] = x3;
- return cost_functor_(parameter_blocks.get(), residuals);
+ CHECK_EQ(accumulate(
+ parameter_block_sizes.begin(), parameter_block_sizes.end(), 0),
+ ParameterDims::kNumParameters);
}
- bool operator()(const double* x0,
- const double* x1,
- const double* x2,
- const double* x3,
- const double* x4,
- double* residuals) const {
- CHECK_NE(N0, 0);
- CHECK_NE(N1, 0);
- CHECK_NE(N2, 0);
- CHECK_NE(N3, 0);
- CHECK_NE(N4, 0);
- CHECK_EQ(N5, 0);
- CHECK_EQ(N6, 0);
- CHECK_EQ(N7, 0);
- CHECK_EQ(N8, 0);
- CHECK_EQ(N9, 0);
- internal::FixedArray<const double*> parameter_blocks(5);
- parameter_blocks[0] = x0;
- parameter_blocks[1] = x1;
- parameter_blocks[2] = x2;
- parameter_blocks[3] = x3;
- parameter_blocks[4] = x4;
- return cost_functor_(parameter_blocks.get(), residuals);
- }
+ template <typename T, typename... Ts>
+ bool operator()(const T* p1, Ts*... ps) const {
+ // Add one because of residual block.
+ static_assert(sizeof...(Ts) + 1 == ParameterDims::kNumParameterBlocks + 1,
+ "Invalid number of parameter blocks specified.");
- bool operator()(const double* x0,
- const double* x1,
- const double* x2,
- const double* x3,
- const double* x4,
- const double* x5,
- double* residuals) const {
- CHECK_NE(N0, 0);
- CHECK_NE(N1, 0);
- CHECK_NE(N2, 0);
- CHECK_NE(N3, 0);
- CHECK_NE(N4, 0);
- CHECK_NE(N5, 0);
- CHECK_EQ(N6, 0);
- CHECK_EQ(N7, 0);
- CHECK_EQ(N8, 0);
- CHECK_EQ(N9, 0);
- internal::FixedArray<const double*> parameter_blocks(6);
- parameter_blocks[0] = x0;
- parameter_blocks[1] = x1;
- parameter_blocks[2] = x2;
- parameter_blocks[3] = x3;
- parameter_blocks[4] = x4;
- parameter_blocks[5] = x5;
- return cost_functor_(parameter_blocks.get(), residuals);
- }
+ auto params = std::make_tuple(p1, ps...);
- bool operator()(const double* x0,
- const double* x1,
- const double* x2,
- const double* x3,
- const double* x4,
- const double* x5,
- const double* x6,
- double* residuals) const {
- CHECK_NE(N0, 0);
- CHECK_NE(N1, 0);
- CHECK_NE(N2, 0);
- CHECK_NE(N3, 0);
- CHECK_NE(N4, 0);
- CHECK_NE(N5, 0);
- CHECK_NE(N6, 0);
- CHECK_EQ(N7, 0);
- CHECK_EQ(N8, 0);
- CHECK_EQ(N9, 0);
- internal::FixedArray<const double*> parameter_blocks(7);
- parameter_blocks[0] = x0;
- parameter_blocks[1] = x1;
- parameter_blocks[2] = x2;
- parameter_blocks[3] = x3;
- parameter_blocks[4] = x4;
- parameter_blocks[5] = x5;
- parameter_blocks[6] = x6;
- return cost_functor_(parameter_blocks.get(), residuals);
- }
+ // Extract residual pointer from params. The residual pointer is the
+ // last pointer.
+ constexpr int kResidualIndex = ParameterDims::kNumParameterBlocks;
+ T* residuals = std::get<kResidualIndex>(params);
- bool operator()(const double* x0,
- const double* x1,
- const double* x2,
- const double* x3,
- const double* x4,
- const double* x5,
- const double* x6,
- const double* x7,
- double* residuals) const {
- CHECK_NE(N0, 0);
- CHECK_NE(N1, 0);
- CHECK_NE(N2, 0);
- CHECK_NE(N3, 0);
- CHECK_NE(N4, 0);
- CHECK_NE(N5, 0);
- CHECK_NE(N6, 0);
- CHECK_NE(N7, 0);
- CHECK_EQ(N8, 0);
- CHECK_EQ(N9, 0);
- internal::FixedArray<const double*> parameter_blocks(8);
- parameter_blocks[0] = x0;
- parameter_blocks[1] = x1;
- parameter_blocks[2] = x2;
- parameter_blocks[3] = x3;
- parameter_blocks[4] = x4;
- parameter_blocks[5] = x5;
- parameter_blocks[6] = x6;
- parameter_blocks[7] = x7;
- return cost_functor_(parameter_blocks.get(), residuals);
- }
-
- bool operator()(const double* x0,
- const double* x1,
- const double* x2,
- const double* x3,
- const double* x4,
- const double* x5,
- const double* x6,
- const double* x7,
- const double* x8,
- double* residuals) const {
- CHECK_NE(N0, 0);
- CHECK_NE(N1, 0);
- CHECK_NE(N2, 0);
- CHECK_NE(N3, 0);
- CHECK_NE(N4, 0);
- CHECK_NE(N5, 0);
- CHECK_NE(N6, 0);
- CHECK_NE(N7, 0);
- CHECK_NE(N8, 0);
- CHECK_EQ(N9, 0);
- internal::FixedArray<const double*> parameter_blocks(9);
- parameter_blocks[0] = x0;
- parameter_blocks[1] = x1;
- parameter_blocks[2] = x2;
- parameter_blocks[3] = x3;
- parameter_blocks[4] = x4;
- parameter_blocks[5] = x5;
- parameter_blocks[6] = x6;
- parameter_blocks[7] = x7;
- parameter_blocks[8] = x8;
- return cost_functor_(parameter_blocks.get(), residuals);
- }
-
- bool operator()(const double* x0,
- const double* x1,
- const double* x2,
- const double* x3,
- const double* x4,
- const double* x5,
- const double* x6,
- const double* x7,
- const double* x8,
- const double* x9,
- double* residuals) const {
- CHECK_NE(N0, 0);
- CHECK_NE(N1, 0);
- CHECK_NE(N2, 0);
- CHECK_NE(N3, 0);
- CHECK_NE(N4, 0);
- CHECK_NE(N5, 0);
- CHECK_NE(N6, 0);
- CHECK_NE(N7, 0);
- CHECK_NE(N8, 0);
- CHECK_NE(N9, 0);
- internal::FixedArray<const double*> parameter_blocks(10);
- parameter_blocks[0] = x0;
- parameter_blocks[1] = x1;
- parameter_blocks[2] = x2;
- parameter_blocks[3] = x3;
- parameter_blocks[4] = x4;
- parameter_blocks[5] = x5;
- parameter_blocks[6] = x6;
- parameter_blocks[7] = x7;
- parameter_blocks[8] = x8;
- parameter_blocks[9] = x9;
- return cost_functor_(parameter_blocks.get(), residuals);
- }
+ // Extract parameter block pointers from params.
+ using Indices =
+ std::make_integer_sequence<int,
+ ParameterDims::kNumParameterBlocks>;
+ std::array<const T*, ParameterDims::kNumParameterBlocks> parameter_blocks =
+ GetParameterPointers<T>(params, Indices());
- template <typename JetT>
- bool operator()(const JetT* x0, JetT* residuals) const {
- CHECK_NE(N0, 0);
- CHECK_EQ(N1, 0);
- CHECK_EQ(N2, 0);
- CHECK_EQ(N3, 0);
- CHECK_EQ(N4, 0);
- CHECK_EQ(N5, 0);
- CHECK_EQ(N6, 0);
- CHECK_EQ(N7, 0);
- CHECK_EQ(N8, 0);
- CHECK_EQ(N9, 0);
- return cost_functor_(&x0, residuals);
+ return cost_functor_(parameter_blocks.data(), residuals);
}
- template <typename JetT>
- bool operator()(const JetT* x0,
- const JetT* x1,
- JetT* residuals) const {
- CHECK_NE(N0, 0);
- CHECK_NE(N1, 0);
- CHECK_EQ(N2, 0);
- CHECK_EQ(N3, 0);
- CHECK_EQ(N4, 0);
- CHECK_EQ(N5, 0);
- CHECK_EQ(N6, 0);
- CHECK_EQ(N7, 0);
- CHECK_EQ(N8, 0);
- CHECK_EQ(N9, 0);
- internal::FixedArray<const JetT*> jets(2);
- jets[0] = x0;
- jets[1] = x1;
- return cost_functor_(jets.get(), residuals);
- }
-
- template <typename JetT>
- bool operator()(const JetT* x0,
- const JetT* x1,
- const JetT* x2,
- JetT* residuals) const {
- CHECK_NE(N0, 0);
- CHECK_NE(N1, 0);
- CHECK_NE(N2, 0);
- CHECK_EQ(N3, 0);
- CHECK_EQ(N4, 0);
- CHECK_EQ(N5, 0);
- CHECK_EQ(N6, 0);
- CHECK_EQ(N7, 0);
- CHECK_EQ(N8, 0);
- CHECK_EQ(N9, 0);
- internal::FixedArray<const JetT*> jets(3);
- jets[0] = x0;
- jets[1] = x1;
- jets[2] = x2;
- return cost_functor_(jets.get(), residuals);
- }
-
- template <typename JetT>
- bool operator()(const JetT* x0,
- const JetT* x1,
- const JetT* x2,
- const JetT* x3,
- JetT* residuals) const {
- CHECK_NE(N0, 0);
- CHECK_NE(N1, 0);
- CHECK_NE(N2, 0);
- CHECK_NE(N3, 0);
- CHECK_EQ(N4, 0);
- CHECK_EQ(N5, 0);
- CHECK_EQ(N6, 0);
- CHECK_EQ(N7, 0);
- CHECK_EQ(N8, 0);
- CHECK_EQ(N9, 0);
- internal::FixedArray<const JetT*> jets(4);
- jets[0] = x0;
- jets[1] = x1;
- jets[2] = x2;
- jets[3] = x3;
- return cost_functor_(jets.get(), residuals);
- }
-
- template <typename JetT>
- bool operator()(const JetT* x0,
- const JetT* x1,
- const JetT* x2,
- const JetT* x3,
- const JetT* x4,
- JetT* residuals) const {
- CHECK_NE(N0, 0);
- CHECK_NE(N1, 0);
- CHECK_NE(N2, 0);
- CHECK_NE(N3, 0);
- CHECK_NE(N4, 0);
- CHECK_EQ(N5, 0);
- CHECK_EQ(N6, 0);
- CHECK_EQ(N7, 0);
- CHECK_EQ(N8, 0);
- CHECK_EQ(N9, 0);
- internal::FixedArray<const JetT*> jets(5);
- jets[0] = x0;
- jets[1] = x1;
- jets[2] = x2;
- jets[3] = x3;
- jets[4] = x4;
- return cost_functor_(jets.get(), residuals);
- }
-
- template <typename JetT>
- bool operator()(const JetT* x0,
- const JetT* x1,
- const JetT* x2,
- const JetT* x3,
- const JetT* x4,
- const JetT* x5,
- JetT* residuals) const {
- CHECK_NE(N0, 0);
- CHECK_NE(N1, 0);
- CHECK_NE(N2, 0);
- CHECK_NE(N3, 0);
- CHECK_NE(N4, 0);
- CHECK_NE(N5, 0);
- CHECK_EQ(N6, 0);
- CHECK_EQ(N7, 0);
- CHECK_EQ(N8, 0);
- CHECK_EQ(N9, 0);
- internal::FixedArray<const JetT*> jets(6);
- jets[0] = x0;
- jets[1] = x1;
- jets[2] = x2;
- jets[3] = x3;
- jets[4] = x4;
- jets[5] = x5;
- return cost_functor_(jets.get(), residuals);
- }
-
- template <typename JetT>
- bool operator()(const JetT* x0,
- const JetT* x1,
- const JetT* x2,
- const JetT* x3,
- const JetT* x4,
- const JetT* x5,
- const JetT* x6,
- JetT* residuals) const {
- CHECK_NE(N0, 0);
- CHECK_NE(N1, 0);
- CHECK_NE(N2, 0);
- CHECK_NE(N3, 0);
- CHECK_NE(N4, 0);
- CHECK_NE(N5, 0);
- CHECK_NE(N6, 0);
- CHECK_EQ(N7, 0);
- CHECK_EQ(N8, 0);
- CHECK_EQ(N9, 0);
- internal::FixedArray<const JetT*> jets(7);
- jets[0] = x0;
- jets[1] = x1;
- jets[2] = x2;
- jets[3] = x3;
- jets[4] = x4;
- jets[5] = x5;
- jets[6] = x6;
- return cost_functor_(jets.get(), residuals);
- }
-
- template <typename JetT>
- bool operator()(const JetT* x0,
- const JetT* x1,
- const JetT* x2,
- const JetT* x3,
- const JetT* x4,
- const JetT* x5,
- const JetT* x6,
- const JetT* x7,
- JetT* residuals) const {
- CHECK_NE(N0, 0);
- CHECK_NE(N1, 0);
- CHECK_NE(N2, 0);
- CHECK_NE(N3, 0);
- CHECK_NE(N4, 0);
- CHECK_NE(N5, 0);
- CHECK_NE(N6, 0);
- CHECK_NE(N7, 0);
- CHECK_EQ(N8, 0);
- CHECK_EQ(N9, 0);
- internal::FixedArray<const JetT*> jets(8);
- jets[0] = x0;
- jets[1] = x1;
- jets[2] = x2;
- jets[3] = x3;
- jets[4] = x4;
- jets[5] = x5;
- jets[6] = x6;
- jets[7] = x7;
- return cost_functor_(jets.get(), residuals);
- }
-
- template <typename JetT>
- bool operator()(const JetT* x0,
- const JetT* x1,
- const JetT* x2,
- const JetT* x3,
- const JetT* x4,
- const JetT* x5,
- const JetT* x6,
- const JetT* x7,
- const JetT* x8,
- JetT* residuals) const {
- CHECK_NE(N0, 0);
- CHECK_NE(N1, 0);
- CHECK_NE(N2, 0);
- CHECK_NE(N3, 0);
- CHECK_NE(N4, 0);
- CHECK_NE(N5, 0);
- CHECK_NE(N6, 0);
- CHECK_NE(N7, 0);
- CHECK_NE(N8, 0);
- CHECK_EQ(N9, 0);
- internal::FixedArray<const JetT*> jets(9);
- jets[0] = x0;
- jets[1] = x1;
- jets[2] = x2;
- jets[3] = x3;
- jets[4] = x4;
- jets[5] = x5;
- jets[6] = x6;
- jets[7] = x7;
- jets[8] = x8;
- return cost_functor_(jets.get(), residuals);
- }
+ private:
+ using ParameterDims = internal::StaticParameterDims<Ns...>;
- template <typename JetT>
- bool operator()(const JetT* x0,
- const JetT* x1,
- const JetT* x2,
- const JetT* x3,
- const JetT* x4,
- const JetT* x5,
- const JetT* x6,
- const JetT* x7,
- const JetT* x8,
- const JetT* x9,
- JetT* residuals) const {
- CHECK_NE(N0, 0);
- CHECK_NE(N1, 0);
- CHECK_NE(N2, 0);
- CHECK_NE(N3, 0);
- CHECK_NE(N4, 0);
- CHECK_NE(N5, 0);
- CHECK_NE(N6, 0);
- CHECK_NE(N7, 0);
- CHECK_NE(N8, 0);
- CHECK_NE(N9, 0);
- internal::FixedArray<const JetT*> jets(10);
- jets[0] = x0;
- jets[1] = x1;
- jets[2] = x2;
- jets[3] = x3;
- jets[4] = x4;
- jets[5] = x5;
- jets[6] = x6;
- jets[7] = x7;
- jets[8] = x8;
- jets[9] = x9;
- return cost_functor_(jets.get(), residuals);
+ template <typename T, typename Tuple, int... Indices>
+ static std::array<const T*, ParameterDims::kNumParameterBlocks>
+ GetParameterPointers(const Tuple& paramPointers,
+ std::integer_sequence<int, Indices...>) {
+ return std::array<const T*, ParameterDims::kNumParameterBlocks>{
+ {std::get<Indices>(paramPointers)...}};
}
- private:
DynamicCostFunctionToFunctor cost_functor_;
};
diff --git a/extern/ceres/include/ceres/covariance.h b/extern/ceres/include/ceres/covariance.h
index 930f96cf3ae..99825c425ad 100644
--- a/extern/ceres/include/ceres/covariance.h
+++ b/extern/ceres/include/ceres/covariance.h
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2019 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -31,12 +31,13 @@
#ifndef CERES_PUBLIC_COVARIANCE_H_
#define CERES_PUBLIC_COVARIANCE_H_
+#include <memory>
#include <utility>
#include <vector>
+
+#include "ceres/internal/disable_warnings.h"
#include "ceres/internal/port.h"
-#include "ceres/internal/scoped_ptr.h"
#include "ceres/types.h"
-#include "ceres/internal/disable_warnings.h"
namespace ceres {
@@ -60,7 +61,7 @@ class CovarianceImpl;
// Background
// ==========
// One way to assess the quality of the solution returned by a
-// non-linear least squares solve is to analyze the covariance of the
+// non-linear least squares solver is to analyze the covariance of the
// solution.
//
// Let us consider the non-linear regression problem
@@ -158,7 +159,7 @@ class CovarianceImpl;
// Gauge Invariance
// ----------------
// In structure from motion (3D reconstruction) problems, the
-// reconstruction is ambiguous upto a similarity transform. This is
+// reconstruction is ambiguous up to a similarity transform. This is
// known as a Gauge Ambiguity. Handling Gauges correctly requires the
// use of SVD or custom inversion algorithms. For small problems the
// user can use the dense algorithm. For more details see
@@ -183,7 +184,7 @@ class CovarianceImpl;
// Covariance::Options options;
// Covariance covariance(options);
//
-// std::vector<std::pair<const double*, const double*> > covariance_blocks;
+// std::vector<std::pair<const double*, const double*>> covariance_blocks;
// covariance_blocks.push_back(make_pair(x, x));
// covariance_blocks.push_back(make_pair(y, y));
// covariance_blocks.push_back(make_pair(x, y));
@@ -200,19 +201,19 @@ class CovarianceImpl;
class CERES_EXPORT Covariance {
public:
struct CERES_EXPORT Options {
- Options()
-#ifndef CERES_NO_SUITESPARSE
- : algorithm_type(SUITE_SPARSE_QR),
+ // Sparse linear algebra library to use when a sparse matrix
+ // factorization is being used to compute the covariance matrix.
+ //
+ // Currently this only applies to SPARSE_QR.
+ SparseLinearAlgebraLibraryType sparse_linear_algebra_library_type =
+#if !defined(CERES_NO_SUITESPARSE)
+ SUITE_SPARSE;
#else
- : algorithm_type(EIGEN_SPARSE_QR),
+ // Eigen's QR factorization is always available.
+ EIGEN_SPARSE;
#endif
- min_reciprocal_condition_number(1e-14),
- null_space_rank(0),
- num_threads(1),
- apply_loss_function(true) {
- }
- // Ceres supports three different algorithms for covariance
+ // Ceres supports two different algorithms for covariance
// estimation, which represent different tradeoffs in speed,
// accuracy and reliability.
//
@@ -229,23 +230,20 @@ class CERES_EXPORT Covariance {
// for small to moderate sized problems. It can handle
// full-rank as well as rank deficient Jacobians.
//
- // 2. EIGEN_SPARSE_QR uses the sparse QR factorization algorithm
- // in Eigen to compute the decomposition
+ // 2. SPARSE_QR uses the sparse QR factorization algorithm
+ // to compute the decomposition
//
// Q * R = J
//
// [J'J]^-1 = [R*R']^-1
//
- // It is a moderately fast algorithm for sparse matrices.
- //
- // 3. SUITE_SPARSE_QR uses the SuiteSparseQR sparse QR
- // factorization algorithm. It uses dense linear algebra and is
- // multi threaded, so for large sparse sparse matrices it is
- // significantly faster than EIGEN_SPARSE_QR.
- //
- // Neither EIGEN_SPARSE_QR not SUITE_SPARSE_QR are capable of
- // computing the covariance if the Jacobian is rank deficient.
- CovarianceAlgorithmType algorithm_type;
+ // SPARSE_QR is not capable of computing the covariance if the
+ // Jacobian is rank deficient. Depending on the value of
+ // Covariance::Options::sparse_linear_algebra_library_type, either
+ // Eigen's Sparse QR factorization algorithm will be used or
+ // SuiteSparse's high performance SuiteSparseQR algorithm will be
+ // used.
+ CovarianceAlgorithmType algorithm_type = SPARSE_QR;
// If the Jacobian matrix is near singular, then inverting J'J
// will result in unreliable results, e.g, if
@@ -270,7 +268,7 @@ class CERES_EXPORT Covariance {
// where min_sigma and max_sigma are the minimum and maxiumum
// singular values of J respectively.
//
- // 2. SUITE_SPARSE_QR and EIGEN_SPARSE_QR
+ // 2. SPARSE_QR
//
// rank(J) < num_col(J)
//
@@ -278,7 +276,7 @@ class CERES_EXPORT Covariance {
// sparse QR factorization algorithm. It is a fairly reliable
// indication of rank deficiency.
//
- double min_reciprocal_condition_number;
+ double min_reciprocal_condition_number = 1e-14;
// When using DENSE_SVD, the user has more control in dealing with
// singular and near singular covariance matrices.
@@ -313,9 +311,9 @@ class CERES_EXPORT Covariance {
//
// This option has no effect on the SUITE_SPARSE_QR and
// EIGEN_SPARSE_QR algorithms.
- int null_space_rank;
+ int null_space_rank = 0;
- int num_threads;
+ int num_threads = 1;
// Even though the residual blocks in the problem may contain loss
// functions, setting apply_loss_function to false will turn off
@@ -323,7 +321,7 @@ class CERES_EXPORT Covariance {
// function and in turn its effect on the covariance.
//
// TODO(sameergaarwal): Expand this based on Jim's experiments.
- bool apply_loss_function;
+ bool apply_loss_function = true;
};
explicit Covariance(const Options& options);
@@ -352,10 +350,9 @@ class CERES_EXPORT Covariance {
// covariance computation. Please see the documentation for
// Covariance::Options for more on the conditions under which this
// function returns false.
- bool Compute(
- const std::vector<std::pair<const double*,
- const double*> >& covariance_blocks,
- Problem* problem);
+ bool Compute(const std::vector<std::pair<const double*, const double*>>&
+ covariance_blocks,
+ Problem* problem);
// Compute a part of the covariance matrix.
//
@@ -428,8 +425,8 @@ class CERES_EXPORT Covariance {
// a square matrix whose row and column count is equal to the sum of
// the sizes of the individual parameter blocks. The covariance
// matrix will be a row-major matrix.
- bool GetCovarianceMatrix(const std::vector<const double *> &parameter_blocks,
- double *covariance_matrix);
+ bool GetCovarianceMatrix(const std::vector<const double*>& parameter_blocks,
+ double* covariance_matrix) const;
// Return the covariance matrix corresponding to parameter_blocks
// in the tangent space if a local parameterization is associated
@@ -448,10 +445,10 @@ class CERES_EXPORT Covariance {
// blocks. The covariance matrix will be a row-major matrix.
bool GetCovarianceMatrixInTangentSpace(
const std::vector<const double*>& parameter_blocks,
- double* covariance_matrix);
+ double* covariance_matrix) const;
private:
- internal::scoped_ptr<internal::CovarianceImpl> impl_;
+ std::unique_ptr<internal::CovarianceImpl> impl_;
};
} // namespace ceres
diff --git a/extern/ceres/include/ceres/crs_matrix.h b/extern/ceres/include/ceres/crs_matrix.h
index 23687c4670e..bc618fa0905 100644
--- a/extern/ceres/include/ceres/crs_matrix.h
+++ b/extern/ceres/include/ceres/crs_matrix.h
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2019 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -32,8 +32,9 @@
#define CERES_PUBLIC_CRS_MATRIX_H_
#include <vector>
-#include "ceres/internal/port.h"
+
#include "ceres/internal/disable_warnings.h"
+#include "ceres/internal/port.h"
namespace ceres {
diff --git a/extern/ceres/include/ceres/cubic_interpolation.h b/extern/ceres/include/ceres/cubic_interpolation.h
new file mode 100644
index 00000000000..9b9ea4a942c
--- /dev/null
+++ b/extern/ceres/include/ceres/cubic_interpolation.h
@@ -0,0 +1,436 @@
+// 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_CUBIC_INTERPOLATION_H_
+#define CERES_PUBLIC_CUBIC_INTERPOLATION_H_
+
+#include "Eigen/Core"
+#include "ceres/internal/port.h"
+#include "glog/logging.h"
+
+namespace ceres {
+
+// Given samples from a function sampled at four equally spaced points,
+//
+// p0 = f(-1)
+// p1 = f(0)
+// p2 = f(1)
+// p3 = f(2)
+//
+// Evaluate the cubic Hermite spline (also known as the Catmull-Rom
+// spline) at a point x that lies in the interval [0, 1].
+//
+// This is also the interpolation kernel (for the case of a = 0.5) as
+// proposed by R. Keys, in:
+//
+// "Cubic convolution interpolation for digital image processing".
+// IEEE Transactions on Acoustics, Speech, and Signal Processing
+// 29 (6): 1153-1160.
+//
+// For more details see
+//
+// 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.
+template <int kDataDimension>
+void CubicHermiteSpline(const Eigen::Matrix<double, kDataDimension, 1>& p0,
+ const Eigen::Matrix<double, kDataDimension, 1>& p1,
+ const Eigen::Matrix<double, kDataDimension, 1>& p2,
+ const Eigen::Matrix<double, kDataDimension, 1>& p3,
+ const double x,
+ double* f,
+ double* dfdx) {
+ typedef Eigen::Matrix<double, kDataDimension, 1> VType;
+ 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);
+ const VType d = p1;
+
+ // Use Horner's rule to evaluate the function value and its
+ // derivative.
+
+ // f = ax^3 + bx^2 + cx + d
+ if (f != NULL) {
+ Eigen::Map<VType>(f, kDataDimension) = d + x * (c + x * (b + x * a));
+ }
+
+ // dfdx = 3ax^2 + 2bx + c
+ if (dfdx != NULL) {
+ Eigen::Map<VType>(dfdx, kDataDimension) = c + x * (2.0 * b + 3.0 * a * x);
+ }
+}
+
+// Given as input an infinite one dimensional grid, which provides the
+// following interface.
+//
+// class Grid {
+// public:
+// enum { DATA_DIMENSION = 2; };
+// void GetValue(int n, double* f) const;
+// };
+//
+// Here, GetValue gives the value of a function f (possibly vector
+// valued) for any integer n.
+//
+// The enum DATA_DIMENSION indicates the dimensionality of the
+// function being interpolated. For example if you are interpolating
+// rotations in axis-angle format over time, then DATA_DIMENSION = 3.
+//
+// CubicInterpolator uses cubic Hermite splines to produce a smooth
+// approximation to it that can be used to evaluate the f(x) and f'(x)
+// at any point on the real number line.
+//
+// For more details on cubic interpolation see
+//
+// http://en.wikipedia.org/wiki/Cubic_Hermite_spline
+//
+// Example usage:
+//
+// const double data[] = {1.0, 2.0, 5.0, 6.0};
+// Grid1D<double, 1> grid(data, 0, 4);
+// CubicInterpolator<Grid1D<double, 1>> interpolator(grid);
+// double f, dfdx;
+// interpolator.Evaluator(1.5, &f, &dfdx);
+template <typename Grid>
+class CubicInterpolator {
+ public:
+ explicit CubicInterpolator(const Grid& grid) : grid_(grid) {
+ // The + casts the enum into an int before doing the
+ // comparison. It is needed to prevent
+ // "-Wunnamed-type-template-args" related errors.
+ CHECK_GE(+Grid::DATA_DIMENSION, 1);
+ }
+
+ void Evaluate(double x, double* f, double* dfdx) const {
+ const int n = std::floor(x);
+ Eigen::Matrix<double, Grid::DATA_DIMENSION, 1> p0, p1, p2, p3;
+ grid_.GetValue(n - 1, p0.data());
+ grid_.GetValue(n, p1.data());
+ grid_.GetValue(n + 1, p2.data());
+ grid_.GetValue(n + 2, p3.data());
+ CubicHermiteSpline<Grid::DATA_DIMENSION>(p0, p1, p2, p3, x - n, f, dfdx);
+ }
+
+ // 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); }
+
+ template <typename JetT>
+ void Evaluate(const JetT& x, JetT* f) const {
+ double fx[Grid::DATA_DIMENSION], dfdx[Grid::DATA_DIMENSION];
+ Evaluate(x.a, fx, dfdx);
+ for (int i = 0; i < Grid::DATA_DIMENSION; ++i) {
+ f[i].a = fx[i];
+ f[i].v = dfdx[i] * x.v;
+ }
+ }
+
+ private:
+ const Grid& grid_;
+};
+
+// An object that implements an infinite one dimensional grid needed
+// by the CubicInterpolator where the source of the function values is
+// an array of type T on the interval
+//
+// [begin, ..., end - 1]
+//
+// Since the input array is finite and the grid is infinite, values
+// outside this interval needs to be computed. Grid1D uses the value
+// from the nearest edge.
+//
+// The function being provided can be vector valued, in which case
+// kDataDimension > 1. The dimensional slices of the function maybe
+// interleaved, or they maybe stacked, i.e, if the function has
+// kDataDimension = 2, if kInterleaved = true, then it is stored as
+//
+// f01, f02, f11, f12 ....
+//
+// and if kInterleaved = false, then it is stored as
+//
+// f01, f11, .. fn1, f02, f12, .. , fn2
+//
+template <typename T, int kDataDimension = 1, bool kInterleaved = true>
+struct Grid1D {
+ public:
+ enum { DATA_DIMENSION = kDataDimension };
+
+ Grid1D(const T* data, const int begin, const int end)
+ : data_(data), begin_(begin), end_(end), num_values_(end - begin) {
+ CHECK_LT(begin, end);
+ }
+
+ EIGEN_STRONG_INLINE void GetValue(const int n, double* f) const {
+ 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]);
+ }
+ } else {
+ for (int i = 0; i < kDataDimension; ++i) {
+ f[i] = static_cast<double>(data_[i * num_values_ + idx]);
+ }
+ }
+ }
+
+ private:
+ const T* data_;
+ const int begin_;
+ const int end_;
+ const int num_values_;
+};
+
+// Given as input an infinite two dimensional grid like object, which
+// provides the following interface:
+//
+// struct Grid {
+// enum { DATA_DIMENSION = 1 };
+// void GetValue(int row, int col, double* f) const;
+// };
+//
+// Where, GetValue gives us the value of a function f (possibly vector
+// valued) for any pairs of integers (row, col), and the enum
+// DATA_DIMENSION indicates the dimensionality of the function being
+// interpolated. For example if you are interpolating a color image
+// with three channels (Red, Green & Blue), then DATA_DIMENSION = 3.
+//
+// BiCubicInterpolator uses the cubic convolution interpolation
+// algorithm of R. Keys, to produce a smooth approximation to it that
+// can be used to evaluate the f(r,c), df(r, c)/dr and df(r,c)/dc at
+// any point in the real plane.
+//
+// For more details on the algorithm used here see:
+//
+// "Cubic convolution interpolation for digital image processing".
+// Robert G. Keys, IEEE Trans. on Acoustics, Speech, and Signal
+// Processing 29 (6): 1153-1160, 1981.
+//
+// http://en.wikipedia.org/wiki/Cubic_Hermite_spline
+// http://en.wikipedia.org/wiki/Bicubic_interpolation
+//
+// Example usage:
+//
+// const double data[] = {1.0, 3.0, -1.0, 4.0,
+// 3.6, 2.1, 4.2, 2.0,
+// 2.0, 1.0, 3.1, 5.2};
+// Grid2D<double, 1> grid(data, 3, 4);
+// BiCubicInterpolator<Grid2D<double, 1>> interpolator(grid);
+// double f, dfdr, dfdc;
+// interpolator.Evaluate(1.2, 2.5, &f, &dfdr, &dfdc);
+
+template <typename Grid>
+class BiCubicInterpolator {
+ public:
+ explicit BiCubicInterpolator(const Grid& grid) : grid_(grid) {
+ // The + casts the enum into an int before doing the
+ // comparison. It is needed to prevent
+ // "-Wunnamed-type-template-args" related errors.
+ CHECK_GE(+Grid::DATA_DIMENSION, 1);
+ }
+
+ // Evaluate the interpolated function value and/or its
+ // derivative. Uses the nearest point on the grid boundary if r or
+ // c is out of bounds.
+ void Evaluate(
+ double r, double c, double* f, double* dfdr, double* dfdc) const {
+ // BiCubic interpolation requires 16 values around the point being
+ // evaluated. We will use pij, to indicate the elements of the
+ // 4x4 grid of values.
+ //
+ // col
+ // p00 p01 p02 p03
+ // row p10 p11 p12 p13
+ // p20 p21 p22 p23
+ // p30 p31 p32 p33
+ //
+ // The point (r,c) being evaluated is assumed to lie in the square
+ // defined by p11, p12, p22 and p21.
+
+ const int row = std::floor(r);
+ const int col = std::floor(c);
+
+ Eigen::Matrix<double, Grid::DATA_DIMENSION, 1> p0, p1, p2, p3;
+
+ // Interpolate along each of the four rows, evaluating the function
+ // value and the horizontal derivative in each row.
+ Eigen::Matrix<double, Grid::DATA_DIMENSION, 1> f0, f1, f2, f3;
+ Eigen::Matrix<double, Grid::DATA_DIMENSION, 1> df0dc, df1dc, df2dc, df3dc;
+
+ grid_.GetValue(row - 1, col - 1, p0.data());
+ grid_.GetValue(row - 1, col, p1.data());
+ grid_.GetValue(row - 1, col + 1, p2.data());
+ grid_.GetValue(row - 1, col + 2, p3.data());
+ CubicHermiteSpline<Grid::DATA_DIMENSION>(
+ p0, p1, p2, p3, c - col, f0.data(), df0dc.data());
+
+ grid_.GetValue(row, col - 1, p0.data());
+ grid_.GetValue(row, col, p1.data());
+ grid_.GetValue(row, col + 1, p2.data());
+ grid_.GetValue(row, col + 2, p3.data());
+ CubicHermiteSpline<Grid::DATA_DIMENSION>(
+ p0, p1, p2, p3, c - col, f1.data(), df1dc.data());
+
+ grid_.GetValue(row + 1, col - 1, p0.data());
+ grid_.GetValue(row + 1, col, p1.data());
+ grid_.GetValue(row + 1, col + 1, p2.data());
+ grid_.GetValue(row + 1, col + 2, p3.data());
+ CubicHermiteSpline<Grid::DATA_DIMENSION>(
+ p0, p1, p2, p3, c - col, f2.data(), df2dc.data());
+
+ grid_.GetValue(row + 2, col - 1, p0.data());
+ grid_.GetValue(row + 2, col, p1.data());
+ grid_.GetValue(row + 2, col + 1, p2.data());
+ grid_.GetValue(row + 2, col + 2, p3.data());
+ CubicHermiteSpline<Grid::DATA_DIMENSION>(
+ p0, p1, p2, p3, c - col, f3.data(), df3dc.data());
+
+ // 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) {
+ // Interpolate vertically the derivative along the columns.
+ CubicHermiteSpline<Grid::DATA_DIMENSION>(
+ df0dc, df1dc, df2dc, df3dc, r - row, dfdc, NULL);
+ }
+ }
+
+ // 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& r, const double& c, double* f) const {
+ Evaluate(r, c, f, NULL, NULL);
+ }
+
+ template <typename JetT>
+ void Evaluate(const JetT& r, const JetT& c, JetT* f) const {
+ double frc[Grid::DATA_DIMENSION];
+ double dfdr[Grid::DATA_DIMENSION];
+ double dfdc[Grid::DATA_DIMENSION];
+ Evaluate(r.a, c.a, frc, dfdr, dfdc);
+ for (int i = 0; i < Grid::DATA_DIMENSION; ++i) {
+ f[i].a = frc[i];
+ f[i].v = dfdr[i] * r.v + dfdc[i] * c.v;
+ }
+ }
+
+ private:
+ const Grid& grid_;
+};
+
+// An object that implements an infinite two dimensional grid needed
+// by the BiCubicInterpolator where the source of the function values
+// is an grid of type T on the grid
+//
+// [(row_start, col_start), ..., (row_start, col_end - 1)]
+// [ ... ]
+// [(row_end - 1, col_start), ..., (row_end - 1, col_end - 1)]
+//
+// Since the input grid is finite and the grid is infinite, values
+// outside this interval needs to be computed. Grid2D uses the value
+// from the nearest edge.
+//
+// The function being provided can be vector valued, in which case
+// kDataDimension > 1. The data maybe stored in row or column major
+// format and the various dimensional slices of the function maybe
+// interleaved, or they maybe stacked, i.e, if the function has
+// kDataDimension = 2, is stored in row-major format and if
+// kInterleaved = true, then it is stored as
+//
+// f001, f002, f011, f012, ...
+//
+// A commonly occuring example are color images (RGB) where the three
+// channels are stored interleaved.
+//
+// If kInterleaved = false, then it is stored as
+//
+// f001, f011, ..., fnm1, f002, f012, ...
+template <typename T,
+ int kDataDimension = 1,
+ bool kRowMajor = true,
+ bool kInterleaved = true>
+struct Grid2D {
+ public:
+ enum { DATA_DIMENSION = kDataDimension };
+
+ Grid2D(const T* data,
+ const int row_begin,
+ const int row_end,
+ const int col_begin,
+ const int col_end)
+ : data_(data),
+ row_begin_(row_begin),
+ row_end_(row_end),
+ col_begin_(col_begin),
+ col_end_(col_end),
+ num_rows_(row_end - row_begin),
+ num_cols_(col_end - col_begin),
+ num_values_(num_rows_ * num_cols_) {
+ CHECK_GE(kDataDimension, 1);
+ CHECK_LT(row_begin, row_end);
+ CHECK_LT(col_begin, col_end);
+ }
+
+ 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_;
+ const int col_idx =
+ 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;
+
+ if (kInterleaved) {
+ for (int i = 0; i < kDataDimension; ++i) {
+ f[i] = static_cast<double>(data_[kDataDimension * n + i]);
+ }
+ } else {
+ for (int i = 0; i < kDataDimension; ++i) {
+ f[i] = static_cast<double>(data_[i * num_values_ + n]);
+ }
+ }
+ }
+
+ private:
+ const T* data_;
+ const int row_begin_;
+ const int row_end_;
+ const int col_begin_;
+ const int col_end_;
+ const int num_rows_;
+ const int num_cols_;
+ const int num_values_;
+};
+
+} // namespace ceres
+
+#endif // CERES_PUBLIC_CUBIC_INTERPOLATOR_H_
diff --git a/extern/ceres/include/ceres/dynamic_autodiff_cost_function.h b/extern/ceres/include/ceres/dynamic_autodiff_cost_function.h
index e6d26111f18..7b75150b5ce 100644
--- a/extern/ceres/include/ceres/dynamic_autodiff_cost_function.h
+++ b/extern/ceres/include/ceres/dynamic_autodiff_cost_function.h
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2019 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -28,7 +28,22 @@
//
// Author: sameeragarwal@google.com (Sameer Agarwal)
// mierle@gmail.com (Keir Mierle)
-//
+
+#ifndef CERES_PUBLIC_DYNAMIC_AUTODIFF_COST_FUNCTION_H_
+#define CERES_PUBLIC_DYNAMIC_AUTODIFF_COST_FUNCTION_H_
+
+#include <cmath>
+#include <memory>
+#include <numeric>
+#include <vector>
+
+#include "ceres/dynamic_cost_function.h"
+#include "ceres/internal/fixed_array.h"
+#include "ceres/jet.h"
+#include "glog/logging.h"
+
+namespace ceres {
+
// This autodiff implementation differs from the one found in
// autodiff_cost_function.h by supporting autodiff on cost functions
// with variable numbers of parameters with variable sizes. With the
@@ -43,7 +58,7 @@
// bool operator()(T const* const* parameters, T* residuals) const {
// // Use parameters[i] to access the i'th parameter block.
// }
-// }
+// };
//
// Since the sizing of the parameters is done at runtime, you must
// also specify the sizes after creating the dynamic autodiff cost
@@ -60,40 +75,17 @@
// default, controlled by the Stride template parameter) with each
// pass. There is a tradeoff with the size of the passes; you may want
// to experiment with the stride.
-
-#ifndef CERES_PUBLIC_DYNAMIC_AUTODIFF_COST_FUNCTION_H_
-#define CERES_PUBLIC_DYNAMIC_AUTODIFF_COST_FUNCTION_H_
-
-#include <cmath>
-#include <numeric>
-#include <vector>
-
-#include "ceres/cost_function.h"
-#include "ceres/internal/scoped_ptr.h"
-#include "ceres/jet.h"
-#include "glog/logging.h"
-
-namespace ceres {
-
template <typename CostFunctor, int Stride = 4>
-class DynamicAutoDiffCostFunction : public CostFunction {
+class DynamicAutoDiffCostFunction : public DynamicCostFunction {
public:
explicit DynamicAutoDiffCostFunction(CostFunctor* functor)
- : functor_(functor) {}
+ : functor_(functor) {}
virtual ~DynamicAutoDiffCostFunction() {}
- void AddParameterBlock(int size) {
- mutable_parameter_block_sizes()->push_back(size);
- }
-
- void SetNumResiduals(int num_residuals) {
- set_num_residuals(num_residuals);
- }
-
- virtual bool Evaluate(double const* const* parameters,
- double* residuals,
- double** jacobians) const {
+ bool Evaluate(double const* const* parameters,
+ double* residuals,
+ double** jacobians) const override {
CHECK_GT(num_residuals(), 0)
<< "You must call DynamicAutoDiffCostFunction::SetNumResiduals() "
<< "before DynamicAutoDiffCostFunction::Evaluate().";
@@ -112,20 +104,23 @@ class DynamicAutoDiffCostFunction : public CostFunction {
// depends on.
//
// To work around this issue, the solution here is to evaluate the
- // jacobians in a series of passes, each one computing Stripe *
+ // jacobians in a series of passes, each one computing Stride *
// num_residuals() derivatives. This is done with small, fixed-size jets.
- const int num_parameter_blocks = parameter_block_sizes().size();
- const int num_parameters = std::accumulate(parameter_block_sizes().begin(),
- parameter_block_sizes().end(),
- 0);
+ const int num_parameter_blocks =
+ static_cast<int>(parameter_block_sizes().size());
+ const int num_parameters = std::accumulate(
+ parameter_block_sizes().begin(), parameter_block_sizes().end(), 0);
// Allocate scratch space for the strided evaluation.
- std::vector<Jet<double, Stride> > input_jets(num_parameters);
- std::vector<Jet<double, Stride> > output_jets(num_residuals());
+ using JetT = Jet<double, Stride>;
+ internal::FixedArray<JetT, (256 * 7) / sizeof(JetT)> input_jets(
+ num_parameters);
+ internal::FixedArray<JetT, (256 * 7) / sizeof(JetT)> output_jets(
+ num_residuals());
// Make the parameter pack that is sent to the functor (reused).
- std::vector<Jet<double, Stride>* > jet_parameters(num_parameter_blocks,
- static_cast<Jet<double, Stride>* >(NULL));
+ internal::FixedArray<Jet<double, Stride>*> jet_parameters(
+ num_parameter_blocks, nullptr);
int num_active_parameters = 0;
// To handle constant parameters between non-constant parameter blocks, the
@@ -172,8 +167,8 @@ class DynamicAutoDiffCostFunction : public CostFunction {
// Evaluate all of the strides. Each stride is a chunk of the derivative to
// evaluate, typically some size proportional to the size of the SIMD
// registers of the CPU.
- int num_strides = static_cast<int>(ceil(num_active_parameters /
- static_cast<float>(Stride)));
+ int num_strides = static_cast<int>(
+ ceil(num_active_parameters / static_cast<float>(Stride)));
int current_derivative_section = 0;
int current_derivative_section_cursor = 0;
@@ -183,7 +178,7 @@ class DynamicAutoDiffCostFunction : public CostFunction {
// non-constant #Stride parameters.
const int initial_derivative_section = current_derivative_section;
const int initial_derivative_section_cursor =
- current_derivative_section_cursor;
+ current_derivative_section_cursor;
int active_parameter_count = 0;
parameter_cursor = 0;
@@ -193,9 +188,9 @@ class DynamicAutoDiffCostFunction : public CostFunction {
++j, parameter_cursor++) {
input_jets[parameter_cursor].v.setZero();
if (active_parameter_count < Stride &&
- parameter_cursor >= (
- start_derivative_section[current_derivative_section] +
- current_derivative_section_cursor)) {
+ parameter_cursor >=
+ (start_derivative_section[current_derivative_section] +
+ current_derivative_section_cursor)) {
if (jacobians[i] != NULL) {
input_jets[parameter_cursor].v[active_parameter_count] = 1.0;
++active_parameter_count;
@@ -222,9 +217,9 @@ class DynamicAutoDiffCostFunction : public CostFunction {
for (int j = 0; j < parameter_block_sizes()[i];
++j, parameter_cursor++) {
if (active_parameter_count < Stride &&
- parameter_cursor >= (
- start_derivative_section[current_derivative_section] +
- current_derivative_section_cursor)) {
+ parameter_cursor >=
+ (start_derivative_section[current_derivative_section] +
+ current_derivative_section_cursor)) {
if (jacobians[i] != NULL) {
for (int k = 0; k < num_residuals(); ++k) {
jacobians[i][k * parameter_block_sizes()[i] + j] =
@@ -252,7 +247,7 @@ class DynamicAutoDiffCostFunction : public CostFunction {
}
private:
- internal::scoped_ptr<CostFunctor> functor_;
+ std::unique_ptr<CostFunctor> functor_;
};
} // namespace ceres
diff --git a/extern/ceres/include/ceres/dynamic_cost_function.h b/extern/ceres/include/ceres/dynamic_cost_function.h
new file mode 100644
index 00000000000..6e8a076ecd0
--- /dev/null
+++ b/extern/ceres/include/ceres/dynamic_cost_function.h
@@ -0,0 +1,56 @@
+// 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_DYNAMIC_COST_FUNCTION_H_
+#define CERES_PUBLIC_DYNAMIC_COST_FUNCTION_H_
+
+#include "ceres/cost_function.h"
+
+namespace ceres {
+
+// A common base class for DynamicAutoDiffCostFunction and
+// DynamicNumericDiffCostFunction which depend on methods that can add
+// 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);
+ }
+
+ virtual void SetNumResiduals(int num_residuals) {
+ set_num_residuals(num_residuals);
+ }
+};
+
+} // namespace ceres
+
+#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 9339a503ea0..8d174d8ecc2 100644
--- a/extern/ceres/include/ceres/dynamic_cost_function_to_functor.h
+++ b/extern/ceres/include/ceres/dynamic_cost_function_to_functor.h
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2019 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -28,7 +28,20 @@
//
// Author: sameeragarwal@google.com (Sameer Agarwal)
// dgossow@google.com (David Gossow)
-//
+
+#ifndef CERES_PUBLIC_DYNAMIC_COST_FUNCTION_TO_FUNCTOR_H_
+#define CERES_PUBLIC_DYNAMIC_COST_FUNCTION_TO_FUNCTOR_H_
+
+#include <memory>
+#include <numeric>
+#include <vector>
+
+#include "ceres/dynamic_cost_function.h"
+#include "ceres/internal/fixed_array.h"
+#include "ceres/internal/port.h"
+
+namespace ceres {
+
// DynamicCostFunctionToFunctor allows users to use CostFunction
// objects in templated functors which are to be used for automatic
// differentiation. It works similar to CostFunctionToFunctor, with the
@@ -40,9 +53,9 @@
// class IntrinsicProjection : public CostFunction {
// public:
// IntrinsicProjection(const double* observation);
-// virtual bool Evaluate(double const* const* parameters,
-// double* residuals,
-// double** jacobians) const;
+// bool Evaluate(double const* const* parameters,
+// double* residuals,
+// double** jacobians) const override;
// };
//
// is a cost function that implements the projection of a point in its
@@ -87,26 +100,12 @@
// private:
// DynamicCostFunctionToFunctor intrinsic_projection_;
// };
-
-#ifndef CERES_PUBLIC_DYNAMIC_COST_FUNCTION_TO_FUNCTOR_H_
-#define CERES_PUBLIC_DYNAMIC_COST_FUNCTION_TO_FUNCTOR_H_
-
-#include <numeric>
-#include <vector>
-
-#include "ceres/cost_function.h"
-#include "ceres/internal/fixed_array.h"
-#include "ceres/internal/port.h"
-#include "ceres/internal/scoped_ptr.h"
-
-namespace ceres {
-
class DynamicCostFunctionToFunctor {
public:
// Takes ownership of cost_function.
explicit DynamicCostFunctionToFunctor(CostFunction* cost_function)
: cost_function_(cost_function) {
- CHECK_NOTNULL(cost_function);
+ CHECK(cost_function != nullptr);
}
bool operator()(double const* const* parameters, double* residuals) const {
@@ -115,12 +114,13 @@ class DynamicCostFunctionToFunctor {
template <typename JetT>
bool operator()(JetT const* const* inputs, JetT* output) const {
- const std::vector<int32>& parameter_block_sizes =
+ const std::vector<int32_t>& parameter_block_sizes =
cost_function_->parameter_block_sizes();
- const int num_parameter_blocks = parameter_block_sizes.size();
+ const int num_parameter_blocks =
+ static_cast<int>(parameter_block_sizes.size());
const int num_residuals = cost_function_->num_residuals();
- const int num_parameters = std::accumulate(parameter_block_sizes.begin(),
- parameter_block_sizes.end(), 0);
+ const int num_parameters = std::accumulate(
+ parameter_block_sizes.begin(), parameter_block_sizes.end(), 0);
internal::FixedArray<double> parameters(num_parameters);
internal::FixedArray<double*> parameter_blocks(num_parameter_blocks);
@@ -130,8 +130,8 @@ class DynamicCostFunctionToFunctor {
// Build a set of arrays to get the residuals and jacobians from
// the CostFunction wrapped by this functor.
- double* parameter_ptr = parameters.get();
- double* jacobian_ptr = jacobians.get();
+ double* parameter_ptr = parameters.data();
+ double* jacobian_ptr = jacobians.data();
for (int i = 0; i < num_parameter_blocks; ++i) {
parameter_blocks[i] = parameter_ptr;
jacobian_blocks[i] = jacobian_ptr;
@@ -141,9 +141,9 @@ class DynamicCostFunctionToFunctor {
jacobian_ptr += num_residuals * parameter_block_sizes[i];
}
- if (!cost_function_->Evaluate(parameter_blocks.get(),
- residuals.get(),
- jacobian_blocks.get())) {
+ if (!cost_function_->Evaluate(parameter_blocks.data(),
+ residuals.data(),
+ jacobian_blocks.data())) {
return false;
}
@@ -170,7 +170,7 @@ class DynamicCostFunctionToFunctor {
output[i].v.setZero();
for (int j = 0; j < num_parameter_blocks; ++j) {
- const int32 block_size = parameter_block_sizes[j];
+ const int32_t block_size = parameter_block_sizes[j];
for (int k = 0; k < parameter_block_sizes[j]; ++k) {
output[i].v +=
jacobian_blocks[j][i * block_size + k] * inputs[j][k].v;
@@ -182,7 +182,7 @@ class DynamicCostFunctionToFunctor {
}
private:
- internal::scoped_ptr<CostFunction> cost_function_;
+ std::unique_ptr<CostFunction> cost_function_;
};
} // namespace ceres
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 5770946a115..119b3f85e8e 100644
--- a/extern/ceres/include/ceres/dynamic_numeric_diff_cost_function.h
+++ b/extern/ceres/include/ceres/dynamic_numeric_diff_cost_function.h
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2019 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -30,7 +30,24 @@
// sameeragarwal@google.com (Sameer Agarwal)
// thadh@gmail.com (Thad Hughes)
// tbennun@gmail.com (Tal Ben-Nun)
-//
+
+#ifndef CERES_PUBLIC_DYNAMIC_NUMERIC_DIFF_COST_FUNCTION_H_
+#define CERES_PUBLIC_DYNAMIC_NUMERIC_DIFF_COST_FUNCTION_H_
+
+#include <cmath>
+#include <memory>
+#include <numeric>
+#include <vector>
+
+#include "ceres/dynamic_cost_function.h"
+#include "ceres/internal/eigen.h"
+#include "ceres/internal/numeric_diff.h"
+#include "ceres/internal/parameter_dims.h"
+#include "ceres/numeric_diff_options.h"
+#include "glog/logging.h"
+
+namespace ceres {
+
// This numeric diff implementation differs from the one found in
// numeric_diff_cost_function.h by supporting numericdiff on cost
// functions with variable numbers of parameters with variable
@@ -42,7 +59,9 @@
// numeric diff; the expected interface for the cost functors is:
//
// struct MyCostFunctor {
-// bool operator()(double const* const* parameters, double* residuals) const {
+// bool operator()(double const*
+// const* parameters,
+// double* residuals) const {
// // Use parameters[i] to access the i'th parameter block.
// }
// }
@@ -56,34 +75,14 @@
// cost_function.AddParameterBlock(5);
// cost_function.AddParameterBlock(10);
// cost_function.SetNumResiduals(21);
-
-#ifndef CERES_PUBLIC_DYNAMIC_NUMERIC_DIFF_COST_FUNCTION_H_
-#define CERES_PUBLIC_DYNAMIC_NUMERIC_DIFF_COST_FUNCTION_H_
-
-#include <cmath>
-#include <numeric>
-#include <vector>
-
-#include "ceres/cost_function.h"
-#include "ceres/internal/scoped_ptr.h"
-#include "ceres/internal/eigen.h"
-#include "ceres/internal/numeric_diff.h"
-#include "ceres/numeric_diff_options.h"
-#include "glog/logging.h"
-
-namespace ceres {
-
template <typename CostFunctor, NumericDiffMethodType method = CENTRAL>
-class DynamicNumericDiffCostFunction : public CostFunction {
+class DynamicNumericDiffCostFunction : public DynamicCostFunction {
public:
explicit DynamicNumericDiffCostFunction(
const CostFunctor* functor,
Ownership ownership = TAKE_OWNERSHIP,
const NumericDiffOptions& options = NumericDiffOptions())
- : functor_(functor),
- ownership_(ownership),
- options_(options) {
- }
+ : functor_(functor), ownership_(ownership), options_(options) {}
virtual ~DynamicNumericDiffCostFunction() {
if (ownership_ != TAKE_OWNERSHIP) {
@@ -91,28 +90,22 @@ class DynamicNumericDiffCostFunction : public CostFunction {
}
}
- void AddParameterBlock(int size) {
- mutable_parameter_block_sizes()->push_back(size);
- }
-
- void SetNumResiduals(int num_residuals) {
- set_num_residuals(num_residuals);
- }
-
- virtual bool Evaluate(double const* const* parameters,
- double* residuals,
- double** jacobians) const {
+ bool Evaluate(double const* const* parameters,
+ double* residuals,
+ double** jacobians) const override {
using internal::NumericDiff;
CHECK_GT(num_residuals(), 0)
<< "You must call DynamicNumericDiffCostFunction::SetNumResiduals() "
<< "before DynamicNumericDiffCostFunction::Evaluate().";
- const std::vector<int32>& block_sizes = parameter_block_sizes();
+ const std::vector<int32_t>& block_sizes = parameter_block_sizes();
CHECK(!block_sizes.empty())
<< "You must call DynamicNumericDiffCostFunction::AddParameterBlock() "
<< "before DynamicNumericDiffCostFunction::Evaluate().";
- const bool status = EvaluateCostFunctor(parameters, residuals);
+ const bool status =
+ internal::VariadicEvaluate<internal::DynamicParameterDims>(
+ *functor_.get(), parameters, residuals);
if (jacobians == NULL || !status) {
return status;
}
@@ -123,8 +116,8 @@ class DynamicNumericDiffCostFunction : public CostFunction {
std::vector<double*> parameters_references_copy(block_sizes.size());
parameters_references_copy[0] = &parameters_copy[0];
for (size_t block = 1; block < block_sizes.size(); ++block) {
- parameters_references_copy[block] = parameters_references_copy[block - 1]
- + block_sizes[block - 1];
+ parameters_references_copy[block] =
+ parameters_references_copy[block - 1] + block_sizes[block - 1];
}
// Copy the parameters into the local temp space.
@@ -136,18 +129,20 @@ class DynamicNumericDiffCostFunction : public CostFunction {
for (size_t block = 0; block < block_sizes.size(); ++block) {
if (jacobians[block] != NULL &&
- !NumericDiff<CostFunctor, method, DYNAMIC,
- DYNAMIC, DYNAMIC, DYNAMIC, DYNAMIC, DYNAMIC,
- DYNAMIC, DYNAMIC, DYNAMIC, DYNAMIC, DYNAMIC,
- DYNAMIC, DYNAMIC>::EvaluateJacobianForParameterBlock(
- functor_.get(),
- residuals,
- options_,
- this->num_residuals(),
- block,
- block_sizes[block],
- &parameters_references_copy[0],
- jacobians[block])) {
+ !NumericDiff<CostFunctor,
+ method,
+ ceres::DYNAMIC,
+ internal::DynamicParameterDims,
+ ceres::DYNAMIC,
+ ceres::DYNAMIC>::
+ EvaluateJacobianForParameterBlock(functor_.get(),
+ residuals,
+ options_,
+ this->num_residuals(),
+ block,
+ block_sizes[block],
+ &parameters_references_copy[0],
+ jacobians[block])) {
return false;
}
}
@@ -155,31 +150,7 @@ class DynamicNumericDiffCostFunction : public CostFunction {
}
private:
- bool EvaluateCostFunctor(double const* const* parameters,
- double* residuals) const {
- return EvaluateCostFunctorImpl(functor_.get(),
- parameters,
- residuals,
- functor_.get());
- }
-
- // Helper templates to allow evaluation of a functor or a
- // CostFunction.
- bool EvaluateCostFunctorImpl(const CostFunctor* functor,
- double const* const* parameters,
- double* residuals,
- const void* /* NOT USED */) const {
- return (*functor)(parameters, residuals);
- }
-
- bool EvaluateCostFunctorImpl(const CostFunctor* functor,
- double const* const* parameters,
- double* residuals,
- const CostFunction* /* NOT USED */) const {
- return functor->Evaluate(parameters, residuals, NULL);
- }
-
- internal::scoped_ptr<const CostFunctor> functor_;
+ std::unique_ptr<const CostFunctor> functor_;
Ownership ownership_;
NumericDiffOptions options_;
};
diff --git a/extern/ceres/include/ceres/evaluation_callback.h b/extern/ceres/include/ceres/evaluation_callback.h
new file mode 100644
index 00000000000..b9f5bbb5194
--- /dev/null
+++ b/extern/ceres/include/ceres/evaluation_callback.h
@@ -0,0 +1,80 @@
+// 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: mierle@gmail.com (Keir Mierle)
+
+#ifndef CERES_PUBLIC_EVALUATION_CALLBACK_H_
+#define CERES_PUBLIC_EVALUATION_CALLBACK_H_
+
+#include "ceres/internal/port.h"
+
+namespace ceres {
+
+// 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 PrepareForEvaluation() before Ceres calls
+// CostFunction::Evaluate(). It also enables caching results between a
+// pure residual evaluation and a residual & jacobian evaluation, via
+// the new_evaluation_point argument.
+//
+// One use case for this callback is if the cost function compute is
+// moved to the GPU. In that case, the prepare call does the actual
+// cost function evaluation, and subsequent calls from Ceres to the
+// actual cost functions merely copy the results from the GPU onto the
+// corresponding blocks for Ceres to plug into the solver.
+//
+// NOTE: Ceres provides no mechanism to share data other than the
+// notification from the callback. Users must provide access to
+// pre-computed shared data to their cost functions behind the scenes;
+// this all happens without Ceres knowing.
+//
+// One approach is to put a pointer to the shared data in each cost
+// function (recommended) or to use a global shared variable
+// (discouraged; bug-prone). As far as Ceres is concerned, it is
+// evaluating cost functions like any other; it just so happens that
+// behind the scenes the cost functions reuse pre-computed data to
+// execute faster.
+class CERES_EXPORT EvaluationCallback {
+ public:
+ 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
+ // functions) are fixed until the next call to PrepareForEvaluation(). If
+ // new_evaluation_point == true, then this is a new point that is different
+ // from the last evaluated point. Otherwise, it is the same point that was
+ // evaluated previously (either jacobian or residual) and the user can use
+ // cached results from previous evaluations.
+ virtual void PrepareForEvaluation(bool evaluate_jacobians,
+ bool new_evaluation_point) = 0;
+};
+
+} // namespace ceres
+
+#endif // CERES_PUBLIC_EVALUATION_CALLBACK_H_
diff --git a/extern/ceres/include/ceres/first_order_function.h b/extern/ceres/include/ceres/first_order_function.h
new file mode 100644
index 00000000000..1420153b2aa
--- /dev/null
+++ b/extern/ceres/include/ceres/first_order_function.h
@@ -0,0 +1,54 @@
+// 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_FIRST_ORDER_FUNCTION_H_
+#define CERES_PUBLIC_FIRST_ORDER_FUNCTION_H_
+
+#include "ceres/internal/port.h"
+
+namespace ceres {
+
+// A FirstOrderFunction object implements the evaluation of a function
+// and its gradient.
+class CERES_EXPORT FirstOrderFunction {
+ public:
+ virtual ~FirstOrderFunction() {}
+
+ // cost is never null. gradient may be null. The return value
+ // indicates whether the evaluation was successful or not.
+ virtual bool Evaluate(const double* const parameters,
+ double* cost,
+ double* gradient) const = 0;
+ virtual int NumParameters() const = 0;
+};
+
+} // namespace ceres
+
+#endif // CERES_PUBLIC_FIRST_ORDER_FUNCTION_H_
diff --git a/extern/ceres/include/ceres/gradient_checker.h b/extern/ceres/include/ceres/gradient_checker.h
index 6d285daf1d9..b79cf86b314 100644
--- a/extern/ceres/include/ceres/gradient_checker.h
+++ b/extern/ceres/include/ceres/gradient_checker.h
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2019 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -34,15 +34,14 @@
#ifndef CERES_PUBLIC_GRADIENT_CHECKER_H_
#define CERES_PUBLIC_GRADIENT_CHECKER_H_
-#include <vector>
+#include <memory>
#include <string>
+#include <vector>
#include "ceres/cost_function.h"
#include "ceres/dynamic_numeric_diff_cost_function.h"
#include "ceres/internal/eigen.h"
#include "ceres/internal/fixed_array.h"
-#include "ceres/internal/macros.h"
-#include "ceres/internal/scoped_ptr.h"
#include "ceres/local_parameterization.h"
#include "glog/logging.h"
@@ -64,13 +63,13 @@ namespace ceres {
//
// How to use: Fill in an array of pointers to parameter blocks for your
// CostFunction, and then call Probe(). Check that the return value is 'true'.
-class GradientChecker {
+class CERES_EXPORT GradientChecker {
public:
// This will not take ownership of the cost function or local
// parameterizations.
//
// function: The cost function to probe.
- // local_parameterization: A vector of local parameterizations for each
+ // local_parameterizations: A vector of local parameterizations for each
// parameter. May be NULL or contain NULL pointers to indicate that the
// respective parameter does not have a local parameterization.
// options: Options to use for numerical differentiation.
@@ -80,7 +79,7 @@ class GradientChecker {
const NumericDiffOptions& options);
// Contains results from a call to Probe for later inspection.
- struct ProbeResults {
+ struct CERES_EXPORT ProbeResults {
// The return value of the cost function.
bool return_value;
@@ -100,10 +99,10 @@ class GradientChecker {
// Derivatives as computed by the cost function in local space.
std::vector<Matrix> local_jacobians;
- // Derivatives as computed by nuerical differentiation in local space.
+ // Derivatives as computed by numerical differentiation in local space.
std::vector<Matrix> numeric_jacobians;
- // Derivatives as computed by nuerical differentiation in local space.
+ // Derivatives as computed by numerical differentiation in local space.
std::vector<Matrix> local_numeric_jacobians;
// Contains the maximum relative error found in the local Jacobians.
@@ -137,11 +136,13 @@ class GradientChecker {
ProbeResults* results) const;
private:
- CERES_DISALLOW_IMPLICIT_CONSTRUCTORS(GradientChecker);
+ GradientChecker() = delete;
+ GradientChecker(const GradientChecker&) = delete;
+ void operator=(const GradientChecker&) = delete;
std::vector<const LocalParameterization*> local_parameterizations_;
const CostFunction* function_;
- internal::scoped_ptr<CostFunction> finite_diff_cost_function_;
+ std::unique_ptr<CostFunction> finite_diff_cost_function_;
};
} // namespace ceres
diff --git a/extern/ceres/include/ceres/gradient_problem.h b/extern/ceres/include/ceres/gradient_problem.h
index 1226a4cd895..49d605ea2d6 100644
--- a/extern/ceres/include/ceres/gradient_problem.h
+++ b/extern/ceres/include/ceres/gradient_problem.h
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2019 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -31,9 +31,10 @@
#ifndef CERES_PUBLIC_GRADIENT_PROBLEM_H_
#define CERES_PUBLIC_GRADIENT_PROBLEM_H_
-#include "ceres/internal/macros.h"
+#include <memory>
+
+#include "ceres/first_order_function.h"
#include "ceres/internal/port.h"
-#include "ceres/internal/scoped_ptr.h"
#include "ceres/local_parameterization.h"
namespace ceres {
@@ -105,21 +106,9 @@ class CERES_EXPORT GradientProblem {
bool Plus(const double* x, const double* delta, double* x_plus_delta) const;
private:
- internal::scoped_ptr<FirstOrderFunction> function_;
- internal::scoped_ptr<LocalParameterization> parameterization_;
- internal::scoped_array<double> scratch_;
-};
-
-// A FirstOrderFunction object implements the evaluation of a function
-// and its gradient.
-class CERES_EXPORT FirstOrderFunction {
- public:
- virtual ~FirstOrderFunction() {}
- // cost is never NULL. gradient may be null.
- virtual bool Evaluate(const double* const parameters,
- double* cost,
- double* gradient) const = 0;
- virtual int NumParameters() const = 0;
+ std::unique_ptr<FirstOrderFunction> function_;
+ std::unique_ptr<LocalParameterization> parameterization_;
+ std::unique_ptr<double[]> scratch_;
};
} // namespace ceres
diff --git a/extern/ceres/include/ceres/gradient_problem_solver.h b/extern/ceres/include/ceres/gradient_problem_solver.h
index a7d0121ea0c..181699d8fd4 100644
--- a/extern/ceres/include/ceres/gradient_problem_solver.h
+++ b/extern/ceres/include/ceres/gradient_problem_solver.h
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2019 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -34,11 +34,11 @@
#include <cmath>
#include <string>
#include <vector>
-#include "ceres/internal/macros.h"
+
+#include "ceres/internal/disable_warnings.h"
#include "ceres/internal/port.h"
#include "ceres/iteration_callback.h"
#include "ceres/types.h"
-#include "ceres/internal/disable_warnings.h"
namespace ceres {
@@ -54,39 +54,15 @@ class CERES_EXPORT GradientProblemSolver {
//
// The constants are defined inside types.h
struct CERES_EXPORT Options {
- // Default constructor that sets up a generic sparse problem.
- Options() {
- line_search_direction_type = LBFGS;
- line_search_type = WOLFE;
- nonlinear_conjugate_gradient_type = FLETCHER_REEVES;
- max_lbfgs_rank = 20;
- use_approximate_eigenvalue_bfgs_scaling = false;
- line_search_interpolation_type = CUBIC;
- min_line_search_step_size = 1e-9;
- line_search_sufficient_function_decrease = 1e-4;
- max_line_search_step_contraction = 1e-3;
- min_line_search_step_contraction = 0.6;
- max_num_line_search_step_size_iterations = 20;
- max_num_line_search_direction_restarts = 5;
- line_search_sufficient_curvature_decrease = 0.9;
- max_line_search_step_expansion = 10.0;
- max_num_iterations = 50;
- max_solver_time_in_seconds = 1e9;
- function_tolerance = 1e-6;
- gradient_tolerance = 1e-10;
- logging_type = PER_MINIMIZER_ITERATION;
- minimizer_progress_to_stdout = false;
- }
-
// Returns true if the options struct has a valid
// configuration. Returns false otherwise, and fills in *error
// with a message describing the problem.
bool IsValid(std::string* error) const;
// Minimizer options ----------------------------------------
- LineSearchDirectionType line_search_direction_type;
- LineSearchType line_search_type;
- NonlinearConjugateGradientType nonlinear_conjugate_gradient_type;
+ LineSearchDirectionType line_search_direction_type = LBFGS;
+ LineSearchType line_search_type = WOLFE;
+ NonlinearConjugateGradientType nonlinear_conjugate_gradient_type = FLETCHER_REEVES;
// The LBFGS hessian approximation is a low rank approximation to
// the inverse of the Hessian matrix. The rank of the
@@ -111,8 +87,8 @@ class CERES_EXPORT GradientProblemSolver {
// method, please see:
//
// Nocedal, J. (1980). "Updating Quasi-Newton Matrices with
- // Limited Storage". Mathematics of Computation 35 (151): 773–782.
- int max_lbfgs_rank;
+ // Limited Storage". Mathematics of Computation 35 (151): 773-782.
+ int max_lbfgs_rank = 20;
// As part of the (L)BFGS update step (BFGS) / right-multiply step (L-BFGS),
// the initial inverse Hessian approximation is taken to be the Identity.
@@ -134,18 +110,18 @@ class CERES_EXPORT GradientProblemSolver {
// Oren S.S., Self-scaling variable metric (SSVM) algorithms
// Part II: Implementation and experiments, Management Science,
// 20(5), 863-874, 1974.
- bool use_approximate_eigenvalue_bfgs_scaling;
+ bool use_approximate_eigenvalue_bfgs_scaling = false;
// Degree of the polynomial used to approximate the objective
// function. Valid values are BISECTION, QUADRATIC and CUBIC.
//
// BISECTION corresponds to pure backtracking search with no
// interpolation.
- LineSearchInterpolationType line_search_interpolation_type;
+ LineSearchInterpolationType line_search_interpolation_type = CUBIC;
// If during the line search, the step_size falls below this
// value, it is truncated to zero.
- double min_line_search_step_size;
+ double min_line_search_step_size = 1e-9;
// Line search parameters.
@@ -159,7 +135,7 @@ class CERES_EXPORT GradientProblemSolver {
//
// f(step_size) <= f(0) + sufficient_decrease * f'(0) * step_size
//
- double line_search_sufficient_function_decrease;
+ double line_search_sufficient_function_decrease = 1e-4;
// In each iteration of the line search,
//
@@ -169,7 +145,7 @@ class CERES_EXPORT GradientProblemSolver {
//
// 0 < max_step_contraction < min_step_contraction < 1
//
- double max_line_search_step_contraction;
+ double max_line_search_step_contraction = 1e-3;
// In each iteration of the line search,
//
@@ -179,19 +155,19 @@ class CERES_EXPORT GradientProblemSolver {
//
// 0 < max_step_contraction < min_step_contraction < 1
//
- double min_line_search_step_contraction;
+ double min_line_search_step_contraction = 0.6;
// Maximum number of trial step size iterations during each line search,
// if a step size satisfying the search conditions cannot be found within
// this number of trials, the line search will terminate.
- int max_num_line_search_step_size_iterations;
+ int max_num_line_search_step_size_iterations = 20;
// Maximum number of restarts of the line search direction algorithm before
// terminating the optimization. Restarts of the line search direction
// algorithm occur when the current algorithm fails to produce a new descent
// direction. This typically indicates a numerical failure, or a breakdown
// in the validity of the approximations used.
- int max_num_line_search_direction_restarts;
+ int max_num_line_search_direction_restarts = 5;
// The strong Wolfe conditions consist of the Armijo sufficient
// decrease condition, and an additional requirement that the
@@ -204,7 +180,7 @@ class CERES_EXPORT GradientProblemSolver {
//
// Where f() is the line search objective and f'() is the derivative
// of f w.r.t step_size (d f / d step_size).
- double line_search_sufficient_curvature_decrease;
+ double line_search_sufficient_curvature_decrease = 0.9;
// During the bracketing phase of the Wolfe search, the step size is
// increased until either a point satisfying the Wolfe conditions is
@@ -215,36 +191,49 @@ class CERES_EXPORT GradientProblemSolver {
// new_step_size <= max_step_expansion * step_size.
//
// By definition for expansion, max_step_expansion > 1.0.
- double max_line_search_step_expansion;
+ double max_line_search_step_expansion = 10.0;
// Maximum number of iterations for the minimizer to run for.
- int max_num_iterations;
+ int max_num_iterations = 50;
// Maximum time for which the minimizer should run for.
- double max_solver_time_in_seconds;
+ double max_solver_time_in_seconds = 1e9;
// Minimizer terminates when
//
// (new_cost - old_cost) < function_tolerance * old_cost;
//
- double function_tolerance;
+ double function_tolerance = 1e-6;
// Minimizer terminates when
//
// max_i |x - Project(Plus(x, -g(x))| < gradient_tolerance
//
// This value should typically be 1e-4 * function_tolerance.
- double gradient_tolerance;
+ double gradient_tolerance = 1e-10;
+
+ // Minimizer terminates when
+ //
+ // |step|_2 <= parameter_tolerance * ( |x|_2 + parameter_tolerance)
+ //
+ double parameter_tolerance = 1e-8;
// Logging options ---------------------------------------------------------
- LoggingType logging_type;
+ LoggingType logging_type = PER_MINIMIZER_ITERATION;
// By default the Minimizer progress is logged to VLOG(1), which
// is sent to STDERR depending on the vlog level. If this flag is
// set to true, and logging_type is not SILENT, the logging output
// is sent to STDOUT.
- bool minimizer_progress_to_stdout;
+ bool minimizer_progress_to_stdout = false;
+
+ // If true, the user's parameter blocks are updated at the end of
+ // every Minimizer iteration, otherwise they are updated when the
+ // Minimizer terminates. This is useful if, for example, the user
+ // wishes to visualize the state of the optimization every
+ // iteration.
+ bool update_state_every_iteration = false;
// Callbacks that are executed at the end of each iteration of the
// Minimizer. An iteration may terminate midway, either due to
@@ -265,8 +254,6 @@ class CERES_EXPORT GradientProblemSolver {
};
struct CERES_EXPORT Summary {
- Summary();
-
// A brief one line description of the state of the solver after
// termination.
std::string BriefReport() const;
@@ -278,65 +265,72 @@ class CERES_EXPORT GradientProblemSolver {
bool IsSolutionUsable() const;
// Minimizer summary -------------------------------------------------
- TerminationType termination_type;
+ TerminationType termination_type = FAILURE;
// Reason why the solver terminated.
- std::string message;
+ std::string message = "ceres::GradientProblemSolve was not called.";
// Cost of the problem (value of the objective function) before
// the optimization.
- double initial_cost;
+ double initial_cost = -1.0;
// Cost of the problem (value of the objective function) after the
// optimization.
- double final_cost;
+ double final_cost = -1.0;
// IterationSummary for each minimizer iteration in order.
std::vector<IterationSummary> iterations;
+ // Number of times the cost (and not the gradient) was evaluated.
+ int num_cost_evaluations = -1;
+
+ // Number of times the gradient (and the cost) were evaluated.
+ int num_gradient_evaluations = -1;
+
// Sum total of all time spent inside Ceres when Solve is called.
- double total_time_in_seconds;
+ double total_time_in_seconds = -1.0;
// Time (in seconds) spent evaluating the cost.
- double cost_evaluation_time_in_seconds;
+ double cost_evaluation_time_in_seconds = -1.0;
// Time (in seconds) spent evaluating the gradient.
- double gradient_evaluation_time_in_seconds;
+ double gradient_evaluation_time_in_seconds = -1.0;
// Time (in seconds) spent minimizing the interpolating polynomial
// to compute the next candidate step size as part of a line search.
- double line_search_polynomial_minimization_time_in_seconds;
+ double line_search_polynomial_minimization_time_in_seconds = -1.0;
- // Number of parameters in the probem.
- int num_parameters;
+ // Number of parameters in the problem.
+ int num_parameters = -1;
// Dimension of the tangent space of the problem.
- int num_local_parameters;
+ int num_local_parameters = -1;
// Type of line search direction used.
- LineSearchDirectionType line_search_direction_type;
+ LineSearchDirectionType line_search_direction_type = LBFGS;
// Type of the line search algorithm used.
- LineSearchType line_search_type;
+ LineSearchType line_search_type = WOLFE;
// When performing line search, the degree of the polynomial used
// to approximate the objective function.
- LineSearchInterpolationType line_search_interpolation_type;
+ LineSearchInterpolationType line_search_interpolation_type = CUBIC;
// If the line search direction is NONLINEAR_CONJUGATE_GRADIENT,
// then this indicates the particular variant of non-linear
// conjugate gradient used.
- NonlinearConjugateGradientType nonlinear_conjugate_gradient_type;
+ NonlinearConjugateGradientType nonlinear_conjugate_gradient_type =
+ FLETCHER_REEVES;
// If the type of the line search direction is LBFGS, then this
// indicates the rank of the Hessian approximation.
- int max_lbfgs_rank;
+ int max_lbfgs_rank = -1;
};
// Once a least squares problem has been built, this function takes
// the problem and optimizes it based on the values of the options
// parameters. Upon return, a detailed summary of the work performed
- // by the preprocessor, the non-linear minmizer and the linear
+ // by the preprocessor, the non-linear minimizer and the linear
// solver are reported in the summary object.
virtual void Solve(const GradientProblemSolver::Options& options,
const GradientProblem& problem,
diff --git a/extern/ceres/include/ceres/internal/array_selector.h b/extern/ceres/include/ceres/internal/array_selector.h
new file mode 100644
index 00000000000..841797f4c69
--- /dev/null
+++ b/extern/ceres/include/ceres/internal/array_selector.h
@@ -0,0 +1,95 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2020 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: darius.rueckert@fau.de (Darius Rueckert)
+//
+
+#ifndef CERES_PUBLIC_INTERNAL_ARRAY_SELECTOR_H_
+#define CERES_PUBLIC_INTERNAL_ARRAY_SELECTOR_H_
+
+#include <array>
+#include <vector>
+
+#include "ceres/internal/fixed_array.h"
+#include "ceres/types.h"
+
+namespace ceres {
+namespace internal {
+
+// StaticFixedArray selects the best array implementation based on template
+// arguments. If the size is not known at compile-time, pass
+// ceres::DYNAMIC as a size-template argument.
+//
+// Three different containers are selected in different scenarios:
+//
+// num_elements == DYNAMIC:
+// -> ceres::internal::FixedArray<T, max_stack_size>(size)
+
+// num_elements != DYNAMIC && num_elements <= max_stack_size
+// -> std::array<T,num_elements>
+
+// num_elements != DYNAMIC && num_elements > max_stack_size
+// -> std::vector<T>(num_elements)
+//
+template <typename T,
+ int num_elements,
+ int max_num_elements_on_stack,
+ bool dynamic = (num_elements == DYNAMIC),
+ bool fits_on_stack = (num_elements <= max_num_elements_on_stack)>
+struct ArraySelector {};
+
+template <typename T,
+ int num_elements,
+ int max_num_elements_on_stack,
+ bool fits_on_stack>
+struct ArraySelector<T,
+ num_elements,
+ max_num_elements_on_stack,
+ true,
+ fits_on_stack>
+ : ceres::internal::FixedArray<T, max_num_elements_on_stack> {
+ 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); }
+};
+
+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); }
+};
+
+} // namespace internal
+} // namespace ceres
+
+#endif // CERES_PUBLIC_INTERNAL_ARRAY_SELECTOR_H_
diff --git a/extern/ceres/include/ceres/internal/autodiff.h b/extern/ceres/include/ceres/internal/autodiff.h
index 136152a36cd..cb7b1aca5b9 100644
--- a/extern/ceres/include/ceres/internal/autodiff.h
+++ b/extern/ceres/include/ceres/internal/autodiff.h
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2019 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -30,10 +30,10 @@
//
// Computation of the Jacobian matrix for vector-valued functions of multiple
// variables, using automatic differentiation based on the implementation of
-// dual numbers in jet.h. Before reading the rest of this file, it is adivsable
+// dual numbers in jet.h. Before reading the rest of this file, it is advisable
// to read jet.h's header comment in detail.
//
-// The helper wrapper AutoDiff::Differentiate() computes the jacobian of
+// The helper wrapper AutoDifferentiate() computes the jacobian of
// functors with templated operator() taking this form:
//
// struct F {
@@ -57,7 +57,7 @@
// [ * ]
//
// Similar to the 2-parameter example for f described in jet.h, computing the
-// jacobian dy/dx is done by substutiting a suitable jet object for x and all
+// jacobian dy/dx is done by substituting a suitable jet object for x and all
// intermediate steps of the computation of F. Since x is has 4 dimensions, use
// a Jet<double, 4>.
//
@@ -142,16 +142,33 @@
#include <stddef.h>
-#include "ceres/jet.h"
+#include <array>
+#include <utility>
+
+#include "ceres/internal/array_selector.h"
#include "ceres/internal/eigen.h"
#include "ceres/internal/fixed_array.h"
+#include "ceres/internal/parameter_dims.h"
#include "ceres/internal/variadic_evaluate.h"
+#include "ceres/jet.h"
+#include "ceres/types.h"
#include "glog/logging.h"
+// If the number of parameters exceeds this values, the corresponding jets are
+// placed on the heap. This will reduce performance by a factor of 2-5 on
+// current compilers.
+#ifndef CERES_AUTODIFF_MAX_PARAMETERS_ON_STACK
+#define CERES_AUTODIFF_MAX_PARAMETERS_ON_STACK 50
+#endif
+
+#ifndef CERES_AUTODIFF_MAX_RESIDUALS_ON_STACK
+#define CERES_AUTODIFF_MAX_RESIDUALS_ON_STACK 20
+#endif
+
namespace ceres {
namespace internal {
-// Extends src by a 1st order pertubation for every dimension and puts it in
+// Extends src by a 1st order perturbation for every dimension and puts it in
// dst. The size of src is N. Since this is also used for perturbations in
// blocked arrays, offset is used to shift which part of the jet the
// perturbation occurs. This is used to set up the extended x augmented by an
@@ -165,21 +182,62 @@ namespace internal {
//
// is what would get put in dst if N was 3, offset was 3, and the jet type JetT
// was 8-dimensional.
-template <typename JetT, typename T, int N>
-inline void Make1stOrderPerturbation(int offset, const T* src, JetT* dst) {
- DCHECK(src);
- DCHECK(dst);
- for (int j = 0; j < N; ++j) {
- dst[j].a = src[j];
- dst[j].v.setZero();
- dst[j].v[offset + j] = T(1.0);
+template <int j, int N, int Offset, typename T, typename JetT>
+struct Make1stOrderPerturbation {
+ public:
+ inline static void Apply(const T* src, JetT* dst) {
+ if (j == 0) {
+ DCHECK(src);
+ DCHECK(dst);
+ }
+ dst[j] = JetT(src[j], j + Offset);
+ Make1stOrderPerturbation<j + 1, N, Offset, T, JetT>::Apply(src, dst);
}
-}
+};
+
+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*/) {}
+};
+
+// Calls Make1stOrderPerturbation for every parameter block.
+//
+// Example:
+// If one having three parameter blocks with dimensions (3, 2, 4), the call
+// Make1stOrderPerturbations<integer_sequence<3, 2, 4>::Apply(params, x);
+// will result in the following calls to Make1stOrderPerturbation:
+// Make1stOrderPerturbation<0, 3, 0>::Apply(params[0], x + 0);
+// Make1stOrderPerturbation<0, 2, 3>::Apply(params[1], x + 3);
+// Make1stOrderPerturbation<0, 4, 5>::Apply(params[2], x + 5);
+template <typename Seq, int ParameterIdx = 0, int Offset = 0>
+struct Make1stOrderPerturbations;
+
+template <int N, int... Ns, int ParameterIdx, int Offset>
+struct Make1stOrderPerturbations<std::integer_sequence<int, N, Ns...>,
+ ParameterIdx,
+ Offset> {
+ template <typename T, typename JetT>
+ inline static void Apply(T const* const* parameters, JetT* x) {
+ Make1stOrderPerturbation<0, N, Offset, T, JetT>::Apply(
+ parameters[ParameterIdx], x + Offset);
+ Make1stOrderPerturbations<std::integer_sequence<int, Ns...>,
+ ParameterIdx + 1,
+ Offset + N>::Apply(parameters, x);
+ }
+};
+
+// End of 'recursion'. Nothing more to do.
+template <int ParameterIdx, int Total>
+struct Make1stOrderPerturbations<std::integer_sequence<int>, ParameterIdx, Total> {
+ template <typename T, typename JetT>
+ static void Apply(T const* const* /* NOT USED */, JetT* /* NOT USED */) {}
+};
// Takes the 0th order part of src, assumed to be a Jet type, and puts it in
// dst. This is used to pick out the "vector" part of the extended y.
template <typename JetT, typename T>
-inline void Take0thOrderPart(int M, const JetT *src, T dst) {
+inline void Take0thOrderPart(int M, const JetT* src, T dst) {
DCHECK(src);
for (int i = 0; i < M; ++i) {
dst[i] = src[i].a;
@@ -188,128 +246,117 @@ inline void Take0thOrderPart(int M, const JetT *src, T dst) {
// Takes N 1st order parts, starting at index N0, and puts them in the M x N
// matrix 'dst'. This is used to pick out the "matrix" parts of the extended y.
-template <typename JetT, typename T, int N0, int N>
-inline void Take1stOrderPart(const int M, const JetT *src, T *dst) {
+template <int N0, int N, typename JetT, typename T>
+inline void Take1stOrderPart(const int M, const JetT* src, T* dst) {
DCHECK(src);
DCHECK(dst);
for (int i = 0; i < M; ++i) {
- Eigen::Map<Eigen::Matrix<T, N, 1> >(dst + N * i, N) =
+ Eigen::Map<Eigen::Matrix<T, N, 1>>(dst + N * i, N) =
src[i].v.template segment<N>(N0);
}
}
-// This is in a struct because default template parameters on a
-// function are not supported in C++03 (though it is available in
-// C++0x). N0 through N5 are the dimension of the input arguments to
-// the user supplied functor.
-template <typename Functor, typename T,
- int N0 = 0, int N1 = 0, int N2 = 0, int N3 = 0, int N4 = 0,
- int N5 = 0, int N6 = 0, int N7 = 0, int N8 = 0, int N9 = 0>
-struct AutoDiff {
- static bool Differentiate(const Functor& functor,
- T const *const *parameters,
- int num_outputs,
- T *function_value,
- T **jacobians) {
- // This block breaks the 80 column rule to keep it somewhat readable.
- DCHECK_GT(num_outputs, 0);
- DCHECK((!N1 && !N2 && !N3 && !N4 && !N5 && !N6 && !N7 && !N8 && !N9) ||
- ((N1 > 0) && !N2 && !N3 && !N4 && !N5 && !N6 && !N7 && !N8 && !N9) ||
- ((N1 > 0) && (N2 > 0) && !N3 && !N4 && !N5 && !N6 && !N7 && !N8 && !N9) || // NOLINT
- ((N1 > 0) && (N2 > 0) && (N3 > 0) && !N4 && !N5 && !N6 && !N7 && !N8 && !N9) || // NOLINT
- ((N1 > 0) && (N2 > 0) && (N3 > 0) && (N4 > 0) && !N5 && !N6 && !N7 && !N8 && !N9) || // NOLINT
- ((N1 > 0) && (N2 > 0) && (N3 > 0) && (N4 > 0) && (N5 > 0) && !N6 && !N7 && !N8 && !N9) || // NOLINT
- ((N1 > 0) && (N2 > 0) && (N3 > 0) && (N4 > 0) && (N5 > 0) && (N6 > 0) && !N7 && !N8 && !N9) || // NOLINT
- ((N1 > 0) && (N2 > 0) && (N3 > 0) && (N4 > 0) && (N5 > 0) && (N6 > 0) && (N7 > 0) && !N8 && !N9) || // NOLINT
- ((N1 > 0) && (N2 > 0) && (N3 > 0) && (N4 > 0) && (N5 > 0) && (N6 > 0) && (N7 > 0) && (N8 > 0) && !N9) || // NOLINT
- ((N1 > 0) && (N2 > 0) && (N3 > 0) && (N4 > 0) && (N5 > 0) && (N6 > 0) && (N7 > 0) && (N8 > 0) && (N9 > 0))) // NOLINT
- << "Zero block cannot precede a non-zero block. Block sizes are "
- << "(ignore trailing 0s): " << N0 << ", " << N1 << ", " << N2 << ", "
- << N3 << ", " << N4 << ", " << N5 << ", " << N6 << ", " << N7 << ", "
- << N8 << ", " << N9;
+// Calls Take1stOrderPart for every parameter block.
+//
+// Example:
+// If one having three parameter blocks with dimensions (3, 2, 4), the call
+// Take1stOrderParts<integer_sequence<3, 2, 4>::Apply(num_outputs,
+// output,
+// jacobians);
+// will result in the following calls to Take1stOrderPart:
+// if (jacobians[0]) {
+// Take1stOrderPart<0, 3>(num_outputs, output, jacobians[0]);
+// }
+// if (jacobians[1]) {
+// Take1stOrderPart<3, 2>(num_outputs, output, jacobians[1]);
+// }
+// if (jacobians[2]) {
+// Take1stOrderPart<5, 4>(num_outputs, output, jacobians[2]);
+// }
+template <typename Seq, int ParameterIdx = 0, int Offset = 0>
+struct Take1stOrderParts;
- typedef Jet<T, N0 + N1 + N2 + N3 + N4 + N5 + N6 + N7 + N8 + N9> JetT;
- FixedArray<JetT, (256 * 7) / sizeof(JetT)> x(
- N0 + N1 + N2 + N3 + N4 + N5 + N6 + N7 + N8 + N9 + num_outputs);
+template <int N, int... Ns, int ParameterIdx, int Offset>
+struct Take1stOrderParts<std::integer_sequence<int, N, Ns...>,
+ ParameterIdx,
+ Offset> {
+ template <typename JetT, typename T>
+ inline static void Apply(int num_outputs, JetT* output, T** jacobians) {
+ if (jacobians[ParameterIdx]) {
+ Take1stOrderPart<Offset, N>(num_outputs, output, jacobians[ParameterIdx]);
+ }
+ Take1stOrderParts<std::integer_sequence<int, Ns...>,
+ ParameterIdx + 1,
+ Offset + N>::Apply(num_outputs, output, jacobians);
+ }
+};
- // These are the positions of the respective jets in the fixed array x.
- const int jet0 = 0;
- const int jet1 = N0;
- const int jet2 = N0 + N1;
- const int jet3 = N0 + N1 + N2;
- const int jet4 = N0 + N1 + N2 + N3;
- const int jet5 = N0 + N1 + N2 + N3 + N4;
- const int jet6 = N0 + N1 + N2 + N3 + N4 + N5;
- const int jet7 = N0 + N1 + N2 + N3 + N4 + N5 + N6;
- const int jet8 = N0 + N1 + N2 + N3 + N4 + N5 + N6 + N7;
- const int jet9 = N0 + N1 + N2 + N3 + N4 + N5 + N6 + N7 + N8;
+// End of 'recursion'. Nothing more to do.
+template <int ParameterIdx, int Offset>
+struct Take1stOrderParts<std::integer_sequence<int>, ParameterIdx, Offset> {
+ template <typename T, typename JetT>
+ static void Apply(int /* NOT USED*/,
+ JetT* /* NOT USED*/,
+ T** /* NOT USED */) {}
+};
- const JetT *unpacked_parameters[10] = {
- x.get() + jet0,
- x.get() + jet1,
- x.get() + jet2,
- x.get() + jet3,
- x.get() + jet4,
- x.get() + jet5,
- x.get() + jet6,
- x.get() + jet7,
- x.get() + jet8,
- x.get() + jet9,
- };
+template <int kNumResiduals,
+ typename ParameterDims,
+ typename Functor,
+ typename T>
+inline bool AutoDifferentiate(const Functor& functor,
+ T const* const* parameters,
+ int dynamic_num_outputs,
+ T* function_value,
+ T** jacobians) {
+ typedef Jet<T, ParameterDims::kNumParameters> JetT;
+ using Parameters = typename ParameterDims::Parameters;
- JetT* output = x.get() + N0 + N1 + N2 + N3 + N4 + N5 + N6 + N7 + N8 + N9;
+ if (kNumResiduals != DYNAMIC) {
+ DCHECK_EQ(kNumResiduals, dynamic_num_outputs);
+ }
-#define CERES_MAKE_1ST_ORDER_PERTURBATION(i) \
- if (N ## i) { \
- internal::Make1stOrderPerturbation<JetT, T, N ## i>( \
- jet ## i, \
- parameters[i], \
- x.get() + jet ## i); \
- }
- CERES_MAKE_1ST_ORDER_PERTURBATION(0);
- CERES_MAKE_1ST_ORDER_PERTURBATION(1);
- CERES_MAKE_1ST_ORDER_PERTURBATION(2);
- CERES_MAKE_1ST_ORDER_PERTURBATION(3);
- CERES_MAKE_1ST_ORDER_PERTURBATION(4);
- CERES_MAKE_1ST_ORDER_PERTURBATION(5);
- CERES_MAKE_1ST_ORDER_PERTURBATION(6);
- CERES_MAKE_1ST_ORDER_PERTURBATION(7);
- CERES_MAKE_1ST_ORDER_PERTURBATION(8);
- CERES_MAKE_1ST_ORDER_PERTURBATION(9);
-#undef CERES_MAKE_1ST_ORDER_PERTURBATION
+ ArraySelector<JetT,
+ ParameterDims::kNumParameters,
+ CERES_AUTODIFF_MAX_PARAMETERS_ON_STACK>
+ parameters_as_jets(ParameterDims::kNumParameters);
- if (!VariadicEvaluate<Functor, JetT,
- N0, N1, N2, N3, N4, N5, N6, N7, N8, N9>::Call(
- functor, unpacked_parameters, output)) {
- return false;
- }
+ // Pointers to the beginning of each parameter block
+ std::array<JetT*, ParameterDims::kNumParameterBlocks> unpacked_parameters =
+ ParameterDims::GetUnpackedParameters(parameters_as_jets.data());
- internal::Take0thOrderPart(num_outputs, output, function_value);
+ // If the number of residuals is fixed, we use the template argument as the
+ // number of outputs. Otherwise we use the num_outputs parameter. Note: The
+ // ?-operator here is compile-time evaluated, therefore num_outputs is also
+ // a compile-time constant for functors with fixed residuals.
+ const int num_outputs =
+ kNumResiduals == DYNAMIC ? dynamic_num_outputs : kNumResiduals;
+ DCHECK_GT(num_outputs, 0);
-#define CERES_TAKE_1ST_ORDER_PERTURBATION(i) \
- if (N ## i) { \
- if (jacobians[i]) { \
- internal::Take1stOrderPart<JetT, T, \
- jet ## i, \
- N ## i>(num_outputs, \
- output, \
- jacobians[i]); \
- } \
- }
- CERES_TAKE_1ST_ORDER_PERTURBATION(0);
- CERES_TAKE_1ST_ORDER_PERTURBATION(1);
- CERES_TAKE_1ST_ORDER_PERTURBATION(2);
- CERES_TAKE_1ST_ORDER_PERTURBATION(3);
- CERES_TAKE_1ST_ORDER_PERTURBATION(4);
- CERES_TAKE_1ST_ORDER_PERTURBATION(5);
- CERES_TAKE_1ST_ORDER_PERTURBATION(6);
- CERES_TAKE_1ST_ORDER_PERTURBATION(7);
- CERES_TAKE_1ST_ORDER_PERTURBATION(8);
- CERES_TAKE_1ST_ORDER_PERTURBATION(9);
-#undef CERES_TAKE_1ST_ORDER_PERTURBATION
- return true;
+ ArraySelector<JetT, kNumResiduals, CERES_AUTODIFF_MAX_RESIDUALS_ON_STACK>
+ residuals_as_jets(num_outputs);
+
+ // Invalidate the output Jets, so that we can detect if the user
+ // did not assign values to all of them.
+ for (int i = 0; i < num_outputs; ++i) {
+ residuals_as_jets[i].a = kImpossibleValue;
+ residuals_as_jets[i].v.setConstant(kImpossibleValue);
}
-};
+
+ Make1stOrderPerturbations<Parameters>::Apply(parameters,
+ parameters_as_jets.data());
+
+ if (!VariadicEvaluate<ParameterDims>(
+ functor, unpacked_parameters.data(), residuals_as_jets.data())) {
+ return false;
+ }
+
+ Take0thOrderPart(num_outputs, residuals_as_jets.data(), function_value);
+ Take1stOrderParts<Parameters>::Apply(
+ num_outputs, residuals_as_jets.data(), jacobians);
+
+ return true;
+}
} // namespace internal
} // namespace ceres
diff --git a/extern/ceres/include/ceres/internal/disable_warnings.h b/extern/ceres/include/ceres/internal/disable_warnings.h
index 094124f7159..fd848feec0f 100644
--- a/extern/ceres/include/ceres/internal/disable_warnings.h
+++ b/extern/ceres/include/ceres/internal/disable_warnings.h
@@ -35,7 +35,7 @@
#ifdef _MSC_VER
#pragma warning( push )
-// Disable the warning C4251 which is trigerred by stl classes in
+// Disable the warning C4251 which is triggered by stl classes in
// Ceres' public interface. To quote MSDN: "C4251 can be ignored "
// "if you are deriving from a type in the Standard C++ Library"
#pragma warning( disable : 4251 )
diff --git a/extern/ceres/include/ceres/internal/eigen.h b/extern/ceres/include/ceres/internal/eigen.h
index 7138804ace4..59545dfd9c9 100644
--- a/extern/ceres/include/ceres/internal/eigen.h
+++ b/extern/ceres/include/ceres/internal/eigen.h
@@ -52,40 +52,27 @@ typedef Eigen::Matrix<double,
Eigen::ColMajor> ColMajorMatrix;
typedef Eigen::Map<ColMajorMatrix, 0,
- Eigen::Stride<Eigen::Dynamic, 1> > ColMajorMatrixRef;
+ Eigen::Stride<Eigen::Dynamic, 1>> ColMajorMatrixRef;
typedef Eigen::Map<const ColMajorMatrix,
0,
- Eigen::Stride<Eigen::Dynamic, 1> > ConstColMajorMatrixRef;
-
-
+ Eigen::Stride<Eigen::Dynamic, 1>> ConstColMajorMatrixRef;
// 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>
+ template <int num_rows = Eigen::Dynamic, int num_cols = Eigen::Dynamic>
struct EigenTypes {
- typedef Eigen::Matrix <double, num_rows, num_cols, Eigen::RowMajor>
- Matrix;
-
- typedef Eigen::Map<
- Eigen::Matrix<double, num_rows, num_cols, Eigen::RowMajor> >
- MatrixRef;
-
- 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, num_cols, Eigen::RowMajor> >
- ConstMatrixRef;
-
- typedef Eigen::Map <
- const Eigen::Matrix<double, num_rows, 1> >
- ConstVectorRef;
+ typedef Eigen::Matrix<double,
+ num_rows,
+ num_cols,
+ num_cols == 1 ? Eigen::ColMajor : Eigen::RowMajor>
+ Matrix;
+
+ 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;
};
} // namespace ceres
diff --git a/extern/ceres/include/ceres/internal/fixed_array.h b/extern/ceres/include/ceres/internal/fixed_array.h
index 387298c58d0..f8ef02d40e8 100644
--- a/extern/ceres/include/ceres/internal/fixed_array.h
+++ b/extern/ceres/include/ceres/internal/fixed_array.h
@@ -1,189 +1,466 @@
-// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
-// http://ceres-solver.org/
+// Copyright 2018 The Abseil Authors.
//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
+// 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
//
-// * 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.
+// https://www.apache.org/licenses/LICENSE-2.0
//
-// 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.
+// 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.
//
-// Author: rennie@google.com (Jeffrey Rennie)
-// Author: sanjay@google.com (Sanjay Ghemawat) -- renamed to FixedArray
+// -----------------------------------------------------------------------------
+// File: fixed_array.h
+// -----------------------------------------------------------------------------
+//
+// A `FixedArray<T>` represents a non-resizable array of `T` where the length of
+// the array can be determined at run-time. It is a good replacement for
+// non-standard and deprecated uses of `alloca()` and variable length arrays
+// within the GCC extension. (See
+// https://gcc.gnu.org/onlinedocs/gcc/Variable-Length.html).
+//
+// `FixedArray` allocates small arrays inline, keeping performance fast by
+// avoiding heap operations. It also helps reduce the chances of
+// accidentally overflowing your stack if large input is passed to
+// your function.
#ifndef CERES_PUBLIC_INTERNAL_FIXED_ARRAY_H_
#define CERES_PUBLIC_INTERNAL_FIXED_ARRAY_H_
+#include <algorithm>
+#include <array>
#include <cstddef>
-#include "Eigen/Core"
-#include "ceres/internal/macros.h"
-#include "ceres/internal/manual_constructor.h"
+#include <memory>
+#include <tuple>
+#include <type_traits>
+
+#include <Eigen/Core> // For Eigen::aligned_allocator
+
+#include "ceres/internal/memory.h"
#include "glog/logging.h"
namespace ceres {
namespace internal {
-// A FixedArray<T> represents a non-resizable array of T where the
-// length of the array does not need to be a compile time constant.
-//
-// FixedArray allocates small arrays inline, and large arrays on
-// the heap. It is a good replacement for non-standard and deprecated
-// uses of alloca() and variable length arrays (a GCC extension).
-//
-// FixedArray keeps performance fast for small arrays, because it
-// avoids heap operations. It also helps reduce the chances of
-// accidentally overflowing your stack if large input is passed to
-// your function.
+constexpr static auto kFixedArrayUseDefault = static_cast<size_t>(-1);
+
+// The default fixed array allocator.
//
-// Also, FixedArray is useful for writing portable code. Not all
-// compilers support arrays of dynamic size.
+// As one can not easily detect if a struct contains or inherits from a fixed
+// size Eigen type, to be safe the Eigen::aligned_allocator is used by default.
+// But trivial types can never contain Eigen types, so std::allocator is used to
+// safe some heap memory.
+template <typename T>
+using FixedArrayDefaultAllocator =
+ typename std::conditional<std::is_trivial<T>::value,
+ std::allocator<T>,
+ Eigen::aligned_allocator<T>>::type;
-// Most users should not specify an inline_elements argument and let
-// FixedArray<> automatically determine the number of elements
-// to store inline based on sizeof(T).
+// -----------------------------------------------------------------------------
+// FixedArray
+// -----------------------------------------------------------------------------
+//
+// A `FixedArray` provides a run-time fixed-size array, allocating a small array
+// inline for efficiency.
//
-// If inline_elements is specified, the FixedArray<> implementation
-// will store arrays of length <= inline_elements inline.
+// Most users should not specify an `inline_elements` argument and let
+// `FixedArray` automatically determine the number of elements
+// to store inline based on `sizeof(T)`. If `inline_elements` is specified, the
+// `FixedArray` implementation will use inline storage for arrays with a
+// length <= `inline_elements`.
//
-// Finally note that unlike vector<T> FixedArray<T> will not zero-initialize
-// simple types like int, double, bool, etc.
+// Note that a `FixedArray` constructed with a `size_type` argument will
+// default-initialize its values by leaving trivially constructible types
+// uninitialized (e.g. int, int[4], double), and others default-constructed.
+// This matches the behavior of c-style arrays and `std::array`, but not
+// `std::vector`.
//
-// Non-POD types will be default-initialized just like regular vectors or
-// arrays.
+// Note that `FixedArray` does not provide a public allocator; if it requires a
+// heap allocation, it will do so with global `::operator new[]()` and
+// `::operator delete[]()`, even if T provides class-scope overrides for these
+// operators.
+template <typename T,
+ size_t N = kFixedArrayUseDefault,
+ typename A = FixedArrayDefaultAllocator<T>>
+class FixedArray {
+ static_assert(!std::is_array<T>::value || std::extent<T>::value > 0,
+ "Arrays with unknown bounds cannot be used with FixedArray.");
-#if defined(_WIN64)
- typedef __int64 ssize_t;
-#elif defined(_WIN32)
- typedef __int32 ssize_t;
-#endif
+ static constexpr size_t kInlineBytesDefault = 256;
+
+ using AllocatorTraits = std::allocator_traits<A>;
+ // std::iterator_traits isn't guaranteed to be SFINAE-friendly until C++17,
+ // but this seems to be mostly pedantic.
+ template <typename Iterator>
+ using EnableIfForwardIterator = typename std::enable_if<std::is_convertible<
+ typename std::iterator_traits<Iterator>::iterator_category,
+ std::forward_iterator_tag>::value>::type;
+ static constexpr bool DefaultConstructorIsNonTrivial() {
+ return !std::is_trivially_default_constructible<StorageElement>::value;
+ }
-template <typename T, ssize_t inline_elements = -1>
-class FixedArray {
public:
- // For playing nicely with stl:
- typedef T value_type;
- typedef T* iterator;
- typedef T const* const_iterator;
- typedef T& reference;
- typedef T const& const_reference;
- typedef T* pointer;
- typedef std::ptrdiff_t difference_type;
- typedef size_t size_type;
-
- // REQUIRES: n >= 0
- // Creates an array object that can store "n" elements.
- //
- // FixedArray<T> will not zero-initialiaze POD (simple) types like int,
- // double, bool, etc.
- // Non-POD types will be default-initialized just like regular vectors or
- // arrays.
- explicit FixedArray(size_type n);
-
- // Releases any resources.
- ~FixedArray();
-
- // Returns the length of the array.
- inline size_type size() const { return size_; }
-
- // Returns the memory size of the array in bytes.
- inline size_t memsize() const { return size_ * sizeof(T); }
-
- // Returns a pointer to the underlying element array.
- inline const T* get() const { return &array_[0].element; }
- inline T* get() { return &array_[0].element; }
+ using allocator_type = typename AllocatorTraits::allocator_type;
+ using value_type = typename AllocatorTraits::value_type;
+ using pointer = typename AllocatorTraits::pointer;
+ using const_pointer = typename AllocatorTraits::const_pointer;
+ using reference = value_type&;
+ using const_reference = const value_type&;
+ using size_type = typename AllocatorTraits::size_type;
+ using difference_type = typename AllocatorTraits::difference_type;
+ using iterator = pointer;
+ using const_iterator = const_pointer;
+ using reverse_iterator = std::reverse_iterator<iterator>;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+
+ static constexpr size_type inline_elements =
+ (N == kFixedArrayUseDefault ? kInlineBytesDefault / sizeof(value_type)
+ : static_cast<size_type>(N));
+
+ FixedArray(const FixedArray& other,
+ const allocator_type& a = allocator_type())
+ : FixedArray(other.begin(), other.end(), a) {}
+
+ FixedArray(FixedArray&& other, const allocator_type& a = allocator_type())
+ : FixedArray(std::make_move_iterator(other.begin()),
+ std::make_move_iterator(other.end()),
+ a) {}
+
+ // Creates an array object that can store `n` elements.
+ // Note that trivially constructible elements will be uninitialized.
+ explicit FixedArray(size_type n, const allocator_type& a = allocator_type())
+ : storage_(n, a) {
+ if (DefaultConstructorIsNonTrivial()) {
+ ConstructRange(storage_.alloc(), storage_.begin(), storage_.end());
+ }
+ }
+ // Creates an array initialized with `n` copies of `val`.
+ FixedArray(size_type n,
+ const value_type& val,
+ const allocator_type& a = allocator_type())
+ : storage_(n, a) {
+ ConstructRange(storage_.alloc(), storage_.begin(), storage_.end(), val);
+ }
+
+ // Creates an array initialized with the size and contents of `init_list`.
+ FixedArray(std::initializer_list<value_type> init_list,
+ const allocator_type& a = allocator_type())
+ : FixedArray(init_list.begin(), init_list.end(), a) {}
+
+ // Creates an array initialized with the elements from the input
+ // range. The array's size will always be `std::distance(first, last)`.
+ // REQUIRES: Iterator must be a forward_iterator or better.
+ template <typename Iterator, EnableIfForwardIterator<Iterator>* = nullptr>
+ FixedArray(Iterator first,
+ Iterator last,
+ const allocator_type& a = allocator_type())
+ : storage_(std::distance(first, last), a) {
+ CopyRange(storage_.alloc(), storage_.begin(), first, last);
+ }
+
+ ~FixedArray() noexcept {
+ for (auto* cur = storage_.begin(); cur != storage_.end(); ++cur) {
+ AllocatorTraits::destroy(storage_.alloc(), cur);
+ }
+ }
+
+ // Assignments are deleted because they break the invariant that the size of a
+ // `FixedArray` never changes.
+ void operator=(FixedArray&&) = delete;
+ void operator=(const FixedArray&) = delete;
+
+ // FixedArray::size()
+ //
+ // Returns the length of the fixed array.
+ size_type size() const { return storage_.size(); }
+
+ // FixedArray::max_size()
+ //
+ // Returns the largest possible value of `std::distance(begin(), end())` for a
+ // `FixedArray<T>`. This is equivalent to the most possible addressable bytes
+ // over the number of bytes taken by T.
+ constexpr size_type max_size() const {
+ return (std::numeric_limits<difference_type>::max)() / sizeof(value_type);
+ }
+
+ // FixedArray::empty()
+ //
+ // Returns whether or not the fixed array is empty.
+ bool empty() const { return size() == 0; }
+
+ // FixedArray::memsize()
+ //
+ // Returns the memory size of the fixed array in bytes.
+ size_t memsize() const { return size() * sizeof(value_type); }
+
+ // FixedArray::data()
+ //
+ // Returns a const T* pointer to elements of the `FixedArray`. This pointer
+ // can be used to access (but not modify) the contained elements.
+ const_pointer data() const { return AsValueType(storage_.begin()); }
+
+ // Overload of FixedArray::data() to return a T* pointer to elements of the
+ // fixed array. This pointer can be used to access and modify the contained
+ // elements.
+ pointer data() { return AsValueType(storage_.begin()); }
+
+ // FixedArray::operator[]
+ //
+ // Returns a reference the ith element of the fixed array.
// REQUIRES: 0 <= i < size()
- // Returns a reference to the "i"th element.
- inline T& operator[](size_type i) {
- DCHECK_LT(i, size_);
- return array_[i].element;
+ reference operator[](size_type i) {
+ DCHECK_LT(i, size());
+ return data()[i];
}
+ // Overload of FixedArray::operator()[] to return a const reference to the
+ // ith element of the fixed array.
// REQUIRES: 0 <= i < size()
- // Returns a reference to the "i"th element.
- inline const T& operator[](size_type i) const {
- DCHECK_LT(i, size_);
- return array_[i].element;
+ const_reference operator[](size_type i) const {
+ DCHECK_LT(i, size());
+ return data()[i];
+ }
+
+ // FixedArray::front()
+ //
+ // Returns a reference to the first element of the fixed array.
+ reference front() { return *begin(); }
+
+ // Overload of FixedArray::front() to return a reference to the first element
+ // of a fixed array of const values.
+ const_reference front() const { return *begin(); }
+
+ // FixedArray::back()
+ //
+ // Returns a reference to the last element of the fixed array.
+ reference back() { return *(end() - 1); }
+
+ // Overload of FixedArray::back() to return a reference to the last element
+ // of a fixed array of const values.
+ const_reference back() const { return *(end() - 1); }
+
+ // FixedArray::begin()
+ //
+ // Returns an iterator to the beginning of the fixed array.
+ iterator begin() { return data(); }
+
+ // Overload of FixedArray::begin() to return a const iterator to the
+ // beginning of the fixed array.
+ const_iterator begin() const { return data(); }
+
+ // FixedArray::cbegin()
+ //
+ // Returns a const iterator to the beginning of the fixed array.
+ const_iterator cbegin() const { return begin(); }
+
+ // FixedArray::end()
+ //
+ // Returns an iterator to the end of the fixed array.
+ iterator end() { return data() + size(); }
+
+ // Overload of FixedArray::end() to return a const iterator to the end of the
+ // fixed array.
+ const_iterator end() const { return data() + size(); }
+
+ // FixedArray::cend()
+ //
+ // Returns a const iterator to the end of the fixed array.
+ const_iterator cend() const { return end(); }
+
+ // FixedArray::rbegin()
+ //
+ // Returns a reverse iterator from the end of the fixed array.
+ reverse_iterator rbegin() { return reverse_iterator(end()); }
+
+ // Overload of FixedArray::rbegin() to return a const reverse iterator from
+ // the end of the fixed array.
+ const_reverse_iterator rbegin() const {
+ return const_reverse_iterator(end());
}
- inline iterator begin() { return &array_[0].element; }
- inline iterator end() { return &array_[size_].element; }
+ // FixedArray::crbegin()
+ //
+ // Returns a const reverse iterator from the end of the fixed array.
+ const_reverse_iterator crbegin() const { return rbegin(); }
- inline const_iterator begin() const { return &array_[0].element; }
- inline const_iterator end() const { return &array_[size_].element; }
+ // FixedArray::rend()
+ //
+ // Returns a reverse iterator from the beginning of the fixed array.
+ reverse_iterator rend() { return reverse_iterator(begin()); }
+
+ // Overload of FixedArray::rend() for returning a const reverse iterator
+ // from the beginning of the fixed array.
+ const_reverse_iterator rend() const {
+ return const_reverse_iterator(begin());
+ }
+
+ // FixedArray::crend()
+ //
+ // Returns a reverse iterator from the beginning of the fixed array.
+ const_reverse_iterator crend() const { return rend(); }
+
+ // FixedArray::fill()
+ //
+ // Assigns the given `value` to all elements in the fixed array.
+ void fill(const value_type& val) { std::fill(begin(), end(), val); }
+
+ // Relational operators. Equality operators are elementwise using
+ // `operator==`, while order operators order FixedArrays lexicographically.
+ friend bool operator==(const FixedArray& lhs, const FixedArray& rhs) {
+ return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
+ }
+
+ friend bool operator!=(const FixedArray& lhs, const FixedArray& rhs) {
+ return !(lhs == rhs);
+ }
+
+ friend bool operator<(const FixedArray& lhs, const FixedArray& rhs) {
+ return std::lexicographical_compare(
+ lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
+ }
+
+ friend bool operator>(const FixedArray& lhs, const FixedArray& rhs) {
+ return rhs < lhs;
+ }
+
+ friend bool operator<=(const FixedArray& lhs, const FixedArray& rhs) {
+ return !(rhs < lhs);
+ }
+
+ friend bool operator>=(const FixedArray& lhs, const FixedArray& rhs) {
+ return !(lhs < rhs);
+ }
private:
- // Container to hold elements of type T. This is necessary to handle
- // the case where T is a a (C-style) array. The size of InnerContainer
- // and T must be the same, otherwise callers' assumptions about use
- // of this code will be broken.
- struct InnerContainer {
- T element;
+ // StorageElement
+ //
+ // For FixedArrays with a C-style-array value_type, StorageElement is a POD
+ // wrapper struct called StorageElementWrapper that holds the value_type
+ // instance inside. This is needed for construction and destruction of the
+ // entire array regardless of how many dimensions it has. For all other cases,
+ // StorageElement is just an alias of value_type.
+ //
+ // Maintainer's Note: The simpler solution would be to simply wrap value_type
+ // in a struct whether it's an array or not. That causes some paranoid
+ // diagnostics to misfire, believing that 'data()' returns a pointer to a
+ // single element, rather than the packed array that it really is.
+ // e.g.:
+ //
+ // FixedArray<char> buf(1);
+ // sprintf(buf.data(), "foo");
+ //
+ // error: call to int __builtin___sprintf_chk(etc...)
+ // will always overflow destination buffer [-Werror]
+ //
+ template <typename OuterT,
+ typename InnerT = typename std::remove_extent<OuterT>::type,
+ size_t InnerN = std::extent<OuterT>::value>
+ struct StorageElementWrapper {
+ InnerT array[InnerN];
};
- // How many elements should we store inline?
- // a. If not specified, use a default of 256 bytes (256 bytes
- // seems small enough to not cause stack overflow or unnecessary
- // stack pollution, while still allowing stack allocation for
- // reasonably long character arrays.
- // b. Never use 0 length arrays (not ISO C++)
- static const size_type S1 = ((inline_elements < 0)
- ? (256/sizeof(T)) : inline_elements);
- static const size_type S2 = (S1 <= 0) ? 1 : S1;
- static const size_type kInlineElements = S2;
-
- size_type const size_;
- InnerContainer* const array_;
-
- // Allocate some space, not an array of elements of type T, so that we can
- // skip calling the T constructors and destructors for space we never use.
- ManualConstructor<InnerContainer> inline_space_[kInlineElements];
-};
+ using StorageElement =
+ typename std::conditional<std::is_array<value_type>::value,
+ StorageElementWrapper<value_type>,
+ value_type>::type;
-// Implementation details follow
-
-template <class T, ssize_t S>
-inline FixedArray<T, S>::FixedArray(typename FixedArray<T, S>::size_type n)
- : size_(n),
- array_((n <= kInlineElements
- ? reinterpret_cast<InnerContainer*>(inline_space_)
- : new InnerContainer[n])) {
- // Construct only the elements actually used.
- if (array_ == reinterpret_cast<InnerContainer*>(inline_space_)) {
- for (size_t i = 0; i != size_; ++i) {
- inline_space_[i].Init();
- }
+ static pointer AsValueType(pointer ptr) { return ptr; }
+ static pointer AsValueType(StorageElementWrapper<value_type>* ptr) {
+ return std::addressof(ptr->array);
}
-}
-template <class T, ssize_t S>
-inline FixedArray<T, S>::~FixedArray() {
- if (array_ != reinterpret_cast<InnerContainer*>(inline_space_)) {
- delete[] array_;
- } else {
- for (size_t i = 0; i != size_; ++i) {
- inline_space_[i].Destroy();
+ static_assert(sizeof(StorageElement) == sizeof(value_type), "");
+ static_assert(alignof(StorageElement) == alignof(value_type), "");
+
+ class NonEmptyInlinedStorage {
+ public:
+ StorageElement* data() { return reinterpret_cast<StorageElement*>(buff_); }
+ void AnnotateConstruct(size_type) {}
+ void AnnotateDestruct(size_type) {}
+
+ // #ifdef ADDRESS_SANITIZER
+ // void* RedzoneBegin() { return &redzone_begin_; }
+ // void* RedzoneEnd() { return &redzone_end_ + 1; }
+ // #endif // ADDRESS_SANITIZER
+
+ private:
+ // ADDRESS_SANITIZER_REDZONE(redzone_begin_);
+ alignas(StorageElement) char buff_[sizeof(StorageElement[inline_elements])];
+ // ADDRESS_SANITIZER_REDZONE(redzone_end_);
+ };
+
+ class EmptyInlinedStorage {
+ public:
+ StorageElement* data() { return nullptr; }
+ void AnnotateConstruct(size_type) {}
+ void AnnotateDestruct(size_type) {}
+ };
+
+ using InlinedStorage =
+ typename std::conditional<inline_elements == 0,
+ EmptyInlinedStorage,
+ NonEmptyInlinedStorage>::type;
+
+ // Storage
+ //
+ // An instance of Storage manages the inline and out-of-line memory for
+ // instances of FixedArray. This guarantees that even when construction of
+ // individual elements fails in the FixedArray constructor body, the
+ // destructor for Storage will still be called and out-of-line memory will be
+ // properly deallocated.
+ //
+ class Storage : public InlinedStorage {
+ public:
+ Storage(size_type n, const allocator_type& a)
+ : size_alloc_(n, a), data_(InitializeData()) {}
+
+ ~Storage() noexcept {
+ if (UsingInlinedStorage(size())) {
+ InlinedStorage::AnnotateDestruct(size());
+ } else {
+ AllocatorTraits::deallocate(alloc(), AsValueType(begin()), size());
+ }
}
- }
-}
+
+ size_type size() const { return std::get<0>(size_alloc_); }
+ StorageElement* begin() const { return data_; }
+ StorageElement* end() const { return begin() + size(); }
+ allocator_type& alloc() { return std::get<1>(size_alloc_); }
+
+ private:
+ static bool UsingInlinedStorage(size_type n) {
+ return n <= inline_elements;
+ }
+
+ StorageElement* InitializeData() {
+ if (UsingInlinedStorage(size())) {
+ InlinedStorage::AnnotateConstruct(size());
+ return InlinedStorage::data();
+ } else {
+ return reinterpret_cast<StorageElement*>(
+ AllocatorTraits::allocate(alloc(), size()));
+ }
+ }
+
+ // Using std::tuple and not absl::CompressedTuple, as it has a lot of
+ // dependencies to other absl headers.
+ std::tuple<size_type, allocator_type> size_alloc_;
+ StorageElement* data_;
+ };
+
+ Storage storage_;
+};
+
+template <typename T, size_t N, typename A>
+constexpr size_t FixedArray<T, N, A>::kInlineBytesDefault;
+
+template <typename T, size_t N, typename A>
+constexpr typename FixedArray<T, N, A>::size_type
+ FixedArray<T, N, A>::inline_elements;
} // namespace internal
} // namespace ceres
diff --git a/extern/ceres/internal/ceres/householder_vector.h b/extern/ceres/include/ceres/internal/householder_vector.h
index f54feea054d..55f68e526a0 100644
--- a/extern/ceres/internal/ceres/householder_vector.h
+++ b/extern/ceres/include/ceres/internal/householder_vector.h
@@ -28,8 +28,8 @@
//
// Author: vitus@google.com (Michael Vitus)
-#ifndef CERES_PUBLIC_HOUSEHOLDER_VECTOR_H_
-#define CERES_PUBLIC_HOUSEHOLDER_VECTOR_H_
+#ifndef CERES_PUBLIC_INTERNAL_HOUSEHOLDER_VECTOR_H_
+#define CERES_PUBLIC_INTERNAL_HOUSEHOLDER_VECTOR_H_
#include "Eigen/Core"
#include "glog/logging.h"
@@ -42,12 +42,15 @@ namespace internal {
// vector as pivot instead of first. This computes the vector v with v(n) = 1
// and beta such that H = I - beta * v * v^T is orthogonal and
// H * x = ||x||_2 * e_n.
-template <typename Scalar>
-void ComputeHouseholderVector(const Eigen::Matrix<Scalar, Eigen::Dynamic, 1>& x,
- Eigen::Matrix<Scalar, Eigen::Dynamic, 1>* v,
+//
+// NOTE: Some versions of MSVC have trouble deducing the type of v if
+// you do not specify all the template arguments explicitly.
+template <typename XVectorType, typename Scalar, int N>
+void ComputeHouseholderVector(const XVectorType& x,
+ Eigen::Matrix<Scalar, N, 1>* v,
Scalar* beta) {
- CHECK_NOTNULL(beta);
- CHECK_NOTNULL(v);
+ CHECK(beta != nullptr);
+ CHECK(v != nullptr);
CHECK_GT(x.rows(), 1);
CHECK_EQ(x.rows(), v->rows());
@@ -82,4 +85,4 @@ void ComputeHouseholderVector(const Eigen::Matrix<Scalar, Eigen::Dynamic, 1>& x,
} // namespace internal
} // namespace ceres
-#endif // CERES_PUBLIC_HOUSEHOLDER_VECTOR_H_
+#endif // CERES_PUBLIC_INTERNAL_HOUSEHOLDER_VECTOR_H_
diff --git a/extern/ceres/include/ceres/internal/integer_sequence_algorithm.h b/extern/ceres/include/ceres/internal/integer_sequence_algorithm.h
new file mode 100644
index 00000000000..170acac2832
--- /dev/null
+++ b/extern/ceres/include/ceres/internal/integer_sequence_algorithm.h
@@ -0,0 +1,165 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2018 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)
+//
+// Algorithms to be used together with integer_sequence, like computing the sum
+// or the exclusive scan (sometimes called exclusive prefix sum) at compile
+// time.
+
+#ifndef CERES_PUBLIC_INTERNAL_INTEGER_SEQUENCE_ALGORITHM_H_
+#define CERES_PUBLIC_INTERNAL_INTEGER_SEQUENCE_ALGORITHM_H_
+
+#include <utility>
+
+namespace ceres {
+namespace internal {
+
+// Implementation of calculating the sum of an integer sequence.
+// Recursively instantiate SumImpl and calculate the sum of the N first
+// numbers. This reduces the number of instantiations and speeds up
+// compilation.
+//
+// Examples:
+// 1) integer_sequence<int, 5>:
+// Value = 5
+//
+// 2) integer_sequence<int, 4, 2>:
+// Value = 4 + 2 + SumImpl<integer_sequence<int>>::Value
+// Value = 4 + 2 + 0
+//
+// 3) integer_sequence<int, 2, 1, 4>:
+// Value = 2 + 1 + SumImpl<integer_sequence<int, 4>>::Value
+// Value = 2 + 1 + 4
+template <typename Seq>
+struct SumImpl;
+
+// Strip of and sum the first number.
+template <typename T, T N, T... Ns>
+struct SumImpl<std::integer_sequence<T, N, Ns...>> {
+ static constexpr T Value = N + SumImpl<std::integer_sequence<T, Ns...>>::Value;
+};
+
+// Strip of and sum the first two numbers.
+template <typename T, T N1, T N2, T... Ns>
+struct SumImpl<std::integer_sequence<T, N1, N2, Ns...>> {
+ static constexpr T Value =
+ N1 + N2 + SumImpl<std::integer_sequence<T, Ns...>>::Value;
+};
+
+// Strip of and sum the first four numbers.
+template <typename T, T N1, T N2, T N3, T N4, T... Ns>
+struct SumImpl<std::integer_sequence<T, N1, N2, N3, N4, Ns...>> {
+ static constexpr T Value =
+ N1 + N2 + N3 + N4 + SumImpl<std::integer_sequence<T, Ns...>>::Value;
+};
+
+// Only one number is left. 'Value' is just that number ('recursion' ends).
+template <typename T, T N>
+struct SumImpl<std::integer_sequence<T, N>> {
+ static constexpr T Value = N;
+};
+
+// No number is left. 'Value' is the identity element (for sum this is zero).
+template <typename T>
+struct SumImpl<std::integer_sequence<T>> {
+ static constexpr T Value = T(0);
+};
+
+// Calculate the sum of an integer sequence. The resulting sum will be stored in
+// 'Value'.
+template <typename Seq>
+class Sum {
+ using T = typename Seq::value_type;
+
+ public:
+ static constexpr T Value = SumImpl<Seq>::Value;
+};
+
+// Implementation of calculating an exclusive scan (exclusive prefix sum) of an
+// integer sequence. Exclusive means that the i-th input element is not included
+// in the i-th sum. Calculating the exclusive scan for an input array I results
+// in the following output R:
+//
+// R[0] = 0
+// R[1] = I[0];
+// R[2] = I[0] + I[1];
+// R[3] = I[0] + I[1] + I[2];
+// ...
+//
+// In C++17 std::exclusive_scan does the same operation at runtime (but
+// cannot be used to calculate the prefix sum at compile time). See
+// https://en.cppreference.com/w/cpp/algorithm/exclusive_scan for a more
+// detailed description.
+//
+// Example for integer_sequence<int, 1, 4, 3> (seq := integer_sequence):
+// T , Sum, Ns... , Rs...
+// ExclusiveScanImpl<int, 0, seq<int, 1, 4, 3>, seq<int >>
+// ExclusiveScanImpl<int, 1, seq<int, 4, 3>, seq<int, 0 >>
+// ExclusiveScanImpl<int, 5, seq<int, 3>, seq<int, 0, 1 >>
+// ExclusiveScanImpl<int, 8, seq<int >, seq<int, 0, 1, 5>>
+// ^^^^^^^^^^^^^^^^^
+// resulting sequence
+template <typename T, T Sum, typename SeqIn, typename SeqOut>
+struct ExclusiveScanImpl;
+
+template <typename T, T Sum, T N, T... Ns, T... Rs>
+struct ExclusiveScanImpl<T, Sum, std::integer_sequence<T, N, Ns...>,
+ std::integer_sequence<T, Rs...>> {
+ using Type =
+ typename ExclusiveScanImpl<T, Sum + N, std::integer_sequence<T, Ns...>,
+ std::integer_sequence<T, Rs..., Sum>>::Type;
+};
+
+// End of 'recursion'. The resulting type is SeqOut.
+template <typename T, T Sum, typename SeqOut>
+struct ExclusiveScanImpl<T, Sum, std::integer_sequence<T>, SeqOut> {
+ using Type = SeqOut;
+};
+
+// Calculates the exclusive scan of the specified integer sequence. The last
+// element (the total) is not included in the resulting sequence so they have
+// same length. This means the exclusive scan of integer_sequence<int, 1, 2, 3>
+// will be integer_sequence<int, 0, 1, 3>.
+template <typename Seq>
+class ExclusiveScanT {
+ using T = typename Seq::value_type;
+
+ public:
+ using Type =
+ typename ExclusiveScanImpl<T, T(0), Seq, std::integer_sequence<T>>::Type;
+};
+
+// Helper to use exclusive scan without typename.
+template <typename Seq>
+using ExclusiveScan = typename ExclusiveScanT<Seq>::Type;
+
+} // namespace internal
+} // namespace ceres
+
+#endif // CERES_PUBLIC_INTERNAL_INTEGER_SEQUENCE_ALGORITHM_H_
diff --git a/extern/ceres/include/ceres/internal/line_parameterization.h b/extern/ceres/include/ceres/internal/line_parameterization.h
new file mode 100644
index 00000000000..eda390148df
--- /dev/null
+++ b/extern/ceres/include/ceres/internal/line_parameterization.h
@@ -0,0 +1,183 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2020 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_INTERNAL_LINE_PARAMETERIZATION_H_
+#define CERES_PUBLIC_INTERNAL_LINE_PARAMETERIZATION_H_
+
+#include "householder_vector.h"
+
+namespace ceres {
+
+template <int AmbientSpaceDimension>
+bool LineParameterization<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 for the homogeneous
+ // vector parameterization:
+ //
+ // 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.
+
+ static constexpr int kDim = AmbientSpaceDimension;
+ using AmbientVector = Eigen::Matrix<double, kDim, 1>;
+ using AmbientVectorRef = Eigen::Map<Eigen::Matrix<double, kDim, 1>>;
+ using ConstAmbientVectorRef =
+ Eigen::Map<const Eigen::Matrix<double, kDim, 1>>;
+ using ConstTangentVectorRef =
+ Eigen::Map<const Eigen::Matrix<double, kDim - 1, 1>>;
+
+ ConstAmbientVectorRef o(x_ptr);
+ ConstAmbientVectorRef d(x_ptr + kDim);
+
+ ConstTangentVectorRef delta_o(delta_ptr);
+ ConstTangentVectorRef delta_d(delta_ptr + kDim - 1);
+ AmbientVectorRef o_plus_delta(x_plus_delta_ptr);
+ AmbientVectorRef d_plus_delta(x_plus_delta_ptr + kDim);
+
+ 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;
+ 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<ConstAmbientVectorRef, double, kDim>(
+ d, &v, &beta);
+
+ if (norm_delta_d != 0.0) {
+ // Map the delta from the minimum representation to the over parameterized
+ // homogeneous vector. See section A6.9.2 on page 624 of Hartley & Zisserman
+ // (2nd Edition) for a detailed description. Note there is a typo on Page
+ // 625, line 4 so check the book errata.
+ const double norm_delta_div_2 = 0.5 * norm_delta_d;
+ const double sin_delta_by_delta =
+ std::sin(norm_delta_div_2) / norm_delta_div_2;
+
+ // Apply the delta update to remain on the unit sphere. See section A6.9.3
+ // on page 625 of Hartley & Zisserman (2nd Edition) for a detailed
+ // description.
+ AmbientVector y;
+ y.template head<kDim - 1>() = 0.5 * sin_delta_by_delta * delta_d;
+ y[kDim - 1] = std::cos(norm_delta_div_2);
+
+ d_plus_delta = d.norm() * (y - v * (beta * (v.transpose() * y)));
+ }
+
+ // 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;
+ y << 0.5 * delta_o, 0;
+ o_plus_delta += y - v * (beta * (v.transpose() * y));
+
+ return true;
+}
+
+template <int AmbientSpaceDimension>
+bool LineParameterization<AmbientSpaceDimension>::ComputeJacobian(
+ const double* x_ptr, double* jacobian_ptr) const {
+ static constexpr int kDim = AmbientSpaceDimension;
+ using AmbientVector = Eigen::Matrix<double, kDim, 1>;
+ using ConstAmbientVectorRef =
+ Eigen::Map<const Eigen::Matrix<double, kDim, 1>>;
+ using MatrixRef = Eigen::Map<
+ Eigen::Matrix<double, 2 * kDim, 2 * (kDim - 1), Eigen::RowMajor>>;
+
+ ConstAmbientVectorRef d(x_ptr + kDim);
+ MatrixRef jacobian(jacobian_ptr);
+
+ // Clear the Jacobian as only half of the matrix is not zero.
+ jacobian.setZero();
+
+ AmbientVector v;
+ 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<ConstAmbientVectorRef, double, kDim>(
+ d, &v, &beta);
+
+ // The Jacobian is equal to J = 0.5 * H.leftCols(kDim - 1) where H is
+ // the Householder matrix (H = I - beta * v * v') for the origin point. For
+ // the line direction part the Jacobian is scaled by the norm of the
+ // direction.
+ for (int i = 0; i < kDim - 1; ++i) {
+ jacobian.block(0, i, kDim, 1) = -0.5 * beta * v(i) * v;
+ jacobian.col(i)(i) += 0.5;
+ }
+
+ jacobian.template block<kDim, kDim - 1>(kDim, kDim - 1) =
+ jacobian.template block<kDim, kDim - 1>(0, 0) * d.norm();
+ return true;
+}
+
+} // namespace ceres
+
+#endif // CERES_PUBLIC_INTERNAL_LINE_PARAMETERIZATION_H_
diff --git a/extern/ceres/include/ceres/internal/macros.h b/extern/ceres/include/ceres/internal/macros.h
deleted file mode 100644
index bebb965e25b..00000000000
--- a/extern/ceres/include/ceres/internal/macros.h
+++ /dev/null
@@ -1,170 +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.
-//
-//
-// Various Google-specific macros.
-//
-// This code is compiled directly on many platforms, including client
-// platforms like Windows, Mac, and embedded systems. Before making
-// any changes here, make sure that you're not breaking any platforms.
-
-#ifndef CERES_PUBLIC_INTERNAL_MACROS_H_
-#define CERES_PUBLIC_INTERNAL_MACROS_H_
-
-#include <cstddef> // For size_t.
-
-// A macro to disallow the copy constructor and operator= functions
-// This should be used in the private: declarations for a class
-//
-// For disallowing only assign or copy, write the code directly, but declare
-// the intend in a comment, for example:
-//
-// void operator=(const TypeName&); // _DISALLOW_ASSIGN
-
-// Note, that most uses of CERES_DISALLOW_ASSIGN and CERES_DISALLOW_COPY
-// are broken semantically, one should either use disallow both or
-// neither. Try to avoid these in new code.
-#define CERES_DISALLOW_COPY_AND_ASSIGN(TypeName) \
- TypeName(const TypeName&); \
- void operator=(const TypeName&)
-
-// A macro to disallow all the implicit constructors, namely the
-// default constructor, copy constructor and operator= functions.
-//
-// This should be used in the private: declarations for a class
-// that wants to prevent anyone from instantiating it. This is
-// especially useful for classes containing only static methods.
-#define CERES_DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
- TypeName(); \
- CERES_DISALLOW_COPY_AND_ASSIGN(TypeName)
-
-// The arraysize(arr) macro returns the # of elements in an array arr.
-// The expression is a compile-time constant, and therefore can be
-// used in defining new arrays, for example. If you use arraysize on
-// a pointer by mistake, you will get a compile-time error.
-//
-// One caveat is that arraysize() doesn't accept any array of an
-// anonymous type or a type defined inside a function. In these rare
-// cases, you have to use the unsafe ARRAYSIZE() macro below. This is
-// due to a limitation in C++'s template system. The limitation might
-// eventually be removed, but it hasn't happened yet.
-
-// This template function declaration is used in defining arraysize.
-// Note that the function doesn't need an implementation, as we only
-// use its type.
-template <typename T, size_t N>
-char (&ArraySizeHelper(T (&array)[N]))[N];
-
-// That gcc wants both of these prototypes seems mysterious. VC, for
-// its part, can't decide which to use (another mystery). Matching of
-// template overloads: the final frontier.
-#ifndef _WIN32
-template <typename T, size_t N>
-char (&ArraySizeHelper(const T (&array)[N]))[N];
-#endif
-
-#define arraysize(array) (sizeof(ArraySizeHelper(array)))
-
-// ARRAYSIZE performs essentially the same calculation as arraysize,
-// but can be used on anonymous types or types defined inside
-// functions. It's less safe than arraysize as it accepts some
-// (although not all) pointers. Therefore, you should use arraysize
-// whenever possible.
-//
-// The expression ARRAYSIZE(a) is a compile-time constant of type
-// size_t.
-//
-// ARRAYSIZE catches a few type errors. If you see a compiler error
-//
-// "warning: division by zero in ..."
-//
-// when using ARRAYSIZE, you are (wrongfully) giving it a pointer.
-// You should only use ARRAYSIZE on statically allocated arrays.
-//
-// The following comments are on the implementation details, and can
-// be ignored by the users.
-//
-// ARRAYSIZE(arr) works by inspecting sizeof(arr) (the # of bytes in
-// the array) and sizeof(*(arr)) (the # of bytes in one array
-// element). If the former is divisible by the latter, perhaps arr is
-// indeed an array, in which case the division result is the # of
-// elements in the array. Otherwise, arr cannot possibly be an array,
-// and we generate a compiler error to prevent the code from
-// compiling.
-//
-// Since the size of bool is implementation-defined, we need to cast
-// !(sizeof(a) & sizeof(*(a))) to size_t in order to ensure the final
-// result has type size_t.
-//
-// This macro is not perfect as it wrongfully accepts certain
-// pointers, namely where the pointer size is divisible by the pointee
-// size. Since all our code has to go through a 32-bit compiler,
-// where a pointer is 4 bytes, this means all pointers to a type whose
-// size is 3 or greater than 4 will be (righteously) rejected.
-//
-// Kudos to Jorg Brown for this simple and elegant implementation.
-//
-// - wan 2005-11-16
-//
-// Starting with Visual C++ 2005, WinNT.h includes ARRAYSIZE. However,
-// the definition comes from the over-broad windows.h header that
-// introduces a macro, ERROR, that conflicts with the logging framework
-// that Ceres uses. Instead, rename ARRAYSIZE to CERES_ARRAYSIZE.
-#define CERES_ARRAYSIZE(a) \
- ((sizeof(a) / sizeof(*(a))) / \
- static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
-
-// Tell the compiler to warn about unused return values for functions
-// declared with this macro. The macro should be used on function
-// declarations following the argument list:
-//
-// Sprocket* AllocateSprocket() MUST_USE_RESULT;
-//
-#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) \
- && !defined(COMPILER_ICC)
-#define CERES_MUST_USE_RESULT __attribute__ ((warn_unused_result))
-#else
-#define CERES_MUST_USE_RESULT
-#endif
-
-// Platform independent macros to get aligned memory allocations.
-// For example
-//
-// MyFoo my_foo CERES_ALIGN_ATTRIBUTE(16);
-//
-// Gives us an instance of MyFoo which is aligned at a 16 byte
-// boundary.
-#if defined(_MSC_VER)
-#define CERES_ALIGN_ATTRIBUTE(n) __declspec(align(n))
-#define CERES_ALIGN_OF(T) __alignof(T)
-#elif defined(__GNUC__)
-#define CERES_ALIGN_ATTRIBUTE(n) __attribute__((aligned(n)))
-#define CERES_ALIGN_OF(T) __alignof(T)
-#endif
-
-#endif // CERES_PUBLIC_INTERNAL_MACROS_H_
diff --git a/extern/ceres/include/ceres/internal/manual_constructor.h b/extern/ceres/include/ceres/internal/manual_constructor.h
deleted file mode 100644
index 0d7633cef83..00000000000
--- a/extern/ceres/include/ceres/internal/manual_constructor.h
+++ /dev/null
@@ -1,208 +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: kenton@google.com (Kenton Varda)
-//
-// ManualConstructor statically-allocates space in which to store some
-// object, but does not initialize it. You can then call the constructor
-// and destructor for the object yourself as you see fit. This is useful
-// for memory management optimizations, where you want to initialize and
-// destroy an object multiple times but only allocate it once.
-//
-// (When I say ManualConstructor statically allocates space, I mean that
-// the ManualConstructor object itself is forced to be the right size.)
-
-#ifndef CERES_PUBLIC_INTERNAL_MANUAL_CONSTRUCTOR_H_
-#define CERES_PUBLIC_INTERNAL_MANUAL_CONSTRUCTOR_H_
-
-#include <new>
-
-namespace ceres {
-namespace internal {
-
-// ------- Define CERES_ALIGNED_CHAR_ARRAY --------------------------------
-
-#ifndef CERES_ALIGNED_CHAR_ARRAY
-
-// Because MSVC and older GCCs require that the argument to their alignment
-// construct to be a literal constant integer, we use a template instantiated
-// at all the possible powers of two.
-template<int alignment, int size> struct AlignType { };
-template<int size> struct AlignType<0, size> { typedef char result[size]; };
-
-#if !defined(CERES_ALIGN_ATTRIBUTE)
-#define CERES_ALIGNED_CHAR_ARRAY you_must_define_CERES_ALIGNED_CHAR_ARRAY_for_your_compiler
-#else // !defined(CERES_ALIGN_ATTRIBUTE)
-
-#define CERES_ALIGN_TYPE_TEMPLATE(X) \
- template<int size> struct AlignType<X, size> { \
- typedef CERES_ALIGN_ATTRIBUTE(X) char result[size]; \
- }
-
-CERES_ALIGN_TYPE_TEMPLATE(1);
-CERES_ALIGN_TYPE_TEMPLATE(2);
-CERES_ALIGN_TYPE_TEMPLATE(4);
-CERES_ALIGN_TYPE_TEMPLATE(8);
-CERES_ALIGN_TYPE_TEMPLATE(16);
-CERES_ALIGN_TYPE_TEMPLATE(32);
-CERES_ALIGN_TYPE_TEMPLATE(64);
-CERES_ALIGN_TYPE_TEMPLATE(128);
-CERES_ALIGN_TYPE_TEMPLATE(256);
-CERES_ALIGN_TYPE_TEMPLATE(512);
-CERES_ALIGN_TYPE_TEMPLATE(1024);
-CERES_ALIGN_TYPE_TEMPLATE(2048);
-CERES_ALIGN_TYPE_TEMPLATE(4096);
-CERES_ALIGN_TYPE_TEMPLATE(8192);
-// Any larger and MSVC++ will complain.
-
-#undef CERES_ALIGN_TYPE_TEMPLATE
-
-#define CERES_ALIGNED_CHAR_ARRAY(T, Size) \
- typename AlignType<CERES_ALIGN_OF(T), sizeof(T) * Size>::result
-
-#endif // !defined(CERES_ALIGN_ATTRIBUTE)
-
-#endif // CERES_ALIGNED_CHAR_ARRAY
-
-template <typename Type>
-class ManualConstructor {
- public:
- // No constructor or destructor because one of the most useful uses of
- // this class is as part of a union, and members of a union cannot have
- // constructors or destructors. And, anyway, the whole point of this
- // class is to bypass these.
-
- inline Type* get() {
- return reinterpret_cast<Type*>(space_);
- }
- inline const Type* get() const {
- return reinterpret_cast<const Type*>(space_);
- }
-
- inline Type* operator->() { return get(); }
- inline const Type* operator->() const { return get(); }
-
- inline Type& operator*() { return *get(); }
- inline const Type& operator*() const { return *get(); }
-
- // This is needed to get around the strict aliasing warning GCC generates.
- inline void* space() {
- return reinterpret_cast<void*>(space_);
- }
-
- // You can pass up to four constructor arguments as arguments of Init().
- inline void Init() {
- new(space()) Type;
- }
-
- template <typename T1>
- inline void Init(const T1& p1) {
- new(space()) Type(p1);
- }
-
- template <typename T1, typename T2>
- inline void Init(const T1& p1, const T2& p2) {
- new(space()) Type(p1, p2);
- }
-
- template <typename T1, typename T2, typename T3>
- inline void Init(const T1& p1, const T2& p2, const T3& p3) {
- new(space()) Type(p1, p2, p3);
- }
-
- template <typename T1, typename T2, typename T3, typename T4>
- inline void Init(const T1& p1, const T2& p2, const T3& p3, const T4& p4) {
- new(space()) Type(p1, p2, p3, p4);
- }
-
- template <typename T1, typename T2, typename T3, typename T4, typename T5>
- inline void Init(const T1& p1, const T2& p2, const T3& p3, const T4& p4,
- const T5& p5) {
- new(space()) Type(p1, p2, p3, p4, p5);
- }
-
- template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6>
- inline void Init(const T1& p1, const T2& p2, const T3& p3, const T4& p4,
- const T5& p5, const T6& p6) {
- new(space()) Type(p1, p2, p3, p4, p5, p6);
- }
-
- template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7>
- inline void Init(const T1& p1, const T2& p2, const T3& p3, const T4& p4,
- const T5& p5, const T6& p6, const T7& p7) {
- new(space()) Type(p1, p2, p3, p4, p5, p6, p7);
- }
-
- template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8>
- inline void Init(const T1& p1, const T2& p2, const T3& p3, const T4& p4,
- const T5& p5, const T6& p6, const T7& p7, const T8& p8) {
- new(space()) Type(p1, p2, p3, p4, p5, p6, p7, p8);
- }
-
- template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9>
- inline void Init(const T1& p1, const T2& p2, const T3& p3, const T4& p4,
- const T5& p5, const T6& p6, const T7& p7, const T8& p8,
- const T9& p9) {
- new(space()) Type(p1, p2, p3, p4, p5, p6, p7, p8, p9);
- }
-
- template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10>
- inline void Init(const T1& p1, const T2& p2, const T3& p3, const T4& p4,
- const T5& p5, const T6& p6, const T7& p7, const T8& p8,
- const T9& p9, const T10& p10) {
- new(space()) Type(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10);
- }
-
- template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11>
- inline void Init(const T1& p1, const T2& p2, const T3& p3, const T4& p4,
- const T5& p5, const T6& p6, const T7& p7, const T8& p8,
- const T9& p9, const T10& p10, const T11& p11) {
- new(space()) Type(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11);
- }
-
- inline void Destroy() {
- get()->~Type();
- }
-
- private:
- CERES_ALIGNED_CHAR_ARRAY(Type, 1) space_;
-};
-
-#undef CERES_ALIGNED_CHAR_ARRAY
-
-} // namespace internal
-} // namespace ceres
-
-#endif // CERES_PUBLIC_INTERNAL_MANUAL_CONSTRUCTOR_H_
diff --git a/extern/ceres/include/ceres/internal/memory.h b/extern/ceres/include/ceres/internal/memory.h
new file mode 100644
index 00000000000..45c5b67c353
--- /dev/null
+++ b/extern/ceres/include/ceres/internal/memory.h
@@ -0,0 +1,90 @@
+// Copyright 2017 The Abseil Authors.
+//
+// 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
+//
+// https://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.
+//
+// -----------------------------------------------------------------------------
+// File: memory.h
+// -----------------------------------------------------------------------------
+//
+// This header file contains utility functions for managing the creation and
+// conversion of smart pointers. This file is an extension to the C++
+// standard <memory> library header file.
+
+#ifndef CERES_PUBLIC_INTERNAL_MEMORY_H_
+#define CERES_PUBLIC_INTERNAL_MEMORY_H_
+
+#include <memory>
+
+#ifdef CERES_HAVE_EXCEPTIONS
+#define CERES_INTERNAL_TRY try
+#define CERES_INTERNAL_CATCH_ANY catch (...)
+#define CERES_INTERNAL_RETHROW \
+ do { \
+ throw; \
+ } while (false)
+#else // CERES_HAVE_EXCEPTIONS
+#define CERES_INTERNAL_TRY if (true)
+#define CERES_INTERNAL_CATCH_ANY else if (false)
+#define CERES_INTERNAL_RETHROW \
+ do { \
+ } while (false)
+#endif // CERES_HAVE_EXCEPTIONS
+
+namespace ceres {
+namespace internal {
+
+template <typename Allocator, typename Iterator, typename... Args>
+void ConstructRange(Allocator& alloc,
+ Iterator first,
+ Iterator last,
+ const Args&... args) {
+ for (Iterator cur = first; cur != last; ++cur) {
+ CERES_INTERNAL_TRY {
+ std::allocator_traits<Allocator>::construct(
+ alloc, std::addressof(*cur), args...);
+ }
+ CERES_INTERNAL_CATCH_ANY {
+ while (cur != first) {
+ --cur;
+ std::allocator_traits<Allocator>::destroy(alloc, std::addressof(*cur));
+ }
+ CERES_INTERNAL_RETHROW;
+ }
+ }
+}
+
+template <typename Allocator, typename Iterator, typename InputIterator>
+void CopyRange(Allocator& alloc,
+ Iterator destination,
+ InputIterator first,
+ InputIterator last) {
+ for (Iterator cur = destination; first != last;
+ static_cast<void>(++cur), static_cast<void>(++first)) {
+ CERES_INTERNAL_TRY {
+ std::allocator_traits<Allocator>::construct(
+ alloc, std::addressof(*cur), *first);
+ }
+ CERES_INTERNAL_CATCH_ANY {
+ while (cur != destination) {
+ --cur;
+ std::allocator_traits<Allocator>::destroy(alloc, std::addressof(*cur));
+ }
+ CERES_INTERNAL_RETHROW;
+ }
+ }
+}
+
+} // namespace internal
+} // namespace ceres
+
+#endif // CERES_PUBLIC_INTERNAL_MEMORY_H_
diff --git a/extern/ceres/include/ceres/internal/numeric_diff.h b/extern/ceres/include/ceres/internal/numeric_diff.h
index 11e8275b1d3..fb2e00baca5 100644
--- a/extern/ceres/include/ceres/internal/numeric_diff.h
+++ b/extern/ceres/include/ceres/internal/numeric_diff.h
@@ -36,12 +36,12 @@
#define CERES_PUBLIC_INTERNAL_NUMERIC_DIFF_H_
#include <cstring>
+#include <utility>
#include "Eigen/Dense"
#include "Eigen/StdVector"
#include "ceres/cost_function.h"
#include "ceres/internal/fixed_array.h"
-#include "ceres/internal/scoped_ptr.h"
#include "ceres/internal/variadic_evaluate.h"
#include "ceres/numeric_diff_options.h"
#include "ceres/types.h"
@@ -51,42 +51,11 @@
namespace ceres {
namespace internal {
-// Helper templates that allow evaluation of a variadic functor or a
-// CostFunction object.
-template <typename CostFunctor,
- int N0, int N1, int N2, int N3, int N4,
- int N5, int N6, int N7, int N8, int N9 >
-bool EvaluateImpl(const CostFunctor* functor,
- double const* const* parameters,
- double* residuals,
- const void* /* NOT USED */) {
- return VariadicEvaluate<CostFunctor,
- double,
- N0, N1, N2, N3, N4, N5, N6, N7, N8, N9>::Call(
- *functor,
- parameters,
- residuals);
-}
-
-template <typename CostFunctor,
- int N0, int N1, int N2, int N3, int N4,
- int N5, int N6, int N7, int N8, int N9 >
-bool EvaluateImpl(const CostFunctor* functor,
- double const* const* parameters,
- double* residuals,
- const CostFunction* /* NOT USED */) {
- return functor->Evaluate(parameters, residuals, NULL);
-}
-
// This is split from the main class because C++ doesn't allow partial template
// specializations for member functions. The alternative is to repeat the main
// class for differing numbers of parameters, which is also unfortunate.
-template <typename CostFunctor,
- NumericDiffMethodType kMethod,
- int kNumResiduals,
- int N0, int N1, int N2, int N3, int N4,
- int N5, int N6, int N7, int N8, int N9,
- int kParameterBlock,
+template <typename CostFunctor, NumericDiffMethodType kMethod,
+ int kNumResiduals, typename ParameterDims, int kParameterBlock,
int kParameterBlockSize>
struct NumericDiff {
// Mutates parameters but must restore them before return.
@@ -104,6 +73,8 @@ struct NumericDiff {
using Eigen::RowMajor;
using Eigen::ColMajor;
+ DCHECK(jacobian);
+
const int num_residuals_internal =
(kNumResiduals != ceres::DYNAMIC ? kNumResiduals : num_residuals);
const int parameter_block_index_internal =
@@ -155,7 +126,7 @@ struct NumericDiff {
// compute the derivative for that parameter.
FixedArray<double> temp_residual_array(num_residuals_internal);
FixedArray<double> residual_array(num_residuals_internal);
- Map<ResidualVector> residuals(residual_array.get(),
+ Map<ResidualVector> residuals(residual_array.data(),
num_residuals_internal);
for (int j = 0; j < parameter_block_size_internal; ++j) {
@@ -170,8 +141,8 @@ struct NumericDiff {
residuals_at_eval_point,
parameters,
x_plus_delta.data(),
- temp_residual_array.get(),
- residual_array.get())) {
+ temp_residual_array.data(),
+ residual_array.data())) {
return false;
}
} else {
@@ -182,8 +153,8 @@ struct NumericDiff {
residuals_at_eval_point,
parameters,
x_plus_delta.data(),
- temp_residual_array.get(),
- residual_array.get())) {
+ temp_residual_array.data(),
+ residual_array.data())) {
return false;
}
}
@@ -220,8 +191,9 @@ struct NumericDiff {
// Mutate 1 element at a time and then restore.
x_plus_delta(parameter_index) = x(parameter_index) + delta;
- if (!EvaluateImpl<CostFunctor, N0, N1, N2, N3, N4, N5, N6, N7, N8, N9>(
- functor, parameters, residuals.data(), functor)) {
+ if (!VariadicEvaluate<ParameterDims>(*functor,
+ parameters,
+ residuals.data())) {
return false;
}
@@ -234,8 +206,9 @@ struct NumericDiff {
// Compute the function on the other side of x(parameter_index).
x_plus_delta(parameter_index) = x(parameter_index) - delta;
- if (!EvaluateImpl<CostFunctor, N0, N1, N2, N3, N4, N5, N6, N7, N8, N9>(
- functor, parameters, temp_residuals.data(), functor)) {
+ if (!VariadicEvaluate<ParameterDims>(*functor,
+ parameters,
+ temp_residuals.data())) {
return false;
}
@@ -407,35 +380,116 @@ struct NumericDiff {
}
};
-template <typename CostFunctor,
- NumericDiffMethodType kMethod,
- int kNumResiduals,
- int N0, int N1, int N2, int N3, int N4,
- int N5, int N6, int N7, int N8, int N9,
- int kParameterBlock>
-struct NumericDiff<CostFunctor, kMethod, kNumResiduals,
- N0, N1, N2, N3, N4, N5, N6, N7, N8, N9,
- kParameterBlock, 0> {
- // Mutates parameters but must restore them before return.
- static bool EvaluateJacobianForParameterBlock(
- const CostFunctor* functor,
- const double* residuals_at_eval_point,
- const NumericDiffOptions& options,
- const int num_residuals,
- const int parameter_block_index,
- const int parameter_block_size,
- double **parameters,
- double *jacobian) {
- // Silence unused parameter compiler warnings.
- (void)functor;
- (void)residuals_at_eval_point;
- (void)options;
- (void)num_residuals;
- (void)parameter_block_index;
- (void)parameter_block_size;
- (void)parameters;
- (void)jacobian;
- LOG(FATAL) << "Control should never reach here.";
+// This function calls NumericDiff<...>::EvaluateJacobianForParameterBlock for
+// each parameter block.
+//
+// Example:
+// A call to
+// EvaluateJacobianForParameterBlocks<StaticParameterDims<2, 3>>(
+// functor,
+// residuals_at_eval_point,
+// options,
+// num_residuals,
+// parameters,
+// jacobians);
+// will result in the following calls to
+// NumericDiff<...>::EvaluateJacobianForParameterBlock:
+//
+// if (jacobians[0] != nullptr) {
+// if (!NumericDiff<
+// CostFunctor,
+// method,
+// kNumResiduals,
+// StaticParameterDims<2, 3>,
+// 0,
+// 2>::EvaluateJacobianForParameterBlock(functor,
+// residuals_at_eval_point,
+// options,
+// num_residuals,
+// 0,
+// 2,
+// parameters,
+// jacobians[0])) {
+// return false;
+// }
+// }
+// if (jacobians[1] != nullptr) {
+// if (!NumericDiff<
+// CostFunctor,
+// method,
+// kNumResiduals,
+// StaticParameterDims<2, 3>,
+// 1,
+// 3>::EvaluateJacobianForParameterBlock(functor,
+// residuals_at_eval_point,
+// options,
+// num_residuals,
+// 1,
+// 3,
+// parameters,
+// jacobians[1])) {
+// return false;
+// }
+// }
+template <typename ParameterDims,
+ typename Parameters = typename ParameterDims::Parameters,
+ int ParameterIdx = 0>
+struct EvaluateJacobianForParameterBlocks;
+
+template <typename ParameterDims, int N, int... Ns, int ParameterIdx>
+struct EvaluateJacobianForParameterBlocks<ParameterDims,
+ std::integer_sequence<int, N, Ns...>,
+ ParameterIdx> {
+ template <NumericDiffMethodType method,
+ int kNumResiduals,
+ typename CostFunctor>
+ static bool Apply(const CostFunctor* functor,
+ const double* residuals_at_eval_point,
+ const NumericDiffOptions& options,
+ int num_residuals,
+ double** parameters,
+ double** jacobians) {
+ if (jacobians[ParameterIdx] != nullptr) {
+ if (!NumericDiff<
+ CostFunctor,
+ method,
+ kNumResiduals,
+ ParameterDims,
+ ParameterIdx,
+ N>::EvaluateJacobianForParameterBlock(functor,
+ residuals_at_eval_point,
+ options,
+ num_residuals,
+ ParameterIdx,
+ N,
+ parameters,
+ jacobians[ParameterIdx])) {
+ return false;
+ }
+ }
+
+ return EvaluateJacobianForParameterBlocks<ParameterDims,
+ std::integer_sequence<int, Ns...>,
+ ParameterIdx + 1>::
+ template Apply<method, kNumResiduals>(functor,
+ residuals_at_eval_point,
+ options,
+ num_residuals,
+ parameters,
+ jacobians);
+ }
+};
+
+// End of 'recursion'. Nothing more to do.
+template <typename ParameterDims, int ParameterIdx>
+struct EvaluateJacobianForParameterBlocks<ParameterDims, std::integer_sequence<int>,
+ ParameterIdx> {
+ template <NumericDiffMethodType method, int kNumResiduals,
+ typename CostFunctor>
+ static bool Apply(const CostFunctor* /* NOT USED*/,
+ const double* /* NOT USED*/,
+ const NumericDiffOptions& /* NOT USED*/, int /* NOT USED*/,
+ double** /* NOT USED*/, double** /* NOT USED*/) {
return true;
}
};
diff --git a/extern/ceres/include/ceres/internal/parameter_dims.h b/extern/ceres/include/ceres/internal/parameter_dims.h
new file mode 100644
index 00000000000..24021061416
--- /dev/null
+++ b/extern/ceres/include/ceres/internal/parameter_dims.h
@@ -0,0 +1,124 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2018 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_INTERNAL_PARAMETER_DIMS_H_
+#define CERES_PUBLIC_INTERNAL_PARAMETER_DIMS_H_
+
+#include <array>
+#include <utility>
+
+#include "ceres/internal/integer_sequence_algorithm.h"
+
+namespace ceres {
+namespace internal {
+
+// Checks, whether the given parameter block sizes are valid. Valid means every
+// dimension is bigger than zero.
+constexpr bool IsValidParameterDimensionSequence(std::integer_sequence<int>) {
+ return true;
+}
+
+template <int N, int... Ts>
+constexpr bool IsValidParameterDimensionSequence(
+ std::integer_sequence<int, N, Ts...>) {
+ return (N <= 0) ? false
+ : IsValidParameterDimensionSequence(
+ std::integer_sequence<int, Ts...>());
+}
+
+// Helper class that represents the parameter dimensions. The parameter
+// dimensions are either dynamic or the sizes are known at compile time. It is
+// used to pass parameter block dimensions around (e.g. between functions or
+// classes).
+//
+// As an example if one have three parameter blocks with dimensions (2, 4, 1),
+// one would use 'StaticParameterDims<2, 4, 1>' which is a synonym for
+// 'ParameterDims<false, 2, 4, 1>'.
+// For dynamic parameter dims, one would just use 'DynamicParameterDims', which
+// is a synonym for 'ParameterDims<true>'.
+template <bool IsDynamic, int... Ns>
+class ParameterDims {
+ public:
+ using Parameters = std::integer_sequence<int, Ns...>;
+
+ // The parameter dimensions are only valid if all parameter block dimensions
+ // are greater than zero.
+ static constexpr bool kIsValid =
+ IsValidParameterDimensionSequence(Parameters());
+ static_assert(kIsValid,
+ "Invalid parameter block dimension detected. Each parameter "
+ "block dimension must be bigger than zero.");
+
+ static constexpr bool kIsDynamic = IsDynamic;
+ static constexpr int kNumParameterBlocks = sizeof...(Ns);
+ static_assert(kIsDynamic || kNumParameterBlocks > 0,
+ "At least one parameter block must be specified.");
+
+ static constexpr int kNumParameters =
+ Sum<std::integer_sequence<int, Ns...>>::Value;
+
+ static constexpr int GetDim(int dim) { return params_[dim]; }
+
+ // If one has all parameters packed into a single array this function unpacks
+ // the parameters.
+ template <typename T>
+ static inline std::array<T*, kNumParameterBlocks> GetUnpackedParameters(
+ T* ptr) {
+ using Offsets = ExclusiveScan<Parameters>;
+ return GetUnpackedParameters(ptr, Offsets());
+ }
+
+ private:
+ template <typename T, int... Indices>
+ static inline std::array<T*, kNumParameterBlocks> GetUnpackedParameters(
+ T* ptr, std::integer_sequence<int, Indices...>) {
+ return std::array<T*, kNumParameterBlocks>{{ptr + Indices...}};
+ }
+
+ static constexpr std::array<int, kNumParameterBlocks> params_{Ns...};
+};
+
+// Even static constexpr member variables needs to be defined (not only
+// declared). As the ParameterDims class is tempalted this definition must
+// be in the header file.
+template <bool IsDynamic, int... Ns>
+constexpr std::array<int, ParameterDims<IsDynamic, Ns...>::kNumParameterBlocks>
+ ParameterDims<IsDynamic, Ns...>::params_;
+
+// Using declarations for static and dynamic parameter dims. This makes client
+// code easier to read.
+template <int... Ns>
+using StaticParameterDims = ParameterDims<false, Ns...>;
+using DynamicParameterDims = ParameterDims<true>;
+
+} // namespace internal
+} // namespace ceres
+
+#endif // CERES_PUBLIC_INTERNAL_PARAMETER_DIMS_H_
diff --git a/extern/ceres/include/ceres/internal/port.h b/extern/ceres/include/ceres/internal/port.h
index f4dcaee7bd8..958b0d15cb7 100644
--- a/extern/ceres/include/ceres/internal/port.h
+++ b/extern/ceres/include/ceres/internal/port.h
@@ -32,45 +32,41 @@
#define CERES_PUBLIC_INTERNAL_PORT_H_
// This file needs to compile as c code.
-#ifdef __cplusplus
-#include <cstddef>
#include "ceres/internal/config.h"
-#if defined(CERES_TR1_MEMORY_HEADER)
-#include <tr1/memory>
-#else
-#include <memory>
-#endif
-namespace ceres {
-
-#if defined(CERES_TR1_SHARED_PTR)
-using std::tr1::shared_ptr;
+#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
-using std::shared_ptr;
+# error One of CERES_USE_OPENMP, CERES_USE_CXX_THREADS or CERES_NO_THREADS must be defined.
#endif
-// We allocate some Eigen objects on the stack and other places they
-// might not be aligned to 16-byte boundaries. If we have C++11, we
-// can specify their alignment anyway, and thus can safely enable
-// vectorization on those matrices; in C++99, we are out of luck. Figure out
-// what case we're in and write macros that do the right thing.
-#ifdef CERES_USE_CXX11
-namespace port_constants {
-static constexpr size_t kMaxAlignBytes =
- // Work around a GCC 4.8 bug
- // (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56019) where
- // std::max_align_t is misplaced.
-#if defined (__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 8
- alignof(::max_align_t);
-#else
- alignof(std::max_align_t);
+// 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
-} // namespace port_constants
-#endif
-
-} // namespace ceres
-
-#endif // __cplusplus
// A macro to signal which functions and classes are exported when
// building a DLL with MSVC.
diff --git a/extern/ceres/include/ceres/internal/scoped_ptr.h b/extern/ceres/include/ceres/internal/scoped_ptr.h
deleted file mode 100644
index fa0ac25a031..00000000000
--- a/extern/ceres/include/ceres/internal/scoped_ptr.h
+++ /dev/null
@@ -1,310 +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: jorg@google.com (Jorg Brown)
-//
-// This is an implementation designed to match the anticipated future TR2
-// implementation of the scoped_ptr class, and its closely-related brethren,
-// scoped_array, scoped_ptr_malloc, and make_scoped_ptr.
-
-#ifndef CERES_PUBLIC_INTERNAL_SCOPED_PTR_H_
-#define CERES_PUBLIC_INTERNAL_SCOPED_PTR_H_
-
-#include <assert.h>
-#include <stdlib.h>
-#include <cstddef>
-#include <algorithm>
-
-namespace ceres {
-namespace internal {
-
-template <class C> class scoped_ptr;
-template <class C, class Free> class scoped_ptr_malloc;
-template <class C> class scoped_array;
-
-template <class C>
-scoped_ptr<C> make_scoped_ptr(C *);
-
-// A scoped_ptr<T> is like a T*, except that the destructor of
-// scoped_ptr<T> automatically deletes the pointer it holds (if
-// any). That is, scoped_ptr<T> owns the T object that it points
-// to. Like a T*, a scoped_ptr<T> may hold either NULL or a pointer to
-// a T object. Also like T*, scoped_ptr<T> is thread-compatible, and
-// once you dereference it, you get the threadsafety guarantees of T.
-//
-// The size of a scoped_ptr is small: sizeof(scoped_ptr<C>) == sizeof(C*)
-template <class C>
-class scoped_ptr {
- public:
- // The element type
- typedef C element_type;
-
- // Constructor. Defaults to intializing with NULL.
- // There is no way to create an uninitialized scoped_ptr.
- // The input parameter must be allocated with new.
- explicit scoped_ptr(C* p = NULL) : ptr_(p) { }
-
- // Destructor. If there is a C object, delete it.
- // We don't need to test ptr_ == NULL because C++ does that for us.
- ~scoped_ptr() {
- enum { type_must_be_complete = sizeof(C) };
- delete ptr_;
- }
-
- // Reset. Deletes the current owned object, if any.
- // Then takes ownership of a new object, if given.
- // this->reset(this->get()) works.
- void reset(C* p = NULL) {
- if (p != ptr_) {
- enum { type_must_be_complete = sizeof(C) };
- delete ptr_;
- ptr_ = p;
- }
- }
-
- // Accessors to get the owned object.
- // operator* and operator-> will assert() if there is no current object.
- C& operator*() const {
- assert(ptr_ != NULL);
- return *ptr_;
- }
- C* operator->() const {
- assert(ptr_ != NULL);
- return ptr_;
- }
- C* get() const { return ptr_; }
-
- // Comparison operators.
- // These return whether a scoped_ptr and a raw pointer refer to
- // the same object, not just to two different but equal objects.
- bool operator==(const C* p) const { return ptr_ == p; }
- bool operator!=(const C* p) const { return ptr_ != p; }
-
- // Swap two scoped pointers.
- void swap(scoped_ptr& p2) {
- C* tmp = ptr_;
- ptr_ = p2.ptr_;
- p2.ptr_ = tmp;
- }
-
- // Release a pointer.
- // The return value is the current pointer held by this object.
- // If this object holds a NULL pointer, the return value is NULL.
- // After this operation, this object will hold a NULL pointer,
- // and will not own the object any more.
- C* release() {
- C* retVal = ptr_;
- ptr_ = NULL;
- return retVal;
- }
-
- private:
- C* ptr_;
-
- // google3 friend class that can access copy ctor (although if it actually
- // calls a copy ctor, there will be a problem) see below
- friend scoped_ptr<C> make_scoped_ptr<C>(C *p);
-
- // Forbid comparison of scoped_ptr types. If C2 != C, it totally doesn't
- // make sense, and if C2 == C, it still doesn't make sense because you should
- // never have the same object owned by two different scoped_ptrs.
- template <class C2> bool operator==(scoped_ptr<C2> const& p2) const;
- template <class C2> bool operator!=(scoped_ptr<C2> const& p2) const;
-
- // Disallow evil constructors
- scoped_ptr(const scoped_ptr&);
- void operator=(const scoped_ptr&);
-};
-
-// Free functions
-template <class C>
-inline void swap(scoped_ptr<C>& p1, scoped_ptr<C>& p2) {
- p1.swap(p2);
-}
-
-template <class C>
-inline bool operator==(const C* p1, const scoped_ptr<C>& p2) {
- return p1 == p2.get();
-}
-
-template <class C>
-inline bool operator==(const C* p1, const scoped_ptr<const C>& p2) {
- return p1 == p2.get();
-}
-
-template <class C>
-inline bool operator!=(const C* p1, const scoped_ptr<C>& p2) {
- return p1 != p2.get();
-}
-
-template <class C>
-inline bool operator!=(const C* p1, const scoped_ptr<const C>& p2) {
- return p1 != p2.get();
-}
-
-template <class C>
-scoped_ptr<C> make_scoped_ptr(C *p) {
- // This does nothing but to return a scoped_ptr of the type that the passed
- // pointer is of. (This eliminates the need to specify the name of T when
- // making a scoped_ptr that is used anonymously/temporarily.) From an
- // access control point of view, we construct an unnamed scoped_ptr here
- // which we return and thus copy-construct. Hence, we need to have access
- // to scoped_ptr::scoped_ptr(scoped_ptr const &). However, it is guaranteed
- // that we never actually call the copy constructor, which is a good thing
- // as we would call the temporary's object destructor (and thus delete p)
- // if we actually did copy some object, here.
- return scoped_ptr<C>(p);
-}
-
-// scoped_array<C> is like scoped_ptr<C>, except that the caller must allocate
-// with new [] and the destructor deletes objects with delete [].
-//
-// As with scoped_ptr<C>, a scoped_array<C> either points to an object
-// or is NULL. A scoped_array<C> owns the object that it points to.
-// scoped_array<T> is thread-compatible, and once you index into it,
-// the returned objects have only the threadsafety guarantees of T.
-//
-// Size: sizeof(scoped_array<C>) == sizeof(C*)
-template <class C>
-class scoped_array {
- public:
- // The element type
- typedef C element_type;
-
- // Constructor. Defaults to intializing with NULL.
- // There is no way to create an uninitialized scoped_array.
- // The input parameter must be allocated with new [].
- explicit scoped_array(C* p = NULL) : array_(p) { }
-
- // Destructor. If there is a C object, delete it.
- // We don't need to test ptr_ == NULL because C++ does that for us.
- ~scoped_array() {
- enum { type_must_be_complete = sizeof(C) };
- delete[] array_;
- }
-
- // Reset. Deletes the current owned object, if any.
- // Then takes ownership of a new object, if given.
- // this->reset(this->get()) works.
- void reset(C* p = NULL) {
- if (p != array_) {
- enum { type_must_be_complete = sizeof(C) };
- delete[] array_;
- array_ = p;
- }
- }
-
- // Get one element of the current object.
- // Will assert() if there is no current object, or index i is negative.
- C& operator[](std::ptrdiff_t i) const {
- assert(i >= 0);
- assert(array_ != NULL);
- return array_[i];
- }
-
- // Get a pointer to the zeroth element of the current object.
- // If there is no current object, return NULL.
- C* get() const {
- return array_;
- }
-
- // Comparison operators.
- // These return whether a scoped_array and a raw pointer refer to
- // the same array, not just to two different but equal arrays.
- bool operator==(const C* p) const { return array_ == p; }
- bool operator!=(const C* p) const { return array_ != p; }
-
- // Swap two scoped arrays.
- void swap(scoped_array& p2) {
- C* tmp = array_;
- array_ = p2.array_;
- p2.array_ = tmp;
- }
-
- // Release an array.
- // The return value is the current pointer held by this object.
- // If this object holds a NULL pointer, the return value is NULL.
- // After this operation, this object will hold a NULL pointer,
- // and will not own the object any more.
- C* release() {
- C* retVal = array_;
- array_ = NULL;
- return retVal;
- }
-
- private:
- C* array_;
-
- // Forbid comparison of different scoped_array types.
- template <class C2> bool operator==(scoped_array<C2> const& p2) const;
- template <class C2> bool operator!=(scoped_array<C2> const& p2) const;
-
- // Disallow evil constructors
- scoped_array(const scoped_array&);
- void operator=(const scoped_array&);
-};
-
-// Free functions
-template <class C>
-inline void swap(scoped_array<C>& p1, scoped_array<C>& p2) {
- p1.swap(p2);
-}
-
-template <class C>
-inline bool operator==(const C* p1, const scoped_array<C>& p2) {
- return p1 == p2.get();
-}
-
-template <class C>
-inline bool operator==(const C* p1, const scoped_array<const C>& p2) {
- return p1 == p2.get();
-}
-
-template <class C>
-inline bool operator!=(const C* p1, const scoped_array<C>& p2) {
- return p1 != p2.get();
-}
-
-template <class C>
-inline bool operator!=(const C* p1, const scoped_array<const C>& p2) {
- return p1 != p2.get();
-}
-
-// This class wraps the c library function free() in a class that can be
-// passed as a template argument to scoped_ptr_malloc below.
-class ScopedPtrMallocFree {
- public:
- inline void operator()(void* x) const {
- free(x);
- }
-};
-
-} // namespace internal
-} // namespace ceres
-
-#endif // CERES_PUBLIC_INTERNAL_SCOPED_PTR_H_
diff --git a/extern/ceres/include/ceres/internal/variadic_evaluate.h b/extern/ceres/include/ceres/internal/variadic_evaluate.h
index b3515b96d18..046832c0bb4 100644
--- a/extern/ceres/include/ceres/internal/variadic_evaluate.h
+++ b/extern/ceres/include/ceres/internal/variadic_evaluate.h
@@ -28,165 +28,77 @@
//
// Author: sameeragarwal@google.com (Sameer Agarwal)
// mierle@gmail.com (Keir Mierle)
+// jodebo_beck@gmx.de (Johannes Beck)
#ifndef CERES_PUBLIC_INTERNAL_VARIADIC_EVALUATE_H_
#define CERES_PUBLIC_INTERNAL_VARIADIC_EVALUATE_H_
#include <stddef.h>
-#include "ceres/jet.h"
-#include "ceres/types.h"
-#include "ceres/internal/eigen.h"
-#include "ceres/internal/fixed_array.h"
-#include "glog/logging.h"
+#include <type_traits>
+#include <utility>
+
+#include "ceres/cost_function.h"
+#include "ceres/internal/parameter_dims.h"
namespace ceres {
namespace internal {
-// This block of quasi-repeated code calls the user-supplied functor, which may
-// take a variable number of arguments. This is accomplished by specializing the
-// struct based on the size of the trailing parameters; parameters with 0 size
-// are assumed missing.
-template<typename Functor, typename T, int N0, int N1, int N2, int N3, int N4,
- int N5, int N6, int N7, int N8, int N9>
-struct VariadicEvaluate {
- static bool Call(const Functor& functor, T const *const *input, T* output) {
- return functor(input[0],
- input[1],
- input[2],
- input[3],
- input[4],
- input[5],
- input[6],
- input[7],
- input[8],
- input[9],
- output);
- }
-};
-
-template<typename Functor, typename T, int N0, int N1, int N2, int N3, int N4,
- int N5, int N6, int N7, int N8>
-struct VariadicEvaluate<Functor, T, N0, N1, N2, N3, N4, N5, N6, N7, N8, 0> {
- static bool Call(const Functor& functor, T const *const *input, T* output) {
- return functor(input[0],
- input[1],
- input[2],
- input[3],
- input[4],
- input[5],
- input[6],
- input[7],
- input[8],
- output);
- }
-};
-
-template<typename Functor, typename T, int N0, int N1, int N2, int N3, int N4,
- int N5, int N6, int N7>
-struct VariadicEvaluate<Functor, T, N0, N1, N2, N3, N4, N5, N6, N7, 0, 0> {
- static bool Call(const Functor& functor, T const *const *input, T* output) {
- return functor(input[0],
- input[1],
- input[2],
- input[3],
- input[4],
- input[5],
- input[6],
- input[7],
- output);
- }
-};
-
-template<typename Functor, typename T, int N0, int N1, int N2, int N3, int N4,
- int N5, int N6>
-struct VariadicEvaluate<Functor, T, N0, N1, N2, N3, N4, N5, N6, 0, 0, 0> {
- static bool Call(const Functor& functor, T const *const *input, T* output) {
- return functor(input[0],
- input[1],
- input[2],
- input[3],
- input[4],
- input[5],
- input[6],
- output);
- }
-};
-
-template<typename Functor, typename T, int N0, int N1, int N2, int N3, int N4,
- int N5>
-struct VariadicEvaluate<Functor, T, N0, N1, N2, N3, N4, N5, 0, 0, 0, 0> {
- static bool Call(const Functor& functor, T const *const *input, T* output) {
- return functor(input[0],
- input[1],
- input[2],
- input[3],
- input[4],
- input[5],
- output);
- }
-};
-
-template<typename Functor, typename T, int N0, int N1, int N2, int N3, int N4>
-struct VariadicEvaluate<Functor, T, N0, N1, N2, N3, N4, 0, 0, 0, 0, 0> {
- static bool Call(const Functor& functor, T const *const *input, T* output) {
- return functor(input[0],
- input[1],
- input[2],
- input[3],
- input[4],
- output);
- }
-};
-
-template<typename Functor, typename T, int N0, int N1, int N2, int N3>
-struct VariadicEvaluate<Functor, T, N0, N1, N2, N3, 0, 0, 0, 0, 0, 0> {
- static bool Call(const Functor& functor, T const *const *input, T* output) {
- return functor(input[0],
- input[1],
- input[2],
- input[3],
- output);
- }
-};
-
-template<typename Functor, typename T, int N0, int N1, int N2>
-struct VariadicEvaluate<Functor, T, N0, N1, N2, 0, 0, 0, 0, 0, 0, 0> {
- static bool Call(const Functor& functor, T const *const *input, T* output) {
- return functor(input[0],
- input[1],
- input[2],
- output);
- }
-};
-
-template<typename Functor, typename T, int N0, int N1>
-struct VariadicEvaluate<Functor, T, N0, N1, 0, 0, 0, 0, 0, 0, 0, 0> {
- static bool Call(const Functor& functor, T const *const *input, T* output) {
- return functor(input[0],
- input[1],
- output);
- }
-};
-
-template<typename Functor, typename T, int N0>
-struct VariadicEvaluate<Functor, T, N0, 0, 0, 0, 0, 0, 0, 0, 0, 0> {
- static bool Call(const Functor& functor, T const *const *input, T* output) {
- return functor(input[0],
- output);
- }
-};
-
-// Template instantiation for dynamically-sized functors.
-template<typename Functor, typename T>
-struct VariadicEvaluate<Functor, T, ceres::DYNAMIC, ceres::DYNAMIC,
- ceres::DYNAMIC, ceres::DYNAMIC, ceres::DYNAMIC,
- ceres::DYNAMIC, ceres::DYNAMIC, ceres::DYNAMIC,
- ceres::DYNAMIC, ceres::DYNAMIC> {
- static bool Call(const Functor& functor, T const *const *input, T* output) {
- return functor(input, output);
- }
-};
+// For fixed size cost functors
+template <typename Functor, typename T, int... Indices>
+inline bool VariadicEvaluateImpl(const Functor& functor, T const* const* input,
+ T* output, std::false_type /*is_dynamic*/,
+ std::integer_sequence<int, Indices...>) {
+ static_assert(sizeof...(Indices),
+ "Invalid number of parameter blocks. At least one parameter "
+ "block must be specified.");
+ return functor(input[Indices]..., output);
+}
+
+// For dynamic sized cost functors
+template <typename Functor, typename T>
+inline bool VariadicEvaluateImpl(const Functor& functor, T const* const* input,
+ T* output, std::true_type /*is_dynamic*/,
+ std::integer_sequence<int>) {
+ return functor(input, output);
+}
+
+// For ceres cost functors (not ceres::CostFunction)
+template <typename ParameterDims, typename Functor, typename T>
+inline bool VariadicEvaluateImpl(const Functor& functor, T const* const* input,
+ T* output, const void* /* NOT USED */) {
+ using ParameterBlockIndices =
+ std::make_integer_sequence<int, ParameterDims::kNumParameterBlocks>;
+ using IsDynamic = std::integral_constant<bool, ParameterDims::kIsDynamic>;
+ return VariadicEvaluateImpl(functor, input, output, IsDynamic(),
+ ParameterBlockIndices());
+}
+
+// For ceres::CostFunction
+template <typename ParameterDims, typename Functor, typename T>
+inline bool VariadicEvaluateImpl(const Functor& functor, T const* const* input,
+ T* output,
+ const CostFunction* /* NOT USED */) {
+ return functor.Evaluate(input, output, nullptr);
+}
+
+// Variadic evaluate is a helper function to evaluate ceres cost function or
+// functors using an input, output and the parameter dimensions. There are
+// several ways different possibilities:
+// 1) If the passed functor is a 'ceres::CostFunction' its evaluate method is
+// called.
+// 2) If the functor is not a 'ceres::CostFunction' and the specified parameter
+// dims is dynamic, the functor must have the following signature
+// 'bool(T const* const* input, T* output)'.
+// 3) If the functor is not a 'ceres::CostFunction' and the specified parameter
+// dims is not dynamic, the input is expanded by using the number of parameter
+// blocks. The signature of the functor must have the following signature
+// 'bool()(const T* i_1, const T* i_2, ... const T* i_n, T* output)'.
+template <typename ParameterDims, typename Functor, typename T>
+inline bool VariadicEvaluate(const Functor& functor, T const* const* input,
+ T* output) {
+ return VariadicEvaluateImpl<ParameterDims>(functor, input, output, &functor);
+}
} // namespace internal
} // namespace ceres
diff --git a/extern/ceres/include/ceres/iteration_callback.h b/extern/ceres/include/ceres/iteration_callback.h
index db5d0efe53a..0a743ecc26f 100644
--- a/extern/ceres/include/ceres/iteration_callback.h
+++ b/extern/ceres/include/ceres/iteration_callback.h
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2019 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -35,42 +35,22 @@
#ifndef CERES_PUBLIC_ITERATION_CALLBACK_H_
#define CERES_PUBLIC_ITERATION_CALLBACK_H_
-#include "ceres/types.h"
#include "ceres/internal/disable_warnings.h"
+#include "ceres/types.h"
namespace ceres {
// This struct describes the state of the optimizer after each
// iteration of the minimization.
struct CERES_EXPORT IterationSummary {
- IterationSummary()
- : iteration(0),
- step_is_valid(false),
- step_is_nonmonotonic(false),
- step_is_successful(false),
- cost(0.0),
- cost_change(0.0),
- gradient_max_norm(0.0),
- gradient_norm(0.0),
- step_norm(0.0),
- eta(0.0),
- step_size(0.0),
- line_search_function_evaluations(0),
- line_search_gradient_evaluations(0),
- line_search_iterations(0),
- linear_solver_iterations(0),
- iteration_time_in_seconds(0.0),
- step_solver_time_in_seconds(0.0),
- cumulative_time_in_seconds(0.0) {}
-
// Current iteration number.
- int32 iteration;
+ int iteration = 0;
// Step was numerically valid, i.e., all values are finite and the
// step reduces the value of the linearized model.
//
// Note: step_is_valid is always true when iteration = 0.
- bool step_is_valid;
+ bool step_is_valid = false;
// Step did not reduce the value of the objective function
// sufficiently, but it was accepted because of the relaxed
@@ -78,7 +58,7 @@ struct CERES_EXPORT IterationSummary {
// algorithm.
//
// Note: step_is_nonmonotonic is always false when iteration = 0;
- bool step_is_nonmonotonic;
+ bool step_is_nonmonotonic = false;
// Whether or not the minimizer accepted this step or not. If the
// ordinary trust region algorithm is used, this means that the
@@ -90,68 +70,68 @@ struct CERES_EXPORT IterationSummary {
// step and the step is declared successful.
//
// Note: step_is_successful is always true when iteration = 0.
- bool step_is_successful;
+ bool step_is_successful = false;
// Value of the objective function.
- double cost;
+ double cost = 0.90;
// Change in the value of the objective function in this
// iteration. This can be positive or negative.
- double cost_change;
+ double cost_change = 0.0;
// Infinity norm of the gradient vector.
- double gradient_max_norm;
+ double gradient_max_norm = 0.0;
// 2-norm of the gradient vector.
- double gradient_norm;
+ double gradient_norm = 0.0;
// 2-norm of the size of the step computed by the optimization
// algorithm.
- double step_norm;
+ double step_norm = 0.0;
// For trust region algorithms, the ratio of the actual change in
// cost and the change in the cost of the linearized approximation.
- double relative_decrease;
+ double relative_decrease = 0.0;
// Size of the trust region at the end of the current iteration. For
// the Levenberg-Marquardt algorithm, the regularization parameter
// mu = 1.0 / trust_region_radius.
- double trust_region_radius;
+ double trust_region_radius = 0.0;
// For the inexact step Levenberg-Marquardt algorithm, this is the
// relative accuracy with which the Newton(LM) step is solved. This
// number affects only the iterative solvers capable of solving
// linear systems inexactly. Factorization-based exact solvers
// ignore it.
- double eta;
+ double eta = 0.0;
// Step sized computed by the line search algorithm.
- double step_size;
+ double step_size = 0.0;
// Number of function value evaluations used by the line search algorithm.
- int line_search_function_evaluations;
+ int line_search_function_evaluations = 0;
// Number of function gradient evaluations used by the line search algorithm.
- int line_search_gradient_evaluations;
+ int line_search_gradient_evaluations = 0;
// Number of iterations taken by the line search algorithm.
- int line_search_iterations;
+ int line_search_iterations = 0;
// Number of iterations taken by the linear solver to solve for the
// Newton step.
- int linear_solver_iterations;
+ int linear_solver_iterations = 0;
// All times reported below are wall times.
// Time (in seconds) spent inside the minimizer loop in the current
// iteration.
- double iteration_time_in_seconds;
+ double iteration_time_in_seconds = 0.0;
// Time (in seconds) spent inside the trust region step solver.
- double step_solver_time_in_seconds;
+ double step_solver_time_in_seconds = 0.0;
// Time (in seconds) since the user called Solve().
- double cumulative_time_in_seconds;
+ double cumulative_time_in_seconds = 0.0;
};
// Interface for specifying callbacks that are executed at the end of
diff --git a/extern/ceres/include/ceres/jet.h b/extern/ceres/include/ceres/jet.h
index a104707298c..7aafaa01d30 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 2015 Google Inc. All rights reserved.
+// Copyright 2019 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -31,7 +31,7 @@
// A simple implementation of N-dimensional dual numbers, for automatically
// computing exact derivatives of functions.
//
-// While a complete treatment of the mechanics of automatic differentation is
+// While a complete treatment of the mechanics of automatic differentiation is
// beyond the scope of this header (see
// http://en.wikipedia.org/wiki/Automatic_differentiation for details), the
// basic idea is to extend normal arithmetic with an extra element, "e," often
@@ -49,7 +49,7 @@
// f(x) = x^2 ,
//
// evaluated at 10. Using normal arithmetic, f(10) = 100, and df/dx(10) = 20.
-// Next, augument 10 with an infinitesimal to get:
+// Next, argument 10 with an infinitesimal to get:
//
// f(10 + e) = (10 + e)^2
// = 100 + 2 * 10 * e + e^2
@@ -102,8 +102,9 @@
// }
//
// // The "2" means there should be 2 dual number components.
-// Jet<double, 2> x(0); // Pick the 0th dual number for x.
-// Jet<double, 2> y(1); // Pick the 1st dual number for y.
+// // It computes the partial derivative at x=10, y=20.
+// Jet<double, 2> x(10, 0); // Pick the 0th dual number for x.
+// Jet<double, 2> y(20, 1); // Pick the 1st dual number for y.
// Jet<double, 2> z = f(x, y);
//
// LOG(INFO) << "df/dx = " << z.v[0]
@@ -124,7 +125,7 @@
//
// x = a + \sum_i v[i] t_i
//
-// A shorthand is to write an element as x = a + u, where u is the pertubation.
+// A shorthand is to write an element as x = a + u, where u is the perturbation.
// Then, the main point about the arithmetic of jets is that the product of
// perturbations is zero:
//
@@ -163,7 +164,6 @@
#include <string>
#include "Eigen/Core"
-#include "ceres/fpclassify.h"
#include "ceres/internal/port.h"
namespace ceres {
@@ -171,26 +171,25 @@ namespace ceres {
template <typename T, int N>
struct Jet {
enum { DIMENSION = N };
+ typedef T Scalar;
// Default-construct "a" because otherwise this can lead to false errors about
// uninitialized uses when other classes relying on default constructed T
// (where T is a Jet<T, N>). This usually only happens in opt mode. Note that
// the C++ standard mandates that e.g. default constructed doubles are
// initialized to 0.0; see sections 8.5 of the C++03 standard.
- Jet() : a() {
- v.setZero();
- }
+ Jet() : a() { v.setConstant(Scalar()); }
// Constructor from scalar: a + 0.
explicit Jet(const T& value) {
a = value;
- v.setZero();
+ v.setConstant(Scalar());
}
// Constructor from scalar plus variable: a + t_i.
Jet(const T& value, int k) {
a = value;
- v.setZero();
+ v.setConstant(Scalar());
v[k] = T(1.0);
}
@@ -198,58 +197,66 @@ struct Jet {
// The use of Eigen::DenseBase allows Eigen expressions
// to be passed in without being fully evaluated until
// they are assigned to v
- template<typename Derived>
- EIGEN_STRONG_INLINE Jet(const T& a, const Eigen::DenseBase<Derived> &v)
- : a(a), v(v) {
- }
+ template <typename Derived>
+ EIGEN_STRONG_INLINE Jet(const T& a, const Eigen::DenseBase<Derived>& v)
+ : a(a), v(v) {}
// Compound operators
- Jet<T, N>& operator+=(const Jet<T, N> &y) {
+ Jet<T, N>& operator+=(const Jet<T, N>& y) {
*this = *this + y;
return *this;
}
- Jet<T, N>& operator-=(const Jet<T, N> &y) {
+ Jet<T, N>& operator-=(const Jet<T, N>& y) {
*this = *this - y;
return *this;
}
- Jet<T, N>& operator*=(const Jet<T, N> &y) {
+ Jet<T, N>& operator*=(const Jet<T, N>& y) {
*this = *this * y;
return *this;
}
- Jet<T, N>& operator/=(const Jet<T, N> &y) {
+ Jet<T, N>& operator/=(const Jet<T, N>& y) {
*this = *this / y;
return *this;
}
+ // Compound with scalar operators.
+ Jet<T, N>& operator+=(const T& s) {
+ *this = *this + s;
+ return *this;
+ }
+
+ Jet<T, N>& operator-=(const T& s) {
+ *this = *this - s;
+ return *this;
+ }
+
+ Jet<T, N>& operator*=(const T& s) {
+ *this = *this * s;
+ return *this;
+ }
+
+ Jet<T, N>& operator/=(const T& s) {
+ *this = *this / s;
+ return *this;
+ }
+
// The scalar part.
T a;
// The infinitesimal part.
+ Eigen::Matrix<T, N, 1> v;
- // We allocate Jets on the stack and other places they
- // might not be aligned to 16-byte boundaries. If we have C++11, we
- // can specify their alignment anyway, and thus can safely enable
- // vectorization on those matrices; in C++99, we are out of luck. Figure out
- // what case we're in and do the right thing.
-#ifndef CERES_USE_CXX11
- // fall back to safe version:
- Eigen::Matrix<T, N, 1, Eigen::DontAlign> v;
-#else
- static constexpr bool kShouldAlignMatrix =
- 16 <= ::ceres::port_constants::kMaxAlignBytes;
- static constexpr int kAlignHint = kShouldAlignMatrix ?
- Eigen::AutoAlign : Eigen::DontAlign;
- static constexpr size_t kAlignment = kShouldAlignMatrix ? 16 : 1;
- alignas(kAlignment) Eigen::Matrix<T, N, 1, kAlignHint> v;
-#endif
+ // This struct needs to have an Eigen aligned operator new as it contains
+ // fixed-size Eigen types.
+ EIGEN_MAKE_ALIGNED_OPERATOR_NEW
};
// Unary +
-template<typename T, int N> inline
-Jet<T, N> const& operator+(const Jet<T, N>& f) {
+template <typename T, int N>
+inline Jet<T, N> const& operator+(const Jet<T, N>& f) {
return f;
}
@@ -257,72 +264,68 @@ Jet<T, N> const& operator+(const Jet<T, N>& f) {
// see if it causes a performance increase.
// Unary -
-template<typename T, int N> inline
-Jet<T, N> operator-(const Jet<T, N>&f) {
+template <typename T, int N>
+inline Jet<T, N> operator-(const Jet<T, N>& f) {
return Jet<T, N>(-f.a, -f.v);
}
// Binary +
-template<typename T, int N> inline
-Jet<T, N> operator+(const Jet<T, N>& f,
- const Jet<T, N>& g) {
+template <typename T, int N>
+inline Jet<T, N> operator+(const Jet<T, N>& f, const Jet<T, N>& g) {
return Jet<T, N>(f.a + g.a, f.v + g.v);
}
// Binary + with a scalar: x + s
-template<typename T, int N> inline
-Jet<T, N> operator+(const Jet<T, N>& f, T s) {
+template <typename T, int N>
+inline Jet<T, N> operator+(const Jet<T, N>& f, T s) {
return Jet<T, N>(f.a + s, f.v);
}
// Binary + with a scalar: s + x
-template<typename T, int N> inline
-Jet<T, N> operator+(T s, const Jet<T, N>& f) {
+template <typename T, int N>
+inline Jet<T, N> operator+(T s, const Jet<T, N>& f) {
return Jet<T, N>(f.a + s, f.v);
}
// Binary -
-template<typename T, int N> inline
-Jet<T, N> operator-(const Jet<T, N>& f,
- const Jet<T, N>& g) {
+template <typename T, int N>
+inline Jet<T, N> operator-(const Jet<T, N>& f, const Jet<T, N>& g) {
return Jet<T, N>(f.a - g.a, f.v - g.v);
}
// Binary - with a scalar: x - s
-template<typename T, int N> inline
-Jet<T, N> operator-(const Jet<T, N>& f, T s) {
+template <typename T, int N>
+inline Jet<T, N> operator-(const Jet<T, N>& f, T s) {
return Jet<T, N>(f.a - s, f.v);
}
// Binary - with a scalar: s - x
-template<typename T, int N> inline
-Jet<T, N> operator-(T s, const Jet<T, N>& f) {
+template <typename T, int N>
+inline Jet<T, N> operator-(T s, const Jet<T, N>& f) {
return Jet<T, N>(s - f.a, -f.v);
}
// Binary *
-template<typename T, int N> inline
-Jet<T, N> operator*(const Jet<T, N>& f,
- const Jet<T, N>& g) {
+template <typename T, int N>
+inline Jet<T, N> operator*(const Jet<T, N>& f, const Jet<T, N>& g) {
return Jet<T, N>(f.a * g.a, f.a * g.v + f.v * g.a);
}
// Binary * with a scalar: x * s
-template<typename T, int N> inline
-Jet<T, N> operator*(const Jet<T, N>& f, T s) {
+template <typename T, int N>
+inline Jet<T, N> operator*(const Jet<T, N>& f, T s) {
return Jet<T, N>(f.a * s, f.v * s);
}
// Binary * with a scalar: s * x
-template<typename T, int N> inline
-Jet<T, N> operator*(T s, const Jet<T, N>& f) {
+template <typename T, int N>
+inline Jet<T, N> operator*(T s, const Jet<T, N>& f) {
return Jet<T, N>(f.a * s, f.v * s);
}
// Binary /
-template<typename T, int N> inline
-Jet<T, N> operator/(const Jet<T, N>& f,
- const Jet<T, N>& g) {
+template <typename T, int N>
+inline Jet<T, N> operator/(const Jet<T, N>& f, const Jet<T, N>& g) {
// This uses:
//
// a + u (a + u)(b - v) (a + u)(b - v)
@@ -332,43 +335,43 @@ Jet<T, N> operator/(const Jet<T, N>& f,
// which holds because v*v = 0.
const T g_a_inverse = T(1.0) / g.a;
const T f_a_by_g_a = f.a * g_a_inverse;
- return Jet<T, N>(f.a * g_a_inverse, (f.v - f_a_by_g_a * g.v) * g_a_inverse);
+ return Jet<T, N>(f_a_by_g_a, (f.v - f_a_by_g_a * g.v) * g_a_inverse);
}
// Binary / with a scalar: s / x
-template<typename T, int N> inline
-Jet<T, N> operator/(T s, const Jet<T, N>& g) {
+template <typename T, int N>
+inline Jet<T, N> operator/(T s, const Jet<T, N>& g) {
const T minus_s_g_a_inverse2 = -s / (g.a * g.a);
return Jet<T, N>(s / g.a, g.v * minus_s_g_a_inverse2);
}
// Binary / with a scalar: x / s
-template<typename T, int N> inline
-Jet<T, N> operator/(const Jet<T, N>& f, T s) {
- const T s_inverse = 1.0 / s;
+template <typename T, int N>
+inline Jet<T, N> operator/(const Jet<T, N>& f, T s) {
+ const T s_inverse = T(1.0) / 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; \
-}
-CERES_DEFINE_JET_COMPARISON_OPERATOR( < ) // NOLINT
-CERES_DEFINE_JET_COMPARISON_OPERATOR( <= ) // NOLINT
-CERES_DEFINE_JET_COMPARISON_OPERATOR( > ) // NOLINT
-CERES_DEFINE_JET_COMPARISON_OPERATOR( >= ) // NOLINT
-CERES_DEFINE_JET_COMPARISON_OPERATOR( == ) // NOLINT
-CERES_DEFINE_JET_COMPARISON_OPERATOR( != ) // NOLINT
+#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; \
+ }
+CERES_DEFINE_JET_COMPARISON_OPERATOR(<) // NOLINT
+CERES_DEFINE_JET_COMPARISON_OPERATOR(<=) // NOLINT
+CERES_DEFINE_JET_COMPARISON_OPERATOR(>) // NOLINT
+CERES_DEFINE_JET_COMPARISON_OPERATOR(>=) // NOLINT
+CERES_DEFINE_JET_COMPARISON_OPERATOR(==) // NOLINT
+CERES_DEFINE_JET_COMPARISON_OPERATOR(!=) // NOLINT
#undef CERES_DEFINE_JET_COMPARISON_OPERATOR
// Pull some functions from namespace std.
@@ -376,112 +379,128 @@ CERES_DEFINE_JET_COMPARISON_OPERATOR( != ) // NOLINT
// This is necessary because we want to use the same name (e.g. 'sqrt') for
// double-valued and Jet-valued functions, but we are not allowed to put
// Jet-valued functions inside namespace std.
-//
-// TODO(keir): Switch to "using".
-inline double abs (double x) { return std::abs(x); }
-inline double log (double x) { return std::log(x); }
-inline double exp (double x) { return std::exp(x); }
-inline double sqrt (double x) { return std::sqrt(x); }
-inline double cos (double x) { return std::cos(x); }
-inline double acos (double x) { return std::acos(x); }
-inline double sin (double x) { return std::sin(x); }
-inline double asin (double x) { return std::asin(x); }
-inline double tan (double x) { return std::tan(x); }
-inline double atan (double x) { return std::atan(x); }
-inline double sinh (double x) { return std::sinh(x); }
-inline double cosh (double x) { return std::cosh(x); }
-inline double tanh (double x) { return std::tanh(x); }
-inline double floor (double x) { return std::floor(x); }
-inline double ceil (double x) { return std::ceil(x); }
-inline double pow (double x, double y) { return std::pow(x, y); }
-inline double atan2(double y, double x) { return std::atan2(y, x); }
+using std::abs;
+using std::acos;
+using std::asin;
+using std::atan;
+using std::atan2;
+using std::cbrt;
+using std::ceil;
+using std::cos;
+using std::cosh;
+using std::exp;
+using std::exp2;
+using std::floor;
+using std::fmax;
+using std::fmin;
+using std::hypot;
+using std::isfinite;
+using std::isinf;
+using std::isnan;
+using std::isnormal;
+using std::log;
+using std::log2;
+using std::pow;
+using std::sin;
+using std::sinh;
+using std::sqrt;
+using std::tan;
+using std::tanh;
+
+// Legacy names from pre-C++11 days.
+// clang-format off
+inline bool IsFinite(double x) { return std::isfinite(x); }
+inline bool IsInfinite(double x) { return std::isinf(x); }
+inline bool IsNaN(double x) { return std::isnan(x); }
+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)
-template <typename T, int N> inline
-Jet<T, N> abs(const Jet<T, N>& f) {
- return f.a < T(0.0) ? -f : f;
+template <typename T, int N>
+inline Jet<T, N> abs(const Jet<T, N>& f) {
+ return (f.a < T(0.0) ? -f : f);
}
// log(a + h) ~= log(a) + h / a
-template <typename T, int N> inline
-Jet<T, N> log(const Jet<T, N>& f) {
+template <typename T, int N>
+inline Jet<T, N> log(const Jet<T, N>& f) {
const T a_inverse = T(1.0) / f.a;
return Jet<T, N>(log(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) {
+template <typename T, int N>
+inline Jet<T, N> exp(const Jet<T, N>& f) {
const T tmp = exp(f.a);
return Jet<T, N>(tmp, tmp * 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) {
+template <typename T, int N>
+inline Jet<T, N> sqrt(const Jet<T, N>& f) {
const T tmp = sqrt(f.a);
const T two_a_inverse = T(1.0) / (T(2.0) * tmp);
return Jet<T, N>(tmp, f.v * two_a_inverse);
}
// cos(a + h) ~= cos(a) - sin(a) h
-template <typename T, int N> inline
-Jet<T, N> cos(const Jet<T, N>& f) {
- return Jet<T, N>(cos(f.a), - sin(f.a) * f.v);
+template <typename T, int N>
+inline Jet<T, N> cos(const Jet<T, N>& f) {
+ return Jet<T, N>(cos(f.a), -sin(f.a) * f.v);
}
// acos(a + h) ~= acos(a) - 1 / sqrt(1 - a^2) h
-template <typename T, int N> inline
-Jet<T, N> acos(const Jet<T, N>& f) {
- const T tmp = - T(1.0) / sqrt(T(1.0) - f.a * f.a);
+template <typename T, int N>
+inline Jet<T, N> acos(const Jet<T, N>& f) {
+ const T tmp = -T(1.0) / sqrt(T(1.0) - f.a * f.a);
return Jet<T, N>(acos(f.a), tmp * f.v);
}
// sin(a + h) ~= sin(a) + cos(a) h
-template <typename T, int N> inline
-Jet<T, N> sin(const Jet<T, N>& f) {
+template <typename T, int N>
+inline Jet<T, N> sin(const Jet<T, N>& f) {
return Jet<T, N>(sin(f.a), cos(f.a) * f.v);
}
// asin(a + h) ~= asin(a) + 1 / sqrt(1 - a^2) h
-template <typename T, int N> inline
-Jet<T, N> asin(const Jet<T, N>& f) {
+template <typename T, int N>
+inline Jet<T, N> asin(const Jet<T, N>& f) {
const T tmp = T(1.0) / sqrt(T(1.0) - f.a * f.a);
return Jet<T, N>(asin(f.a), tmp * f.v);
}
// tan(a + h) ~= tan(a) + (1 + tan(a)^2) h
-template <typename T, int N> inline
-Jet<T, N> tan(const Jet<T, N>& f) {
+template <typename T, int N>
+inline Jet<T, N> tan(const Jet<T, N>& f) {
const T tan_a = tan(f.a);
const T tmp = T(1.0) + tan_a * tan_a;
return Jet<T, N>(tan_a, tmp * f.v);
}
// atan(a + h) ~= atan(a) + 1 / (1 + a^2) h
-template <typename T, int N> inline
-Jet<T, N> atan(const Jet<T, N>& f) {
+template <typename T, int N>
+inline Jet<T, N> atan(const Jet<T, N>& f) {
const T tmp = T(1.0) / (T(1.0) + f.a * f.a);
return Jet<T, N>(atan(f.a), tmp * f.v);
}
// sinh(a + h) ~= sinh(a) + cosh(a) h
-template <typename T, int N> inline
-Jet<T, N> sinh(const Jet<T, N>& f) {
+template <typename T, int N>
+inline Jet<T, N> sinh(const Jet<T, N>& f) {
return Jet<T, N>(sinh(f.a), cosh(f.a) * f.v);
}
// cosh(a + h) ~= cosh(a) + sinh(a) h
-template <typename T, int N> inline
-Jet<T, N> cosh(const Jet<T, N>& f) {
+template <typename T, int N>
+inline Jet<T, N> cosh(const Jet<T, N>& f) {
return Jet<T, N>(cosh(f.a), sinh(f.a) * f.v);
}
// tanh(a + h) ~= tanh(a) + (1 - tanh(a)^2) h
-template <typename T, int N> inline
-Jet<T, N> tanh(const Jet<T, N>& f) {
+template <typename T, int N>
+inline Jet<T, N> tanh(const Jet<T, N>& f) {
const T tanh_a = tanh(f.a);
const T tmp = T(1.0) - tanh_a * tanh_a;
return Jet<T, N>(tanh_a, tmp * f.v);
@@ -491,8 +510,8 @@ Jet<T, N> tanh(const Jet<T, N>& f) {
// result in a zero derivative which provides no information to the solver.
//
// floor(a + h) ~= floor(a) + 0
-template <typename T, int N> inline
-Jet<T, N> floor(const Jet<T, N>& f) {
+template <typename T, int N>
+inline Jet<T, N> floor(const Jet<T, N>& f) {
return Jet<T, N>(floor(f.a));
}
@@ -500,11 +519,60 @@ Jet<T, N> floor(const Jet<T, N>& f) {
// result in a zero derivative which provides no information to the solver.
//
// ceil(a + h) ~= ceil(a) + 0
-template <typename T, int N> inline
-Jet<T, N> ceil(const Jet<T, N>& f) {
+template <typename T, int N>
+inline Jet<T, N> ceil(const Jet<T, N>& f) {
return Jet<T, N>(ceil(f.a));
}
+// Some new additions to C++11:
+
+// cbrt(a + h) ~= cbrt(a) + h / (3 a ^ (2/3))
+template <typename T, int N>
+inline Jet<T, N> cbrt(const Jet<T, N>& f) {
+ const T derivative = T(1.0) / (T(3.0) * cbrt(f.a * f.a));
+ return Jet<T, N>(cbrt(f.a), f.v * derivative);
+}
+
+// exp2(x + h) = 2^(x+h) ~= 2^x + h*2^x*log(2)
+template <typename T, int N>
+inline Jet<T, N> exp2(const Jet<T, N>& f) {
+ const T tmp = exp2(f.a);
+ const T derivative = tmp * log(T(2));
+ return Jet<T, N>(tmp, f.v * derivative);
+}
+
+// log2(x + h) ~= log2(x) + h / (x * log(2))
+template <typename T, int N>
+inline Jet<T, N> log2(const Jet<T, N>& f) {
+ const T derivative = T(1.0) / (f.a * log(T(2)));
+ return Jet<T, N>(log2(f.a), f.v * derivative);
+}
+
+// Like sqrt(x^2 + y^2),
+// but acts to prevent underflow/overflow for small/large x/y.
+// Note that the function is non-smooth at x=y=0,
+// so the derivative is undefined there.
+template <typename T, int N>
+inline Jet<T, N> hypot(const Jet<T, N>& x, const Jet<T, N>& y) {
+ // d/da sqrt(a) = 0.5 / sqrt(a)
+ // d/dx x^2 + y^2 = 2x
+ // So by the chain rule:
+ // d/dx sqrt(x^2 + y^2) = 0.5 / sqrt(x^2 + y^2) * 2x = x / sqrt(x^2 + y^2)
+ // d/dy sqrt(x^2 + y^2) = y / sqrt(x^2 + y^2)
+ const T tmp = hypot(x.a, y.a);
+ return Jet<T, N>(tmp, x.a / tmp * x.v + y.a / tmp * y.v);
+}
+
+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;
+}
+
+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;
+}
+
// Bessel functions of the first kind with integer order equal to 0, 1, n.
//
// Microsoft has deprecated the j[0,1,n]() POSIX Bessel functions in favour of
@@ -512,21 +580,21 @@ Jet<T, N> ceil(const Jet<T, N>& f) {
// function errors in client code (the specific warning is suppressed when
// Ceres itself is built).
inline double BesselJ0(double x) {
-#if defined(_MSC_VER) && defined(_j0)
+#if defined(CERES_MSVC_USE_UNDERSCORE_PREFIXED_BESSEL_FUNCTIONS)
return _j0(x);
#else
return j0(x);
#endif
}
inline double BesselJ1(double x) {
-#if defined(_MSC_VER) && defined(_j1)
+#if defined(CERES_MSVC_USE_UNDERSCORE_PREFIXED_BESSEL_FUNCTIONS)
return _j1(x);
#else
return j1(x);
#endif
}
inline double BesselJn(int n, double x) {
-#if defined(_MSC_VER) && defined(_jn)
+#if defined(CERES_MSVC_USE_UNDERSCORE_PREFIXED_BESSEL_FUNCTIONS)
return _jn(n, x);
#else
return jn(n, x);
@@ -541,32 +609,32 @@ inline double BesselJn(int n, double x) {
// See formula http://dlmf.nist.gov/10.6#E3
// j0(a + h) ~= j0(a) - j1(a) h
-template <typename T, int N> inline
-Jet<T, N> BesselJ0(const Jet<T, N>& f) {
- return Jet<T, N>(BesselJ0(f.a),
- -BesselJ1(f.a) * f.v);
+template <typename T, int N>
+inline Jet<T, N> BesselJ0(const Jet<T, N>& f) {
+ return Jet<T, N>(BesselJ0(f.a), -BesselJ1(f.a) * f.v);
}
// See formula http://dlmf.nist.gov/10.6#E1
// j1(a + h) ~= j1(a) + 0.5 ( j0(a) - j2(a) ) h
-template <typename T, int N> inline
-Jet<T, N> BesselJ1(const Jet<T, N>& f) {
+template <typename T, int N>
+inline Jet<T, N> BesselJ1(const Jet<T, N>& f) {
return Jet<T, N>(BesselJ1(f.a),
T(0.5) * (BesselJ0(f.a) - BesselJn(2, f.a)) * f.v);
}
// See formula http://dlmf.nist.gov/10.6#E1
// j_n(a + h) ~= j_n(a) + 0.5 ( j_{n-1}(a) - j_{n+1}(a) ) h
-template <typename T, int N> inline
-Jet<T, N> BesselJn(int n, const Jet<T, N>& f) {
- return Jet<T, N>(BesselJn(n, f.a),
- T(0.5) * (BesselJn(n - 1, f.a) - BesselJn(n + 1, f.a)) * f.v);
+template <typename T, int N>
+inline Jet<T, N> BesselJn(int n, const Jet<T, N>& f) {
+ return Jet<T, N>(
+ BesselJn(n, f.a),
+ 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 IsFinite and 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
+// 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
@@ -574,81 +642,88 @@ Jet<T, N> BesselJn(int n, const Jet<T, N>& f) {
// derivatives are sane.
// The jet is finite if all parts of the jet are finite.
-template <typename T, int N> inline
-bool IsFinite(const Jet<T, N>& f) {
- if (!IsFinite(f.a)) {
- return false;
- }
+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) {
- if (!IsFinite(f.v[i])) {
- return false;
- }
+ result = result & isfinite(f.v[i]);
}
- return true;
+ return result;
}
-// The jet is infinite if any part of the jet is infinite.
-template <typename T, int N> inline
-bool IsInfinite(const Jet<T, N>& f) {
- if (IsInfinite(f.a)) {
- return true;
- }
- for (int i = 0; i < N; i++) {
- if (IsInfinite(f.v[i])) {
- return true;
- }
+// The jet is infinite if any 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 false;
+ return result;
}
// The jet is NaN if any part of the jet is NaN.
-template <typename T, int N> inline
-bool IsNaN(const Jet<T, N>& f) {
- if (IsNaN(f.a)) {
- return true;
- }
+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) {
- if (IsNaN(f.v[i])) {
- return true;
- }
+ result = result | isnan(f.v[i]);
}
- return false;
+ return result;
}
// The jet is normal if all parts of the jet are normal.
-template <typename T, int N> inline
-bool IsNormal(const Jet<T, N>& f) {
- if (!IsNormal(f.a)) {
- return false;
- }
+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) {
- if (!IsNormal(f.v[i])) {
- return false;
- }
+ result = result & isnormal(f.v[i]);
}
- return true;
+ return result;
+}
+
+// Legacy functions from the pre-C++11 days.
+template <typename T, int N>
+inline bool IsFinite(const Jet<T, N>& f) {
+ return isfinite(f);
+}
+
+template <typename T, int N>
+inline bool IsNaN(const Jet<T, N>& f) {
+ return isnan(f);
+}
+
+template <typename T, int N>
+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>
+inline bool IsInfinite(const Jet<T, N>& f) {
+ return isinf(f);
}
// 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
// change of (x, y) in the positive angular direction.
-template <typename T, int N> inline
-Jet<T, N> atan2(const Jet<T, N>& g, const Jet<T, N>& f) {
+template <typename T, int N>
+inline Jet<T, N> atan2(const Jet<T, N>& g, const Jet<T, N>& f) {
// Note order of arguments:
//
// f = a + da
// g = b + db
T const tmp = T(1.0) / (f.a * f.a + g.a * g.a);
- return Jet<T, N>(atan2(g.a, f.a), tmp * (- g.a * f.v + f.a * g.v));
+ return Jet<T, N>(atan2(g.a, f.a), tmp * (-g.a * f.v + f.a * g.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> inline
-Jet<T, N> pow(const Jet<T, N>& f, double g) {
+template <typename T, int N>
+inline Jet<T, N> pow(const Jet<T, N>& f, double g) {
T const tmp = g * pow(f.a, g - T(1.0));
return Jet<T, N>(pow(f.a, g), tmp * f.v);
}
@@ -664,26 +739,30 @@ Jet<T, N> pow(const Jet<T, N>& f, double g) {
// 3. For f < 0 and integer g we have: (f)^(g + dg) ~= f^g but if dg
// != 0, the derivatives are not defined and we return NaN.
-template <typename T, int N> inline
-Jet<T, N> pow(double f, const Jet<T, N>& g) {
- if (f == 0 && g.a > 0) {
+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)) {
// Handle case 2.
- return Jet<T, N>(T(0.0));
- }
- if (f < 0 && g.a == floor(g.a)) {
- // Handle case 3.
- Jet<T, N> ret(pow(f, g.a));
- for (int i = 0; i < N; i++) {
- if (g.v[i] != T(0.0)) {
- // Return a NaN when g.v != 0.
- ret.v[i] = std::numeric_limits<T>::quiet_NaN();
+ result = Jet<T, N>(T(0.0));
+ } else {
+ if (f < 0 && g.a == 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)) {
+ // Return a NaN when g.v != 0.
+ result.v[i] = std::numeric_limits<T>::quiet_NaN();
+ }
}
+ } else {
+ // Handle case 1.
+ T const tmp = pow(f, g.a);
+ result = Jet<T, N>(tmp, log(f) * tmp * g.v);
}
- return ret;
}
- // Handle case 1.
- T const tmp = pow(f, g.a);
- return Jet<T, N>(tmp, log(f) * tmp * g.v);
+
+ return result;
}
// pow -- both base and exponent are differentiable functions. This has a
@@ -722,73 +801,48 @@ Jet<T, N> pow(double f, const Jet<T, N>& g) {
//
// 9. For f < 0, g noninteger: The value and derivatives of f^g are not finite.
-template <typename T, int N> inline
-Jet<T, N> pow(const Jet<T, N>& f, const Jet<T, N>& g) {
- if (f.a == 0 && g.a >= 1) {
+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)) {
// Handle cases 2 and 3.
- if (g.a > 1) {
- return Jet<T, N>(T(0.0));
+ if (g.a > T(1)) {
+ result = Jet<T, N>(T(0.0));
+ } else {
+ result = f;
}
- return f;
- }
- if (f.a < 0 && g.a == floor(g.a)) {
- // Handle cases 7 and 8.
- T const tmp = g.a * pow(f.a, g.a - T(1.0));
- Jet<T, N> ret(pow(f.a, g.a), tmp * f.v);
- for (int i = 0; i < N; i++) {
- if (g.v[i] != T(0.0)) {
- // Return a NaN when g.v != 0.
- ret.v[i] = std::numeric_limits<T>::quiet_NaN();
+
+ } else {
+ if (f.a < T(0) && g.a == 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)) {
+ // Return a NaN when g.v != 0.
+ result.v[i] = T(std::numeric_limits<double>::quiet_NaN());
+ }
}
+ } else {
+ // Handle the remaining cases. For cases 4,5,6,9 we allow the log()
+ // function to generate -HUGE_VAL or NaN, since those cases result in a
+ // nonfinite derivative.
+ T const tmp1 = pow(f.a, g.a);
+ T const tmp2 = g.a * pow(f.a, g.a - T(1.0));
+ T const tmp3 = tmp1 * log(f.a);
+ result = Jet<T, N>(tmp1, tmp2 * f.v + tmp3 * g.v);
}
- return ret;
}
- // Handle the remaining cases. For cases 4,5,6,9 we allow the log() function
- // to generate -HUGE_VAL or NaN, since those cases result in a nonfinite
- // derivative.
- T const tmp1 = pow(f.a, g.a);
- T const tmp2 = g.a * pow(f.a, g.a - T(1.0));
- T const tmp3 = tmp1 * log(f.a);
- return Jet<T, N>(tmp1, tmp2 * f.v + tmp3 * g.v);
-}
-
-// Define the helper functions Eigen needs to embed Jet types.
-//
-// NOTE(keir): machine_epsilon() and precision() are missing, because they don't
-// work with nested template types (e.g. where the scalar is itself templated).
-// Among other things, this means that decompositions of Jet's does not work,
-// for example
-//
-// Matrix<Jet<T, N> ... > A, x, b;
-// ...
-// A.solve(b, &x)
-//
-// does not work and will fail with a strange compiler error.
-//
-// TODO(keir): This is an Eigen 2.0 limitation that is lifted in 3.0. When we
-// switch to 3.0, also add the rest of the specialization functionality.
-template<typename T, int N> inline const Jet<T, N>& ei_conj(const Jet<T, N>& x) { return x; } // NOLINT
-template<typename T, int N> inline const Jet<T, N>& ei_real(const Jet<T, N>& x) { return x; } // NOLINT
-template<typename T, int N> inline Jet<T, N> ei_imag(const Jet<T, N>& ) { return Jet<T, N>(0.0); } // NOLINT
-template<typename T, int N> inline Jet<T, N> ei_abs (const Jet<T, N>& x) { return fabs(x); } // NOLINT
-template<typename T, int N> inline Jet<T, N> ei_abs2(const Jet<T, N>& x) { return x * x; } // NOLINT
-template<typename T, int N> inline Jet<T, N> ei_sqrt(const Jet<T, N>& x) { return sqrt(x); } // NOLINT
-template<typename T, int N> inline Jet<T, N> ei_exp (const Jet<T, N>& x) { return exp(x); } // NOLINT
-template<typename T, int N> inline Jet<T, N> ei_log (const Jet<T, N>& x) { return log(x); } // NOLINT
-template<typename T, int N> inline Jet<T, N> ei_sin (const Jet<T, N>& x) { return sin(x); } // NOLINT
-template<typename T, int N> inline Jet<T, N> ei_cos (const Jet<T, N>& x) { return cos(x); } // NOLINT
-template<typename T, int N> inline Jet<T, N> ei_tan (const Jet<T, N>& x) { return tan(x); } // NOLINT
-template<typename T, int N> inline Jet<T, N> ei_atan(const Jet<T, N>& x) { return atan(x); } // NOLINT
-template<typename T, int N> inline Jet<T, N> ei_sinh(const Jet<T, N>& x) { return sinh(x); } // NOLINT
-template<typename T, int N> inline Jet<T, N> ei_cosh(const Jet<T, N>& x) { return cosh(x); } // NOLINT
-template<typename T, int N> inline Jet<T, N> ei_tanh(const Jet<T, N>& x) { return tanh(x); } // NOLINT
-template<typename T, int N> inline Jet<T, N> ei_pow (const Jet<T, N>& x, Jet<T, N> y) { return pow(x, y); } // NOLINT
+
+ return result;
+}
// Note: This has to be in the ceres namespace for argument dependent lookup to
// function correctly. Otherwise statements like CHECK_LE(x, 2.0) fail with
// strange compile errors.
template <typename T, int N>
-inline std::ostream &operator<<(std::ostream &s, const Jet<T, N>& z) {
+inline std::ostream& operator<<(std::ostream& s, const Jet<T, N>& z) {
s << "[" << z.a << " ; ";
for (int i = 0; i < N; ++i) {
s << z.v[i];
@@ -799,15 +853,78 @@ inline std::ostream &operator<<(std::ostream &s, const Jet<T, N>& z) {
s << "]";
return s;
}
-
} // namespace ceres
+namespace std {
+template <typename T, int N>
+struct numeric_limits<ceres::Jet<T, N>> {
+ static constexpr bool is_specialized = true;
+ static constexpr bool is_signed = std::numeric_limits<T>::is_signed;
+ static constexpr bool is_integer = std::numeric_limits<T>::is_integer;
+ static constexpr bool is_exact = std::numeric_limits<T>::is_exact;
+ static constexpr bool has_infinity = std::numeric_limits<T>::has_infinity;
+ static constexpr bool has_quiet_NaN = std::numeric_limits<T>::has_quiet_NaN;
+ static constexpr bool has_signaling_NaN =
+ std::numeric_limits<T>::has_signaling_NaN;
+ static constexpr bool is_iec559 = std::numeric_limits<T>::is_iec559;
+ static constexpr bool is_bounded = std::numeric_limits<T>::is_bounded;
+ static constexpr bool is_modulo = std::numeric_limits<T>::is_modulo;
+
+ static constexpr std::float_denorm_style has_denorm =
+ std::numeric_limits<T>::has_denorm;
+ static constexpr std::float_round_style round_style =
+ std::numeric_limits<T>::round_style;
+
+ static constexpr int digits = std::numeric_limits<T>::digits;
+ static constexpr int digits10 = std::numeric_limits<T>::digits10;
+ static constexpr int max_digits10 = std::numeric_limits<T>::max_digits10;
+ static constexpr int radix = std::numeric_limits<T>::radix;
+ static constexpr int min_exponent = std::numeric_limits<T>::min_exponent;
+ static constexpr int min_exponent10 = std::numeric_limits<T>::max_exponent10;
+ static constexpr int max_exponent = std::numeric_limits<T>::max_exponent;
+ static constexpr int max_exponent10 = std::numeric_limits<T>::max_exponent10;
+ static constexpr bool traps = std::numeric_limits<T>::traps;
+ 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> lowest() noexcept {
+ return ceres::Jet<T, N>(std::numeric_limits<T>::lowest());
+ }
+ static constexpr ceres::Jet<T, N> epsilon() noexcept {
+ return ceres::Jet<T, N>(std::numeric_limits<T>::epsilon());
+ }
+ static constexpr ceres::Jet<T, N> round_error() noexcept {
+ return ceres::Jet<T, N>(std::numeric_limits<T>::round_error());
+ }
+ static constexpr ceres::Jet<T, N> infinity() noexcept {
+ return ceres::Jet<T, N>(std::numeric_limits<T>::infinity());
+ }
+ static constexpr ceres::Jet<T, N> quiet_NaN() noexcept {
+ return ceres::Jet<T, N>(std::numeric_limits<T>::quiet_NaN());
+ }
+ static constexpr ceres::Jet<T, N> signaling_NaN() noexcept {
+ return ceres::Jet<T, N>(std::numeric_limits<T>::signaling_NaN());
+ }
+ static constexpr ceres::Jet<T, N> denorm_min() noexcept {
+ 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());
+ }
+};
+
+} // namespace std
+
namespace Eigen {
// Creating a specialization of NumTraits enables placing Jet objects inside
// Eigen arrays, getting all the goodness of Eigen combined with autodiff.
-template<typename T, int N>
-struct NumTraits<ceres::Jet<T, N> > {
+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;
@@ -821,6 +938,8 @@ struct NumTraits<ceres::Jet<T, N> > {
return Real(std::numeric_limits<T>::epsilon());
}
+ static inline int digits10() { return NumTraits<T>::digits10(); }
+
enum {
IsComplex = 0,
IsInteger = 0,
@@ -833,7 +952,7 @@ struct NumTraits<ceres::Jet<T, N> > {
RequireInitialization = 1
};
- template<bool Vectorized>
+ template <bool Vectorized>
struct Div {
enum {
#if defined(EIGEN_VECTORIZE_AVX)
@@ -847,6 +966,24 @@ struct NumTraits<ceres::Jet<T, N> > {
Cost = 3
};
};
+
+ 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
+// allows you to perform matrix/array operations with Eigen matrices and arrays
+// such as addition, subtraction, multiplication, and division where one Eigen
+// matrix/array is of type Jet and the other is a scalar type. This improves
+// performance by using the optimized scalar-to-Jet binary operations but
+// 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;
+};
+template <typename BinaryOp, typename T, int N>
+struct ScalarBinaryOpTraits<T, ceres::Jet<T, N>, BinaryOp> {
+ typedef ceres::Jet<T, N> ReturnType;
};
} // namespace Eigen
diff --git a/extern/ceres/include/ceres/local_parameterization.h b/extern/ceres/include/ceres/local_parameterization.h
index 379fc684921..1576e829e73 100644
--- a/extern/ceres/include/ceres/local_parameterization.h
+++ b/extern/ceres/include/ceres/local_parameterization.h
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2019 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -32,10 +32,12 @@
#ifndef CERES_PUBLIC_LOCAL_PARAMETERIZATION_H_
#define CERES_PUBLIC_LOCAL_PARAMETERIZATION_H_
+#include <array>
+#include <memory>
#include <vector>
-#include "ceres/internal/port.h"
-#include "ceres/internal/scoped_ptr.h"
+
#include "ceres/internal/disable_warnings.h"
+#include "ceres/internal/port.h"
namespace ceres {
@@ -61,7 +63,7 @@ namespace ceres {
// optimize over two dimensional vector delta in the tangent space at
// that point and then "move" to the point x + delta, where the move
// operation involves projecting back onto the sphere. Doing so
-// removes a redundent dimension from the optimization, making it
+// removes a redundant dimension from the optimization, making it
// numerically more robust and efficient.
//
// More generally we can define a function
@@ -154,17 +156,16 @@ class CERES_EXPORT IdentityParameterization : public LocalParameterization {
public:
explicit IdentityParameterization(int size);
virtual ~IdentityParameterization() {}
- virtual bool Plus(const double* x,
- const double* delta,
- double* x_plus_delta) const;
- virtual bool ComputeJacobian(const double* x,
- double* jacobian) const;
- virtual bool MultiplyByJacobian(const double* x,
- const int num_cols,
- const double* global_matrix,
- double* local_matrix) const;
- virtual int GlobalSize() const { return size_; }
- virtual int LocalSize() const { return size_; }
+ bool Plus(const double* x,
+ const double* delta,
+ double* x_plus_delta) const override;
+ bool ComputeJacobian(const double* x, double* jacobian) const override;
+ bool MultiplyByJacobian(const double* x,
+ const int num_cols,
+ const double* global_matrix,
+ double* local_matrix) const override;
+ int GlobalSize() const override { return size_; }
+ int LocalSize() const override { return size_; }
private:
const int size_;
@@ -176,19 +177,18 @@ class CERES_EXPORT SubsetParameterization : public LocalParameterization {
explicit SubsetParameterization(int size,
const std::vector<int>& constant_parameters);
virtual ~SubsetParameterization() {}
- virtual bool Plus(const double* x,
- const double* delta,
- double* x_plus_delta) const;
- virtual bool ComputeJacobian(const double* x,
- double* jacobian) const;
- virtual bool MultiplyByJacobian(const double* x,
- const int num_cols,
- const double* global_matrix,
- double* local_matrix) const;
- virtual int GlobalSize() const {
+ bool Plus(const double* x,
+ const double* delta,
+ double* x_plus_delta) const override;
+ bool ComputeJacobian(const double* x, double* jacobian) const override;
+ bool MultiplyByJacobian(const double* x,
+ const int num_cols,
+ const double* global_matrix,
+ double* local_matrix) const override;
+ int GlobalSize() const override {
return static_cast<int>(constancy_mask_.size());
}
- virtual int LocalSize() const { return local_size_; }
+ int LocalSize() const override { return local_size_; }
private:
const int local_size_;
@@ -202,13 +202,12 @@ class CERES_EXPORT SubsetParameterization : public LocalParameterization {
class CERES_EXPORT QuaternionParameterization : public LocalParameterization {
public:
virtual ~QuaternionParameterization() {}
- virtual bool Plus(const double* x,
- const double* delta,
- double* x_plus_delta) const;
- virtual bool ComputeJacobian(const double* x,
- double* jacobian) const;
- virtual int GlobalSize() const { return 4; }
- virtual int LocalSize() const { return 3; }
+ bool Plus(const double* x,
+ const double* delta,
+ double* x_plus_delta) const override;
+ bool ComputeJacobian(const double* x, double* jacobian) const override;
+ int GlobalSize() const override { return 4; }
+ int LocalSize() const override { return 3; }
};
// Implements the quaternion local parameterization for Eigen's representation
@@ -222,16 +221,16 @@ class CERES_EXPORT QuaternionParameterization : public LocalParameterization {
//
// Plus(x, delta) = [sin(|delta|) delta / |delta|, cos(|delta|)] * x
// with * being the quaternion multiplication operator.
-class EigenQuaternionParameterization : public ceres::LocalParameterization {
+class CERES_EXPORT EigenQuaternionParameterization
+ : public ceres::LocalParameterization {
public:
virtual ~EigenQuaternionParameterization() {}
- virtual bool Plus(const double* x,
- const double* delta,
- double* x_plus_delta) const;
- virtual bool ComputeJacobian(const double* x,
- double* jacobian) const;
- virtual int GlobalSize() const { return 4; }
- virtual int LocalSize() const { return 3; }
+ bool Plus(const double* x,
+ const double* delta,
+ double* x_plus_delta) const override;
+ bool ComputeJacobian(const double* x, double* jacobian) const override;
+ int GlobalSize() const override { return 4; }
+ int LocalSize() const override { return 3; }
};
// This provides a parameterization for homogeneous vectors which are commonly
@@ -247,32 +246,55 @@ class EigenQuaternionParameterization : public ceres::LocalParameterization {
// 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_EXPORT HomogeneousVectorParameterization
+ : public LocalParameterization {
public:
explicit HomogeneousVectorParameterization(int size);
virtual ~HomogeneousVectorParameterization() {}
- virtual bool Plus(const double* x,
- const double* delta,
- double* x_plus_delta) const;
- virtual bool ComputeJacobian(const double* x,
- double* jacobian) const;
- virtual int GlobalSize() const { return size_; }
- virtual int LocalSize() const { return size_ - 1; }
+ bool Plus(const double* x,
+ const double* delta,
+ double* x_plus_delta) const override;
+ bool ComputeJacobian(const double* x, double* jacobian) const override;
+ int GlobalSize() const override { return size_; }
+ int LocalSize() const override { return size_ - 1; }
private:
const int size_;
};
+// This provides a parameterization 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
+// HomogeneousVectorParameterization. The update of the origin point is
+// perpendicular to the line direction before the update.
+//
+// This local parameterization 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).
+template <int AmbientSpaceDimension>
+class LineParameterization : public LocalParameterization {
+ public:
+ static_assert(AmbientSpaceDimension >= 2,
+ "The ambient space must be at least 2");
+
+ bool Plus(const double* x,
+ const double* delta,
+ double* x_plus_delta) const override;
+ bool ComputeJacobian(const double* x, double* jacobian) const override;
+ int GlobalSize() const override { return 2 * AmbientSpaceDimension; }
+ int LocalSize() const override { return 2 * (AmbientSpaceDimension - 1); }
+};
+
// Construct a local parameterization by taking the Cartesian product
// of a number of other local parameterizations. 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.
//
-// Currently this class supports taking the cartesian product of up to
-// four local parameterizations.
-//
// Example usage:
//
// ProductParameterization product_param(new QuaterionionParameterization(),
@@ -282,35 +304,49 @@ class CERES_EXPORT HomogeneousVectorParameterization :
// rotation is represented using a quaternion.
class CERES_EXPORT ProductParameterization : public LocalParameterization {
public:
+ ProductParameterization(const ProductParameterization&) = delete;
+ ProductParameterization& operator=(const ProductParameterization&) = delete;
//
- // NOTE: All the constructors take ownership of the input local
+ // NOTE: The constructor takes ownership of the input local
// parameterizations.
//
- ProductParameterization(LocalParameterization* local_param1,
- LocalParameterization* local_param2);
+ template <typename... LocalParams>
+ ProductParameterization(LocalParams*... local_params)
+ : local_params_(sizeof...(LocalParams)),
+ local_size_{0},
+ global_size_{0},
+ buffer_size_{0} {
+ constexpr int kNumLocalParams = sizeof...(LocalParams);
+ static_assert(kNumLocalParams >= 2,
+ "At least two local parameterizations must be specified.");
- ProductParameterization(LocalParameterization* local_param1,
- LocalParameterization* local_param2,
- LocalParameterization* local_param3);
+ using LocalParameterizationPtr = std::unique_ptr<LocalParameterization>;
- ProductParameterization(LocalParameterization* local_param1,
- LocalParameterization* local_param2,
- LocalParameterization* local_param3,
- LocalParameterization* local_param4);
+ // Wrap all raw pointers into std::unique_ptr for exception safety.
+ std::array<LocalParameterizationPtr, kNumLocalParams> local_params_array{
+ LocalParameterizationPtr(local_params)...};
- virtual ~ProductParameterization();
- virtual bool Plus(const double* x,
- const double* delta,
- double* x_plus_delta) const;
- virtual bool ComputeJacobian(const double* x,
- double* jacobian) const;
- virtual int GlobalSize() const { return global_size_; }
- virtual int LocalSize() const { return local_size_; }
+ // Initialize internal state.
+ for (int i = 0; i < kNumLocalParams; ++i) {
+ LocalParameterizationPtr& param = local_params_[i];
+ param = std::move(local_params_array[i]);
- private:
- void Init();
+ buffer_size_ =
+ std::max(buffer_size_, param->LocalSize() * param->GlobalSize());
+ global_size_ += param->GlobalSize();
+ local_size_ += param->LocalSize();
+ }
+ }
+
+ bool Plus(const double* x,
+ const double* delta,
+ double* x_plus_delta) 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_; }
- std::vector<LocalParameterization*> local_params_;
+ private:
+ std::vector<std::unique_ptr<LocalParameterization>> local_params_;
int local_size_;
int global_size_;
int buffer_size_;
@@ -319,5 +355,7 @@ class CERES_EXPORT ProductParameterization : public LocalParameterization {
} // namespace ceres
#include "ceres/internal/reenable_warnings.h"
+#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 0512c135143..7aabf7dfce1 100644
--- a/extern/ceres/include/ceres/loss_function.h
+++ b/extern/ceres/include/ceres/loss_function.h
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2019 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -57,7 +57,7 @@
// anything special (i.e. if we used a basic quadratic loss), the
// residual for the erroneous measurement will result in extreme error
// due to the quadratic nature of squared loss. This results in the
-// entire solution getting pulled away from the optimimum to reduce
+// entire solution getting pulled away from the optimum to reduce
// the large error that would otherwise be attributed to the wrong
// measurement.
//
@@ -75,11 +75,11 @@
#ifndef CERES_PUBLIC_LOSS_FUNCTION_H_
#define CERES_PUBLIC_LOSS_FUNCTION_H_
-#include "glog/logging.h"
-#include "ceres/internal/macros.h"
-#include "ceres/internal/scoped_ptr.h"
-#include "ceres/types.h"
+#include <memory>
+
#include "ceres/internal/disable_warnings.h"
+#include "ceres/types.h"
+#include "glog/logging.h"
namespace ceres {
@@ -119,7 +119,6 @@ class CERES_EXPORT LossFunction {
// Note: in the region of interest (i.e. s < 3) we have:
// TrivialLoss >= HuberLoss >= SoftLOneLoss >= CauchyLoss
-
// This corresponds to no robustification.
//
// rho(s) = s
@@ -131,7 +130,7 @@ class CERES_EXPORT LossFunction {
// thing.
class CERES_EXPORT TrivialLoss : public LossFunction {
public:
- virtual void Evaluate(double, double*) const;
+ void Evaluate(double, double*) const override;
};
// Scaling
@@ -174,8 +173,8 @@ class CERES_EXPORT TrivialLoss : public LossFunction {
// http://en.wikipedia.org/wiki/Huber_Loss_Function
class CERES_EXPORT HuberLoss : public LossFunction {
public:
- explicit HuberLoss(double a) : a_(a), b_(a * a) { }
- virtual void Evaluate(double, double*) const;
+ explicit HuberLoss(double a) : a_(a), b_(a * a) {}
+ void Evaluate(double, double*) const override;
private:
const double a_;
@@ -187,11 +186,11 @@ class CERES_EXPORT HuberLoss : public LossFunction {
//
// rho(s) = 2 (sqrt(1 + s) - 1).
//
-// At s = 0: rho = [0, 1, -1/2].
+// At s = 0: rho = [0, 1, -1 / (2 * a^2)].
class CERES_EXPORT SoftLOneLoss : public LossFunction {
public:
- explicit SoftLOneLoss(double a) : b_(a * a), c_(1 / b_) { }
- virtual void Evaluate(double, double*) const;
+ explicit SoftLOneLoss(double a) : b_(a * a), c_(1 / b_) {}
+ void Evaluate(double, double*) const override;
private:
// b = a^2.
@@ -204,11 +203,11 @@ class CERES_EXPORT SoftLOneLoss : public LossFunction {
//
// rho(s) = log(1 + s).
//
-// At s = 0: rho = [0, 1, -1].
+// At s = 0: rho = [0, 1, -1 / a^2].
class CERES_EXPORT CauchyLoss : public LossFunction {
public:
- explicit CauchyLoss(double a) : b_(a * a), c_(1 / b_) { }
- virtual void Evaluate(double, double*) const;
+ explicit CauchyLoss(double a) : b_(a * a), c_(1 / b_) {}
+ void Evaluate(double, double*) const override;
private:
// b = a^2.
@@ -228,8 +227,8 @@ class CERES_EXPORT CauchyLoss : public LossFunction {
// At s = 0: rho = [0, 1, 0].
class CERES_EXPORT ArctanLoss : public LossFunction {
public:
- explicit ArctanLoss(double a) : a_(a), b_(1 / (a * a)) { }
- virtual void Evaluate(double, double*) const;
+ explicit ArctanLoss(double a) : a_(a), b_(1 / (a * a)) {}
+ void Evaluate(double, double*) const override;
private:
const double a_;
@@ -268,7 +267,7 @@ class CERES_EXPORT ArctanLoss : public LossFunction {
class CERES_EXPORT TolerantLoss : public LossFunction {
public:
explicit TolerantLoss(double a, double b);
- virtual void Evaluate(double, double*) const;
+ void Evaluate(double, double*) const override;
private:
const double a_, b_, c_;
@@ -277,16 +276,17 @@ class CERES_EXPORT TolerantLoss : public LossFunction {
// This is the Tukey biweight loss function which aggressively
// attempts to suppress large errors.
//
-// The term is computed as:
+// The term is computed as follows where the equations are scaled by a
+// factor of 2 because the cost function is given by 1/2 rho(s):
//
-// rho(s) = a^2 / 6 * (1 - (1 - s / a^2)^3 ) for s <= a^2,
-// rho(s) = a^2 / 6 for s > a^2.
+// rho(s) = a^2 / 3 * (1 - (1 - s / a^2)^3 ) for s <= a^2,
+// rho(s) = a^2 / 3 for s > a^2.
//
-// At s = 0: rho = [0, 0.5, -1 / a^2]
+// At s = 0: rho = [0, 1, -2 / a^2]
class CERES_EXPORT TukeyLoss : public ceres::LossFunction {
public:
- explicit TukeyLoss(double a) : a_squared_(a * a) { }
- virtual void Evaluate(double, double*) const;
+ explicit TukeyLoss(double a) : a_squared_(a * a) {}
+ void Evaluate(double, double*) const override;
private:
const double a_squared_;
@@ -297,13 +297,15 @@ class CERES_EXPORT TukeyLoss : public ceres::LossFunction {
// The loss functions must not be NULL.
class CERES_EXPORT ComposedLoss : public LossFunction {
public:
- explicit ComposedLoss(const LossFunction* f, Ownership ownership_f,
- const LossFunction* g, Ownership ownership_g);
+ explicit ComposedLoss(const LossFunction* f,
+ Ownership ownership_f,
+ const LossFunction* g,
+ Ownership ownership_g);
virtual ~ComposedLoss();
- virtual void Evaluate(double, double*) const;
+ void Evaluate(double, double*) const override;
private:
- internal::scoped_ptr<const LossFunction> f_, g_;
+ std::unique_ptr<const LossFunction> f_, g_;
const Ownership ownership_f_, ownership_g_;
};
@@ -329,21 +331,22 @@ class CERES_EXPORT ScaledLoss : public LossFunction {
// Constructs a ScaledLoss wrapping another loss function. Takes
// ownership of the wrapped loss function or not depending on the
// ownership parameter.
- ScaledLoss(const LossFunction* rho, double a, Ownership ownership) :
- rho_(rho), a_(a), ownership_(ownership) { }
+ ScaledLoss(const LossFunction* rho, double a, Ownership ownership)
+ : rho_(rho), a_(a), ownership_(ownership) {}
+ ScaledLoss(const ScaledLoss&) = delete;
+ void operator=(const ScaledLoss&) = delete;
virtual ~ScaledLoss() {
if (ownership_ == DO_NOT_TAKE_OWNERSHIP) {
rho_.release();
}
}
- virtual void Evaluate(double, double*) const;
+ void Evaluate(double, double*) const override;
private:
- internal::scoped_ptr<const LossFunction> rho_;
+ std::unique_ptr<const LossFunction> rho_;
const double a_;
const Ownership ownership_;
- CERES_DISALLOW_COPY_AND_ASSIGN(ScaledLoss);
};
// Sometimes after the optimization problem has been constructed, we
@@ -387,8 +390,10 @@ class CERES_EXPORT ScaledLoss : public LossFunction {
class CERES_EXPORT LossFunctionWrapper : public LossFunction {
public:
LossFunctionWrapper(LossFunction* rho, Ownership ownership)
- : rho_(rho), ownership_(ownership) {
- }
+ : rho_(rho), ownership_(ownership) {}
+
+ LossFunctionWrapper(const LossFunctionWrapper&) = delete;
+ void operator=(const LossFunctionWrapper&) = delete;
virtual ~LossFunctionWrapper() {
if (ownership_ == DO_NOT_TAKE_OWNERSHIP) {
@@ -396,13 +401,12 @@ class CERES_EXPORT LossFunctionWrapper : public LossFunction {
}
}
- virtual void Evaluate(double sq_norm, double out[3]) const {
+ void Evaluate(double sq_norm, double out[3]) const override {
if (rho_.get() == NULL) {
out[0] = sq_norm;
out[1] = 1.0;
out[2] = 0.0;
- }
- else {
+ } else {
rho_->Evaluate(sq_norm, out);
}
}
@@ -416,9 +420,8 @@ class CERES_EXPORT LossFunctionWrapper : public LossFunction {
}
private:
- internal::scoped_ptr<const LossFunction> rho_;
+ std::unique_ptr<const LossFunction> rho_;
Ownership ownership_;
- CERES_DISALLOW_COPY_AND_ASSIGN(LossFunctionWrapper);
};
} // namespace ceres
diff --git a/extern/ceres/include/ceres/normal_prior.h b/extern/ceres/include/ceres/normal_prior.h
index cd98b4c846b..14ab379f4af 100644
--- a/extern/ceres/include/ceres/normal_prior.h
+++ b/extern/ceres/include/ceres/normal_prior.h
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2019 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -35,8 +35,8 @@
#define CERES_PUBLIC_NORMAL_PRIOR_H_
#include "ceres/cost_function.h"
-#include "ceres/internal/eigen.h"
#include "ceres/internal/disable_warnings.h"
+#include "ceres/internal/eigen.h"
namespace ceres {
@@ -57,15 +57,15 @@ 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 : 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.
NormalPrior(const Matrix& A, const Vector& b);
+ bool Evaluate(double const* const* parameters,
+ double* residuals,
+ double** jacobians) const override;
- virtual bool Evaluate(double const* const* parameters,
- double* residuals,
- double** jacobians) const;
private:
Matrix A_;
Vector b_;
diff --git a/extern/ceres/include/ceres/numeric_diff_cost_function.h b/extern/ceres/include/ceres/numeric_diff_cost_function.h
index 5dfaeab6241..c69f262f572 100644
--- a/extern/ceres/include/ceres/numeric_diff_cost_function.h
+++ b/extern/ceres/include/ceres/numeric_diff_cost_function.h
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2019 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -52,16 +52,16 @@
// The actual cost added to the total problem is e^2, or (k - x'k)^2; however,
// the squaring is implicitly done by the optimization framework.
//
-// To write an numerically-differentiable cost function for the above model, first
-// define the object
+// To write an numerically-differentiable cost function for the above model,
+// first define the object
//
// class MyScalarCostFunctor {
-// MyScalarCostFunctor(double k): k_(k) {}
+// explicit MyScalarCostFunctor(double k): k_(k) {}
//
// bool operator()(const double* const x,
// const double* const y,
// double* residuals) const {
-// residuals[0] = k_ - x[0] * y[0] + x[1] * y[1];
+// residuals[0] = k_ - x[0] * y[0] - x[1] * y[1];
// return true;
// }
//
@@ -98,6 +98,8 @@
// NumericDiffCostFunction also supports cost functions with a
// runtime-determined number of residuals. For example:
//
+// clang-format off
+//
// CostFunction* cost_function
// = new NumericDiffCostFunction<MyScalarCostFunctor, CENTRAL, DYNAMIC, 2, 2>(
// new CostFunctorWithDynamicNumResiduals(1.0), ^ ^ ^
@@ -109,10 +111,8 @@
// Indicate dynamic number of residuals --------------------+ | |
// Dimension of x ------------------------------------------------+ |
// Dimension of y ---------------------------------------------------+
+// clang-format on
//
-// The framework can currently accommodate cost functions of up to 10
-// independent variables, and there is no limit on the dimensionality
-// of each of them.
//
// The central difference method is considerably more accurate at the cost of
// twice as many function evaluations than forward difference. Consider using
@@ -161,10 +161,13 @@
#ifndef CERES_PUBLIC_NUMERIC_DIFF_COST_FUNCTION_H_
#define CERES_PUBLIC_NUMERIC_DIFF_COST_FUNCTION_H_
+#include <array>
+#include <memory>
+
#include "Eigen/Dense"
#include "ceres/cost_function.h"
#include "ceres/internal/numeric_diff.h"
-#include "ceres/internal/scoped_ptr.h"
+#include "ceres/internal/parameter_dims.h"
#include "ceres/numeric_diff_options.h"
#include "ceres/sized_cost_function.h"
#include "ceres/types.h"
@@ -175,34 +178,17 @@ namespace ceres {
template <typename CostFunctor,
NumericDiffMethodType method = CENTRAL,
int kNumResiduals = 0, // Number of residuals, or ceres::DYNAMIC
- int N0 = 0, // Number of parameters in block 0.
- int N1 = 0, // Number of parameters in block 1.
- int N2 = 0, // Number of parameters in block 2.
- int N3 = 0, // Number of parameters in block 3.
- int N4 = 0, // Number of parameters in block 4.
- int N5 = 0, // Number of parameters in block 5.
- int N6 = 0, // Number of parameters in block 6.
- int N7 = 0, // Number of parameters in block 7.
- int N8 = 0, // Number of parameters in block 8.
- int N9 = 0> // Number of parameters in block 9.
-class NumericDiffCostFunction
- : public SizedCostFunction<kNumResiduals,
- N0, N1, N2, N3, N4,
- N5, N6, N7, N8, N9> {
+ int... Ns> // Parameters dimensions for each block.
+class NumericDiffCostFunction : public SizedCostFunction<kNumResiduals, Ns...> {
public:
NumericDiffCostFunction(
CostFunctor* functor,
Ownership ownership = TAKE_OWNERSHIP,
int num_residuals = kNumResiduals,
const NumericDiffOptions& options = NumericDiffOptions())
- : functor_(functor),
- ownership_(ownership),
- options_(options) {
+ : functor_(functor), ownership_(ownership), options_(options) {
if (kNumResiduals == DYNAMIC) {
- SizedCostFunction<kNumResiduals,
- N0, N1, N2, N3, N4,
- N5, N6, N7, N8, N9>
- ::set_num_residuals(num_residuals);
+ SizedCostFunction<kNumResiduals, Ns...>::set_num_residuals(num_residuals);
}
}
@@ -212,24 +198,21 @@ class NumericDiffCostFunction
}
}
- virtual bool Evaluate(double const* const* parameters,
- double* residuals,
- double** jacobians) const {
+ bool Evaluate(double const* const* parameters,
+ double* residuals,
+ double** jacobians) const override {
using internal::FixedArray;
using internal::NumericDiff;
- const int kNumParameters = N0 + N1 + N2 + N3 + N4 + N5 + N6 + N7 + N8 + N9;
- const int kNumParameterBlocks =
- (N0 > 0) + (N1 > 0) + (N2 > 0) + (N3 > 0) + (N4 > 0) +
- (N5 > 0) + (N6 > 0) + (N7 > 0) + (N8 > 0) + (N9 > 0);
+ using ParameterDims =
+ typename SizedCostFunction<kNumResiduals, Ns...>::ParameterDims;
+
+ constexpr int kNumParameters = ParameterDims::kNumParameters;
+ constexpr int kNumParameterBlocks = ParameterDims::kNumParameterBlocks;
// Get the function value (residuals) at the the point to evaluate.
- if (!internal::EvaluateImpl<CostFunctor,
- N0, N1, N2, N3, N4, N5, N6, N7, N8, N9>(
- functor_.get(),
- parameters,
- residuals,
- functor_.get())) {
+ if (!internal::VariadicEvaluate<ParameterDims>(
+ *functor_, parameters, residuals)) {
return false;
}
@@ -239,77 +222,29 @@ class NumericDiffCostFunction
// Create a copy of the parameters which will get mutated.
FixedArray<double> parameters_copy(kNumParameters);
- FixedArray<double*> parameters_reference_copy(kNumParameterBlocks);
-
- parameters_reference_copy[0] = parameters_copy.get();
- if (N1) parameters_reference_copy[1] = parameters_reference_copy[0] + N0;
- if (N2) parameters_reference_copy[2] = parameters_reference_copy[1] + N1;
- if (N3) parameters_reference_copy[3] = parameters_reference_copy[2] + N2;
- if (N4) parameters_reference_copy[4] = parameters_reference_copy[3] + N3;
- if (N5) parameters_reference_copy[5] = parameters_reference_copy[4] + N4;
- if (N6) parameters_reference_copy[6] = parameters_reference_copy[5] + N5;
- if (N7) parameters_reference_copy[7] = parameters_reference_copy[6] + N6;
- if (N8) parameters_reference_copy[8] = parameters_reference_copy[7] + N7;
- if (N9) parameters_reference_copy[9] = parameters_reference_copy[8] + N8;
+ std::array<double*, kNumParameterBlocks> parameters_reference_copy =
+ ParameterDims::GetUnpackedParameters(parameters_copy.data());
-#define CERES_COPY_PARAMETER_BLOCK(block) \
- if (N ## block) memcpy(parameters_reference_copy[block], \
- parameters[block], \
- sizeof(double) * N ## block); // NOLINT
-
- CERES_COPY_PARAMETER_BLOCK(0);
- CERES_COPY_PARAMETER_BLOCK(1);
- CERES_COPY_PARAMETER_BLOCK(2);
- CERES_COPY_PARAMETER_BLOCK(3);
- CERES_COPY_PARAMETER_BLOCK(4);
- CERES_COPY_PARAMETER_BLOCK(5);
- CERES_COPY_PARAMETER_BLOCK(6);
- CERES_COPY_PARAMETER_BLOCK(7);
- CERES_COPY_PARAMETER_BLOCK(8);
- CERES_COPY_PARAMETER_BLOCK(9);
-
-#undef CERES_COPY_PARAMETER_BLOCK
-
-#define CERES_EVALUATE_JACOBIAN_FOR_BLOCK(block) \
- if (N ## block && jacobians[block] != NULL) { \
- if (!NumericDiff<CostFunctor, \
- method, \
- kNumResiduals, \
- N0, N1, N2, N3, N4, N5, N6, N7, N8, N9, \
- block, \
- N ## block >::EvaluateJacobianForParameterBlock( \
- functor_.get(), \
- residuals, \
- options_, \
- SizedCostFunction<kNumResiduals, \
- N0, N1, N2, N3, N4, \
- N5, N6, N7, N8, N9>::num_residuals(), \
- block, \
- N ## block, \
- parameters_reference_copy.get(), \
- jacobians[block])) { \
- return false; \
- } \
+ for (int block = 0; block < kNumParameterBlocks; ++block) {
+ memcpy(parameters_reference_copy[block],
+ parameters[block],
+ sizeof(double) * ParameterDims::GetDim(block));
}
- CERES_EVALUATE_JACOBIAN_FOR_BLOCK(0);
- CERES_EVALUATE_JACOBIAN_FOR_BLOCK(1);
- CERES_EVALUATE_JACOBIAN_FOR_BLOCK(2);
- CERES_EVALUATE_JACOBIAN_FOR_BLOCK(3);
- CERES_EVALUATE_JACOBIAN_FOR_BLOCK(4);
- CERES_EVALUATE_JACOBIAN_FOR_BLOCK(5);
- CERES_EVALUATE_JACOBIAN_FOR_BLOCK(6);
- CERES_EVALUATE_JACOBIAN_FOR_BLOCK(7);
- CERES_EVALUATE_JACOBIAN_FOR_BLOCK(8);
- CERES_EVALUATE_JACOBIAN_FOR_BLOCK(9);
-
-#undef CERES_EVALUATE_JACOBIAN_FOR_BLOCK
+ internal::EvaluateJacobianForParameterBlocks<ParameterDims>::
+ template Apply<method, kNumResiduals>(
+ functor_.get(),
+ residuals,
+ options_,
+ SizedCostFunction<kNumResiduals, Ns...>::num_residuals(),
+ parameters_reference_copy.data(),
+ jacobians);
return true;
}
private:
- internal::scoped_ptr<CostFunctor> functor_;
+ std::unique_ptr<CostFunctor> functor_;
Ownership ownership_;
NumericDiffOptions options_;
};
diff --git a/extern/ceres/include/ceres/numeric_diff_options.h b/extern/ceres/include/ceres/numeric_diff_options.h
index 119c8a86596..64919ed5ab1 100644
--- a/extern/ceres/include/ceres/numeric_diff_options.h
+++ b/extern/ceres/include/ceres/numeric_diff_options.h
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2019 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -32,23 +32,17 @@
#ifndef CERES_PUBLIC_NUMERIC_DIFF_OPTIONS_H_
#define CERES_PUBLIC_NUMERIC_DIFF_OPTIONS_H_
+#include "ceres/internal/port.h"
+
namespace ceres {
// Options pertaining to numeric differentiation (e.g., convergence criteria,
// step sizes).
struct CERES_EXPORT NumericDiffOptions {
- NumericDiffOptions() {
- relative_step_size = 1e-6;
- ridders_relative_initial_step_size = 1e-2;
- max_num_ridders_extrapolations = 10;
- ridders_epsilon = 1e-12;
- ridders_step_shrink_factor = 2.0;
- }
-
// Numeric differentiation step size (multiplied by parameter block's
// order of magnitude). If parameters are close to zero, the step size
// is set to sqrt(machine_epsilon).
- double relative_step_size;
+ double relative_step_size = 1e-6;
// Initial step size for Ridders adaptive numeric differentiation (multiplied
// by parameter block's order of magnitude).
@@ -59,19 +53,19 @@ struct CERES_EXPORT NumericDiffOptions {
// Note: For Ridders' method to converge, the step size should be initialized
// to a value that is large enough to produce a significant change in the
// function. As the derivative is estimated, the step size decreases.
- double ridders_relative_initial_step_size;
+ double ridders_relative_initial_step_size = 1e-2;
// Maximal number of adaptive extrapolations (sampling) in Ridders' method.
- int max_num_ridders_extrapolations;
+ int max_num_ridders_extrapolations = 10;
// Convergence criterion on extrapolation error for Ridders adaptive
// differentiation. The available error estimation methods are defined in
// NumericDiffErrorType and set in the "ridders_error_method" field.
- double ridders_epsilon;
+ double ridders_epsilon = 1e-12;
// The factor in which to shrink the step size with each extrapolation in
// Ridders' method.
- double ridders_step_shrink_factor;
+ double ridders_step_shrink_factor = 2.0;
};
} // namespace ceres
diff --git a/extern/ceres/include/ceres/ordered_groups.h b/extern/ceres/include/ceres/ordered_groups.h
index aa1bd3a7da1..954663c97e6 100644
--- a/extern/ceres/include/ceres/ordered_groups.h
+++ b/extern/ceres/include/ceres/ordered_groups.h
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2019 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -33,7 +33,9 @@
#include <map>
#include <set>
+#include <unordered_map>
#include <vector>
+
#include "ceres/internal/port.h"
#include "glog/logging.h"
@@ -63,8 +65,7 @@ class OrderedGroups {
return false;
}
- typename std::map<T, int>::const_iterator it =
- element_to_group_.find(element);
+ auto it = element_to_group_.find(element);
if (it != element_to_group_.end()) {
if (it->second == group) {
// Element is already in the right group, nothing to do.
@@ -126,17 +127,14 @@ class OrderedGroups {
return;
}
- typename std::map<int, std::set<T> >::reverse_iterator it =
- group_to_elements_.rbegin();
- std::map<int, std::set<T> > new_group_to_elements;
+ auto it = group_to_elements_.rbegin();
+ std::map<int, std::set<T>> new_group_to_elements;
new_group_to_elements[it->first] = it->second;
int new_group_id = it->first + 1;
for (++it; it != group_to_elements_.rend(); ++it) {
- for (typename std::set<T>::const_iterator element_it = it->second.begin();
- element_it != it->second.end();
- ++element_it) {
- element_to_group_[*element_it] = new_group_id;
+ for (const auto& element : it->second) {
+ element_to_group_[element] = new_group_id;
}
new_group_to_elements[new_group_id] = it->second;
new_group_id++;
@@ -148,8 +146,7 @@ class OrderedGroups {
// Return the group id for the element. If the element is not a
// member of any group, return -1.
int GroupId(const T element) const {
- typename std::map<T, int>::const_iterator it =
- element_to_group_.find(element);
+ auto it = element_to_group_.find(element);
if (it == element_to_group_.end()) {
return -1;
}
@@ -157,27 +154,21 @@ class OrderedGroups {
}
bool IsMember(const T element) const {
- typename std::map<T, int>::const_iterator it =
- element_to_group_.find(element);
+ auto it = element_to_group_.find(element);
return (it != element_to_group_.end());
}
// This function always succeeds, i.e., implicitly there exists a
// group for every integer.
int GroupSize(const int group) const {
- typename std::map<int, std::set<T> >::const_iterator it =
- group_to_elements_.find(group);
- return (it == group_to_elements_.end()) ? 0 : it->second.size();
+ auto it = group_to_elements_.find(group);
+ return (it == group_to_elements_.end()) ? 0 : it->second.size();
}
- int NumElements() const {
- return element_to_group_.size();
- }
+ int NumElements() const { return element_to_group_.size(); }
// Number of groups with one or more elements.
- int NumGroups() const {
- return group_to_elements_.size();
- }
+ int NumGroups() const { return group_to_elements_.size(); }
// The first group with one or more elements. Calling this when
// there are no groups with non-zero elements will result in a
@@ -187,17 +178,15 @@ class OrderedGroups {
return group_to_elements_.begin()->first;
}
- const std::map<int, std::set<T> >& group_to_elements() const {
+ const std::map<int, std::set<T>>& group_to_elements() const {
return group_to_elements_;
}
- const std::map<T, int>& element_to_group() const {
- return element_to_group_;
- }
+ const std::map<T, int>& element_to_group() const { return element_to_group_; }
private:
- std::map<int, std::set<T> > group_to_elements_;
- std::map<T, int> element_to_group_;
+ std::map<int, std::set<T>> group_to_elements_;
+ std::unordered_map<T, int> element_to_group_;
};
// Typedef for the most commonly used version of OrderedGroups.
diff --git a/extern/ceres/include/ceres/problem.h b/extern/ceres/include/ceres/problem.h
index 27ed4ef15da..88f99663f65 100644
--- a/extern/ceres/include/ceres/problem.h
+++ b/extern/ceres/include/ceres/problem.h
@@ -34,22 +34,23 @@
#ifndef CERES_PUBLIC_PROBLEM_H_
#define CERES_PUBLIC_PROBLEM_H_
+#include <array>
#include <cstddef>
#include <map>
+#include <memory>
#include <set>
#include <vector>
-#include "glog/logging.h"
-#include "ceres/internal/macros.h"
+#include "ceres/context.h"
+#include "ceres/internal/disable_warnings.h"
#include "ceres/internal/port.h"
-#include "ceres/internal/scoped_ptr.h"
#include "ceres/types.h"
-#include "ceres/internal/disable_warnings.h"
-
+#include "glog/logging.h"
namespace ceres {
class CostFunction;
+class EvaluationCallback;
class LossFunction;
class LocalParameterization;
class Solver;
@@ -114,20 +115,13 @@ typedef internal::ResidualBlock* ResidualBlockId;
//
// Problem problem;
//
-// problem.AddResidualBlock(new MyUnaryCostFunction(...), x1);
-// problem.AddResidualBlock(new MyBinaryCostFunction(...), x2, x3);
+// problem.AddResidualBlock(new MyUnaryCostFunction(...), nullptr, x1);
+// problem.AddResidualBlock(new MyBinaryCostFunction(...), nullptr, x2, x3);
//
// Please see cost_function.h for details of the CostFunction object.
class CERES_EXPORT Problem {
public:
struct CERES_EXPORT Options {
- Options()
- : cost_function_ownership(TAKE_OWNERSHIP),
- loss_function_ownership(TAKE_OWNERSHIP),
- local_parameterization_ownership(TAKE_OWNERSHIP),
- enable_fast_removal(false),
- disable_all_safety_checks(false) {}
-
// 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
@@ -135,25 +129,25 @@ class CERES_EXPORT Problem {
// destruction. The destructor is careful to delete the pointers
// only once, since sharing cost/loss/parameterizations is
// allowed.
- Ownership cost_function_ownership;
- Ownership loss_function_ownership;
- Ownership local_parameterization_ownership;
+ Ownership cost_function_ownership = TAKE_OWNERSHIP;
+ Ownership loss_function_ownership = TAKE_OWNERSHIP;
+ Ownership local_parameterization_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
- // parameters or residuals from the problem occassionally, this might be
+ // parameters or residuals from the problem occasionally, this might be
// 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 additonal hash set per
+ // The increase in memory usage is twofold: 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;
+ 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
@@ -164,22 +158,51 @@ class CERES_EXPORT Problem {
//
// WARNING: Do not set this to true, unless you are absolutely sure of what
// you are doing.
- bool disable_all_safety_checks;
+ bool disable_all_safety_checks = false;
+
+ // A Ceres global context to use for solving this problem. This may help to
+ // reduce computation time as Ceres can reuse expensive objects to create.
+ // The context object can be nullptr, in which case Ceres may create one.
+ //
+ // 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
+ // EvaluationCallback::PrepareForEvaluation() before Ceres calls
+ // 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.
+ EvaluationCallback* evaluation_callback = nullptr;
};
// The default constructor is equivalent to the
// invocation Problem(Problem::Options()).
Problem();
explicit Problem(const Options& options);
+ Problem(Problem&&);
+ Problem& operator=(Problem&&);
+
+ Problem(const Problem&) = delete;
+ Problem& operator=(const Problem&) = delete;
~Problem();
// Add a residual block to the overall cost function. The cost
- // function carries with it information about the sizes of the
+ // 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
- // NULL, in which case the cost of the term is just the squared norm
+ // 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
@@ -208,59 +231,35 @@ class CERES_EXPORT Problem {
//
// Problem problem;
//
- // problem.AddResidualBlock(new MyUnaryCostFunction(...), NULL, x1);
- // problem.AddResidualBlock(new MyBinaryCostFunction(...), NULL, x2, x1);
+ // 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.
+ template <typename... Ts>
+ ResidualBlockId AddResidualBlock(CostFunction* cost_function,
+ LossFunction* loss_function,
+ double* x0,
+ Ts*... xs) {
+ const std::array<double*, sizeof...(Ts) + 1> parameter_blocks{{x0, xs...}};
+ return AddResidualBlock(cost_function,
+ loss_function,
+ parameter_blocks.data(),
+ static_cast<int>(parameter_blocks.size()));
+ }
+
+ // Add a residual block by providing a vector of parameter blocks.
ResidualBlockId AddResidualBlock(
CostFunction* cost_function,
LossFunction* loss_function,
const std::vector<double*>& parameter_blocks);
- // Convenience methods for adding residuals with a small number of
- // parameters. This is the common case. Instead of specifying the
- // parameter block arguments as a vector, list them as pointers.
- ResidualBlockId AddResidualBlock(CostFunction* cost_function,
- LossFunction* loss_function,
- double* x0);
- ResidualBlockId AddResidualBlock(CostFunction* cost_function,
- LossFunction* loss_function,
- double* x0, double* x1);
+ // Add a residual block by providing a pointer to the parameter block array
+ // and the number of parameter blocks.
ResidualBlockId AddResidualBlock(CostFunction* cost_function,
LossFunction* loss_function,
- double* x0, double* x1, double* x2);
- ResidualBlockId AddResidualBlock(CostFunction* cost_function,
- LossFunction* loss_function,
- double* x0, double* x1, double* x2,
- double* x3);
- ResidualBlockId AddResidualBlock(CostFunction* cost_function,
- LossFunction* loss_function,
- double* x0, double* x1, double* x2,
- double* x3, double* x4);
- ResidualBlockId AddResidualBlock(CostFunction* cost_function,
- LossFunction* loss_function,
- double* x0, double* x1, double* x2,
- double* x3, double* x4, double* x5);
- ResidualBlockId AddResidualBlock(CostFunction* cost_function,
- LossFunction* loss_function,
- double* x0, double* x1, double* x2,
- double* x3, double* x4, double* x5,
- double* x6);
- ResidualBlockId AddResidualBlock(CostFunction* cost_function,
- LossFunction* loss_function,
- double* x0, double* x1, double* x2,
- double* x3, double* x4, double* x5,
- double* x6, double* x7);
- ResidualBlockId AddResidualBlock(CostFunction* cost_function,
- LossFunction* loss_function,
- double* x0, double* x1, double* x2,
- double* x3, double* x4, double* x5,
- double* x6, double* x7, double* x8);
- ResidualBlockId AddResidualBlock(CostFunction* cost_function,
- LossFunction* loss_function,
- double* x0, double* x1, double* x2,
- double* x3, double* x4, double* x5,
- double* x6, double* x7, double* x8,
- double* x9);
+ 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
@@ -290,7 +289,7 @@ class CERES_EXPORT Problem {
// ordering, rendering the jacobian or residuals returned from the solver
// uninterpretable. If you depend on the evaluated jacobian, do not use
// remove! This may change in a future release.
- void RemoveParameterBlock(double* values);
+ void RemoveParameterBlock(const double* values);
// Remove a residual block from the problem. Any parameters that the residual
// block depends on are not removed. The cost and loss functions for the
@@ -304,32 +303,43 @@ class CERES_EXPORT Problem {
void RemoveResidualBlock(ResidualBlockId residual_block);
// Hold the indicated parameter block constant during optimization.
- void SetParameterBlockConstant(double* values);
+ void SetParameterBlockConstant(const double* values);
// 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.
- bool IsParameterBlockConstant(double* values) const;
+ // 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.
+ 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. The local parameterization can only
- // be set once per parameter, and cannot be changed once set.
+ // parameterizations only once. Calling SetParameterization with
+ // nullptr will clear any previously set parameterization.
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 NULL is returned.
- const LocalParameterization* GetParameterization(double* values) const;
+ // associated then nullptr is returned.
+ const LocalParameterization* GetParameterization(const double* values) const;
- // Set the lower/upper bound for the parameter with position "index".
+ // 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().
+ double GetParameterLowerBound(const double* values, int index) const;
+ double GetParameterUpperBound(const double* values, int index) const;
+
// Number of parameter blocks in the problem. Always equals
// parameter_blocks().size() and parameter_block_sizes().size().
int NumParameterBlocks() const;
@@ -376,7 +386,7 @@ class CERES_EXPORT Problem {
const CostFunction* GetCostFunctionForResidualBlock(
const ResidualBlockId residual_block) const;
- // Get the LossFunction for the given residual block. Returns NULL
+ // Get the LossFunction for the given residual block. Returns nullptr
// if no loss function is associated with this residual block.
const LossFunction* GetLossFunctionForResidualBlock(
const ResidualBlockId residual_block) const;
@@ -393,11 +403,6 @@ class CERES_EXPORT Problem {
// Options struct to control Problem::Evaluate.
struct EvaluateOptions {
- EvaluateOptions()
- : apply_loss_function(true),
- num_threads(1) {
- }
-
// 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
@@ -430,12 +435,12 @@ class CERES_EXPORT Problem {
// 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;
+ bool apply_loss_function = true;
- int num_threads;
+ int num_threads = 1;
};
- // Evaluate Problem. Any of the output pointers can be NULL. Which
+ // 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.
//
@@ -445,16 +450,16 @@ class CERES_EXPORT Problem {
//
// Problem problem;
// double x = 1;
- // problem.AddResidualBlock(new MyCostFunction, NULL, &x);
+ // problem.AddResidualBlock(new MyCostFunction, nullptr, &x);
//
// double cost = 0.0;
- // problem.Evaluate(Problem::EvaluateOptions(), &cost, NULL, NULL, NULL);
+ // 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
//
- // x = 2;
- // problem.Evaluate(Problem::EvaluateOptions(), &cost, NULL, NULL, NULL);
+ // x = 2;
+ // problem.Evaluate(Problem::EvaluateOptions(), &cost, nullptr, nullptr, nullptr);
//
// is the way to do so.
//
@@ -468,17 +473,63 @@ class CERES_EXPORT Problem {
// 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 everytime
+ // 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.
+ //
+ // 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.
+ //
+ // 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 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.
+ //
+ // apply_loss_function as the name implies allows the user to switch
+ // the application of the loss function on and off.
+ //
+ // WARNING: If an EvaluationCallback is associated with the problem
+ // then it is the user's responsibility to call it before calling
+ // this method.
+ //
+ // This is because, if the user calls this method multiple times, we
+ // cannot tell if the underlying parameter blocks have changed
+ // between calls or not. So if EvaluateResidualBlock was responsible
+ // for calling the EvaluationCallback, it will have to do it
+ // everytime it is called. Which makes the common case where the
+ // parameter blocks do not change, inefficient. So we leave it to
+ // the user to call the EvaluationCallback as needed.
+ bool EvaluateResidualBlock(ResidualBlockId residual_block_id,
+ bool apply_loss_function,
+ double* cost,
+ double* residuals,
+ double** jacobians) const;
+
private:
friend class Solver;
friend class Covariance;
- internal::scoped_ptr<internal::ProblemImpl> problem_impl_;
- CERES_DISALLOW_COPY_AND_ASSIGN(Problem);
+ std::unique_ptr<internal::ProblemImpl> impl_;
};
} // namespace ceres
diff --git a/extern/ceres/include/ceres/rotation.h b/extern/ceres/include/ceres/rotation.h
index b6a06f772c4..7d5c8ef1fb2 100644
--- a/extern/ceres/include/ceres/rotation.h
+++ b/extern/ceres/include/ceres/rotation.h
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2019 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -49,6 +49,8 @@
#include <cmath>
#include <limits>
+#include "glog/logging.h"
+
namespace ceres {
// Trivial wrapper to index linear arrays as matrices, given a fixed
@@ -82,47 +84,44 @@ MatrixAdapter<T, 3, 1> RowMajorAdapter3x3(T* pointer);
// and quaternion is a 4-tuple that will contain the resulting quaternion.
// The implementation may be used with auto-differentiation up to the first
// derivative, higher derivatives may have unexpected results near the origin.
-template<typename T>
+template <typename T>
void AngleAxisToQuaternion(const T* angle_axis, T* quaternion);
// Convert a quaternion to the equivalent combined axis-angle representation.
// The value quaternion must be a unit quaternion - it is not normalized first,
// and angle_axis will be filled with a value whose norm is the angle of
// rotation in radians, and whose direction is the axis of rotation.
-// The implemention may be used with auto-differentiation up to the first
+// The implementation may be used with auto-differentiation up to the first
// derivative, higher derivatives may have unexpected results near the origin.
-template<typename T>
+template <typename T>
void QuaternionToAngleAxis(const T* quaternion, T* angle_axis);
// Conversions between 3x3 rotation matrix (in column major order) and
-// quaternion rotation representations. Templated for use with
+// quaternion rotation representations. Templated for use with
// autodifferentiation.
template <typename T>
void RotationMatrixToQuaternion(const T* R, T* quaternion);
template <typename T, int row_stride, int col_stride>
void RotationMatrixToQuaternion(
- const MatrixAdapter<const T, row_stride, col_stride>& R,
- T* quaternion);
+ const MatrixAdapter<const T, row_stride, col_stride>& R, T* quaternion);
// Conversions between 3x3 rotation matrix (in column major order) and
-// axis-angle rotation representations. Templated for use with
+// axis-angle rotation representations. Templated for use with
// autodifferentiation.
template <typename T>
void RotationMatrixToAngleAxis(const T* R, T* angle_axis);
template <typename T, int row_stride, int col_stride>
void RotationMatrixToAngleAxis(
- const MatrixAdapter<const T, row_stride, col_stride>& R,
- T* angle_axis);
+ const MatrixAdapter<const T, row_stride, col_stride>& R, T* angle_axis);
template <typename T>
void AngleAxisToRotationMatrix(const T* angle_axis, T* R);
template <typename T, int row_stride, int col_stride>
void AngleAxisToRotationMatrix(
- const T* angle_axis,
- const MatrixAdapter<T, row_stride, col_stride>& R);
+ const T* angle_axis, const MatrixAdapter<T, row_stride, col_stride>& R);
// Conversions between 3x3 rotation matrix (in row major order) and
// Euler angle (in degrees) rotation representations.
@@ -135,8 +134,7 @@ void EulerAnglesToRotationMatrix(const T* euler, int row_stride, T* R);
template <typename T, int row_stride, int col_stride>
void EulerAnglesToRotationMatrix(
- const T* euler,
- const MatrixAdapter<T, row_stride, col_stride>& R);
+ const T* euler, const MatrixAdapter<T, row_stride, col_stride>& R);
// Convert a 4-vector to a 3x3 scaled rotation matrix.
//
@@ -157,25 +155,23 @@ void EulerAnglesToRotationMatrix(
// such that det(Q) = 1 and Q*Q' = I
//
// WARNING: The rotation matrix is ROW MAJOR
-template <typename T> inline
-void QuaternionToScaledRotation(const T q[4], T R[3 * 3]);
+template <typename T>
+inline void QuaternionToScaledRotation(const T q[4], T R[3 * 3]);
-template <typename T, int row_stride, int col_stride> inline
-void QuaternionToScaledRotation(
- const T q[4],
- const MatrixAdapter<T, row_stride, col_stride>& R);
+template <typename T, int row_stride, int col_stride>
+inline void QuaternionToScaledRotation(
+ const T q[4], const MatrixAdapter<T, row_stride, col_stride>& R);
// Same as above except that the rotation matrix is normalized by the
// Frobenius norm, so that R * R' = I (and det(R) = 1).
//
// WARNING: The rotation matrix is ROW MAJOR
-template <typename T> inline
-void QuaternionToRotation(const T q[4], T R[3 * 3]);
+template <typename T>
+inline void QuaternionToRotation(const T q[4], T R[3 * 3]);
-template <typename T, int row_stride, int col_stride> inline
-void QuaternionToRotation(
- const T q[4],
- const MatrixAdapter<T, row_stride, col_stride>& R);
+template <typename T, int row_stride, int col_stride>
+inline void QuaternionToRotation(
+ const T q[4], const MatrixAdapter<T, row_stride, col_stride>& R);
// Rotates a point pt by a quaternion q:
//
@@ -185,37 +181,54 @@ void QuaternionToRotation(
// write the transform as (something)*pt + pt, as is clear from the
// formula below. If you pass in a quaternion with |q|^2 = 2 then you
// WILL NOT get back 2 times the result you get for a unit quaternion.
-template <typename T> inline
-void UnitQuaternionRotatePoint(const T q[4], const T pt[3], T result[3]);
+//
+// Inplace rotation is not supported. pt and result must point to different
+// memory locations, otherwise the result will be undefined.
+template <typename T>
+inline void UnitQuaternionRotatePoint(const T q[4], const T pt[3], T result[3]);
// With this function you do not need to assume that q has unit norm.
// It does assume that the norm is non-zero.
-template <typename T> inline
-void QuaternionRotatePoint(const T q[4], const T pt[3], T result[3]);
+//
+// Inplace rotation is not supported. pt and result must point to different
+// memory locations, otherwise the result will be undefined.
+template <typename T>
+inline void QuaternionRotatePoint(const T q[4], const T pt[3], T result[3]);
// zw = z * w, where * is the Quaternion product between 4 vectors.
-template<typename T> inline
-void QuaternionProduct(const T z[4], const T w[4], T zw[4]);
+//
+// Inplace quaternion product is not supported. The resulting quaternion zw must
+// not share the memory with the input quaternion z and w, otherwise the result
+// will be undefined.
+template <typename T>
+inline void QuaternionProduct(const T z[4], const T w[4], T zw[4]);
// xy = x cross y;
-template<typename T> inline
-void CrossProduct(const T x[3], const T y[3], T x_cross_y[3]);
+//
+// Inplace cross product is not supported. The resulting vector x_cross_y must
+// not share the memory with the input vectors x and y, otherwise the result
+// will be undefined.
+template <typename T>
+inline void CrossProduct(const T x[3], const T y[3], T x_cross_y[3]);
-template<typename T> inline
-T DotProduct(const T x[3], const T y[3]);
+template <typename T>
+inline T DotProduct(const T x[3], const T y[3]);
// y = R(angle_axis) * x;
-template<typename T> inline
-void AngleAxisRotatePoint(const T angle_axis[3], const T pt[3], T result[3]);
+//
+// Inplace rotation is not supported. pt and result must point to different
+// memory locations, otherwise the result will be undefined.
+template <typename T>
+inline void AngleAxisRotatePoint(const T angle_axis[3],
+ const T pt[3],
+ T result[3]);
// --- IMPLEMENTATION
-template<typename T, int row_stride, int col_stride>
+template <typename T, int row_stride, int col_stride>
struct MatrixAdapter {
T* pointer_;
- explicit MatrixAdapter(T* pointer)
- : pointer_(pointer)
- {}
+ explicit MatrixAdapter(T* pointer) : pointer_(pointer) {}
T& operator()(int r, int c) const {
return pointer_[r * row_stride + c * col_stride];
@@ -232,7 +245,7 @@ MatrixAdapter<T, 3, 1> RowMajorAdapter3x3(T* pointer) {
return MatrixAdapter<T, 3, 1>(pointer);
}
-template<typename T>
+template <typename T>
inline void AngleAxisToQuaternion(const T* angle_axis, T* quaternion) {
const T& a0 = angle_axis[0];
const T& a1 = angle_axis[1];
@@ -261,7 +274,7 @@ inline void AngleAxisToQuaternion(const T* angle_axis, T* quaternion) {
}
}
-template<typename T>
+template <typename T>
inline void QuaternionToAngleAxis(const T* quaternion, T* angle_axis) {
const T& q1 = quaternion[1];
const T& q2 = quaternion[2];
@@ -288,9 +301,8 @@ inline void QuaternionToAngleAxis(const T* quaternion, T* angle_axis) {
// = atan(-sin(theta), -cos(theta))
//
const T two_theta =
- T(2.0) * ((cos_theta < 0.0)
- ? atan2(-sin_theta, -cos_theta)
- : atan2(sin_theta, cos_theta));
+ T(2.0) * ((cos_theta < T(0.0)) ? atan2(-sin_theta, -cos_theta)
+ : atan2(sin_theta, cos_theta));
const T k = two_theta / sin_theta;
angle_axis[0] = q1 * k;
angle_axis[1] = q2 * k;
@@ -316,8 +328,7 @@ void RotationMatrixToQuaternion(const T* R, T* angle_axis) {
// Ken Shoemake, 1987 SIGGRAPH course notes
template <typename T, int row_stride, int col_stride>
void RotationMatrixToQuaternion(
- const MatrixAdapter<const T, row_stride, col_stride>& R,
- T* quaternion) {
+ const MatrixAdapter<const T, row_stride, col_stride>& R, T* quaternion) {
const T trace = R(0, 0) + R(1, 1) + R(2, 2);
if (trace >= 0.0) {
T t = sqrt(trace + T(1.0));
@@ -359,8 +370,7 @@ inline void RotationMatrixToAngleAxis(const T* R, T* angle_axis) {
template <typename T, int row_stride, int col_stride>
void RotationMatrixToAngleAxis(
- const MatrixAdapter<const T, row_stride, col_stride>& R,
- T* angle_axis) {
+ const MatrixAdapter<const T, row_stride, col_stride>& R, T* angle_axis) {
T quaternion[4];
RotationMatrixToQuaternion(R, quaternion);
QuaternionToAngleAxis(quaternion, angle_axis);
@@ -374,8 +384,7 @@ inline void AngleAxisToRotationMatrix(const T* angle_axis, T* R) {
template <typename T, int row_stride, int col_stride>
void AngleAxisToRotationMatrix(
- const T* angle_axis,
- const MatrixAdapter<T, row_stride, col_stride>& R) {
+ const T* angle_axis, const MatrixAdapter<T, row_stride, col_stride>& R) {
static const T kOne = T(1.0);
const T theta2 = DotProduct(angle_axis, angle_axis);
if (theta2 > T(std::numeric_limits<double>::epsilon())) {
@@ -390,6 +399,7 @@ void AngleAxisToRotationMatrix(
const T costheta = cos(theta);
const T sintheta = sin(theta);
+ // clang-format off
R(0, 0) = costheta + wx*wx*(kOne - costheta);
R(1, 0) = wz*sintheta + wx*wy*(kOne - costheta);
R(2, 0) = -wy*sintheta + wx*wz*(kOne - costheta);
@@ -399,15 +409,16 @@ void AngleAxisToRotationMatrix(
R(0, 2) = wy*sintheta + wx*wz*(kOne - costheta);
R(1, 2) = -wx*sintheta + wy*wz*(kOne - costheta);
R(2, 2) = costheta + wz*wz*(kOne - costheta);
+ // clang-format on
} else {
// Near zero, we switch to using the first order Taylor expansion.
- R(0, 0) = kOne;
- R(1, 0) = angle_axis[2];
+ R(0, 0) = kOne;
+ R(1, 0) = angle_axis[2];
R(2, 0) = -angle_axis[1];
R(0, 1) = -angle_axis[2];
- R(1, 1) = kOne;
- R(2, 1) = angle_axis[0];
- R(0, 2) = angle_axis[1];
+ R(1, 1) = kOne;
+ R(2, 1) = angle_axis[0];
+ R(0, 2) = angle_axis[1];
R(1, 2) = -angle_axis[0];
R(2, 2) = kOne;
}
@@ -422,8 +433,7 @@ inline void EulerAnglesToRotationMatrix(const T* euler,
template <typename T, int row_stride, int col_stride>
void EulerAnglesToRotationMatrix(
- const T* euler,
- const MatrixAdapter<T, row_stride, col_stride>& R) {
+ const T* euler, const MatrixAdapter<T, row_stride, col_stride>& R) {
const double kPi = 3.14159265358979323846;
const T degrees_to_radians(kPi / 180.0);
@@ -438,28 +448,27 @@ void EulerAnglesToRotationMatrix(
const T c3 = cos(pitch);
const T s3 = sin(pitch);
- R(0, 0) = c1*c2;
- R(0, 1) = -s1*c3 + c1*s2*s3;
- R(0, 2) = s1*s3 + c1*s2*c3;
+ R(0, 0) = c1 * c2;
+ R(0, 1) = -s1 * c3 + c1 * s2 * s3;
+ R(0, 2) = s1 * s3 + c1 * s2 * c3;
- R(1, 0) = s1*c2;
- R(1, 1) = c1*c3 + s1*s2*s3;
- R(1, 2) = -c1*s3 + s1*s2*c3;
+ R(1, 0) = s1 * c2;
+ R(1, 1) = c1 * c3 + s1 * s2 * s3;
+ R(1, 2) = -c1 * s3 + s1 * s2 * c3;
R(2, 0) = -s2;
- R(2, 1) = c2*s3;
- R(2, 2) = c2*c3;
+ R(2, 1) = c2 * s3;
+ R(2, 2) = c2 * c3;
}
-template <typename T> inline
-void QuaternionToScaledRotation(const T q[4], T R[3 * 3]) {
+template <typename T>
+inline void QuaternionToScaledRotation(const T q[4], T R[3 * 3]) {
QuaternionToScaledRotation(q, RowMajorAdapter3x3(R));
}
-template <typename T, int row_stride, int col_stride> inline
-void QuaternionToScaledRotation(
- const T q[4],
- const MatrixAdapter<T, row_stride, col_stride>& R) {
+template <typename T, int row_stride, int col_stride>
+inline void QuaternionToScaledRotation(
+ const T q[4], const MatrixAdapter<T, row_stride, col_stride>& R) {
// Make convenient names for elements of q.
T a = q[0];
T b = q[1];
@@ -478,22 +487,24 @@ void QuaternionToScaledRotation(
T cd = c * d;
T dd = d * d;
- R(0, 0) = aa + bb - cc - dd; R(0, 1) = T(2) * (bc - ad); R(0, 2) = T(2) * (ac + bd); // NOLINT
- R(1, 0) = T(2) * (ad + bc); R(1, 1) = aa - bb + cc - dd; R(1, 2) = T(2) * (cd - ab); // NOLINT
- R(2, 0) = T(2) * (bd - ac); R(2, 1) = T(2) * (ab + cd); R(2, 2) = aa - bb - cc + dd; // NOLINT
+ // clang-format off
+ R(0, 0) = aa + bb - cc - dd; R(0, 1) = T(2) * (bc - ad); R(0, 2) = T(2) * (ac + bd);
+ R(1, 0) = T(2) * (ad + bc); R(1, 1) = aa - bb + cc - dd; R(1, 2) = T(2) * (cd - ab);
+ R(2, 0) = T(2) * (bd - ac); R(2, 1) = T(2) * (ab + cd); R(2, 2) = aa - bb - cc + dd;
+ // clang-format on
}
-template <typename T> inline
-void QuaternionToRotation(const T q[4], T R[3 * 3]) {
+template <typename T>
+inline void QuaternionToRotation(const T q[4], T R[3 * 3]) {
QuaternionToRotation(q, RowMajorAdapter3x3(R));
}
-template <typename T, int row_stride, int col_stride> inline
-void QuaternionToRotation(const T q[4],
- const MatrixAdapter<T, row_stride, col_stride>& R) {
+template <typename T, int row_stride, int col_stride>
+inline void QuaternionToRotation(
+ const T q[4], const MatrixAdapter<T, row_stride, col_stride>& R) {
QuaternionToScaledRotation(q, R);
- T normalizer = q[0]*q[0] + q[1]*q[1] + q[2]*q[2] + q[3]*q[3];
+ T normalizer = q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3];
normalizer = T(1) / normalizer;
for (int i = 0; i < 3; ++i) {
@@ -503,8 +514,13 @@ void QuaternionToRotation(const T q[4],
}
}
-template <typename T> inline
-void UnitQuaternionRotatePoint(const T q[4], const T pt[3], T result[3]) {
+template <typename T>
+inline void UnitQuaternionRotatePoint(const T q[4],
+ const T pt[3],
+ T result[3]) {
+ 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];
@@ -517,50 +533,63 @@ void UnitQuaternionRotatePoint(const T q[4], const T pt[3], T result[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
+ // clang-format on
}
-template <typename T> inline
-void QuaternionRotatePoint(const T q[4], const T pt[3], T result[3]) {
+template <typename T>
+inline void QuaternionRotatePoint(const T q[4], const T pt[3], T result[3]) {
+ DCHECK_NE(pt, result) << "Inplace rotation is not supported.";
+
// 'scale' is 1 / norm(q).
- const T scale = T(1) / sqrt(q[0] * q[0] +
- q[1] * q[1] +
- q[2] * q[2] +
- q[3] * q[3]);
+ const T scale =
+ T(1) / sqrt(q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3]);
// Make unit-norm version of q.
const T unit[4] = {
- scale * q[0],
- scale * q[1],
- scale * q[2],
- scale * q[3],
+ scale * q[0],
+ scale * q[1],
+ scale * q[2],
+ scale * q[3],
};
UnitQuaternionRotatePoint(unit, pt, result);
}
-template<typename T> inline
-void QuaternionProduct(const T z[4], const T w[4], T zw[4]) {
+template <typename T>
+inline void QuaternionProduct(const T z[4], const T w[4], T zw[4]) {
+ DCHECK_NE(z, zw) << "Inplace quaternion product is not supported.";
+ DCHECK_NE(w, zw) << "Inplace quaternion product is not supported.";
+
+ // clang-format off
zw[0] = z[0] * w[0] - z[1] * w[1] - z[2] * w[2] - z[3] * w[3];
zw[1] = z[0] * w[1] + z[1] * w[0] + z[2] * w[3] - z[3] * w[2];
zw[2] = z[0] * w[2] - z[1] * w[3] + z[2] * w[0] + z[3] * w[1];
zw[3] = z[0] * w[3] + z[1] * w[2] - z[2] * w[1] + z[3] * w[0];
+ // clang-format on
}
// xy = x cross y;
-template<typename T> inline
-void CrossProduct(const T x[3], const T y[3], T x_cross_y[3]) {
+template <typename T>
+inline void CrossProduct(const T x[3], const T y[3], T x_cross_y[3]) {
+ DCHECK_NE(x, x_cross_y) << "Inplace cross product is not supported.";
+ DCHECK_NE(y, x_cross_y) << "Inplace cross product is not supported.";
+
x_cross_y[0] = x[1] * y[2] - x[2] * y[1];
x_cross_y[1] = x[2] * y[0] - x[0] * y[2];
x_cross_y[2] = x[0] * y[1] - x[1] * y[0];
}
-template<typename T> inline
-T DotProduct(const T x[3], const T y[3]) {
+template <typename T>
+inline T DotProduct(const T x[3], const T y[3]) {
return (x[0] * y[0] + x[1] * y[1] + x[2] * y[2]);
}
-template<typename T> inline
-void AngleAxisRotatePoint(const T angle_axis[3], const T pt[3], T result[3]) {
+template <typename T>
+inline void AngleAxisRotatePoint(const T angle_axis[3],
+ const T pt[3],
+ T result[3]) {
+ DCHECK_NE(pt, result) << "Inplace rotation is not supported.";
+
const T theta2 = DotProduct(angle_axis, angle_axis);
if (theta2 > T(std::numeric_limits<double>::epsilon())) {
// Away from zero, use the rodriguez formula
@@ -576,17 +605,17 @@ void AngleAxisRotatePoint(const T angle_axis[3], const T pt[3], T result[3]) {
const T theta = sqrt(theta2);
const T costheta = cos(theta);
const T sintheta = sin(theta);
- const T theta_inverse = 1.0 / theta;
+ const T theta_inverse = T(1.0) / theta;
- const T w[3] = { angle_axis[0] * theta_inverse,
- angle_axis[1] * theta_inverse,
- angle_axis[2] * theta_inverse };
+ const T w[3] = {angle_axis[0] * theta_inverse,
+ angle_axis[1] * theta_inverse,
+ angle_axis[2] * theta_inverse};
// Explicitly inlined evaluation of the cross product for
// performance reasons.
- const T w_cross_pt[3] = { w[1] * pt[2] - w[2] * pt[1],
- w[2] * pt[0] - w[0] * pt[2],
- w[0] * pt[1] - w[1] * pt[0] };
+ const T w_cross_pt[3] = {w[1] * pt[2] - w[2] * pt[1],
+ w[2] * pt[0] - w[0] * pt[2],
+ w[0] * pt[1] - w[1] * pt[0]};
const T tmp =
(w[0] * pt[0] + w[1] * pt[1] + w[2] * pt[2]) * (T(1.0) - costheta);
@@ -611,9 +640,9 @@ void AngleAxisRotatePoint(const T angle_axis[3], const T pt[3], T result[3]) {
//
// Explicitly inlined evaluation of the cross product for
// performance reasons.
- const T w_cross_pt[3] = { angle_axis[1] * pt[2] - angle_axis[2] * pt[1],
- angle_axis[2] * pt[0] - angle_axis[0] * pt[2],
- angle_axis[0] * pt[1] - angle_axis[1] * pt[0] };
+ const T w_cross_pt[3] = {angle_axis[1] * pt[2] - angle_axis[2] * pt[1],
+ angle_axis[2] * pt[0] - angle_axis[0] * pt[2],
+ angle_axis[0] * pt[1] - angle_axis[1] * pt[0]};
result[0] = pt[0] + w_cross_pt[0];
result[1] = pt[1] + w_cross_pt[1];
diff --git a/extern/ceres/include/ceres/sized_cost_function.h b/extern/ceres/include/ceres/sized_cost_function.h
index b10421e81be..8e92f1b796c 100644
--- a/extern/ceres/include/ceres/sized_cost_function.h
+++ b/extern/ceres/include/ceres/sized_cost_function.h
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2019 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -38,55 +38,30 @@
#ifndef CERES_PUBLIC_SIZED_COST_FUNCTION_H_
#define CERES_PUBLIC_SIZED_COST_FUNCTION_H_
-#include "ceres/types.h"
#include "ceres/cost_function.h"
+#include "ceres/types.h"
#include "glog/logging.h"
+#include "internal/parameter_dims.h"
namespace ceres {
-template<int kNumResiduals,
- int N0 = 0, int N1 = 0, int N2 = 0, int N3 = 0, int N4 = 0,
- int N5 = 0, int N6 = 0, int N7 = 0, int N8 = 0, int N9 = 0>
+template <int kNumResiduals, int... Ns>
class SizedCostFunction : public CostFunction {
public:
- SizedCostFunction() {
- CHECK(kNumResiduals > 0 || kNumResiduals == DYNAMIC)
- << "Cost functions must have at least one residual block.";
+ static_assert(kNumResiduals > 0 || kNumResiduals == DYNAMIC,
+ "Cost functions must have at least one residual block.");
+ static_assert(internal::StaticParameterDims<Ns...>::kIsValid,
+ "Invalid parameter block dimension detected. Each parameter "
+ "block dimension must be bigger than zero.");
- // This block breaks the 80 column rule to keep it somewhat readable.
- CHECK((!N1 && !N2 && !N3 && !N4 && !N5 && !N6 && !N7 && !N8 && !N9) ||
- ((N1 > 0) && !N2 && !N3 && !N4 && !N5 && !N6 && !N7 && !N8 && !N9) ||
- ((N1 > 0) && (N2 > 0) && !N3 && !N4 && !N5 && !N6 && !N7 && !N8 && !N9) || // NOLINT
- ((N1 > 0) && (N2 > 0) && (N3 > 0) && !N4 && !N5 && !N6 && !N7 && !N8 && !N9) || // NOLINT
- ((N1 > 0) && (N2 > 0) && (N3 > 0) && (N4 > 0) && !N5 && !N6 && !N7 && !N8 && !N9) || // NOLINT
- ((N1 > 0) && (N2 > 0) && (N3 > 0) && (N4 > 0) && (N5 > 0) && !N6 && !N7 && !N8 && !N9) || // NOLINT
- ((N1 > 0) && (N2 > 0) && (N3 > 0) && (N4 > 0) && (N5 > 0) && (N6 > 0) && !N7 && !N8 && !N9) || // NOLINT
- ((N1 > 0) && (N2 > 0) && (N3 > 0) && (N4 > 0) && (N5 > 0) && (N6 > 0) && (N7 > 0) && !N8 && !N9) || // NOLINT
- ((N1 > 0) && (N2 > 0) && (N3 > 0) && (N4 > 0) && (N5 > 0) && (N6 > 0) && (N7 > 0) && (N8 > 0) && !N9) || // NOLINT
- ((N1 > 0) && (N2 > 0) && (N3 > 0) && (N4 > 0) && (N5 > 0) && (N6 > 0) && (N7 > 0) && (N8 > 0) && (N9 > 0))) // NOLINT
- << "Zero block cannot precede a non-zero block. Block sizes are "
- << "(ignore trailing 0s): " << N0 << ", " << N1 << ", " << N2 << ", "
- << N3 << ", " << N4 << ", " << N5 << ", " << N6 << ", " << N7 << ", "
- << N8 << ", " << N9;
+ using ParameterDims = internal::StaticParameterDims<Ns...>;
+ SizedCostFunction() {
set_num_residuals(kNumResiduals);
-
-#define CERES_ADD_PARAMETER_BLOCK(N) \
- if (N) mutable_parameter_block_sizes()->push_back(N);
- CERES_ADD_PARAMETER_BLOCK(N0);
- CERES_ADD_PARAMETER_BLOCK(N1);
- CERES_ADD_PARAMETER_BLOCK(N2);
- CERES_ADD_PARAMETER_BLOCK(N3);
- CERES_ADD_PARAMETER_BLOCK(N4);
- CERES_ADD_PARAMETER_BLOCK(N5);
- CERES_ADD_PARAMETER_BLOCK(N6);
- CERES_ADD_PARAMETER_BLOCK(N7);
- CERES_ADD_PARAMETER_BLOCK(N8);
- CERES_ADD_PARAMETER_BLOCK(N9);
-#undef CERES_ADD_PARAMETER_BLOCK
+ *mutable_parameter_block_sizes() = std::vector<int32_t>{Ns...};
}
- virtual ~SizedCostFunction() { }
+ virtual ~SizedCostFunction() {}
// Subclasses must implement Evaluate().
};
diff --git a/extern/ceres/include/ceres/solver.h b/extern/ceres/include/ceres/solver.h
index 0d77d242dfe..62631744fe2 100644
--- a/extern/ceres/include/ceres/solver.h
+++ b/extern/ceres/include/ceres/solver.h
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2019 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -32,20 +32,21 @@
#define CERES_PUBLIC_SOLVER_H_
#include <cmath>
+#include <memory>
#include <string>
+#include <unordered_set>
#include <vector>
+
#include "ceres/crs_matrix.h"
-#include "ceres/internal/macros.h"
+#include "ceres/internal/disable_warnings.h"
#include "ceres/internal/port.h"
#include "ceres/iteration_callback.h"
#include "ceres/ordered_groups.h"
+#include "ceres/problem.h"
#include "ceres/types.h"
-#include "ceres/internal/disable_warnings.h"
namespace ceres {
-class Problem;
-
// Interface for non-linear least squares solvers.
class CERES_EXPORT Solver {
public:
@@ -57,87 +58,6 @@ class CERES_EXPORT Solver {
//
// The constants are defined inside types.h
struct CERES_EXPORT Options {
- // Default constructor that sets up a generic sparse problem.
- Options() {
- minimizer_type = TRUST_REGION;
- line_search_direction_type = LBFGS;
- line_search_type = WOLFE;
- nonlinear_conjugate_gradient_type = FLETCHER_REEVES;
- max_lbfgs_rank = 20;
- use_approximate_eigenvalue_bfgs_scaling = false;
- line_search_interpolation_type = CUBIC;
- min_line_search_step_size = 1e-9;
- line_search_sufficient_function_decrease = 1e-4;
- max_line_search_step_contraction = 1e-3;
- min_line_search_step_contraction = 0.6;
- max_num_line_search_step_size_iterations = 20;
- max_num_line_search_direction_restarts = 5;
- line_search_sufficient_curvature_decrease = 0.9;
- max_line_search_step_expansion = 10.0;
- trust_region_strategy_type = LEVENBERG_MARQUARDT;
- dogleg_type = TRADITIONAL_DOGLEG;
- use_nonmonotonic_steps = false;
- max_consecutive_nonmonotonic_steps = 5;
- max_num_iterations = 50;
- max_solver_time_in_seconds = 1e9;
- num_threads = 1;
- initial_trust_region_radius = 1e4;
- max_trust_region_radius = 1e16;
- min_trust_region_radius = 1e-32;
- min_relative_decrease = 1e-3;
- min_lm_diagonal = 1e-6;
- max_lm_diagonal = 1e32;
- max_num_consecutive_invalid_steps = 5;
- function_tolerance = 1e-6;
- gradient_tolerance = 1e-10;
- parameter_tolerance = 1e-8;
-
-#if defined(CERES_NO_SUITESPARSE) && defined(CERES_NO_CXSPARSE) && !defined(CERES_ENABLE_LGPL_CODE) // NOLINT
- linear_solver_type = DENSE_QR;
-#else
- linear_solver_type = SPARSE_NORMAL_CHOLESKY;
-#endif
-
- preconditioner_type = JACOBI;
- visibility_clustering_type = CANONICAL_VIEWS;
- dense_linear_algebra_library_type = EIGEN;
-
- // Choose a default sparse linear algebra library in the order:
- //
- // SUITE_SPARSE > CX_SPARSE > EIGEN_SPARSE > NO_SPARSE
- sparse_linear_algebra_library_type = NO_SPARSE;
-#if !defined(CERES_NO_SUITESPARSE)
- sparse_linear_algebra_library_type = SUITE_SPARSE;
-#else
- #if !defined(CERES_NO_CXSPARSE)
- sparse_linear_algebra_library_type = CX_SPARSE;
- #else
- #if defined(CERES_USE_EIGEN_SPARSE)
- sparse_linear_algebra_library_type = EIGEN_SPARSE;
- #endif
- #endif
-#endif
-
- num_linear_solver_threads = 1;
- use_explicit_schur_complement = false;
- use_postordering = false;
- dynamic_sparsity = false;
- min_linear_solver_iterations = 0;
- max_linear_solver_iterations = 500;
- eta = 1e-1;
- jacobi_scaling = true;
- use_inner_iterations = false;
- inner_iteration_tolerance = 1e-3;
- logging_type = PER_MINIMIZER_ITERATION;
- minimizer_progress_to_stdout = false;
- trust_region_problem_dump_directory = "/tmp";
- trust_region_problem_dump_format_type = TEXTFILE;
- check_gradients = false;
- gradient_check_relative_precision = 1e-8;
- gradient_check_numeric_derivative_relative_step_size = 1e-6;
- update_state_every_iteration = false;
- }
-
// Returns true if the options struct has a valid
// configuration. Returns false otherwise, and fills in *error
// with a message describing the problem.
@@ -157,7 +77,7 @@ class CERES_EXPORT Solver {
// exactly or inexactly.
//
// 2. The trust region approach approximates the objective
- // function using using a model function (often a quadratic) over
+ // function using a model function (often a quadratic) over
// a subset of the search space known as the trust region. If the
// model function succeeds in minimizing the true objective
// function the trust region is expanded; conversely, otherwise it
@@ -168,11 +88,12 @@ class CERES_EXPORT Solver {
// trust region methods first choose a step size (the size of the
// trust region) and then a step direction while line search methods
// first choose a step direction and then a step size.
- MinimizerType minimizer_type;
+ MinimizerType minimizer_type = TRUST_REGION;
- LineSearchDirectionType line_search_direction_type;
- LineSearchType line_search_type;
- NonlinearConjugateGradientType nonlinear_conjugate_gradient_type;
+ LineSearchDirectionType line_search_direction_type = LBFGS;
+ LineSearchType line_search_type = WOLFE;
+ NonlinearConjugateGradientType nonlinear_conjugate_gradient_type =
+ FLETCHER_REEVES;
// The LBFGS hessian approximation is a low rank approximation to
// the inverse of the Hessian matrix. The rank of the
@@ -197,8 +118,8 @@ class CERES_EXPORT Solver {
// method, please see:
//
// Nocedal, J. (1980). "Updating Quasi-Newton Matrices with
- // Limited Storage". Mathematics of Computation 35 (151): 773–782.
- int max_lbfgs_rank;
+ // Limited Storage". Mathematics of Computation 35 (151): 773-782.
+ int max_lbfgs_rank = 20;
// As part of the (L)BFGS update step (BFGS) / right-multiply step (L-BFGS),
// the initial inverse Hessian approximation is taken to be the Identity.
@@ -220,18 +141,18 @@ class CERES_EXPORT Solver {
// Oren S.S., Self-scaling variable metric (SSVM) algorithms
// Part II: Implementation and experiments, Management Science,
// 20(5), 863-874, 1974.
- bool use_approximate_eigenvalue_bfgs_scaling;
+ bool use_approximate_eigenvalue_bfgs_scaling = false;
// Degree of the polynomial used to approximate the objective
// function. Valid values are BISECTION, QUADRATIC and CUBIC.
//
// BISECTION corresponds to pure backtracking search with no
// interpolation.
- LineSearchInterpolationType line_search_interpolation_type;
+ LineSearchInterpolationType line_search_interpolation_type = CUBIC;
// If during the line search, the step_size falls below this
// value, it is truncated to zero.
- double min_line_search_step_size;
+ double min_line_search_step_size = 1e-9;
// Line search parameters.
@@ -245,7 +166,7 @@ class CERES_EXPORT Solver {
//
// f(step_size) <= f(0) + sufficient_decrease * f'(0) * step_size
//
- double line_search_sufficient_function_decrease;
+ double line_search_sufficient_function_decrease = 1e-4;
// In each iteration of the line search,
//
@@ -255,7 +176,7 @@ class CERES_EXPORT Solver {
//
// 0 < max_step_contraction < min_step_contraction < 1
//
- double max_line_search_step_contraction;
+ double max_line_search_step_contraction = 1e-3;
// In each iteration of the line search,
//
@@ -265,19 +186,25 @@ class CERES_EXPORT Solver {
//
// 0 < max_step_contraction < min_step_contraction < 1
//
- double min_line_search_step_contraction;
+ double min_line_search_step_contraction = 0.6;
- // Maximum number of trial step size iterations during each line search,
- // if a step size satisfying the search conditions cannot be found within
- // this number of trials, the line search will terminate.
- int max_num_line_search_step_size_iterations;
+ // Maximum number of trial step size iterations during each line
+ // search, if a step size satisfying the search conditions cannot
+ // be found within this number of trials, the line search will
+ // terminate.
+
+ // The minimum allowed value is 0 for trust region minimizer and 1
+ // otherwise. If 0 is specified for the trust region minimizer,
+ // then line search will not be used when solving constrained
+ // optimization problems.
+ int max_num_line_search_step_size_iterations = 20;
// Maximum number of restarts of the line search direction algorithm before
// terminating the optimization. Restarts of the line search direction
// algorithm occur when the current algorithm fails to produce a new descent
// direction. This typically indicates a numerical failure, or a breakdown
// in the validity of the approximations used.
- int max_num_line_search_direction_restarts;
+ int max_num_line_search_direction_restarts = 5;
// The strong Wolfe conditions consist of the Armijo sufficient
// decrease condition, and an additional requirement that the
@@ -290,7 +217,7 @@ class CERES_EXPORT Solver {
//
// Where f() is the line search objective and f'() is the derivative
// of f w.r.t step_size (d f / d step_size).
- double line_search_sufficient_curvature_decrease;
+ double line_search_sufficient_curvature_decrease = 0.9;
// During the bracketing phase of the Wolfe search, the step size is
// increased until either a point satisfying the Wolfe conditions is
@@ -301,12 +228,12 @@ class CERES_EXPORT Solver {
// new_step_size <= max_step_expansion * step_size.
//
// By definition for expansion, max_step_expansion > 1.0.
- double max_line_search_step_expansion;
+ double max_line_search_step_expansion = 10.0;
- TrustRegionStrategyType trust_region_strategy_type;
+ TrustRegionStrategyType trust_region_strategy_type = LEVENBERG_MARQUARDT;
// Type of dogleg strategy to use.
- DoglegType dogleg_type;
+ DoglegType dogleg_type = TRADITIONAL_DOGLEG;
// The classical trust region methods are descent methods, in that
// they only accept a point if it strictly reduces the value of
@@ -317,7 +244,7 @@ class CERES_EXPORT Solver {
// in the value of the objective function.
//
// This is because allowing for non-decreasing objective function
- // values in a princpled manner allows the algorithm to "jump over
+ // values in a principled manner allows the algorithm to "jump over
// boulders" as the method is not restricted to move into narrow
// valleys while preserving its convergence properties.
//
@@ -333,30 +260,30 @@ class CERES_EXPORT Solver {
// than the minimum value encountered over the course of the
// optimization, the final parameters returned to the user are the
// ones corresponding to the minimum cost over all iterations.
- bool use_nonmonotonic_steps;
- int max_consecutive_nonmonotonic_steps;
+ bool use_nonmonotonic_steps = false;
+ int max_consecutive_nonmonotonic_steps = 5;
// Maximum number of iterations for the minimizer to run for.
- int max_num_iterations;
+ int max_num_iterations = 50;
// Maximum time for which the minimizer should run for.
- double max_solver_time_in_seconds;
+ double max_solver_time_in_seconds = 1e9;
// Number of threads used by Ceres for evaluating the cost and
// jacobians.
- int num_threads;
+ int num_threads = 1;
// Trust region minimizer settings.
- double initial_trust_region_radius;
- double max_trust_region_radius;
+ double initial_trust_region_radius = 1e4;
+ double max_trust_region_radius = 1e16;
// Minimizer terminates when the trust region radius becomes
// smaller than this value.
- double min_trust_region_radius;
+ double min_trust_region_radius = 1e-32;
// Lower bound for the relative decrease before a step is
// accepted.
- double min_relative_decrease;
+ double min_relative_decrease = 1e-3;
// For the Levenberg-Marquadt algorithm, the scaled diagonal of
// the normal equations J'J is used to control the size of the
@@ -365,46 +292,75 @@ class CERES_EXPORT Solver {
// fail. max_lm_diagonal and min_lm_diagonal, clamp the values of
// diag(J'J) from above and below. In the normal course of
// operation, the user should not have to modify these parameters.
- double min_lm_diagonal;
- double max_lm_diagonal;
+ double min_lm_diagonal = 1e-6;
+ double max_lm_diagonal = 1e32;
// Sometimes due to numerical conditioning problems or linear
// solver flakiness, the trust region strategy may return a
// numerically invalid step that can be fixed by reducing the
// trust region size. So the TrustRegionMinimizer allows for a few
// successive invalid steps before it declares NUMERICAL_FAILURE.
- int max_num_consecutive_invalid_steps;
+ int max_num_consecutive_invalid_steps = 5;
// Minimizer terminates when
//
// (new_cost - old_cost) < function_tolerance * old_cost;
//
- double function_tolerance;
+ double function_tolerance = 1e-6;
// Minimizer terminates when
//
// max_i |x - Project(Plus(x, -g(x))| < gradient_tolerance
//
// This value should typically be 1e-4 * function_tolerance.
- double gradient_tolerance;
+ double gradient_tolerance = 1e-10;
// Minimizer terminates when
//
// |step|_2 <= parameter_tolerance * ( |x|_2 + parameter_tolerance)
//
- double parameter_tolerance;
+ double parameter_tolerance = 1e-8;
// Linear least squares solver options -------------------------------------
- LinearSolverType linear_solver_type;
+ LinearSolverType linear_solver_type =
+#if defined(CERES_NO_SPARSE)
+ DENSE_QR;
+#else
+ SPARSE_NORMAL_CHOLESKY;
+#endif
// Type of preconditioner to use with the iterative linear solvers.
- PreconditionerType preconditioner_type;
+ PreconditionerType preconditioner_type = JACOBI;
// Type of clustering algorithm to use for visibility based
// preconditioning. This option is used only when the
// preconditioner_type is CLUSTER_JACOBI or CLUSTER_TRIDIAGONAL.
- VisibilityClusteringType visibility_clustering_type;
+ VisibilityClusteringType visibility_clustering_type = CANONICAL_VIEWS;
+
+ // Subset preconditioner is a preconditioner for problems with
+ // general sparsity. Given a subset of residual blocks of a
+ // problem, it uses the corresponding subset of the rows of the
+ // Jacobian to construct a preconditioner.
+ //
+ // Suppose the Jacobian J has been horizontally partitioned as
+ //
+ // J = [P]
+ // [Q]
+ //
+ // Where, Q is the set of rows corresponding to the residual
+ // blocks in residual_blocks_for_subset_preconditioner.
+ //
+ // The preconditioner is the inverse of the matrix Q'Q.
+ //
+ // Obviously, the efficacy of the preconditioner depends on how
+ // well the matrix Q approximates J'J, or how well the chosen
+ // residual blocks approximate the non-linear least squares
+ // problem.
+ //
+ // If Solver::Options::preconditioner_type == SUBSET, then
+ // residual_blocks_for_subset_preconditioner must be non-empty.
+ 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
@@ -413,22 +369,28 @@ class CERES_EXPORT Solver {
// available.
//
// This setting affects the DENSE_QR, DENSE_NORMAL_CHOLESKY and
- // DENSE_SCHUR solvers. For small to moderate sized probem EIGEN
+ // 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.
- DenseLinearAlgebraLibraryType dense_linear_algebra_library_type;
+ 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.
- SparseLinearAlgebraLibraryType sparse_linear_algebra_library_type;
-
- // Number of threads used by Ceres to solve the Newton
- // step. Currently only the SPARSE_SCHUR solver is capable of
- // using this setting.
- int num_linear_solver_threads;
+ SparseLinearAlgebraLibraryType sparse_linear_algebra_library_type =
+#if !defined(CERES_NO_SUITESPARSE)
+ SUITE_SPARSE;
+#elif defined(CERES_USE_EIGEN_SPARSE)
+ EIGEN_SPARSE;
+#elif !defined(CERES_NO_CXSPARSE)
+ CX_SPARSE;
+#elif !defined(CERES_NO_ACCELERATE_SPARSE)
+ ACCELERATE_SPARSE;
+#else
+ NO_SPARSE;
+#endif
// The order in which variables are eliminated in a linear solver
// can have a significant of impact on the efficiency and accuracy
@@ -456,7 +418,7 @@ class CERES_EXPORT Solver {
//
// Given such an ordering, Ceres ensures that the parameter blocks in
// the lowest numbered group are eliminated first, and then the
- // parmeter blocks in the next lowest numbered group and so on. Within
+ // parameter blocks in the next lowest numbered group and so on. Within
// each group, Ceres is free to order the parameter blocks as it
// chooses.
//
@@ -496,13 +458,13 @@ class CERES_EXPORT Solver {
// the parameter blocks into two groups, one for the points and one
// for the cameras, where the group containing the points has an id
// smaller than the group containing cameras.
- shared_ptr<ParameterBlockOrdering> linear_solver_ordering;
+ std::shared_ptr<ParameterBlockOrdering> linear_solver_ordering;
// Use an explicitly computed Schur complement matrix with
// ITERATIVE_SCHUR.
//
// By default this option is disabled and ITERATIVE_SCHUR
- // evaluates evaluates matrix-vector products between the Schur
+ // evaluates matrix-vector products between the Schur
// complement and a vector implicitly by exploiting the algebraic
// expression for the Schur complement.
//
@@ -519,7 +481,7 @@ class CERES_EXPORT Solver {
//
// NOTE: This option can only be used with the SCHUR_JACOBI
// preconditioner.
- bool use_explicit_schur_complement;
+ bool use_explicit_schur_complement = false;
// Sparse Cholesky factorization algorithms use a fill-reducing
// ordering to permute the columns of the Jacobian matrix. There
@@ -540,7 +502,7 @@ class CERES_EXPORT Solver {
// reordering algorithm which has slightly better runtime
// performance at the expense of an extra copy of the Jacobian
// matrix. Setting use_postordering to true enables this tradeoff.
- bool use_postordering;
+ bool use_postordering = false;
// Some non-linear least squares problems are symbolically dense but
// numerically sparse. i.e. at any given state only a small number
@@ -554,8 +516,32 @@ class CERES_EXPORT Solver {
// then it is probably best to keep this false, otherwise it will
// likely lead to worse performance.
- // This settings affects the SPARSE_NORMAL_CHOLESKY solver.
- bool dynamic_sparsity;
+ // This settings only affects the SPARSE_NORMAL_CHOLESKY solver.
+ bool dynamic_sparsity = false;
+
+ // TODO(sameeragarwal): Further expand the documentation for the
+ // following two options.
+
+ // NOTE1: EXPERIMENTAL FEATURE, UNDER DEVELOPMENT, USE AT YOUR OWN RISK.
+ //
+ // If use_mixed_precision_solves is true, the Gauss-Newton matrix
+ // is computed in double precision, but its factorization is
+ // computed in single precision. This can result in significant
+ // time and memory savings at the cost of some accuracy in the
+ // Gauss-Newton step. Iterative refinement is used to recover some
+ // of this accuracy back.
+ //
+ // If use_mixed_precision_solves is true, we recommend setting
+ // 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.
+ bool use_mixed_precision_solves = false;
+
+ // Number steps of the iterative refinement process to run when
+ // computing the Gauss-Newton step.
+ int max_num_refinement_iterations = 0;
// Some non-linear least squares problems have additional
// structure in the way the parameter blocks interact that it is
@@ -583,7 +569,7 @@ class CERES_EXPORT Solver {
// known as Wiberg's algorithm.
//
// Ruhe & Wedin (Algorithms for Separable Nonlinear Least Squares
- // Problems, SIAM Reviews, 22(3), 1980) present an analyis of
+ // Problems, SIAM Reviews, 22(3), 1980) present an analysis of
// various algorithms for solving separable non-linear least
// squares problems and refer to "Variable Projection" as
// Algorithm I in their paper.
@@ -615,7 +601,7 @@ class CERES_EXPORT Solver {
// displays better convergence behaviour per iteration. Setting
// Solver::Options::num_threads to the maximum number possible is
// highly recommended.
- bool use_inner_iterations;
+ bool use_inner_iterations = false;
// If inner_iterations is true, then the user has two choices.
//
@@ -627,7 +613,7 @@ class CERES_EXPORT Solver {
// the lower numbered groups are optimized before the higher
// number groups. Each group must be an independent set. Not
// all parameter blocks need to be present in the ordering.
- shared_ptr<ParameterBlockOrdering> inner_iteration_ordering;
+ std::shared_ptr<ParameterBlockOrdering> inner_iteration_ordering;
// Generally speaking, inner iterations make significant progress
// in the early stages of the solve and then their contribution
@@ -638,17 +624,17 @@ class CERES_EXPORT Solver {
// inner iterations drops below inner_iteration_tolerance, the use
// of inner iterations in subsequent trust region minimizer
// iterations is disabled.
- double inner_iteration_tolerance;
+ double inner_iteration_tolerance = 1e-3;
// Minimum number of iterations for which the linear solver should
// run, even if the convergence criterion is satisfied.
- int min_linear_solver_iterations;
+ int min_linear_solver_iterations = 0;
// Maximum number of iterations for which the linear solver should
// run. If the solver does not converge in less than
// max_linear_solver_iterations, then it returns MAX_ITERATIONS,
// as its termination type.
- int max_linear_solver_iterations;
+ int max_linear_solver_iterations = 500;
// Forcing sequence parameter. The truncated Newton solver uses
// this number to control the relative accuracy with which the
@@ -658,21 +644,21 @@ class CERES_EXPORT Solver {
// it to terminate the iterations when
//
// (Q_i - Q_{i-1})/Q_i < eta/i
- double eta;
+ double eta = 1e-1;
// Normalize the jacobian using Jacobi scaling before calling
// the linear least squares solver.
- bool jacobi_scaling;
+ bool jacobi_scaling = true;
// Logging options ---------------------------------------------------------
- LoggingType logging_type;
+ LoggingType logging_type = PER_MINIMIZER_ITERATION;
// By default the Minimizer progress is logged to VLOG(1), which
// is sent to STDERR depending on the vlog level. If this flag is
// set to true, and logging_type is not SILENT, the logging output
// is sent to STDOUT.
- bool minimizer_progress_to_stdout;
+ bool minimizer_progress_to_stdout = false;
// List of iterations at which the minimizer should dump the trust
// region problem. Useful for testing and benchmarking. If empty
@@ -683,8 +669,8 @@ class CERES_EXPORT Solver {
// non-empty if trust_region_minimizer_iterations_to_dump is
// non-empty and trust_region_problem_dump_format_type is not
// CONSOLE.
- std::string trust_region_problem_dump_directory;
- DumpFormatType trust_region_problem_dump_format_type;
+ std::string trust_region_problem_dump_directory = "/tmp";
+ DumpFormatType trust_region_problem_dump_format_type = TEXTFILE;
// Finite differences options ----------------------------------------------
@@ -694,12 +680,12 @@ class CERES_EXPORT Solver {
// etc), then also computing it using finite differences. The
// results are compared, and if they differ substantially, details
// are printed to the log.
- bool check_gradients;
+ bool check_gradients = false;
// Relative precision to check for in the gradient checker. If the
// relative difference between an element in a jacobian exceeds
// this number, then the jacobian for that cost term is dumped.
- double gradient_check_relative_precision;
+ double gradient_check_relative_precision = 1e-8;
// WARNING: This option only applies to the to the numeric
// differentiation used for checking the user provided derivatives
@@ -723,7 +709,7 @@ class CERES_EXPORT Solver {
//
// The finite differencing is done along each dimension. The
// reason to use a relative (rather than absolute) step size is
- // that this way, numeric differentation works for functions where
+ // that this way, numeric differentiation works for functions where
// the arguments are typically large (e.g. 1e9) and when the
// values are small (e.g. 1e-5). It is possible to construct
// "torture cases" which break this finite difference heuristic,
@@ -733,14 +719,21 @@ class CERES_EXPORT Solver {
// theory a good choice is sqrt(eps) * x, which for doubles means
// about 1e-8 * x. However, I have found this number too
// optimistic. This number should be exposed for users to change.
- double gradient_check_numeric_derivative_relative_step_size;
-
- // If true, the user's parameter blocks are updated at the end of
- // every Minimizer iteration, otherwise they are updated when the
- // Minimizer terminates. This is useful if, for example, the user
- // wishes to visualize the state of the optimization every
- // iteration.
- bool update_state_every_iteration;
+ double gradient_check_numeric_derivative_relative_step_size = 1e-6;
+
+ // If update_state_every_iteration is true, then Ceres Solver will
+ // guarantee that at the end of every iteration and before any
+ // user provided IterationCallback is called, the parameter blocks
+ // are updated to the current best solution found by the
+ // solver. Thus the IterationCallback can inspect the values of
+ // the parameter blocks for purposes of computation, visualization
+ // or termination.
+
+ // If update_state_every_iteration is false then there is no such
+ // guarantee, and user provided IterationCallbacks should not
+ // expect to look at the parameter blocks and interpret their
+ // values.
+ bool update_state_every_iteration = false;
// Callbacks that are executed at the end of each iteration of the
// Minimizer. An iteration may terminate midway, either due to
@@ -749,20 +742,18 @@ class CERES_EXPORT Solver {
// executed.
// Callbacks are executed in the order that they are specified in
- // this vector. By default, parameter blocks are updated only at
- // the end of the optimization, i.e when the Minimizer
- // terminates. This behaviour is controlled by
- // update_state_every_variable. If the user wishes to have access
- // to the update parameter blocks when his/her callbacks are
- // executed, then set update_state_every_iteration to true.
+ // this vector. By default, parameter blocks are updated only at the
+ // end of the optimization, i.e when the Minimizer terminates. This
+ // behaviour is controlled by update_state_every_iteration. If the
+ // user wishes to have access to the updated parameter blocks when
+ // his/her callbacks are executed, then set
+ // update_state_every_iteration to true.
//
// The solver does NOT take ownership of these pointers.
std::vector<IterationCallback*> callbacks;
};
struct CERES_EXPORT Summary {
- Summary();
-
// A brief one line description of the state of the solver after
// termination.
std::string BriefReport() const;
@@ -774,25 +765,25 @@ class CERES_EXPORT Solver {
bool IsSolutionUsable() const;
// Minimizer summary -------------------------------------------------
- MinimizerType minimizer_type;
+ MinimizerType minimizer_type = TRUST_REGION;
- TerminationType termination_type;
+ TerminationType termination_type = FAILURE;
// Reason why the solver terminated.
- std::string message;
+ std::string message = "ceres::Solve was not called.";
// Cost of the problem (value of the objective function) before
// the optimization.
- double initial_cost;
+ double initial_cost = -1.0;
// Cost of the problem (value of the objective function) after the
// optimization.
- double final_cost;
+ double final_cost = -1.0;
// The part of the total cost that comes from residual blocks that
// were held fixed by the preprocessor because all the parameter
// blocks that they depend on were fixed.
- double fixed_cost;
+ double fixed_cost = -1.0;
// IterationSummary for each minimizer iteration in order.
std::vector<IterationSummary> iterations;
@@ -801,22 +792,22 @@ class CERES_EXPORT Solver {
// accepted. Unless use_non_monotonic_steps is true this is also
// the number of steps in which the objective function value/cost
// went down.
- int num_successful_steps;
+ int num_successful_steps = -1;
// Number of minimizer iterations in which the step was rejected
// either because it did not reduce the cost enough or the step
// was not numerically valid.
- int num_unsuccessful_steps;
+ int num_unsuccessful_steps = -1;
// Number of times inner iterations were performed.
- int num_inner_iteration_steps;
+ int num_inner_iteration_steps = -1;
// Total number of iterations inside the line search algorithm
// across all invocations. We call these iterations "steps" to
// distinguish them from the outer iterations of the line search
// and trust region minimizer algorithms which call the line
// search algorithm as a subroutine.
- int num_line_search_steps;
+ int num_line_search_steps = -1;
// All times reported below are wall times.
@@ -824,31 +815,42 @@ class CERES_EXPORT Solver {
// occurs, Ceres performs a number of preprocessing steps. These
// include error checks, memory allocations, and reorderings. This
// time is accounted for as preprocessing time.
- double preprocessor_time_in_seconds;
+ double preprocessor_time_in_seconds = -1.0;
// Time spent in the TrustRegionMinimizer.
- double minimizer_time_in_seconds;
+ double minimizer_time_in_seconds = -1.0;
// After the Minimizer is finished, some time is spent in
// re-evaluating residuals etc. This time is accounted for in the
// postprocessor time.
- double postprocessor_time_in_seconds;
+ double postprocessor_time_in_seconds = -1.0;
// Some total of all time spent inside Ceres when Solve is called.
- double total_time_in_seconds;
+ double total_time_in_seconds = -1.0;
// Time (in seconds) spent in the linear solver computing the
// trust region step.
- double linear_solver_time_in_seconds;
+ double linear_solver_time_in_seconds = -1.0;
+
+ // Number of times the Newton step was computed by solving a
+ // linear system. This does not include linear solves used by
+ // inner iterations.
+ int num_linear_solves = -1;
// Time (in seconds) spent evaluating the residual vector.
- double residual_evaluation_time_in_seconds;
+ double residual_evaluation_time_in_seconds = 1.0;
+
+ // Number of residual only evaluations.
+ int num_residual_evaluations = -1;
// Time (in seconds) spent evaluating the jacobian matrix.
- double jacobian_evaluation_time_in_seconds;
+ double jacobian_evaluation_time_in_seconds = -1.0;
+
+ // Number of Jacobian (and residual) evaluations.
+ int num_jacobian_evaluations = -1;
// Time (in seconds) spent doing inner iterations.
- double inner_iteration_time_in_seconds;
+ double inner_iteration_time_in_seconds = -1.0;
// Cumulative timing information for line searches performed as part of the
// solve. Note that in addition to the case when the Line Search minimizer
@@ -857,89 +859,89 @@ class CERES_EXPORT Solver {
// Time (in seconds) spent evaluating the univariate cost function as part
// of a line search.
- double line_search_cost_evaluation_time_in_seconds;
+ double line_search_cost_evaluation_time_in_seconds = -1.0;
// Time (in seconds) spent evaluating the gradient of the univariate cost
// function as part of a line search.
- double line_search_gradient_evaluation_time_in_seconds;
+ double line_search_gradient_evaluation_time_in_seconds = -1.0;
// Time (in seconds) spent minimizing the interpolating polynomial
// to compute the next candidate step size as part of a line search.
- double line_search_polynomial_minimization_time_in_seconds;
+ double line_search_polynomial_minimization_time_in_seconds = -1.0;
// Total time (in seconds) spent performing line searches.
- double line_search_total_time_in_seconds;
+ double line_search_total_time_in_seconds = -1.0;
// Number of parameter blocks in the problem.
- int num_parameter_blocks;
+ int num_parameter_blocks = -1;
- // Number of parameters in the probem.
- int num_parameters;
+ // Number of parameters in the problem.
+ int num_parameters = -1;
// 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
- int num_effective_parameters;
+ int num_effective_parameters = -1;
// Number of residual blocks in the problem.
- int num_residual_blocks;
+ int num_residual_blocks = -1;
// Number of residuals in the problem.
- int num_residuals;
+ int num_residuals = -1;
// Number of parameter blocks in the problem after the inactive
// and constant parameter blocks have been removed. A parameter
// block is inactive if no residual block refers to it.
- int num_parameter_blocks_reduced;
+ int num_parameter_blocks_reduced = -1;
// Number of parameters in the reduced problem.
- int num_parameters_reduced;
+ int num_parameters_reduced = -1;
// Dimension of the tangent space of the reduced problem (or the
// 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.
- int num_effective_parameters_reduced;
+ int num_effective_parameters_reduced = -1;
// Number of residual blocks in the reduced problem.
- int num_residual_blocks_reduced;
+ int num_residual_blocks_reduced = -1;
// Number of residuals in the reduced problem.
- int num_residuals_reduced;
+ int num_residuals_reduced = -1;
// Is the reduced problem bounds constrained.
- bool is_constrained;
+ bool is_constrained = false;
// Number of threads specified by the user for Jacobian and
// residual evaluation.
- int num_threads_given;
+ int num_threads_given = -1;
// Number of threads actually used by the solver for Jacobian and
// residual evaluation. This number is not equal to
// num_threads_given if OpenMP is not available.
- int num_threads_used;
-
- // Number of threads specified by the user for solving the trust
- // region problem.
- int num_linear_solver_threads_given;
-
- // Number of threads actually used by the solver for solving the
- // trust region problem. This number is not equal to
- // num_threads_given if OpenMP is not available.
- int num_linear_solver_threads_used;
+ int num_threads_used = -1;
// Type of the linear solver requested by the user.
- LinearSolverType linear_solver_type_given;
-
+ LinearSolverType linear_solver_type_given =
+#if defined(CERES_NO_SPARSE)
+ DENSE_QR;
+#else
+ SPARSE_NORMAL_CHOLESKY;
+#endif
// Type of the linear solver actually used. This may be different
// from linear_solver_type_given if Ceres determines that the
// problem structure is not compatible with the linear solver
// requested or if the linear solver requested by the user is not
// available, e.g. The user requested SPARSE_NORMAL_CHOLESKY but
// no sparse linear algebra library was available.
- LinearSolverType linear_solver_type_used;
+ LinearSolverType linear_solver_type_used =
+#if defined(CERES_NO_SPARSE)
+ DENSE_QR;
+#else
+ SPARSE_NORMAL_CHOLESKY;
+#endif
// Size of the elimination groups given by the user as hints to
// the linear solver.
@@ -953,15 +955,29 @@ class CERES_EXPORT Solver {
// parameter blocks.
std::vector<int> linear_solver_ordering_used;
+ // For Schur type linear solvers, this string describes the
+ // template specialization which was detected in the problem and
+ // should be used.
+ std::string schur_structure_given;
+
+ // This is the Schur template specialization that was actually
+ // instantiated and used. The reason this will be different from
+ // schur_structure_given is because the corresponding template
+ // specialization does not exist.
+ //
+ // Template specializations can be added to ceres by editing
+ // internal/ceres/generate_template_specializations.py
+ std::string schur_structure_used;
+
// True if the user asked for inner iterations to be used as part
// of the optimization.
- bool inner_iterations_given;
+ bool inner_iterations_given = false;
// True if the user asked for inner iterations to be used as part
// of the optimization and the problem structure was such that
// they were actually performed. e.g., in a problem with just one
// parameter block, inner iterations are not performed.
- bool inner_iterations_used;
+ bool inner_iterations_used = false;
// Size of the parameter groups given by the user for performing
// inner iterations.
@@ -976,57 +992,59 @@ class CERES_EXPORT Solver {
std::vector<int> inner_iteration_ordering_used;
// Type of the preconditioner requested by the user.
- PreconditionerType preconditioner_type_given;
+ PreconditionerType preconditioner_type_given = IDENTITY;
// Type of the preconditioner actually used. This may be different
// from linear_solver_type_given if Ceres determines that the
// problem structure is not compatible with the linear solver
// requested or if the linear solver requested by the user is not
// available.
- PreconditionerType preconditioner_type_used;
+ PreconditionerType preconditioner_type_used = IDENTITY;
// Type of clustering algorithm used for visibility based
// preconditioning. Only meaningful when the preconditioner_type
// is CLUSTER_JACOBI or CLUSTER_TRIDIAGONAL.
- VisibilityClusteringType visibility_clustering_type;
+ VisibilityClusteringType visibility_clustering_type = CANONICAL_VIEWS;
// Type of trust region strategy.
- TrustRegionStrategyType trust_region_strategy_type;
+ TrustRegionStrategyType trust_region_strategy_type = LEVENBERG_MARQUARDT;
// Type of dogleg strategy used for solving the trust region
// problem.
- DoglegType dogleg_type;
+ DoglegType dogleg_type = TRADITIONAL_DOGLEG;
// Type of the dense linear algebra library used.
- DenseLinearAlgebraLibraryType dense_linear_algebra_library_type;
+ DenseLinearAlgebraLibraryType dense_linear_algebra_library_type = EIGEN;
// Type of the sparse linear algebra library used.
- SparseLinearAlgebraLibraryType sparse_linear_algebra_library_type;
+ SparseLinearAlgebraLibraryType sparse_linear_algebra_library_type =
+ NO_SPARSE;
// Type of line search direction used.
- LineSearchDirectionType line_search_direction_type;
+ LineSearchDirectionType line_search_direction_type = LBFGS;
// Type of the line search algorithm used.
- LineSearchType line_search_type;
+ LineSearchType line_search_type = WOLFE;
// When performing line search, the degree of the polynomial used
// to approximate the objective function.
- LineSearchInterpolationType line_search_interpolation_type;
+ LineSearchInterpolationType line_search_interpolation_type = CUBIC;
// If the line search direction is NONLINEAR_CONJUGATE_GRADIENT,
// then this indicates the particular variant of non-linear
// conjugate gradient used.
- NonlinearConjugateGradientType nonlinear_conjugate_gradient_type;
+ NonlinearConjugateGradientType nonlinear_conjugate_gradient_type =
+ FLETCHER_REEVES;
// If the type of the line search direction is LBFGS, then this
// indicates the rank of the Hessian approximation.
- int max_lbfgs_rank;
+ int max_lbfgs_rank = -1;
};
// Once a least squares problem has been built, this function takes
// the problem and optimizes it based on the values of the options
// parameters. Upon return, a detailed summary of the work performed
- // by the preprocessor, the non-linear minmizer and the linear
+ // by the preprocessor, the non-linear minimizer and the linear
// solver are reported in the summary object.
virtual void Solve(const Options& options,
Problem* problem,
@@ -1035,8 +1053,8 @@ class CERES_EXPORT Solver {
// Helper function which avoids going through the interface.
CERES_EXPORT void Solve(const Solver::Options& options,
- Problem* problem,
- Solver::Summary* summary);
+ Problem* problem,
+ Solver::Summary* summary);
} // namespace ceres
diff --git a/extern/ceres/include/ceres/tiny_solver.h b/extern/ceres/include/ceres/tiny_solver.h
new file mode 100644
index 00000000000..47db5824dc5
--- /dev/null
+++ b/extern/ceres/include/ceres/tiny_solver.h
@@ -0,0 +1,368 @@
+// 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: mierle@gmail.com (Keir Mierle)
+//
+// WARNING WARNING WARNING
+// WARNING WARNING WARNING Tiny solver is experimental and will change.
+// WARNING WARNING WARNING
+//
+// A tiny least squares solver using Levenberg-Marquardt, intended for solving
+// small dense problems with low latency and low overhead. The implementation
+// takes care to do all allocation up front, so that no memory is allocated
+// during solving. This is especially useful when solving many similar problems;
+// for example, inverse pixel distortion for every pixel on a grid.
+//
+// Note: This code has no dependencies beyond Eigen, including on other parts of
+// Ceres, so it is possible to take this file alone and put it in another
+// project without the rest of Ceres.
+//
+// Algorithm based off of:
+//
+// [1] K. Madsen, H. Nielsen, O. Tingleoff.
+// Methods for Non-linear Least Squares Problems.
+// http://www2.imm.dtu.dk/pubdb/views/edoc_download.php/3215/pdf/imm3215.pdf
+
+#ifndef CERES_PUBLIC_TINY_SOLVER_H_
+#define CERES_PUBLIC_TINY_SOLVER_H_
+
+#include <cassert>
+#include <cmath>
+
+#include "Eigen/Dense"
+
+namespace ceres {
+
+// To use tiny solver, create a class or struct that allows computing the cost
+// function (described below). This is similar to a ceres::CostFunction, but is
+// different to enable statically allocating all memory for the solver
+// (specifically, enum sizes). Key parts are the Scalar typedef, the enums to
+// describe problem sizes (needed to remove all heap allocations), and the
+// operator() overload to evaluate the cost and (optionally) jacobians.
+//
+// struct TinySolverCostFunctionTraits {
+// typedef double Scalar;
+// enum {
+// NUM_RESIDUALS = <int> OR Eigen::Dynamic,
+// NUM_PARAMETERS = <int> OR Eigen::Dynamic,
+// };
+// bool operator()(const double* parameters,
+// double* residuals,
+// double* jacobian) const;
+//
+// int NumResiduals() const; -- Needed if NUM_RESIDUALS == Eigen::Dynamic.
+// int NumParameters() const; -- Needed if NUM_PARAMETERS == Eigen::Dynamic.
+// };
+//
+// For operator(), the size of the objects is:
+//
+// 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.
+//
+// An example (fully statically sized):
+//
+// struct MyCostFunctionExample {
+// typedef double Scalar;
+// enum {
+// NUM_RESIDUALS = 2,
+// NUM_PARAMETERS = 3,
+// };
+// bool operator()(const double* parameters,
+// double* residuals,
+// double* jacobian) const {
+// residuals[0] = x + 2*y + 4*z;
+// residuals[1] = y * z;
+// if (jacobian) {
+// jacobian[0 * 2 + 0] = 1; // First column (x).
+// jacobian[0 * 2 + 1] = 0;
+//
+// jacobian[1 * 2 + 0] = 2; // Second column (y).
+// jacobian[1 * 2 + 1] = z;
+//
+// jacobian[2 * 2 + 0] = 4; // Third column (z).
+// jacobian[2 * 2 + 1] = y;
+// }
+// return true;
+// }
+// };
+//
+// The solver supports either statically or dynamically sized cost
+// functions. If the number of residuals is dynamic then the Function
+// must define:
+//
+// int NumResiduals() const;
+//
+// If the number of parameters is dynamic then the Function must
+// define:
+//
+// int NumParameters() const;
+//
+template <typename Function,
+ typename LinearSolver =
+ Eigen::LDLT<Eigen::Matrix<typename Function::Scalar,
+ Function::NUM_PARAMETERS,
+ Function::NUM_PARAMETERS>>>
+class TinySolver {
+ public:
+ // This class needs to have an Eigen aligned operator new as it contains
+ // fixed-size Eigen types.
+ EIGEN_MAKE_ALIGNED_OPERATOR_NEW
+
+ enum {
+ NUM_RESIDUALS = Function::NUM_RESIDUALS,
+ NUM_PARAMETERS = Function::NUM_PARAMETERS
+ };
+ typedef typename Function::Scalar Scalar;
+ typedef typename Eigen::Matrix<Scalar, NUM_PARAMETERS, 1> Parameters;
+
+ 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
+ HIT_MAX_ITERATIONS,
+
+ // 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;
+ };
+
+ 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))
+ int iterations = -1;
+ Status status = HIT_MAX_ITERATIONS;
+ };
+
+ bool Update(const Function& function, const Parameters& x) {
+ if (!function(x.data(), error_.data(), jacobian_.data())) {
+ return false;
+ }
+
+ error_ = -error_;
+
+ // On the first iteration, compute a diagonal (Jacobi) scaling
+ // matrix, which we store as a vector.
+ if (summary.iterations == 0) {
+ // jacobi_scaling = 1 / (1 + diagonal(J'J))
+ //
+ // 1 is added to the denominator to regularize small diagonal
+ // entries.
+ jacobi_scaling_ = 1.0 / (1.0 + jacobian_.colwise().norm().array());
+ }
+
+ // This explicitly computes the normal equations, which is numerically
+ // unstable. Nevertheless, it is often good enough and is fast.
+ //
+ // TODO(sameeragarwal): Refactor this to allow for DenseQR
+ // factorization.
+ jacobian_ = jacobian_ * jacobi_scaling_.asDiagonal();
+ jtj_ = jacobian_.transpose() * jacobian_;
+ g_ = jacobian_.transpose() * error_;
+ summary.gradient_max_norm = g_.array().abs().maxCoeff();
+ cost_ = error_.squaredNorm() / 2;
+ return true;
+ }
+
+ const Summary& Solve(const Function& function, Parameters* x_and_min) {
+ Initialize<NUM_RESIDUALS, NUM_PARAMETERS>(function);
+ assert(x_and_min);
+ Parameters& x = *x_and_min;
+ summary = Summary();
+ summary.iterations = 0;
+
+ // TODO(sameeragarwal): Deal with failure here.
+ Update(function, x);
+ summary.initial_cost = cost_;
+ summary.final_cost = cost_;
+
+ if (summary.gradient_max_norm < options.gradient_tolerance) {
+ summary.status = GRADIENT_TOO_SMALL;
+ return summary;
+ }
+
+ if (cost_ < options.cost_threshold) {
+ summary.status = COST_TOO_SMALL;
+ return summary;
+ }
+
+ Scalar u = 1.0 / options.initial_trust_region_radius;
+ Scalar v = 2;
+
+ for (summary.iterations = 1;
+ summary.iterations < options.max_num_iterations;
+ summary.iterations++) {
+ jtj_regularized_ = jtj_;
+ const Scalar min_diagonal = 1e-6;
+ 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));
+ jtj_regularized_(i, i) += lm_diagonal_[i] * lm_diagonal_[i];
+ }
+
+ // TODO(sameeragarwal): Check for failure and deal with it.
+ linear_solver_.compute(jtj_regularized_);
+ lm_step_ = linear_solver_.solve(g_);
+ dx_ = jacobi_scaling_.asDiagonal() * lm_step_;
+
+ // Adding parameter_tolerance to x.norm() ensures that this
+ // works if x is near zero.
+ const Scalar parameter_tolerance =
+ options.parameter_tolerance *
+ (x.norm() + options.parameter_tolerance);
+ if (dx_.norm() < parameter_tolerance) {
+ summary.status = RELATIVE_STEP_SIZE_TOO_SMALL;
+ break;
+ }
+ x_new_ = x + dx_;
+
+ // TODO(keir): Add proper handling of errors from user eval of cost
+ // functions.
+ function(&x_new_[0], &f_x_new_[0], NULL);
+
+ 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_);
+
+ // rho is the ratio of the actual reduction in error to the reduction
+ // in error that would be obtained if the problem was linear. See [1]
+ // for details.
+ Scalar rho(cost_change / model_cost_change);
+ if (rho > 0) {
+ // Accept the Levenberg-Marquardt step because the linear
+ // model fits well.
+ x = x_new_;
+
+ // TODO(sameeragarwal): Deal with failure.
+ Update(function, x);
+ if (summary.gradient_max_norm < options.gradient_tolerance) {
+ summary.status = GRADIENT_TOO_SMALL;
+ break;
+ }
+
+ if (cost_ < options.cost_threshold) {
+ summary.status = COST_TOO_SMALL;
+ break;
+ }
+
+ Scalar tmp = Scalar(2 * rho - 1);
+ u = u * std::max(1 / 3., 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;
+ }
+
+ summary.final_cost = cost_;
+ return summary;
+ }
+
+ Options options;
+ Summary summary;
+
+ private:
+ // Preallocate everything, including temporary storage needed for solving the
+ // linear system. This allows reusing the intermediate storage across solves.
+ 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, NUM_PARAMETERS> jacobian_;
+ Eigen::Matrix<Scalar, NUM_PARAMETERS, NUM_PARAMETERS> jtj_, jtj_regularized_;
+
+ // The following definitions are needed for template metaprogramming.
+ template <bool Condition, typename T>
+ struct enable_if;
+
+ template <typename T>
+ struct enable_if<true, T> {
+ typedef T type;
+ };
+
+ // The number of parameters and residuals are dynamically sized.
+ template <int R, int P>
+ typename enable_if<(R == Eigen::Dynamic && P == Eigen::Dynamic), void>::type
+ Initialize(const Function& function) {
+ Initialize(function.NumResiduals(), function.NumParameters());
+ }
+
+ // The number of parameters is dynamically sized and the number of
+ // residuals is statically sized.
+ template <int R, int P>
+ typename enable_if<(R == Eigen::Dynamic && P != Eigen::Dynamic), void>::type
+ Initialize(const Function& function) {
+ Initialize(function.NumResiduals(), P);
+ }
+
+ // The number of parameters is statically sized and the number of
+ // residuals is dynamically sized.
+ template <int R, int P>
+ typename enable_if<(R != Eigen::Dynamic && P == Eigen::Dynamic), void>::type
+ Initialize(const Function& function) {
+ Initialize(R, function.NumParameters());
+ }
+
+ // The number of parameters and residuals are statically sized.
+ template <int R, int P>
+ typename enable_if<(R != Eigen::Dynamic && P != Eigen::Dynamic), void>::type
+ Initialize(const Function& /* function */) {}
+
+ void Initialize(int num_residuals, int num_parameters) {
+ dx_.resize(num_parameters);
+ x_new_.resize(num_parameters);
+ g_.resize(num_parameters);
+ jacobi_scaling_.resize(num_parameters);
+ lm_diagonal_.resize(num_parameters);
+ lm_step_.resize(num_parameters);
+ error_.resize(num_residuals);
+ f_x_new_.resize(num_residuals);
+ jacobian_.resize(num_residuals, num_parameters);
+ jtj_.resize(num_parameters, num_parameters);
+ jtj_regularized_.resize(num_parameters, num_parameters);
+ }
+};
+
+} // namespace ceres
+
+#endif // CERES_PUBLIC_TINY_SOLVER_H_
diff --git a/extern/ceres/include/ceres/tiny_solver_autodiff_function.h b/extern/ceres/include/ceres/tiny_solver_autodiff_function.h
new file mode 100644
index 00000000000..b782f549cc1
--- /dev/null
+++ b/extern/ceres/include/ceres/tiny_solver_autodiff_function.h
@@ -0,0 +1,206 @@
+// 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: mierle@gmail.com (Keir Mierle)
+//
+// WARNING WARNING WARNING
+// WARNING WARNING WARNING Tiny solver is experimental and will change.
+// WARNING WARNING WARNING
+
+#ifndef CERES_PUBLIC_TINY_SOLVER_AUTODIFF_FUNCTION_H_
+#define CERES_PUBLIC_TINY_SOLVER_AUTODIFF_FUNCTION_H_
+
+#include <memory>
+#include <type_traits>
+
+#include "Eigen/Core"
+#include "ceres/jet.h"
+#include "ceres/types.h" // For kImpossibleValue.
+
+namespace ceres {
+
+// An adapter around autodiff-style CostFunctors to enable easier use of
+// TinySolver. See the example below showing how to use it:
+//
+// // Example for cost functor with static residual size.
+// // Same as an autodiff cost functor, but taking only 1 parameter.
+// struct MyFunctor {
+// template<typename T>
+// bool operator()(const T* const parameters, T* residuals) const {
+// const T& x = parameters[0];
+// const T& y = parameters[1];
+// const T& z = parameters[2];
+// residuals[0] = x + 2.*y + 4.*z;
+// residuals[1] = y * z;
+// return true;
+// }
+// };
+//
+// typedef TinySolverAutoDiffFunction<MyFunctor, 2, 3>
+// AutoDiffFunction;
+//
+// MyFunctor my_functor;
+// AutoDiffFunction f(my_functor);
+//
+// Vec3 x = ...;
+// TinySolver<AutoDiffFunction> solver;
+// solver.Solve(f, &x);
+//
+// // Example for cost functor with dynamic residual size.
+// // NumResiduals() supplies dynamic size of residuals.
+// // Same functionality as in tiny_solver.h but with autodiff.
+// struct MyFunctorWithDynamicResiduals {
+// int NumResiduals() const {
+// return 2;
+// }
+//
+// template<typename T>
+// bool operator()(const T* const parameters, T* residuals) const {
+// const T& x = parameters[0];
+// const T& y = parameters[1];
+// const T& z = parameters[2];
+// residuals[0] = x + static_cast<T>(2.)*y + static_cast<T>(4.)*z;
+// residuals[1] = y * z;
+// return true;
+// }
+// };
+//
+// typedef TinySolverAutoDiffFunction<MyFunctorWithDynamicResiduals,
+// Eigen::Dynamic,
+// 3>
+// AutoDiffFunctionWithDynamicResiduals;
+//
+// MyFunctorWithDynamicResiduals my_functor_dyn;
+// AutoDiffFunctionWithDynamicResiduals f(my_functor_dyn);
+//
+// Vec3 x = ...;
+// TinySolver<AutoDiffFunctionWithDynamicResiduals> solver;
+// solver.Solve(f, &x);
+//
+// WARNING: The cost function adapter is not thread safe.
+template <typename CostFunctor,
+ int kNumResiduals,
+ int kNumParameters,
+ typename T = double>
+class TinySolverAutoDiffFunction {
+ public:
+ // This class needs to have an Eigen aligned operator new as it contains
+ // 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)
+ : cost_functor_(cost_functor) {
+ Initialize<kNumResiduals>(cost_functor);
+ }
+
+ typedef T Scalar;
+ enum {
+ NUM_PARAMETERS = kNumParameters,
+ NUM_RESIDUALS = kNumResiduals,
+ };
+
+ // 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) {
+ // No jacobian requested, so just directly call the cost function with
+ // doubles, skipping jets and derivatives.
+ return cost_functor_(parameters, residuals);
+ }
+ // Initialize the input jets with passed parameters.
+ for (int i = 0; i < kNumParameters; ++i) {
+ jet_parameters_[i].a = parameters[i]; // Scalar part.
+ jet_parameters_[i].v.setZero(); // Derivative part.
+ jet_parameters_[i].v[i] = T(1.0);
+ }
+
+ // Initialize the output jets such that we can detect user errors.
+ for (int i = 0; i < num_residuals_; ++i) {
+ jet_residuals_[i].a = kImpossibleValue;
+ jet_residuals_[i].v.setConstant(kImpossibleValue);
+ }
+
+ // Execute the cost function, but with jets to find the derivative.
+ if (!cost_functor_(jet_parameters_, jet_residuals_.data())) {
+ return false;
+ }
+
+ // Copy the jacobian out of the derivative part of the residual jets.
+ Eigen::Map<Eigen::Matrix<T, kNumResiduals, kNumParameters>> jacobian_matrix(
+ jacobian, num_residuals_, kNumParameters);
+ for (int r = 0; r < num_residuals_; ++r) {
+ residuals[r] = jet_residuals_[r].a;
+ // Note that while this looks like a fast vectorized write, in practice it
+ // unfortunately thrashes the cache since the writes to the column-major
+ // jacobian are strided (e.g. rows are non-contiguous).
+ jacobian_matrix.row(r) = jet_residuals_[r].v;
+ }
+ return true;
+ }
+
+ int NumResiduals() const {
+ return num_residuals_; // Set by Initialize.
+ }
+
+ private:
+ const CostFunctor& cost_functor_;
+
+ // The number of residuals at runtime.
+ // This will be overriden if NUM_RESIDUALS == Eigen::Dynamic.
+ int num_residuals_ = kNumResiduals;
+
+ // To evaluate the cost function with jets, temporary storage is needed. These
+ // are the buffers that are used during evaluation; parameters for the input,
+ // and jet_residuals_ are where the final cost and derivatives end up.
+ //
+ // Since this buffer is used for evaluation, the adapter is not thread safe.
+ using JetType = Jet<T, kNumParameters>;
+ mutable JetType jet_parameters_[kNumParameters];
+ // Eigen::Matrix serves as static or dynamic container.
+ mutable Eigen::Matrix<JetType, kNumResiduals, 1> jet_residuals_;
+
+ // The number of residuals is dynamically sized and the number of
+ // parameters is statically sized.
+ template <int R>
+ typename std::enable_if<(R == Eigen::Dynamic), void>::type Initialize(
+ const CostFunctor& function) {
+ jet_residuals_.resize(function.NumResiduals());
+ num_residuals_ = function.NumResiduals();
+ }
+
+ // The number of parameters and residuals are statically sized.
+ template <int R>
+ typename std::enable_if<(R != Eigen::Dynamic), void>::type Initialize(
+ const CostFunctor& /* function */) {
+ num_residuals_ = kNumResiduals;
+ }
+};
+
+} // namespace ceres
+
+#endif // CERES_PUBLIC_TINY_SOLVER_AUTODIFF_FUNCTION_H_
diff --git a/extern/ceres/include/ceres/tiny_solver_cost_function_adapter.h b/extern/ceres/include/ceres/tiny_solver_cost_function_adapter.h
new file mode 100644
index 00000000000..18ccb398f90
--- /dev/null
+++ b/extern/ceres/include/ceres/tiny_solver_cost_function_adapter.h
@@ -0,0 +1,142 @@
+// 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_TINY_SOLVER_COST_FUNCTION_ADAPTER_H_
+#define CERES_PUBLIC_TINY_SOLVER_COST_FUNCTION_ADAPTER_H_
+
+#include "Eigen/Core"
+#include "ceres/cost_function.h"
+#include "glog/logging.h"
+
+namespace ceres {
+
+// An adapter class that lets users of TinySolver use
+// ceres::CostFunction objects that have exactly one parameter block.
+//
+// The adapter allows for the number of residuals and the size of the
+// parameter block to be specified at compile or run-time.
+//
+// WARNING: This object is not thread-safe.
+//
+// Example usage:
+//
+// CostFunction* cost_function = ...
+//
+// Number of residuals and parameter block size known at compile time:
+//
+// TinySolverCostFunctionAdapter<kNumResiduals, kNumParameters>
+// cost_function_adapter(*cost_function);
+//
+// Number of residuals known at compile time and the parameter block
+// size not known at compile time.
+//
+// TinySolverCostFunctionAdapter<kNumResiduals, Eigen::Dynamic>
+// cost_function_adapter(*cost_function);
+//
+// Number of residuals not known at compile time and the parameter
+// block size known at compile time.
+//
+// TinySolverCostFunctionAdapter<Eigen::Dynamic, kParameterBlockSize>
+// cost_function_adapter(*cost_function);
+//
+// Number of residuals not known at compile time and the parameter
+// block size not known at compile time.
+//
+// TinySolverCostFunctionAdapter cost_function_adapter(*cost_function);
+//
+template <int kNumResiduals = Eigen::Dynamic,
+ int kNumParameters = Eigen::Dynamic>
+class TinySolverCostFunctionAdapter {
+ public:
+ typedef double Scalar;
+ enum ComponentSizeType {
+ NUM_PARAMETERS = kNumParameters,
+ NUM_RESIDUALS = kNumResiduals
+ };
+
+ // This struct needs to have an Eigen aligned operator new as it contains
+ // fixed-size Eigen types.
+ EIGEN_MAKE_ALIGNED_OPERATOR_NEW
+
+ 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.";
+
+ const int parameter_block_size = cost_function_.parameter_block_sizes()[0];
+ if (NUM_PARAMETERS == Eigen::Dynamic || NUM_RESIDUALS == Eigen::Dynamic) {
+ if (NUM_RESIDUALS != Eigen::Dynamic) {
+ CHECK_EQ(cost_function_.num_residuals(), NUM_RESIDUALS);
+ }
+ if (NUM_PARAMETERS != Eigen::Dynamic) {
+ CHECK_EQ(parameter_block_size, NUM_PARAMETERS);
+ }
+
+ row_major_jacobian_.resize(cost_function_.num_residuals(),
+ parameter_block_size);
+ }
+ }
+
+ bool operator()(const double* parameters,
+ double* residuals,
+ double* jacobian) const {
+ if (!jacobian) {
+ return cost_function_.Evaluate(&parameters, residuals, NULL);
+ }
+
+ double* jacobians[1] = {row_major_jacobian_.data()};
+ if (!cost_function_.Evaluate(&parameters, residuals, jacobians)) {
+ return false;
+ }
+
+ // The Function object used by TinySolver takes its Jacobian in a
+ // column-major layout, and the CostFunction objects use row-major
+ // Jacobian matrices. So the following bit of code does the
+ // conversion from row-major Jacobians to column-major Jacobians.
+ Eigen::Map<Eigen::Matrix<double, NUM_RESIDUALS, NUM_PARAMETERS>>
+ col_major_jacobian(jacobian, NumResiduals(), NumParameters());
+ col_major_jacobian = row_major_jacobian_;
+ return true;
+ }
+
+ int NumResiduals() const { return cost_function_.num_residuals(); }
+ int NumParameters() const {
+ return cost_function_.parameter_block_sizes()[0];
+ }
+
+ private:
+ const CostFunction& cost_function_;
+ mutable Eigen::Matrix<double, NUM_RESIDUALS, NUM_PARAMETERS, Eigen::RowMajor>
+ row_major_jacobian_;
+};
+
+} // namespace ceres
+
+#endif // CERES_PUBLIC_TINY_SOLVER_COST_FUNCTION_ADAPTER_H_
diff --git a/extern/ceres/include/ceres/types.h b/extern/ceres/include/ceres/types.h
index 2ea41803629..3a19b7333b2 100644
--- a/extern/ceres/include/ceres/types.h
+++ b/extern/ceres/include/ceres/types.h
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2019 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -39,15 +39,11 @@
#include <string>
-#include "ceres/internal/port.h"
#include "ceres/internal/disable_warnings.h"
+#include "ceres/internal/port.h"
namespace ceres {
-// Basic integer types. These typedefs are in the Ceres namespace to avoid
-// conflicts with other packages having similar typedefs.
-typedef int int32;
-
// Argument type used in interfaces that can optionally take ownership
// of a passed in argument. If TAKE_OWNERSHIP is passed, the called
// object takes ownership of the pointer argument, and will call
@@ -116,10 +112,29 @@ enum PreconditionerType {
// the scene to determine the sparsity structure of the
// preconditioner. This is done using a clustering algorithm. The
// available visibility clustering algorithms are described below.
- //
- // Note: Requires SuiteSparse.
CLUSTER_JACOBI,
- CLUSTER_TRIDIAGONAL
+ CLUSTER_TRIDIAGONAL,
+
+ // Subset preconditioner is a general purpose preconditioner
+ // linear least squares problems. Given a set of residual blocks,
+ // it uses the corresponding subset of the rows of the Jacobian to
+ // construct a preconditioner.
+ //
+ // Suppose the Jacobian J has been horizontally partitioned as
+ //
+ // J = [P]
+ // [Q]
+ //
+ // Where, Q is the set of rows corresponding to the residual
+ // blocks in residual_blocks_for_subset_preconditioner.
+ //
+ // The preconditioner is the inverse of the matrix Q'Q.
+ //
+ // Obviously, the efficacy of the preconditioner depends on how
+ // well the matrix Q approximates J'J, or how well the chosen
+ // residual blocks approximate the non-linear least squares
+ // problem.
+ SUBSET,
};
enum VisibilityClusteringType {
@@ -150,7 +165,7 @@ enum SparseLinearAlgebraLibraryType {
// minimum degree ordering.
SUITE_SPARSE,
- // A lightweight replacment for SuiteSparse, which does not require
+ // A lightweight replacement for SuiteSparse, which does not require
// a LAPACK/BLAS implementation. Consequently, its performance is
// also a bit lower than SuiteSparse.
CX_SPARSE,
@@ -159,6 +174,9 @@ enum SparseLinearAlgebraLibraryType {
// the Simplicial LDLT routines.
EIGEN_SPARSE,
+ // Apple's Accelerate framework sparse linear algebra routines.
+ ACCELERATE_SPARSE,
+
// No sparse linear solver should be used. This does not necessarily
// imply that Ceres was built without any sparse library, although that
// is the likely use case, merely that one should not be used.
@@ -202,7 +220,7 @@ enum LineSearchDirectionType {
// symmetric matrix but only N conditions are specified by the Secant
// equation. The requirement that the Hessian approximation be positive
// definite imposes another N additional constraints, but that still leaves
- // remaining degrees-of-freedom. (L)BFGS methods uniquely deteremine the
+ // remaining degrees-of-freedom. (L)BFGS methods uniquely determine the
// approximate Hessian by imposing the additional constraints that the
// approximation at the next iteration must be the 'closest' to the current
// approximation (the nature of how this proximity is measured is actually
@@ -222,26 +240,26 @@ enum LineSearchDirectionType {
// For more details on BFGS see:
//
// Broyden, C.G., "The Convergence of a Class of Double-rank Minimization
- // Algorithms,"; J. Inst. Maths. Applics., Vol. 6, pp 76–90, 1970.
+ // Algorithms,"; J. Inst. Maths. Applics., Vol. 6, pp 76-90, 1970.
//
// Fletcher, R., "A New Approach to Variable Metric Algorithms,"
- // Computer Journal, Vol. 13, pp 317–322, 1970.
+ // Computer Journal, Vol. 13, pp 317-322, 1970.
//
// Goldfarb, D., "A Family of Variable Metric Updates Derived by Variational
- // Means," Mathematics of Computing, Vol. 24, pp 23–26, 1970.
+ // Means," Mathematics of Computing, Vol. 24, pp 23-26, 1970.
//
// Shanno, D.F., "Conditioning of Quasi-Newton Methods for Function
- // Minimization," Mathematics of Computing, Vol. 24, pp 647–656, 1970.
+ // Minimization," Mathematics of Computing, Vol. 24, pp 647-656, 1970.
//
// For more details on L-BFGS see:
//
// Nocedal, J. (1980). "Updating Quasi-Newton Matrices with Limited
- // Storage". Mathematics of Computation 35 (151): 773–782.
+ // Storage". Mathematics of Computation 35 (151): 773-782.
//
// 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):
- // 129–156.
+ // 129-156.
//
// A general reference for both methods:
//
@@ -250,7 +268,7 @@ enum LineSearchDirectionType {
BFGS,
};
-// Nonliner conjugate gradient methods are a generalization of the
+// Nonlinear conjugate gradient methods are a generalization of the
// method of Conjugate Gradients for linear systems. The
// generalization can be carried out in a number of different ways
// leading to number of different rules for computing the search
@@ -420,10 +438,16 @@ enum LineSearchInterpolationType {
enum CovarianceAlgorithmType {
DENSE_SVD,
- SUITE_SPARSE_QR,
- EIGEN_SPARSE_QR
+ SPARSE_QR,
};
+// It is a near impossibility that user code generates this exact
+// value in normal operation, thus we will use it to fill arrays
+// before passing them to user code. If on return an element of the
+// array still contains this value, we will assume that the user code
+// did not write to that memory location.
+const double kImpossibleValue = 1e302;
+
CERES_EXPORT const char* LinearSolverTypeToString(
LinearSolverType type);
CERES_EXPORT bool StringToLinearSolverType(std::string value,
@@ -493,6 +517,13 @@ CERES_EXPORT bool StringToNumericDiffMethodType(
std::string value,
NumericDiffMethodType* type);
+CERES_EXPORT const char* LoggingTypeToString(LoggingType type);
+CERES_EXPORT bool StringtoLoggingType(std::string value, LoggingType* type);
+
+CERES_EXPORT const char* DumpFormatTypeToString(DumpFormatType type);
+CERES_EXPORT bool StringtoDumpFormatType(std::string value, DumpFormatType* type);
+CERES_EXPORT bool StringtoDumpFormatType(std::string value, LoggingType* type);
+
CERES_EXPORT const char* TerminationTypeToString(TerminationType type);
CERES_EXPORT bool IsSchurType(LinearSolverType type);
diff --git a/extern/ceres/include/ceres/version.h b/extern/ceres/include/ceres/version.h
index 2f1cc297a38..50aa2124e75 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 2015 Google Inc. All rights reserved.
+// Copyright 2019 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -31,8 +31,8 @@
#ifndef CERES_PUBLIC_VERSION_H_
#define CERES_PUBLIC_VERSION_H_
-#define CERES_VERSION_MAJOR 1
-#define CERES_VERSION_MINOR 12
+#define CERES_VERSION_MAJOR 2
+#define CERES_VERSION_MINOR 0
#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
new file mode 100644
index 00000000000..eb04e7113d7
--- /dev/null
+++ b/extern/ceres/internal/ceres/accelerate_sparse.cc
@@ -0,0 +1,289 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2018 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: alexs.mac@gmail.com (Alex Stewart)
+
+// This include must come before any #ifndef check on Ceres compile options.
+#include "ceres/internal/port.h"
+
+#ifndef CERES_NO_ACCELERATE_SPARSE
+
+#include "ceres/accelerate_sparse.h"
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "ceres/compressed_col_sparse_matrix_utils.h"
+#include "ceres/compressed_row_sparse_matrix.h"
+#include "ceres/triplet_sparse_matrix.h"
+#include "glog/logging.h"
+
+#define CASESTR(x) case x: return #x
+
+namespace ceres {
+namespace internal {
+
+namespace {
+const char* SparseStatusToString(SparseStatus_t status) {
+ switch (status) {
+ CASESTR(SparseStatusOK);
+ CASESTR(SparseFactorizationFailed);
+ CASESTR(SparseMatrixIsSingular);
+ CASESTR(SparseInternalError);
+ CASESTR(SparseParameterError);
+ CASESTR(SparseStatusReleased);
+ default:
+ return "UKNOWN";
+ }
+}
+} // namespace.
+
+// Resizes workspace as required to contain at least required_size bytes
+// aligned to kAccelerateRequiredAlignment and returns a pointer to the
+// aligned start.
+void* ResizeForAccelerateAlignment(const size_t required_size,
+ std::vector<uint8_t> *workspace) {
+ // As per the Accelerate documentation, all workspace memory passed to the
+ // sparse solver functions must be 16-byte aligned.
+ constexpr int kAccelerateRequiredAlignment = 16;
+ // Although malloc() on macOS should always be 16-byte aligned, it is unclear
+ // if this holds for new(), or on other Apple OSs (phoneOS, watchOS etc).
+ // As such we assume it is not and use std::align() to create a (potentially
+ // offset) 16-byte aligned sub-buffer of the specified size within workspace.
+ workspace->resize(required_size + kAccelerateRequiredAlignment);
+ size_t size_from_aligned_start = workspace->size();
+ void* aligned_solve_workspace_start =
+ reinterpret_cast<void*>(workspace->data());
+ aligned_solve_workspace_start =
+ std::align(kAccelerateRequiredAlignment,
+ required_size,
+ aligned_solve_workspace_start,
+ size_from_aligned_start);
+ CHECK(aligned_solve_workspace_start != nullptr)
+ << "required_size: " << required_size
+ << ", workspace size: " << workspace->size();
+ return aligned_solve_workspace_start;
+}
+
+template<typename Scalar>
+void AccelerateSparse<Scalar>::Solve(NumericFactorization* numeric_factor,
+ DenseVector* rhs_and_solution) {
+ // From SparseSolve() documentation in Solve.h
+ const int required_size =
+ numeric_factor->solveWorkspaceRequiredStatic +
+ numeric_factor->solveWorkspaceRequiredPerRHS;
+ SparseSolve(*numeric_factor, *rhs_and_solution,
+ ResizeForAccelerateAlignment(required_size, &solve_workspace_));
+}
+
+template<typename Scalar>
+typename AccelerateSparse<Scalar>::ASSparseMatrix
+AccelerateSparse<Scalar>::CreateSparseMatrixTransposeView(
+ CompressedRowSparseMatrix* A) {
+ // Accelerate uses CSC as its sparse storage format whereas Ceres uses CSR.
+ // As this method returns the transpose view we can flip rows/cols to map
+ // from CSR to CSC^T.
+ //
+ // Accelerate's columnStarts is a long*, not an int*. These types might be
+ // different (e.g. ARM on iOS) so always make a copy.
+ column_starts_.resize(A->num_rows() +1); // +1 for final column length.
+ std::copy_n(A->rows(), column_starts_.size(), &column_starts_[0]);
+
+ ASSparseMatrix At;
+ At.structure.rowCount = A->num_cols();
+ At.structure.columnCount = A->num_rows();
+ At.structure.columnStarts = &column_starts_[0];
+ At.structure.rowIndices = A->mutable_cols();
+ At.structure.attributes.transpose = false;
+ At.structure.attributes.triangle = SparseUpperTriangle;
+ At.structure.attributes.kind = SparseSymmetric;
+ At.structure.attributes._reserved = 0;
+ At.structure.attributes._allocatedBySparse = 0;
+ At.structure.blockSize = 1;
+ if (std::is_same<Scalar, double>::value) {
+ At.data = reinterpret_cast<Scalar*>(A->mutable_values());
+ } else {
+ values_ =
+ ConstVectorRef(A->values(), A->num_nonzeros()).template cast<Scalar>();
+ At.data = values_.data();
+ }
+ return At;
+}
+
+template<typename Scalar>
+typename AccelerateSparse<Scalar>::SymbolicFactorization
+AccelerateSparse<Scalar>::AnalyzeCholesky(ASSparseMatrix* A) {
+ return SparseFactor(SparseFactorizationCholesky, A->structure);
+}
+
+template<typename Scalar>
+typename AccelerateSparse<Scalar>::NumericFactorization
+AccelerateSparse<Scalar>::Cholesky(ASSparseMatrix* A,
+ SymbolicFactorization* symbolic_factor) {
+ return SparseFactor(*symbolic_factor, *A);
+}
+
+template<typename Scalar>
+void AccelerateSparse<Scalar>::Cholesky(ASSparseMatrix* A,
+ NumericFactorization* numeric_factor) {
+ // From SparseRefactor() documentation in Solve.h
+ const int required_size = std::is_same<Scalar, double>::value
+ ? numeric_factor->symbolicFactorization.workspaceSize_Double
+ : numeric_factor->symbolicFactorization.workspaceSize_Float;
+ return SparseRefactor(*A, numeric_factor,
+ ResizeForAccelerateAlignment(required_size,
+ &factorization_workspace_));
+}
+
+// Instantiate only for the specific template types required/supported s/t the
+// definition can be in the .cc file.
+template class AccelerateSparse<double>;
+template class AccelerateSparse<float>;
+
+template<typename Scalar>
+std::unique_ptr<SparseCholesky>
+AppleAccelerateCholesky<Scalar>::Create(OrderingType ordering_type) {
+ return std::unique_ptr<SparseCholesky>(
+ new AppleAccelerateCholesky<Scalar>(ordering_type));
+}
+
+template<typename Scalar>
+AppleAccelerateCholesky<Scalar>::AppleAccelerateCholesky(
+ const OrderingType ordering_type)
+ : ordering_type_(ordering_type) {}
+
+template<typename Scalar>
+AppleAccelerateCholesky<Scalar>::~AppleAccelerateCholesky() {
+ FreeSymbolicFactorization();
+ FreeNumericFactorization();
+}
+
+template<typename Scalar>
+CompressedRowSparseMatrix::StorageType
+AppleAccelerateCholesky<Scalar>::StorageType() const {
+ return CompressedRowSparseMatrix::LOWER_TRIANGULAR;
+}
+
+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.";
+ 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)));
+ if (symbolic_factor_->status != SparseStatusOK) {
+ *message = StringPrintf(
+ "Apple Accelerate Failure : Symbolic factorisation failed: %s",
+ SparseStatusToString(symbolic_factor_->status));
+ FreeSymbolicFactorization();
+ return LINEAR_SOLVER_FATAL_ERROR;
+ }
+ }
+
+ if (!numeric_factor_) {
+ numeric_factor_.reset(
+ new 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());
+ }
+ if (numeric_factor_->status != SparseStatusOK) {
+ *message = StringPrintf(
+ "Apple Accelerate Failure : Numeric factorisation failed: %s",
+ SparseStatusToString(numeric_factor_->status));
+ FreeNumericFactorization();
+ return LINEAR_SOLVER_FAILURE;
+ }
+
+ return LINEAR_SOLVER_SUCCESS;
+}
+
+template<typename Scalar>
+LinearSolverTerminationType
+AppleAccelerateCholesky<Scalar>::Solve(const double* rhs,
+ double* solution,
+ std::string* message) {
+ CHECK_EQ(numeric_factor_->status, SparseStatusOK)
+ << "Solve called without a call to Factorize first ("
+ << SparseStatusToString(numeric_factor_->status) << ").";
+ const int num_cols = numeric_factor_->symbolicFactorization.columnCount;
+
+ typename SparseTypesTrait<Scalar>::DenseVector as_rhs_and_solution;
+ as_rhs_and_solution.count = num_cols;
+ if (std::is_same<Scalar, double>::value) {
+ as_rhs_and_solution.data = reinterpret_cast<Scalar*>(solution);
+ std::copy_n(rhs, num_cols, solution);
+ } else {
+ scalar_rhs_and_solution_ =
+ ConstVectorRef(rhs, num_cols).template cast<Scalar>();
+ as_rhs_and_solution.data = scalar_rhs_and_solution_.data();
+ }
+ as_.Solve(numeric_factor_.get(), &as_rhs_and_solution);
+ if (!std::is_same<Scalar, double>::value) {
+ VectorRef(solution, num_cols) =
+ scalar_rhs_and_solution_.template cast<double>();
+ }
+ return LINEAR_SOLVER_SUCCESS;
+}
+
+template<typename Scalar>
+void AppleAccelerateCholesky<Scalar>::FreeSymbolicFactorization() {
+ if (symbolic_factor_) {
+ SparseCleanup(*symbolic_factor_);
+ symbolic_factor_.reset();
+ }
+}
+
+template<typename Scalar>
+void AppleAccelerateCholesky<Scalar>::FreeNumericFactorization() {
+ if (numeric_factor_) {
+ SparseCleanup(*numeric_factor_);
+ numeric_factor_.reset();
+ }
+}
+
+// Instantiate only for the specific template types required/supported s/t the
+// definition can be in the .cc file.
+template class AppleAccelerateCholesky<double>;
+template class AppleAccelerateCholesky<float>;
+
+}
+}
+
+#endif // CERES_NO_ACCELERATE_SPARSE
diff --git a/extern/ceres/internal/ceres/accelerate_sparse.h b/extern/ceres/internal/ceres/accelerate_sparse.h
new file mode 100644
index 00000000000..43b4ea5fd70
--- /dev/null
+++ b/extern/ceres/internal/ceres/accelerate_sparse.h
@@ -0,0 +1,147 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2018 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: alexs.mac@gmail.com (Alex Stewart)
+
+#ifndef CERES_INTERNAL_ACCELERATE_SPARSE_H_
+#define CERES_INTERNAL_ACCELERATE_SPARSE_H_
+
+// This include must come before any #ifndef check on Ceres compile options.
+#include "ceres/internal/port.h"
+
+#ifndef CERES_NO_ACCELERATE_SPARSE
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "ceres/linear_solver.h"
+#include "ceres/sparse_cholesky.h"
+#include "Accelerate.h"
+
+namespace ceres {
+namespace internal {
+
+class CompressedRowSparseMatrix;
+class TripletSparseMatrix;
+
+template<typename Scalar>
+struct SparseTypesTrait {
+};
+
+template<>
+struct SparseTypesTrait<double> {
+ typedef DenseVector_Double DenseVector;
+ typedef SparseMatrix_Double SparseMatrix;
+ typedef SparseOpaqueSymbolicFactorization SymbolicFactorization;
+ typedef SparseOpaqueFactorization_Double NumericFactorization;
+};
+
+template<>
+struct SparseTypesTrait<float> {
+ typedef DenseVector_Float DenseVector;
+ typedef SparseMatrix_Float SparseMatrix;
+ typedef SparseOpaqueSymbolicFactorization SymbolicFactorization;
+ typedef SparseOpaqueFactorization_Float NumericFactorization;
+};
+
+template<typename Scalar>
+class AccelerateSparse {
+ public:
+ using DenseVector = typename SparseTypesTrait<Scalar>::DenseVector;
+ // Use ASSparseMatrix to avoid collision with ceres::internal::SparseMatrix.
+ using ASSparseMatrix = typename SparseTypesTrait<Scalar>::SparseMatrix;
+ using SymbolicFactorization = typename SparseTypesTrait<Scalar>::SymbolicFactorization;
+ using NumericFactorization = typename SparseTypesTrait<Scalar>::NumericFactorization;
+
+ // Solves a linear system given its symbolic (reference counted within
+ // NumericFactorization) and numeric factorization.
+ void Solve(NumericFactorization* numeric_factor,
+ DenseVector* rhs_and_solution);
+
+ // Note: Accelerate's API passes/returns its objects by value, but as the
+ // objects contain pointers to the underlying data these copies are
+ // all shallow (in some cases Accelerate also reference counts the
+ // objects internally).
+ ASSparseMatrix CreateSparseMatrixTransposeView(CompressedRowSparseMatrix* A);
+ // Computes a symbolic factorisation of A that can be used in Solve().
+ SymbolicFactorization AnalyzeCholesky(ASSparseMatrix* A);
+ // Compute the numeric Cholesky factorization of A, given its
+ // symbolic factorization.
+ NumericFactorization Cholesky(ASSparseMatrix* A,
+ SymbolicFactorization* symbolic_factor);
+ // Reuse the NumericFactorization from a previous matrix with the same
+ // symbolic factorization to represent a new numeric factorization.
+ void Cholesky(ASSparseMatrix* A, NumericFactorization* numeric_factor);
+
+ private:
+ std::vector<long> column_starts_;
+ std::vector<uint8_t> solve_workspace_;
+ std::vector<uint8_t> factorization_workspace_;
+ // Storage for the values of A if Scalar != double (necessitating a copy).
+ Eigen::Matrix<Scalar, Eigen::Dynamic, 1> values_;
+};
+
+// An implementation of SparseCholesky interface using Apple's Accelerate
+// framework.
+template<typename Scalar>
+class AppleAccelerateCholesky : public SparseCholesky {
+ public:
+ // Factory
+ static std::unique_ptr<SparseCholesky> Create(OrderingType ordering_type);
+
+ // SparseCholesky interface.
+ virtual ~AppleAccelerateCholesky();
+ CompressedRowSparseMatrix::StorageType StorageType() const;
+ LinearSolverTerminationType Factorize(CompressedRowSparseMatrix* lhs,
+ std::string* message) final;
+ LinearSolverTerminationType Solve(const double* rhs,
+ double* solution,
+ std::string* message) final ;
+
+ private:
+ AppleAccelerateCholesky(const OrderingType ordering_type);
+ void FreeSymbolicFactorization();
+ void FreeNumericFactorization();
+
+ const OrderingType ordering_type_;
+ AccelerateSparse<Scalar> as_;
+ std::unique_ptr<typename AccelerateSparse<Scalar>::SymbolicFactorization>
+ symbolic_factor_;
+ std::unique_ptr<typename AccelerateSparse<Scalar>::NumericFactorization>
+ numeric_factor_;
+ // Copy of rhs/solution if Scalar != double (necessitating a copy).
+ Eigen::Matrix<Scalar, Eigen::Dynamic, 1> scalar_rhs_and_solution_;
+};
+
+}
+}
+
+#endif // CERES_NO_ACCELERATE_SPARSE
+
+#endif // CERES_INTERNAL_ACCELERATE_SPARSE_H_
diff --git a/extern/ceres/internal/ceres/array_utils.cc b/extern/ceres/internal/ceres/array_utils.cc
index 7be3c78ce24..32459e6dcd9 100644
--- a/extern/ceres/internal/ceres/array_utils.cc
+++ b/extern/ceres/internal/ceres/array_utils.cc
@@ -35,25 +35,17 @@
#include <cstddef>
#include <string>
#include <vector>
-#include "ceres/fpclassify.h"
#include "ceres/stringprintf.h"
-
+#include "ceres/types.h"
namespace ceres {
namespace internal {
using std::string;
-// It is a near impossibility that user code generates this exact
-// value in normal operation, thus we will use it to fill arrays
-// before passing them to user code. If on return an element of the
-// array still contains this value, we will assume that the user code
-// did not write to that memory location.
-const double kImpossibleValue = 1e302;
-
bool IsArrayValid(const int size, const double* x) {
if (x != NULL) {
for (int i = 0; i < size; ++i) {
- if (!IsFinite(x[i]) || (x[i] == kImpossibleValue)) {
+ if (!std::isfinite(x[i]) || (x[i] == kImpossibleValue)) {
return false;
}
}
@@ -67,7 +59,7 @@ int FindInvalidValue(const int size, const double* x) {
}
for (int i = 0; i < size; ++i) {
- if (!IsFinite(x[i]) || (x[i] == kImpossibleValue)) {
+ if (!std::isfinite(x[i]) || (x[i] == kImpossibleValue)) {
return i;
}
}
diff --git a/extern/ceres/internal/ceres/array_utils.h b/extern/ceres/internal/ceres/array_utils.h
index 2d2ffca8809..1d55733b7b8 100644
--- a/extern/ceres/internal/ceres/array_utils.h
+++ b/extern/ceres/internal/ceres/array_utils.h
@@ -66,11 +66,9 @@ int FindInvalidValue(const int size, const double* x);
// array pointer is NULL, it is treated as an array of zeros.
void AppendArrayToString(const int size, const double* x, std::string* result);
-extern const double kImpossibleValue;
-
// 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
-// sorted+uniqued array. By doing this, if there are are k unique
+// sorted+uniqued array. By doing this, if there are k unique
// values in the array, each value is replaced by an integer in the
// range [0, k-1], while preserving their relative order.
//
diff --git a/extern/ceres/internal/ceres/block_jacobi_preconditioner.cc b/extern/ceres/internal/ceres/block_jacobi_preconditioner.cc
index 22d4b351c51..772c7af2ba5 100644
--- a/extern/ceres/internal/ceres/block_jacobi_preconditioner.cc
+++ b/extern/ceres/internal/ceres/block_jacobi_preconditioner.cc
@@ -34,7 +34,6 @@
#include "ceres/block_structure.h"
#include "ceres/block_random_access_diagonal_matrix.h"
#include "ceres/casts.h"
-#include "ceres/integral_types.h"
#include "ceres/internal/eigen.h"
namespace ceres {
diff --git a/extern/ceres/internal/ceres/block_jacobi_preconditioner.h b/extern/ceres/internal/ceres/block_jacobi_preconditioner.h
index 14007295823..856b506e073 100644
--- a/extern/ceres/internal/ceres/block_jacobi_preconditioner.h
+++ b/extern/ceres/internal/ceres/block_jacobi_preconditioner.h
@@ -31,9 +31,8 @@
#ifndef CERES_INTERNAL_BLOCK_JACOBI_PRECONDITIONER_H_
#define CERES_INTERNAL_BLOCK_JACOBI_PRECONDITIONER_H_
-#include <vector>
+#include <memory>
#include "ceres/block_random_access_diagonal_matrix.h"
-#include "ceres/internal/scoped_ptr.h"
#include "ceres/preconditioner.h"
namespace ceres {
@@ -56,18 +55,21 @@ class BlockJacobiPreconditioner : public BlockSparseMatrixPreconditioner {
public:
// A must remain valid while the BlockJacobiPreconditioner is.
explicit BlockJacobiPreconditioner(const BlockSparseMatrix& A);
+ BlockJacobiPreconditioner(const BlockJacobiPreconditioner&) = delete;
+ void operator=(const BlockJacobiPreconditioner&) = delete;
+
virtual ~BlockJacobiPreconditioner();
// Preconditioner interface
- virtual void RightMultiply(const double* x, double* y) const;
- virtual int num_rows() const { return m_->num_rows(); }
- virtual int num_cols() const { return m_->num_rows(); }
-
+ void RightMultiply(const double* x, double* y) const final;
+ int num_rows() const final { return m_->num_rows(); }
+ int num_cols() const final { return m_->num_rows(); }
const BlockRandomAccessDiagonalMatrix& matrix() const { return *m_; }
+
private:
- virtual bool UpdateImpl(const BlockSparseMatrix& A, const double* D);
+ bool UpdateImpl(const BlockSparseMatrix& A, const double* D) final;
- scoped_ptr<BlockRandomAccessDiagonalMatrix> m_;
+ std::unique_ptr<BlockRandomAccessDiagonalMatrix> m_;
};
} // namespace internal
diff --git a/extern/ceres/internal/ceres/block_jacobian_writer.cc b/extern/ceres/internal/ceres/block_jacobian_writer.cc
index 7a3fee4fbdf..6998bd66e61 100644
--- a/extern/ceres/internal/ceres/block_jacobian_writer.cc
+++ b/extern/ceres/internal/ceres/block_jacobian_writer.cc
@@ -37,7 +37,6 @@
#include "ceres/residual_block.h"
#include "ceres/internal/eigen.h"
#include "ceres/internal/port.h"
-#include "ceres/internal/scoped_ptr.h"
namespace ceres {
namespace internal {
@@ -206,7 +205,7 @@ SparseMatrix* BlockJacobianWriter::CreateJacobian() const {
}
BlockSparseMatrix* jacobian = new BlockSparseMatrix(bs);
- CHECK_NOTNULL(jacobian);
+ CHECK(jacobian != nullptr);
return jacobian;
}
diff --git a/extern/ceres/internal/ceres/block_jacobian_writer.h b/extern/ceres/internal/ceres/block_jacobian_writer.h
index 8e6f45130a4..c94a0d3f909 100644
--- a/extern/ceres/internal/ceres/block_jacobian_writer.h
+++ b/extern/ceres/internal/ceres/block_jacobian_writer.h
@@ -49,6 +49,7 @@ class BlockEvaluatePreparer;
class Program;
class SparseMatrix;
+// TODO(sameeragarwal): This class needs documemtation.
class BlockJacobianWriter {
public:
BlockJacobianWriter(const Evaluator::Options& options,
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 61748ef6f7f..f567aa5816b 100644
--- a/extern/ceres/internal/ceres/block_random_access_dense_matrix.cc
+++ b/extern/ceres/internal/ceres/block_random_access_dense_matrix.cc
@@ -32,7 +32,6 @@
#include <vector>
#include "ceres/internal/eigen.h"
-#include "ceres/internal/scoped_ptr.h"
#include "glog/logging.h"
namespace ceres {
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 89689082561..8c5e2527ec1 100644
--- a/extern/ceres/internal/ceres/block_random_access_dense_matrix.h
+++ b/extern/ceres/internal/ceres/block_random_access_dense_matrix.h
@@ -33,11 +33,10 @@
#include "ceres/block_random_access_matrix.h"
+#include <memory>
#include <vector>
-#include "ceres/internal/macros.h"
#include "ceres/internal/port.h"
-#include "ceres/internal/scoped_ptr.h"
namespace ceres {
namespace internal {
@@ -57,27 +56,29 @@ class BlockRandomAccessDenseMatrix : public BlockRandomAccessMatrix {
// blocks is a vector of block sizes. The resulting matrix has
// blocks.size() * blocks.size() cells.
explicit BlockRandomAccessDenseMatrix(const std::vector<int>& blocks);
+ BlockRandomAccessDenseMatrix(const BlockRandomAccessDenseMatrix&) = delete;
+ void operator=(const BlockRandomAccessDenseMatrix&) = delete;
// The destructor is not thread safe. It assumes that no one is
// modifying any cells when the matrix is being destroyed.
virtual ~BlockRandomAccessDenseMatrix();
// BlockRandomAccessMatrix interface.
- virtual CellInfo* GetCell(int row_block_id,
- int col_block_id,
- int* row,
- int* col,
- int* row_stride,
- int* col_stride);
+ CellInfo* GetCell(int row_block_id,
+ int col_block_id,
+ int* row,
+ int* col,
+ int* row_stride,
+ int* col_stride) final;
// This is not a thread safe method, it assumes that no cell is
// locked.
- virtual void SetZero();
+ void SetZero() final;
// Since the matrix is square with the same row and column block
// structure, num_rows() = num_cols().
- virtual int num_rows() const { return num_rows_; }
- virtual int num_cols() const { return num_rows_; }
+ int num_rows() const final { return num_rows_; }
+ int num_cols() const final { return num_rows_; }
// The underlying matrix storing the cells.
const double* values() const { return values_.get(); }
@@ -86,10 +87,8 @@ class BlockRandomAccessDenseMatrix : public BlockRandomAccessMatrix {
private:
int num_rows_;
std::vector<int> block_layout_;
- scoped_array<double> values_;
- scoped_array<CellInfo> cell_infos_;
-
- CERES_DISALLOW_COPY_AND_ASSIGN(BlockRandomAccessDenseMatrix);
+ std::unique_ptr<double[]> values_;
+ std::unique_ptr<CellInfo[]> cell_infos_;
};
} // namespace internal
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 052690d18be..526d173e4b0 100644
--- a/extern/ceres/internal/ceres/block_random_access_diagonal_matrix.cc
+++ b/extern/ceres/internal/ceres/block_random_access_diagonal_matrix.cc
@@ -34,9 +34,9 @@
#include <set>
#include <utility>
#include <vector>
+
#include "Eigen/Dense"
#include "ceres/internal/port.h"
-#include "ceres/internal/scoped_ptr.h"
#include "ceres/stl_util.h"
#include "ceres/triplet_sparse_matrix.h"
#include "ceres/types.h"
@@ -137,8 +137,8 @@ void BlockRandomAccessDiagonalMatrix::Invert() {
void BlockRandomAccessDiagonalMatrix::RightMultiply(const double* x,
double* y) const {
- CHECK_NOTNULL(x);
- CHECK_NOTNULL(y);
+ 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];
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 07ffc9d4a0d..3bda7d19074 100644
--- a/extern/ceres/internal/ceres/block_random_access_diagonal_matrix.h
+++ b/extern/ceres/internal/ceres/block_random_access_diagonal_matrix.h
@@ -31,17 +31,14 @@
#ifndef CERES_INTERNAL_BLOCK_RANDOM_ACCESS_DIAGONAL_MATRIX_H_
#define CERES_INTERNAL_BLOCK_RANDOM_ACCESS_DIAGONAL_MATRIX_H_
+#include <memory>
#include <set>
-#include <vector>
#include <utility>
-#include "ceres/mutex.h"
+#include <vector>
+
#include "ceres/block_random_access_matrix.h"
-#include "ceres/collections_port.h"
-#include "ceres/triplet_sparse_matrix.h"
-#include "ceres/integral_types.h"
-#include "ceres/internal/macros.h"
#include "ceres/internal/port.h"
-#include "ceres/internal/scoped_ptr.h"
+#include "ceres/triplet_sparse_matrix.h"
#include "ceres/types.h"
namespace ceres {
@@ -53,22 +50,24 @@ class BlockRandomAccessDiagonalMatrix : public BlockRandomAccessMatrix {
public:
// blocks is an array of block sizes.
explicit BlockRandomAccessDiagonalMatrix(const std::vector<int>& blocks);
+ BlockRandomAccessDiagonalMatrix(const BlockRandomAccessDiagonalMatrix&) = delete;
+ void operator=(const BlockRandomAccessDiagonalMatrix&) = delete;
// The destructor is not thread safe. It assumes that no one is
// modifying any cells when the matrix is being destroyed.
virtual ~BlockRandomAccessDiagonalMatrix();
// BlockRandomAccessMatrix Interface.
- virtual CellInfo* GetCell(int row_block_id,
- int col_block_id,
- int* row,
- int* col,
- int* row_stride,
- int* col_stride);
+ CellInfo* GetCell(int row_block_id,
+ int col_block_id,
+ int* row,
+ int* col,
+ int* row_stride,
+ int* col_stride) final;
// This is not a thread safe method, it assumes that no cell is
// locked.
- virtual void SetZero();
+ void SetZero() final;
// Invert the matrix assuming that each block is positive definite.
void Invert();
@@ -77,8 +76,8 @@ class BlockRandomAccessDiagonalMatrix : public BlockRandomAccessMatrix {
void RightMultiply(const double* x, double* y) const;
// Since the matrix is square, num_rows() == num_cols().
- virtual int num_rows() const { return tsm_->num_rows(); }
- virtual int num_cols() const { return tsm_->num_cols(); }
+ int num_rows() const final { return tsm_->num_rows(); }
+ int num_cols() const final { return tsm_->num_cols(); }
const TripletSparseMatrix* matrix() const { return tsm_.get(); }
TripletSparseMatrix* mutable_matrix() { return tsm_.get(); }
@@ -89,10 +88,9 @@ class BlockRandomAccessDiagonalMatrix : public BlockRandomAccessMatrix {
std::vector<CellInfo*> layout_;
// The underlying matrix object which actually stores the cells.
- scoped_ptr<TripletSparseMatrix> tsm_;
+ std::unique_ptr<TripletSparseMatrix> tsm_;
friend class BlockRandomAccessDiagonalMatrixTest;
- CERES_DISALLOW_COPY_AND_ASSIGN(BlockRandomAccessDiagonalMatrix);
};
} // namespace internal
diff --git a/extern/ceres/internal/ceres/block_random_access_matrix.h b/extern/ceres/internal/ceres/block_random_access_matrix.h
index 34c8bf5cd4d..6fcf0dc8a7c 100644
--- a/extern/ceres/internal/ceres/block_random_access_matrix.h
+++ b/extern/ceres/internal/ceres/block_random_access_matrix.h
@@ -33,7 +33,7 @@
#ifndef CERES_INTERNAL_BLOCK_RANDOM_ACCESS_MATRIX_H_
#define CERES_INTERNAL_BLOCK_RANDOM_ACCESS_MATRIX_H_
-#include "ceres/mutex.h"
+#include <mutex>
namespace ceres {
namespace internal {
@@ -77,23 +77,18 @@ namespace internal {
//
// if (cell != NULL) {
// MatrixRef m(cell->values, row_stride, col_stride);
-// CeresMutexLock l(&cell->m);
+// std::lock_guard<std::mutex> l(&cell->m);
// m.block(row, col, row_block_size, col_block_size) = ...
// }
// Structure to carry a pointer to the array containing a cell and the
-// Mutex guarding it.
+// mutex guarding it.
struct CellInfo {
- CellInfo()
- : values(NULL) {
- }
-
- explicit CellInfo(double* ptr)
- : values(ptr) {
- }
+ CellInfo() : values(nullptr) {}
+ explicit CellInfo(double* values) : values(values) {}
double* values;
- Mutex m;
+ std::mutex m;
};
class BlockRandomAccessMatrix {
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 5432ec1064a..9c164546635 100644
--- a/extern/ceres/internal/ceres/block_random_access_sparse_matrix.cc
+++ b/extern/ceres/internal/ceres/block_random_access_sparse_matrix.cc
@@ -31,12 +31,12 @@
#include "ceres/block_random_access_sparse_matrix.h"
#include <algorithm>
+#include <memory>
#include <set>
#include <utility>
#include <vector>
+
#include "ceres/internal/port.h"
-#include "ceres/internal/scoped_ptr.h"
-#include "ceres/mutex.h"
#include "ceres/triplet_sparse_matrix.h"
#include "ceres/types.h"
#include "glog/logging.h"
@@ -51,7 +51,7 @@ using std::vector;
BlockRandomAccessSparseMatrix::BlockRandomAccessSparseMatrix(
const vector<int>& blocks,
- const set<pair<int, int> >& block_pairs)
+ const set<pair<int, int>>& block_pairs)
: kMaxRowBlocks(10 * 1000 * 1000),
blocks_(blocks) {
CHECK_LT(blocks.size(), kMaxRowBlocks);
@@ -69,11 +69,9 @@ BlockRandomAccessSparseMatrix::BlockRandomAccessSparseMatrix(
// object for looking into the values array of the
// TripletSparseMatrix.
int num_nonzeros = 0;
- for (set<pair<int, int> >::const_iterator it = block_pairs.begin();
- it != block_pairs.end();
- ++it) {
- const int row_block_size = blocks_[it->first];
- const int col_block_size = blocks_[it->second];
+ 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];
num_nonzeros += row_block_size * col_block_size;
}
@@ -88,24 +86,19 @@ BlockRandomAccessSparseMatrix::BlockRandomAccessSparseMatrix(
double* values = tsm_->mutable_values();
int pos = 0;
- for (set<pair<int, int> >::const_iterator it = block_pairs.begin();
- it != block_pairs.end();
- ++it) {
- const int row_block_size = blocks_[it->first];
- const int col_block_size = blocks_[it->second];
- cell_values_.push_back(make_pair(make_pair(it->first, it->second),
- values + pos));
- layout_[IntPairToLong(it->first, it->second)] =
+ 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));
+ layout_[IntPairToLong(block_pair.first, block_pair.second)] =
new CellInfo(values + pos);
pos += row_block_size * col_block_size;
}
// Fill the sparsity pattern of the underlying matrix.
- for (set<pair<int, int> >::const_iterator it = block_pairs.begin();
- it != block_pairs.end();
- ++it) {
- const int row_block_id = it->first;
- const int col_block_id = it->second;
+ for (const auto& block_pair : block_pairs) {
+ const int row_block_id = block_pair.first;
+ const int col_block_id = block_pair.second;
const int row_block_size = blocks_[row_block_id];
const int col_block_size = blocks_[col_block_id];
int pos =
@@ -125,10 +118,8 @@ BlockRandomAccessSparseMatrix::BlockRandomAccessSparseMatrix(
// Assume that the user does not hold any locks on any cell blocks
// when they are calling SetZero.
BlockRandomAccessSparseMatrix::~BlockRandomAccessSparseMatrix() {
- for (LayoutType::iterator it = layout_.begin();
- it != layout_.end();
- ++it) {
- delete it->second;
+ for (const auto& entry : layout_) {
+ delete entry.second;
}
}
@@ -163,19 +154,17 @@ void BlockRandomAccessSparseMatrix::SetZero() {
void BlockRandomAccessSparseMatrix::SymmetricRightMultiply(const double* x,
double* y) const {
- vector< pair<pair<int, int>, double*> >::const_iterator it =
- cell_values_.begin();
- for (; it != cell_values_.end(); ++it) {
- const int row = it->first.first;
+ for (const auto& cell_position_and_data : cell_values_) {
+ const int row = cell_position_and_data.first.first;
const int row_block_size = blocks_[row];
const int row_block_pos = block_positions_[row];
- const int col = it->first.second;
+ const int col = cell_position_and_data.first.second;
const int col_block_size = blocks_[col];
const int col_block_pos = block_positions_[col];
MatrixVectorMultiply<Eigen::Dynamic, Eigen::Dynamic, 1>(
- it->second, row_block_size, col_block_size,
+ cell_position_and_data.second, row_block_size, col_block_size,
x + col_block_pos,
y + row_block_pos);
@@ -185,7 +174,7 @@ void BlockRandomAccessSparseMatrix::SymmetricRightMultiply(const double* x,
// triangular multiply also.
if (row != col) {
MatrixTransposeVectorMultiply<Eigen::Dynamic, Eigen::Dynamic, 1>(
- it->second, row_block_size, col_block_size,
+ cell_position_and_data.second, row_block_size, col_block_size,
x + row_block_pos,
y + col_block_pos);
}
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 2b3c7fdabae..d542a3d64e3 100644
--- a/extern/ceres/internal/ceres/block_random_access_sparse_matrix.h
+++ b/extern/ceres/internal/ceres/block_random_access_sparse_matrix.h
@@ -31,17 +31,16 @@
#ifndef CERES_INTERNAL_BLOCK_RANDOM_ACCESS_SPARSE_MATRIX_H_
#define CERES_INTERNAL_BLOCK_RANDOM_ACCESS_SPARSE_MATRIX_H_
+#include <cstdint>
+#include <memory>
#include <set>
-#include <vector>
+#include <unordered_map>
#include <utility>
-#include "ceres/mutex.h"
+#include <vector>
+
#include "ceres/block_random_access_matrix.h"
-#include "ceres/collections_port.h"
#include "ceres/triplet_sparse_matrix.h"
-#include "ceres/integral_types.h"
-#include "ceres/internal/macros.h"
#include "ceres/internal/port.h"
-#include "ceres/internal/scoped_ptr.h"
#include "ceres/types.h"
#include "ceres/small_blas.h"
@@ -59,23 +58,25 @@ class BlockRandomAccessSparseMatrix : public BlockRandomAccessMatrix {
// of this matrix.
BlockRandomAccessSparseMatrix(
const std::vector<int>& blocks,
- const std::set<std::pair<int, int> >& block_pairs);
+ const std::set<std::pair<int, int>>& block_pairs);
+ BlockRandomAccessSparseMatrix(const BlockRandomAccessSparseMatrix&) = delete;
+ void operator=(const BlockRandomAccessSparseMatrix&) = delete;
// The destructor is not thread safe. It assumes that no one is
// modifying any cells when the matrix is being destroyed.
virtual ~BlockRandomAccessSparseMatrix();
// BlockRandomAccessMatrix Interface.
- virtual CellInfo* GetCell(int row_block_id,
- int col_block_id,
- int* row,
- int* col,
- int* row_stride,
- int* col_stride);
+ CellInfo* GetCell(int row_block_id,
+ int col_block_id,
+ int* row,
+ int* col,
+ int* row_stride,
+ int* col_stride) final;
// This is not a thread safe method, it assumes that no cell is
// locked.
- virtual void SetZero();
+ void SetZero() final;
// Assume that the matrix is symmetric and only one half of the
// matrix is stored.
@@ -84,24 +85,24 @@ class BlockRandomAccessSparseMatrix : public BlockRandomAccessMatrix {
void SymmetricRightMultiply(const double* x, double* y) const;
// Since the matrix is square, num_rows() == num_cols().
- virtual int num_rows() const { return tsm_->num_rows(); }
- virtual int num_cols() const { return tsm_->num_cols(); }
+ int num_rows() const final { return tsm_->num_rows(); }
+ int num_cols() const final { return tsm_->num_cols(); }
// Access to the underlying matrix object.
const TripletSparseMatrix* matrix() const { return tsm_.get(); }
TripletSparseMatrix* mutable_matrix() { return tsm_.get(); }
private:
- int64 IntPairToLong(int row, int col) const {
+ int64_t IntPairToLong(int row, int col) const {
return row * kMaxRowBlocks + col;
}
- void LongToIntPair(int64 index, int* row, int* col) const {
+ void LongToIntPair(int64_t index, int* row, int* col) const {
*row = index / kMaxRowBlocks;
*col = index % kMaxRowBlocks;
}
- const int64 kMaxRowBlocks;
+ const int64_t kMaxRowBlocks;
// row/column block sizes.
const std::vector<int> blocks_;
@@ -109,18 +110,17 @@ class BlockRandomAccessSparseMatrix : public BlockRandomAccessMatrix {
// A mapping from <row_block_id, col_block_id> to the position in
// the values array of tsm_ where the block is stored.
- typedef HashMap<long int, CellInfo* > LayoutType;
+ typedef std::unordered_map<long int, CellInfo* > LayoutType;
LayoutType layout_;
// In order traversal of contents of the matrix. This allows us to
// implement a matrix-vector which is 20% faster than using the
// iterator in the Layout object instead.
- std::vector<std::pair<std::pair<int, int>, double*> > cell_values_;
+ std::vector<std::pair<std::pair<int, int>, double*>> cell_values_;
// The underlying matrix object which actually stores the cells.
- scoped_ptr<TripletSparseMatrix> tsm_;
+ std::unique_ptr<TripletSparseMatrix> tsm_;
friend class BlockRandomAccessSparseMatrixTest;
- CERES_DISALLOW_COPY_AND_ASSIGN(BlockRandomAccessSparseMatrix);
};
} // namespace internal
diff --git a/extern/ceres/internal/ceres/block_sparse_matrix.cc b/extern/ceres/internal/ceres/block_sparse_matrix.cc
index 68d0780156c..8f50f3561e2 100644
--- a/extern/ceres/internal/ceres/block_sparse_matrix.cc
+++ b/extern/ceres/internal/ceres/block_sparse_matrix.cc
@@ -35,6 +35,7 @@
#include <vector>
#include "ceres/block_structure.h"
#include "ceres/internal/eigen.h"
+#include "ceres/random.h"
#include "ceres/small_blas.h"
#include "ceres/triplet_sparse_matrix.h"
#include "glog/logging.h"
@@ -51,9 +52,8 @@ BlockSparseMatrix::BlockSparseMatrix(
: num_rows_(0),
num_cols_(0),
num_nonzeros_(0),
- values_(NULL),
block_structure_(block_structure) {
- CHECK_NOTNULL(block_structure_.get());
+ CHECK(block_structure_ != nullptr);
// Count the number of columns in the matrix.
for (int i = 0; i < block_structure_->cols.size(); ++i) {
@@ -80,7 +80,8 @@ BlockSparseMatrix::BlockSparseMatrix(
VLOG(2) << "Allocating values array with "
<< num_nonzeros_ * sizeof(double) << " bytes."; // NOLINT
values_.reset(new double[num_nonzeros_]);
- CHECK_NOTNULL(values_.get());
+ max_num_nonzeros_ = num_nonzeros_;
+ CHECK(values_ != nullptr);
}
void BlockSparseMatrix::SetZero() {
@@ -88,8 +89,8 @@ void BlockSparseMatrix::SetZero() {
}
void BlockSparseMatrix::RightMultiply(const double* x, double* y) const {
- CHECK_NOTNULL(x);
- CHECK_NOTNULL(y);
+ CHECK(x != nullptr);
+ CHECK(y != nullptr);
for (int i = 0; i < block_structure_->rows.size(); ++i) {
int row_block_pos = block_structure_->rows[i].block.position;
@@ -108,8 +109,8 @@ void BlockSparseMatrix::RightMultiply(const double* x, double* y) const {
}
void BlockSparseMatrix::LeftMultiply(const double* x, double* y) const {
- CHECK_NOTNULL(x);
- CHECK_NOTNULL(y);
+ CHECK(x != nullptr);
+ CHECK(y != nullptr);
for (int i = 0; i < block_structure_->rows.size(); ++i) {
int row_block_pos = block_structure_->rows[i].block.position;
@@ -128,7 +129,7 @@ void BlockSparseMatrix::LeftMultiply(const double* x, double* y) const {
}
void BlockSparseMatrix::SquaredColumnNorm(double* x) const {
- CHECK_NOTNULL(x);
+ CHECK(x != nullptr);
VectorRef(x, num_cols_).setZero();
for (int i = 0; i < block_structure_->rows.size(); ++i) {
int row_block_size = block_structure_->rows[i].block.size;
@@ -145,7 +146,7 @@ void BlockSparseMatrix::SquaredColumnNorm(double* x) const {
}
void BlockSparseMatrix::ScaleColumns(const double* scale) {
- CHECK_NOTNULL(scale);
+ CHECK(scale != nullptr);
for (int i = 0; i < block_structure_->rows.size(); ++i) {
int row_block_size = block_structure_->rows[i].block.size;
@@ -162,7 +163,7 @@ void BlockSparseMatrix::ScaleColumns(const double* scale) {
}
void BlockSparseMatrix::ToDenseMatrix(Matrix* dense_matrix) const {
- CHECK_NOTNULL(dense_matrix);
+ CHECK(dense_matrix != nullptr);
dense_matrix->resize(num_rows_, num_cols_);
dense_matrix->setZero();
@@ -185,7 +186,7 @@ void BlockSparseMatrix::ToDenseMatrix(Matrix* dense_matrix) const {
void BlockSparseMatrix::ToTripletSparseMatrix(
TripletSparseMatrix* matrix) const {
- CHECK_NOTNULL(matrix);
+ CHECK(matrix != nullptr);
matrix->Reserve(num_nonzeros_);
matrix->Resize(num_rows_, num_cols_);
@@ -220,7 +221,7 @@ const CompressedRowBlockStructure* BlockSparseMatrix::block_structure()
}
void BlockSparseMatrix::ToTextFile(FILE* file) const {
- CHECK_NOTNULL(file);
+ CHECK(file != nullptr);
for (int i = 0; i < block_structure_->rows.size(); ++i) {
const int row_block_pos = block_structure_->rows[i].block.position;
const int row_block_size = block_structure_->rows[i].block.size;
@@ -242,5 +243,162 @@ void BlockSparseMatrix::ToTextFile(FILE* file) const {
}
}
+BlockSparseMatrix* BlockSparseMatrix::CreateDiagonalMatrix(
+ const double* diagonal, const std::vector<Block>& column_blocks) {
+ // Create the block structure for the diagonal matrix.
+ CompressedRowBlockStructure* bs = new CompressedRowBlockStructure();
+ bs->cols = column_blocks;
+ int position = 0;
+ bs->rows.resize(column_blocks.size(), CompressedRow(1));
+ for (int i = 0; i < column_blocks.size(); ++i) {
+ CompressedRow& row = bs->rows[i];
+ row.block = column_blocks[i];
+ Cell& cell = row.cells[0];
+ cell.block_id = i;
+ cell.position = position;
+ position += row.block.size * row.block.size;
+ }
+
+ // Create the BlockSparseMatrix with the given block structure.
+ BlockSparseMatrix* matrix = new 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 (int j = 0; j < size; ++j) {
+ // (j + 1) * size is compact way of accessing the (j,j) entry.
+ values[j * (size + 1)] = diagonal[j];
+ }
+ diagonal += size;
+ values += size * size;
+ }
+
+ return matrix;
+}
+
+void BlockSparseMatrix::AppendRows(const BlockSparseMatrix& m) {
+ CHECK_EQ(m.num_cols(), num_cols());
+ const CompressedRowBlockStructure* m_bs = m.block_structure();
+ CHECK_EQ(m_bs->cols.size(), block_structure_->cols.size());
+
+ const int old_num_nonzeros = num_nonzeros_;
+ const int old_num_row_blocks = block_structure_->rows.size();
+ block_structure_->rows.resize(old_num_row_blocks + m_bs->rows.size());
+
+ for (int i = 0; i < m_bs->rows.size(); ++i) {
+ const CompressedRow& m_row = m_bs->rows[i];
+ CompressedRow& row = block_structure_->rows[old_num_row_blocks + i];
+ row.block.size = m_row.block.size;
+ row.block.position = num_rows_;
+ num_rows_ += m_row.block.size;
+ row.cells.resize(m_row.cells.size());
+ for (int c = 0; c < m_row.cells.size(); ++c) {
+ const int block_id = m_row.cells[c].block_id;
+ row.cells[c].block_id = block_id;
+ row.cells[c].position = num_nonzeros_;
+ num_nonzeros_ += m_row.block.size * m_bs->cols[block_id].size;
+ }
+ }
+
+ 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);
+ max_num_nonzeros_ = num_nonzeros_;
+ }
+
+ std::copy(m.values(),
+ m.values() + m.num_nonzeros(),
+ values_.get() + old_num_nonzeros);
+}
+
+void BlockSparseMatrix::DeleteRowBlocks(const int delta_row_blocks) {
+ const int num_row_blocks = block_structure_->rows.size();
+ int delta_num_nonzeros = 0;
+ int delta_num_rows = 0;
+ const std::vector<Block>& column_blocks = block_structure_->cols;
+ for (int i = 0; i < delta_row_blocks; ++i) {
+ const CompressedRow& row = block_structure_->rows[num_row_blocks - i - 1];
+ delta_num_rows += row.block.size;
+ for (int c = 0; c < row.cells.size(); ++c) {
+ const Cell& cell = row.cells[c];
+ delta_num_nonzeros += row.block.size * column_blocks[cell.block_id].size;
+ }
+ }
+ num_nonzeros_ -= delta_num_nonzeros;
+ num_rows_ -= delta_num_rows;
+ block_structure_->rows.resize(num_row_blocks - delta_row_blocks);
+}
+
+BlockSparseMatrix* BlockSparseMatrix::CreateRandomMatrix(
+ const BlockSparseMatrix::RandomMatrixOptions& options) {
+ CHECK_GT(options.num_row_blocks, 0);
+ CHECK_GT(options.min_row_block_size, 0);
+ CHECK_GT(options.max_row_block_size, 0);
+ CHECK_LE(options.min_row_block_size, options.max_row_block_size);
+ CHECK_GT(options.block_density, 0.0);
+ CHECK_LE(options.block_density, 1.0);
+
+ CompressedRowBlockStructure* bs = new CompressedRowBlockStructure();
+ if (options.col_blocks.empty()) {
+ CHECK_GT(options.num_col_blocks, 0);
+ CHECK_GT(options.min_col_block_size, 0);
+ CHECK_GT(options.max_col_block_size, 0);
+ CHECK_LE(options.min_col_block_size, options.max_col_block_size);
+
+ // Generate the col block structure.
+ int col_block_position = 0;
+ for (int i = 0; i < options.num_col_blocks; ++i) {
+ // Generate a random integer in [min_col_block_size, max_col_block_size]
+ 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));
+ col_block_position += col_block_size;
+ }
+ } else {
+ bs->cols = options.col_blocks;
+ }
+
+ bool matrix_has_blocks = false;
+ while (!matrix_has_blocks) {
+ VLOG(1) << "Clearing";
+ bs->rows.clear();
+ int row_block_position = 0;
+ int value_position = 0;
+ for (int r = 0; r < options.num_row_blocks; ++r) {
+
+ 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());
+ CompressedRow& row = bs->rows.back();
+ row.block.size = row_block_size;
+ row.block.position = row_block_position;
+ row_block_position += row_block_size;
+ for (int c = 0; c < bs->cols.size(); ++c) {
+ if (RandDouble() > options.block_density) continue;
+
+ row.cells.push_back(Cell());
+ Cell& cell = row.cells.back();
+ cell.block_id = c;
+ cell.position = value_position;
+ value_position += row_block_size * bs->cols[c].size;
+ matrix_has_blocks = true;
+ }
+ }
+ }
+
+ BlockSparseMatrix* matrix = new BlockSparseMatrix(bs);
+ double* values = matrix->mutable_values();
+ for (int i = 0; i < matrix->num_nonzeros(); ++i) {
+ values[i] = RandNormal();
+ }
+
+ return matrix;
+}
+
} // namespace internal
} // namespace ceres
diff --git a/extern/ceres/internal/ceres/block_sparse_matrix.h b/extern/ceres/internal/ceres/block_sparse_matrix.h
index 2f9afb738f8..d0c255de612 100644
--- a/extern/ceres/internal/ceres/block_sparse_matrix.h
+++ b/extern/ceres/internal/ceres/block_sparse_matrix.h
@@ -34,11 +34,10 @@
#ifndef CERES_INTERNAL_BLOCK_SPARSE_MATRIX_H_
#define CERES_INTERNAL_BLOCK_SPARSE_MATRIX_H_
+#include <memory>
#include "ceres/block_structure.h"
#include "ceres/sparse_matrix.h"
#include "ceres/internal/eigen.h"
-#include "ceres/internal/macros.h"
-#include "ceres/internal/scoped_ptr.h"
namespace ceres {
namespace internal {
@@ -64,34 +63,99 @@ class BlockSparseMatrix : public SparseMatrix {
explicit BlockSparseMatrix(CompressedRowBlockStructure* block_structure);
BlockSparseMatrix();
+ BlockSparseMatrix(const BlockSparseMatrix&) = delete;
+ void operator=(const BlockSparseMatrix&) = delete;
+
virtual ~BlockSparseMatrix();
// Implementation of SparseMatrix interface.
- virtual void SetZero();
- virtual void RightMultiply(const double* x, double* y) const;
- virtual void LeftMultiply(const double* x, double* y) const;
- virtual void SquaredColumnNorm(double* x) const;
- virtual void ScaleColumns(const double* scale);
- virtual void ToDenseMatrix(Matrix* dense_matrix) const;
- virtual void ToTextFile(FILE* file) const;
-
- virtual int num_rows() const { return num_rows_; }
- virtual int num_cols() const { return num_cols_; }
- virtual int num_nonzeros() const { return num_nonzeros_; }
- virtual const double* values() const { return values_.get(); }
- virtual double* mutable_values() { return values_.get(); }
+ void SetZero() final;
+ void RightMultiply(const double* x, double* y) const final;
+ void LeftMultiply(const double* x, double* y) const final;
+ void SquaredColumnNorm(double* x) const final;
+ void ScaleColumns(const double* scale) final;
+ void ToDenseMatrix(Matrix* dense_matrix) const final;
+ void ToTextFile(FILE* file) const final;
+
+ int num_rows() const final { return num_rows_; }
+ int num_cols() const final { return num_cols_; }
+ int num_nonzeros() const final { return num_nonzeros_; }
+ const double* values() const final { return values_.get(); }
+ double* mutable_values() final { return values_.get(); }
void ToTripletSparseMatrix(TripletSparseMatrix* matrix) const;
const CompressedRowBlockStructure* block_structure() const;
+ // Append the contents of m to the bottom of this matrix. m must
+ // have the same column blocks structure as this matrix.
+ void AppendRows(const BlockSparseMatrix& m);
+
+ // Delete the bottom delta_rows_blocks.
+ void DeleteRowBlocks(int delta_row_blocks);
+
+ static BlockSparseMatrix* CreateDiagonalMatrix(
+ const double* diagonal,
+ const std::vector<Block>& column_blocks);
+
+ struct RandomMatrixOptions {
+ int num_row_blocks = 0;
+ int min_row_block_size = 0;
+ int max_row_block_size = 0;
+ int num_col_blocks = 0;
+ int min_col_block_size = 0;
+ int max_col_block_size = 0;
+
+ // 0 < block_density <= 1 is the probability of a block being
+ // present in the matrix. A given random matrix will not have
+ // precisely this density.
+ double block_density = 0.0;
+
+ // If col_blocks is non-empty, then the generated random matrix
+ // has this block structure and the column related options in this
+ // struct are ignored.
+ std::vector<Block> col_blocks;
+ };
+
+ // Create a random BlockSparseMatrix whose entries are normally
+ // distributed and whose structure is determined by
+ // RandomMatrixOptions.
+ //
+ // Caller owns the result.
+ static BlockSparseMatrix* CreateRandomMatrix(
+ const RandomMatrixOptions& options);
+
private:
int num_rows_;
int num_cols_;
- int max_num_nonzeros_;
int num_nonzeros_;
- scoped_array<double> values_;
- scoped_ptr<CompressedRowBlockStructure> block_structure_;
- CERES_DISALLOW_COPY_AND_ASSIGN(BlockSparseMatrix);
+ int max_num_nonzeros_;
+ std::unique_ptr<double[]> values_;
+ std::unique_ptr<CompressedRowBlockStructure> block_structure_;
+};
+
+// A number of algorithms like the SchurEliminator do not need
+// access to the full BlockSparseMatrix interface. They only
+// need read only access to the values array and the block structure.
+//
+// BlockSparseDataMatrix a struct that carries these two bits of
+// information
+class BlockSparseMatrixData {
+ public:
+ BlockSparseMatrixData(const BlockSparseMatrix& m)
+ : block_structure_(m.block_structure()), values_(m.values()){};
+
+ BlockSparseMatrixData(const CompressedRowBlockStructure* block_structure,
+ const double* values)
+ : block_structure_(block_structure), values_(values) {}
+
+ const CompressedRowBlockStructure* block_structure() const {
+ return block_structure_;
+ }
+ const double* values() const { return values_; }
+
+ private:
+ const CompressedRowBlockStructure* block_structure_;
+ const double* values_;
};
} // namespace internal
diff --git a/extern/ceres/internal/ceres/block_structure.h b/extern/ceres/internal/ceres/block_structure.h
index 6e7003addb6..b5218c0c2cc 100644
--- a/extern/ceres/internal/ceres/block_structure.h
+++ b/extern/ceres/internal/ceres/block_structure.h
@@ -38,14 +38,14 @@
#ifndef CERES_INTERNAL_BLOCK_STRUCTURE_H_
#define CERES_INTERNAL_BLOCK_STRUCTURE_H_
+#include <cstdint>
#include <vector>
#include "ceres/internal/port.h"
-#include "ceres/types.h"
namespace ceres {
namespace internal {
-typedef int32 BlockSize;
+typedef int32_t BlockSize;
struct Block {
Block() : size(-1), position(-1) {}
@@ -70,6 +70,11 @@ struct Cell {
bool CellLessThan(const Cell& lhs, const Cell& rhs);
struct CompressedList {
+ CompressedList() {}
+
+ // Construct a CompressedList with the cells containing num_cells
+ // entries.
+ CompressedList(int num_cells) : cells(num_cells) {}
Block block;
std::vector<Cell> cells;
};
diff --git a/extern/ceres/internal/ceres/c_api.cc b/extern/ceres/internal/ceres/c_api.cc
index ada8f3e0013..2244909131a 100644
--- a/extern/ceres/internal/ceres/c_api.cc
+++ b/extern/ceres/internal/ceres/c_api.cc
@@ -80,9 +80,9 @@ class CallbackCostFunction : public ceres::CostFunction {
virtual ~CallbackCostFunction() {}
- virtual bool Evaluate(double const* const* parameters,
+ bool Evaluate(double const* const* parameters,
double* residuals,
- double** jacobians) const {
+ double** jacobians) const final {
return (*cost_function_)(user_data_,
const_cast<double**>(parameters),
residuals,
@@ -101,7 +101,7 @@ class CallbackLossFunction : public ceres::LossFunction {
explicit CallbackLossFunction(ceres_loss_function_t loss_function,
void* user_data)
: loss_function_(loss_function), user_data_(user_data) {}
- virtual void Evaluate(double sq_norm, double* rho) const {
+ void Evaluate(double sq_norm, double* rho) const final {
(*loss_function_)(user_data_, sq_norm, rho);
}
diff --git a/extern/ceres/internal/ceres/callbacks.cc b/extern/ceres/internal/ceres/callbacks.cc
index 50a0ec19924..84576e40e7d 100644
--- a/extern/ceres/internal/ceres/callbacks.cc
+++ b/extern/ceres/internal/ceres/callbacks.cc
@@ -47,9 +47,29 @@ StateUpdatingCallback::~StateUpdatingCallback() {}
CallbackReturnType StateUpdatingCallback::operator()(
const IterationSummary& summary) {
+ program_->StateVectorToParameterBlocks(parameters_);
+ program_->CopyParameterBlockStateToUserState();
+ return SOLVER_CONTINUE;
+}
+
+GradientProblemSolverStateUpdatingCallback::
+ GradientProblemSolverStateUpdatingCallback(
+ int num_parameters,
+ const double* internal_parameters,
+ double* user_parameters)
+ : num_parameters_(num_parameters),
+ internal_parameters_(internal_parameters),
+ user_parameters_(user_parameters) {}
+
+GradientProblemSolverStateUpdatingCallback::
+ ~GradientProblemSolverStateUpdatingCallback() {}
+
+CallbackReturnType GradientProblemSolverStateUpdatingCallback::operator()(
+ const IterationSummary& summary) {
if (summary.step_is_successful) {
- program_->StateVectorToParameterBlocks(parameters_);
- program_->CopyParameterBlockStateToUserState();
+ std::copy(internal_parameters_,
+ internal_parameters_ + num_parameters_,
+ user_parameters_);
}
return SOLVER_CONTINUE;
}
diff --git a/extern/ceres/internal/ceres/callbacks.h b/extern/ceres/internal/ceres/callbacks.h
index 33c66df5c11..d68bf7f6301 100644
--- a/extern/ceres/internal/ceres/callbacks.h
+++ b/extern/ceres/internal/ceres/callbacks.h
@@ -46,19 +46,34 @@ class StateUpdatingCallback : public IterationCallback {
public:
StateUpdatingCallback(Program* program, double* parameters);
virtual ~StateUpdatingCallback();
- virtual CallbackReturnType operator()(const IterationSummary& summary);
+ CallbackReturnType operator()(const IterationSummary& summary) final;
private:
Program* program_;
double* parameters_;
};
+// Callback for updating the externally visible state of the
+// parameters vector for GradientProblemSolver.
+class GradientProblemSolverStateUpdatingCallback : public IterationCallback {
+ public:
+ GradientProblemSolverStateUpdatingCallback(int num_parameters,
+ const double* internal_parameters,
+ double* user_parameters);
+ virtual ~GradientProblemSolverStateUpdatingCallback();
+ CallbackReturnType operator()(const IterationSummary& summary) final;
+ private:
+ int num_parameters_;
+ const double* internal_parameters_;
+ double* user_parameters_;
+};
+
// 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 {
public:
LoggingCallback(MinimizerType minimizer_type, bool log_to_stdout);
virtual ~LoggingCallback();
- virtual CallbackReturnType operator()(const IterationSummary& summary);
+ CallbackReturnType operator()(const IterationSummary& summary) final;
private:
const MinimizerType minimizer_type;
diff --git a/extern/ceres/internal/ceres/canonical_views_clustering.cc b/extern/ceres/internal/ceres/canonical_views_clustering.cc
new file mode 100644
index 00000000000..e927e1f91be
--- /dev/null
+++ b/extern/ceres/internal/ceres/canonical_views_clustering.cc
@@ -0,0 +1,232 @@
+// 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: David Gallup (dgallup@google.com)
+// Sameer Agarwal (sameeragarwal@google.com)
+
+#include "ceres/canonical_views_clustering.h"
+
+#include <unordered_set>
+#include <unordered_map>
+
+#include "ceres/graph.h"
+#include "ceres/map_util.h"
+#include "glog/logging.h"
+
+namespace ceres {
+namespace internal {
+
+using std::vector;
+
+typedef std::unordered_map<int, int> IntMap;
+typedef std::unordered_set<int> IntSet;
+
+class 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
+ // from vertices to cluster_ids. The i^th cluster center corresponds
+ // to the i^th cluster. 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 = kInvalidClusterId.
+ void ComputeClustering(const CanonicalViewsClusteringOptions& options,
+ const WeightedGraph<int>& graph,
+ vector<int>* centers,
+ IntMap* membership);
+
+ private:
+ void FindValidViews(IntSet* valid_views) const;
+ double ComputeClusteringQualityDifference(const int candidate,
+ const vector<int>& centers) const;
+ void UpdateCanonicalViewAssignments(const int canonical_view);
+ void ComputeClusterMembership(const vector<int>& centers,
+ IntMap* membership) const;
+
+ CanonicalViewsClusteringOptions options_;
+ const WeightedGraph<int>* graph_;
+ // Maps a view to its representative canonical view (its cluster
+ // center).
+ IntMap view_to_canonical_view_;
+ // Maps a view to its similarity to its current cluster center.
+ std::unordered_map<int, double> view_to_canonical_view_similarity_;
+};
+
+void ComputeCanonicalViewsClustering(
+ const CanonicalViewsClusteringOptions& options,
+ const WeightedGraph<int>& graph,
+ vector<int>* centers,
+ IntMap* membership) {
+ time_t start_time = time(NULL);
+ CanonicalViewsClustering cv;
+ cv.ComputeClustering(options, graph, centers, membership);
+ VLOG(2) << "Canonical views clustering time (secs): "
+ << time(NULL) - start_time;
+}
+
+// Implementation of CanonicalViewsClustering
+void CanonicalViewsClustering::ComputeClustering(
+ const CanonicalViewsClusteringOptions& options,
+ const WeightedGraph<int>& graph,
+ vector<int>* centers,
+ IntMap* membership) {
+ options_ = options;
+ CHECK(centers != nullptr);
+ CHECK(membership != nullptr);
+ centers->clear();
+ membership->clear();
+ graph_ = &graph;
+
+ IntSet valid_views;
+ FindValidViews(&valid_views);
+ while (valid_views.size() > 0) {
+ // Find the next best canonical view.
+ double best_difference = -std::numeric_limits<double>::max();
+ int best_view = 0;
+
+ // TODO(sameeragarwal): Make this loop multi-threaded.
+ for (const auto& view : valid_views) {
+ const double difference =
+ ComputeClusteringQualityDifference(view, *centers);
+ if (difference > best_difference) {
+ best_difference = difference;
+ best_view = view;
+ }
+ }
+
+ CHECK_GT(best_difference, -std::numeric_limits<double>::max());
+
+ // Add canonical view if quality improves, or if minimum is not
+ // yet met, otherwise break.
+ if ((best_difference <= 0) &&
+ (centers->size() >= options_.min_views)) {
+ break;
+ }
+
+ centers->push_back(best_view);
+ valid_views.erase(best_view);
+ UpdateCanonicalViewAssignments(best_view);
+ }
+
+ ComputeClusterMembership(*centers, membership);
+}
+
+// Return the set of vertices of the graph which have valid vertex
+// weights.
+void CanonicalViewsClustering::FindValidViews(
+ IntSet* valid_views) const {
+ const IntSet& views = graph_->vertices();
+ for (const auto& view : views) {
+ if (graph_->VertexWeight(view) != WeightedGraph<int>::InvalidWeight()) {
+ valid_views->insert(view);
+ }
+ }
+}
+
+// Computes the difference in the quality score if 'candidate' were
+// added to the set of canonical views.
+double CanonicalViewsClustering::ComputeClusteringQualityDifference(
+ const int candidate,
+ const vector<int>& centers) const {
+ // View score.
+ double difference =
+ options_.view_score_weight * graph_->VertexWeight(candidate);
+
+ // Compute how much the quality score changes if the candidate view
+ // was added to the list of canonical views and its nearest
+ // neighbors became members of its cluster.
+ const IntSet& neighbors = graph_->Neighbors(candidate);
+ for (const auto& neighbor : neighbors) {
+ const double old_similarity =
+ FindWithDefault(view_to_canonical_view_similarity_, neighbor, 0.0);
+ const double new_similarity = graph_->EdgeWeight(neighbor, candidate);
+ if (new_similarity > old_similarity) {
+ difference += new_similarity - old_similarity;
+ }
+ }
+
+ // Number of views penalty.
+ difference -= options_.size_penalty_weight;
+
+ // Orthogonality.
+ for (int i = 0; i < centers.size(); ++i) {
+ difference -= options_.similarity_penalty_weight *
+ graph_->EdgeWeight(centers[i], candidate);
+ }
+
+ return difference;
+}
+
+// Reassign views if they're more similar to the new canonical view.
+void CanonicalViewsClustering::UpdateCanonicalViewAssignments(
+ const int canonical_view) {
+ const IntSet& neighbors = graph_->Neighbors(canonical_view);
+ for (const auto& neighbor : neighbors) {
+ const double old_similarity =
+ FindWithDefault(view_to_canonical_view_similarity_, neighbor, 0.0);
+ const double new_similarity =
+ graph_->EdgeWeight(neighbor, canonical_view);
+ if (new_similarity > old_similarity) {
+ view_to_canonical_view_[neighbor] = canonical_view;
+ view_to_canonical_view_similarity_[neighbor] = new_similarity;
+ }
+ }
+}
+
+// Assign a cluster id to each view.
+void CanonicalViewsClustering::ComputeClusterMembership(
+ const vector<int>& centers,
+ IntMap* membership) const {
+ CHECK(membership != nullptr);
+ membership->clear();
+
+ // The i^th cluster has cluster id i.
+ IntMap center_to_cluster_id;
+ for (int i = 0; i < centers.size(); ++i) {
+ center_to_cluster_id[centers[i]] = i;
+ }
+
+ static constexpr int kInvalidClusterId = -1;
+
+ const IntSet& views = graph_->vertices();
+ for (const auto& view : views) {
+ auto it = view_to_canonical_view_.find(view);
+ int cluster_id = kInvalidClusterId;
+ if (it != view_to_canonical_view_.end()) {
+ cluster_id = FindOrDie(center_to_cluster_id, it->second);
+ }
+
+ InsertOrDie(membership, view, cluster_id);
+ }
+}
+
+} // namespace internal
+} // namespace ceres
diff --git a/extern/ceres/internal/ceres/canonical_views_clustering.h b/extern/ceres/internal/ceres/canonical_views_clustering.h
new file mode 100644
index 00000000000..630adfed717
--- /dev/null
+++ b/extern/ceres/internal/ceres/canonical_views_clustering.h
@@ -0,0 +1,124 @@
+// 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)
+//
+// An implementation of the Canonical Views clustering algorithm from
+// "Scene Summarization for Online Image Collections", Ian Simon, Noah
+// Snavely, Steven M. Seitz, ICCV 2007.
+//
+// More details can be found at
+// http://grail.cs.washington.edu/projects/canonview/
+//
+// Ceres uses this algorithm to perform view clustering for
+// constructing visibility based preconditioners.
+
+#ifndef CERES_INTERNAL_CANONICAL_VIEWS_CLUSTERING_H_
+#define CERES_INTERNAL_CANONICAL_VIEWS_CLUSTERING_H_
+
+#include <unordered_map>
+#include <vector>
+
+#include "ceres/graph.h"
+
+namespace ceres {
+namespace internal {
+
+struct CanonicalViewsClusteringOptions;
+
+// Compute a partitioning of the vertices of the graph using the
+// canonical views clustering algorithm.
+//
+// In the following we will use the terms vertices and views
+// interchangeably. Given a weighted Graph G(V,E), the canonical views
+// of G are the set of vertices that best "summarize" the content
+// of the graph. If w_ij i s the weight connecting the vertex i to
+// vertex j, and C is the set of canonical views. Then the objective
+// of the canonical views algorithm is
+//
+// E[C] = sum_[i in V] max_[j in C] w_ij
+// - size_penalty_weight * |C|
+// - similarity_penalty_weight * sum_[i in C, j in C, j > i] w_ij
+//
+// alpha is the size penalty that penalizes large number of canonical
+// views.
+//
+// beta is the similarity penalty that penalizes canonical views that
+// are too similar to other canonical views.
+//
+// Thus the canonical views algorithm tries to find a canonical view
+// for each vertex in the graph which best explains it, while trying
+// to minimize the number of canonical views and the overlap between
+// them.
+//
+// We further augment the above objective function by allowing for per
+// vertex weights, higher weights indicating a higher preference for
+// being chosen as a canonical view. Thus if w_i is the vertex weight
+// for vertex i, the objective function is then
+//
+// E[C] = sum_[i in V] max_[j in C] w_ij
+// - size_penalty_weight * |C|
+// - similarity_penalty_weight * sum_[i in C, j in C, j > i] w_ij
+// + view_score_weight * sum_[i in C] w_i
+//
+// centers will contain the vertices that are the identified
+// as the canonical views/cluster centers, and membership is a map
+// from vertices to cluster_ids. The i^th cluster center corresponds
+// to the i^th cluster.
+//
+// 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;
+void ComputeCanonicalViewsClustering(
+ const CanonicalViewsClusteringOptions& options,
+ const WeightedGraph<int>& graph,
+ std::vector<int>* centers,
+ std::unordered_map<int, int>* membership);
+
+struct CanonicalViewsClusteringOptions {
+ // The minimum number of canonical views to compute.
+ int min_views = 3;
+
+ // Penalty weight for the number of canonical views. A higher
+ // number will result in fewer canonical views.
+ double size_penalty_weight = 5.75;
+
+ // Penalty weight for the diversity (orthogonality) of the
+ // canonical views. A higher number will encourage less similar
+ // canonical views.
+ double similarity_penalty_weight = 100;
+
+ // Weight for per-view scores. Lower weight places less
+ // confidence in the view scores.
+ double view_score_weight = 0.0;
+};
+
+} // namespace internal
+} // namespace ceres
+
+#endif // CERES_INTERNAL_CANONICAL_VIEWS_CLUSTERING_H_
diff --git a/extern/ceres/internal/ceres/cgnr_linear_operator.h b/extern/ceres/internal/ceres/cgnr_linear_operator.h
index 44c07cabd01..8e8febcc934 100644
--- a/extern/ceres/internal/ceres/cgnr_linear_operator.h
+++ b/extern/ceres/internal/ceres/cgnr_linear_operator.h
@@ -32,8 +32,8 @@
#define CERES_INTERNAL_CGNR_LINEAR_OPERATOR_H_
#include <algorithm>
+#include <memory>
#include "ceres/linear_operator.h"
-#include "ceres/internal/scoped_ptr.h"
#include "ceres/internal/eigen.h"
namespace ceres {
@@ -84,7 +84,7 @@ class CgnrLinearOperator : public LinearOperator {
}
virtual ~CgnrLinearOperator() {}
- virtual void RightMultiply(const double* x, double* y) const {
+ void RightMultiply(const double* x, double* y) const final {
std::fill(z_.get(), z_.get() + A_.num_rows(), 0.0);
// z = Ax
@@ -101,17 +101,17 @@ class CgnrLinearOperator : public LinearOperator {
}
}
- virtual void LeftMultiply(const double* x, double* y) const {
+ void LeftMultiply(const double* x, double* y) const final {
RightMultiply(x, y);
}
- virtual int num_rows() const { return A_.num_cols(); }
- virtual int num_cols() const { return A_.num_cols(); }
+ int num_rows() const final { return A_.num_cols(); }
+ int num_cols() const final { return A_.num_cols(); }
private:
const LinearOperator& A_;
const double* D_;
- scoped_array<double> z_;
+ std::unique_ptr<double[]> z_;
};
} // namespace internal
diff --git a/extern/ceres/internal/ceres/cgnr_solver.cc b/extern/ceres/internal/ceres/cgnr_solver.cc
index 61fae758d5b..9dba1cfb4a8 100644
--- a/extern/ceres/internal/ceres/cgnr_solver.cc
+++ b/extern/ceres/internal/ceres/cgnr_solver.cc
@@ -35,6 +35,7 @@
#include "ceres/conjugate_gradients_solver.h"
#include "ceres/internal/eigen.h"
#include "ceres/linear_solver.h"
+#include "ceres/subset_preconditioner.h"
#include "ceres/wall_time.h"
#include "glog/logging.h"
@@ -42,14 +43,19 @@ namespace ceres {
namespace internal {
CgnrSolver::CgnrSolver(const LinearSolver::Options& options)
- : options_(options),
- preconditioner_(NULL) {
+ : options_(options) {
if (options_.preconditioner_type != JACOBI &&
- options_.preconditioner_type != IDENTITY) {
- LOG(FATAL) << "CGNR only supports IDENTITY and JACOBI preconditioners.";
+ options_.preconditioner_type != IDENTITY &&
+ options_.preconditioner_type != SUBSET) {
+ LOG(FATAL)
+ << "Preconditioner = "
+ << PreconditionerTypeToString(options_.preconditioner_type) << ". "
+ << "Congratulations, you found a bug in Ceres. Please report it.";
}
}
+CgnrSolver::~CgnrSolver() {}
+
LinearSolver::Summary CgnrSolver::SolveImpl(
BlockSparseMatrix* A,
const double* b,
@@ -62,16 +68,31 @@ LinearSolver::Summary CgnrSolver::SolveImpl(
z.setZero();
A->LeftMultiply(b, z.data());
- // Precondition if necessary.
- LinearSolver::PerSolveOptions cg_per_solve_options = per_solve_options;
- if (options_.preconditioner_type == JACOBI) {
- if (preconditioner_.get() == NULL) {
+ if (!preconditioner_) {
+ if (options_.preconditioner_type == JACOBI) {
preconditioner_.reset(new BlockJacobiPreconditioner(*A));
+ } else if (options_.preconditioner_type == SUBSET) {
+ Preconditioner::Options preconditioner_options;
+ preconditioner_options.type = SUBSET;
+ preconditioner_options.subset_preconditioner_start_row_block =
+ options_.subset_preconditioner_start_row_block;
+ preconditioner_options.sparse_linear_algebra_library_type =
+ options_.sparse_linear_algebra_library_type;
+ 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));
}
+ }
+
+ if (preconditioner_) {
preconditioner_->Update(*A, per_solve_options.D);
- cg_per_solve_options.preconditioner = preconditioner_.get();
}
+ LinearSolver::PerSolveOptions cg_per_solve_options = per_solve_options;
+ cg_per_solve_options.preconditioner = preconditioner_.get();
+
// Solve (AtA + DtD)x = z (= Atb).
VectorRef(x, A->num_cols()).setZero();
CgnrLinearOperator lhs(*A, per_solve_options.D);
diff --git a/extern/ceres/internal/ceres/cgnr_solver.h b/extern/ceres/internal/ceres/cgnr_solver.h
index f7a15736925..52927333daa 100644
--- a/extern/ceres/internal/ceres/cgnr_solver.h
+++ b/extern/ceres/internal/ceres/cgnr_solver.h
@@ -31,7 +31,7 @@
#ifndef CERES_INTERNAL_CGNR_SOLVER_H_
#define CERES_INTERNAL_CGNR_SOLVER_H_
-#include "ceres/internal/scoped_ptr.h"
+#include <memory>
#include "ceres/linear_solver.h"
namespace ceres {
@@ -51,16 +51,19 @@ class BlockJacobiPreconditioner;
class CgnrSolver : public BlockSparseMatrixSolver {
public:
explicit CgnrSolver(const LinearSolver::Options& options);
- virtual Summary SolveImpl(
+ CgnrSolver(const CgnrSolver&) = delete;
+ void operator=(const CgnrSolver&) = delete;
+ virtual ~CgnrSolver();
+
+ Summary SolveImpl(
BlockSparseMatrix* A,
const double* b,
const LinearSolver::PerSolveOptions& per_solve_options,
- double* x);
+ double* x) final;
private:
const LinearSolver::Options options_;
- scoped_ptr<Preconditioner> preconditioner_;
- CERES_DISALLOW_COPY_AND_ASSIGN(CgnrSolver);
+ std::unique_ptr<Preconditioner> preconditioner_;
};
} // namespace internal
diff --git a/extern/ceres/internal/ceres/collections_port.h b/extern/ceres/internal/ceres/collections_port.h
deleted file mode 100644
index e699a661b8b..00000000000
--- a/extern/ceres/internal/ceres/collections_port.h
+++ /dev/null
@@ -1,196 +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)
-//
-// Portable HashMap and HashSet, and a specialized overload for hashing pairs.
-
-#ifndef CERES_INTERNAL_COLLECTIONS_PORT_H_
-#define CERES_INTERNAL_COLLECTIONS_PORT_H_
-
-#include "ceres/internal/port.h"
-
-#if defined(CERES_NO_UNORDERED_MAP)
-# include <map>
-# include <set>
-#endif
-
-#if defined(CERES_TR1_UNORDERED_MAP)
-# include <tr1/unordered_map>
-# include <tr1/unordered_set>
-# define CERES_HASH_NAMESPACE_START namespace std { namespace tr1 {
-# define CERES_HASH_NAMESPACE_END } }
-#endif
-
-#if defined(CERES_STD_UNORDERED_MAP)
-# include <unordered_map>
-# include <unordered_set>
-# define CERES_HASH_NAMESPACE_START namespace std {
-# define CERES_HASH_NAMESPACE_END }
-#endif
-
-#if defined(CERES_STD_UNORDERED_MAP_IN_TR1_NAMESPACE)
-# include <unordered_map>
-# include <unordered_set>
-# define CERES_HASH_NAMESPACE_START namespace std { namespace tr1 {
-# define CERES_HASH_NAMESPACE_END } }
-#endif
-
-#if !defined(CERES_NO_UNORDERED_MAP) && !defined(CERES_TR1_UNORDERED_MAP) && \
- !defined(CERES_STD_UNORDERED_MAP) && !defined(CERES_STD_UNORDERED_MAP_IN_TR1_NAMESPACE) // NOLINT
-# error One of: CERES_NO_UNORDERED_MAP, CERES_TR1_UNORDERED_MAP,\
- CERES_STD_UNORDERED_MAP, CERES_STD_UNORDERED_MAP_IN_TR1_NAMESPACE must be defined! // NOLINT
-#endif
-
-#include <utility>
-#include "ceres/integral_types.h"
-
-// Some systems don't have access to unordered_map/unordered_set. In
-// that case, substitute the hash map/set with normal map/set. The
-// price to pay is slower speed for some operations.
-#if defined(CERES_NO_UNORDERED_MAP)
-
-namespace ceres {
-namespace internal {
-
-template<typename K, typename V>
-struct HashMap : map<K, V> {};
-
-template<typename K>
-struct HashSet : set<K> {};
-
-} // namespace internal
-} // namespace ceres
-
-#else
-
-namespace ceres {
-namespace internal {
-
-#if defined(CERES_TR1_UNORDERED_MAP) || \
- defined(CERES_STD_UNORDERED_MAP_IN_TR1_NAMESPACE)
-template<typename K, typename V>
-struct HashMap : std::tr1::unordered_map<K, V> {};
-template<typename K>
-struct HashSet : std::tr1::unordered_set<K> {};
-#endif
-
-#if defined(CERES_STD_UNORDERED_MAP)
-template<typename K, typename V>
-struct HashMap : std::unordered_map<K, V> {};
-template<typename K>
-struct HashSet : std::unordered_set<K> {};
-#endif
-
-#if defined(_WIN32) && !defined(__MINGW64__) && !defined(__MINGW32__)
-#define GG_LONGLONG(x) x##I64
-#define GG_ULONGLONG(x) x##UI64
-#else
-#define GG_LONGLONG(x) x##LL
-#define GG_ULONGLONG(x) x##ULL
-#endif
-
-// The hash function is due to Bob Jenkins (see
-// http://burtleburtle.net/bob/hash/index.html). Each mix takes 36 instructions,
-// in 18 cycles if you're lucky. On x86 architectures, this requires 45
-// instructions in 27 cycles, if you're lucky.
-//
-// 32bit version
-inline void hash_mix(uint32& a, uint32& b, uint32& c) {
- a -= b; a -= c; a ^= (c>>13);
- b -= c; b -= a; b ^= (a<<8);
- c -= a; c -= b; c ^= (b>>13);
- a -= b; a -= c; a ^= (c>>12);
- b -= c; b -= a; b ^= (a<<16);
- c -= a; c -= b; c ^= (b>>5);
- a -= b; a -= c; a ^= (c>>3);
- b -= c; b -= a; b ^= (a<<10);
- c -= a; c -= b; c ^= (b>>15);
-}
-
-// 64bit version
-inline void hash_mix(uint64& a, uint64& b, uint64& c) {
- a -= b; a -= c; a ^= (c>>43);
- b -= c; b -= a; b ^= (a<<9);
- c -= a; c -= b; c ^= (b>>8);
- a -= b; a -= c; a ^= (c>>38);
- b -= c; b -= a; b ^= (a<<23);
- c -= a; c -= b; c ^= (b>>5);
- a -= b; a -= c; a ^= (c>>35);
- b -= c; b -= a; b ^= (a<<49);
- c -= a; c -= b; c ^= (b>>11);
-}
-
-inline uint32 Hash32NumWithSeed(uint32 num, uint32 c) {
- // The golden ratio; an arbitrary value.
- uint32 b = 0x9e3779b9UL;
- hash_mix(num, b, c);
- return c;
-}
-
-inline uint64 Hash64NumWithSeed(uint64 num, uint64 c) {
- // More of the golden ratio.
- uint64 b = GG_ULONGLONG(0xe08c1d668b756f82);
- hash_mix(num, b, c);
- return c;
-}
-
-} // namespace internal
-} // namespace ceres
-
-// Since on some platforms this is a doubly-nested namespace (std::tr1) and
-// others it is not, the entire namespace line must be in a macro.
-CERES_HASH_NAMESPACE_START
-
-// The outrageously annoying specializations below are for portability reasons.
-// In short, it's not possible to have two overloads of hash<pair<T1, T2>
-
-// Hasher for STL pairs. Requires hashers for both members to be defined.
-template<typename T>
-struct hash<pair<T, T> > {
- size_t operator()(const pair<T, T>& p) const {
- size_t h1 = hash<T>()(p.first);
- size_t h2 = hash<T>()(p.second);
- // The decision below is at compile time
- return (sizeof(h1) <= sizeof(ceres::internal::uint32)) ?
- ceres::internal::Hash32NumWithSeed(h1, h2) :
- ceres::internal::Hash64NumWithSeed(h1, h2);
- }
- // Less than operator for MSVC.
- bool operator()(const pair<T, T>& a,
- const pair<T, T>& b) const {
- return a < b;
- }
- static const size_t bucket_size = 4; // These are required by MSVC
- static const size_t min_buckets = 8; // 4 and 8 are defaults.
-};
-
-CERES_HASH_NAMESPACE_END
-
-#endif // CERES_NO_UNORDERED_MAP
-#endif // CERES_INTERNAL_COLLECTIONS_PORT_H_
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 ebb2a62c544..3f6672f858c 100644
--- a/extern/ceres/internal/ceres/compressed_col_sparse_matrix_utils.cc
+++ b/extern/ceres/internal/ceres/compressed_col_sparse_matrix_utils.cc
@@ -47,8 +47,10 @@ void CompressedColumnScalarMatrixToBlockMatrix(
const vector<int>& col_blocks,
vector<int>* block_rows,
vector<int>* block_cols) {
- CHECK_NOTNULL(block_rows)->clear();
- CHECK_NOTNULL(block_cols)->clear();
+ CHECK(block_rows != nullptr);
+ CHECK(block_cols != nullptr);
+ block_rows->clear();
+ block_cols->clear();
const int num_row_blocks = row_blocks.size();
const int num_col_blocks = col_blocks.size();
diff --git a/extern/ceres/internal/ceres/compressed_row_jacobian_writer.cc b/extern/ceres/internal/ceres/compressed_row_jacobian_writer.cc
index 40977b74c67..1fc0116815c 100644
--- a/extern/ceres/internal/ceres/compressed_row_jacobian_writer.cc
+++ b/extern/ceres/internal/ceres/compressed_row_jacobian_writer.cc
@@ -30,6 +30,7 @@
#include "ceres/compressed_row_jacobian_writer.h"
+#include <iterator>
#include <utility>
#include <vector>
@@ -70,7 +71,7 @@ void CompressedRowJacobianWriter::PopulateJacobianRowAndColumnBlockVectors(
void CompressedRowJacobianWriter::GetOrderedParameterBlocks(
const Program* program,
int residual_id,
- vector<pair<int, int> >* evaluated_jacobian_blocks) {
+ vector<pair<int, int>>* evaluated_jacobian_blocks) {
const ResidualBlock* residual_block =
program->residual_blocks()[residual_id];
const int num_parameter_blocks = residual_block->NumParameterBlocks();
@@ -121,6 +122,7 @@ SparseMatrix* CompressedRowJacobianWriter::CreateJacobian() const {
// seems to be the only way to construct it without doing a memory copy.
int* rows = jacobian->mutable_rows();
int* cols = jacobian->mutable_cols();
+
int row_pos = 0;
rows[0] = 0;
for (int i = 0; i < residual_blocks.size(); ++i) {
@@ -206,7 +208,7 @@ void CompressedRowJacobianWriter::Write(int residual_id,
program_->residual_blocks()[residual_id];
const int num_residuals = residual_block->NumResiduals();
- vector<pair<int, int> > evaluated_jacobian_blocks;
+ vector<pair<int, int>> evaluated_jacobian_blocks;
GetOrderedParameterBlocks(program_, residual_id, &evaluated_jacobian_blocks);
// Where in the current row does the jacobian for a parameter block begin.
diff --git a/extern/ceres/internal/ceres/compressed_row_jacobian_writer.h b/extern/ceres/internal/ceres/compressed_row_jacobian_writer.h
index 1cd01235ccf..9fb414e2519 100644
--- a/extern/ceres/internal/ceres/compressed_row_jacobian_writer.h
+++ b/extern/ceres/internal/ceres/compressed_row_jacobian_writer.h
@@ -83,7 +83,7 @@ class CompressedRowJacobianWriter {
static void GetOrderedParameterBlocks(
const Program* program,
int residual_id,
- std::vector<std::pair<int, int> >* evaluated_jacobian_blocks);
+ std::vector<std::pair<int, int>>* evaluated_jacobian_blocks);
// JacobianWriter interface.
diff --git a/extern/ceres/internal/ceres/compressed_row_sparse_matrix.cc b/extern/ceres/internal/ceres/compressed_row_sparse_matrix.cc
index 91d18bbd604..e56de16bf92 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 2015 Google Inc. All rights reserved.
+// Copyright 2017 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 <vector>
#include "ceres/crs_matrix.h"
#include "ceres/internal/port.h"
+#include "ceres/random.h"
#include "ceres/triplet_sparse_matrix.h"
#include "glog/logging.h"
@@ -54,9 +55,7 @@ namespace {
//
// If this is the case, this functor will not be a StrictWeakOrdering.
struct RowColLessThan {
- RowColLessThan(const int* rows, const int* cols)
- : rows(rows), cols(cols) {
- }
+ RowColLessThan(const int* rows, const int* cols) : rows(rows), cols(cols) {}
bool operator()(const int x, const int y) const {
if (rows[x] == rows[y]) {
@@ -69,6 +68,91 @@ struct RowColLessThan {
const int* cols;
};
+void TransposeForCompressedRowSparseStructure(const int num_rows,
+ const int num_cols,
+ const int num_nonzeros,
+ const int* rows,
+ const int* cols,
+ const double* values,
+ int* transpose_rows,
+ int* transpose_cols,
+ double* transpose_values) {
+ // Explicitly zero out transpose_rows.
+ std::fill(transpose_rows, transpose_rows + num_cols + 1, 0);
+
+ // Count the number of entries in each column of the original matrix
+ // and assign to transpose_rows[col + 1].
+ for (int idx = 0; idx < num_nonzeros; ++idx) {
+ ++transpose_rows[cols[idx] + 1];
+ }
+
+ // Compute the starting position for each row in the transpose by
+ // computing the cumulative sum of the entries of transpose_rows.
+ for (int i = 1; i < num_cols + 1; ++i) {
+ transpose_rows[i] += transpose_rows[i - 1];
+ }
+
+ // Populate transpose_cols and (optionally) transpose_values by
+ // walking the entries of the source matrices. For each entry that
+ // is added, the value of transpose_row is incremented allowing us
+ // to keep track of where the next entry for that row should go.
+ //
+ // As a result transpose_row is shifted to the left by one entry.
+ for (int r = 0; r < num_rows; ++r) {
+ for (int idx = rows[r]; idx < rows[r + 1]; ++idx) {
+ const int c = cols[idx];
+ const int transpose_idx = transpose_rows[c]++;
+ transpose_cols[transpose_idx] = r;
+ if (values != NULL && transpose_values != NULL) {
+ transpose_values[transpose_idx] = values[idx];
+ }
+ }
+ }
+
+ // This loop undoes the left shift to transpose_rows introduced by
+ // the previous loop.
+ for (int i = num_cols - 1; i > 0; --i) {
+ transpose_rows[i] = transpose_rows[i - 1];
+ }
+ transpose_rows[0] = 0;
+}
+
+void AddRandomBlock(const int num_rows,
+ const int num_cols,
+ const int row_block_begin,
+ const int col_block_begin,
+ std::vector<int>* rows,
+ std::vector<int>* cols,
+ std::vector<double>* values) {
+ for (int r = 0; r < num_rows; ++r) {
+ for (int c = 0; c < num_cols; ++c) {
+ rows->push_back(row_block_begin + r);
+ cols->push_back(col_block_begin + c);
+ values->push_back(RandNormal());
+ }
+ }
+}
+
+void AddSymmetricRandomBlock(const int num_rows,
+ const int row_block_begin,
+ std::vector<int>* rows,
+ std::vector<int>* cols,
+ std::vector<double>* values) {
+ for (int r = 0; r < num_rows; ++r) {
+ for (int c = r; c < num_rows; ++c) {
+ const double v = RandNormal();
+ rows->push_back(row_block_begin + r);
+ cols->push_back(row_block_begin + c);
+ values->push_back(v);
+ if (r != c) {
+ rows->push_back(row_block_begin + c);
+ cols->push_back(row_block_begin + r);
+ values->push_back(v);
+ }
+ }
+ }
+}
+
} // namespace
// This constructor gives you a semi-initialized CompressedRowSparseMatrix.
@@ -77,69 +161,96 @@ CompressedRowSparseMatrix::CompressedRowSparseMatrix(int num_rows,
int max_num_nonzeros) {
num_rows_ = num_rows;
num_cols_ = num_cols;
+ storage_type_ = UNSYMMETRIC;
rows_.resize(num_rows + 1, 0);
cols_.resize(max_num_nonzeros, 0);
values_.resize(max_num_nonzeros, 0.0);
+ VLOG(1) << "# of rows: " << num_rows_ << " # of columns: " << num_cols_
+ << " max_num_nonzeros: " << cols_.size() << ". Allocating "
+ << (num_rows_ + 1) * sizeof(int) + // NOLINT
+ cols_.size() * sizeof(int) + // NOLINT
+ cols_.size() * sizeof(double); // NOLINT
+}
+
+CompressedRowSparseMatrix* CompressedRowSparseMatrix::FromTripletSparseMatrix(
+ const TripletSparseMatrix& input) {
+ return CompressedRowSparseMatrix::FromTripletSparseMatrix(input, false);
+}
- VLOG(1) << "# of rows: " << num_rows_
- << " # of columns: " << num_cols_
- << " max_num_nonzeros: " << cols_.size()
- << ". Allocating " << (num_rows_ + 1) * sizeof(int) + // NOLINT
- cols_.size() * sizeof(int) + // NOLINT
- cols_.size() * sizeof(double); // NOLINT
+CompressedRowSparseMatrix*
+CompressedRowSparseMatrix::FromTripletSparseMatrixTransposed(
+ const TripletSparseMatrix& input) {
+ return CompressedRowSparseMatrix::FromTripletSparseMatrix(input, true);
}
-CompressedRowSparseMatrix::CompressedRowSparseMatrix(
- const TripletSparseMatrix& m) {
- num_rows_ = m.num_rows();
- num_cols_ = m.num_cols();
+CompressedRowSparseMatrix* CompressedRowSparseMatrix::FromTripletSparseMatrix(
+ const TripletSparseMatrix& input, bool transpose) {
+ int num_rows = input.num_rows();
+ int num_cols = input.num_cols();
+ const int* rows = input.rows();
+ const int* cols = input.cols();
+ const double* values = input.values();
- rows_.resize(num_rows_ + 1, 0);
- cols_.resize(m.num_nonzeros(), 0);
- values_.resize(m.max_num_nonzeros(), 0.0);
+ if (transpose) {
+ std::swap(num_rows, num_cols);
+ std::swap(rows, cols);
+ }
- // index is the list of indices into the TripletSparseMatrix m.
- vector<int> index(m.num_nonzeros(), 0);
- for (int i = 0; i < m.num_nonzeros(); ++i) {
+ // index is the list of indices into the TripletSparseMatrix input.
+ vector<int> index(input.num_nonzeros(), 0);
+ for (int i = 0; i < input.num_nonzeros(); ++i) {
index[i] = i;
}
// Sort index such that the entries of m are ordered by row and ties
// are broken by column.
- sort(index.begin(), index.end(), RowColLessThan(m.rows(), m.cols()));
+ std::sort(index.begin(), index.end(), RowColLessThan(rows, cols));
+
+ VLOG(1) << "# of rows: " << num_rows << " # of columns: " << num_cols
+ << " num_nonzeros: " << input.num_nonzeros() << ". Allocating "
+ << ((num_rows + 1) * sizeof(int) + // NOLINT
+ input.num_nonzeros() * sizeof(int) + // NOLINT
+ input.num_nonzeros() * sizeof(double)); // NOLINT
+
+ CompressedRowSparseMatrix* output =
+ new CompressedRowSparseMatrix(num_rows, num_cols, input.num_nonzeros());
- VLOG(1) << "# of rows: " << num_rows_
- << " # of columns: " << num_cols_
- << " max_num_nonzeros: " << cols_.size()
- << ". Allocating "
- << ((num_rows_ + 1) * sizeof(int) + // NOLINT
- cols_.size() * sizeof(int) + // NOLINT
- cols_.size() * sizeof(double)); // NOLINT
+ if (num_rows == 0) {
+ // No data to copy.
+ return output;
+ }
// Copy the contents of the cols and values array in the order given
// by index and count the number of entries in each row.
- for (int i = 0; i < m.num_nonzeros(); ++i) {
+ int* output_rows = output->mutable_rows();
+ int* output_cols = output->mutable_cols();
+ double* output_values = output->mutable_values();
+
+ output_rows[0] = 0;
+ for (int i = 0; i < index.size(); ++i) {
const int idx = index[i];
- ++rows_[m.rows()[idx] + 1];
- cols_[i] = m.cols()[idx];
- values_[i] = m.values()[idx];
+ ++output_rows[rows[idx] + 1];
+ output_cols[i] = cols[idx];
+ output_values[i] = values[idx];
}
// Find the cumulative sum of the row counts.
- for (int i = 1; i < num_rows_ + 1; ++i) {
- rows_[i] += rows_[i - 1];
+ for (int i = 1; i < num_rows + 1; ++i) {
+ output_rows[i] += output_rows[i - 1];
}
- CHECK_EQ(num_nonzeros(), m.num_nonzeros());
+ CHECK_EQ(output->num_nonzeros(), input.num_nonzeros());
+ return output;
}
CompressedRowSparseMatrix::CompressedRowSparseMatrix(const double* diagonal,
int num_rows) {
- CHECK_NOTNULL(diagonal);
+ CHECK(diagonal != nullptr);
num_rows_ = num_rows;
num_cols_ = num_rows;
+ storage_type_ = UNSYMMETRIC;
rows_.resize(num_rows + 1);
cols_.resize(num_rows);
values_.resize(num_rows);
@@ -154,47 +265,150 @@ CompressedRowSparseMatrix::CompressedRowSparseMatrix(const double* diagonal,
CHECK_EQ(num_nonzeros(), num_rows);
}
-CompressedRowSparseMatrix::~CompressedRowSparseMatrix() {
-}
+CompressedRowSparseMatrix::~CompressedRowSparseMatrix() {}
void CompressedRowSparseMatrix::SetZero() {
std::fill(values_.begin(), values_.end(), 0);
}
+// TODO(sameeragarwal): Make RightMultiply and LeftMultiply
+// block-aware for higher performance.
void CompressedRowSparseMatrix::RightMultiply(const double* x,
double* y) const {
- CHECK_NOTNULL(x);
- CHECK_NOTNULL(y);
+ CHECK(x != nullptr);
+ CHECK(y != nullptr);
+
+ if (storage_type_ == UNSYMMETRIC) {
+ for (int r = 0; r < num_rows_; ++r) {
+ for (int idx = rows_[r]; idx < rows_[r + 1]; ++idx) {
+ const int c = cols_[idx];
+ const double v = values_[idx];
+ y[r] += v * x[c];
+ }
+ }
+ } else if (storage_type_ == UPPER_TRIANGULAR) {
+ // Because of their block structure, we will have entries that lie
+ // above (below) the diagonal for lower (upper) triangular matrices,
+ // so the loops below need to account for this.
+ for (int r = 0; r < num_rows_; ++r) {
+ int idx = rows_[r];
+ const int idx_end = rows_[r + 1];
+
+ // For upper triangular matrices r <= c, so skip entries with r
+ // > c.
+ while (idx < idx_end && r > cols_[idx]) {
+ ++idx;
+ }
- for (int r = 0; r < num_rows_; ++r) {
- for (int idx = rows_[r]; idx < rows_[r + 1]; ++idx) {
- y[r] += values_[idx] * x[cols_[idx]];
+ for (; idx < idx_end; ++idx) {
+ const int c = cols_[idx];
+ const double v = values_[idx];
+ y[r] += v * x[c];
+ // Since we are only iterating over the upper triangular part
+ // of the matrix, add contributions for the strictly lower
+ // triangular part.
+ if (r != c) {
+ y[c] += v * x[r];
+ }
+ }
+ }
+ } else if (storage_type_ == LOWER_TRIANGULAR) {
+ for (int r = 0; r < num_rows_; ++r) {
+ int idx = rows_[r];
+ const int idx_end = rows_[r + 1];
+ // For lower triangular matrices, we only iterate till we are r >=
+ // c.
+ for (; idx < idx_end && r >= cols_[idx]; ++idx) {
+ const int c = cols_[idx];
+ const double v = values_[idx];
+ y[r] += v * x[c];
+ // Since we are only iterating over the lower triangular part
+ // of the matrix, add contributions for the strictly upper
+ // triangular part.
+ if (r != c) {
+ y[c] += v * x[r];
+ }
+ }
}
+ } else {
+ LOG(FATAL) << "Unknown storage type: " << storage_type_;
}
}
void CompressedRowSparseMatrix::LeftMultiply(const double* x, double* y) const {
- CHECK_NOTNULL(x);
- CHECK_NOTNULL(y);
+ CHECK(x != nullptr);
+ CHECK(y != nullptr);
- for (int r = 0; r < num_rows_; ++r) {
- for (int idx = rows_[r]; idx < rows_[r + 1]; ++idx) {
- y[cols_[idx]] += values_[idx] * x[r];
+ if (storage_type_ == UNSYMMETRIC) {
+ for (int r = 0; r < num_rows_; ++r) {
+ for (int idx = rows_[r]; idx < rows_[r + 1]; ++idx) {
+ y[cols_[idx]] += values_[idx] * x[r];
+ }
}
+ } else {
+ // Since the matrix is symmetric, LeftMultiply = RightMultiply.
+ RightMultiply(x, y);
}
}
void CompressedRowSparseMatrix::SquaredColumnNorm(double* x) const {
- CHECK_NOTNULL(x);
+ CHECK(x != nullptr);
std::fill(x, x + num_cols_, 0.0);
- for (int idx = 0; idx < rows_[num_rows_]; ++idx) {
- x[cols_[idx]] += values_[idx] * values_[idx];
+ if (storage_type_ == UNSYMMETRIC) {
+ for (int idx = 0; idx < rows_[num_rows_]; ++idx) {
+ x[cols_[idx]] += values_[idx] * values_[idx];
+ }
+ } else if (storage_type_ == UPPER_TRIANGULAR) {
+ // Because of their block structure, we will have entries that lie
+ // above (below) the diagonal for lower (upper) triangular
+ // matrices, so the loops below need to account for this.
+ for (int r = 0; r < num_rows_; ++r) {
+ int idx = rows_[r];
+ const int idx_end = rows_[r + 1];
+
+ // For upper triangular matrices r <= c, so skip entries with r
+ // > c.
+ while (idx < idx_end && r > cols_[idx]) {
+ ++idx;
+ }
+
+ for (; idx < idx_end; ++idx) {
+ const int c = cols_[idx];
+ const double v2 = values_[idx] * values_[idx];
+ x[c] += v2;
+ // Since we are only iterating over the upper triangular part
+ // of the matrix, add contributions for the strictly lower
+ // triangular part.
+ if (r != c) {
+ x[r] += v2;
+ }
+ }
+ }
+ } else if (storage_type_ == LOWER_TRIANGULAR) {
+ for (int r = 0; r < num_rows_; ++r) {
+ int idx = rows_[r];
+ const int idx_end = rows_[r + 1];
+ // For lower triangular matrices, we only iterate till we are r >=
+ // c.
+ for (; idx < idx_end && r >= cols_[idx]; ++idx) {
+ const int c = cols_[idx];
+ const double v2 = values_[idx] * values_[idx];
+ x[c] += v2;
+ // Since we are only iterating over the lower triangular part
+ // of the matrix, add contributions for the strictly upper
+ // triangular part.
+ if (r != c) {
+ x[r] += v2;
+ }
+ }
+ }
+ } else {
+ LOG(FATAL) << "Unknown storage type: " << storage_type_;
}
}
-
void CompressedRowSparseMatrix::ScaleColumns(const double* scale) {
- CHECK_NOTNULL(scale);
+ CHECK(scale != nullptr);
for (int idx = 0; idx < rows_[num_rows_]; ++idx) {
values_[idx] *= scale[cols_[idx]];
@@ -202,7 +416,7 @@ void CompressedRowSparseMatrix::ScaleColumns(const double* scale) {
}
void CompressedRowSparseMatrix::ToDenseMatrix(Matrix* dense_matrix) const {
- CHECK_NOTNULL(dense_matrix);
+ CHECK(dense_matrix != nullptr);
dense_matrix->resize(num_rows_, num_cols_);
dense_matrix->setZero();
@@ -216,10 +430,17 @@ void CompressedRowSparseMatrix::ToDenseMatrix(Matrix* dense_matrix) const {
void CompressedRowSparseMatrix::DeleteRows(int delta_rows) {
CHECK_GE(delta_rows, 0);
CHECK_LE(delta_rows, num_rows_);
+ CHECK_EQ(storage_type_, UNSYMMETRIC);
num_rows_ -= delta_rows;
rows_.resize(num_rows_ + 1);
+ // The rest of the code updates the block information. Immediately
+ // return in case of no block information.
+ if (row_blocks_.empty()) {
+ return;
+ }
+
// Walk the list of row blocks until we reach the new number of rows
// and the drop the rest of the row blocks.
int num_row_blocks = 0;
@@ -233,9 +454,11 @@ void CompressedRowSparseMatrix::DeleteRows(int delta_rows) {
}
void CompressedRowSparseMatrix::AppendRows(const CompressedRowSparseMatrix& m) {
+ CHECK_EQ(storage_type_, UNSYMMETRIC);
CHECK_EQ(m.num_cols(), num_cols_);
- CHECK(row_blocks_.size() == 0 || m.row_blocks().size() !=0)
+ CHECK((row_blocks_.empty() && m.row_blocks().empty()) ||
+ (!row_blocks_.empty() && !m.row_blocks().empty()))
<< "Cannot append a matrix with row blocks to one without and vice versa."
<< "This matrix has : " << row_blocks_.size() << " row blocks."
<< "The matrix being appended has: " << m.row_blocks().size()
@@ -254,9 +477,8 @@ void CompressedRowSparseMatrix::AppendRows(const CompressedRowSparseMatrix& m) {
DCHECK_LT(num_nonzeros(), cols_.size());
if (m.num_nonzeros() > 0) {
std::copy(m.cols(), m.cols() + m.num_nonzeros(), &cols_[num_nonzeros()]);
- std::copy(m.values(),
- m.values() + m.num_nonzeros(),
- &values_[num_nonzeros()]);
+ std::copy(
+ m.values(), m.values() + m.num_nonzeros(), &values_[num_nonzeros()]);
}
rows_.resize(num_rows_ + m.num_rows() + 1);
@@ -270,20 +492,22 @@ void CompressedRowSparseMatrix::AppendRows(const CompressedRowSparseMatrix& m) {
}
num_rows_ += m.num_rows();
- row_blocks_.insert(row_blocks_.end(),
- m.row_blocks().begin(),
- m.row_blocks().end());
+
+ // The rest of the code updates the block information. Immediately
+ // return in case of no block information.
+ if (row_blocks_.empty()) {
+ return;
+ }
+
+ row_blocks_.insert(
+ row_blocks_.end(), m.row_blocks().begin(), m.row_blocks().end());
}
void CompressedRowSparseMatrix::ToTextFile(FILE* file) const {
- CHECK_NOTNULL(file);
+ CHECK(file != nullptr);
for (int r = 0; r < num_rows_; ++r) {
for (int idx = rows_[r]; idx < rows_[r + 1]; ++idx) {
- fprintf(file,
- "% 10d % 10d %17f\n",
- r,
- cols_[idx],
- values_[idx]);
+ fprintf(file, "% 10d % 10d %17f\n", r, cols_[idx], values_[idx]);
}
}
}
@@ -308,29 +532,8 @@ void CompressedRowSparseMatrix::SetMaxNumNonZeros(int num_nonzeros) {
values_.resize(num_nonzeros);
}
-void CompressedRowSparseMatrix::SolveLowerTriangularInPlace(
- double* solution) const {
- for (int r = 0; r < num_rows_; ++r) {
- for (int idx = rows_[r]; idx < rows_[r + 1] - 1; ++idx) {
- solution[r] -= values_[idx] * solution[cols_[idx]];
- }
- solution[r] /= values_[rows_[r + 1] - 1];
- }
-}
-
-void CompressedRowSparseMatrix::SolveLowerTriangularTransposeInPlace(
- double* solution) const {
- for (int r = num_rows_ - 1; r >= 0; --r) {
- solution[r] /= values_[rows_[r + 1] - 1];
- for (int idx = rows_[r + 1] - 2; idx >= rows_[r]; --idx) {
- solution[cols_[idx]] -= values_[idx] * solution[r];
- }
- }
-}
-
CompressedRowSparseMatrix* CompressedRowSparseMatrix::CreateBlockDiagonalMatrix(
- const double* diagonal,
- const vector<int>& blocks) {
+ const double* diagonal, const vector<int>& blocks) {
int num_rows = 0;
int num_nonzeros = 0;
for (int i = 0; i < blocks.size(); ++i) {
@@ -373,189 +576,152 @@ CompressedRowSparseMatrix* CompressedRowSparseMatrix::Transpose() const {
CompressedRowSparseMatrix* transpose =
new CompressedRowSparseMatrix(num_cols_, num_rows_, num_nonzeros());
- int* transpose_rows = transpose->mutable_rows();
- int* transpose_cols = transpose->mutable_cols();
- double* transpose_values = transpose->mutable_values();
-
- for (int idx = 0; idx < num_nonzeros(); ++idx) {
- ++transpose_rows[cols_[idx] + 1];
- }
-
- for (int i = 1; i < transpose->num_rows() + 1; ++i) {
- transpose_rows[i] += transpose_rows[i - 1];
- }
-
- for (int r = 0; r < num_rows(); ++r) {
- for (int idx = rows_[r]; idx < rows_[r + 1]; ++idx) {
- const int c = cols_[idx];
- const int transpose_idx = transpose_rows[c]++;
- transpose_cols[transpose_idx] = r;
- transpose_values[transpose_idx] = values_[idx];
- }
- }
-
- for (int i = transpose->num_rows() - 1; i > 0 ; --i) {
- transpose_rows[i] = transpose_rows[i - 1];
+ switch (storage_type_) {
+ case UNSYMMETRIC:
+ transpose->set_storage_type(UNSYMMETRIC);
+ break;
+ case LOWER_TRIANGULAR:
+ transpose->set_storage_type(UPPER_TRIANGULAR);
+ break;
+ case UPPER_TRIANGULAR:
+ transpose->set_storage_type(LOWER_TRIANGULAR);
+ break;
+ default:
+ LOG(FATAL) << "Unknown storage type: " << storage_type_;
+ };
+
+ TransposeForCompressedRowSparseStructure(num_rows(),
+ num_cols(),
+ num_nonzeros(),
+ rows(),
+ cols(),
+ values(),
+ transpose->mutable_rows(),
+ transpose->mutable_cols(),
+ transpose->mutable_values());
+
+ // The rest of the code updates the block information. Immediately
+ // return in case of no block information.
+ if (row_blocks_.empty()) {
+ return transpose;
}
- transpose_rows[0] = 0;
*(transpose->mutable_row_blocks()) = col_blocks_;
*(transpose->mutable_col_blocks()) = row_blocks_;
-
return transpose;
}
-namespace {
-// A ProductTerm is a term in the outer product of a matrix with
-// itself.
-struct ProductTerm {
- ProductTerm(const int row, const int col, const int index)
- : row(row), col(col), index(index) {
- }
-
- bool operator<(const ProductTerm& right) const {
- if (row == right.row) {
- if (col == right.col) {
- return index < right.index;
- }
- return col < right.col;
- }
- return row < right.row;
- }
-
- int row;
- int col;
- int index;
-};
-
-CompressedRowSparseMatrix*
-CompressAndFillProgram(const int num_rows,
- const int num_cols,
- const vector<ProductTerm>& product,
- vector<int>* program) {
- CHECK_GT(product.size(), 0);
-
- // Count the number of unique product term, which in turn is the
- // number of non-zeros in the outer product.
- int num_nonzeros = 1;
- for (int i = 1; i < product.size(); ++i) {
- if (product[i].row != product[i - 1].row ||
- product[i].col != product[i - 1].col) {
- ++num_nonzeros;
- }
- }
-
- CompressedRowSparseMatrix* matrix =
- new CompressedRowSparseMatrix(num_rows, num_cols, num_nonzeros);
-
- int* crsm_rows = matrix->mutable_rows();
- std::fill(crsm_rows, crsm_rows + num_rows + 1, 0);
- int* crsm_cols = matrix->mutable_cols();
- std::fill(crsm_cols, crsm_cols + num_nonzeros, 0);
-
- CHECK_NOTNULL(program)->clear();
- program->resize(product.size());
-
- // Iterate over the sorted product terms. This means each row is
- // filled one at a time, and we are able to assign a position in the
- // values array to each term.
- //
- // If terms repeat, i.e., they contribute to the same entry in the
- // result matrix), then they do not affect the sparsity structure of
- // the result matrix.
- int nnz = 0;
- crsm_cols[0] = product[0].col;
- crsm_rows[product[0].row + 1]++;
- (*program)[product[0].index] = nnz;
- for (int i = 1; i < product.size(); ++i) {
- const ProductTerm& previous = product[i - 1];
- const ProductTerm& current = product[i];
-
- // Sparsity structure is updated only if the term is not a repeat.
- if (previous.row != current.row || previous.col != current.col) {
- crsm_cols[++nnz] = current.col;
- crsm_rows[current.row + 1]++;
- }
-
- // All terms get assigned the position in the values array where
- // their value is accumulated.
- (*program)[current.index] = nnz;
- }
-
- for (int i = 1; i < num_rows + 1; ++i) {
- crsm_rows[i] += crsm_rows[i - 1];
- }
-
- return matrix;
-}
-
-} // namespace
-
-CompressedRowSparseMatrix*
-CompressedRowSparseMatrix::CreateOuterProductMatrixAndProgram(
- const CompressedRowSparseMatrix& m,
- vector<int>* program) {
- CHECK_NOTNULL(program)->clear();
- CHECK_GT(m.num_nonzeros(), 0)
- << "Congratulations, "
- << "you found a bug in Ceres. Please report it.";
-
- vector<ProductTerm> product;
- const vector<int>& row_blocks = m.row_blocks();
- int row_block_begin = 0;
- // Iterate over row blocks
- for (int row_block = 0; row_block < row_blocks.size(); ++row_block) {
- const int row_block_end = row_block_begin + row_blocks[row_block];
- // Compute the outer product terms for just one row per row block.
- const int r = row_block_begin;
- // Compute the lower triangular part of the product.
- for (int idx1 = m.rows()[r]; idx1 < m.rows()[r + 1]; ++idx1) {
- for (int idx2 = m.rows()[r]; idx2 <= idx1; ++idx2) {
- product.push_back(ProductTerm(m.cols()[idx1],
- m.cols()[idx2],
- product.size()));
- }
+CompressedRowSparseMatrix* CompressedRowSparseMatrix::CreateRandomMatrix(
+ CompressedRowSparseMatrix::RandomMatrixOptions options) {
+ CHECK_GT(options.num_row_blocks, 0);
+ CHECK_GT(options.min_row_block_size, 0);
+ CHECK_GT(options.max_row_block_size, 0);
+ CHECK_LE(options.min_row_block_size, options.max_row_block_size);
+
+ if (options.storage_type == UNSYMMETRIC) {
+ CHECK_GT(options.num_col_blocks, 0);
+ CHECK_GT(options.min_col_block_size, 0);
+ CHECK_GT(options.max_col_block_size, 0);
+ CHECK_LE(options.min_col_block_size, options.max_col_block_size);
+ } else {
+ // Symmetric matrices (LOWER_TRIANGULAR or UPPER_TRIANGULAR);
+ options.num_col_blocks = options.num_row_blocks;
+ options.min_col_block_size = options.min_row_block_size;
+ options.max_col_block_size = options.max_row_block_size;
+ }
+
+ CHECK_GT(options.block_density, 0.0);
+ CHECK_LE(options.block_density, 1.0);
+
+ vector<int> row_blocks;
+ vector<int> col_blocks;
+
+ // Generate the row block structure.
+ for (int i = 0; i < options.num_row_blocks; ++i) {
+ // Generate a random integer in [min_row_block_size, max_row_block_size]
+ const int delta_block_size =
+ Uniform(options.max_row_block_size - options.min_row_block_size);
+ row_blocks.push_back(options.min_row_block_size + delta_block_size);
+ }
+
+ if (options.storage_type == UNSYMMETRIC) {
+ // Generate the col block structure.
+ for (int i = 0; i < options.num_col_blocks; ++i) {
+ // Generate a random integer in [min_col_block_size, max_col_block_size]
+ const int delta_block_size =
+ Uniform(options.max_col_block_size - options.min_col_block_size);
+ col_blocks.push_back(options.min_col_block_size + delta_block_size);
}
- row_block_begin = row_block_end;
- }
- CHECK_EQ(row_block_begin, m.num_rows());
- sort(product.begin(), product.end());
- return CompressAndFillProgram(m.num_cols(), m.num_cols(), product, program);
-}
+ } else {
+ // Symmetric matrices (LOWER_TRIANGULAR or UPPER_TRIANGULAR);
+ col_blocks = row_blocks;
+ }
+
+ vector<int> tsm_rows;
+ vector<int> tsm_cols;
+ vector<double> tsm_values;
+
+ // For ease of construction, we are going to generate the
+ // CompressedRowSparseMatrix by generating it as a
+ // TripletSparseMatrix and then converting it to a
+ // CompressedRowSparseMatrix.
+
+ // It is possible that the random matrix is empty which is likely
+ // not what the user wants, so do the matrix generation till we have
+ // at least one non-zero entry.
+ while (tsm_values.empty()) {
+ tsm_rows.clear();
+ tsm_cols.clear();
+ tsm_values.clear();
+
+ int row_block_begin = 0;
+ for (int r = 0; r < options.num_row_blocks; ++r) {
+ int col_block_begin = 0;
+ for (int c = 0; c < options.num_col_blocks; ++c) {
+ if (((options.storage_type == UPPER_TRIANGULAR) && (r > c)) ||
+ ((options.storage_type == LOWER_TRIANGULAR) && (r < c))) {
+ col_block_begin += col_blocks[c];
+ continue;
+ }
-void CompressedRowSparseMatrix::ComputeOuterProduct(
- const CompressedRowSparseMatrix& m,
- const vector<int>& program,
- CompressedRowSparseMatrix* result) {
- result->SetZero();
- double* values = result->mutable_values();
- const vector<int>& row_blocks = m.row_blocks();
-
- int cursor = 0;
- int row_block_begin = 0;
- const double* m_values = m.values();
- const int* m_rows = m.rows();
- // Iterate over row blocks.
- for (int row_block = 0; row_block < row_blocks.size(); ++row_block) {
- const int row_block_end = row_block_begin + row_blocks[row_block];
- const int saved_cursor = cursor;
- for (int r = row_block_begin; r < row_block_end; ++r) {
- // Reuse the program segment for each row in this row block.
- cursor = saved_cursor;
- const int row_begin = m_rows[r];
- const int row_end = m_rows[r + 1];
- for (int idx1 = row_begin; idx1 < row_end; ++idx1) {
- const double v1 = m_values[idx1];
- for (int idx2 = row_begin; idx2 <= idx1; ++idx2, ++cursor) {
- values[program[cursor]] += v1 * m_values[idx2];
+ // Randomly determine if this block is present or not.
+ if (RandDouble() <= options.block_density) {
+ // If the matrix is symmetric, then we take care to generate
+ // symmetric diagonal blocks.
+ if (options.storage_type == UNSYMMETRIC || r != c) {
+ AddRandomBlock(row_blocks[r],
+ col_blocks[c],
+ row_block_begin,
+ col_block_begin,
+ &tsm_rows,
+ &tsm_cols,
+ &tsm_values);
+ } else {
+ AddSymmetricRandomBlock(row_blocks[r],
+ row_block_begin,
+ &tsm_rows,
+ &tsm_cols,
+ &tsm_values);
+ }
}
+ col_block_begin += col_blocks[c];
}
+ row_block_begin += row_blocks[r];
}
- row_block_begin = row_block_end;
}
- CHECK_EQ(row_block_begin, m.num_rows());
- CHECK_EQ(cursor, program.size());
+ 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 =
+ CompressedRowSparseMatrix::FromTripletSparseMatrix(
+ TripletSparseMatrix(
+ num_rows, num_cols, tsm_rows, tsm_cols, tsm_values),
+ kDoNotTranspose);
+ (*matrix->mutable_row_blocks()) = row_blocks;
+ (*matrix->mutable_col_blocks()) = col_blocks;
+ matrix->set_storage_type(options.storage_type);
+ return matrix;
}
} // namespace internal
diff --git a/extern/ceres/internal/ceres/compressed_row_sparse_matrix.h b/extern/ceres/internal/ceres/compressed_row_sparse_matrix.h
index 987339d09a1..758b40bbc8a 100644
--- a/extern/ceres/internal/ceres/compressed_row_sparse_matrix.h
+++ b/extern/ceres/internal/ceres/compressed_row_sparse_matrix.h
@@ -32,7 +32,6 @@
#define CERES_INTERNAL_COMPRESSED_ROW_SPARSE_MATRIX_H_
#include <vector>
-#include "ceres/internal/macros.h"
#include "ceres/internal/port.h"
#include "ceres/sparse_matrix.h"
#include "ceres/types.h"
@@ -48,13 +47,35 @@ class TripletSparseMatrix;
class CompressedRowSparseMatrix : public SparseMatrix {
public:
- // Build a matrix with the same content as the TripletSparseMatrix
- // m. TripletSparseMatrix objects are easier to construct
- // incrementally, so we use them to initialize SparseMatrix
- // objects.
+ enum StorageType {
+ UNSYMMETRIC,
+ // Matrix is assumed to be symmetric but only the lower triangular
+ // part of the matrix is stored.
+ LOWER_TRIANGULAR,
+ // Matrix is assumed to be symmetric but only the upper triangular
+ // part of the matrix is stored.
+ UPPER_TRIANGULAR
+ };
+
+ // Create a matrix with the same content as the TripletSparseMatrix
+ // input. We assume that input does not have any repeated
+ // entries.
//
- // We assume that m does not have any repeated entries.
- explicit CompressedRowSparseMatrix(const TripletSparseMatrix& m);
+ // The storage type of the matrix is set to UNSYMMETRIC.
+ //
+ // Caller owns the result.
+ static CompressedRowSparseMatrix* FromTripletSparseMatrix(
+ const TripletSparseMatrix& input);
+
+ // Create a matrix with the same content as the TripletSparseMatrix
+ // input transposed. We assume that input does not have any repeated
+ // entries.
+ //
+ // The storage type of the matrix is set to UNSYMMETRIC.
+ //
+ // Caller owns the result.
+ static 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
@@ -67,30 +88,30 @@ class CompressedRowSparseMatrix : public SparseMatrix {
// manually, instead of going via the indirect route of first
// constructing a TripletSparseMatrix, which leads to more than
// double the peak memory usage.
- CompressedRowSparseMatrix(int num_rows,
- int num_cols,
- int max_num_nonzeros);
+ //
+ // The storage type is set to UNSYMMETRIC.
+ CompressedRowSparseMatrix(int num_rows, int num_cols, int max_num_nonzeros);
// Build a square sparse diagonal matrix with num_rows rows and
// columns. The diagonal m(i,i) = diagonal(i);
+ //
+ // The storage type is set to UNSYMMETRIC
CompressedRowSparseMatrix(const double* diagonal, int num_rows);
- virtual ~CompressedRowSparseMatrix();
-
// SparseMatrix interface.
- virtual void SetZero();
- virtual void RightMultiply(const double* x, double* y) const;
- virtual void LeftMultiply(const double* x, double* y) const;
- virtual void SquaredColumnNorm(double* x) const;
- virtual void ScaleColumns(const double* scale);
-
- virtual void ToDenseMatrix(Matrix* dense_matrix) const;
- virtual void ToTextFile(FILE* file) const;
- virtual int num_rows() const { return num_rows_; }
- virtual int num_cols() const { return num_cols_; }
- virtual int num_nonzeros() const { return rows_[num_rows_]; }
- virtual const double* values() const { return &values_[0]; }
- virtual double* mutable_values() { return &values_[0]; }
+ virtual ~CompressedRowSparseMatrix();
+ void SetZero() final;
+ void RightMultiply(const double* x, double* y) const final;
+ void LeftMultiply(const double* x, double* y) const final;
+ void SquaredColumnNorm(double* x) const final;
+ void ScaleColumns(const double* scale) final;
+ void ToDenseMatrix(Matrix* dense_matrix) const final;
+ void ToTextFile(FILE* file) const final;
+ int num_rows() const final { return num_rows_; }
+ int num_cols() const final { return num_cols_; }
+ int num_nonzeros() const final { return rows_[num_rows_]; }
+ const double* values() const final { return &values_[0]; }
+ double* mutable_values() final { return &values_[0]; }
// Delete the bottom delta_rows.
// num_rows -= delta_rows
@@ -102,6 +123,15 @@ class CompressedRowSparseMatrix : public SparseMatrix {
void ToCRSMatrix(CRSMatrix* matrix) const;
+ CompressedRowSparseMatrix* Transpose() const;
+
+ // Destructive array resizing method.
+ void SetMaxNumNonZeros(int num_nonzeros);
+
+ // Non-destructive array resizing method.
+ void set_num_rows(const int num_rows) { num_rows_ = num_rows; }
+ void set_num_cols(const int num_cols) { num_cols_ = num_cols; }
+
// Low level access methods that expose the structure of the matrix.
const int* cols() const { return &cols_[0]; }
int* mutable_cols() { return &cols_[0]; }
@@ -109,60 +139,79 @@ class CompressedRowSparseMatrix : public SparseMatrix {
const int* rows() const { return &rows_[0]; }
int* mutable_rows() { return &rows_[0]; }
+ const StorageType storage_type() const { return storage_type_; }
+ void set_storage_type(const StorageType storage_type) {
+ storage_type_ = storage_type;
+ }
+
const std::vector<int>& row_blocks() const { return row_blocks_; }
std::vector<int>* mutable_row_blocks() { return &row_blocks_; }
const std::vector<int>& col_blocks() const { return col_blocks_; }
std::vector<int>* mutable_col_blocks() { return &col_blocks_; }
- // Destructive array resizing method.
- void SetMaxNumNonZeros(int num_nonzeros);
-
- // Non-destructive array resizing method.
- void set_num_rows(const int num_rows) { num_rows_ = num_rows; }
- void set_num_cols(const int num_cols) { num_cols_ = num_cols; }
-
- void SolveLowerTriangularInPlace(double* solution) const;
- void SolveLowerTriangularTransposeInPlace(double* solution) const;
-
- CompressedRowSparseMatrix* Transpose() const;
-
+ // 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(
- const double* diagonal,
- const std::vector<int>& blocks);
+ const double* diagonal, const std::vector<int>& blocks);
- // Compute the sparsity structure of the product m.transpose() * m
- // and create a CompressedRowSparseMatrix corresponding to it.
+ // Options struct to control the generation of random block sparse
+ // matrices in compressed row sparse format.
+ //
+ // The random matrix generation proceeds as follows.
//
- // Also compute a "program" vector, which for every term in the
- // outer product points to the entry in the values array of the
- // result matrix where it should be accumulated.
+ // First the row and column block structure is determined by
+ // generating random row and column block sizes that lie within the
+ // given bounds.
//
- // This program is used by the ComputeOuterProduct function below to
- // compute the outer product.
+ // Then we walk the block structure of the resulting matrix, and with
+ // probability block_density detemine whether they are structurally
+ // zero or not. If the answer is no, then we generate entries for the
+ // block which are distributed normally.
+ struct RandomMatrixOptions {
+ // Type of matrix to create.
+ //
+ // If storage_type is UPPER_TRIANGULAR (LOWER_TRIANGULAR), then
+ // create a square symmetric matrix with just the upper triangular
+ // (lower triangular) part. In this case, num_col_blocks,
+ // min_col_block_size and max_col_block_size will be ignored and
+ // assumed to be equal to the corresponding row settings.
+ StorageType storage_type = UNSYMMETRIC;
+
+ int num_row_blocks = 0;
+ int min_row_block_size = 0;
+ int max_row_block_size = 0;
+ int num_col_blocks = 0;
+ int min_col_block_size = 0;
+ int max_col_block_size = 0;
+
+ // 0 < block_density <= 1 is the probability of a block being
+ // present in the matrix. A given random matrix will not have
+ // precisely this density.
+ double block_density = 0.0;
+ };
+
+ // Create a random CompressedRowSparseMatrix whose entries are
+ // normally distributed and whose structure is determined by
+ // RandomMatrixOptions.
//
- // Since the entries of the program are the same for rows with the
- // same sparsity structure, the program only stores the result for
- // one row per row block. The ComputeOuterProduct function reuses
- // this information for each row in the row block.
- static CompressedRowSparseMatrix* CreateOuterProductMatrixAndProgram(
- const CompressedRowSparseMatrix& m,
- std::vector<int>* program);
-
- // Compute the values array for the expression m.transpose() * m,
- // where the matrix used to store the result and a program have been
- // created using the CreateOuterProductMatrixAndProgram function
- // above.
- static void ComputeOuterProduct(const CompressedRowSparseMatrix& m,
- const std::vector<int>& program,
- CompressedRowSparseMatrix* result);
+ // Caller owns the result.
+ static CompressedRowSparseMatrix* CreateRandomMatrix(
+ RandomMatrixOptions options);
private:
+ static CompressedRowSparseMatrix* FromTripletSparseMatrix(
+ const TripletSparseMatrix& input, bool transpose);
+
int num_rows_;
int num_cols_;
std::vector<int> rows_;
std::vector<int> cols_;
std::vector<double> values_;
+ StorageType storage_type_;
// If the matrix has an underlying block structure, then it can also
// carry with it row and column block sizes. This is auxilliary and
@@ -171,8 +220,6 @@ class CompressedRowSparseMatrix : public SparseMatrix {
// any way.
std::vector<int> row_blocks_;
std::vector<int> col_blocks_;
-
- CERES_DISALLOW_COPY_AND_ASSIGN(CompressedRowSparseMatrix);
};
} // namespace internal
diff --git a/extern/ceres/internal/ceres/concurrent_queue.h b/extern/ceres/internal/ceres/concurrent_queue.h
new file mode 100644
index 00000000000..52e2903022b
--- /dev/null
+++ b/extern/ceres/internal/ceres/concurrent_queue.h
@@ -0,0 +1,159 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2018 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 (Michael Vitus)
+
+#ifndef CERES_INTERNAL_CONCURRENT_QUEUE_H_
+#define CERES_INTERNAL_CONCURRENT_QUEUE_H_
+
+#include <condition_variable>
+#include <mutex>
+#include <queue>
+#include <thread>
+
+#include "glog/logging.h"
+
+namespace ceres {
+namespace internal {
+
+// A thread-safe multi-producer, multi-consumer queue for queueing items that
+// are typically handled asynchronously by multiple threads. The ConcurrentQueue
+// has two states which only affect the Wait call:
+//
+// (1) Waiters have been enabled (enabled by default or calling
+// EnableWaiters). The call to Wait will block until an item is available.
+// Push and pop will operate as expected.
+//
+// (2) StopWaiters has been called. All threads blocked in a Wait() call will
+// be woken up and pop any available items from the queue. All future Wait
+// requests will either return an element from the queue or return
+// immediately if no element is present. Push and pop will operate as
+// expected.
+//
+// A common use case is using the concurrent queue as an interface for
+// scheduling tasks for a set of thread workers:
+//
+// ConcurrentQueue<Task> task_queue;
+//
+// [Worker threads]:
+// Task task;
+// while(task_queue.Wait(&task)) {
+// ...
+// }
+//
+// [Producers]:
+// task_queue.Push(...);
+// ..
+// task_queue.Push(...);
+// ...
+// // Signal worker threads to stop blocking on Wait and terminate.
+// task_queue.StopWaiters();
+//
+template <typename T>
+class ConcurrentQueue {
+ public:
+ // Defaults the queue to blocking on Wait calls.
+ ConcurrentQueue() : wait_(true) {}
+
+ // Atomically push an element onto the queue. If a thread was waiting for an
+ // element, wake it up.
+ void Push(const T& value) {
+ std::lock_guard<std::mutex> lock(mutex_);
+ queue_.push(value);
+ work_pending_condition_.notify_one();
+ }
+
+ // Atomically pop an element from the queue. If an element is present, return
+ // true. If the queue was empty, return false.
+ bool Pop(T* value) {
+ CHECK(value != nullptr);
+
+ std::lock_guard<std::mutex> lock(mutex_);
+ return PopUnlocked(value);
+ }
+
+ // Atomically pop an element from the queue. Blocks until one is available or
+ // StopWaiters is called. Returns true if an element was successfully popped
+ // from the queue, otherwise returns false.
+ bool Wait(T* value) {
+ CHECK(value != nullptr);
+
+ std::unique_lock<std::mutex> lock(mutex_);
+ work_pending_condition_.wait(lock,
+ [&]() { return !(wait_ && queue_.empty()); });
+
+ return PopUnlocked(value);
+ }
+
+ // Unblock all threads waiting to pop a value from the queue, and they will
+ // exit Wait() without getting a value. All future Wait requests will return
+ // immediately if no element is present until EnableWaiters is called.
+ void StopWaiters() {
+ std::lock_guard<std::mutex> lock(mutex_);
+ wait_ = false;
+ work_pending_condition_.notify_all();
+ }
+
+ // Enable threads to block on Wait calls.
+ void EnableWaiters() {
+ std::lock_guard<std::mutex> lock(mutex_);
+ wait_ = true;
+ }
+
+ private:
+ // Pops an element from the queue. If an element is present, return
+ // true. If the queue was empty, return false. Not thread-safe. Must acquire
+ // the lock before calling.
+ bool PopUnlocked(T* value) {
+ if (queue_.empty()) {
+ return false;
+ }
+
+ *value = queue_.front();
+ queue_.pop();
+
+ return true;
+ }
+
+ // The mutex controls read and write access to the queue_ and stop_
+ // variables. It is also used to block the calling thread until an element is
+ // available to pop from the queue.
+ std::mutex mutex_;
+ std::condition_variable work_pending_condition_;
+
+ std::queue<T> queue_;
+ // If true, signals that callers of Wait will block waiting to pop an
+ // element off the queue.
+ bool wait_;
+};
+
+
+} // namespace internal
+} // namespace ceres
+
+#endif // CERES_INTERNAL_CONCURRENT_QUEUE_H_
diff --git a/extern/ceres/internal/ceres/conditioned_cost_function.cc b/extern/ceres/internal/ceres/conditioned_cost_function.cc
index 08899e3d246..d933ad7c462 100644
--- a/extern/ceres/internal/ceres/conditioned_cost_function.cc
+++ b/extern/ceres/internal/ceres/conditioned_cost_function.cc
@@ -68,7 +68,7 @@ ConditionedCostFunction::ConditionedCostFunction(
ConditionedCostFunction::~ConditionedCostFunction() {
if (ownership_ == TAKE_OWNERSHIP) {
- STLDeleteElements(&conditioners_);
+ STLDeleteUniqueContainerPointers(conditioners_.begin(), conditioners_.end());
} else {
wrapped_cost_function_.release();
}
diff --git a/extern/ceres/internal/ceres/conjugate_gradients_solver.cc b/extern/ceres/internal/ceres/conjugate_gradients_solver.cc
index 3702276a2fb..c6f85c15ea0 100644
--- a/extern/ceres/internal/ceres/conjugate_gradients_solver.cc
+++ b/extern/ceres/internal/ceres/conjugate_gradients_solver.cc
@@ -41,7 +41,6 @@
#include <cmath>
#include <cstddef>
-#include "ceres/fpclassify.h"
#include "ceres/internal/eigen.h"
#include "ceres/linear_operator.h"
#include "ceres/stringprintf.h"
@@ -53,7 +52,7 @@ namespace internal {
namespace {
bool IsZeroOrInfinity(double x) {
- return ((x == 0.0) || (IsInfinite(x)));
+ return ((x == 0.0) || std::isinf(x));
}
} // namespace
@@ -68,9 +67,9 @@ LinearSolver::Summary ConjugateGradientsSolver::Solve(
const double* b,
const LinearSolver::PerSolveOptions& per_solve_options,
double* x) {
- CHECK_NOTNULL(A);
- CHECK_NOTNULL(x);
- CHECK_NOTNULL(b);
+ CHECK(A != nullptr);
+ CHECK(x != nullptr);
+ CHECK(b != nullptr);
CHECK_EQ(A->num_rows(), A->num_cols());
LinearSolver::Summary summary;
@@ -148,7 +147,7 @@ LinearSolver::Summary ConjugateGradientsSolver::Solve(
q.setZero();
A->RightMultiply(p.data(), q.data());
const double pq = p.dot(q);
- if ((pq <= 0) || IsInfinite(pq)) {
+ if ((pq <= 0) || std::isinf(pq)) {
summary.termination_type = LINEAR_SOLVER_NO_CONVERGENCE;
summary.message = StringPrintf(
"Matrix is indefinite, no more progress can be made. "
@@ -158,7 +157,7 @@ LinearSolver::Summary ConjugateGradientsSolver::Solve(
}
const double alpha = rho / pq;
- if (IsInfinite(alpha)) {
+ if (std::isinf(alpha)) {
summary.termination_type = LINEAR_SOLVER_FAILURE;
summary.message =
StringPrintf("Numerical failure. alpha = rho / pq = %e, "
diff --git a/extern/ceres/internal/ceres/conjugate_gradients_solver.h b/extern/ceres/internal/ceres/conjugate_gradients_solver.h
index a1e18334414..d89383e6359 100644
--- a/extern/ceres/internal/ceres/conjugate_gradients_solver.h
+++ b/extern/ceres/internal/ceres/conjugate_gradients_solver.h
@@ -35,7 +35,6 @@
#define CERES_INTERNAL_CONJUGATE_GRADIENTS_SOLVER_H_
#include "ceres/linear_solver.h"
-#include "ceres/internal/macros.h"
namespace ceres {
namespace internal {
@@ -58,14 +57,13 @@ class LinearOperator;
class ConjugateGradientsSolver : public LinearSolver {
public:
explicit ConjugateGradientsSolver(const LinearSolver::Options& options);
- virtual Summary Solve(LinearOperator* A,
- const double* b,
- const LinearSolver::PerSolveOptions& per_solve_options,
- double* x);
+ Summary Solve(LinearOperator* A,
+ const double* b,
+ const LinearSolver::PerSolveOptions& per_solve_options,
+ double* x) final;
private:
const LinearSolver::Options options_;
- CERES_DISALLOW_COPY_AND_ASSIGN(ConjugateGradientsSolver);
};
} // namespace internal
diff --git a/extern/ceres/internal/ceres/context.cc b/extern/ceres/internal/ceres/context.cc
new file mode 100644
index 00000000000..e2232013b4b
--- /dev/null
+++ b/extern/ceres/internal/ceres/context.cc
@@ -0,0 +1,41 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2018 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 (Michael Vitus)
+
+#include "ceres/context.h"
+
+#include "ceres/context_impl.h"
+
+namespace ceres {
+
+Context* Context::Create() {
+ return new internal::ContextImpl();
+}
+
+} // namespace ceres
diff --git a/extern/ceres/internal/ceres/context_impl.cc b/extern/ceres/internal/ceres/context_impl.cc
new file mode 100644
index 00000000000..622f33a9dc0
--- /dev/null
+++ b/extern/ceres/internal/ceres/context_impl.cc
@@ -0,0 +1,43 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2018 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 (Michael Vitus)
+
+#include "ceres/context_impl.h"
+
+namespace ceres {
+namespace internal {
+
+void ContextImpl::EnsureMinimumThreads(int num_threads) {
+#ifdef CERES_USE_CXX_THREADS
+ thread_pool.Resize(num_threads);
+#endif // CERES_USE_CXX_THREADS
+
+}
+} // namespace internal
+} // namespace ceres
diff --git a/extern/ceres/internal/ceres/context_impl.h b/extern/ceres/internal/ceres/context_impl.h
new file mode 100644
index 00000000000..5c03ad71bab
--- /dev/null
+++ b/extern/ceres/internal/ceres/context_impl.h
@@ -0,0 +1,67 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2018 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 (Michael Vitus)
+
+#ifndef CERES_INTERNAL_CONTEXT_IMPL_H_
+#define CERES_INTERNAL_CONTEXT_IMPL_H_
+
+// This include must come before any #ifndef check on Ceres compile options.
+#include "ceres/internal/port.h"
+
+#include "ceres/context.h"
+
+#ifdef CERES_USE_CXX_THREADS
+#include "ceres/thread_pool.h"
+#endif // CERES_USE_CXX_THREADS
+
+namespace ceres {
+namespace internal {
+
+class ContextImpl : public Context {
+ public:
+ ContextImpl() {}
+ 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.
+ void EnsureMinimumThreads(int num_threads);
+
+#ifdef CERES_USE_CXX_THREADS
+ ThreadPool thread_pool;
+#endif // CERES_USE_CXX_THREADS
+};
+
+} // namespace internal
+} // namespace ceres
+
+#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 c6b42cf1516..c5d56f30bc3 100644
--- a/extern/ceres/internal/ceres/coordinate_descent_minimizer.cc
+++ b/extern/ceres/internal/ceres/coordinate_descent_minimizer.cc
@@ -30,16 +30,16 @@
#include "ceres/coordinate_descent_minimizer.h"
-#ifdef CERES_USE_OPENMP
-#include <omp.h>
-#endif
-
+#include <algorithm>
#include <iterator>
+#include <memory>
#include <numeric>
#include <vector>
+
#include "ceres/evaluator.h"
#include "ceres/linear_solver.h"
#include "ceres/minimizer.h"
+#include "ceres/parallel_for.h"
#include "ceres/parameter_block.h"
#include "ceres/parameter_block_ordering.h"
#include "ceres/problem_impl.h"
@@ -59,6 +59,11 @@ using std::set;
using std::string;
using std::vector;
+CoordinateDescentMinimizer::CoordinateDescentMinimizer(ContextImpl* context)
+ : context_(context) {
+ CHECK(context_ != nullptr);
+}
+
CoordinateDescentMinimizer::~CoordinateDescentMinimizer() {
}
@@ -74,19 +79,16 @@ bool CoordinateDescentMinimizer::Init(
// Serialize the OrderedGroups into a vector of parameter block
// offsets for parallel access.
map<ParameterBlock*, int> parameter_block_index;
- map<int, set<double*> > group_to_elements = ordering.group_to_elements();
- for (map<int, set<double*> >::const_iterator it = group_to_elements.begin();
- it != group_to_elements.end();
- ++it) {
- for (set<double*>::const_iterator ptr_it = it->second.begin();
- ptr_it != it->second.end();
- ++ptr_it) {
- parameter_blocks_.push_back(parameter_map.find(*ptr_it)->second);
+ map<int, set<double*>> group_to_elements = ordering.group_to_elements();
+ for (const auto& g_t_e : group_to_elements) {
+ const auto& elements = g_t_e.second;
+ for (double* parameter_block: elements) {
+ parameter_blocks_.push_back(parameter_map.find(parameter_block)->second);
parameter_block_index[parameter_blocks_.back()] =
parameter_blocks_.size() - 1;
}
independent_set_offsets_.push_back(
- independent_set_offsets_.back() + it->second.size());
+ independent_set_offsets_.back() + elements.size());
}
// The ordering does not have to contain all parameter blocks, so
@@ -109,8 +111,7 @@ bool CoordinateDescentMinimizer::Init(
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];
- const map<ParameterBlock*, int>::const_iterator it =
- parameter_block_index.find(parameter_block);
+ const auto it = parameter_block_index.find(parameter_block);
if (it != parameter_block_index.end()) {
residual_blocks_[it->second].push_back(residual_block);
}
@@ -120,6 +121,7 @@ bool CoordinateDescentMinimizer::Init(
evaluator_options_.linear_solver_type = DENSE_QR;
evaluator_options_.num_eliminate_blocks = 0;
evaluator_options_.num_threads = 1;
+ evaluator_options_.context = context_;
return true;
}
@@ -135,11 +137,12 @@ void CoordinateDescentMinimizer::Minimize(
parameter_block->SetConstant();
}
- scoped_array<LinearSolver*> linear_solvers(
+ std::unique_ptr<LinearSolver*[]> linear_solvers(
new LinearSolver*[options.num_threads]);
LinearSolver::Options linear_solver_options;
linear_solver_options.type = DENSE_QR;
+ linear_solver_options.context = context_;
for (int i = 0; i < options.num_threads; ++i) {
linear_solvers[i] = LinearSolver::Create(linear_solver_options);
@@ -148,13 +151,11 @@ void CoordinateDescentMinimizer::Minimize(
for (int i = 0; i < independent_set_offsets_.size() - 1; ++i) {
const int num_problems =
independent_set_offsets_[i + 1] - independent_set_offsets_[i];
- // No point paying the price for an OpemMP call if the set is of
- // size zero.
+ // Avoid parallelization overhead call if the set is empty.
if (num_problems == 0) {
continue;
}
-#ifdef CERES_USE_OPENMP
const int num_inner_iteration_threads =
min(options.num_threads, num_problems);
evaluator_options_.num_threads =
@@ -162,47 +163,43 @@ void CoordinateDescentMinimizer::Minimize(
// The parameter blocks in each independent set can be optimized
// in parallel, since they do not co-occur in any residual block.
-#pragma omp parallel for num_threads(num_inner_iteration_threads)
-#endif
- for (int j = independent_set_offsets_[i];
- j < independent_set_offsets_[i + 1];
- ++j) {
-#ifdef CERES_USE_OPENMP
- int thread_id = omp_get_thread_num();
-#else
- int thread_id = 0;
-#endif
-
- ParameterBlock* parameter_block = parameter_blocks_[j];
- const int old_index = parameter_block->index();
- const int old_delta_offset = parameter_block->delta_offset();
- parameter_block->SetVarying();
- parameter_block->set_index(0);
- parameter_block->set_delta_offset(0);
-
- Program inner_program;
- inner_program.mutable_parameter_blocks()->push_back(parameter_block);
- *inner_program.mutable_residual_blocks() = residual_blocks_[j];
-
- // TODO(sameeragarwal): Better error handling. Right now we
- // assume that this is not going to lead to problems of any
- // sort. Basically we should be checking for numerical failure
- // of some sort.
- //
- // On the other hand, if the optimization is a failure, that in
- // some ways is fine, since it won't change the parameters and
- // we are fine.
- Solver::Summary inner_summary;
- Solve(&inner_program,
- linear_solvers[thread_id],
- parameters + parameter_block->state_offset(),
- &inner_summary);
-
- parameter_block->set_index(old_index);
- parameter_block->set_delta_offset(old_delta_offset);
- parameter_block->SetState(parameters + parameter_block->state_offset());
- parameter_block->SetConstant();
- }
+ ParallelFor(
+ context_,
+ independent_set_offsets_[i],
+ independent_set_offsets_[i + 1],
+ num_inner_iteration_threads,
+ [&](int thread_id, int j) {
+ ParameterBlock* parameter_block = parameter_blocks_[j];
+ const int old_index = parameter_block->index();
+ const int old_delta_offset = parameter_block->delta_offset();
+ parameter_block->SetVarying();
+ parameter_block->set_index(0);
+ parameter_block->set_delta_offset(0);
+
+ Program inner_program;
+ inner_program.mutable_parameter_blocks()->push_back(parameter_block);
+ *inner_program.mutable_residual_blocks() = residual_blocks_[j];
+
+ // TODO(sameeragarwal): Better error handling. Right now we
+ // assume that this is not going to lead to problems of any
+ // sort. Basically we should be checking for numerical failure
+ // of some sort.
+ //
+ // On the other hand, if the optimization is a failure, that in
+ // some ways is fine, since it won't change the parameters and
+ // we are fine.
+ Solver::Summary inner_summary;
+ Solve(&inner_program,
+ linear_solvers[thread_id],
+ parameters + parameter_block->state_offset(),
+ &inner_summary);
+
+ parameter_block->set_index(old_index);
+ parameter_block->set_delta_offset(old_delta_offset);
+ parameter_block->SetState(parameters +
+ parameter_block->state_offset());
+ parameter_block->SetConstant();
+ });
}
for (int i = 0; i < parameter_blocks_.size(); ++i) {
@@ -227,14 +224,17 @@ void CoordinateDescentMinimizer::Solve(Program* program,
Minimizer::Options minimizer_options;
minimizer_options.evaluator.reset(
- CHECK_NOTNULL(Evaluator::Create(evaluator_options_, program, &error)));
+ Evaluator::Create(evaluator_options_, program, &error));
+ CHECK(minimizer_options.evaluator != nullptr);
minimizer_options.jacobian.reset(
- CHECK_NOTNULL(minimizer_options.evaluator->CreateJacobian()));
+ 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(
- CHECK_NOTNULL(TrustRegionStrategy::Create(trs_options)));
+ TrustRegionStrategy::Create(trs_options));
+ CHECK(minimizer_options.trust_region_strategy != nullptr);
minimizer_options.is_silent = true;
TrustRegionMinimizer minimizer;
@@ -245,17 +245,16 @@ bool CoordinateDescentMinimizer::IsOrderingValid(
const Program& program,
const ParameterBlockOrdering& ordering,
string* message) {
- const map<int, set<double*> >& group_to_elements =
+ const map<int, set<double*>>& group_to_elements =
ordering.group_to_elements();
// Verify that each group is an independent set
- map<int, set<double*> >::const_iterator it = group_to_elements.begin();
- for (; it != group_to_elements.end(); ++it) {
- if (!program.IsParameterBlockSetIndependent(it->second)) {
+ for (const auto& g_t_e : group_to_elements) {
+ if (!program.IsParameterBlockSetIndependent(g_t_e.second)) {
*message =
StringPrintf("The user-provided "
"parameter_blocks_for_inner_iterations does not "
- "form an independent set. Group Id: %d", it->first);
+ "form an independent set. Group Id: %d", g_t_e.first);
return false;
}
}
@@ -268,7 +267,7 @@ bool CoordinateDescentMinimizer::IsOrderingValid(
// points.
ParameterBlockOrdering* CoordinateDescentMinimizer::CreateOrdering(
const Program& program) {
- scoped_ptr<ParameterBlockOrdering> ordering(new ParameterBlockOrdering);
+ std::unique_ptr<ParameterBlockOrdering> ordering(new ParameterBlockOrdering);
ComputeRecursiveIndependentSetOrdering(program, ordering.get());
ordering->Reverse();
return ordering.release();
diff --git a/extern/ceres/internal/ceres/coordinate_descent_minimizer.h b/extern/ceres/internal/ceres/coordinate_descent_minimizer.h
index 25ea04ce622..7d17d53eb0f 100644
--- a/extern/ceres/internal/ceres/coordinate_descent_minimizer.h
+++ b/extern/ceres/internal/ceres/coordinate_descent_minimizer.h
@@ -34,6 +34,7 @@
#include <string>
#include <vector>
+#include "ceres/context_impl.h"
#include "ceres/evaluator.h"
#include "ceres/minimizer.h"
#include "ceres/problem_impl.h"
@@ -57,6 +58,8 @@ class LinearSolver;
// program are constant.
class CoordinateDescentMinimizer : public Minimizer {
public:
+ explicit CoordinateDescentMinimizer(ContextImpl* context);
+
bool Init(const Program& program,
const ProblemImpl::ParameterMap& parameter_map,
const ParameterBlockOrdering& ordering,
@@ -64,9 +67,10 @@ class CoordinateDescentMinimizer : public Minimizer {
// Minimizer interface.
virtual ~CoordinateDescentMinimizer();
- virtual void Minimize(const Minimizer::Options& options,
- double* parameters,
- Solver::Summary* summary);
+
+ void Minimize(const Minimizer::Options& options,
+ double* parameters,
+ Solver::Summary* summary) final;
// Verify that each group in the ordering forms an independent set.
static bool IsOrderingValid(const Program& program,
@@ -86,7 +90,7 @@ class CoordinateDescentMinimizer : public Minimizer {
Solver::Summary* summary);
std::vector<ParameterBlock*> parameter_blocks_;
- std::vector<std::vector<ResidualBlock*> > residual_blocks_;
+ std::vector<std::vector<ResidualBlock*>> residual_blocks_;
// The optimization is performed in rounds. In each round all the
// parameter blocks that form one independent set are optimized in
// parallel. This array, marks the boundaries of the independent
@@ -94,6 +98,8 @@ class CoordinateDescentMinimizer : public Minimizer {
std::vector<int> independent_set_offsets_;
Evaluator::Options evaluator_options_;
+
+ ContextImpl* context_;
};
} // namespace internal
diff --git a/extern/ceres/internal/ceres/corrector.cc b/extern/ceres/internal/ceres/corrector.cc
index 720182868c1..4ac0dc3cd86 100644
--- a/extern/ceres/internal/ceres/corrector.cc
+++ b/extern/ceres/internal/ceres/corrector.cc
@@ -43,7 +43,7 @@ Corrector::Corrector(const double sq_norm, const double rho[3]) {
sqrt_rho1_ = sqrt(rho[1]);
// If sq_norm = 0.0, the correction becomes trivial, the residual
- // and the jacobian are scaled by the squareroot of the derivative
+ // and the jacobian are scaled by the square root of the derivative
// of rho. Handling this case explicitly avoids the divide by zero
// error that would occur below.
//
@@ -59,7 +59,7 @@ Corrector::Corrector(const double sq_norm, const double rho[3]) {
// correction which re-wights the gradient of the function by the
// square root of the derivative of rho, and the Gauss-Newton
// Hessian gets both the scaling and the rank-1 curvature
- // correction. Normaly, alpha is upper bounded by one, but with this
+ // correction. Normally, alpha is upper bounded by one, but with this
// change, alpha is bounded above by zero.
//
// Empirically we have observed that the full Triggs correction and
diff --git a/extern/ceres/internal/ceres/corrector.h b/extern/ceres/internal/ceres/corrector.h
index 315f012ab1d..a5b03dda803 100644
--- a/extern/ceres/internal/ceres/corrector.h
+++ b/extern/ceres/internal/ceres/corrector.h
@@ -43,7 +43,7 @@ namespace internal {
// radial robust loss.
//
// The key idea here is to look at the expressions for the robustified
-// gauss newton approximation and then take its squareroot to get the
+// 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 Corrector {
diff --git a/extern/ceres/internal/ceres/covariance.cc b/extern/ceres/internal/ceres/covariance.cc
index cb280a36847..8256078409a 100644
--- a/extern/ceres/internal/ceres/covariance.cc
+++ b/extern/ceres/internal/ceres/covariance.cc
@@ -50,15 +50,15 @@ Covariance::~Covariance() {
}
bool Covariance::Compute(
- const vector<pair<const double*, const double*> >& covariance_blocks,
+ const vector<pair<const double*, const double*>>& covariance_blocks,
Problem* problem) {
- return impl_->Compute(covariance_blocks, problem->problem_impl_.get());
+ return impl_->Compute(covariance_blocks, problem->impl_.get());
}
bool Covariance::Compute(
const vector<const double*>& parameter_blocks,
Problem* problem) {
- return impl_->Compute(parameter_blocks, problem->problem_impl_.get());
+ return impl_->Compute(parameter_blocks, problem->impl_.get());
}
bool Covariance::GetCovarianceBlock(const double* parameter_block1,
@@ -82,7 +82,7 @@ bool Covariance::GetCovarianceBlockInTangentSpace(
bool Covariance::GetCovarianceMatrix(
const vector<const double*>& parameter_blocks,
- double* covariance_matrix) {
+ double* covariance_matrix) const {
return impl_->GetCovarianceMatrixInTangentOrAmbientSpace(parameter_blocks,
true, // ambient
covariance_matrix);
@@ -90,7 +90,7 @@ bool Covariance::GetCovarianceMatrix(
bool Covariance::GetCovarianceMatrixInTangentSpace(
const std::vector<const double *>& parameter_blocks,
- double *covariance_matrix) {
+ double *covariance_matrix) const {
return impl_->GetCovarianceMatrixInTangentOrAmbientSpace(parameter_blocks,
false, // tangent
covariance_matrix);
diff --git a/extern/ceres/internal/ceres/covariance_impl.cc b/extern/ceres/internal/ceres/covariance_impl.cc
index d698f88fa9b..6c26412d854 100644
--- a/extern/ceres/internal/ceres/covariance_impl.cc
+++ b/extern/ceres/internal/ceres/covariance_impl.cc
@@ -30,14 +30,12 @@
#include "ceres/covariance_impl.h"
-#ifdef CERES_USE_OPENMP
-#include <omp.h>
-#endif
-
#include <algorithm>
#include <cstdlib>
+#include <memory>
#include <numeric>
#include <sstream>
+#include <unordered_set>
#include <utility>
#include <vector>
@@ -45,13 +43,14 @@
#include "Eigen/SparseQR"
#include "Eigen/SVD"
-#include "ceres/collections_port.h"
#include "ceres/compressed_col_sparse_matrix_utils.h"
#include "ceres/compressed_row_sparse_matrix.h"
#include "ceres/covariance.h"
#include "ceres/crs_matrix.h"
#include "ceres/internal/eigen.h"
#include "ceres/map_util.h"
+#include "ceres/parallel_for.h"
+#include "ceres/parallel_utils.h"
#include "ceres/parameter_block.h"
#include "ceres/problem_impl.h"
#include "ceres/residual_block.h"
@@ -69,21 +68,22 @@ using std::sort;
using std::swap;
using std::vector;
-typedef vector<pair<const double*, const double*> > CovarianceBlocks;
+typedef vector<pair<const double*, const double*>> CovarianceBlocks;
CovarianceImpl::CovarianceImpl(const Covariance::Options& options)
: options_(options),
is_computed_(false),
is_valid_(false) {
-#ifndef CERES_USE_OPENMP
+#ifdef CERES_NO_THREADS
if (options_.num_threads > 1) {
LOG(WARNING)
- << "OpenMP support is not compiled into this binary; "
+ << "No threading support is compiled into this binary; "
<< "only options.num_threads = 1 is supported. Switching "
<< "to single threaded mode.";
options_.num_threads = 1;
}
#endif
+
evaluate_options_.num_threads = options_.num_threads;
evaluate_options_.apply_loss_function = options_.apply_loss_function;
}
@@ -97,7 +97,7 @@ template <typename T> void CheckForDuplicates(vector<T> blocks) {
std::adjacent_find(blocks.begin(), blocks.end());
if (it != blocks.end()) {
// In case there are duplicates, we search for their location.
- map<T, vector<int> > blocks_map;
+ map<T, vector<int>> blocks_map;
for (int i = 0; i < blocks.size(); ++i) {
blocks_map[blocks[i]].push_back(i);
}
@@ -122,7 +122,7 @@ template <typename T> void CheckForDuplicates(vector<T> blocks) {
bool CovarianceImpl::Compute(const CovarianceBlocks& covariance_blocks,
ProblemImpl* problem) {
- CheckForDuplicates<pair<const double*, const double*> >(covariance_blocks);
+ CheckForDuplicates<pair<const double*, const double*>>(covariance_blocks);
problem_ = problem;
parameter_block_to_row_index_.clear();
covariance_matrix_.reset(NULL);
@@ -333,55 +333,47 @@ bool CovarianceImpl::GetCovarianceMatrixInTangentOrAmbientSpace(
// Assemble the blocks in the covariance matrix.
MatrixRef covariance(covariance_matrix, covariance_size, covariance_size);
const int num_threads = options_.num_threads;
- scoped_array<double> workspace(
+ std::unique_ptr<double[]> workspace(
new double[num_threads * max_covariance_block_size *
max_covariance_block_size]);
bool success = true;
-// The collapse() directive is only supported in OpenMP 3.0 and higher. OpenMP
-// 3.0 was released in May 2008 (hence the version number).
-#if _OPENMP >= 200805
-# pragma omp parallel for num_threads(num_threads) schedule(dynamic) collapse(2)
-#else
-# pragma omp parallel for num_threads(num_threads) schedule(dynamic)
-#endif
- for (int i = 0; i < num_parameters; ++i) {
- for (int j = 0; j < num_parameters; ++j) {
- // The second loop can't start from j = i for compatibility with OpenMP
- // collapse command. The conditional serves as a workaround
- if (j >= i) {
+ // Technically the following code is a double nested loop where
+ // i = 1:n, j = i:n.
+ int iteration_count = (num_parameters * (num_parameters + 1)) / 2;
+ problem_->context()->EnsureMinimumThreads(num_threads);
+ ParallelFor(
+ problem_->context(),
+ 0,
+ iteration_count,
+ num_threads,
+ [&](int thread_id, int k) {
+ int i, j;
+ LinearIndexToUpperTriangularIndex(k, num_parameters, &i, &j);
+
int covariance_row_idx = cum_parameter_size[i];
int covariance_col_idx = cum_parameter_size[j];
int size_i = parameter_sizes[i];
int size_j = parameter_sizes[j];
-#ifdef CERES_USE_OPENMP
- int thread_id = omp_get_thread_num();
-#else
- int thread_id = 0;
-#endif
double* covariance_block =
- workspace.get() +
- thread_id * max_covariance_block_size * max_covariance_block_size;
+ workspace.get() + thread_id * max_covariance_block_size *
+ max_covariance_block_size;
if (!GetCovarianceBlockInTangentOrAmbientSpace(
- parameters[i], parameters[j], lift_covariance_to_ambient_space,
- covariance_block)) {
+ parameters[i], parameters[j],
+ lift_covariance_to_ambient_space, covariance_block)) {
success = false;
}
- covariance.block(covariance_row_idx, covariance_col_idx,
- size_i, size_j) =
- MatrixRef(covariance_block, size_i, size_j);
+ covariance.block(covariance_row_idx, covariance_col_idx, size_i,
+ size_j) = MatrixRef(covariance_block, size_i, size_j);
if (i != j) {
covariance.block(covariance_col_idx, covariance_row_idx,
size_j, size_i) =
MatrixRef(covariance_block, size_i, size_j).transpose();
-
}
- }
- }
- }
+ });
return success;
}
@@ -397,7 +389,7 @@ bool CovarianceImpl::ComputeCovarianceSparsity(
vector<double*> all_parameter_blocks;
problem->GetParameterBlocks(&all_parameter_blocks);
const ProblemImpl::ParameterMap& parameter_map = problem->parameter_map();
- HashSet<ParameterBlock*> parameter_blocks_in_use;
+ std::unordered_set<ParameterBlock*> parameter_blocks_in_use;
vector<ResidualBlock*> residual_blocks;
problem->GetResidualBlocks(&residual_blocks);
@@ -496,13 +488,10 @@ bool CovarianceImpl::ComputeCovarianceSparsity(
// rows of the covariance matrix in order.
int i = 0; // index into covariance_blocks.
int cursor = 0; // index into the covariance matrix.
- for (map<const double*, int>::const_iterator it =
- parameter_block_to_row_index_.begin();
- it != parameter_block_to_row_index_.end();
- ++it) {
- const double* row_block = it->first;
+ for (const auto& entry : parameter_block_to_row_index_) {
+ const double* row_block = entry.first;
const int row_block_size = problem->ParameterBlockLocalSize(row_block);
- int row_begin = it->second;
+ int row_begin = entry.second;
// Iterate over the covariance blocks contained in this row block
// and count the number of columns in this row block.
@@ -538,24 +527,37 @@ bool CovarianceImpl::ComputeCovarianceSparsity(
}
bool CovarianceImpl::ComputeCovarianceValues() {
- switch (options_.algorithm_type) {
- case DENSE_SVD:
- return ComputeCovarianceValuesUsingDenseSVD();
- case SUITE_SPARSE_QR:
-#ifndef CERES_NO_SUITESPARSE
+ if (options_.algorithm_type == DENSE_SVD) {
+ return ComputeCovarianceValuesUsingDenseSVD();
+ }
+
+ if (options_.algorithm_type == SPARSE_QR) {
+ if (options_.sparse_linear_algebra_library_type == EIGEN_SPARSE) {
+ return ComputeCovarianceValuesUsingEigenSparseQR();
+ }
+
+ if (options_.sparse_linear_algebra_library_type == SUITE_SPARSE) {
+#if !defined(CERES_NO_SUITESPARSE)
return ComputeCovarianceValuesUsingSuiteSparseQR();
#else
- LOG(ERROR) << "SuiteSparse is required to use the "
- << "SUITE_SPARSE_QR algorithm.";
+ LOG(ERROR) << "SuiteSparse is required to use the SPARSE_QR algorithm "
+ << "with "
+ << "Covariance::Options::sparse_linear_algebra_library_type "
+ << "= SUITE_SPARSE.";
return false;
#endif
- case EIGEN_SPARSE_QR:
- return ComputeCovarianceValuesUsingEigenSparseQR();
- default:
- LOG(ERROR) << "Unsupported covariance estimation algorithm type: "
- << CovarianceAlgorithmTypeToString(options_.algorithm_type);
- return false;
+ }
+
+ LOG(ERROR) << "Unsupported "
+ << "Covariance::Options::sparse_linear_algebra_library_type "
+ << "= "
+ << SparseLinearAlgebraLibraryTypeToString(
+ options_.sparse_linear_algebra_library_type);
+ return false;
}
+
+ LOG(ERROR) << "Unsupported Covariance::Options::algorithm_type = "
+ << CovarianceAlgorithmTypeToString(options_.algorithm_type);
return false;
}
@@ -649,8 +651,12 @@ bool CovarianceImpl::ComputeCovarianceValuesUsingSuiteSparseQR() {
&permutation,
&cc);
event_logger.AddEvent("Numeric Factorization");
- CHECK_NOTNULL(permutation);
- CHECK_NOTNULL(R);
+ if (R == nullptr) {
+ LOG(ERROR) << "Something is wrong. SuiteSparseQR returned R = nullptr.";
+ free(permutation);
+ cholmod_l_finish(&cc);
+ return false;
+ }
if (rank < cholmod_jacobian.ncol) {
LOG(ERROR) << "Jacobian matrix is rank deficient. "
@@ -663,8 +669,14 @@ bool CovarianceImpl::ComputeCovarianceValuesUsingSuiteSparseQR() {
}
vector<int> inverse_permutation(num_cols);
- for (SuiteSparse_long i = 0; i < num_cols; ++i) {
- inverse_permutation[permutation[i]] = i;
+ if (permutation) {
+ for (SuiteSparse_long i = 0; i < num_cols; ++i) {
+ inverse_permutation[permutation[i]] = i;
+ }
+ } else {
+ for (SuiteSparse_long i = 0; i < num_cols; ++i) {
+ inverse_permutation[i] = i;
+ }
}
const int* rows = covariance_matrix_->rows();
@@ -681,35 +693,29 @@ bool CovarianceImpl::ComputeCovarianceValuesUsingSuiteSparseQR() {
// Since the covariance matrix is symmetric, the i^th row and column
// are equal.
const int num_threads = options_.num_threads;
- scoped_array<double> workspace(new double[num_threads * num_cols]);
-
-#pragma omp parallel for num_threads(num_threads) schedule(dynamic)
- for (int r = 0; r < num_cols; ++r) {
- const int row_begin = rows[r];
- const int row_end = rows[r + 1];
- if (row_end == row_begin) {
- continue;
- }
-
-# ifdef CERES_USE_OPENMP
- int thread_id = omp_get_thread_num();
-# else
- int thread_id = 0;
-# endif
-
- double* solution = workspace.get() + thread_id * num_cols;
- SolveRTRWithSparseRHS<SuiteSparse_long>(
- num_cols,
- static_cast<SuiteSparse_long*>(R->i),
- static_cast<SuiteSparse_long*>(R->p),
- static_cast<double*>(R->x),
- inverse_permutation[r],
- solution);
- for (int idx = row_begin; idx < row_end; ++idx) {
- const int c = cols[idx];
- values[idx] = solution[inverse_permutation[c]];
- }
- }
+ std::unique_ptr<double[]> workspace(new double[num_threads * num_cols]);
+
+ problem_->context()->EnsureMinimumThreads(num_threads);
+ ParallelFor(
+ problem_->context(),
+ 0,
+ num_cols,
+ num_threads,
+ [&](int thread_id, int r) {
+ const int row_begin = rows[r];
+ const int row_end = rows[r + 1];
+ if (row_end != row_begin) {
+ double* solution = workspace.get() + thread_id * num_cols;
+ SolveRTRWithSparseRHS<SuiteSparse_long>(
+ num_cols, static_cast<SuiteSparse_long*>(R->i),
+ static_cast<SuiteSparse_long*>(R->p), static_cast<double*>(R->x),
+ inverse_permutation[r], solution);
+ for (int idx = row_begin; idx < row_end; ++idx) {
+ const int c = cols[idx];
+ values[idx] = solution[inverse_permutation[c]];
+ }
+ }
+ });
free(permutation);
cholmod_l_free_sparse(&R, &cc);
@@ -746,8 +752,8 @@ bool CovarianceImpl::ComputeCovarianceValuesUsingDenseSVD() {
}
event_logger.AddEvent("ConvertToDenseMatrix");
- Eigen::JacobiSVD<Matrix> svd(dense_jacobian,
- Eigen::ComputeThinU | Eigen::ComputeThinV);
+ Eigen::BDCSVD<Matrix> svd(dense_jacobian,
+ Eigen::ComputeThinU | Eigen::ComputeThinV);
event_logger.AddEvent("SingularValueDecomposition");
@@ -838,7 +844,7 @@ bool CovarianceImpl::ComputeCovarianceValuesUsingEigenSparseQR() {
jacobian.rows.data(), jacobian.cols.data(), jacobian.values.data());
event_logger.AddEvent("ConvertToSparseMatrix");
- Eigen::SparseQR<EigenSparseMatrix, Eigen::COLAMDOrdering<int> >
+ Eigen::SparseQR<EigenSparseMatrix, Eigen::COLAMDOrdering<int>>
qr_solver(sparse_jacobian);
event_logger.AddEvent("QRDecomposition");
@@ -873,38 +879,35 @@ bool CovarianceImpl::ComputeCovarianceValuesUsingEigenSparseQR() {
// are equal.
const int num_cols = jacobian.num_cols;
const int num_threads = options_.num_threads;
- scoped_array<double> workspace(new double[num_threads * num_cols]);
-
-#pragma omp parallel for num_threads(num_threads) schedule(dynamic)
- for (int r = 0; r < num_cols; ++r) {
- const int row_begin = rows[r];
- const int row_end = rows[r + 1];
- if (row_end == row_begin) {
- continue;
- }
-
-# ifdef CERES_USE_OPENMP
- int thread_id = omp_get_thread_num();
-# else
- int thread_id = 0;
-# endif
-
- double* solution = workspace.get() + thread_id * num_cols;
- SolveRTRWithSparseRHS<int>(
- num_cols,
- qr_solver.matrixR().innerIndexPtr(),
- qr_solver.matrixR().outerIndexPtr(),
- &qr_solver.matrixR().data().value(0),
- inverse_permutation.indices().coeff(r),
- solution);
-
- // Assign the values of the computed covariance using the
- // inverse permutation used in the QR factorization.
- for (int idx = row_begin; idx < row_end; ++idx) {
- const int c = cols[idx];
- values[idx] = solution[inverse_permutation.indices().coeff(c)];
- }
- }
+ std::unique_ptr<double[]> workspace(new double[num_threads * num_cols]);
+
+ problem_->context()->EnsureMinimumThreads(num_threads);
+ ParallelFor(
+ problem_->context(),
+ 0,
+ num_cols,
+ num_threads,
+ [&](int thread_id, int r) {
+ const int row_begin = rows[r];
+ const int row_end = rows[r + 1];
+ if (row_end != row_begin) {
+ double* solution = workspace.get() + thread_id * num_cols;
+ SolveRTRWithSparseRHS<int>(
+ num_cols,
+ qr_solver.matrixR().innerIndexPtr(),
+ qr_solver.matrixR().outerIndexPtr(),
+ &qr_solver.matrixR().data().value(0),
+ inverse_permutation.indices().coeff(r),
+ solution);
+
+ // Assign the values of the computed covariance using the
+ // inverse permutation used in the QR factorization.
+ for (int idx = row_begin; idx < row_end; ++idx) {
+ const int c = cols[idx];
+ values[idx] = solution[inverse_permutation.indices().coeff(c)];
+ }
+ }
+ });
event_logger.AddEvent("Inverse");
diff --git a/extern/ceres/internal/ceres/covariance_impl.h b/extern/ceres/internal/ceres/covariance_impl.h
index a3f0761f57c..065e43c60fc 100644
--- a/extern/ceres/internal/ceres/covariance_impl.h
+++ b/extern/ceres/internal/ceres/covariance_impl.h
@@ -32,11 +32,11 @@
#define CERES_INTERNAL_COVARIANCE_IMPL_H_
#include <map>
+#include <memory>
#include <set>
#include <utility>
#include <vector>
#include "ceres/covariance.h"
-#include "ceres/internal/scoped_ptr.h"
#include "ceres/problem_impl.h"
#include "ceres/suitesparse.h"
@@ -52,7 +52,7 @@ class CovarianceImpl {
bool Compute(
const std::vector<std::pair<const double*,
- const double*> >& covariance_blocks,
+ const double*>>& covariance_blocks,
ProblemImpl* problem);
bool Compute(
@@ -72,7 +72,7 @@ class CovarianceImpl {
bool ComputeCovarianceSparsity(
const std::vector<std::pair<const double*,
- const double*> >& covariance_blocks,
+ const double*>>& covariance_blocks,
ProblemImpl* problem);
bool ComputeCovarianceValues();
@@ -92,7 +92,7 @@ class CovarianceImpl {
bool is_valid_;
std::map<const double*, int> parameter_block_to_row_index_;
std::set<const double*> constant_parameter_blocks_;
- scoped_ptr<CompressedRowSparseMatrix> covariance_matrix_;
+ std::unique_ptr<CompressedRowSparseMatrix> covariance_matrix_;
};
} // namespace internal
diff --git a/extern/ceres/internal/ceres/cxsparse.cc b/extern/ceres/internal/ceres/cxsparse.cc
new file mode 100644
index 00000000000..5a028773206
--- /dev/null
+++ b/extern/ceres/internal/ceres/cxsparse.cc
@@ -0,0 +1,284 @@
+// 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: strandmark@google.com (Petter Strandmark)
+
+// This include must come before any #ifndef check on Ceres compile options.
+#include "ceres/internal/port.h"
+
+#ifndef CERES_NO_CXSPARSE
+
+#include "ceres/cxsparse.h"
+
+#include <string>
+#include <vector>
+
+#include "ceres/compressed_col_sparse_matrix_utils.h"
+#include "ceres/compressed_row_sparse_matrix.h"
+#include "ceres/triplet_sparse_matrix.h"
+#include "glog/logging.h"
+
+namespace ceres {
+namespace internal {
+
+using std::vector;
+
+CXSparse::CXSparse() : scratch_(NULL), scratch_size_(0) {}
+
+CXSparse::~CXSparse() {
+ if (scratch_size_ > 0) {
+ cs_di_free(scratch_);
+ }
+}
+
+csn* CXSparse::Cholesky(cs_di* A, cs_dis* symbolic_factor) {
+ return cs_di_chol(A, symbolic_factor);
+}
+
+void CXSparse::Solve(cs_dis* symbolic_factor, csn* numeric_factor, double* b) {
+ // Make sure we have enough scratch space available.
+ const int num_cols = numeric_factor->L->n;
+ if (scratch_size_ < num_cols) {
+ if (scratch_size_ > 0) {
+ cs_di_free(scratch_);
+ }
+ scratch_ =
+ reinterpret_cast<CS_ENTRY*>(cs_di_malloc(num_cols, sizeof(CS_ENTRY)));
+ scratch_size_ = num_cols;
+ }
+
+ // When the Cholesky factor succeeded, these methods are
+ // guaranteed to succeeded as well. In the comments below, "x"
+ // refers to the scratch space.
+ //
+ // Set x = P * b.
+ CHECK(cs_di_ipvec(symbolic_factor->pinv, b, scratch_, num_cols));
+ // Set x = L \ x.
+ CHECK(cs_di_lsolve(numeric_factor->L, scratch_));
+ // Set x = L' \ x.
+ CHECK(cs_di_ltsolve(numeric_factor->L, scratch_));
+ // Set b = P' * x.
+ CHECK(cs_di_pvec(symbolic_factor->pinv, scratch_, b, num_cols));
+}
+
+bool CXSparse::SolveCholesky(cs_di* lhs, double* rhs_and_solution) {
+ return cs_cholsol(1, lhs, rhs_and_solution);
+}
+
+cs_dis* CXSparse::AnalyzeCholesky(cs_di* A) {
+ // order = 1 for Cholesky factor.
+ return cs_schol(1, A);
+}
+
+cs_dis* CXSparse::AnalyzeCholeskyWithNaturalOrdering(cs_di* A) {
+ // order = 0 for Natural ordering.
+ return cs_schol(0, A);
+}
+
+cs_dis* CXSparse::BlockAnalyzeCholesky(cs_di* A,
+ const vector<int>& row_blocks,
+ const vector<int>& col_blocks) {
+ const int num_row_blocks = row_blocks.size();
+ const int num_col_blocks = col_blocks.size();
+
+ vector<int> block_rows;
+ vector<int> block_cols;
+ CompressedColumnScalarMatrixToBlockMatrix(
+ A->i, A->p, row_blocks, col_blocks, &block_rows, &block_cols);
+ cs_di block_matrix;
+ block_matrix.m = num_row_blocks;
+ block_matrix.n = num_col_blocks;
+ block_matrix.nz = -1;
+ block_matrix.nzmax = block_rows.size();
+ block_matrix.p = &block_cols[0];
+ block_matrix.i = &block_rows[0];
+ block_matrix.x = NULL;
+
+ int* ordering = cs_amd(1, &block_matrix);
+ vector<int> block_ordering(num_row_blocks, -1);
+ std::copy(ordering, ordering + num_row_blocks, &block_ordering[0]);
+ cs_free(ordering);
+
+ vector<int> scalar_ordering;
+ BlockOrderingToScalarOrdering(row_blocks, block_ordering, &scalar_ordering);
+
+ cs_dis* 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);
+
+ symbolic_factor->parent = cs_etree(permuted_A, 0);
+ int* postordering = cs_post(symbolic_factor->parent, A->n);
+ int* column_counts =
+ cs_counts(permuted_A, symbolic_factor->parent, postordering, 0);
+ cs_free(postordering);
+ cs_spfree(permuted_A);
+
+ symbolic_factor->cp = (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;
+
+ cs_free(column_counts);
+
+ if (symbolic_factor->lnz < 0) {
+ cs_sfree(symbolic_factor);
+ symbolic_factor = NULL;
+ }
+
+ return symbolic_factor;
+}
+
+cs_di CXSparse::CreateSparseMatrixTransposeView(CompressedRowSparseMatrix* A) {
+ cs_di At;
+ At.m = A->num_cols();
+ At.n = A->num_rows();
+ At.nz = -1;
+ At.nzmax = A->num_nonzeros();
+ At.p = A->mutable_rows();
+ At.i = A->mutable_cols();
+ At.x = A->mutable_values();
+ return At;
+}
+
+cs_di* CXSparse::CreateSparseMatrix(TripletSparseMatrix* tsm) {
+ cs_di_sparse tsm_wrapper;
+ tsm_wrapper.nzmax = tsm->num_nonzeros();
+ tsm_wrapper.nz = tsm->num_nonzeros();
+ tsm_wrapper.m = tsm->num_rows();
+ tsm_wrapper.n = tsm->num_cols();
+ tsm_wrapper.p = tsm->mutable_cols();
+ tsm_wrapper.i = tsm->mutable_rows();
+ tsm_wrapper.x = tsm->mutable_values();
+
+ return cs_compress(&tsm_wrapper);
+}
+
+void CXSparse::ApproximateMinimumDegreeOrdering(cs_di* A, int* ordering) {
+ int* cs_ordering = cs_amd(1, A);
+ std::copy(cs_ordering, cs_ordering + A->m, ordering);
+ cs_free(cs_ordering);
+}
+
+cs_di* CXSparse::TransposeMatrix(cs_di* A) { return cs_di_transpose(A, 1); }
+
+cs_di* CXSparse::MatrixMatrixMultiply(cs_di* A, cs_di* B) {
+ return cs_di_multiply(A, B);
+}
+
+void CXSparse::Free(cs_di* sparse_matrix) { cs_di_spfree(sparse_matrix); }
+
+void CXSparse::Free(cs_dis* symbolic_factor) { cs_di_sfree(symbolic_factor); }
+
+void CXSparse::Free(csn* numeric_factor) { cs_di_nfree(numeric_factor); }
+
+std::unique_ptr<SparseCholesky> CXSparseCholesky::Create(
+ const OrderingType ordering_type) {
+ return std::unique_ptr<SparseCholesky>(new CXSparseCholesky(ordering_type));
+}
+
+CompressedRowSparseMatrix::StorageType CXSparseCholesky::StorageType() const {
+ return CompressedRowSparseMatrix::LOWER_TRIANGULAR;
+}
+
+CXSparseCholesky::CXSparseCholesky(const OrderingType ordering_type)
+ : ordering_type_(ordering_type),
+ symbolic_factor_(NULL),
+ numeric_factor_(NULL) {}
+
+CXSparseCholesky::~CXSparseCholesky() {
+ FreeSymbolicFactorization();
+ FreeNumericFactorization();
+}
+
+LinearSolverTerminationType CXSparseCholesky::Factorize(
+ CompressedRowSparseMatrix* lhs, std::string* message) {
+ CHECK_EQ(lhs->storage_type(), StorageType());
+ if (lhs == NULL) {
+ *message = "Failure: Input lhs is NULL.";
+ return LINEAR_SOLVER_FATAL_ERROR;
+ }
+
+ cs_di cs_lhs = cs_.CreateSparseMatrixTransposeView(lhs);
+
+ if (symbolic_factor_ == NULL) {
+ if (ordering_type_ == NATURAL) {
+ symbolic_factor_ = cs_.AnalyzeCholeskyWithNaturalOrdering(&cs_lhs);
+ } else {
+ if (!lhs->col_blocks().empty() && !(lhs->row_blocks().empty())) {
+ symbolic_factor_ = cs_.BlockAnalyzeCholesky(
+ &cs_lhs, lhs->col_blocks(), lhs->row_blocks());
+ } else {
+ symbolic_factor_ = cs_.AnalyzeCholesky(&cs_lhs);
+ }
+ }
+
+ if (symbolic_factor_ == NULL) {
+ *message = "CXSparse Failure : Symbolic factorization failed.";
+ return LINEAR_SOLVER_FATAL_ERROR;
+ }
+ }
+
+ FreeNumericFactorization();
+ numeric_factor_ = cs_.Cholesky(&cs_lhs, symbolic_factor_);
+ if (numeric_factor_ == NULL) {
+ *message = "CXSparse Failure : Numeric factorization failed.";
+ return LINEAR_SOLVER_FAILURE;
+ }
+
+ return LINEAR_SOLVER_SUCCESS;
+}
+
+LinearSolverTerminationType CXSparseCholesky::Solve(const double* rhs,
+ double* solution,
+ std::string* message) {
+ CHECK(numeric_factor_ != NULL)
+ << "Solve called without a call to Factorize first.";
+ const int num_cols = numeric_factor_->L->n;
+ memcpy(solution, rhs, num_cols * sizeof(*solution));
+ cs_.Solve(symbolic_factor_, numeric_factor_, solution);
+ return LINEAR_SOLVER_SUCCESS;
+}
+
+void CXSparseCholesky::FreeSymbolicFactorization() {
+ if (symbolic_factor_ != NULL) {
+ cs_.Free(symbolic_factor_);
+ symbolic_factor_ = NULL;
+ }
+}
+
+void CXSparseCholesky::FreeNumericFactorization() {
+ if (numeric_factor_ != NULL) {
+ cs_.Free(numeric_factor_);
+ numeric_factor_ = NULL;
+ }
+}
+
+} // namespace internal
+} // namespace ceres
+
+#endif // CERES_NO_CXSPARSE
diff --git a/extern/ceres/internal/ceres/cxsparse.h b/extern/ceres/internal/ceres/cxsparse.h
index 26dd1927a78..dc4740ceaee 100644
--- a/extern/ceres/internal/ceres/cxsparse.h
+++ b/extern/ceres/internal/ceres/cxsparse.h
@@ -36,7 +36,12 @@
#ifndef CERES_NO_CXSPARSE
+#include <memory>
+#include <string>
#include <vector>
+
+#include "ceres/linear_solver.h"
+#include "ceres/sparse_cholesky.h"
#include "cs.h"
namespace ceres {
@@ -47,21 +52,27 @@ class TripletSparseMatrix;
// This object provides access to solving linear systems using Cholesky
// factorization with a known symbolic factorization. This features does not
-// explicity exist in CXSparse. The methods in the class are nonstatic because
+// explicitly exist in CXSparse. The methods in the class are nonstatic because
// the class manages internal scratch space.
class CXSparse {
public:
CXSparse();
~CXSparse();
- // Solves a symmetric linear system A * x = b using Cholesky factorization.
- // A - The system matrix.
- // symbolic_factorization - The symbolic factorization of A. This is obtained
- // from AnalyzeCholesky.
- // b - The right hand size of the linear equation. This
- // array will also recieve the solution.
- // Returns false if Cholesky factorization of A fails.
- bool SolveCholesky(cs_di* A, cs_dis* symbolic_factorization, double* b);
+ // Solve the system lhs * solution = rhs in place by using an
+ // approximate minimum degree fill reducing ordering.
+ bool SolveCholesky(cs_di* lhs, double* rhs_and_solution);
+
+ // Solves a linear system given its symbolic and numeric factorization.
+ void Solve(cs_dis* symbolic_factor,
+ csn* numeric_factor,
+ double* rhs_and_solution);
+
+ // Compute the numeric Cholesky factorization of A, given its
+ // symbolic factorization.
+ //
+ // Caller owns the result.
+ csn* Cholesky(cs_di* A, cs_dis* symbolic_factor);
// Creates a sparse matrix from a compressed-column form. No memory is
// allocated or copied; the structure A is filled out with info from the
@@ -117,6 +128,7 @@ class CXSparse {
void Free(cs_di* sparse_matrix);
void Free(cs_dis* symbolic_factorization);
+ void Free(csn* numeric_factorization);
private:
// Cached scratch space
@@ -124,10 +136,37 @@ class CXSparse {
int scratch_size_;
};
+// An implementation of SparseCholesky interface using the CXSparse
+// library.
+class CXSparseCholesky : public SparseCholesky {
+ public:
+ // Factory
+ static std::unique_ptr<SparseCholesky> Create(OrderingType ordering_type);
+
+ // SparseCholesky interface.
+ virtual ~CXSparseCholesky();
+ CompressedRowSparseMatrix::StorageType StorageType() const final;
+ LinearSolverTerminationType Factorize(CompressedRowSparseMatrix* lhs,
+ std::string* message) final;
+ LinearSolverTerminationType Solve(const double* rhs,
+ double* solution,
+ std::string* message) final;
+
+ private:
+ CXSparseCholesky(const OrderingType ordering_type);
+ void FreeSymbolicFactorization();
+ void FreeNumericFactorization();
+
+ const OrderingType ordering_type_;
+ CXSparse cs_;
+ cs_dis* symbolic_factor_;
+ csn* numeric_factor_;
+};
+
} // namespace internal
} // namespace ceres
-#else // CERES_NO_CXSPARSE
+#else // CERES_NO_CXSPARSE
typedef void cs_dis;
diff --git a/extern/ceres/internal/ceres/dense_normal_cholesky_solver.cc b/extern/ceres/internal/ceres/dense_normal_cholesky_solver.cc
index b13cf3fc9f6..fe7d931a3fd 100644
--- a/extern/ceres/internal/ceres/dense_normal_cholesky_solver.cc
+++ b/extern/ceres/internal/ceres/dense_normal_cholesky_solver.cc
@@ -36,7 +36,6 @@
#include "ceres/blas.h"
#include "ceres/dense_sparse_matrix.h"
#include "ceres/internal/eigen.h"
-#include "ceres/internal/scoped_ptr.h"
#include "ceres/lapack.h"
#include "ceres/linear_solver.h"
#include "ceres/types.h"
diff --git a/extern/ceres/internal/ceres/dense_normal_cholesky_solver.h b/extern/ceres/internal/ceres/dense_normal_cholesky_solver.h
index 11287ebf675..976718e8615 100644
--- a/extern/ceres/internal/ceres/dense_normal_cholesky_solver.h
+++ b/extern/ceres/internal/ceres/dense_normal_cholesky_solver.h
@@ -35,7 +35,6 @@
#define CERES_INTERNAL_DENSE_NORMAL_CHOLESKY_SOLVER_H_
#include "ceres/linear_solver.h"
-#include "ceres/internal/macros.h"
namespace ceres {
namespace internal {
@@ -79,11 +78,11 @@ class DenseNormalCholeskySolver: public DenseSparseMatrixSolver {
explicit DenseNormalCholeskySolver(const LinearSolver::Options& options);
private:
- virtual LinearSolver::Summary SolveImpl(
+ LinearSolver::Summary SolveImpl(
DenseSparseMatrix* A,
const double* b,
const LinearSolver::PerSolveOptions& per_solve_options,
- double* x);
+ double* x) final;
LinearSolver::Summary SolveUsingLAPACK(
DenseSparseMatrix* A,
@@ -98,7 +97,6 @@ class DenseNormalCholeskySolver: public DenseSparseMatrixSolver {
double* x);
const LinearSolver::Options options_;
- CERES_DISALLOW_COPY_AND_ASSIGN(DenseNormalCholeskySolver);
};
} // namespace internal
diff --git a/extern/ceres/internal/ceres/dense_qr_solver.cc b/extern/ceres/internal/ceres/dense_qr_solver.cc
index e85fdfc0c68..161e9c67a00 100644
--- a/extern/ceres/internal/ceres/dense_qr_solver.cc
+++ b/extern/ceres/internal/ceres/dense_qr_solver.cc
@@ -30,12 +30,10 @@
#include "ceres/dense_qr_solver.h"
-
#include <cstddef>
#include "Eigen/Dense"
#include "ceres/dense_sparse_matrix.h"
#include "ceres/internal/eigen.h"
-#include "ceres/internal/scoped_ptr.h"
#include "ceres/lapack.h"
#include "ceres/linear_solver.h"
#include "ceres/types.h"
diff --git a/extern/ceres/internal/ceres/dense_qr_solver.h b/extern/ceres/internal/ceres/dense_qr_solver.h
index 1a6e0898c56..9ea959db68d 100644
--- a/extern/ceres/internal/ceres/dense_qr_solver.h
+++ b/extern/ceres/internal/ceres/dense_qr_solver.h
@@ -34,7 +34,6 @@
#include "ceres/linear_solver.h"
#include "ceres/internal/eigen.h"
-#include "ceres/internal/macros.h"
namespace ceres {
namespace internal {
@@ -84,11 +83,11 @@ class DenseQRSolver: public DenseSparseMatrixSolver {
explicit DenseQRSolver(const LinearSolver::Options& options);
private:
- virtual LinearSolver::Summary SolveImpl(
+ LinearSolver::Summary SolveImpl(
DenseSparseMatrix* A,
const double* b,
const LinearSolver::PerSolveOptions& per_solve_options,
- double* x);
+ double* x) final;
LinearSolver::Summary SolveUsingEigen(
DenseSparseMatrix* A,
@@ -106,7 +105,6 @@ class DenseQRSolver: public DenseSparseMatrixSolver {
ColMajorMatrix lhs_;
Vector rhs_;
Vector work_;
- CERES_DISALLOW_COPY_AND_ASSIGN(DenseQRSolver);
};
} // namespace internal
diff --git a/extern/ceres/internal/ceres/dense_sparse_matrix.cc b/extern/ceres/internal/ceres/dense_sparse_matrix.cc
index 19db867d4aa..72e08360dd0 100644
--- a/extern/ceres/internal/ceres/dense_sparse_matrix.cc
+++ b/extern/ceres/internal/ceres/dense_sparse_matrix.cc
@@ -166,7 +166,7 @@ ColMajorMatrixRef DenseSparseMatrix::mutable_matrix() {
void DenseSparseMatrix::ToTextFile(FILE* file) const {
- CHECK_NOTNULL(file);
+ CHECK(file != nullptr);
const int active_rows =
(has_diagonal_reserved_ && !has_diagonal_appended_)
? (m_.rows() - m_.cols())
diff --git a/extern/ceres/internal/ceres/dense_sparse_matrix.h b/extern/ceres/internal/ceres/dense_sparse_matrix.h
index b011bfddee7..6d3d504ea36 100644
--- a/extern/ceres/internal/ceres/dense_sparse_matrix.h
+++ b/extern/ceres/internal/ceres/dense_sparse_matrix.h
@@ -33,10 +33,8 @@
#ifndef CERES_INTERNAL_DENSE_SPARSE_MATRIX_H_
#define CERES_INTERNAL_DENSE_SPARSE_MATRIX_H_
-#include "ceres/sparse_matrix.h"
#include "ceres/internal/eigen.h"
-#include "ceres/internal/macros.h"
-#include "ceres/internal/scoped_ptr.h"
+#include "ceres/sparse_matrix.h"
#include "ceres/types.h"
namespace ceres {
@@ -57,18 +55,18 @@ class DenseSparseMatrix : public SparseMatrix {
virtual ~DenseSparseMatrix() {}
// SparseMatrix interface.
- virtual void SetZero();
- virtual void RightMultiply(const double* x, double* y) const;
- virtual void LeftMultiply(const double* x, double* y) const;
- virtual void SquaredColumnNorm(double* x) const;
- virtual void ScaleColumns(const double* scale);
- virtual void ToDenseMatrix(Matrix* dense_matrix) const;
- virtual void ToTextFile(FILE* file) const;
- virtual int num_rows() const;
- virtual int num_cols() const;
- virtual int num_nonzeros() const;
- virtual const double* values() const { return m_.data(); }
- virtual double* mutable_values() { return m_.data(); }
+ void SetZero() final;
+ void RightMultiply(const double* x, double* y) const final;
+ void LeftMultiply(const double* x, double* y) const final;
+ void SquaredColumnNorm(double* x) const final;
+ void ScaleColumns(const double* scale) final;
+ void ToDenseMatrix(Matrix* dense_matrix) const final;
+ void ToTextFile(FILE* file) const final;
+ int num_rows() const final;
+ int num_cols() const final;
+ int num_nonzeros() const final;
+ const double* values() const final { return m_.data(); }
+ double* mutable_values() final { return m_.data(); }
ConstColMajorMatrixRef matrix() const;
ColMajorMatrixRef mutable_matrix();
diff --git a/extern/ceres/internal/ceres/dogleg_strategy.cc b/extern/ceres/internal/ceres/dogleg_strategy.cc
index 839e1816338..ecc6b882338 100644
--- a/extern/ceres/internal/ceres/dogleg_strategy.cc
+++ b/extern/ceres/internal/ceres/dogleg_strategy.cc
@@ -30,7 +30,9 @@
#include "ceres/dogleg_strategy.h"
+#include <algorithm>
#include <cmath>
+
#include "Eigen/Dense"
#include "ceres/array_utils.h"
#include "ceres/internal/eigen.h"
@@ -64,7 +66,7 @@ DoglegStrategy::DoglegStrategy(const TrustRegionStrategy::Options& options)
dogleg_step_norm_(0.0),
reuse_(false),
dogleg_type_(options.dogleg_type) {
- CHECK_NOTNULL(linear_solver_);
+ CHECK(linear_solver_ != nullptr);
CHECK_GT(min_diagonal_, 0.0);
CHECK_LE(min_diagonal_, max_diagonal_);
CHECK_GT(max_radius_, 0.0);
@@ -79,9 +81,9 @@ TrustRegionStrategy::Summary DoglegStrategy::ComputeStep(
SparseMatrix* jacobian,
const double* residuals,
double* step) {
- CHECK_NOTNULL(jacobian);
- CHECK_NOTNULL(residuals);
- CHECK_NOTNULL(step);
+ CHECK(jacobian != nullptr);
+ CHECK(residuals != nullptr);
+ CHECK(step != nullptr);
const int n = jacobian->num_cols();
if (reuse_) {
@@ -469,7 +471,7 @@ double DoglegStrategy::EvaluateSubspaceModel(const Vector2d& x) const {
// In the failure case, another step should be taken, such as the traditional
// dogleg step.
bool DoglegStrategy::FindMinimumOnTrustRegionBoundary(Vector2d* minimum) const {
- CHECK_NOTNULL(minimum);
+ CHECK(minimum != nullptr);
// Return (0, 0) in all error cases.
minimum->setZero();
diff --git a/extern/ceres/internal/ceres/dogleg_strategy.h b/extern/ceres/internal/ceres/dogleg_strategy.h
index 046b9d824c9..1150940efd3 100644
--- a/extern/ceres/internal/ceres/dogleg_strategy.h
+++ b/extern/ceres/internal/ceres/dogleg_strategy.h
@@ -58,15 +58,14 @@ class DoglegStrategy : public TrustRegionStrategy {
virtual ~DoglegStrategy() {}
// TrustRegionStrategy interface
- virtual Summary ComputeStep(const PerSolveOptions& per_solve_options,
+ Summary ComputeStep(const PerSolveOptions& per_solve_options,
SparseMatrix* jacobian,
const double* residuals,
- double* step);
- virtual void StepAccepted(double step_quality);
- virtual void StepRejected(double step_quality);
- virtual void StepIsInvalid();
-
- virtual double Radius() const;
+ double* step) final;
+ void StepAccepted(double step_quality) final;
+ void StepRejected(double step_quality) final;
+ void StepIsInvalid();
+ double Radius() const final;
// These functions are predominantly for testing.
Vector gradient() const { return gradient_; }
@@ -103,7 +102,7 @@ class DoglegStrategy : public TrustRegionStrategy {
// mu is used to scale the diagonal matrix used to make the
// Gauss-Newton solve full rank. In each solve, the strategy starts
- // out with mu = min_mu, and tries values upto max_mu. If the user
+ // out with mu = min_mu, and tries values up to max_mu. If the user
// reports an invalid step, the value of mu_ is increased so that
// the next solve starts with a stronger regularization.
//
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 fd5d89e350a..1749449043e 100644
--- a/extern/ceres/internal/ceres/dynamic_compressed_row_jacobian_writer.cc
+++ b/extern/ceres/internal/ceres/dynamic_compressed_row_jacobian_writer.cc
@@ -28,9 +28,10 @@
//
// Author: richie.stebbing@gmail.com (Richard Stebbing)
-#include "ceres/compressed_row_jacobian_writer.h"
#include "ceres/dynamic_compressed_row_jacobian_writer.h"
+
#include "ceres/casts.h"
+#include "ceres/compressed_row_jacobian_writer.h"
#include "ceres/dynamic_compressed_row_sparse_matrix.h"
#include "ceres/parameter_block.h"
#include "ceres/program.h"
@@ -48,43 +49,28 @@ DynamicCompressedRowJacobianWriter::CreateEvaluatePreparers(int num_threads) {
}
SparseMatrix* DynamicCompressedRowJacobianWriter::CreateJacobian() const {
- // Initialize `jacobian` with zero number of `max_num_nonzeros`.
- const int num_residuals = program_->NumResiduals();
- const int num_effective_parameters = program_->NumEffectiveParameters();
-
DynamicCompressedRowSparseMatrix* jacobian =
- new DynamicCompressedRowSparseMatrix(num_residuals,
- num_effective_parameters,
- 0);
-
- vector<int>* row_blocks = jacobian->mutable_row_blocks();
- for (int i = 0; i < jacobian->num_rows(); ++i) {
- row_blocks->push_back(1);
- }
-
- vector<int>* col_blocks = jacobian->mutable_col_blocks();
- for (int i = 0; i < jacobian->num_cols(); ++i) {
- col_blocks->push_back(1);
- }
-
+ new DynamicCompressedRowSparseMatrix(program_->NumResiduals(),
+ program_->NumEffectiveParameters(),
+ 0 /* max_num_nonzeros */);
return jacobian;
}
void DynamicCompressedRowJacobianWriter::Write(int residual_id,
int residual_offset,
- double **jacobians,
+ double** jacobians,
SparseMatrix* base_jacobian) {
DynamicCompressedRowSparseMatrix* jacobian =
- down_cast<DynamicCompressedRowSparseMatrix*>(base_jacobian);
+ down_cast<DynamicCompressedRowSparseMatrix*>(base_jacobian);
// Get the `residual_block` of interest.
const ResidualBlock* residual_block =
program_->residual_blocks()[residual_id];
const int num_residuals = residual_block->NumResiduals();
- vector<pair<int, int> > evaluated_jacobian_blocks;
+ vector<pair<int, int>> evaluated_jacobian_blocks;
CompressedRowJacobianWriter::GetOrderedParameterBlocks(
- program_, residual_id, &evaluated_jacobian_blocks);
+ program_, residual_id, &evaluated_jacobian_blocks);
// `residual_offset` is the residual row in the global jacobian.
// Empty the jacobian rows.
@@ -97,16 +83,17 @@ void DynamicCompressedRowJacobianWriter::Write(int residual_id,
const int parameter_block_jacobian_index =
evaluated_jacobian_blocks[i].second;
const int parameter_block_size = parameter_block->LocalSize();
+ const double* parameter_jacobian =
+ jacobians[parameter_block_jacobian_index];
// For each parameter block only insert its non-zero entries.
for (int r = 0; r < num_residuals; ++r) {
- for (int c = 0; c < parameter_block_size; ++c) {
- const double& v = jacobians[parameter_block_jacobian_index][
- r * parameter_block_size + c];
+ for (int c = 0; c < parameter_block_size; ++c, ++parameter_jacobian) {
+ const double v = *parameter_jacobian;
// Only insert non-zero entries.
if (v != 0.0) {
jacobian->InsertEntry(
- residual_offset + r, parameter_block->delta_offset() + c, v);
+ residual_offset + r, parameter_block->delta_offset() + c, v);
}
}
}
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 cab860bddbd..ad41da7b15a 100644
--- a/extern/ceres/internal/ceres/dynamic_compressed_row_sparse_matrix.h
+++ b/extern/ceres/internal/ceres/dynamic_compressed_row_sparse_matrix.h
@@ -91,8 +91,8 @@ class DynamicCompressedRowSparseMatrix : public CompressedRowSparseMatrix {
void Finalize(int num_additional_elements);
private:
- std::vector<std::vector<int> > dynamic_cols_;
- std::vector<std::vector<double> > dynamic_values_;
+ std::vector<std::vector<int>> dynamic_cols_;
+ std::vector<std::vector<double>> dynamic_values_;
};
} // namespace internal
diff --git a/extern/ceres/internal/ceres/dynamic_sparse_normal_cholesky_solver.cc b/extern/ceres/internal/ceres/dynamic_sparse_normal_cholesky_solver.cc
new file mode 100644
index 00000000000..25d5417bca8
--- /dev/null
+++ b/extern/ceres/internal/ceres/dynamic_sparse_normal_cholesky_solver.cc
@@ -0,0 +1,286 @@
+// 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)
+
+#include "ceres/dynamic_sparse_normal_cholesky_solver.h"
+
+#include <algorithm>
+#include <cstring>
+#include <ctime>
+#include <memory>
+#include <sstream>
+
+#include "Eigen/SparseCore"
+#include "ceres/compressed_row_sparse_matrix.h"
+#include "ceres/cxsparse.h"
+#include "ceres/internal/eigen.h"
+#include "ceres/linear_solver.h"
+#include "ceres/suitesparse.h"
+#include "ceres/triplet_sparse_matrix.h"
+#include "ceres/types.h"
+#include "ceres/wall_time.h"
+
+#ifdef CERES_USE_EIGEN_SPARSE
+#include "Eigen/SparseCholesky"
+#endif
+
+namespace ceres {
+namespace internal {
+
+DynamicSparseNormalCholeskySolver::DynamicSparseNormalCholeskySolver(
+ const LinearSolver::Options& options)
+ : options_(options) {}
+
+LinearSolver::Summary DynamicSparseNormalCholeskySolver::SolveImpl(
+ CompressedRowSparseMatrix* A,
+ const double* b,
+ const LinearSolver::PerSolveOptions& per_solve_options,
+ double* x) {
+ const int num_cols = A->num_cols();
+ VectorRef(x, num_cols).setZero();
+ A->LeftMultiply(b, x);
+
+ 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<CompressedRowSparseMatrix> regularizer;
+ if (!A->col_blocks().empty()) {
+ regularizer.reset(CompressedRowSparseMatrix::CreateBlockDiagonalMatrix(
+ per_solve_options.D, A->col_blocks()));
+ } else {
+ regularizer.reset(
+ new CompressedRowSparseMatrix(per_solve_options.D, num_cols));
+ }
+ A->AppendRows(*regularizer);
+ }
+
+ LinearSolver::Summary summary;
+ switch (options_.sparse_linear_algebra_library_type) {
+ case SUITE_SPARSE:
+ summary = SolveImplUsingSuiteSparse(A, x);
+ break;
+ case CX_SPARSE:
+ summary = SolveImplUsingCXSparse(A, x);
+ break;
+ case EIGEN_SPARSE:
+ summary = SolveImplUsingEigen(A, x);
+ break;
+ default:
+ LOG(FATAL) << "Unsupported sparse linear algebra library for "
+ << "dynamic sparsity: "
+ << SparseLinearAlgebraLibraryTypeToString(
+ options_.sparse_linear_algebra_library_type);
+ }
+
+ if (per_solve_options.D != nullptr) {
+ A->DeleteRows(num_cols);
+ }
+
+ return summary;
+}
+
+LinearSolver::Summary DynamicSparseNormalCholeskySolver::SolveImplUsingEigen(
+ CompressedRowSparseMatrix* A, double* rhs_and_solution) {
+#ifndef CERES_USE_EIGEN_SPARSE
+
+ LinearSolver::Summary summary;
+ summary.num_iterations = 0;
+ summary.termination_type = LINEAR_SOLVER_FATAL_ERROR;
+ summary.message =
+ "SPARSE_NORMAL_CHOLESKY cannot be used with EIGEN_SPARSE "
+ "because Ceres was not built with support for "
+ "Eigen's SimplicialLDLT decomposition. "
+ "This requires enabling building with -DEIGENSPARSE=ON.";
+ return summary;
+
+#else
+
+ 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::SparseMatrix<double> lhs = a.transpose() * a;
+ Eigen::SimplicialLDLT<Eigen::SparseMatrix<double>> solver;
+
+ LinearSolver::Summary summary;
+ summary.num_iterations = 1;
+ summary.termination_type = LINEAR_SOLVER_SUCCESS;
+ summary.message = "Success.";
+
+ solver.analyzePattern(lhs);
+ if (VLOG_IS_ON(2)) {
+ std::stringstream ss;
+ solver.dumpMemory(ss);
+ VLOG(2) << "Symbolic Analysis\n" << ss.str();
+ }
+
+ event_logger.AddEvent("Analyze");
+ if (solver.info() != Eigen::Success) {
+ summary.termination_type = LINEAR_SOLVER_FATAL_ERROR;
+ summary.message = "Eigen failure. Unable to find symbolic factorization.";
+ return summary;
+ }
+
+ solver.factorize(lhs);
+ event_logger.AddEvent("Factorize");
+ if (solver.info() != Eigen::Success) {
+ summary.termination_type = LINEAR_SOLVER_FAILURE;
+ summary.message = "Eigen failure. Unable to find numeric factorization.";
+ return summary;
+ }
+
+ const Vector rhs = VectorRef(rhs_and_solution, lhs.cols());
+ VectorRef(rhs_and_solution, lhs.cols()) = solver.solve(rhs);
+ event_logger.AddEvent("Solve");
+ if (solver.info() != Eigen::Success) {
+ summary.termination_type = LINEAR_SOLVER_FAILURE;
+ summary.message = "Eigen failure. Unable to do triangular solve.";
+ return summary;
+ }
+
+ return summary;
+#endif // CERES_USE_EIGEN_SPARSE
+}
+
+LinearSolver::Summary DynamicSparseNormalCholeskySolver::SolveImplUsingCXSparse(
+ CompressedRowSparseMatrix* A, double* rhs_and_solution) {
+#ifdef CERES_NO_CXSPARSE
+
+ LinearSolver::Summary summary;
+ summary.num_iterations = 0;
+ summary.termination_type = LINEAR_SOLVER_FATAL_ERROR;
+ summary.message =
+ "SPARSE_NORMAL_CHOLESKY cannot be used with CX_SPARSE "
+ "because Ceres was not built with support for CXSparse. "
+ "This requires enabling building with -DCXSPARSE=ON.";
+
+ return summary;
+
+#else
+ EventLogger event_logger(
+ "DynamicSparseNormalCholeskySolver::CXSparse::Solve");
+
+ LinearSolver::Summary summary;
+ summary.num_iterations = 1;
+ summary.termination_type = LINEAR_SOLVER_SUCCESS;
+ summary.message = "Success.";
+
+ CXSparse cxsparse;
+
+ // Wrap the augmented Jacobian in a compressed sparse column matrix.
+ cs_di a_transpose = cxsparse.CreateSparseMatrixTransposeView(A);
+
+ // Compute the normal equations. J'J delta = J'f and solve them
+ // using a sparse Cholesky factorization. Notice that when compared
+ // to SuiteSparse we have to explicitly compute the transpose of Jt,
+ // and then the normal equations before they can be
+ // factorized. CHOLMOD/SuiteSparse on the other hand can just work
+ // off of Jt to compute the Cholesky factorization of the normal
+ // equations.
+ cs_di* a = cxsparse.TransposeMatrix(&a_transpose);
+ cs_di* lhs = cxsparse.MatrixMatrixMultiply(&a_transpose, a);
+ cxsparse.Free(a);
+ event_logger.AddEvent("NormalEquations");
+
+ if (!cxsparse.SolveCholesky(lhs, rhs_and_solution)) {
+ summary.termination_type = LINEAR_SOLVER_FAILURE;
+ summary.message = "CXSparse::SolveCholesky failed";
+ }
+ event_logger.AddEvent("Solve");
+
+ cxsparse.Free(lhs);
+ event_logger.AddEvent("TearDown");
+ return summary;
+#endif
+}
+
+LinearSolver::Summary
+DynamicSparseNormalCholeskySolver::SolveImplUsingSuiteSparse(
+ CompressedRowSparseMatrix* A, double* rhs_and_solution) {
+#ifdef CERES_NO_SUITESPARSE
+
+ LinearSolver::Summary summary;
+ summary.num_iterations = 0;
+ summary.termination_type = LINEAR_SOLVER_FATAL_ERROR;
+ summary.message =
+ "SPARSE_NORMAL_CHOLESKY cannot be used with SUITE_SPARSE "
+ "because Ceres was not built with support for SuiteSparse. "
+ "This requires enabling building with -DSUITESPARSE=ON.";
+ return summary;
+
+#else
+
+ EventLogger event_logger(
+ "DynamicSparseNormalCholeskySolver::SuiteSparse::Solve");
+ LinearSolver::Summary summary;
+ summary.termination_type = LINEAR_SOLVER_SUCCESS;
+ summary.num_iterations = 1;
+ summary.message = "Success.";
+
+ SuiteSparse ss;
+ const int num_cols = A->num_cols();
+ cholmod_sparse lhs = ss.CreateSparseMatrixTransposeView(A);
+ event_logger.AddEvent("Setup");
+ cholmod_factor* factor = ss.AnalyzeCholesky(&lhs, &summary.message);
+ event_logger.AddEvent("Analysis");
+
+ if (factor == nullptr) {
+ summary.termination_type = LINEAR_SOLVER_FATAL_ERROR;
+ return summary;
+ }
+
+ summary.termination_type = ss.Cholesky(&lhs, factor, &summary.message);
+ if (summary.termination_type == LINEAR_SOLVER_SUCCESS) {
+ cholmod_dense cholmod_rhs =
+ ss.CreateDenseVectorView(rhs_and_solution, num_cols);
+ cholmod_dense* solution = ss.Solve(factor, &cholmod_rhs, &summary.message);
+ event_logger.AddEvent("Solve");
+ if (solution != nullptr) {
+ memcpy(
+ rhs_and_solution, solution->x, num_cols * sizeof(*rhs_and_solution));
+ ss.Free(solution);
+ } else {
+ summary.termination_type = LINEAR_SOLVER_FAILURE;
+ }
+ }
+
+ ss.Free(factor);
+ event_logger.AddEvent("Teardown");
+ return summary;
+
+#endif
+}
+
+} // namespace internal
+} // namespace ceres
diff --git a/extern/ceres/internal/ceres/dynamic_sparse_normal_cholesky_solver.h b/extern/ceres/internal/ceres/dynamic_sparse_normal_cholesky_solver.h
new file mode 100644
index 00000000000..4e31c7a8492
--- /dev/null
+++ b/extern/ceres/internal/ceres/dynamic_sparse_normal_cholesky_solver.h
@@ -0,0 +1,86 @@
+// 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)
+//
+// A solver for sparse linear least squares problem based on solving
+// the normal equations via a sparse cholesky factorization.
+
+#ifndef CERES_INTERNAL_DYNAMIC_SPARSE_NORMAL_CHOLESKY_SOLVER_H_
+#define CERES_INTERNAL_DYNAMIC_SPARSE_NORMAL_CHOLESKY_SOLVER_H_
+
+// This include must come before any #ifndef check on Ceres compile options.
+#include "ceres/internal/port.h"
+
+#include "ceres/linear_solver.h"
+
+namespace ceres {
+namespace internal {
+
+class CompressedRowSparseMatrix;
+
+// A variant of SparseNormalCholeskySolver in the case where matrix
+// sparsity is not constant across calls to Solve. This means that
+// there is no benefit to symbolically factorizing the matrix and
+// caching this factorization.
+//
+// TODO(alex): Add support for Accelerate sparse solvers:
+// https://github.com/ceres-solver/ceres-solver/issues/397
+class DynamicSparseNormalCholeskySolver
+ : public CompressedRowSparseMatrixSolver {
+ public:
+ explicit DynamicSparseNormalCholeskySolver(
+ const LinearSolver::Options& options);
+ virtual ~DynamicSparseNormalCholeskySolver() {}
+
+ private:
+ LinearSolver::Summary SolveImpl(
+ CompressedRowSparseMatrix* A,
+ const double* b,
+ const LinearSolver::PerSolveOptions& options,
+ double* x) final;
+
+ LinearSolver::Summary SolveImplUsingSuiteSparse(
+ CompressedRowSparseMatrix* A,
+ double* rhs_and_solution);
+
+ LinearSolver::Summary SolveImplUsingCXSparse(
+ CompressedRowSparseMatrix* A,
+ double* rhs_and_solution);
+
+ LinearSolver::Summary SolveImplUsingEigen(
+ CompressedRowSparseMatrix* A,
+ double* rhs_and_solution);
+
+ const LinearSolver::Options options_;
+};
+
+} // namespace internal
+} // namespace ceres
+
+#endif // CERES_INTERNAL_DYNAMIC_SPARSE_NORMAL_CHOLESKY_SOLVER_H_
diff --git a/extern/ceres/internal/ceres/eigensparse.cc b/extern/ceres/internal/ceres/eigensparse.cc
new file mode 100644
index 00000000000..22ed2c43b5d
--- /dev/null
+++ b/extern/ceres/internal/ceres/eigensparse.cc
@@ -0,0 +1,190 @@
+// 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)
+
+#include "ceres/eigensparse.h"
+
+#ifdef CERES_USE_EIGEN_SPARSE
+
+#include <sstream>
+
+#include "Eigen/SparseCholesky"
+#include "Eigen/SparseCore"
+#include "ceres/compressed_row_sparse_matrix.h"
+#include "ceres/linear_solver.h"
+
+namespace ceres {
+namespace internal {
+
+// TODO(sameeragarwal): Use enable_if to clean up the implementations
+// for when Scalar == double.
+template <typename Solver>
+class EigenSparseCholeskyTemplate : public SparseCholesky {
+ public:
+ EigenSparseCholeskyTemplate() : analyzed_(false) {}
+ virtual ~EigenSparseCholeskyTemplate() {}
+ CompressedRowSparseMatrix::StorageType StorageType() const final {
+ return CompressedRowSparseMatrix::LOWER_TRIANGULAR;
+ }
+
+ LinearSolverTerminationType Factorize(
+ const Eigen::SparseMatrix<typename Solver::Scalar>& lhs,
+ std::string* message) {
+ if (!analyzed_) {
+ solver_.analyzePattern(lhs);
+
+ if (VLOG_IS_ON(2)) {
+ std::stringstream ss;
+ solver_.dumpMemory(ss);
+ VLOG(2) << "Symbolic Analysis\n" << ss.str();
+ }
+
+ if (solver_.info() != Eigen::Success) {
+ *message = "Eigen failure. Unable to find symbolic factorization.";
+ return LINEAR_SOLVER_FATAL_ERROR;
+ }
+
+ analyzed_ = true;
+ }
+
+ solver_.factorize(lhs);
+ if (solver_.info() != Eigen::Success) {
+ *message = "Eigen failure. Unable to find numeric factorization.";
+ return LINEAR_SOLVER_FAILURE;
+ }
+ return LINEAR_SOLVER_SUCCESS;
+ }
+
+ LinearSolverTerminationType Solve(const double* rhs_ptr,
+ double* solution_ptr,
+ std::string* message) {
+ CHECK(analyzed_) << "Solve called without a call to Factorize first.";
+
+ scalar_rhs_ = ConstVectorRef(rhs_ptr, solver_.cols())
+ .template cast<typename Solver::Scalar>();
+
+ // The two casts are needed if the Scalar in this class is not
+ // double. For code simplicity we are going to assume that Eigen
+ // is smart enough to figure out that casting a double Vector to a
+ // double Vector is a straight copy. If this turns into a
+ // performance bottleneck (unlikely), we can revisit this.
+ scalar_solution_ = solver_.solve(scalar_rhs_);
+ VectorRef(solution_ptr, solver_.cols()) =
+ scalar_solution_.template cast<double>();
+
+ if (solver_.info() != Eigen::Success) {
+ *message = "Eigen failure. Unable to do triangular solve.";
+ return LINEAR_SOLVER_FAILURE;
+ }
+ return LINEAR_SOLVER_SUCCESS;
+ }
+
+ LinearSolverTerminationType Factorize(CompressedRowSparseMatrix* lhs,
+ std::string* message) final {
+ CHECK_EQ(lhs->storage_type(), StorageType());
+
+ typename Solver::Scalar* values_ptr = NULL;
+ if (std::is_same<typename Solver::Scalar, double>::value) {
+ values_ptr =
+ reinterpret_cast<typename Solver::Scalar*>(lhs->mutable_values());
+ } else {
+ // In the case where the scalar used in this class is not
+ // double. In that case, make a copy of the values array in the
+ // CompressedRowSparseMatrix and cast it to Scalar along the way.
+ values_ = ConstVectorRef(lhs->values(), lhs->num_nonzeros())
+ .cast<typename Solver::Scalar>();
+ values_ptr = values_.data();
+ }
+
+ Eigen::MappedSparseMatrix<typename Solver::Scalar, Eigen::ColMajor>
+ eigen_lhs(lhs->num_rows(),
+ lhs->num_rows(),
+ lhs->num_nonzeros(),
+ lhs->mutable_rows(),
+ lhs->mutable_cols(),
+ values_ptr);
+ return Factorize(eigen_lhs, message);
+ }
+
+ private:
+ Eigen::Matrix<typename Solver::Scalar, Eigen::Dynamic, 1> values_,
+ scalar_rhs_, scalar_solution_;
+ bool analyzed_;
+ 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;
+ if (ordering_type == AMD) {
+ sparse_cholesky.reset(new EigenSparseCholeskyTemplate<WithAMDOrdering>());
+ } else {
+ sparse_cholesky.reset(
+ new EigenSparseCholeskyTemplate<WithNaturalOrdering>());
+ }
+ return sparse_cholesky;
+}
+
+EigenSparseCholesky::~EigenSparseCholesky() {}
+
+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;
+ if (ordering_type == AMD) {
+ sparse_cholesky.reset(new EigenSparseCholeskyTemplate<WithAMDOrdering>());
+ } else {
+ sparse_cholesky.reset(
+ new EigenSparseCholeskyTemplate<WithNaturalOrdering>());
+ }
+ return sparse_cholesky;
+}
+
+FloatEigenSparseCholesky::~FloatEigenSparseCholesky() {}
+
+} // namespace internal
+} // namespace ceres
+
+#endif // CERES_USE_EIGEN_SPARSE
diff --git a/extern/ceres/internal/ceres/eigensparse.h b/extern/ceres/internal/ceres/eigensparse.h
new file mode 100644
index 00000000000..2e6c6f01abb
--- /dev/null
+++ b/extern/ceres/internal/ceres/eigensparse.h
@@ -0,0 +1,90 @@
+// 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)
+//
+// A simple C++ interface to the Eigen's Sparse Cholesky routines.
+
+#ifndef CERES_INTERNAL_EIGENSPARSE_H_
+#define CERES_INTERNAL_EIGENSPARSE_H_
+
+// This include must come before any #ifndef check on Ceres compile options.
+#include "ceres/internal/port.h"
+
+#ifdef CERES_USE_EIGEN_SPARSE
+
+#include <memory>
+#include <string>
+
+#include "Eigen/SparseCore"
+#include "ceres/linear_solver.h"
+#include "ceres/sparse_cholesky.h"
+
+namespace ceres {
+namespace internal {
+
+class 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;
+};
+
+// Even though the input is double precision linear system, this class
+// solves it by computing a single precision Cholesky factorization.
+class 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;
+};
+
+} // namespace internal
+} // namespace ceres
+
+#endif // CERES_USE_EIGEN_SPARSE
+
+#endif // CERES_INTERNAL_EIGENSPARSE_H_
diff --git a/extern/ceres/internal/ceres/evaluator.cc b/extern/ceres/internal/ceres/evaluator.cc
index baba9afa11b..8387983553d 100644
--- a/extern/ceres/internal/ceres/evaluator.cc
+++ b/extern/ceres/internal/ceres/evaluator.cc
@@ -51,6 +51,8 @@ Evaluator::~Evaluator() {}
Evaluator* Evaluator::Create(const Evaluator::Options& options,
Program* program,
std::string* error) {
+ CHECK(options.context != NULL);
+
switch (options.linear_solver_type) {
case DENSE_QR:
case DENSE_NORMAL_CHOLESKY:
@@ -71,9 +73,9 @@ Evaluator* Evaluator::Create(const Evaluator::Options& options,
DynamicCompressedRowJacobianFinalizer>(
options, program);
} else {
- return new ProgramEvaluator<ScratchEvaluatePreparer,
- CompressedRowJacobianWriter>(options,
- program);
+ return new ProgramEvaluator<BlockEvaluatePreparer,
+ BlockJacobianWriter>(options,
+ program);
}
default:
diff --git a/extern/ceres/internal/ceres/evaluator.h b/extern/ceres/internal/ceres/evaluator.h
index fea307919d0..b820958ed77 100644
--- a/extern/ceres/internal/ceres/evaluator.h
+++ b/extern/ceres/internal/ceres/evaluator.h
@@ -36,6 +36,7 @@
#include <string>
#include <vector>
+#include "ceres/context_impl.h"
#include "ceres/execution_summary.h"
#include "ceres/internal/port.h"
#include "ceres/types.h"
@@ -43,6 +44,7 @@
namespace ceres {
struct CRSMatrix;
+class EvaluationCallback;
namespace internal {
@@ -58,47 +60,18 @@ class Evaluator {
virtual ~Evaluator();
struct Options {
- Options()
- : num_threads(1),
- num_eliminate_blocks(-1),
- linear_solver_type(DENSE_QR),
- dynamic_sparsity(false) {}
-
- int num_threads;
- int num_eliminate_blocks;
- LinearSolverType linear_solver_type;
- bool dynamic_sparsity;
+ int num_threads = 1;
+ int num_eliminate_blocks = -1;
+ LinearSolverType linear_solver_type = DENSE_QR;
+ bool dynamic_sparsity = false;
+ ContextImpl* context = nullptr;
+ EvaluationCallback* evaluation_callback = nullptr;
};
static Evaluator* Create(const Options& options,
Program* program,
std::string* error);
- // This is used for computing the cost, residual and Jacobian for
- // returning to the user. For actually solving the optimization
- // problem, the optimization algorithm uses the ProgramEvaluator
- // objects directly.
- //
- // The residual, gradients and jacobian pointers can be NULL, in
- // which case they will not be evaluated. cost cannot be NULL.
- //
- // The parallelism of the evaluator is controlled by num_threads; it
- // should be at least 1.
- //
- // Note: That this function does not take a parameter vector as
- // input. The parameter blocks are evaluated on the values contained
- // in the arrays pointed to by their user_state pointers.
- //
- // Also worth noting is that this function mutates program by
- // calling Program::SetParameterOffsetsAndIndex() on it so that an
- // evaluator object can be constructed.
- static bool Evaluate(Program* program,
- int num_threads,
- double* cost,
- std::vector<double>* residuals,
- std::vector<double>* gradient,
- CRSMatrix* jacobian);
-
// Build and return a sparse matrix for storing and working with the Jacobian
// of the objective function. The jacobian has dimensions
// NumEffectiveParameters() by NumParameters(), and is typically extremely
@@ -117,16 +90,14 @@ class Evaluator {
// Schur complement based methods.
virtual SparseMatrix* CreateJacobian() const = 0;
-
// Options struct to control Evaluator::Evaluate;
struct EvaluateOptions {
- EvaluateOptions()
- : apply_loss_function(true) {
- }
-
// If false, the loss function correction is not applied to the
// residual blocks.
- bool apply_loss_function;
+ bool apply_loss_function = true;
+
+ // If false, this evaluation point is the same as the last one.
+ bool new_evaluation_point = true;
};
// Evaluate the cost function for the given state. Returns the cost,
@@ -190,12 +161,8 @@ class Evaluator {
// that the base class implementation does not have to worry about
// life time issues. Further, these calls are not expected to be
// frequent or performance sensitive.
- virtual std::map<std::string, int> CallStatistics() const {
- return std::map<std::string, int>();
- }
-
- virtual std::map<std::string, double> TimeStatistics() const {
- return std::map<std::string, double>();
+ virtual std::map<std::string, CallStatistics> Statistics() const {
+ return std::map<std::string, CallStatistics>();
}
};
diff --git a/extern/ceres/internal/ceres/execution_summary.h b/extern/ceres/internal/ceres/execution_summary.h
index aa9929d8974..17fd882af03 100644
--- a/extern/ceres/internal/ceres/execution_summary.h
+++ b/extern/ceres/internal/ceres/execution_summary.h
@@ -32,47 +32,45 @@
#define CERES_INTERNAL_EXECUTION_SUMMARY_H_
#include <map>
+#include <mutex>
#include <string>
#include "ceres/internal/port.h"
#include "ceres/wall_time.h"
-#include "ceres/mutex.h"
namespace ceres {
namespace internal {
-// Struct used by various objects to report statistics and other
-// information about their execution. e.g., ExecutionSummary::times
-// can be used for reporting times associated with various activities.
+struct CallStatistics {
+ CallStatistics() : time(0.), calls(0) {}
+ double time;
+ int calls;
+};
+
+// Struct used by various objects to report statistics about their
+// execution.
class ExecutionSummary {
public:
void IncrementTimeBy(const std::string& name, const double value) {
- CeresMutexLock l(&times_mutex_);
- times_[name] += value;
+ std::lock_guard<std::mutex> l(mutex_);
+ CallStatistics& call_stats = statistics_[name];
+ call_stats.time += value;
+ ++call_stats.calls;
}
- void IncrementCall(const std::string& name) {
- CeresMutexLock l(&calls_mutex_);
- calls_[name] += 1;
+ const std::map<std::string, CallStatistics>& statistics() const {
+ return statistics_;
}
- const std::map<std::string, double>& times() const { return times_; }
- const std::map<std::string, int>& calls() const { return calls_; }
-
private:
- Mutex times_mutex_;
- std::map<std::string, double> times_;
-
- Mutex calls_mutex_;
- std::map<std::string, int> calls_;
+ std::mutex mutex_;
+ std::map<std::string, CallStatistics> statistics_;
};
class ScopedExecutionTimer {
public:
ScopedExecutionTimer(const std::string& name, ExecutionSummary* summary)
- : start_time_(WallTimeInSeconds()),
- name_(name),
- summary_(summary) {}
+ : start_time_(WallTimeInSeconds()), name_(name), summary_(summary) {}
~ScopedExecutionTimer() {
summary_->IncrementTimeBy(name_, WallTimeInSeconds() - start_time_);
diff --git a/extern/ceres/internal/ceres/float_cxsparse.cc b/extern/ceres/internal/ceres/float_cxsparse.cc
new file mode 100644
index 00000000000..6c688303444
--- /dev/null
+++ b/extern/ceres/internal/ceres/float_cxsparse.cc
@@ -0,0 +1,47 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2018 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/float_cxsparse.h"
+
+#if !defined(CERES_NO_CXSPARSE)
+
+namespace ceres {
+namespace internal {
+
+std::unique_ptr<SparseCholesky> FloatCXSparseCholesky::Create(
+ OrderingType ordering_type) {
+ LOG(FATAL) << "FloatCXSparseCholesky is not available.";
+ return std::unique_ptr<SparseCholesky>();
+}
+
+} // namespace internal
+} // namespace ceres
+
+#endif // !defined(CERES_NO_CXSPARSE)
diff --git a/extern/ceres/internal/ceres/float_cxsparse.h b/extern/ceres/internal/ceres/float_cxsparse.h
new file mode 100644
index 00000000000..57fc5e4c010
--- /dev/null
+++ b/extern/ceres/internal/ceres/float_cxsparse.h
@@ -0,0 +1,58 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2018 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_FLOAT_CXSPARSE_H_
+#define CERES_INTERNAL_FLOAT_CXSPARSE_H_
+
+// This include must come before any #ifndef check on Ceres compile options.
+#include "ceres/internal/port.h"
+
+#if !defined(CERES_NO_CXSPARSE)
+
+#include <memory>
+#include "ceres/sparse_cholesky.h"
+
+namespace ceres {
+namespace internal {
+
+// Fake implementation of a single precision Sparse Cholesky using
+// CXSparse.
+class FloatCXSparseCholesky : public SparseCholesky {
+ public:
+ static std::unique_ptr<SparseCholesky> Create(
+ OrderingType ordering_type);
+};
+
+} // namespace internal
+} // namespace ceres
+
+#endif // !defined(CERES_NO_CXSPARSE)
+
+#endif // CERES_INTERNAL_FLOAT_CXSPARSE_H_
diff --git a/extern/ceres/internal/ceres/float_suitesparse.cc b/extern/ceres/internal/ceres/float_suitesparse.cc
new file mode 100644
index 00000000000..03604572b5c
--- /dev/null
+++ b/extern/ceres/internal/ceres/float_suitesparse.cc
@@ -0,0 +1,47 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2018 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/float_suitesparse.h"
+
+#if !defined(CERES_NO_SUITESPARSE)
+
+namespace ceres {
+namespace internal {
+
+std::unique_ptr<SparseCholesky> FloatSuiteSparseCholesky::Create(
+ OrderingType ordering_type) {
+ LOG(FATAL) << "FloatSuiteSparseCholesky is not available.";
+ return std::unique_ptr<SparseCholesky>();
+}
+
+} // namespace internal
+} // namespace ceres
+
+#endif // !defined(CERES_NO_SUITESPARSE)
diff --git a/extern/ceres/internal/ceres/float_suitesparse.h b/extern/ceres/internal/ceres/float_suitesparse.h
new file mode 100644
index 00000000000..ac4d4090922
--- /dev/null
+++ b/extern/ceres/internal/ceres/float_suitesparse.h
@@ -0,0 +1,58 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2018 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_FLOAT_SUITESPARSE_H_
+#define CERES_INTERNAL_FLOAT_SUITESPARSE_H_
+
+// This include must come before any #ifndef check on Ceres compile options.
+#include "ceres/internal/port.h"
+
+#include <memory>
+#include "ceres/sparse_cholesky.h"
+
+#if !defined(CERES_NO_SUITESPARSE)
+
+namespace ceres {
+namespace internal {
+
+// Fake implementation of a single precision Sparse Cholesky using
+// SuiteSparse.
+class FloatSuiteSparseCholesky : public SparseCholesky {
+ public:
+ static std::unique_ptr<SparseCholesky> Create(
+ OrderingType ordering_type);
+};
+
+} // namespace internal
+} // namespace ceres
+
+#endif // !defined(CERES_NO_SUITESPARSE)
+
+#endif // CERES_INTERNAL_FLOAT_SUITESPARSE_H_
diff --git a/extern/ceres/internal/ceres/integral_types.h b/extern/ceres/internal/ceres/function_sample.cc
index 98a746f13ff..2fd3dbdf7c5 100644
--- a/extern/ceres/internal/ceres/integral_types.h
+++ b/extern/ceres/internal/ceres/function_sample.cc
@@ -26,66 +26,48 @@
// 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)
-//
-// Portable typedefs for various fixed-size integers. Uses template
-// metaprogramming instead of fragile compiler defines.
+// Author: sameeragarwal@google.com (Sameer Agarwal)
-#ifndef CERES_INTERNAL_INTEGRAL_TYPES_H_
-#define CERES_INTERNAL_INTEGRAL_TYPES_H_
+#include "ceres/function_sample.h"
+#include "ceres/stringprintf.h"
namespace ceres {
namespace internal {
-// Compile time ternary on types.
-template<bool kCondition, typename kTrueType, typename kFalseType>
-struct Ternary {
- typedef kTrueType type;
-};
-template<typename kTrueType, typename kFalseType>
-struct Ternary<false, kTrueType, kFalseType> {
- typedef kFalseType type;
-};
-
-#define CERES_INTSIZE(TYPE) \
- typename Ternary<sizeof(TYPE) * 8 == kBits, TYPE,
-
-template<int kBits>
-struct Integer {
- typedef
- CERES_INTSIZE(char)
- CERES_INTSIZE(short)
- CERES_INTSIZE(int)
- CERES_INTSIZE(long int)
- CERES_INTSIZE(long long)
- void>::type >::type >::type >::type >::type
- type;
-};
+FunctionSample::FunctionSample()
+ : x(0.0),
+ vector_x_is_valid(false),
+ value(0.0),
+ value_is_valid(false),
+ vector_gradient_is_valid(false),
+ gradient(0.0),
+ gradient_is_valid(false) {}
-template<int kBits>
-struct UnsignedInteger {
- typedef
- CERES_INTSIZE(unsigned char)
- CERES_INTSIZE(unsigned short)
- CERES_INTSIZE(unsigned int)
- CERES_INTSIZE(unsigned long int)
- CERES_INTSIZE(unsigned long long)
- void>::type >::type >::type >::type >::type
- type;
-};
+FunctionSample::FunctionSample(const double x, const double value)
+ : x(x),
+ vector_x_is_valid(false),
+ value(value),
+ value_is_valid(true),
+ vector_gradient_is_valid(false),
+ gradient(0.0),
+ gradient_is_valid(false) {}
-#undef CERES_INTSIZE
+FunctionSample::FunctionSample(const double x,
+ const double value,
+ const double gradient)
+ : x(x),
+ vector_x_is_valid(false),
+ value(value),
+ value_is_valid(true),
+ vector_gradient_is_valid(false),
+ gradient(gradient),
+ gradient_is_valid(true) {}
-typedef Integer< 8>::type int8;
-typedef Integer<32>::type int32;
-typedef Integer<64>::type int64;
-
-typedef UnsignedInteger< 8>::type uint8;
-typedef UnsignedInteger<16>::type uint16;
-typedef UnsignedInteger<32>::type uint32;
-typedef UnsignedInteger<64>::type uint64;
+std::string FunctionSample::ToDebugString() const {
+ return StringPrintf("[x: %.8e, value: %.8e, gradient: %.8e, "
+ "value_is_valid: %d, gradient_is_valid: %d]",
+ x, value, gradient, value_is_valid, gradient_is_valid);
+}
} // namespace internal
} // namespace ceres
-
-#endif // CERES_INTERNAL_INTEGRAL_TYPES_H_
diff --git a/extern/ceres/internal/ceres/function_sample.h b/extern/ceres/internal/ceres/function_sample.h
new file mode 100644
index 00000000000..df79aef944f
--- /dev/null
+++ b/extern/ceres/internal/ceres/function_sample.h
@@ -0,0 +1,94 @@
+// 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)
+
+#ifndef CERES_INTERNAL_FUNCTION_SAMPLE_H_
+#define CERES_INTERNAL_FUNCTION_SAMPLE_H_
+
+#include <string>
+#include "ceres/internal/eigen.h"
+
+namespace ceres {
+namespace internal {
+
+// FunctionSample is used by the line search routines to store and
+// communicate the value and (optionally) the gradient of the function
+// being minimized.
+//
+// Since line search as the name implies happens along a certain
+// line/direction. FunctionSample contains the information in two
+// ways. Information in the ambient space and information along the
+// direction of search.
+struct FunctionSample {
+ FunctionSample();
+ FunctionSample(double x, double value);
+ FunctionSample(double x, double value, double gradient);
+
+ std::string ToDebugString() const;
+
+ // x is the location of the sample along the search direction.
+ double x;
+
+ // Let p be a point and d be the search direction then
+ //
+ // vector_x = p + x * d;
+ Vector vector_x;
+ // True if vector_x has been assigned a valid value.
+ bool vector_x_is_valid;
+
+ // value = f(vector_x)
+ double value;
+ // True of the evaluation was successful and value is a finite
+ // number.
+ bool value_is_valid;
+
+ // vector_gradient = Df(vector_position);
+ //
+ // D is the derivative operator.
+ Vector vector_gradient;
+ // True if the vector gradient was evaluated and the evaluation was
+ // successful (the value is a finite number).
+ bool vector_gradient_is_valid;
+
+ // gradient = d.transpose() * vector_gradient
+ //
+ // where d is the search direction.
+ double gradient;
+ // True if the evaluation of the gradient was sucessful and the
+ // value is a finite number.
+ bool gradient_is_valid;
+};
+
+
+
+
+} // namespace internal
+} // namespace ceres
+
+#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
new file mode 100644
index 00000000000..5e91f8d2b6a
--- /dev/null
+++ b/extern/ceres/internal/ceres/generate_template_specializations.py
@@ -0,0 +1,246 @@
+# 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 500115b9897..86ad17b4f71 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
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
//=========================================
//
-// This file is generated using generate_partitioned_matrix_view_specializations.py.
-// Editing it manually is not recommended.
+// 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"
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 1384cb619e3..33018d573a4 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
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
//=========================================
//
-// This file is generated using generate_partitioned_matrix_view_specializations.py.
-// Editing it manually is not recommended.
+// 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"
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 030035ec97b..a429a546e3d 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
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
//=========================================
//
-// This file is generated using generate_partitioned_matrix_view_specializations.py.
-// Editing it manually is not recommended.
+// 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"
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 c9501b50170..f6f03ea6dcc 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
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
//=========================================
//
-// This file is generated using generate_partitioned_matrix_view_specializations.py.
-// Editing it manually is not recommended.
+// 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"
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 c2639bff69e..0b73e1a2aa8 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
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
//=========================================
//
-// This file is generated using generate_partitioned_matrix_view_specializations.py.
-// Editing it manually is not recommended.
+// 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"
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 693e43959c1..bc4a86194eb 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
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
//=========================================
//
-// This file is generated using generate_partitioned_matrix_view_specializations.py.
-// Editing it manually is not recommended.
+// 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"
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 7b9368ffefd..fe8f7dd37af 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
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
//=========================================
//
-// This file is generated using generate_partitioned_matrix_view_specializations.py.
-// Editing it manually is not recommended.
+// 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"
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 e72c5f6937a..ac493fcd0c0 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
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
//=========================================
//
-// This file is generated using generate_partitioned_matrix_view_specializations.py.
-// Editing it manually is not recommended.
+// 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"
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 c1f410eb64c..e29efaf4832 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
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
//=========================================
//
-// This file is generated using generate_partitioned_matrix_view_specializations.py.
-// Editing it manually is not recommended.
+// 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"
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 7292c333d5d..e61e0a31314 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
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
//=========================================
//
-// This file is generated using generate_partitioned_matrix_view_specializations.py.
-// Editing it manually is not recommended.
+// 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"
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 891d65a8646..2e1170da01f 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
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
//=========================================
//
-// This file is generated using generate_partitioned_matrix_view_specializations.py.
-// Editing it manually is not recommended.
+// 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"
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
new file mode 100644
index 00000000000..4a5590d9751
--- /dev/null
+++ b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_6.cc
@@ -0,0 +1,58 @@
+// 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.
+
+// 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"
+#include "ceres/internal/eigen.h"
+
+namespace ceres {
+namespace internal {
+
+template class PartitionedMatrixView<2, 4, 6>;
+
+} // namespace internal
+} // namespace ceres
+
+#endif // 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 395f6bd4c13..83015f1ecc5 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
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
//=========================================
//
-// This file is generated using generate_partitioned_matrix_view_specializations.py.
-// Editing it manually is not recommended.
+// 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"
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 88952b10e34..25671f913dd 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
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
//=========================================
//
-// This file is generated using generate_partitioned_matrix_view_specializations.py.
-// Editing it manually is not recommended.
+// 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"
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 7733e1993eb..d259802bd5a 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
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
//=========================================
//
-// This file is generated using generate_partitioned_matrix_view_specializations.py.
-// Editing it manually is not recommended.
+// 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"
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 117a0cdb8c1..c9567595acd 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
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
//=========================================
//
-// This file is generated using generate_partitioned_matrix_view_specializations.py.
-// Editing it manually is not recommended.
+// 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"
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
new file mode 100644
index 00000000000..d3b20be70e7
--- /dev/null
+++ b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_3_3_3.cc
@@ -0,0 +1,58 @@
+// 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.
+
+// 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"
+#include "ceres/internal/eigen.h"
+
+namespace ceres {
+namespace internal {
+
+template class PartitionedMatrixView<3, 3, 3>;
+
+} // namespace internal
+} // namespace ceres
+
+#endif // 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 a620bb70dba..f08049c9653 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
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
//=========================================
//
-// This file is generated using generate_partitioned_matrix_view_specializations.py.
-// Editing it manually is not recommended.
+// 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"
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 2978630832c..9342612022f 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
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
//=========================================
//
-// This file is generated using generate_partitioned_matrix_view_specializations.py.
-// Editing it manually is not recommended.
+// 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"
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 bcd03b02e3a..8b273fa0da0 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
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
//=========================================
//
-// This file is generated using generate_partitioned_matrix_view_specializations.py.
-// Editing it manually is not recommended.
+// 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"
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 6b541ecf0d9..e8b45e49eca 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
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
//=========================================
//
-// This file is generated using generate_partitioned_matrix_view_specializations.py.
-// Editing it manually is not recommended.
+// 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"
diff --git a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_d_d_d.cc b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_d_d_d.cc
index 85111e722c4..3545b869d5f 100644
--- a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_d_d_d.cc
+++ b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_d_d_d.cc
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
//=========================================
//
-// This file is generated using generate_partitioned_matrix_view_specializations.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
#include "ceres/partitioned_matrix_view_impl.h"
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 ac07a3f229e..79fcf437981 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
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
//=========================================
//
-// This file is generated using generate_eliminator_specialization.py.
-// Editing it manually is not recommended.
+// 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"
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 0ec09553f9e..edd7fb649b4 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
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
//=========================================
//
-// This file is generated using generate_eliminator_specialization.py.
-// Editing it manually is not recommended.
+// 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"
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 74a42cc4a16..692267dba46 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
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
//=========================================
//
-// This file is generated using generate_eliminator_specialization.py.
-// Editing it manually is not recommended.
+// 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"
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 5ce757fda5d..33d9c6de270 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
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
//=========================================
//
-// This file is generated using generate_eliminator_specialization.py.
-// Editing it manually is not recommended.
+// 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"
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 2e7ae28b4ea..4a5e2fe30c0 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
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
//=========================================
//
-// This file is generated using generate_eliminator_specialization.py.
-// Editing it manually is not recommended.
+// 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"
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 443207070cf..7ee63d069aa 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
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
//=========================================
//
-// This file is generated using generate_eliminator_specialization.py.
-// Editing it manually is not recommended.
+// 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"
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 ac2f358b383..108760ef1f8 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
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
//=========================================
//
-// This file is generated using generate_eliminator_specialization.py.
-// Editing it manually is not recommended.
+// 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"
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 930ab440fa5..4fea2fa4417 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
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
//=========================================
//
-// This file is generated using generate_eliminator_specialization.py.
-// Editing it manually is not recommended.
+// 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"
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 486c53d36f4..0d13c99e7ca 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
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
//=========================================
//
-// This file is generated using generate_eliminator_specialization.py.
-// Editing it manually is not recommended.
+// 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"
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 6f247a7b832..3827c653a63 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
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
//=========================================
//
-// This file is generated using generate_eliminator_specialization.py.
-// Editing it manually is not recommended.
+// 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"
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 c44cd045263..47bdfab1f22 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
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
//=========================================
//
-// This file is generated using generate_eliminator_specialization.py.
-// Editing it manually is not recommended.
+// 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"
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
new file mode 100644
index 00000000000..3777be22707
--- /dev/null
+++ b/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_6.cc
@@ -0,0 +1,58 @@
+// 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.
+
+// 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"
+#include "ceres/internal/eigen.h"
+
+namespace ceres {
+namespace internal {
+
+template class SchurEliminator<2, 4, 6>;
+
+} // namespace internal
+} // namespace ceres
+
+#endif // 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 c9a0d5fc729..862c76a2a9c 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
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
//=========================================
//
-// This file is generated using generate_eliminator_specialization.py.
-// Editing it manually is not recommended.
+// 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"
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 b0455b0bca0..5b5b7ccd415 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
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
//=========================================
//
-// This file is generated using generate_eliminator_specialization.py.
-// Editing it manually is not recommended.
+// 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"
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 3234380f23c..ce2d450b073 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
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
//=========================================
//
-// This file is generated using generate_eliminator_specialization.py.
-// Editing it manually is not recommended.
+// 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"
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 311f8556932..9b02bd9db5a 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
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
//=========================================
//
-// This file is generated using generate_eliminator_specialization.py.
-// Editing it manually is not recommended.
+// 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"
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
new file mode 100644
index 00000000000..1cbeadf518f
--- /dev/null
+++ b/extern/ceres/internal/ceres/generated/schur_eliminator_3_3_3.cc
@@ -0,0 +1,58 @@
+// 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.
+
+// 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"
+#include "ceres/internal/eigen.h"
+
+namespace ceres {
+namespace internal {
+
+template class SchurEliminator<3, 3, 3>;
+
+} // namespace internal
+} // namespace ceres
+
+#endif // 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 bc40bd55296..10f709d7577 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
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
//=========================================
//
-// This file is generated using generate_eliminator_specialization.py.
-// Editing it manually is not recommended.
+// 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"
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 cca88c802b0..bcbcc745519 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
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
//=========================================
//
-// This file is generated using generate_eliminator_specialization.py.
-// Editing it manually is not recommended.
+// 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"
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 33c94a907b9..44ecc87deba 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
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
//=========================================
//
-// This file is generated using generate_eliminator_specialization.py.
-// Editing it manually is not recommended.
+// 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"
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 1a1866f93a8..69c856304f0 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
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
//=========================================
//
-// This file is generated using generate_eliminator_specialization.py.
-// Editing it manually is not recommended.
+// 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"
diff --git a/extern/ceres/internal/ceres/generated/schur_eliminator_d_d_d.cc b/extern/ceres/internal/ceres/generated/schur_eliminator_d_d_d.cc
index 6b18ef8c863..348708bb335 100644
--- a/extern/ceres/internal/ceres/generated/schur_eliminator_d_d_d.cc
+++ b/extern/ceres/internal/ceres/generated/schur_eliminator_d_d_d.cc
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
//=========================================
//
-// This file is generated using generate_eliminator_specialization.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
#include "ceres/schur_eliminator_impl.h"
diff --git a/extern/ceres/internal/ceres/gradient_checker.cc b/extern/ceres/internal/ceres/gradient_checker.cc
index c16c141db09..ef56666970d 100644
--- a/extern/ceres/internal/ceres/gradient_checker.cc
+++ b/extern/ceres/internal/ceres/gradient_checker.cc
@@ -34,6 +34,7 @@
#include <algorithm>
#include <cmath>
+#include <cstdint>
#include <numeric>
#include <string>
#include <vector>
@@ -61,11 +62,11 @@ bool EvaluateCostFunction(
Vector* residuals,
std::vector<Matrix>* jacobians,
std::vector<Matrix>* local_jacobians) {
- CHECK_NOTNULL(residuals);
- CHECK_NOTNULL(jacobians);
- CHECK_NOTNULL(local_jacobians);
+ CHECK(residuals != nullptr);
+ CHECK(jacobians != nullptr);
+ CHECK(local_jacobians != nullptr);
- const vector<int32>& block_sizes = function->parameter_block_sizes();
+ const vector<int32_t>& block_sizes = function->parameter_block_sizes();
const int num_parameter_blocks = block_sizes.size();
// Allocate Jacobian matrices in local space.
@@ -110,7 +111,7 @@ bool EvaluateCostFunction(
Matrix global_J_local(global_size, local_size);
local_parameterizations.at(i)->ComputeJacobian(
parameters[i], global_J_local.data());
- local_jacobians->at(i) = jacobians->at(i) * global_J_local;
+ local_jacobians->at(i).noalias() = jacobians->at(i) * global_J_local;
}
}
return true;
@@ -122,20 +123,20 @@ GradientChecker::GradientChecker(
const vector<const LocalParameterization*>* local_parameterizations,
const NumericDiffOptions& options) :
function_(function) {
- CHECK_NOTNULL(function);
+ CHECK(function != nullptr);
if (local_parameterizations != NULL) {
local_parameterizations_ = *local_parameterizations;
} else {
local_parameterizations_.resize(function->parameter_block_sizes().size(),
NULL);
}
- DynamicNumericDiffCostFunction<CostFunction, CENTRAL>*
+ DynamicNumericDiffCostFunction<CostFunction, RIDDERS>*
finite_diff_cost_function =
- new DynamicNumericDiffCostFunction<CostFunction, CENTRAL>(
+ new DynamicNumericDiffCostFunction<CostFunction, RIDDERS>(
function, DO_NOT_TAKE_OWNERSHIP, options);
finite_diff_cost_function_.reset(finite_diff_cost_function);
- const vector<int32>& parameter_block_sizes =
+ const vector<int32_t>& parameter_block_sizes =
function->parameter_block_sizes();
const int num_parameter_blocks = parameter_block_sizes.size();
for (int i = 0; i < num_parameter_blocks; ++i) {
diff --git a/extern/ceres/internal/ceres/gradient_checking_cost_function.cc b/extern/ceres/internal/ceres/gradient_checking_cost_function.cc
index f2c73367891..13d6c58a6b7 100644
--- a/extern/ceres/internal/ceres/gradient_checking_cost_function.cc
+++ b/extern/ceres/internal/ceres/gradient_checking_cost_function.cc
@@ -33,13 +33,13 @@
#include <algorithm>
#include <cmath>
+#include <cstdint>
#include <numeric>
#include <string>
#include <vector>
#include "ceres/gradient_checker.h"
#include "ceres/internal/eigen.h"
-#include "ceres/internal/scoped_ptr.h"
#include "ceres/parameter_block.h"
#include "ceres/problem.h"
#include "ceres/problem_impl.h"
@@ -74,8 +74,8 @@ class GradientCheckingCostFunction : public CostFunction {
relative_precision_(relative_precision),
extra_info_(extra_info),
callback_(callback) {
- CHECK_NOTNULL(callback_);
- const vector<int32>& parameter_block_sizes =
+ CHECK(callback_ != nullptr);
+ const vector<int32_t>& parameter_block_sizes =
function->parameter_block_sizes();
*mutable_parameter_block_sizes() = parameter_block_sizes;
set_num_residuals(function->num_residuals());
@@ -83,9 +83,9 @@ class GradientCheckingCostFunction : public CostFunction {
virtual ~GradientCheckingCostFunction() { }
- virtual bool Evaluate(double const* const* parameters,
- double* residuals,
- double** jacobians) const {
+ 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);
@@ -107,7 +107,7 @@ class GradientCheckingCostFunction : public CostFunction {
MatrixRef(residuals, num_residuals, 1) = results.residuals;
// Copy the original jacobian blocks into the jacobians array.
- const vector<int32>& block_sizes = function_->parameter_block_sizes();
+ const vector<int32_t>& block_sizes = function_->parameter_block_sizes();
for (int k = 0; k < block_sizes.size(); k++) {
if (jacobians[k] != NULL) {
MatrixRef(jacobians[k],
@@ -148,10 +148,9 @@ CallbackReturnType GradientCheckingIterationCallback::operator()(
}
void GradientCheckingIterationCallback::SetGradientErrorDetected(
std::string& error_log) {
- mutex_.Lock();
+ std::lock_guard<std::mutex> l(mutex_);
gradient_error_detected_ = true;
error_log_ += "\n" + error_log;
- mutex_.Unlock();
}
CostFunction* CreateGradientCheckingCostFunction(
@@ -176,7 +175,7 @@ ProblemImpl* CreateGradientCheckingProblemImpl(
double relative_step_size,
double relative_precision,
GradientCheckingIterationCallback* callback) {
- CHECK_NOTNULL(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
@@ -189,6 +188,7 @@ ProblemImpl* CreateGradientCheckingProblemImpl(
DO_NOT_TAKE_OWNERSHIP;
gradient_checking_problem_options.local_parameterization_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;
@@ -212,6 +212,17 @@ ProblemImpl* CreateGradientCheckingProblemImpl(
gradient_checking_problem_impl->SetParameterBlockConstant(
parameter_block->mutable_user_state());
}
+
+ for (int i = 0; i < parameter_block->Size(); ++i) {
+ gradient_checking_problem_impl->SetParameterUpperBound(
+ parameter_block->mutable_user_state(),
+ i,
+ parameter_block->UpperBound(i));
+ gradient_checking_problem_impl->SetParameterLowerBound(
+ parameter_block->mutable_user_state(),
+ i,
+ parameter_block->LowerBound(i));
+ }
}
// For every ResidualBlock in problem_impl, create a new
@@ -255,7 +266,8 @@ ProblemImpl* CreateGradientCheckingProblemImpl(
gradient_checking_problem_impl->AddResidualBlock(
gradient_checking_cost_function,
const_cast<LossFunction*>(residual_block->loss_function()),
- parameter_blocks);
+ parameter_blocks.data(),
+ static_cast<int>(parameter_blocks.size()));
}
// Normally, when a problem is given to the solver, we guarantee
diff --git a/extern/ceres/internal/ceres/gradient_checking_cost_function.h b/extern/ceres/internal/ceres/gradient_checking_cost_function.h
index 497f8e2a594..e9a34f7eb9f 100644
--- a/extern/ceres/internal/ceres/gradient_checking_cost_function.h
+++ b/extern/ceres/internal/ceres/gradient_checking_cost_function.h
@@ -32,12 +32,12 @@
#ifndef CERES_INTERNAL_GRADIENT_CHECKING_COST_FUNCTION_H_
#define CERES_INTERNAL_GRADIENT_CHECKING_COST_FUNCTION_H_
+#include <mutex>
#include <string>
#include "ceres/cost_function.h"
#include "ceres/iteration_callback.h"
#include "ceres/local_parameterization.h"
-#include "ceres/mutex.h"
namespace ceres {
namespace internal {
@@ -52,7 +52,7 @@ class GradientCheckingIterationCallback : public IterationCallback {
// Will return SOLVER_CONTINUE until a gradient error has been detected,
// then return SOLVER_ABORT.
- virtual CallbackReturnType operator()(const IterationSummary& summary);
+ CallbackReturnType operator()(const IterationSummary& summary) final;
// Notify this that a gradient error has occurred (thread safe).
void SetGradientErrorDetected(std::string& error_log);
@@ -63,8 +63,7 @@ class GradientCheckingIterationCallback : public IterationCallback {
private:
bool gradient_error_detected_;
std::string error_log_;
- // Mutex protecting member variables.
- ceres::internal::Mutex mutex_;
+ std::mutex mutex_;
};
// Creates a CostFunction that checks the Jacobians that cost_function computes
diff --git a/extern/ceres/internal/ceres/gradient_problem_evaluator.h b/extern/ceres/internal/ceres/gradient_problem_evaluator.h
index 2c562544768..c5ad1d71607 100644
--- a/extern/ceres/internal/ceres/gradient_problem_evaluator.h
+++ b/extern/ceres/internal/ceres/gradient_problem_evaluator.h
@@ -48,43 +48,46 @@ class GradientProblemEvaluator : public Evaluator {
explicit GradientProblemEvaluator(const GradientProblem& problem)
: problem_(problem) {}
virtual ~GradientProblemEvaluator() {}
- virtual SparseMatrix* CreateJacobian() const { return NULL; }
- virtual bool Evaluate(const EvaluateOptions& evaluate_options,
- const double* state,
- double* cost,
- double* residuals,
- double* gradient,
- SparseMatrix* jacobian) {
+ 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);
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
+ // that the line search minimizer code is used by both the
+ // GradientProblemSolver and the main CeresSolver coder where the
+ // Evaluator evaluates the Jacobian, and these magic strings need
+ // to be consistent across the code base for the time accounting
+ // to work.
ScopedExecutionTimer call_type_timer(
- gradient == NULL ? "Evaluator::Cost" : "Evaluator::Gradient",
+ gradient == NULL ? "Evaluator::Residual" : "Evaluator::Jacobian",
&execution_summary_);
return problem_.Evaluate(state, cost, gradient);
}
- virtual bool Plus(const double* state,
- const double* delta,
- double* state_plus_delta) const {
+ bool Plus(const double* state,
+ const double* delta,
+ double* state_plus_delta) const final {
return problem_.Plus(state, delta, state_plus_delta);
}
- virtual int NumParameters() const {
+ int NumParameters() const final {
return problem_.NumParameters();
}
- virtual int NumEffectiveParameters() const {
+ int NumEffectiveParameters() const final {
return problem_.NumLocalParameters();
}
- virtual int NumResiduals() const { return 1; }
+ int NumResiduals() const final { return 1; }
- virtual std::map<std::string, int> CallStatistics() const {
- return execution_summary_.calls();
- }
-
- virtual std::map<std::string, double> TimeStatistics() const {
- return execution_summary_.times();
+ std::map<std::string, internal::CallStatistics> Statistics() const final {
+ return execution_summary_.statistics();
}
private:
diff --git a/extern/ceres/internal/ceres/gradient_problem_solver.cc b/extern/ceres/internal/ceres/gradient_problem_solver.cc
index 8709f8f3fbd..1639e30666c 100644
--- a/extern/ceres/internal/ceres/gradient_problem_solver.cc
+++ b/extern/ceres/internal/ceres/gradient_problem_solver.cc
@@ -30,6 +30,7 @@
#include "ceres/gradient_problem_solver.h"
+#include <memory>
#include "ceres/callbacks.h"
#include "ceres/gradient_problem.h"
#include "ceres/gradient_problem_evaluator.h"
@@ -72,6 +73,7 @@ Solver::Options GradientProblemSolverOptionsToSolverOptions(
COPY_OPTION(max_line_search_step_expansion);
COPY_OPTION(max_num_iterations);
COPY_OPTION(max_solver_time_in_seconds);
+ COPY_OPTION(parameter_tolerance);
COPY_OPTION(function_tolerance);
COPY_OPTION(gradient_tolerance);
COPY_OPTION(logging_type);
@@ -97,16 +99,18 @@ void GradientProblemSolver::Solve(const GradientProblemSolver::Options& options,
const GradientProblem& problem,
double* parameters_ptr,
GradientProblemSolver::Summary* summary) {
- using internal::scoped_ptr;
- using internal::WallTimeInSeconds;
- using internal::Minimizer;
+ using internal::CallStatistics;
using internal::GradientProblemEvaluator;
+ using internal::GradientProblemSolverStateUpdatingCallback;
using internal::LoggingCallback;
+ using internal::Minimizer;
using internal::SetSummaryFinalCost;
+ using internal::WallTimeInSeconds;
double start_time = WallTimeInSeconds();
- *CHECK_NOTNULL(summary) = Summary();
+ CHECK(summary != nullptr);
+ *summary = Summary();
summary->num_parameters = problem.NumParameters();
summary->num_local_parameters = problem.NumLocalParameters();
summary->line_search_direction_type = options.line_search_direction_type; // NOLINT
@@ -121,6 +125,10 @@ void GradientProblemSolver::Solve(const GradientProblemSolver::Options& options,
return;
}
+ VectorRef parameters(parameters_ptr, problem.NumParameters());
+ Vector solution(problem.NumParameters());
+ solution = parameters;
+
// TODO(sameeragarwal): This is a bit convoluted, we should be able
// to convert to minimizer options directly, but this will do for
// now.
@@ -128,7 +136,7 @@ void GradientProblemSolver::Solve(const GradientProblemSolver::Options& options,
Minimizer::Options(GradientProblemSolverOptionsToSolverOptions(options));
minimizer_options.evaluator.reset(new GradientProblemEvaluator(problem));
- scoped_ptr<IterationCallback> logging_callback;
+ std::unique_ptr<IterationCallback> logging_callback;
if (options.logging_type != SILENT) {
logging_callback.reset(
new LoggingCallback(LINE_SEARCH, options.minimizer_progress_to_stdout));
@@ -136,10 +144,16 @@ void GradientProblemSolver::Solve(const GradientProblemSolver::Options& options,
logging_callback.get());
}
- scoped_ptr<Minimizer> minimizer(Minimizer::Create(LINE_SEARCH));
- Vector solution(problem.NumParameters());
- VectorRef parameters(parameters_ptr, problem.NumParameters());
- solution = parameters;
+ 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));
+ minimizer_options.callbacks.insert(minimizer_options.callbacks.begin(),
+ state_updating_callback.get());
+ }
+
+ std::unique_ptr<Minimizer> minimizer(Minimizer::Create(LINE_SEARCH));
Solver::Summary solver_summary;
solver_summary.fixed_cost = 0.0;
@@ -162,34 +176,23 @@ void GradientProblemSolver::Solve(const GradientProblemSolver::Options& options,
SetSummaryFinalCost(summary);
}
- const std::map<string, double>& evaluator_time_statistics =
- minimizer_options.evaluator->TimeStatistics();
- summary->cost_evaluation_time_in_seconds =
- FindWithDefault(evaluator_time_statistics, "Evaluator::Residual", 0.0);
- summary->gradient_evaluation_time_in_seconds =
- FindWithDefault(evaluator_time_statistics, "Evaluator::Jacobian", 0.0);
+ const std::map<string, CallStatistics>& evaluator_statistics =
+ minimizer_options.evaluator->Statistics();
+ {
+ const CallStatistics& call_stats = FindWithDefault(
+ evaluator_statistics, "Evaluator::Residual", CallStatistics());
+ summary->cost_evaluation_time_in_seconds = call_stats.time;
+ summary->num_cost_evaluations = call_stats.calls;
+ }
- summary->total_time_in_seconds = WallTimeInSeconds() - start_time;
-}
+ {
+ const CallStatistics& call_stats = FindWithDefault(
+ evaluator_statistics, "Evaluator::Jacobian", CallStatistics());
+ summary->gradient_evaluation_time_in_seconds = call_stats.time;
+ summary->num_gradient_evaluations = call_stats.calls;
+ }
-// Invalid values for most fields, to ensure that we are not
-// accidentally reporting default values.
-GradientProblemSolver::Summary::Summary()
- : termination_type(FAILURE),
- message("ceres::GradientProblemSolve was not called."),
- initial_cost(-1.0),
- final_cost(-1.0),
- total_time_in_seconds(-1.0),
- cost_evaluation_time_in_seconds(-1.0),
- gradient_evaluation_time_in_seconds(-1.0),
- line_search_polynomial_minimization_time_in_seconds(-1.0),
- num_parameters(-1),
- num_local_parameters(-1),
- line_search_direction_type(LBFGS),
- line_search_type(ARMIJO),
- line_search_interpolation_type(BISECTION),
- nonlinear_conjugate_gradient_type(FLETCHER_REEVES),
- max_lbfgs_rank(-1) {
+ summary->total_time_in_seconds = WallTimeInSeconds() - start_time;
}
bool GradientProblemSolver::Summary::IsSolutionUsable() const {
@@ -256,15 +259,15 @@ string GradientProblemSolver::Summary::FullReport() const {
static_cast<int>(iterations.size()));
StringAppendF(&report, "\nTime (in seconds):\n");
-
- StringAppendF(&report, "\n Cost evaluation %23.4f\n",
- cost_evaluation_time_in_seconds);
- StringAppendF(&report, " Gradient evaluation %23.4f\n",
- gradient_evaluation_time_in_seconds);
- StringAppendF(&report, " Polynomial minimization %17.4f\n",
+ StringAppendF(&report, "\n Cost evaluation %23.6f (%d)\n",
+ cost_evaluation_time_in_seconds,
+ num_cost_evaluations);
+ StringAppendF(&report, " Gradient & cost evaluation %16.6f (%d)\n",
+ gradient_evaluation_time_in_seconds,
+ num_gradient_evaluations);
+ StringAppendF(&report, " Polynomial minimization %17.6f\n",
line_search_polynomial_minimization_time_in_seconds);
-
- StringAppendF(&report, "Total %25.4f\n\n",
+ StringAppendF(&report, "Total %25.6f\n\n",
total_time_in_seconds);
StringAppendF(&report, "Termination: %25s (%s)\n",
diff --git a/extern/ceres/internal/ceres/graph.h b/extern/ceres/internal/ceres/graph.h
index b96b67265cb..4e1fd81c1ea 100644
--- a/extern/ceres/internal/ceres/graph.h
+++ b/extern/ceres/internal/ceres/graph.h
@@ -32,11 +32,11 @@
#define CERES_INTERNAL_GRAPH_H_
#include <limits>
+#include <unordered_set>
+#include <unordered_map>
#include <utility>
-#include "ceres/integral_types.h"
#include "ceres/map_util.h"
-#include "ceres/collections_port.h"
-#include "ceres/internal/macros.h"
+#include "ceres/pair_hash.h"
#include "ceres/types.h"
#include "glog/logging.h"
@@ -53,7 +53,7 @@ class Graph {
// Add a vertex.
void AddVertex(const Vertex& vertex) {
if (vertices_.insert(vertex).second) {
- edges_[vertex] = HashSet<Vertex>();
+ edges_[vertex] = std::unordered_set<Vertex>();
}
}
@@ -63,10 +63,9 @@ class Graph {
}
vertices_.erase(vertex);
- const HashSet<Vertex>& sinks = edges_[vertex];
- for (typename HashSet<Vertex>::const_iterator it = sinks.begin();
- it != sinks.end(); ++it) {
- edges_[*it].erase(vertex);
+ const std::unordered_set<Vertex>& sinks = edges_[vertex];
+ for (const Vertex& s : sinks) {
+ edges_[s].erase(vertex);
}
edges_.erase(vertex);
@@ -90,19 +89,17 @@ class Graph {
// Calling Neighbors on a vertex not in the graph will result in
// undefined behaviour.
- const HashSet<Vertex>& Neighbors(const Vertex& vertex) const {
+ const std::unordered_set<Vertex>& Neighbors(const Vertex& vertex) const {
return FindOrDie(edges_, vertex);
}
- const HashSet<Vertex>& vertices() const {
+ const std::unordered_set<Vertex>& vertices() const {
return vertices_;
}
private:
- HashSet<Vertex> vertices_;
- HashMap<Vertex, HashSet<Vertex> > edges_;
-
- CERES_DISALLOW_COPY_AND_ASSIGN(Graph);
+ std::unordered_set<Vertex> vertices_;
+ std::unordered_map<Vertex, std::unordered_set<Vertex>> edges_;
};
// A weighted undirected graph templated over the vertex ids. Vertex
@@ -117,7 +114,7 @@ class WeightedGraph {
void AddVertex(const Vertex& vertex, double weight) {
if (vertices_.find(vertex) == vertices_.end()) {
vertices_.insert(vertex);
- edges_[vertex] = HashSet<Vertex>();
+ edges_[vertex] = std::unordered_set<Vertex>();
}
vertex_weights_[vertex] = weight;
}
@@ -135,15 +132,14 @@ class WeightedGraph {
vertices_.erase(vertex);
vertex_weights_.erase(vertex);
- const HashSet<Vertex>& sinks = edges_[vertex];
- for (typename HashSet<Vertex>::const_iterator it = sinks.begin();
- it != sinks.end(); ++it) {
- if (vertex < *it) {
- edge_weights_.erase(std::make_pair(vertex, *it));
+ const std::unordered_set<Vertex>& sinks = edges_[vertex];
+ for (const Vertex& s : sinks) {
+ if (vertex < s) {
+ edge_weights_.erase(std::make_pair(vertex, s));
} else {
- edge_weights_.erase(std::make_pair(*it, vertex));
+ edge_weights_.erase(std::make_pair(s, vertex));
}
- edges_[*it].erase(vertex);
+ edges_[s].erase(vertex);
}
edges_.erase(vertex);
@@ -198,11 +194,11 @@ class WeightedGraph {
// Calling Neighbors on a vertex not in the graph will result in
// undefined behaviour.
- const HashSet<Vertex>& Neighbors(const Vertex& vertex) const {
+ const std::unordered_set<Vertex>& Neighbors(const Vertex& vertex) const {
return FindOrDie(edges_, vertex);
}
- const HashSet<Vertex>& vertices() const {
+ const std::unordered_set<Vertex>& vertices() const {
return vertices_;
}
@@ -211,12 +207,11 @@ class WeightedGraph {
}
private:
- HashSet<Vertex> vertices_;
- HashMap<Vertex, double> vertex_weights_;
- HashMap<Vertex, HashSet<Vertex> > edges_;
- HashMap<std::pair<Vertex, Vertex>, double> edge_weights_;
-
- CERES_DISALLOW_COPY_AND_ASSIGN(WeightedGraph);
+ std::unordered_set<Vertex> vertices_;
+ std::unordered_map<Vertex, double> vertex_weights_;
+ std::unordered_map<Vertex, std::unordered_set<Vertex>> edges_;
+ std::unordered_map<std::pair<Vertex, Vertex>, double, pair_hash>
+ edge_weights_;
};
} // namespace internal
diff --git a/extern/ceres/internal/ceres/graph_algorithms.h b/extern/ceres/internal/ceres/graph_algorithms.h
index d1d3f52cd22..b0629313159 100644
--- a/extern/ceres/internal/ceres/graph_algorithms.h
+++ b/extern/ceres/internal/ceres/graph_algorithms.h
@@ -34,9 +34,10 @@
#define CERES_INTERNAL_GRAPH_ALGORITHMS_H_
#include <algorithm>
+#include <unordered_map>
+#include <unordered_set>
#include <vector>
#include <utility>
-#include "ceres/collections_port.h"
#include "ceres/graph.h"
#include "ceres/wall_time.h"
#include "glog/logging.h"
@@ -96,10 +97,10 @@ class VertexDegreeLessThan {
template <typename Vertex>
int IndependentSetOrdering(const Graph<Vertex>& graph,
std::vector<Vertex>* ordering) {
- const HashSet<Vertex>& vertices = graph.vertices();
+ const std::unordered_set<Vertex>& vertices = graph.vertices();
const int num_vertices = vertices.size();
- CHECK_NOTNULL(ordering);
+ CHECK(ordering != nullptr);
ordering->clear();
ordering->reserve(num_vertices);
@@ -109,34 +110,29 @@ int IndependentSetOrdering(const Graph<Vertex>& graph,
const char kBlack = 2;
// Mark all vertices white.
- HashMap<Vertex, char> vertex_color;
+ std::unordered_map<Vertex, char> vertex_color;
std::vector<Vertex> vertex_queue;
- for (typename HashSet<Vertex>::const_iterator it = vertices.begin();
- it != vertices.end();
- ++it) {
- vertex_color[*it] = kWhite;
- vertex_queue.push_back(*it);
+ for (const Vertex& vertex : vertices) {
+ vertex_color[vertex] = kWhite;
+ vertex_queue.push_back(vertex);
}
-
- std::sort(vertex_queue.begin(), vertex_queue.end(),
+ std::sort(vertex_queue.begin(),
+ vertex_queue.end(),
VertexTotalOrdering<Vertex>(graph));
// Iterate over vertex_queue. Pick the first white vertex, add it
// to the independent set. Mark it black and its neighbors grey.
- for (int i = 0; i < vertex_queue.size(); ++i) {
- const Vertex& vertex = vertex_queue[i];
+ for (const Vertex& vertex : vertex_queue) {
if (vertex_color[vertex] != kWhite) {
continue;
}
ordering->push_back(vertex);
vertex_color[vertex] = kBlack;
- const HashSet<Vertex>& neighbors = graph.Neighbors(vertex);
- for (typename HashSet<Vertex>::const_iterator it = neighbors.begin();
- it != neighbors.end();
- ++it) {
- vertex_color[*it] = kGrey;
+ const std::unordered_set<Vertex>& neighbors = graph.Neighbors(vertex);
+ for (const Vertex& neighbor : neighbors) {
+ vertex_color[neighbor] = kGrey;
}
}
@@ -145,10 +141,7 @@ int IndependentSetOrdering(const Graph<Vertex>& graph,
// Iterate over the vertices and add all the grey vertices to the
// ordering. At this stage there should only be black or grey
// vertices in the graph.
- for (typename std::vector<Vertex>::const_iterator it = vertex_queue.begin();
- it != vertex_queue.end();
- ++it) {
- const Vertex vertex = *it;
+ for (const Vertex& vertex : vertex_queue) {
DCHECK(vertex_color[vertex] != kWhite);
if (vertex_color[vertex] != kBlack) {
ordering->push_back(vertex);
@@ -172,8 +165,8 @@ int IndependentSetOrdering(const Graph<Vertex>& graph,
template <typename Vertex>
int StableIndependentSetOrdering(const Graph<Vertex>& graph,
std::vector<Vertex>* ordering) {
- CHECK_NOTNULL(ordering);
- const HashSet<Vertex>& vertices = graph.vertices();
+ CHECK(ordering != nullptr);
+ const std::unordered_set<Vertex>& vertices = graph.vertices();
const int num_vertices = vertices.size();
CHECK_EQ(vertices.size(), ordering->size());
@@ -188,11 +181,9 @@ int StableIndependentSetOrdering(const Graph<Vertex>& graph,
VertexDegreeLessThan<Vertex>(graph));
// Mark all vertices white.
- HashMap<Vertex, char> vertex_color;
- for (typename HashSet<Vertex>::const_iterator it = vertices.begin();
- it != vertices.end();
- ++it) {
- vertex_color[*it] = kWhite;
+ std::unordered_map<Vertex, char> vertex_color;
+ for (const Vertex& vertex : vertices) {
+ vertex_color[vertex] = kWhite;
}
ordering->clear();
@@ -207,11 +198,9 @@ int StableIndependentSetOrdering(const Graph<Vertex>& graph,
ordering->push_back(vertex);
vertex_color[vertex] = kBlack;
- const HashSet<Vertex>& neighbors = graph.Neighbors(vertex);
- for (typename HashSet<Vertex>::const_iterator it = neighbors.begin();
- it != neighbors.end();
- ++it) {
- vertex_color[*it] = kGrey;
+ const std::unordered_set<Vertex>& neighbors = graph.Neighbors(vertex);
+ for (const Vertex& neighbor : neighbors) {
+ vertex_color[neighbor] = kGrey;
}
}
@@ -220,10 +209,7 @@ int StableIndependentSetOrdering(const Graph<Vertex>& graph,
// Iterate over the vertices and add all the grey vertices to the
// ordering. At this stage there should only be black or grey
// vertices in the graph.
- for (typename std::vector<Vertex>::const_iterator it = vertex_queue.begin();
- it != vertex_queue.end();
- ++it) {
- const Vertex vertex = *it;
+ for (const Vertex& vertex : vertex_queue) {
DCHECK(vertex_color[vertex] != kWhite);
if (vertex_color[vertex] != kBlack) {
ordering->push_back(vertex);
@@ -242,8 +228,8 @@ int StableIndependentSetOrdering(const Graph<Vertex>& graph,
// is what gives this data structure its efficiency.
template <typename Vertex>
Vertex FindConnectedComponent(const Vertex& vertex,
- HashMap<Vertex, Vertex>* union_find) {
- typename HashMap<Vertex, Vertex>::iterator it = union_find->find(vertex);
+ std::unordered_map<Vertex, Vertex>* union_find) {
+ auto it = union_find->find(vertex);
DCHECK(it != union_find->end());
if (it->second != vertex) {
it->second = FindConnectedComponent(it->second, union_find);
@@ -274,30 +260,24 @@ template <typename Vertex>
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;
+ std::vector<std::pair<double, std::pair<Vertex, Vertex>>> weighted_edges;
WeightedGraph<Vertex>* forest = new WeightedGraph<Vertex>();
// Disjoint-set to keep track of the connected components in the
// maximum spanning tree.
- HashMap<Vertex, Vertex> disjoint_set;
+ std::unordered_map<Vertex, Vertex> disjoint_set;
// Sort of the edges in the graph in decreasing order of their
// weight. Also add the vertices of the graph to the Maximum
// Spanning Tree graph and set each vertex to be its own connected
// component in the disjoint_set structure.
- const HashSet<Vertex>& vertices = graph.vertices();
- for (typename HashSet<Vertex>::const_iterator it = vertices.begin();
- it != vertices.end();
- ++it) {
- const Vertex vertex1 = *it;
+ const std::unordered_set<Vertex>& vertices = graph.vertices();
+ for (const Vertex& vertex1 : vertices) {
forest->AddVertex(vertex1, graph.VertexWeight(vertex1));
disjoint_set[vertex1] = vertex1;
- const HashSet<Vertex>& neighbors = graph.Neighbors(vertex1);
- for (typename HashSet<Vertex>::const_iterator it2 = neighbors.begin();
- it2 != neighbors.end();
- ++it2) {
- const Vertex vertex2 = *it2;
+ const std::unordered_set<Vertex>& neighbors = graph.Neighbors(vertex1);
+ for (const Vertex& vertex2 : neighbors) {
if (vertex1 >= vertex2) {
continue;
}
diff --git a/extern/ceres/internal/ceres/implicit_schur_complement.cc b/extern/ceres/internal/ceres/implicit_schur_complement.cc
index d05f03817b7..bf680d1d952 100644
--- a/extern/ceres/internal/ceres/implicit_schur_complement.cc
+++ b/extern/ceres/internal/ceres/implicit_schur_complement.cc
@@ -34,7 +34,6 @@
#include "ceres/block_sparse_matrix.h"
#include "ceres/block_structure.h"
#include "ceres/internal/eigen.h"
-#include "ceres/internal/scoped_ptr.h"
#include "ceres/linear_solver.h"
#include "ceres/types.h"
#include "glog/logging.h"
diff --git a/extern/ceres/internal/ceres/implicit_schur_complement.h b/extern/ceres/internal/ceres/implicit_schur_complement.h
index 5d822ebaeef..f4ddf724910 100644
--- a/extern/ceres/internal/ceres/implicit_schur_complement.h
+++ b/extern/ceres/internal/ceres/implicit_schur_complement.h
@@ -34,11 +34,11 @@
#ifndef CERES_INTERNAL_IMPLICIT_SCHUR_COMPLEMENT_H_
#define CERES_INTERNAL_IMPLICIT_SCHUR_COMPLEMENT_H_
+#include <memory>
#include "ceres/linear_operator.h"
#include "ceres/linear_solver.h"
#include "ceres/partitioned_matrix_view.h"
#include "ceres/internal/eigen.h"
-#include "ceres/internal/scoped_ptr.h"
#include "ceres/types.h"
namespace ceres {
@@ -113,11 +113,11 @@ class ImplicitSchurComplement : public LinearOperator {
void Init(const BlockSparseMatrix& A, const double* D, const double* b);
// y += Sx, where S is the Schur complement.
- virtual void RightMultiply(const double* x, double* y) const;
+ void RightMultiply(const double* x, double* y) const final;
// The Schur complement is a symmetric positive definite matrix,
// thus the left and right multiply operators are the same.
- virtual void LeftMultiply(const double* x, double* y) const {
+ void LeftMultiply(const double* x, double* y) const final {
RightMultiply(x, y);
}
@@ -127,8 +127,8 @@ class ImplicitSchurComplement : public LinearOperator {
// complement.
void BackSubstitute(const double* x, double* y);
- virtual int num_rows() const { return A_->num_cols_f(); }
- virtual int num_cols() const { return A_->num_cols_f(); }
+ int num_rows() const final { return A_->num_cols_f(); }
+ int num_cols() const final { return A_->num_cols_f(); }
const Vector& rhs() const { return rhs_; }
const BlockSparseMatrix* block_diagonal_EtE_inverse() const {
@@ -145,12 +145,12 @@ class ImplicitSchurComplement : public LinearOperator {
const LinearSolver::Options& options_;
- scoped_ptr<PartitionedMatrixViewBase> A_;
+ std::unique_ptr<PartitionedMatrixViewBase> A_;
const double* D_;
const double* b_;
- scoped_ptr<BlockSparseMatrix> block_diagonal_EtE_inverse_;
- scoped_ptr<BlockSparseMatrix> block_diagonal_FtF_inverse_;
+ std::unique_ptr<BlockSparseMatrix> block_diagonal_EtE_inverse_;
+ std::unique_ptr<BlockSparseMatrix> block_diagonal_FtF_inverse_;
Vector rhs_;
diff --git a/extern/ceres/internal/ceres/inner_product_computer.cc b/extern/ceres/internal/ceres/inner_product_computer.cc
new file mode 100644
index 00000000000..2bf88365d99
--- /dev/null
+++ b/extern/ceres/internal/ceres/inner_product_computer.cc
@@ -0,0 +1,330 @@
+// 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)
+
+#include "ceres/inner_product_computer.h"
+
+#include <algorithm>
+#include "ceres/small_blas.h"
+
+namespace ceres {
+namespace internal {
+
+
+// Create the CompressedRowSparseMatrix matrix that will contain the
+// inner product.
+//
+// storage_type controls whether the result matrix contains the upper
+// or the lower triangular part of the product.
+//
+// num_nonzeros is the number of non-zeros in the result matrix.
+CompressedRowSparseMatrix* InnerProductComputer::CreateResultMatrix(
+ const CompressedRowSparseMatrix::StorageType storage_type,
+ const int num_nonzeros) {
+ CompressedRowSparseMatrix* matrix =
+ new CompressedRowSparseMatrix(m_.num_cols(), m_.num_cols(), num_nonzeros);
+ matrix->set_storage_type(storage_type);
+
+ const CompressedRowBlockStructure* bs = m_.block_structure();
+ const std::vector<Block>& blocks = bs->cols;
+ matrix->mutable_row_blocks()->resize(blocks.size());
+ matrix->mutable_col_blocks()->resize(blocks.size());
+ for (int i = 0; i < blocks.size(); ++i) {
+ (*(matrix->mutable_row_blocks()))[i] = blocks[i].size;
+ (*(matrix->mutable_col_blocks()))[i] = blocks[i].size;
+ }
+
+ return matrix;
+}
+
+// Given the set of product terms in the inner product, return the
+// total number of non-zeros in the result and for each row block of
+// the result matrix, compute the number of non-zeros in any one row
+// of the row block.
+int InnerProductComputer::ComputeNonzeros(
+ const std::vector<InnerProductComputer::ProductTerm>& product_terms,
+ std::vector<int>* row_nnz) {
+ const CompressedRowBlockStructure* bs = m_.block_structure();
+ const std::vector<Block>& blocks = bs->cols;
+
+ row_nnz->resize(blocks.size());
+ std::fill(row_nnz->begin(), row_nnz->end(), 0);
+
+ // First product term.
+ (*row_nnz)[product_terms[0].row] = blocks[product_terms[0].col].size;
+ int num_nonzeros =
+ blocks[product_terms[0].row].size * blocks[product_terms[0].col].size;
+
+ // Remaining product terms.
+ for (int i = 1; i < product_terms.size(); ++i) {
+ const ProductTerm& previous = product_terms[i - 1];
+ const ProductTerm& current = product_terms[i];
+
+ // Each (row, col) block counts only once.
+ // This check depends on product sorted on (row, col).
+ if (current.row != previous.row || current.col != previous.col) {
+ (*row_nnz)[current.row] += blocks[current.col].size;
+ num_nonzeros += blocks[current.row].size * blocks[current.col].size;
+ }
+ }
+
+ return num_nonzeros;
+}
+
+InnerProductComputer::InnerProductComputer(const BlockSparseMatrix& m,
+ const int start_row_block,
+ const int end_row_block)
+ : m_(m), start_row_block_(start_row_block), end_row_block_(end_row_block) {}
+
+// Compute the sparsity structure of the product m.transpose() * m
+// and create a CompressedRowSparseMatrix corresponding to it.
+//
+// Also compute the "program" vector, which for every term in the
+// block outer product provides the information for the entry in the
+// values array of the result matrix where it should be accumulated.
+//
+// Since the entries of the program are the same for rows with the
+// same sparsity structure, the program only stores the result for one
+// row per row block. The Compute function reuses this information for
+// each row in the row block.
+//
+// product_storage_type controls the form of the output matrix. It
+// can be LOWER_TRIANGULAR or UPPER_TRIANGULAR.
+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(
+ const BlockSparseMatrix& m,
+ const int start_row_block,
+ const int end_row_block,
+ CompressedRowSparseMatrix::StorageType product_storage_type) {
+ CHECK(product_storage_type == CompressedRowSparseMatrix::LOWER_TRIANGULAR ||
+ 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);
+ inner_product_computer->Init(product_storage_type);
+ return inner_product_computer;
+}
+
+void InnerProductComputer::Init(
+ const CompressedRowSparseMatrix::StorageType product_storage_type) {
+ std::vector<InnerProductComputer::ProductTerm> product_terms;
+ const CompressedRowBlockStructure* bs = m_.block_structure();
+
+ // Give input matrix m in Block Sparse format
+ // (row_block, col_block)
+ // represent each block multiplication
+ // (row_block, col_block1)' X (row_block, col_block2)
+ // by its product term:
+ // (col_block1, col_block2, index)
+ for (int row_block = start_row_block_; row_block < end_row_block_;
+ ++row_block) {
+ const CompressedRow& row = bs->rows[row_block];
+ for (int c1 = 0; c1 < row.cells.size(); ++c1) {
+ const Cell& cell1 = row.cells[c1];
+ int c2_begin, c2_end;
+ if (product_storage_type == CompressedRowSparseMatrix::LOWER_TRIANGULAR) {
+ c2_begin = 0;
+ c2_end = c1 + 1;
+ } else {
+ c2_begin = c1;
+ c2_end = row.cells.size();
+ }
+
+ 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()));
+ }
+ }
+ }
+
+ std::sort(product_terms.begin(), product_terms.end());
+ ComputeOffsetsAndCreateResultMatrix(product_storage_type, product_terms);
+}
+
+void InnerProductComputer::ComputeOffsetsAndCreateResultMatrix(
+ const CompressedRowSparseMatrix::StorageType product_storage_type,
+ const std::vector<InnerProductComputer::ProductTerm>& product_terms) {
+ const std::vector<Block>& col_blocks = m_.block_structure()->cols;
+
+ std::vector<int> row_block_nnz;
+ const int num_nonzeros = ComputeNonzeros(product_terms, &row_block_nnz);
+
+ result_.reset(CreateResultMatrix(product_storage_type, num_nonzeros));
+
+ // Populate the row non-zero counts in the result matrix.
+ int* crsm_rows = result_->mutable_rows();
+ crsm_rows[0] = 0;
+ for (int i = 0; i < col_blocks.size(); ++i) {
+ for (int j = 0; j < col_blocks[i].size; ++j, ++crsm_rows) {
+ *(crsm_rows + 1) = *crsm_rows + row_block_nnz[i];
+ }
+ }
+
+ // The following macro FILL_CRSM_COL_BLOCK is key to understanding
+ // how this class works.
+ //
+ // It does two things.
+ //
+ // Sets the value for the current term in the result_offsets_ array
+ // and populates the cols array of the result matrix.
+ //
+ // row_block and col_block as the names imply, refer to the row and
+ // column blocks of the current term.
+ //
+ // row_nnz is the number of nonzeros in the result_matrix at the
+ // beginning of the first row of row_block.
+ //
+ // col_nnz is the number of nonzeros in the first row of the row
+ // block that occur before the current column block, i.e. this is
+ // sum of the sizes of all the column blocks in this row block that
+ // came before this column block.
+ //
+ // Given these two numbers and the total number of nonzeros in this
+ // row (nnz_in_row), we can now populate the cols array as follows:
+ //
+ // nnz + j * nnz_in_row is the beginning of the j^th row.
+ //
+ // nnz + j * nnz_in_row + col_nnz is the beginning of the column
+ // block in the j^th row.
+ //
+ // nnz + j * nnz_in_row + col_nnz + k is then the j^th row and the
+ // k^th column of the product block, whose value is
+ //
+ // col_blocks[col_block].position + k, which is the column number of
+ // the k^th column of the current column block.
+#define FILL_CRSM_COL_BLOCK \
+ const int row_block = current->row; \
+ const int col_block = current->col; \
+ const int nnz_in_row = row_block_nnz[row_block]; \
+ int* crsm_cols = result_->mutable_cols(); \
+ result_offsets_[current->index] = nnz + col_nnz; \
+ for (int j = 0; j < col_blocks[row_block].size; ++j) { \
+ for (int k = 0; k < col_blocks[col_block].size; ++k) { \
+ crsm_cols[nnz + j * nnz_in_row + col_nnz + k] = \
+ col_blocks[col_block].position + k; \
+ } \
+ }
+
+ result_offsets_.resize(product_terms.size());
+ int col_nnz = 0;
+ int nnz = 0;
+
+ // Process the first term.
+ const InnerProductComputer::ProductTerm* current = &product_terms[0];
+ FILL_CRSM_COL_BLOCK;
+
+ // Process the rest of the terms.
+ for (int i = 1; i < product_terms.size(); ++i) {
+ current = &product_terms[i];
+ const InnerProductComputer::ProductTerm* previous = &product_terms[i - 1];
+
+ // If the current term is the same as the previous term, then it
+ // stores its product at the same location as the previous term.
+ if (previous->row == current->row && previous->col == current->col) {
+ result_offsets_[current->index] = result_offsets_[previous->index];
+ continue;
+ }
+
+ if (previous->row == current->row) {
+ // if the current and previous terms are in the same row block,
+ // then they differ in the column block, in which case advance
+ // col_nnz by the column size of the prevous term.
+ col_nnz += col_blocks[previous->col].size;
+ } else {
+ // If we have moved to a new row-block , then col_nnz is zero,
+ // and nnz is set to the beginning of the row block.
+ col_nnz = 0;
+ nnz += row_block_nnz[previous->row] * col_blocks[previous->row].size;
+ }
+
+ FILL_CRSM_COL_BLOCK;
+ }
+}
+
+// Use the results_offsets_ array to numerically compute the product
+// m' * m and store it in result_.
+//
+// TODO(sameeragarwal): Multithreading support.
+void InnerProductComputer::Compute() {
+ const double* m_values = m_.values();
+ const CompressedRowBlockStructure* bs = m_.block_structure();
+
+ const CompressedRowSparseMatrix::StorageType storage_type =
+ result_->storage_type();
+ result_->SetZero();
+ double* values = result_->mutable_values();
+ const int* rows = result_->rows();
+ int cursor = 0;
+
+ // Iterate row blocks.
+ for (int r = start_row_block_; r < end_row_block_; ++r) {
+ const CompressedRow& m_row = bs->rows[r];
+ for (int c1 = 0; c1 < m_row.cells.size(); ++c1) {
+ const Cell& cell1 = m_row.cells[c1];
+ const int c1_size = bs->cols[cell1.block_id].size;
+ const int row_nnz = rows[bs->cols[cell1.block_id].position + 1] -
+ rows[bs->cols[cell1.block_id].position];
+
+ int c2_begin, c2_end;
+ if (storage_type == CompressedRowSparseMatrix::LOWER_TRIANGULAR) {
+ c2_begin = 0;
+ c2_end = c1 + 1;
+ } else {
+ c2_begin = c1;
+ c2_end = m_row.cells.size();
+ }
+
+ for (int c2 = c2_begin; c2 < c2_end; ++c2, ++cursor) {
+ const Cell& cell2 = m_row.cells[c2];
+ const int c2_size = bs->cols[cell2.block_id].size;
+ MatrixTransposeMatrixMultiply<Eigen::Dynamic, Eigen::Dynamic,
+ Eigen::Dynamic, Eigen::Dynamic, 1>(
+ m_values + cell1.position,
+ m_row.block.size, c1_size,
+ m_values + cell2.position,
+ m_row.block.size, c2_size,
+ values + result_offsets_[cursor],
+ 0, 0, c1_size, row_nnz);
+ }
+ }
+ }
+
+ CHECK_EQ(cursor, result_offsets_.size());
+}
+
+} // namespace internal
+} // namespace ceres
diff --git a/extern/ceres/internal/ceres/inner_product_computer.h b/extern/ceres/internal/ceres/inner_product_computer.h
new file mode 100644
index 00000000000..73073f8ad06
--- /dev/null
+++ b/extern/ceres/internal/ceres/inner_product_computer.h
@@ -0,0 +1,157 @@
+// 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)
+
+#ifndef CERES_INTERNAL_INNER_PRODUCT_COMPUTER_H_
+#define CERES_INTERNAL_INNER_PRODUCT_COMPUTER_H_
+
+#include <memory>
+#include <vector>
+
+#include "ceres/block_sparse_matrix.h"
+#include "ceres/compressed_row_sparse_matrix.h"
+
+namespace ceres {
+namespace internal {
+
+// This class is used to repeatedly compute the inner product
+//
+// result = m' * m
+//
+// where the sparsity structure of m remains constant across calls.
+//
+// Upon creation, the class computes and caches information needed to
+// compute v, and then uses it to efficiently compute the product
+// every time InnerProductComputer::Compute is called.
+//
+// See sparse_normal_cholesky_solver.cc for example usage.
+//
+// Note that the result matrix is a block upper or lower-triangular
+// matrix, i.e., it will contain entries in the upper or lower
+// triangular part of the matrix corresponding to the block that occur
+// along its diagonal.
+//
+// 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 InnerProductComputer {
+ public:
+ // Factory
+ //
+ // m is the input matrix
+ //
+ // Since m' * m is a symmetric matrix, we only compute half of the
+ // matrix and the value of storage_type which must be
+ // UPPER_TRIANGULAR or LOWER_TRIANGULAR determines which half is
+ // computed.
+ //
+ // The user must ensure that the matrix m is valid for the life time
+ // of this object.
+ static InnerProductComputer* Create(
+ const BlockSparseMatrix& m,
+ CompressedRowSparseMatrix::StorageType storage_type);
+
+ // This factory method allows the user control over range of row
+ // blocks of m that should be used to compute the inner product.
+ //
+ // a = m(start_row_block : end_row_block, :);
+ // result = a' * a;
+ static InnerProductComputer* Create(
+ const BlockSparseMatrix& m,
+ int start_row_block,
+ int end_row_block,
+ CompressedRowSparseMatrix::StorageType storage_type);
+
+ // Update result_ to be numerically equal to m' * m.
+ void Compute();
+
+ // Accessors for the result containing the inner product.
+ //
+ // Compute must be called before accessing this result for
+ // the first time.
+ const CompressedRowSparseMatrix& result() const { return *result_; }
+ CompressedRowSparseMatrix* mutable_result() const { return result_.get(); }
+
+ private:
+ // A ProductTerm is a term in the block inner product of a matrix
+ // with itself.
+ struct ProductTerm {
+ ProductTerm(const int row, const int col, const int index)
+ : row(row), col(col), index(index) {}
+
+ bool operator<(const ProductTerm& right) const {
+ if (row == right.row) {
+ if (col == right.col) {
+ return index < right.index;
+ }
+ return col < right.col;
+ }
+ return row < right.row;
+ }
+
+ int row;
+ int col;
+ int index;
+ };
+
+ InnerProductComputer(const BlockSparseMatrix& m,
+ int start_row_block,
+ int end_row_block);
+
+ void Init(CompressedRowSparseMatrix::StorageType storage_type);
+
+ CompressedRowSparseMatrix* CreateResultMatrix(
+ const CompressedRowSparseMatrix::StorageType storage_type,
+ int num_nonzeros);
+
+ int ComputeNonzeros(const std::vector<ProductTerm>& product_terms,
+ std::vector<int>* row_block_nnz);
+
+ void ComputeOffsetsAndCreateResultMatrix(
+ const CompressedRowSparseMatrix::StorageType storage_type,
+ const std::vector<ProductTerm>& product_terms);
+
+ const BlockSparseMatrix& m_;
+ const int start_row_block_;
+ const int end_row_block_;
+ std::unique_ptr<CompressedRowSparseMatrix> result_;
+
+ // For each term in the inner product, result_offsets_ contains the
+ // location in the values array of the result_ matrix where it
+ // should be stored.
+ //
+ // This is the principal look up table that allows this class to
+ // compute the inner product fast.
+ std::vector<int> result_offsets_;
+};
+
+} // namespace internal
+} // namespace ceres
+
+#endif // CERES_INTERNAL_INNER_PRODUCT_COMPUTER_H_
diff --git a/extern/ceres/internal/ceres/invert_psd_matrix.h b/extern/ceres/internal/ceres/invert_psd_matrix.h
new file mode 100644
index 00000000000..21d301a77ee
--- /dev/null
+++ b/extern/ceres/internal/ceres/invert_psd_matrix.h
@@ -0,0 +1,79 @@
+// 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)
+
+#ifndef CERES_INTERNAL_INVERT_PSD_MATRIX_H_
+#define CERES_INTERNAL_INVERT_PSD_MATRIX_H_
+
+#include "ceres/internal/eigen.h"
+#include "glog/logging.h"
+#include "Eigen/Dense"
+
+namespace ceres {
+namespace internal {
+
+// Helper routine to compute the inverse or pseudo-inverse of a
+// symmetric positive semi-definite matrix.
+//
+// assume_full_rank controls whether a Cholesky factorization or an
+// Singular Value Decomposition is used to compute the inverse and the
+// pseudo-inverse respectively.
+//
+// The template parameter kSize can either be Eigen::Dynamic or a
+// positive integer equal to the number of rows of m.
+template <int kSize>
+typename EigenTypes<kSize, kSize>::Matrix InvertPSDMatrix(
+ const bool assume_full_rank,
+ const typename EigenTypes<kSize, kSize>::Matrix& m) {
+ using MType = typename EigenTypes<kSize, kSize>::Matrix;
+ const int size = m.rows();
+
+ // If the matrix can be assumed to be full rank, then if it is small
+ // (< 5) and fixed size, use Eigen's optimized inverse()
+ // implementation.
+ //
+ // https://eigen.tuxfamily.org/dox/group__TutorialLinearAlgebra.html#title3
+ if (assume_full_rank) {
+ if (kSize > 0 && kSize < 5) {
+ return m.inverse();
+ }
+ return m.template selfadjointView<Eigen::Upper>().llt().solve(
+ MType::Identity(size, size));
+ }
+
+ // For a thin SVD the number of columns of the matrix need to be dynamic.
+ using SVDMType = typename EigenTypes<kSize, Eigen::Dynamic>::Matrix;
+ Eigen::JacobiSVD<SVDMType> svd(m, Eigen::ComputeThinU | Eigen::ComputeThinV);
+ return svd.solve(MType::Identity(size, size));
+}
+
+} // namespace internal
+} // namespace ceres
+
+#endif // CERES_INTERNAL_INVERT_PSD_MATRIX_H_
diff --git a/extern/ceres/internal/ceres/iterative_refiner.cc b/extern/ceres/internal/ceres/iterative_refiner.cc
new file mode 100644
index 00000000000..fb0e45bdcdd
--- /dev/null
+++ b/extern/ceres/internal/ceres/iterative_refiner.cc
@@ -0,0 +1,74 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2018 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 <string>
+#include "ceres/iterative_refiner.h"
+
+#include "Eigen/Core"
+#include "ceres/sparse_cholesky.h"
+#include "ceres/sparse_matrix.h"
+
+namespace ceres {
+namespace internal {
+
+IterativeRefiner::IterativeRefiner(const int max_num_iterations)
+ : max_num_iterations_(max_num_iterations) {}
+
+IterativeRefiner::~IterativeRefiner() {}
+
+void IterativeRefiner::Allocate(int num_cols) {
+ residual_.resize(num_cols);
+ correction_.resize(num_cols);
+ lhs_x_solution_.resize(num_cols);
+}
+
+void IterativeRefiner::Refine(const SparseMatrix& lhs,
+ const double* rhs_ptr,
+ SparseCholesky* sparse_cholesky,
+ double* solution_ptr) {
+ const int num_cols = lhs.num_cols();
+ Allocate(num_cols);
+ ConstVectorRef rhs(rhs_ptr, num_cols);
+ VectorRef solution(solution_ptr, num_cols);
+ for (int i = 0; i < max_num_iterations_; ++i) {
+ // residual = rhs - lhs * solution
+ lhs_x_solution_.setZero();
+ lhs.RightMultiply(solution_ptr, lhs_x_solution_.data());
+ residual_ = rhs - lhs_x_solution_;
+ // solution += lhs^-1 residual
+ std::string ignored_message;
+ sparse_cholesky->Solve(
+ residual_.data(), correction_.data(), &ignored_message);
+ solution += correction_;
+ }
+};
+
+} // namespace internal
+} // namespace ceres
diff --git a/extern/ceres/internal/ceres/iterative_refiner.h b/extern/ceres/internal/ceres/iterative_refiner.h
new file mode 100644
index 00000000000..f969935ae46
--- /dev/null
+++ b/extern/ceres/internal/ceres/iterative_refiner.h
@@ -0,0 +1,93 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2018 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_ITERATIVE_REFINER_H_
+#define CERES_INTERNAL_ITERATIVE_REFINER_H_
+
+// This include must come before any #ifndef check on Ceres compile options.
+#include "ceres/internal/port.h"
+#include "ceres/internal/eigen.h"
+
+namespace ceres {
+namespace internal {
+
+class SparseCholesky;
+class SparseMatrix;
+
+// Iterative refinement
+// (https://en.wikipedia.org/wiki/Iterative_refinement) is the process
+// of improving the solution to a linear system, by using the
+// following iteration.
+//
+// r_i = b - Ax_i
+// Ad_i = r_i
+// x_{i+1} = x_i + d_i
+//
+// IterativeRefiner implements this process for Symmetric Positive
+// Definite linear systems.
+//
+// The above iterative loop is run until max_num_iterations is reached.
+class IterativeRefiner {
+ public:
+ // max_num_iterations is the number of refinement iterations to
+ // perform.
+ IterativeRefiner(int max_num_iterations);
+
+ // Needed for mocking.
+ virtual ~IterativeRefiner();
+
+ // Given an initial estimate of the solution of lhs * x = rhs, use
+ // max_num_iterations rounds of iterative refinement to improve it.
+ //
+ // sparse_cholesky is assumed to contain an already computed
+ // factorization (or approximation thereof) of lhs.
+ //
+ // solution is expected to contain a approximation to the solution
+ // to lhs * x = rhs. It can be zero.
+ //
+ // This method is virtual to facilitate mocking.
+ virtual void Refine(const SparseMatrix& lhs,
+ const double* rhs,
+ SparseCholesky* sparse_cholesky,
+ double* solution);
+
+ private:
+ void Allocate(int num_cols);
+
+ int max_num_iterations_;
+ Vector residual_;
+ Vector correction_;
+ Vector lhs_x_solution_;
+};
+
+} // namespace internal
+} // namespace ceres
+
+#endif // CERES_INTERNAL_ITERATIVE_REFINER_H_
diff --git a/extern/ceres/internal/ceres/iterative_schur_complement_solver.cc b/extern/ceres/internal/ceres/iterative_schur_complement_solver.cc
index 9d4e30d69d2..6076c38c71d 100644
--- a/extern/ceres/internal/ceres/iterative_schur_complement_solver.cc
+++ b/extern/ceres/internal/ceres/iterative_schur_complement_solver.cc
@@ -41,7 +41,6 @@
#include "ceres/detect_structure.h"
#include "ceres/implicit_schur_complement.h"
#include "ceres/internal/eigen.h"
-#include "ceres/internal/scoped_ptr.h"
#include "ceres/linear_solver.h"
#include "ceres/preconditioner.h"
#include "ceres/schur_jacobi_preconditioner.h"
@@ -59,8 +58,7 @@ IterativeSchurComplementSolver::IterativeSchurComplementSolver(
: options_(options) {
}
-IterativeSchurComplementSolver::~IterativeSchurComplementSolver() {
-}
+IterativeSchurComplementSolver::~IterativeSchurComplementSolver() {}
LinearSolver::Summary IterativeSchurComplementSolver::SolveImpl(
BlockSparseMatrix* A,
@@ -69,7 +67,7 @@ LinearSolver::Summary IterativeSchurComplementSolver::SolveImpl(
double* x) {
EventLogger event_logger("IterativeSchurComplementSolver::Solve");
- CHECK_NOTNULL(A->block_structure());
+ CHECK(A->block_structure() != nullptr);
const int num_eliminate_blocks = options_.elimination_groups[0];
// Initialize a ImplicitSchurComplement object.
if (schur_complement_ == NULL) {
@@ -86,29 +84,61 @@ LinearSolver::Summary IterativeSchurComplementSolver::SolveImpl(
A->block_structure()->cols.size() - num_eliminate_blocks;
if (num_schur_complement_blocks == 0) {
VLOG(2) << "No parameter blocks left in the schur complement.";
- LinearSolver::Summary cg_summary;
- cg_summary.num_iterations = 0;
- cg_summary.termination_type = LINEAR_SOLVER_SUCCESS;
+ LinearSolver::Summary summary;
+ summary.num_iterations = 0;
+ summary.termination_type = LINEAR_SOLVER_SUCCESS;
schur_complement_->BackSubstitute(NULL, x);
- return cg_summary;
+ return summary;
}
// Initialize the solution to the Schur complement system to zero.
reduced_linear_system_solution_.resize(schur_complement_->num_rows());
reduced_linear_system_solution_.setZero();
- // Instantiate a conjugate gradient solver that runs on the Schur
- // complement matrix with the block diagonal of the matrix F'F as
- // the preconditioner.
LinearSolver::Options cg_options;
cg_options.min_num_iterations = options_.min_num_iterations;
cg_options.max_num_iterations = options_.max_num_iterations;
ConjugateGradientsSolver cg_solver(cg_options);
- LinearSolver::PerSolveOptions cg_per_solve_options;
+ LinearSolver::PerSolveOptions cg_per_solve_options;
cg_per_solve_options.r_tolerance = per_solve_options.r_tolerance;
cg_per_solve_options.q_tolerance = per_solve_options.q_tolerance;
+ CreatePreconditioner(A);
+ if (preconditioner_.get() != NULL) {
+ if (!preconditioner_->Update(*A, per_solve_options.D)) {
+ LinearSolver::Summary summary;
+ summary.num_iterations = 0;
+ summary.termination_type = LINEAR_SOLVER_FAILURE;
+ summary.message = "Preconditioner update failed.";
+ return summary;
+ }
+
+ cg_per_solve_options.preconditioner = preconditioner_.get();
+ }
+
+ event_logger.AddEvent("Setup");
+ LinearSolver::Summary summary =
+ cg_solver.Solve(schur_complement_.get(),
+ schur_complement_->rhs().data(),
+ cg_per_solve_options,
+ reduced_linear_system_solution_.data());
+ if (summary.termination_type != LINEAR_SOLVER_FAILURE &&
+ summary.termination_type != LINEAR_SOLVER_FATAL_ERROR) {
+ schur_complement_->BackSubstitute(reduced_linear_system_solution_.data(),
+ x);
+ }
+ event_logger.AddEvent("Solve");
+ return summary;
+}
+
+void IterativeSchurComplementSolver::CreatePreconditioner(
+ BlockSparseMatrix* A) {
+ if (options_.preconditioner_type == IDENTITY ||
+ preconditioner_.get() != NULL) {
+ return;
+ }
+
Preconditioner::Options preconditioner_options;
preconditioner_options.type = options_.preconditioner_type;
preconditioner_options.visibility_clustering_type =
@@ -120,63 +150,27 @@ LinearSolver::Summary IterativeSchurComplementSolver::SolveImpl(
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);
+ preconditioner_options.context = options_.context;
switch (options_.preconditioner_type) {
- case IDENTITY:
- break;
case JACOBI:
- preconditioner_.reset(
- new SparseMatrixPreconditionerWrapper(
- schur_complement_->block_diagonal_FtF_inverse()));
+ preconditioner_.reset(new SparseMatrixPreconditionerWrapper(
+ schur_complement_->block_diagonal_FtF_inverse()));
break;
case SCHUR_JACOBI:
- if (preconditioner_.get() == NULL) {
- preconditioner_.reset(
- new SchurJacobiPreconditioner(*A->block_structure(),
- preconditioner_options));
- }
+ preconditioner_.reset(new SchurJacobiPreconditioner(
+ *A->block_structure(), preconditioner_options));
break;
case CLUSTER_JACOBI:
case CLUSTER_TRIDIAGONAL:
- if (preconditioner_.get() == NULL) {
- preconditioner_.reset(
- new VisibilityBasedPreconditioner(*A->block_structure(),
- preconditioner_options));
- }
+ preconditioner_.reset(new VisibilityBasedPreconditioner(
+ *A->block_structure(), preconditioner_options));
break;
default:
LOG(FATAL) << "Unknown Preconditioner Type";
}
-
- bool preconditioner_update_was_successful = true;
- if (preconditioner_.get() != NULL) {
- preconditioner_update_was_successful =
- preconditioner_->Update(*A, per_solve_options.D);
- cg_per_solve_options.preconditioner = preconditioner_.get();
- }
- event_logger.AddEvent("Setup");
-
- LinearSolver::Summary cg_summary;
- cg_summary.num_iterations = 0;
- cg_summary.termination_type = LINEAR_SOLVER_FAILURE;
-
- // TODO(sameeragarwal): Refactor preconditioners to return a more
- // sane message.
- cg_summary.message = "Preconditioner update failed.";
- if (preconditioner_update_was_successful) {
- cg_summary = cg_solver.Solve(schur_complement_.get(),
- schur_complement_->rhs().data(),
- cg_per_solve_options,
- reduced_linear_system_solution_.data());
- if (cg_summary.termination_type != LINEAR_SOLVER_FAILURE &&
- cg_summary.termination_type != LINEAR_SOLVER_FATAL_ERROR) {
- schur_complement_->BackSubstitute(
- reduced_linear_system_solution_.data(), x);
- }
- }
- event_logger.AddEvent("Solve");
- return cg_summary;
-}
+};
} // namespace internal
} // namespace ceres
diff --git a/extern/ceres/internal/ceres/iterative_schur_complement_solver.h b/extern/ceres/internal/ceres/iterative_schur_complement_solver.h
index e90d310de07..9aed94fcb1a 100644
--- a/extern/ceres/internal/ceres/iterative_schur_complement_solver.h
+++ b/extern/ceres/internal/ceres/iterative_schur_complement_solver.h
@@ -31,9 +31,9 @@
#ifndef CERES_INTERNAL_ITERATIVE_SCHUR_COMPLEMENT_SOLVER_H_
#define CERES_INTERNAL_ITERATIVE_SCHUR_COMPLEMENT_SOLVER_H_
+#include <memory>
#include "ceres/linear_solver.h"
#include "ceres/internal/eigen.h"
-#include "ceres/internal/scoped_ptr.h"
#include "ceres/types.h"
namespace ceres {
@@ -70,20 +70,24 @@ class Preconditioner;
class IterativeSchurComplementSolver : public BlockSparseMatrixSolver {
public:
explicit IterativeSchurComplementSolver(const LinearSolver::Options& options);
+ IterativeSchurComplementSolver(const IterativeSchurComplementSolver&) = delete;
+ void operator=(const IterativeSchurComplementSolver&) = delete;
+
virtual ~IterativeSchurComplementSolver();
private:
- virtual LinearSolver::Summary SolveImpl(
+ LinearSolver::Summary SolveImpl(
BlockSparseMatrix* A,
const double* b,
const LinearSolver::PerSolveOptions& options,
- double* x);
+ double* x) final;
+
+ void CreatePreconditioner(BlockSparseMatrix* A);
LinearSolver::Options options_;
- scoped_ptr<internal::ImplicitSchurComplement> schur_complement_;
- scoped_ptr<Preconditioner> preconditioner_;
+ std::unique_ptr<internal::ImplicitSchurComplement> schur_complement_;
+ std::unique_ptr<Preconditioner> preconditioner_;
Vector reduced_linear_system_solution_;
- CERES_DISALLOW_COPY_AND_ASSIGN(IterativeSchurComplementSolver);
};
} // namespace internal
diff --git a/extern/ceres/internal/ceres/lapack.cc b/extern/ceres/internal/ceres/lapack.cc
index 6fc23f4e658..37efbcd27d1 100644
--- a/extern/ceres/internal/ceres/lapack.cc
+++ b/extern/ceres/internal/ceres/lapack.cc
@@ -34,6 +34,7 @@
#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,
@@ -61,7 +62,7 @@ extern "C" void dgels_(char* uplo,
double* work,
int* lwork,
int* info);
-
+#endif
namespace ceres {
namespace internal {
diff --git a/extern/ceres/internal/ceres/levenberg_marquardt_strategy.cc b/extern/ceres/internal/ceres/levenberg_marquardt_strategy.cc
index e9833805ef5..9eec631e6dd 100644
--- a/extern/ceres/internal/ceres/levenberg_marquardt_strategy.cc
+++ b/extern/ceres/internal/ceres/levenberg_marquardt_strategy.cc
@@ -30,7 +30,9 @@
#include "ceres/levenberg_marquardt_strategy.h"
+#include <algorithm>
#include <cmath>
+
#include "Eigen/Core"
#include "ceres/array_utils.h"
#include "ceres/internal/eigen.h"
@@ -53,7 +55,7 @@ LevenbergMarquardtStrategy::LevenbergMarquardtStrategy(
max_diagonal_(options.max_lm_diagonal),
decrease_factor_(2.0),
reuse_diagonal_(false) {
- CHECK_NOTNULL(linear_solver_);
+ CHECK(linear_solver_ != nullptr);
CHECK_GT(min_diagonal_, 0.0);
CHECK_LE(min_diagonal_, max_diagonal_);
CHECK_GT(max_radius_, 0.0);
@@ -67,9 +69,9 @@ TrustRegionStrategy::Summary LevenbergMarquardtStrategy::ComputeStep(
SparseMatrix* jacobian,
const double* residuals,
double* step) {
- CHECK_NOTNULL(jacobian);
- CHECK_NOTNULL(residuals);
- CHECK_NOTNULL(step);
+ CHECK(jacobian != nullptr);
+ CHECK(residuals != nullptr);
+ CHECK(step != nullptr);
const int num_parameters = jacobian->num_cols();
if (!reuse_diagonal_) {
@@ -98,7 +100,7 @@ TrustRegionStrategy::Summary LevenbergMarquardtStrategy::ComputeStep(
// Invalidate the output array lm_step, so that we can detect if
// the linear solver generated numerical garbage. This is known
// to happen for the DENSE_QR and then DENSE_SCHUR solver when
- // the Jacobin is severly rank deficient and mu is too small.
+ // the Jacobin is severely rank deficient and mu is too small.
InvalidateArray(num_parameters, step);
// Instead of solving Jx = -r, solve Jy = r.
diff --git a/extern/ceres/internal/ceres/levenberg_marquardt_strategy.h b/extern/ceres/internal/ceres/levenberg_marquardt_strategy.h
index c87a016c8f4..8fb37f32959 100644
--- a/extern/ceres/internal/ceres/levenberg_marquardt_strategy.h
+++ b/extern/ceres/internal/ceres/levenberg_marquardt_strategy.h
@@ -49,14 +49,14 @@ class LevenbergMarquardtStrategy : public TrustRegionStrategy {
virtual ~LevenbergMarquardtStrategy();
// TrustRegionStrategy interface
- virtual TrustRegionStrategy::Summary ComputeStep(
+ TrustRegionStrategy::Summary ComputeStep(
const TrustRegionStrategy::PerSolveOptions& per_solve_options,
SparseMatrix* jacobian,
const double* residuals,
- double* step);
- virtual void StepAccepted(double step_quality);
- virtual void StepRejected(double step_quality);
- virtual void StepIsInvalid() {
+ double* step) final;
+ void StepAccepted(double step_quality) final;
+ void StepRejected(double step_quality) final;
+ void StepIsInvalid() final {
// Treat the current step as a rejected step with no increase in
// solution quality. Since rejected steps lead to decrease in the
// size of the trust region, the next time ComputeStep is called,
@@ -64,7 +64,7 @@ class LevenbergMarquardtStrategy : public TrustRegionStrategy {
StepRejected(0.0);
}
- virtual double Radius() const;
+ double Radius() const final;
private:
LinearSolver* linear_solver_;
diff --git a/extern/ceres/internal/ceres/line_search.cc b/extern/ceres/internal/ceres/line_search.cc
index 9cdcb7b77e5..352c64f5057 100644
--- a/extern/ceres/internal/ceres/line_search.cc
+++ b/extern/ceres/internal/ceres/line_search.cc
@@ -30,17 +30,19 @@
#include "ceres/line_search.h"
+#include <algorithm>
+#include <cmath>
#include <iomanip>
#include <iostream> // NOLINT
-#include "glog/logging.h"
#include "ceres/evaluator.h"
+#include "ceres/function_sample.h"
#include "ceres/internal/eigen.h"
-#include "ceres/fpclassify.h"
#include "ceres/map_util.h"
#include "ceres/polynomial.h"
#include "ceres/stringprintf.h"
#include "ceres/wall_time.h"
+#include "glog/logging.h"
namespace ceres {
namespace internal {
@@ -53,30 +55,8 @@ using std::vector;
namespace {
// Precision used for floating point values in error message output.
const int kErrorMessageNumericPrecision = 8;
-
-FunctionSample ValueSample(const double x, const double value) {
- FunctionSample sample;
- sample.x = x;
- sample.value = value;
- sample.value_is_valid = true;
- return sample;
-}
-
-FunctionSample ValueAndGradientSample(const double x,
- const double value,
- const double gradient) {
- FunctionSample sample;
- sample.x = x;
- sample.value = value;
- sample.gradient = gradient;
- sample.value_is_valid = true;
- sample.gradient_is_valid = true;
- return sample;
-}
-
} // namespace
-
ostream& operator<<(ostream &os, const FunctionSample& sample);
// Convenience stream operator for pushing FunctionSamples into log messages.
@@ -112,9 +92,7 @@ LineSearchFunction::LineSearchFunction(Evaluator* evaluator)
: evaluator_(evaluator),
position_(evaluator->NumParameters()),
direction_(evaluator->NumEffectiveParameters()),
- evaluation_point_(evaluator->NumParameters()),
scaled_direction_(evaluator->NumEffectiveParameters()),
- gradient_(evaluator->NumEffectiveParameters()),
initial_evaluator_residual_time_in_seconds(0.0),
initial_evaluator_jacobian_time_in_seconds(0.0) {}
@@ -124,27 +102,48 @@ void LineSearchFunction::Init(const Vector& position,
direction_ = direction;
}
-bool LineSearchFunction::Evaluate(double x, double* f, double* g) {
- scaled_direction_ = x * direction_;
+void LineSearchFunction::Evaluate(const double x,
+ const bool evaluate_gradient,
+ FunctionSample* output) {
+ output->x = x;
+ output->vector_x_is_valid = false;
+ output->value_is_valid = false;
+ output->gradient_is_valid = false;
+ output->vector_gradient_is_valid = false;
+
+ scaled_direction_ = output->x * direction_;
+ output->vector_x.resize(position_.rows(), 1);
if (!evaluator_->Plus(position_.data(),
scaled_direction_.data(),
- evaluation_point_.data())) {
- return false;
+ output->vector_x.data())) {
+ return;
}
+ output->vector_x_is_valid = true;
- if (g == NULL) {
- return (evaluator_->Evaluate(evaluation_point_.data(),
- f, NULL, NULL, NULL) &&
- IsFinite(*f));
+ double* gradient = NULL;
+ 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);
- if (!evaluator_->Evaluate(evaluation_point_.data(),
- f, NULL, gradient_.data(), NULL)) {
- return false;
+ if (!eval_status || !std::isfinite(output->value)) {
+ return;
+ }
+
+ output->value_is_valid = true;
+ if (!evaluate_gradient) {
+ return;
+ }
+
+ output->gradient = direction_.dot(output->vector_gradient);
+ if (!std::isfinite(output->gradient)) {
+ return;
}
- *g = direction_.dot(gradient_);
- return IsFinite(*f) && IsFinite(*g);
+ output->gradient_is_valid = true;
+ output->vector_gradient_is_valid = true;
}
double LineSearchFunction::DirectionInfinityNorm() const {
@@ -152,21 +151,28 @@ double LineSearchFunction::DirectionInfinityNorm() const {
}
void LineSearchFunction::ResetTimeStatistics() {
- const map<string, double> evaluator_time_statistics =
- evaluator_->TimeStatistics();
+ const map<string, CallStatistics> evaluator_statistics =
+ evaluator_->Statistics();
+
initial_evaluator_residual_time_in_seconds =
- FindWithDefault(evaluator_time_statistics, "Evaluator::Residual", 0.0);
+ FindWithDefault(
+ evaluator_statistics, "Evaluator::Residual", CallStatistics())
+ .time;
initial_evaluator_jacobian_time_in_seconds =
- FindWithDefault(evaluator_time_statistics, "Evaluator::Jacobian", 0.0);
+ FindWithDefault(
+ evaluator_statistics, "Evaluator::Jacobian", CallStatistics())
+ .time;
}
void LineSearchFunction::TimeStatistics(
double* cost_evaluation_time_in_seconds,
double* gradient_evaluation_time_in_seconds) const {
- const map<string, double> evaluator_time_statistics =
- evaluator_->TimeStatistics();
+ const map<string, CallStatistics> evaluator_time_statistics =
+ evaluator_->Statistics();
*cost_evaluation_time_in_seconds =
- FindWithDefault(evaluator_time_statistics, "Evaluator::Residual", 0.0) -
+ FindWithDefault(
+ evaluator_time_statistics, "Evaluator::Residual", CallStatistics())
+ .time -
initial_evaluator_residual_time_in_seconds;
// Strictly speaking this will slightly underestimate the time spent
// evaluating the gradient of the line search univariate cost function as it
@@ -175,7 +181,9 @@ void LineSearchFunction::TimeStatistics(
// allows direct subtraction of the timing information from the totals for
// the evaluator returned in the solver summary.
*gradient_evaluation_time_in_seconds =
- FindWithDefault(evaluator_time_statistics, "Evaluator::Jacobian", 0.0) -
+ FindWithDefault(
+ evaluator_time_statistics, "Evaluator::Jacobian", CallStatistics())
+ .time -
initial_evaluator_jacobian_time_in_seconds;
}
@@ -184,12 +192,12 @@ void LineSearch::Search(double step_size_estimate,
double initial_gradient,
Summary* summary) const {
const double start_time = WallTimeInSeconds();
- *CHECK_NOTNULL(summary) = LineSearch::Summary();
+ CHECK(summary != nullptr);
+ *summary = LineSearch::Summary();
summary->cost_evaluation_time_in_seconds = 0.0;
summary->gradient_evaluation_time_in_seconds = 0.0;
summary->polynomial_minimization_time_in_seconds = 0.0;
-
options().function->ResetTimeStatistics();
this->DoSearch(step_size_estimate, initial_cost, initial_gradient, summary);
options().function->
@@ -243,12 +251,12 @@ double LineSearch::InterpolatingPolynomialMinimizingStepSize(
if (interpolation_type == QUADRATIC) {
// Two point interpolation using function values and the
// gradient at the lower bound.
- samples.push_back(ValueSample(current.x, current.value));
+ samples.push_back(FunctionSample(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(ValueSample(previous.x, previous.value));
+ samples.push_back(FunctionSample(previous.x, previous.value));
}
} else if (interpolation_type == CUBIC) {
// Two point interpolation using the function values and the gradients.
@@ -286,34 +294,26 @@ void ArmijoLineSearch::DoSearch(const double step_size_estimate,
// Note initial_cost & initial_gradient are evaluated at step_size = 0,
// not step_size_estimate, which is our starting guess.
- const FunctionSample initial_position =
- ValueAndGradientSample(0.0, initial_cost, initial_gradient);
+ FunctionSample initial_position(0.0, initial_cost, initial_gradient);
+ initial_position.vector_x = function->position();
+ initial_position.vector_x_is_valid = true;
- FunctionSample previous = ValueAndGradientSample(0.0, 0.0, 0.0);
- previous.value_is_valid = false;
-
- FunctionSample current = ValueAndGradientSample(step_size_estimate, 0.0, 0.0);
- current.value_is_valid = false;
+ const double descent_direction_max_norm = function->DirectionInfinityNorm();
+ FunctionSample previous;
+ FunctionSample current;
// As the Armijo line search algorithm always uses the initial point, for
// which both the function value and derivative are known, when fitting a
// minimizing polynomial, we can fit up to a quadratic without requiring the
// gradient at the current query point.
- const bool interpolation_uses_gradient_at_current_sample =
- options().interpolation_type == CUBIC;
- const double descent_direction_max_norm = function->DirectionInfinityNorm();
+ const bool kEvaluateGradient = options().interpolation_type == CUBIC;
++summary->num_function_evaluations;
- if (interpolation_uses_gradient_at_current_sample) {
+ if (kEvaluateGradient) {
++summary->num_gradient_evaluations;
}
- current.value_is_valid =
- function->Evaluate(current.x,
- &current.value,
- interpolation_uses_gradient_at_current_sample
- ? &current.gradient : NULL);
- current.gradient_is_valid =
- interpolation_uses_gradient_at_current_sample && current.value_is_valid;
+
+ function->Evaluate(step_size_estimate, kEvaluateGradient, &current);
while (!current.value_is_valid ||
current.value > (initial_cost
+ options().sufficient_decrease
@@ -354,22 +354,16 @@ void ArmijoLineSearch::DoSearch(const double step_size_estimate,
}
previous = current;
- current.x = step_size;
++summary->num_function_evaluations;
- if (interpolation_uses_gradient_at_current_sample) {
+ if (kEvaluateGradient) {
++summary->num_gradient_evaluations;
}
- current.value_is_valid =
- function->Evaluate(current.x,
- &current.value,
- interpolation_uses_gradient_at_current_sample
- ? &current.gradient : NULL);
- current.gradient_is_valid =
- interpolation_uses_gradient_at_current_sample && current.value_is_valid;
+
+ function->Evaluate(step_size, kEvaluateGradient, &current);
}
- summary->optimal_step_size = current.x;
+ summary->optimal_point = current;
summary->success = true;
}
@@ -391,9 +385,9 @@ void WolfeLineSearch::DoSearch(const double step_size_estimate,
// Note initial_cost & initial_gradient are evaluated at step_size = 0,
// not step_size_estimate, which is our starting guess.
- const FunctionSample initial_position =
- ValueAndGradientSample(0.0, initial_cost, initial_gradient);
-
+ FunctionSample initial_position(0.0, initial_cost, initial_gradient);
+ initial_position.vector_x = options().function->position();
+ initial_position.vector_x_is_valid = true;
bool do_zoom_search = false;
// Important: The high/low in bracket_high & bracket_low refer to their
// _function_ values, not their step sizes i.e. it is _not_ required that
@@ -435,7 +429,7 @@ void WolfeLineSearch::DoSearch(const double step_size_estimate,
// produce a valid point when ArmijoLineSearch would succeed, we return the
// point with the lowest cost found thus far which satsifies the Armijo
// condition (but not the Wolfe conditions).
- summary->optimal_step_size = bracket_low.x;
+ summary->optimal_point = bracket_low;
summary->success = true;
return;
}
@@ -481,11 +475,13 @@ void WolfeLineSearch::DoSearch(const double step_size_estimate,
// satisfies the strong Wolfe curvature condition, that we return the point
// amongst those found thus far, which minimizes f() and satisfies the Armijo
// condition.
- solution =
- solution.value_is_valid && solution.value <= bracket_low.value
- ? solution : bracket_low;
- summary->optimal_step_size = solution.x;
+ if (!solution.value_is_valid || solution.value > bracket_low.value) {
+ summary->optimal_point = bracket_low;
+ } else {
+ summary->optimal_point = solution;
+ }
+
summary->success = true;
}
@@ -515,8 +511,7 @@ bool WolfeLineSearch::BracketingPhase(
LineSearchFunction* function = options().function;
FunctionSample previous = initial_position;
- FunctionSample current = ValueAndGradientSample(step_size_estimate, 0.0, 0.0);
- current.value_is_valid = false;
+ FunctionSample current;
const double descent_direction_max_norm =
function->DirectionInfinityNorm();
@@ -535,12 +530,8 @@ bool WolfeLineSearch::BracketingPhase(
// issues).
++summary->num_function_evaluations;
++summary->num_gradient_evaluations;
- current.value_is_valid =
- function->Evaluate(current.x,
- &current.value,
- &current.gradient);
- current.gradient_is_valid = current.value_is_valid;
-
+ const bool kEvaluateGradient = true;
+ function->Evaluate(step_size_estimate, kEvaluateGradient, &current);
while (true) {
++summary->num_iterations;
@@ -637,7 +628,18 @@ bool WolfeLineSearch::BracketingPhase(
// being a boundary of a bracket.
// If f(current) is valid, (but meets no criteria) expand the search by
- // increasing the step size.
+ // increasing the step size. If f(current) is invalid, contract the step
+ // size.
+ //
+ // In Nocedal & Wright [1] (p60), the step-size can only increase in the
+ // bracketing phase: step_size_{k+1} \in [step_size_k, step_size_k * factor].
+ // However this does not account for the function returning invalid values
+ // which we support, in which case we need to contract the step size whilst
+ // ensuring that we do not invert the bracket, i.e, we require that:
+ // step_size_{k-1} <= step_size_{k+1} < step_size_k.
+ const double min_step_size =
+ current.value_is_valid
+ ? current.x : previous.x;
const double max_step_size =
current.value_is_valid
? (current.x * options().max_step_expansion) : current.x;
@@ -656,7 +658,7 @@ bool WolfeLineSearch::BracketingPhase(
previous,
unused_previous,
current,
- previous.x,
+ min_step_size,
max_step_size);
summary->polynomial_minimization_time_in_seconds +=
(WallTimeInSeconds() - polynomial_minimization_start_time);
@@ -669,16 +671,14 @@ bool WolfeLineSearch::BracketingPhase(
return false;
}
+ // Only advance the lower boundary (in x) of the bracket if f(current)
+ // is valid such that we can support contracting the step size when
+ // f(current) is invalid without risking inverting the bracket in x, i.e.
+ // prevent previous.x > current.x.
previous = current.value_is_valid ? current : previous;
- current.x = step_size;
-
++summary->num_function_evaluations;
++summary->num_gradient_evaluations;
- current.value_is_valid =
- function->Evaluate(current.x,
- &current.value,
- &current.gradient);
- current.gradient_is_valid = current.value_is_valid;
+ function->Evaluate(step_size, kEvaluateGradient, &current);
}
// Ensure that even if a valid bracket was found, we will only mark a zoom
@@ -799,7 +799,7 @@ bool WolfeLineSearch::ZoomPhase(const FunctionSample& initial_position,
const FunctionSample unused_previous;
DCHECK(!unused_previous.value_is_valid);
const double polynomial_minimization_start_time = WallTimeInSeconds();
- solution->x =
+ const double step_size =
this->InterpolatingPolynomialMinimizingStepSize(
options().interpolation_type,
lower_bound_step,
@@ -823,12 +823,9 @@ bool WolfeLineSearch::ZoomPhase(const FunctionSample& initial_position,
// to numerical issues).
++summary->num_function_evaluations;
++summary->num_gradient_evaluations;
- solution->value_is_valid =
- function->Evaluate(solution->x,
- &solution->value,
- &solution->gradient);
- solution->gradient_is_valid = solution->value_is_valid;
- if (!solution->value_is_valid) {
+ const bool kEvaluateGradient = true;
+ function->Evaluate(step_size, kEvaluateGradient, solution);
+ if (!solution->value_is_valid || !solution->gradient_is_valid) {
summary->error =
StringPrintf("Line search failed: Wolfe Zoom phase found "
"step_size: %.5e, for which function is invalid, "
diff --git a/extern/ceres/internal/ceres/line_search.h b/extern/ceres/internal/ceres/line_search.h
index 6a21cbeac11..d59fd777367 100644
--- a/extern/ceres/internal/ceres/line_search.h
+++ b/extern/ceres/internal/ceres/line_search.h
@@ -35,6 +35,7 @@
#include <string>
#include <vector>
+#include "ceres/function_sample.h"
#include "ceres/internal/eigen.h"
#include "ceres/internal/port.h"
#include "ceres/types.h"
@@ -43,7 +44,6 @@ namespace ceres {
namespace internal {
class Evaluator;
-struct FunctionSample;
class LineSearchFunction;
// Line search is another name for a one dimensional optimization
@@ -51,7 +51,7 @@ class LineSearchFunction;
// dimensional optimization problems that arise as subproblems of
// general multidimensional optimization problems.
//
-// While finding the exact minimum of a one dimensionl function is
+// While finding the exact minimum of a one dimensional function is
// hard, instances of LineSearch find a point that satisfies a
// sufficient decrease condition. Depending on the particular
// condition used, we get a variety of different line search
@@ -61,21 +61,9 @@ class LineSearch {
struct Summary;
struct Options {
- Options()
- : interpolation_type(CUBIC),
- sufficient_decrease(1e-4),
- max_step_contraction(1e-3),
- min_step_contraction(0.9),
- min_step_size(1e-9),
- max_num_iterations(20),
- sufficient_curvature_decrease(0.9),
- max_step_expansion(10.0),
- is_silent(false),
- function(NULL) {}
-
// Degree of the polynomial used to approximate the objective
// function.
- LineSearchInterpolationType interpolation_type;
+ LineSearchInterpolationType interpolation_type = CUBIC;
// Armijo and Wolfe line search parameters.
@@ -88,7 +76,7 @@ class LineSearch {
// s.t.
//
// f(step_size) <= f(0) + sufficient_decrease * f'(0) * step_size
- double sufficient_decrease;
+ double sufficient_decrease = 1e-4;
// In each iteration of the Armijo / Wolfe line search,
//
@@ -98,7 +86,7 @@ class LineSearch {
//
// 0 < max_step_contraction < min_step_contraction < 1
//
- double max_step_contraction;
+ double max_step_contraction = 1e-3;
// In each iteration of the Armijo / Wolfe line search,
//
@@ -107,16 +95,16 @@ class LineSearch {
//
// 0 < max_step_contraction < min_step_contraction < 1
//
- double min_step_contraction;
+ double min_step_contraction = 0.9;
// If during the line search, the step_size falls below this
// value, it is truncated to zero.
- double min_step_size;
+ double min_step_size = 1e-9;
// Maximum number of trial step size iterations during each line search,
// if a step size satisfying the search conditions cannot be found within
// this number of trials, the line search will terminate.
- int max_num_iterations;
+ int max_num_iterations = 20;
// Wolfe-specific line search parameters.
@@ -131,7 +119,7 @@ class LineSearch {
//
// Where f() is the line search objective and f'() is the derivative
// of f w.r.t step_size (d f / d step_size).
- double sufficient_curvature_decrease;
+ double sufficient_curvature_decrease = 0.9;
// During the bracketing phase of the Wolfe search, the step size is
// increased until either a point satisfying the Wolfe conditions is
@@ -142,43 +130,32 @@ class LineSearch {
// new_step_size <= max_step_expansion * step_size.
//
// By definition for expansion, max_step_expansion > 1.0.
- double max_step_expansion;
+ double max_step_expansion = 10;
- bool is_silent;
+ bool is_silent = false;
// The one dimensional function that the line search algorithm
// minimizes.
- LineSearchFunction* function;
+ LineSearchFunction* function = nullptr;
};
// Result of the line search.
struct Summary {
- Summary()
- : success(false),
- optimal_step_size(0.0),
- num_function_evaluations(0),
- num_gradient_evaluations(0),
- num_iterations(0),
- cost_evaluation_time_in_seconds(-1.0),
- gradient_evaluation_time_in_seconds(-1.0),
- polynomial_minimization_time_in_seconds(-1.0),
- total_time_in_seconds(-1.0) {}
-
- bool success;
- double optimal_step_size;
- int num_function_evaluations;
- int num_gradient_evaluations;
- int num_iterations;
+ bool success = false;
+ FunctionSample optimal_point;
+ int num_function_evaluations = 0;
+ int num_gradient_evaluations = 0;
+ int num_iterations = 0;
// Cumulative time spent evaluating the value of the cost function across
// all iterations.
- double cost_evaluation_time_in_seconds;
+ double cost_evaluation_time_in_seconds = 0.0;
// Cumulative time spent evaluating the gradient of the cost function across
// all iterations.
- double gradient_evaluation_time_in_seconds;
+ double gradient_evaluation_time_in_seconds = 0.0;
// Cumulative time spent minimizing the interpolating polynomial to compute
// the next candidate step size across all iterations.
- double polynomial_minimization_time_in_seconds;
- double total_time_in_seconds;
+ double polynomial_minimization_time_in_seconds = 0.0;
+ double total_time_in_seconds = 0.0;
std::string error;
};
@@ -234,6 +211,7 @@ class LineSearchFunction {
public:
explicit LineSearchFunction(Evaluator* evaluator);
void Init(const Vector& position, const Vector& direction);
+
// Evaluate the line search objective
//
// f(x) = p(position + x * direction)
@@ -241,27 +219,29 @@ class LineSearchFunction {
// Where, p is the objective function of the general optimization
// problem.
//
- // g is the gradient f'(x) at x.
+ // evaluate_gradient controls whether the gradient will be evaluated
+ // or not.
//
- // f must not be null. The gradient is computed only if g is not null.
- bool Evaluate(double x, double* f, double* g);
+ // On return output->*_is_valid indicate indicate whether the
+ // corresponding fields have numerically valid values or not.
+ void Evaluate(double x, bool evaluate_gradient, FunctionSample* output);
+
double DirectionInfinityNorm() const;
+
// Resets to now, the start point for the results from TimeStatistics().
void ResetTimeStatistics();
void TimeStatistics(double* cost_evaluation_time_in_seconds,
double* gradient_evaluation_time_in_seconds) const;
+ const Vector& position() const { return position_; }
+ const Vector& direction() const { return direction_; }
private:
Evaluator* evaluator_;
Vector position_;
Vector direction_;
- // evaluation_point = Evaluator::Plus(position_, x * direction_);
- Vector evaluation_point_;
-
// scaled_direction = x * direction_;
Vector scaled_direction_;
- Vector gradient_;
// We may not exclusively own the evaluator (e.g. in the Trust Region
// minimizer), hence we need to save the initial evaluation durations for the
@@ -282,10 +262,10 @@ class ArmijoLineSearch : public LineSearch {
virtual ~ArmijoLineSearch() {}
private:
- virtual void DoSearch(double step_size_estimate,
- double initial_cost,
- double initial_gradient,
- Summary* summary) const;
+ void DoSearch(double step_size_estimate,
+ double initial_cost,
+ double initial_gradient,
+ Summary* summary) const final;
};
// Bracketing / Zoom Strong Wolfe condition line search. This implementation
@@ -315,10 +295,10 @@ class WolfeLineSearch : public LineSearch {
Summary* summary) const;
private:
- virtual void DoSearch(double step_size_estimate,
- double initial_cost,
- double initial_gradient,
- Summary* summary) const;
+ void DoSearch(double step_size_estimate,
+ double initial_cost,
+ double initial_gradient,
+ Summary* summary) const final;
};
} // namespace internal
diff --git a/extern/ceres/internal/ceres/line_search_minimizer.cc b/extern/ceres/internal/ceres/line_search_minimizer.cc
index fdde1ca9c86..931f56c960c 100644
--- a/extern/ceres/internal/ceres/line_search_minimizer.cc
+++ b/extern/ceres/internal/ceres/line_search_minimizer.cc
@@ -43,6 +43,7 @@
#include <algorithm>
#include <cstdlib>
#include <cmath>
+#include <memory>
#include <string>
#include <vector>
@@ -51,7 +52,6 @@
#include "ceres/evaluator.h"
#include "ceres/internal/eigen.h"
#include "ceres/internal/port.h"
-#include "ceres/internal/scoped_ptr.h"
#include "ceres/line_search.h"
#include "ceres/line_search_direction.h"
#include "ceres/stringprintf.h"
@@ -63,27 +63,14 @@ namespace ceres {
namespace internal {
namespace {
-// TODO(sameeragarwal): I think there is a small bug here, in that if
-// the evaluation fails, then the state can contain garbage. Look at
-// this more carefully.
-bool Evaluate(Evaluator* evaluator,
- const Vector& x,
- LineSearchMinimizer::State* state,
- std::string* message) {
- if (!evaluator->Evaluate(x.data(),
- &(state->cost),
- NULL,
- state->gradient.data(),
- NULL)) {
- *message = "Gradient evaluation failed.";
- return false;
- }
-
+bool EvaluateGradientNorms(Evaluator* evaluator,
+ const Vector& x,
+ LineSearchMinimizer::State* state,
+ std::string* message) {
Vector negative_gradient = -state->gradient;
Vector projected_gradient_step(x.size());
- if (!evaluator->Plus(x.data(),
- negative_gradient.data(),
- projected_gradient_step.data())) {
+ if (!evaluator->Plus(
+ x.data(), negative_gradient.data(), projected_gradient_step.data())) {
*message = "projected_gradient_step = Plus(x, -gradient) failed.";
return false;
}
@@ -103,7 +90,8 @@ void LineSearchMinimizer::Minimize(const Minimizer::Options& options,
double start_time = WallTimeInSeconds();
double iteration_start_time = start_time;
- Evaluator* evaluator = CHECK_NOTNULL(options.evaluator.get());
+ CHECK(options.evaluator != nullptr);
+ Evaluator* evaluator = options.evaluator.get();
const int num_parameters = evaluator->NumParameters();
const int num_effective_parameters = evaluator->NumEffectiveParameters();
@@ -116,9 +104,6 @@ void LineSearchMinimizer::Minimize(const Minimizer::Options& options,
State current_state(num_parameters, num_effective_parameters);
State previous_state(num_parameters, num_effective_parameters);
- Vector delta(num_effective_parameters);
- Vector x_plus_delta(num_parameters);
-
IterationSummary iteration_summary;
iteration_summary.iteration = 0;
iteration_summary.step_is_valid = false;
@@ -130,8 +115,19 @@ void LineSearchMinimizer::Minimize(const Minimizer::Options& options,
iteration_summary.linear_solver_iterations = 0;
iteration_summary.step_solver_time_in_seconds = 0;
- // Do initial cost and Jacobian evaluation.
- if (!Evaluate(evaluator, x, &current_state, &summary->message)) {
+ // Do initial cost and gradient evaluation.
+ if (!evaluator->Evaluate(x.data(),
+ &(current_state.cost),
+ nullptr,
+ current_state.gradient.data(),
+ nullptr)) {
+ summary->termination_type = FAILURE;
+ summary->message = "Initial cost and jacobian evaluation failed.";
+ LOG_IF(WARNING, is_not_silent) << "Terminating: " << summary->message;
+ return;
+ }
+
+ if (!EvaluateGradientNorms(evaluator, x, &current_state, &summary->message)) {
summary->termination_type = FAILURE;
summary->message = "Initial cost and jacobian evaluation failed. "
"More details: " + summary->message;
@@ -142,9 +138,8 @@ void LineSearchMinimizer::Minimize(const Minimizer::Options& options,
summary->initial_cost = current_state.cost + summary->fixed_cost;
iteration_summary.cost = current_state.cost + summary->fixed_cost;
- iteration_summary.gradient_max_norm = current_state.gradient_max_norm;
iteration_summary.gradient_norm = sqrt(current_state.gradient_squared_norm);
-
+ iteration_summary.gradient_max_norm = current_state.gradient_max_norm;
if (iteration_summary.gradient_max_norm <= options.gradient_tolerance) {
summary->message = StringPrintf("Gradient tolerance reached. "
"Gradient max norm: %e <= %e",
@@ -170,7 +165,7 @@ 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;
- scoped_ptr<LineSearchDirection> line_search_direction(
+ std::unique_ptr<LineSearchDirection> line_search_direction(
LineSearchDirection::Create(line_search_direction_options));
LineSearchFunction line_search_function(evaluator);
@@ -194,11 +189,11 @@ void LineSearchMinimizer::Minimize(const Minimizer::Options& options,
line_search_options.is_silent = options.is_silent;
line_search_options.function = &line_search_function;
- scoped_ptr<LineSearch>
+ std::unique_ptr<LineSearch>
line_search(LineSearch::Create(options.line_search_type,
line_search_options,
&summary->message));
- if (line_search.get() == NULL) {
+ if (line_search.get() == nullptr) {
summary->termination_type = FAILURE;
LOG_IF(ERROR, is_not_silent) << "Terminating: " << summary->message;
return;
@@ -326,28 +321,37 @@ void LineSearchMinimizer::Minimize(const Minimizer::Options& options,
break;
}
- current_state.step_size = line_search_summary.optimal_step_size;
- delta = current_state.step_size * current_state.search_direction;
-
+ const FunctionSample& optimal_point = line_search_summary.optimal_point;
+ CHECK(optimal_point.vector_x_is_valid)
+ << "Congratulations, you found a bug in Ceres. Please report it.";
+ current_state.step_size = optimal_point.x;
previous_state = current_state;
iteration_summary.step_solver_time_in_seconds =
WallTimeInSeconds() - iteration_start_time;
- const double x_norm = x.norm();
-
- if (!evaluator->Plus(x.data(), delta.data(), x_plus_delta.data())) {
- summary->termination_type = FAILURE;
- summary->message =
- "x_plus_delta = Plus(x, delta) failed. This should not happen "
- "as the step was valid when it was selected by the line search.";
- LOG_IF(WARNING, is_not_silent) << "Terminating: " << summary->message;
- break;
+ if (optimal_point.vector_gradient_is_valid) {
+ current_state.cost = optimal_point.value;
+ current_state.gradient = optimal_point.vector_gradient;
+ } else {
+ Evaluator::EvaluateOptions evaluate_options;
+ evaluate_options.new_evaluation_point = false;
+ if (!evaluator->Evaluate(evaluate_options,
+ optimal_point.vector_x.data(),
+ &(current_state.cost),
+ nullptr,
+ current_state.gradient.data(),
+ nullptr)) {
+ summary->termination_type = FAILURE;
+ summary->message = "Cost and jacobian evaluation failed.";
+ LOG_IF(WARNING, is_not_silent) << "Terminating: " << summary->message;
+ return;
+ }
}
- if (!Evaluate(evaluator,
- x_plus_delta,
- &current_state,
- &summary->message)) {
+ if (!EvaluateGradientNorms(evaluator,
+ optimal_point.vector_x,
+ &current_state,
+ &summary->message)) {
summary->termination_type = FAILURE;
summary->message =
"Step failed to evaluate. This should not happen as the step was "
@@ -358,8 +362,9 @@ void LineSearchMinimizer::Minimize(const Minimizer::Options& options,
}
// Compute the norm of the step in the ambient space.
- iteration_summary.step_norm = (x_plus_delta - x).norm();
- x = x_plus_delta;
+ iteration_summary.step_norm = (optimal_point.vector_x - x).norm();
+ const double x_norm = x.norm();
+ x = optimal_point.vector_x;
iteration_summary.gradient_max_norm = current_state.gradient_max_norm;
iteration_summary.gradient_norm = sqrt(current_state.gradient_squared_norm);
@@ -380,6 +385,7 @@ void LineSearchMinimizer::Minimize(const Minimizer::Options& options,
iteration_summary.cumulative_time_in_seconds =
WallTimeInSeconds() - start_time
+ summary->preprocessor_time_in_seconds;
+ summary->iterations.push_back(iteration_summary);
// Iterations inside the line search algorithm are considered
// 'steps' in the broader context, to distinguish these inner
@@ -423,20 +429,18 @@ void LineSearchMinimizer::Minimize(const Minimizer::Options& options,
}
const double absolute_function_tolerance =
- options.function_tolerance * previous_state.cost;
- if (fabs(iteration_summary.cost_change) <= absolute_function_tolerance) {
- summary->message =
- StringPrintf("Function tolerance reached. "
- "|cost_change|/cost: %e <= %e",
- fabs(iteration_summary.cost_change) /
- previous_state.cost,
- options.function_tolerance);
+ options.function_tolerance * std::abs(previous_state.cost);
+ if (std::abs(iteration_summary.cost_change) <=
+ absolute_function_tolerance) {
+ summary->message = StringPrintf(
+ "Function tolerance reached. "
+ "|cost_change|/cost: %e <= %e",
+ std::abs(iteration_summary.cost_change) / previous_state.cost,
+ options.function_tolerance);
summary->termination_type = CONVERGENCE;
VLOG_IF(1, is_not_silent) << "Terminating: " << summary->message;
break;
}
-
- summary->iterations.push_back(iteration_summary);
}
}
diff --git a/extern/ceres/internal/ceres/line_search_minimizer.h b/extern/ceres/internal/ceres/line_search_minimizer.h
index 54b7202e0c3..191128a933b 100644
--- a/extern/ceres/internal/ceres/line_search_minimizer.h
+++ b/extern/ceres/internal/ceres/line_search_minimizer.h
@@ -66,9 +66,9 @@ class LineSearchMinimizer : public Minimizer {
};
~LineSearchMinimizer() {}
- virtual void Minimize(const Minimizer::Options& options,
- double* parameters,
- Solver::Summary* summary);
+ void Minimize(const Minimizer::Options& options,
+ double* parameters,
+ Solver::Summary* summary) final;
};
} // namespace internal
diff --git a/extern/ceres/internal/ceres/line_search_preprocessor.cc b/extern/ceres/internal/ceres/line_search_preprocessor.cc
index 831f5e8d079..5a21809e509 100644
--- a/extern/ceres/internal/ceres/line_search_preprocessor.cc
+++ b/extern/ceres/internal/ceres/line_search_preprocessor.cc
@@ -32,6 +32,8 @@
#include <numeric>
#include <string>
+#include "ceres/casts.h"
+#include "ceres/context_impl.h"
#include "ceres/evaluator.h"
#include "ceres/minimizer.h"
#include "ceres/problem_impl.h"
@@ -57,6 +59,9 @@ bool SetupEvaluator(PreprocessedProblem* pp) {
pp->evaluator_options.linear_solver_type = CGNR;
pp->evaluator_options.num_eliminate_blocks = 0;
pp->evaluator_options.num_threads = pp->options.num_threads;
+ 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));
@@ -71,7 +76,7 @@ LineSearchPreprocessor::~LineSearchPreprocessor() {
bool LineSearchPreprocessor::Preprocess(const Solver::Options& options,
ProblemImpl* problem,
PreprocessedProblem* pp) {
- CHECK_NOTNULL(pp);
+ CHECK(pp != nullptr);
pp->options = options;
ChangeNumThreadsIfNeeded(&pp->options);
diff --git a/extern/ceres/internal/ceres/line_search_preprocessor.h b/extern/ceres/internal/ceres/line_search_preprocessor.h
index 132d83a0a9a..12ccb53e011 100644
--- a/extern/ceres/internal/ceres/line_search_preprocessor.h
+++ b/extern/ceres/internal/ceres/line_search_preprocessor.h
@@ -39,9 +39,9 @@ namespace internal {
class LineSearchPreprocessor : public Preprocessor {
public:
virtual ~LineSearchPreprocessor();
- virtual bool Preprocess(const Solver::Options& options,
- ProblemImpl* problem,
- PreprocessedProblem* preprocessed_problem);
+ bool Preprocess(const Solver::Options& options,
+ ProblemImpl* problem,
+ PreprocessedProblem* preprocessed_problem) final;
};
} // namespace internal
diff --git a/extern/ceres/internal/ceres/linear_least_squares_problems.cc b/extern/ceres/internal/ceres/linear_least_squares_problems.cc
index 0a69375f7b5..7c523d399e1 100644
--- a/extern/ceres/internal/ceres/linear_least_squares_problems.cc
+++ b/extern/ceres/internal/ceres/linear_least_squares_problems.cc
@@ -31,13 +31,14 @@
#include "ceres/linear_least_squares_problems.h"
#include <cstdio>
+#include <memory>
#include <string>
#include <vector>
+
#include "ceres/block_sparse_matrix.h"
#include "ceres/block_structure.h"
#include "ceres/casts.h"
#include "ceres/file.h"
-#include "ceres/internal/scoped_ptr.h"
#include "ceres/stringprintf.h"
#include "ceres/triplet_sparse_matrix.h"
#include "ceres/types.h"
@@ -297,7 +298,7 @@ LinearLeastSquaresProblem* LinearLeastSquaresProblem2() {
problem->num_eliminate_blocks = 2;
CompressedRowBlockStructure* bs = new CompressedRowBlockStructure;
- scoped_array<double> values(new double[num_rows * num_cols]);
+ std::unique_ptr<double[]> values(new double[num_rows * num_cols]);
for (int c = 0; c < num_cols; ++c) {
bs->cols.push_back(Block());
@@ -431,7 +432,7 @@ LinearLeastSquaresProblem* LinearLeastSquaresProblem3() {
problem->num_eliminate_blocks = 2;
CompressedRowBlockStructure* bs = new CompressedRowBlockStructure;
- scoped_array<double> values(new double[num_rows * num_cols]);
+ std::unique_ptr<double[]> values(new double[num_rows * num_cols]);
for (int c = 0; c < num_cols; ++c) {
bs->cols.push_back(Block());
@@ -538,7 +539,7 @@ LinearLeastSquaresProblem* LinearLeastSquaresProblem4() {
problem->num_eliminate_blocks = 1;
CompressedRowBlockStructure* bs = new CompressedRowBlockStructure;
- scoped_array<double> values(new double[num_rows * num_cols]);
+ std::unique_ptr<double[]> values(new double[num_rows * num_cols]);
// Column block structure
bs->cols.push_back(Block());
@@ -613,7 +614,7 @@ bool DumpLinearLeastSquaresProblemToConsole(const SparseMatrix* A,
const double* b,
const double* x,
int num_eliminate_blocks) {
- CHECK_NOTNULL(A);
+ CHECK(A != nullptr);
Matrix AA;
A->ToDenseMatrix(&AA);
LOG(INFO) << "A^T: \n" << AA.transpose();
@@ -636,10 +637,10 @@ bool DumpLinearLeastSquaresProblemToConsole(const SparseMatrix* A,
void WriteArrayToFileOrDie(const string& filename,
const double* x,
const int size) {
- CHECK_NOTNULL(x);
+ CHECK(x != nullptr);
VLOG(2) << "Writing array to: " << filename;
FILE* fptr = fopen(filename.c_str(), "w");
- CHECK_NOTNULL(fptr);
+ CHECK(fptr != nullptr);
for (int i = 0; i < size; ++i) {
fprintf(fptr, "%17f\n", x[i]);
}
@@ -652,7 +653,7 @@ bool DumpLinearLeastSquaresProblemToTextFile(const string& filename_base,
const double* b,
const double* x,
int num_eliminate_blocks) {
- CHECK_NOTNULL(A);
+ CHECK(A != nullptr);
LOG(INFO) << "writing to: " << filename_base << "*";
string matlab_script;
@@ -666,7 +667,7 @@ bool DumpLinearLeastSquaresProblemToTextFile(const string& filename_base,
{
string filename = filename_base + "_A.txt";
FILE* fptr = fopen(filename.c_str(), "w");
- CHECK_NOTNULL(fptr);
+ CHECK(fptr != nullptr);
A->ToTextFile(fptr);
fclose(fptr);
StringAppendF(&matlab_script,
diff --git a/extern/ceres/internal/ceres/linear_least_squares_problems.h b/extern/ceres/internal/ceres/linear_least_squares_problems.h
index 384efb59a2b..5dfcd34e109 100644
--- a/extern/ceres/internal/ceres/linear_least_squares_problems.h
+++ b/extern/ceres/internal/ceres/linear_least_squares_problems.h
@@ -31,11 +31,11 @@
#ifndef CERES_INTERNAL_LINEAR_LEAST_SQUARES_PROBLEMS_H_
#define CERES_INTERNAL_LINEAR_LEAST_SQUARES_PROBLEMS_H_
+#include <memory>
#include <string>
#include <vector>
#include "ceres/sparse_matrix.h"
#include "ceres/internal/port.h"
-#include "ceres/internal/scoped_ptr.h"
namespace ceres {
namespace internal {
@@ -44,21 +44,20 @@ namespace internal {
// ground truth solutions. To be used by various LinearSolver tests.
struct LinearLeastSquaresProblem {
LinearLeastSquaresProblem()
- : A(NULL), b(NULL), D(NULL), num_eliminate_blocks(0),
- x(NULL), x_D(NULL) {
+ : num_eliminate_blocks(0) {
}
- scoped_ptr<SparseMatrix> A;
- scoped_array<double> b;
- scoped_array<double> D;
+ 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;
// Solution to min_x |Ax - b|^2
- scoped_array<double> x;
+ std::unique_ptr<double[]> x;
// Solution to min_x |Ax - b|^2 + |Dx|^2
- scoped_array<double> x_D;
+ std::unique_ptr<double[]> x_D;
};
// Factories for linear least squares problem.
diff --git a/extern/ceres/internal/ceres/linear_solver.cc b/extern/ceres/internal/ceres/linear_solver.cc
index 38e4625f747..107af6afcc8 100644
--- a/extern/ceres/internal/ceres/linear_solver.cc
+++ b/extern/ceres/internal/ceres/linear_solver.cc
@@ -35,6 +35,7 @@
#include "ceres/dense_qr_solver.h"
#include "ceres/iterative_schur_complement_solver.h"
#include "ceres/schur_complement_solver.h"
+#include "ceres/dynamic_sparse_normal_cholesky_solver.h"
#include "ceres/sparse_normal_cholesky_solver.h"
#include "ceres/types.h"
#include "glog/logging.h"
@@ -70,23 +71,25 @@ LinearSolverType LinearSolver::LinearSolverForZeroEBlocks(
}
LinearSolver* LinearSolver::Create(const LinearSolver::Options& options) {
+ CHECK(options.context != NULL);
+
switch (options.type) {
case CGNR:
return new CgnrSolver(options);
case SPARSE_NORMAL_CHOLESKY:
-#if defined(CERES_NO_SUITESPARSE) && \
- defined(CERES_NO_CXSPARSE) && \
- !defined(CERES_USE_EIGEN_SPARSE)
+#if defined(CERES_NO_SPARSE)
return NULL;
#else
+ if (options.dynamic_sparsity) {
+ return new DynamicSparseNormalCholeskySolver(options);
+ }
+
return new SparseNormalCholeskySolver(options);
#endif
case SPARSE_SCHUR:
-#if defined(CERES_NO_SUITESPARSE) && \
- defined(CERES_NO_CXSPARSE) && \
- !defined(CERES_USE_EIGEN_SPARSE)
+#if defined(CERES_NO_SPARSE)
return NULL;
#else
return new SparseSchurComplementSolver(options);
diff --git a/extern/ceres/internal/ceres/linear_solver.h b/extern/ceres/internal/ceres/linear_solver.h
index fb9332ca6e3..cb624b372dd 100644
--- a/extern/ceres/internal/ceres/linear_solver.h
+++ b/extern/ceres/internal/ceres/linear_solver.h
@@ -41,6 +41,7 @@
#include "ceres/block_sparse_matrix.h"
#include "ceres/casts.h"
#include "ceres/compressed_row_sparse_matrix.h"
+#include "ceres/context_impl.h"
#include "ceres/dense_sparse_matrix.h"
#include "ceres/execution_summary.h"
#include "ceres/triplet_sparse_matrix.h"
@@ -69,6 +70,16 @@ enum LinearSolverTerminationType {
LINEAR_SOLVER_FATAL_ERROR
};
+// This enum controls the fill-reducing ordering a sparse linear
+// algebra library should use before computing a sparse factorization
+// (usually Cholesky).
+enum OrderingType {
+ NATURAL, // Do not re-order the matrix. This is useful when the
+ // matrix has been ordered using a fill-reducing ordering
+ // already.
+ AMD // Use the Approximate Minimum Degree algorithm to re-order
+ // the matrix.
+};
class LinearOperator;
@@ -91,42 +102,25 @@ class LinearOperator;
class LinearSolver {
public:
struct Options {
- Options()
- : type(SPARSE_NORMAL_CHOLESKY),
- preconditioner_type(JACOBI),
- visibility_clustering_type(CANONICAL_VIEWS),
- dense_linear_algebra_library_type(EIGEN),
- sparse_linear_algebra_library_type(SUITE_SPARSE),
- use_postordering(false),
- dynamic_sparsity(false),
- use_explicit_schur_complement(false),
- min_num_iterations(1),
- max_num_iterations(1),
- num_threads(1),
- residual_reset_period(10),
- row_block_size(Eigen::Dynamic),
- e_block_size(Eigen::Dynamic),
- f_block_size(Eigen::Dynamic) {
- }
-
- LinearSolverType type;
- PreconditionerType preconditioner_type;
- VisibilityClusteringType visibility_clustering_type;
- DenseLinearAlgebraLibraryType dense_linear_algebra_library_type;
- SparseLinearAlgebraLibraryType sparse_linear_algebra_library_type;
+ LinearSolverType type = SPARSE_NORMAL_CHOLESKY;
+ PreconditionerType preconditioner_type = JACOBI;
+ VisibilityClusteringType visibility_clustering_type = CANONICAL_VIEWS;
+ DenseLinearAlgebraLibraryType dense_linear_algebra_library_type = EIGEN;
+ SparseLinearAlgebraLibraryType sparse_linear_algebra_library_type =
+ SUITE_SPARSE;
// See solver.h for information about these flags.
- bool use_postordering;
- bool dynamic_sparsity;
- bool use_explicit_schur_complement;
+ bool use_postordering = false;
+ bool dynamic_sparsity = false;
+ bool use_explicit_schur_complement = false;
// Number of internal iterations that the solver uses. This
// parameter only makes sense for iterative solvers like CG.
- int min_num_iterations;
- int max_num_iterations;
+ int min_num_iterations = 1;
+ int max_num_iterations = 1;
// If possible, how many threads can the solver use.
- int num_threads;
+ int num_threads = 1;
// Hints about the order in which the parameter blocks should be
// eliminated by the linear solver.
@@ -151,7 +145,7 @@ class LinearSolver {
// inaccurate over time. Thus for non-zero values of this
// parameter, the solver can be told to recalculate the value of
// the residual using a |b - Ax| evaluation.
- int residual_reset_period;
+ int residual_reset_period = 10;
// If the block sizes in a BlockSparseMatrix are fixed, then in
// some cases the Schur complement based solvers can detect and
@@ -162,20 +156,18 @@ class LinearSolver {
//
// Please see schur_complement_solver.h and schur_eliminator.h for
// more details.
- int row_block_size;
- int e_block_size;
- int f_block_size;
+ int row_block_size = Eigen::Dynamic;
+ int e_block_size = Eigen::Dynamic;
+ int f_block_size = Eigen::Dynamic;
+
+ bool use_mixed_precision_solves = false;
+ int max_num_refinement_iterations = 0;
+ int subset_preconditioner_start_row_block = -1;
+ ContextImpl* context = nullptr;
};
// Options for the Solve method.
struct PerSolveOptions {
- PerSolveOptions()
- : D(NULL),
- preconditioner(NULL),
- r_tolerance(0.0),
- q_tolerance(0.0) {
- }
-
// This option only makes sense for unsymmetric linear solvers
// that can solve rectangular linear systems.
//
@@ -199,7 +191,7 @@ class LinearSolver {
// does not have full column rank, the results returned by the
// solver cannot be relied on. D, if it is not null is an array of
// size n. b is an array of size m and x is an array of size n.
- double * D;
+ double* D = nullptr;
// This option only makes sense for iterative solvers.
//
@@ -221,7 +213,7 @@ class LinearSolver {
//
// A null preconditioner is equivalent to an identity matrix being
// used a preconditioner.
- LinearOperator* preconditioner;
+ LinearOperator* preconditioner = nullptr;
// The following tolerance related options only makes sense for
@@ -233,7 +225,7 @@ class LinearSolver {
//
// This is the most commonly used termination criterion for
// iterative solvers.
- double r_tolerance;
+ double r_tolerance = 0.0;
// For PSD matrices A, let
//
@@ -257,22 +249,16 @@ class LinearSolver {
// Journal of Computational and Applied Mathematics,
// 124(1-2), 45-59, 2000.
//
- double q_tolerance;
+ double q_tolerance = 0.0;
};
// Summary of a call to the Solve method. We should move away from
// the true/false method for determining solver success. We should
// let the summary object do the talking.
struct Summary {
- Summary()
- : residual_norm(0.0),
- num_iterations(-1),
- termination_type(LINEAR_SOLVER_FAILURE) {
- }
-
- double residual_norm;
- int num_iterations;
- LinearSolverTerminationType termination_type;
+ double residual_norm = -1.0;
+ int num_iterations = -1;
+ LinearSolverTerminationType termination_type = LINEAR_SOLVER_FAILURE;
std::string message;
};
@@ -292,16 +278,12 @@ class LinearSolver {
const PerSolveOptions& per_solve_options,
double* x) = 0;
- // The following two methods return copies instead of references so
- // that the base class implementation does not have to worry about
- // life time issues. Further, these calls are not expected to be
- // frequent or performance sensitive.
- virtual std::map<std::string, int> CallStatistics() const {
- return std::map<std::string, int>();
- }
-
- virtual std::map<std::string, double> TimeStatistics() const {
- return std::map<std::string, double>();
+ // This method returns copies instead of references so that the base
+ // class implementation does not have to worry about life time
+ // 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>();
}
// Factory
@@ -325,18 +307,14 @@ class TypedLinearSolver : public LinearSolver {
const LinearSolver::PerSolveOptions& per_solve_options,
double* x) {
ScopedExecutionTimer total_time("LinearSolver::Solve", &execution_summary_);
- CHECK_NOTNULL(A);
- CHECK_NOTNULL(b);
- CHECK_NOTNULL(x);
+ CHECK(A != nullptr);
+ CHECK(b != nullptr);
+ CHECK(x != nullptr);
return SolveImpl(down_cast<MatrixType*>(A), b, per_solve_options, x);
}
- virtual std::map<std::string, int> CallStatistics() const {
- return execution_summary_.calls();
- }
-
- virtual std::map<std::string, double> TimeStatistics() const {
- return execution_summary_.times();
+ virtual std::map<std::string, CallStatistics> Statistics() const {
+ return execution_summary_.statistics();
}
private:
diff --git a/extern/ceres/internal/ceres/local_parameterization.cc b/extern/ceres/internal/ceres/local_parameterization.cc
index a6bf1f6ddcc..97fdbed3eda 100644
--- a/extern/ceres/internal/ceres/local_parameterization.cc
+++ b/extern/ceres/internal/ceres/local_parameterization.cc
@@ -31,10 +31,11 @@
#include "ceres/local_parameterization.h"
#include <algorithm>
+
#include "Eigen/Geometry"
-#include "ceres/householder_vector.h"
#include "ceres/internal/eigen.h"
#include "ceres/internal/fixed_array.h"
+#include "ceres/internal/householder_vector.h"
#include "ceres/rotation.h"
#include "glog/logging.h"
@@ -42,13 +43,16 @@ namespace ceres {
using std::vector;
-LocalParameterization::~LocalParameterization() {
-}
+LocalParameterization::~LocalParameterization() {}
bool LocalParameterization::MultiplyByJacobian(const double* x,
const int num_rows,
const double* global_matrix,
double* local_matrix) const {
+ if (LocalSize() == 0) {
+ return true;
+ }
+
Matrix jacobian(GlobalSize(), LocalSize());
if (!ComputeJacobian(x, jacobian.data())) {
return false;
@@ -74,7 +78,7 @@ bool IdentityParameterization::Plus(const double* x,
bool IdentityParameterization::ComputeJacobian(const double* x,
double* jacobian) const {
- MatrixRef(jacobian, size_, size_) = Matrix::Identity(size_, size_);
+ MatrixRef(jacobian, size_, size_).setIdentity();
return true;
}
@@ -82,9 +86,8 @@ bool IdentityParameterization::MultiplyByJacobian(const double* x,
const int num_cols,
const double* global_matrix,
double* local_matrix) const {
- std::copy(global_matrix,
- global_matrix + num_cols * GlobalSize(),
- local_matrix);
+ std::copy(
+ global_matrix, global_matrix + num_cols * GlobalSize(), local_matrix);
return true;
}
@@ -93,8 +96,8 @@ SubsetParameterization::SubsetParameterization(
: local_size_(size - constant_parameters.size()), constancy_mask_(size, 0) {
vector<int> constant = constant_parameters;
std::sort(constant.begin(), constant.end());
- CHECK_GE(constant.front(), 0)
- << "Indices indicating constant parameter must be greater than zero.";
+ 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.";
@@ -108,7 +111,8 @@ SubsetParameterization::SubsetParameterization(
bool SubsetParameterization::Plus(const double* x,
const double* delta,
double* x_plus_delta) const {
- for (int i = 0, j = 0; i < constancy_mask_.size(); ++i) {
+ const int global_size = GlobalSize();
+ for (int i = 0, j = 0; i < global_size; ++i) {
if (constancy_mask_[i]) {
x_plus_delta[i] = x[i];
} else {
@@ -124,9 +128,10 @@ bool SubsetParameterization::ComputeJacobian(const double* x,
return true;
}
- MatrixRef m(jacobian, constancy_mask_.size(), local_size_);
+ const int global_size = GlobalSize();
+ MatrixRef m(jacobian, global_size, local_size_);
m.setZero();
- for (int i = 0, j = 0; i < constancy_mask_.size(); ++i) {
+ for (int i = 0, j = 0; i < global_size; ++i) {
if (!constancy_mask_[i]) {
m(i, j++) = 1.0;
}
@@ -135,18 +140,19 @@ bool SubsetParameterization::ComputeJacobian(const double* x,
}
bool SubsetParameterization::MultiplyByJacobian(const double* x,
- const int num_rows,
- const double* global_matrix,
- double* local_matrix) const {
+ const int num_cols,
+ const double* global_matrix,
+ double* local_matrix) const {
if (local_size_ == 0) {
return true;
}
- for (int row = 0; row < num_rows; ++row) {
- for (int col = 0, j = 0; col < constancy_mask_.size(); ++col) {
- if (!constancy_mask_[col]) {
- local_matrix[row * LocalSize() + j++] =
- global_matrix[row * GlobalSize() + col];
+ const int global_size = GlobalSize();
+ for (int col = 0; col < num_cols; ++col) {
+ for (int i = 0, j = 0; i < global_size; ++i) {
+ if (!constancy_mask_[i]) {
+ local_matrix[col * local_size_ + j++] =
+ global_matrix[col * global_size + i];
}
}
}
@@ -176,10 +182,12 @@ bool QuaternionParameterization::Plus(const double* x,
bool QuaternionParameterization::ComputeJacobian(const double* x,
double* jacobian) const {
- jacobian[0] = -x[1]; jacobian[1] = -x[2]; jacobian[2] = -x[3]; // NOLINT
- jacobian[3] = x[0]; jacobian[4] = x[3]; jacobian[5] = -x[2]; // NOLINT
- jacobian[6] = -x[3]; jacobian[7] = x[0]; jacobian[8] = x[1]; // NOLINT
- jacobian[9] = x[2]; jacobian[10] = -x[1]; jacobian[11] = x[0]; // NOLINT
+ // clang-format off
+ jacobian[0] = -x[1]; jacobian[1] = -x[2]; jacobian[2] = -x[3];
+ jacobian[3] = x[0]; jacobian[4] = x[3]; jacobian[5] = -x[2];
+ jacobian[6] = -x[3]; jacobian[7] = x[0]; jacobian[8] = x[1];
+ jacobian[9] = x[2]; jacobian[10] = -x[1]; jacobian[11] = x[0];
+ // clang-format on
return true;
}
@@ -209,10 +217,12 @@ bool EigenQuaternionParameterization::Plus(const double* x_ptr,
bool EigenQuaternionParameterization::ComputeJacobian(const double* x,
double* jacobian) const {
- jacobian[0] = x[3]; jacobian[1] = x[2]; jacobian[2] = -x[1]; // NOLINT
- jacobian[3] = -x[2]; jacobian[4] = x[3]; jacobian[5] = x[0]; // NOLINT
- jacobian[6] = x[1]; jacobian[7] = -x[0]; jacobian[8] = x[3]; // NOLINT
- jacobian[9] = -x[0]; jacobian[10] = -x[1]; jacobian[11] = -x[2]; // NOLINT
+ // clang-format off
+ jacobian[0] = x[3]; jacobian[1] = x[2]; jacobian[2] = -x[1];
+ jacobian[3] = -x[2]; jacobian[4] = x[3]; jacobian[5] = x[0];
+ jacobian[6] = x[1]; jacobian[7] = -x[0]; jacobian[8] = x[3];
+ jacobian[9] = -x[0]; jacobian[10] = -x[1]; jacobian[11] = -x[2];
+ // clang-format on
return true;
}
@@ -241,21 +251,26 @@ bool HomogeneousVectorParameterization::Plus(const double* x_ptr,
// (2nd Edition) for a detailed description. Note there is a typo on Page
// 625, line 4 so check the book errata.
const double norm_delta_div_2 = 0.5 * norm_delta;
- const double sin_delta_by_delta = sin(norm_delta_div_2) /
- norm_delta_div_2;
+ const double sin_delta_by_delta =
+ std::sin(norm_delta_div_2) / norm_delta_div_2;
Vector y(size_);
y.head(size_ - 1) = 0.5 * sin_delta_by_delta * delta;
- y(size_ - 1) = cos(norm_delta_div_2);
+ y(size_ - 1) = std::cos(norm_delta_div_2);
Vector v(size_);
double beta;
- internal::ComputeHouseholderVector<double>(x, &v, &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<ConstVectorRef, double, Eigen::Dynamic>(
+ x, &v, &beta);
// Apply the delta update to remain on the unit sphere. See section A6.9.3
// on page 625 of Hartley & Zisserman (2nd Edition) for a detailed
// description.
- x_plus_delta = x.norm() * (y - v * (beta * (v.transpose() * y)));
+ x_plus_delta = x.norm() * (y - v * (beta * (v.transpose() * y)));
return true;
}
@@ -267,7 +282,12 @@ bool HomogeneousVectorParameterization::ComputeJacobian(
Vector v(size_);
double beta;
- internal::ComputeHouseholderVector<double>(x, &v, &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<ConstVectorRef, double, Eigen::Dynamic>(
+ 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').
@@ -280,65 +300,14 @@ bool HomogeneousVectorParameterization::ComputeJacobian(
return true;
}
-ProductParameterization::ProductParameterization(
- LocalParameterization* local_param1,
- LocalParameterization* local_param2) {
- local_params_.push_back(local_param1);
- local_params_.push_back(local_param2);
- Init();
-}
-
-ProductParameterization::ProductParameterization(
- LocalParameterization* local_param1,
- LocalParameterization* local_param2,
- LocalParameterization* local_param3) {
- local_params_.push_back(local_param1);
- local_params_.push_back(local_param2);
- local_params_.push_back(local_param3);
- Init();
-}
-
-ProductParameterization::ProductParameterization(
- LocalParameterization* local_param1,
- LocalParameterization* local_param2,
- LocalParameterization* local_param3,
- LocalParameterization* local_param4) {
- local_params_.push_back(local_param1);
- local_params_.push_back(local_param2);
- local_params_.push_back(local_param3);
- local_params_.push_back(local_param4);
- Init();
-}
-
-ProductParameterization::~ProductParameterization() {
- for (int i = 0; i < local_params_.size(); ++i) {
- delete local_params_[i];
- }
-}
-
-void ProductParameterization::Init() {
- global_size_ = 0;
- local_size_ = 0;
- buffer_size_ = 0;
- for (int i = 0; i < local_params_.size(); ++i) {
- const LocalParameterization* param = local_params_[i];
- buffer_size_ = std::max(buffer_size_,
- param->LocalSize() * param->GlobalSize());
- global_size_ += param->GlobalSize();
- local_size_ += param->LocalSize();
- }
-}
-
bool ProductParameterization::Plus(const double* x,
const double* delta,
double* x_plus_delta) const {
int x_cursor = 0;
int delta_cursor = 0;
- for (int i = 0; i < local_params_.size(); ++i) {
- const LocalParameterization* param = local_params_[i];
- if (!param->Plus(x + x_cursor,
- delta + delta_cursor,
- x_plus_delta + x_cursor)) {
+ for (const auto& param : local_params_) {
+ if (!param->Plus(
+ x + x_cursor, delta + delta_cursor, x_plus_delta + x_cursor)) {
return false;
}
delta_cursor += param->LocalSize();
@@ -356,16 +325,15 @@ bool ProductParameterization::ComputeJacobian(const double* x,
int x_cursor = 0;
int delta_cursor = 0;
- for (int i = 0; i < local_params_.size(); ++i) {
- const LocalParameterization* param = local_params_[i];
+ for (const auto& param : local_params_) {
const int local_size = param->LocalSize();
const int global_size = param->GlobalSize();
- if (!param->ComputeJacobian(x + x_cursor, buffer.get())) {
+ if (!param->ComputeJacobian(x + x_cursor, buffer.data())) {
return false;
}
- jacobian.block(x_cursor, delta_cursor, global_size, local_size)
- = MatrixRef(buffer.get(), global_size, local_size);
+ jacobian.block(x_cursor, delta_cursor, global_size, local_size) =
+ MatrixRef(buffer.data(), global_size, local_size);
delta_cursor += local_size;
x_cursor += global_size;
diff --git a/extern/ceres/internal/ceres/loss_function.cc b/extern/ceres/internal/ceres/loss_function.cc
index eb5026784dd..2c21a7377ca 100644
--- a/extern/ceres/internal/ceres/loss_function.cc
+++ b/extern/ceres/internal/ceres/loss_function.cc
@@ -32,6 +32,7 @@
#include "ceres/loss_function.h"
+#include <algorithm>
#include <cmath>
#include <cstddef>
#include <limits>
@@ -101,7 +102,9 @@ void TolerantLoss::Evaluate(double s, double rho[3]) const {
// large, it will overflow. Since numerically 1 + e^x == e^x when the
// x is greater than about ln(2^53) for doubles, beyond this threshold
// we substitute x for ln(1 + e^x) as a numerically equivalent approximation.
- static const double kLog2Pow53 = 36.7; // ln(MathLimits<double>::kEpsilon).
+
+ // ln(MathLimits<double>::kEpsilon).
+ static constexpr double kLog2Pow53 = 36.7;
if (x > kLog2Pow53) {
rho[0] = s - a_ - c_;
rho[1] = 1.0;
@@ -119,12 +122,12 @@ void TukeyLoss::Evaluate(double s, double* rho) const {
// Inlier region.
const double value = 1.0 - s / a_squared_;
const double value_sq = value * value;
- rho[0] = a_squared_ / 6.0 * (1.0 - value_sq * value);
- rho[1] = 0.5 * value_sq;
- rho[2] = -1.0 / a_squared_ * value;
+ rho[0] = a_squared_ / 3.0 * (1.0 - value_sq * value);
+ rho[1] = value_sq;
+ rho[2] = -2.0 / a_squared_ * value;
} else {
// Outlier region.
- rho[0] = a_squared_ / 6.0;
+ rho[0] = a_squared_ / 3.0;
rho[1] = 0.0;
rho[2] = 0.0;
}
@@ -132,10 +135,12 @@ void TukeyLoss::Evaluate(double s, double* rho) const {
ComposedLoss::ComposedLoss(const LossFunction* f, Ownership ownership_f,
const LossFunction* g, Ownership ownership_g)
- : f_(CHECK_NOTNULL(f)),
- g_(CHECK_NOTNULL(g)),
+ : f_(f),
+ g_(g),
ownership_f_(ownership_f),
ownership_g_(ownership_g) {
+ CHECK(f_ != nullptr);
+ CHECK(g_ != nullptr);
}
ComposedLoss::~ComposedLoss() {
diff --git a/extern/ceres/internal/ceres/low_rank_inverse_hessian.cc b/extern/ceres/internal/ceres/low_rank_inverse_hessian.cc
index 1c6c9925f1c..f3953c46006 100644
--- a/extern/ceres/internal/ceres/low_rank_inverse_hessian.cc
+++ b/extern/ceres/internal/ceres/low_rank_inverse_hessian.cc
@@ -175,12 +175,10 @@ void LowRankInverseHessian::RightMultiply(const double* x_ptr,
<< "approximation.";
}
- for (list<int>::const_iterator it = indices_.begin();
- it != indices_.end();
- ++it) {
- const double beta = delta_gradient_history_.col(*it).dot(search_direction) /
- delta_x_dot_delta_gradient_(*it);
- search_direction += delta_x_history_.col(*it) * (alpha(*it) - beta);
+ for (const int i : indices_) {
+ const double beta = delta_gradient_history_.col(i).dot(search_direction) /
+ delta_x_dot_delta_gradient_(i);
+ search_direction += delta_x_history_.col(i) * (alpha(i) - beta);
}
}
diff --git a/extern/ceres/internal/ceres/low_rank_inverse_hessian.h b/extern/ceres/internal/ceres/low_rank_inverse_hessian.h
index 2c768c2ca53..0028a988923 100644
--- a/extern/ceres/internal/ceres/low_rank_inverse_hessian.h
+++ b/extern/ceres/internal/ceres/low_rank_inverse_hessian.h
@@ -54,7 +54,7 @@ namespace internal {
// enhanced with scaling rule by Byrd, Nocedal and Schanbel.
//
// Nocedal, J. (1980). "Updating Quasi-Newton Matrices with Limited
-// Storage". Mathematics of Computation 35 (151): 773–782.
+// Storage". Mathematics of Computation 35 (151): 773-782.
//
// Byrd, R. H.; Nocedal, J.; Schnabel, R. B. (1994).
// "Representations of Quasi-Newton Matrices and their use in
@@ -84,12 +84,12 @@ class LowRankInverseHessian : public LinearOperator {
bool Update(const Vector& delta_x, const Vector& delta_gradient);
// LinearOperator interface
- virtual void RightMultiply(const double* x, double* y) const;
- virtual void LeftMultiply(const double* x, double* y) const {
+ void RightMultiply(const double* x, double* y) const final;
+ void LeftMultiply(const double* x, double* y) const final {
RightMultiply(x, y);
}
- virtual int num_rows() const { return num_parameters_; }
- virtual int num_cols() const { return num_parameters_; }
+ int num_rows() const final { return num_parameters_; }
+ int num_cols() const final { return num_parameters_; }
private:
const int num_parameters_;
diff --git a/extern/ceres/internal/ceres/minimizer.h b/extern/ceres/internal/ceres/minimizer.h
index b59372806e7..afdd60d2944 100644
--- a/extern/ceres/internal/ceres/minimizer.h
+++ b/extern/ceres/internal/ceres/minimizer.h
@@ -31,6 +31,7 @@
#ifndef CERES_INTERNAL_MINIMIZER_H_
#define CERES_INTERNAL_MINIMIZER_H_
+#include <memory>
#include <string>
#include <vector>
#include "ceres/internal/port.h"
@@ -167,19 +168,19 @@ class Minimizer {
// Object responsible for evaluating the cost, residuals and
// Jacobian matrix.
- shared_ptr<Evaluator> evaluator;
+ std::shared_ptr<Evaluator> evaluator;
// Object responsible for actually computing the trust region
// step, and sizing the trust region radius.
- shared_ptr<TrustRegionStrategy> trust_region_strategy;
+ std::shared_ptr<TrustRegionStrategy> trust_region_strategy;
// Object holding the Jacobian matrix. It is assumed that the
// sparsity structure of the matrix has already been initialized
// and will remain constant for the life time of the
// optimization.
- shared_ptr<SparseMatrix> jacobian;
+ std::shared_ptr<SparseMatrix> jacobian;
- shared_ptr<CoordinateDescentMinimizer> inner_iteration_minimizer;
+ std::shared_ptr<CoordinateDescentMinimizer> inner_iteration_minimizer;
};
static Minimizer* Create(MinimizerType minimizer_type);
diff --git a/extern/ceres/internal/ceres/mutex.h b/extern/ceres/internal/ceres/mutex.h
deleted file mode 100644
index 2ce97772755..00000000000
--- a/extern/ceres/internal/ceres/mutex.h
+++ /dev/null
@@ -1,329 +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: Craig Silverstein.
-//
-// A simple mutex wrapper, supporting locks and read-write locks.
-// You should assume the locks are *not* re-entrant.
-//
-// This class is meant to be internal-only and should be wrapped by an
-// internal namespace. Before you use this module, please give the
-// name of your internal namespace for this module. Or, if you want
-// to expose it, you'll want to move it to the Google namespace. We
-// cannot put this class in global namespace because there can be some
-// problems when we have multiple versions of Mutex in each shared object.
-//
-// NOTE: by default, we have #ifdef'ed out the TryLock() method.
-// This is for two reasons:
-// 1) TryLock() under Windows is a bit annoying (it requires a
-// #define to be defined very early).
-// 2) TryLock() is broken for NO_THREADS mode, at least in NDEBUG
-// mode.
-// If you need TryLock(), and either these two caveats are not a
-// problem for you, or you're willing to work around them, then
-// feel free to #define GMUTEX_TRYLOCK, or to remove the #ifdefs
-// in the code below.
-//
-// CYGWIN NOTE: Cygwin support for rwlock seems to be buggy:
-// http://www.cygwin.com/ml/cygwin/2008-12/msg00017.html
-// Because of that, we might as well use windows locks for
-// cygwin. They seem to be more reliable than the cygwin pthreads layer.
-//
-// TRICKY IMPLEMENTATION NOTE:
-// This class is designed to be safe to use during
-// dynamic-initialization -- that is, by global constructors that are
-// run before main() starts. The issue in this case is that
-// dynamic-initialization happens in an unpredictable order, and it
-// could be that someone else's dynamic initializer could call a
-// function that tries to acquire this mutex -- but that all happens
-// before this mutex's constructor has run. (This can happen even if
-// the mutex and the function that uses the mutex are in the same .cc
-// file.) Basically, because Mutex does non-trivial work in its
-// constructor, it's not, in the naive implementation, safe to use
-// before dynamic initialization has run on it.
-//
-// The solution used here is to pair the actual mutex primitive with a
-// bool that is set to true when the mutex is dynamically initialized.
-// (Before that it's false.) Then we modify all mutex routines to
-// look at the bool, and not try to lock/unlock until the bool makes
-// it to true (which happens after the Mutex constructor has run.)
-//
-// This works because before main() starts -- particularly, during
-// dynamic initialization -- there are no threads, so a) it's ok that
-// the mutex operations are a no-op, since we don't need locking then
-// anyway; and b) we can be quite confident our bool won't change
-// state between a call to Lock() and a call to Unlock() (that would
-// require a global constructor in one translation unit to call Lock()
-// and another global constructor in another translation unit to call
-// Unlock() later, which is pretty perverse).
-//
-// That said, it's tricky, and can conceivably fail; it's safest to
-// avoid trying to acquire a mutex in a global constructor, if you
-// can. One way it can fail is that a really smart compiler might
-// initialize the bool to true at static-initialization time (too
-// early) rather than at dynamic-initialization time. To discourage
-// that, we set is_safe_ to true in code (not the constructor
-// colon-initializer) and set it to true via a function that always
-// evaluates to true, but that the compiler can't know always
-// evaluates to true. This should be good enough.
-
-#ifndef CERES_INTERNAL_MUTEX_H_
-#define CERES_INTERNAL_MUTEX_H_
-
-#include "ceres/internal/port.h"
-
-#if defined(CERES_NO_THREADS)
- typedef int MutexType; // to keep a lock-count
-#elif defined(_WIN32) || defined(__CYGWIN32__) || defined(__CYGWIN64__)
-# define CERES_WIN32_LEAN_AND_MEAN // We only need minimal includes
-# ifdef CERES_GMUTEX_TRYLOCK
- // We need Windows NT or later for TryEnterCriticalSection(). If you
- // don't need that functionality, you can remove these _WIN32_WINNT
- // lines, and change TryLock() to assert(0) or something.
-# ifndef _WIN32_WINNT
-# define _WIN32_WINNT 0x0400
-# endif
-# endif
-// Unfortunately, windows.h defines a bunch of macros with common
-// names. Two in particular need avoiding: ERROR and min/max.
-// To avoid macro definition of ERROR.
-# define NOGDI
-// To avoid macro definition of min/max.
-# ifndef NOMINMAX
-# define NOMINMAX
-# endif
-# include <windows.h>
- typedef CRITICAL_SECTION MutexType;
-#elif defined(CERES_HAVE_PTHREAD) && defined(CERES_HAVE_RWLOCK)
- // Needed for pthread_rwlock_*. If it causes problems, you could take it
- // out, but then you'd have to unset CERES_HAVE_RWLOCK (at least on linux --
- // it *does* cause problems for FreeBSD, or MacOSX, but isn't needed for
- // locking there.)
-# if defined(__linux__) && !defined(_XOPEN_SOURCE)
-# define _XOPEN_SOURCE 500 // may be needed to get the rwlock calls
-# endif
-# include <pthread.h>
- typedef pthread_rwlock_t MutexType;
-#elif defined(CERES_HAVE_PTHREAD)
-# include <pthread.h>
- typedef pthread_mutex_t MutexType;
-#else
-# error Need to implement mutex.h for your architecture, or #define NO_THREADS
-#endif
-
-// We need to include these header files after defining _XOPEN_SOURCE
-// as they may define the _XOPEN_SOURCE macro.
-#include <assert.h>
-#include <stdlib.h> // for abort()
-
-namespace ceres {
-namespace internal {
-
-class Mutex {
- public:
- // Create a Mutex that is not held by anybody. This constructor is
- // typically used for Mutexes allocated on the heap or the stack.
- // See below for a recommendation for constructing global Mutex
- // objects.
- inline Mutex();
-
- // Destructor
- inline ~Mutex();
-
- inline void Lock(); // Block if needed until free then acquire exclusively
- inline void Unlock(); // Release a lock acquired via Lock()
-#ifdef CERES_GMUTEX_TRYLOCK
- inline bool TryLock(); // If free, Lock() and return true, else return false
-#endif
- // Note that on systems that don't support read-write locks, these may
- // be implemented as synonyms to Lock() and Unlock(). So you can use
- // these for efficiency, but don't use them anyplace where being able
- // to do shared reads is necessary to avoid deadlock.
- inline void ReaderLock(); // Block until free or shared then acquire a share
- inline void ReaderUnlock(); // Release a read share of this Mutex
- inline void WriterLock() { Lock(); } // Acquire an exclusive lock
- inline void WriterUnlock() { Unlock(); } // Release a lock from WriterLock()
-
- // TODO(hamaji): Do nothing, implement correctly.
- inline void AssertHeld() {}
-
- private:
- MutexType mutex_;
- // We want to make sure that the compiler sets is_safe_ to true only
- // when we tell it to, and never makes assumptions is_safe_ is
- // always true. volatile is the most reliable way to do that.
- volatile bool is_safe_;
-
- inline void SetIsSafe() { is_safe_ = true; }
-
- // Catch the error of writing Mutex when intending MutexLock.
- Mutex(Mutex* /*ignored*/) {}
- // Disallow "evil" constructors
- Mutex(const Mutex&);
- void operator=(const Mutex&);
-};
-
-// Now the implementation of Mutex for various systems
-#if defined(CERES_NO_THREADS)
-
-// When we don't have threads, we can be either reading or writing,
-// but not both. We can have lots of readers at once (in no-threads
-// mode, that's most likely to happen in recursive function calls),
-// but only one writer. We represent this by having mutex_ be -1 when
-// writing and a number > 0 when reading (and 0 when no lock is held).
-//
-// In debug mode, we assert these invariants, while in non-debug mode
-// we do nothing, for efficiency. That's why everything is in an
-// assert.
-
-Mutex::Mutex() : mutex_(0) { }
-Mutex::~Mutex() { assert(mutex_ == 0); }
-void Mutex::Lock() { assert(--mutex_ == -1); }
-void Mutex::Unlock() { assert(mutex_++ == -1); }
-#ifdef CERES_GMUTEX_TRYLOCK
-bool Mutex::TryLock() { if (mutex_) return false; Lock(); return true; }
-#endif
-void Mutex::ReaderLock() { assert(++mutex_ > 0); }
-void Mutex::ReaderUnlock() { assert(mutex_-- > 0); }
-
-#elif defined(_WIN32) || defined(__CYGWIN32__) || defined(__CYGWIN64__)
-
-Mutex::Mutex() { InitializeCriticalSection(&mutex_); SetIsSafe(); }
-Mutex::~Mutex() { DeleteCriticalSection(&mutex_); }
-void Mutex::Lock() { if (is_safe_) EnterCriticalSection(&mutex_); }
-void Mutex::Unlock() { if (is_safe_) LeaveCriticalSection(&mutex_); }
-#ifdef GMUTEX_TRYLOCK
-bool Mutex::TryLock() { return is_safe_ ?
- TryEnterCriticalSection(&mutex_) != 0 : true; }
-#endif
-void Mutex::ReaderLock() { Lock(); } // we don't have read-write locks
-void Mutex::ReaderUnlock() { Unlock(); }
-
-#elif defined(CERES_HAVE_PTHREAD) && defined(CERES_HAVE_RWLOCK)
-
-#define CERES_SAFE_PTHREAD(fncall) do { /* run fncall if is_safe_ is true */ \
- if (is_safe_ && fncall(&mutex_) != 0) abort(); \
-} while (0)
-
-Mutex::Mutex() {
- SetIsSafe();
- if (is_safe_ && pthread_rwlock_init(&mutex_, NULL) != 0) abort();
-}
-Mutex::~Mutex() { CERES_SAFE_PTHREAD(pthread_rwlock_destroy); }
-void Mutex::Lock() { CERES_SAFE_PTHREAD(pthread_rwlock_wrlock); }
-void Mutex::Unlock() { CERES_SAFE_PTHREAD(pthread_rwlock_unlock); }
-#ifdef CERES_GMUTEX_TRYLOCK
-bool Mutex::TryLock() { return is_safe_ ?
- pthread_rwlock_trywrlock(&mutex_) == 0 :
- true; }
-#endif
-void Mutex::ReaderLock() { CERES_SAFE_PTHREAD(pthread_rwlock_rdlock); }
-void Mutex::ReaderUnlock() { CERES_SAFE_PTHREAD(pthread_rwlock_unlock); }
-#undef CERES_SAFE_PTHREAD
-
-#elif defined(CERES_HAVE_PTHREAD)
-
-#define CERES_SAFE_PTHREAD(fncall) do { /* run fncall if is_safe_ is true */ \
- if (is_safe_ && fncall(&mutex_) != 0) abort(); \
-} while (0)
-
-Mutex::Mutex() {
- SetIsSafe();
- if (is_safe_ && pthread_mutex_init(&mutex_, NULL) != 0) abort();
-}
-Mutex::~Mutex() { CERES_SAFE_PTHREAD(pthread_mutex_destroy); }
-void Mutex::Lock() { CERES_SAFE_PTHREAD(pthread_mutex_lock); }
-void Mutex::Unlock() { CERES_SAFE_PTHREAD(pthread_mutex_unlock); }
-#ifdef CERES_GMUTEX_TRYLOCK
-bool Mutex::TryLock() { return is_safe_ ?
- pthread_mutex_trylock(&mutex_) == 0 : true; }
-#endif
-void Mutex::ReaderLock() { Lock(); }
-void Mutex::ReaderUnlock() { Unlock(); }
-#undef CERES_SAFE_PTHREAD
-
-#endif
-
-// --------------------------------------------------------------------------
-// Some helper classes
-
-// Note: The weird "Ceres" prefix for the class is a workaround for having two
-// similar mutex.h files included in the same translation unit. This is a
-// problem because macros do not respect C++ namespaces, and as a result, this
-// does not work well (e.g. inside Chrome). The offending macros are
-// "MutexLock(x) COMPILE_ASSERT(false)". To work around this, "Ceres" is
-// prefixed to the class names; this permits defining the classes.
-
-// CeresMutexLock(mu) acquires mu when constructed and releases it
-// when destroyed.
-class CeresMutexLock {
- public:
- explicit CeresMutexLock(Mutex *mu) : mu_(mu) { mu_->Lock(); }
- ~CeresMutexLock() { mu_->Unlock(); }
- private:
- Mutex * const mu_;
- // Disallow "evil" constructors
- CeresMutexLock(const CeresMutexLock&);
- void operator=(const CeresMutexLock&);
-};
-
-// CeresReaderMutexLock and CeresWriterMutexLock do the same, for rwlocks
-class CeresReaderMutexLock {
- public:
- explicit CeresReaderMutexLock(Mutex *mu) : mu_(mu) { mu_->ReaderLock(); }
- ~CeresReaderMutexLock() { mu_->ReaderUnlock(); }
- private:
- Mutex * const mu_;
- // Disallow "evil" constructors
- CeresReaderMutexLock(const CeresReaderMutexLock&);
- void operator=(const CeresReaderMutexLock&);
-};
-
-class CeresWriterMutexLock {
- public:
- explicit CeresWriterMutexLock(Mutex *mu) : mu_(mu) { mu_->WriterLock(); }
- ~CeresWriterMutexLock() { mu_->WriterUnlock(); }
- private:
- Mutex * const mu_;
- // Disallow "evil" constructors
- CeresWriterMutexLock(const CeresWriterMutexLock&);
- void operator=(const CeresWriterMutexLock&);
-};
-
-// Catch bug where variable name is omitted, e.g. MutexLock (&mu);
-#define CeresMutexLock(x) \
- COMPILE_ASSERT(0, ceres_mutex_lock_decl_missing_var_name)
-#define CeresReaderMutexLock(x) \
- COMPILE_ASSERT(0, ceres_rmutex_lock_decl_missing_var_name)
-#define CeresWriterMutexLock(x) \
- COMPILE_ASSERT(0, ceres_wmutex_lock_decl_missing_var_name)
-
-} // namespace internal
-} // namespace ceres
-
-#endif // CERES_INTERNAL_MUTEX_H_
diff --git a/extern/ceres/internal/ceres/normal_prior.cc b/extern/ceres/internal/ceres/normal_prior.cc
index b3666cd702f..a3d5d8ed772 100644
--- a/extern/ceres/internal/ceres/normal_prior.cc
+++ b/extern/ceres/internal/ceres/normal_prior.cc
@@ -33,7 +33,6 @@
#include <cstddef>
#include <vector>
#include "ceres/internal/eigen.h"
-#include "ceres/internal/scoped_ptr.h"
#include "ceres/types.h"
#include "glog/logging.h"
diff --git a/extern/ceres/internal/ceres/pair_hash.h b/extern/ceres/internal/ceres/pair_hash.h
new file mode 100644
index 00000000000..80453bae7db
--- /dev/null
+++ b/extern/ceres/internal/ceres/pair_hash.h
@@ -0,0 +1,112 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2018 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)
+//
+// A hasher for std::pair<T, T>.
+
+#ifndef CERES_INTERNAL_PAIR_HASH_H_
+#define CERES_INTERNAL_PAIR_HASH_H_
+
+#include "ceres/internal/port.h"
+#include <cstdint>
+#include <utility>
+
+namespace ceres {
+namespace internal {
+
+#if defined(_WIN32) && !defined(__MINGW64__) && !defined(__MINGW32__)
+#define GG_LONGLONG(x) x##I64
+#define GG_ULONGLONG(x) x##UI64
+#else
+#define GG_LONGLONG(x) x##LL
+#define GG_ULONGLONG(x) x##ULL
+#endif
+
+// The hash function is due to Bob Jenkins (see
+// http://burtleburtle.net/bob/hash/index.html). Each mix takes 36 instructions,
+// in 18 cycles if you're lucky. On x86 architectures, this requires 45
+// instructions in 27 cycles, if you're lucky.
+//
+// 32bit version
+inline void hash_mix(uint32_t& a, uint32_t& b, uint32_t& c) {
+ a -= b; a -= c; a ^= (c>>13);
+ b -= c; b -= a; b ^= (a<<8);
+ c -= a; c -= b; c ^= (b>>13);
+ a -= b; a -= c; a ^= (c>>12);
+ b -= c; b -= a; b ^= (a<<16);
+ c -= a; c -= b; c ^= (b>>5);
+ a -= b; a -= c; a ^= (c>>3);
+ b -= c; b -= a; b ^= (a<<10);
+ c -= a; c -= b; c ^= (b>>15);
+}
+
+// 64bit version
+inline void hash_mix(uint64_t& a, uint64_t& b, uint64_t& c) {
+ a -= b; a -= c; a ^= (c>>43);
+ b -= c; b -= a; b ^= (a<<9);
+ c -= a; c -= b; c ^= (b>>8);
+ a -= b; a -= c; a ^= (c>>38);
+ b -= c; b -= a; b ^= (a<<23);
+ c -= a; c -= b; c ^= (b>>5);
+ a -= b; a -= c; a ^= (c>>35);
+ b -= c; b -= a; b ^= (a<<49);
+ c -= a; c -= b; c ^= (b>>11);
+}
+
+inline uint32_t Hash32NumWithSeed(uint32_t num, uint32_t c) {
+ // The golden ratio; an arbitrary value.
+ uint32_t b = 0x9e3779b9UL;
+ hash_mix(num, b, c);
+ return c;
+}
+
+inline uint64_t Hash64NumWithSeed(uint64_t num, uint64_t c) {
+ // More of the golden ratio.
+ uint64_t b = GG_ULONGLONG(0xe08c1d668b756f82);
+ hash_mix(num, b, c);
+ return c;
+}
+
+// Hasher for STL pairs. Requires hashers for both members to be defined.
+struct pair_hash {
+ public:
+ template <typename T>
+ std::size_t operator()(const std::pair<T, T>& p) const {
+ const std::size_t h1 = std::hash<T>()(p.first);
+ const std::size_t h2 = std::hash<T>()(p.second);
+ // The decision below is at compile time
+ return (sizeof(h1) <= sizeof(uint32_t)) ? Hash32NumWithSeed(h1, h2)
+ : Hash64NumWithSeed(h1, h2);
+ }
+};
+
+} // namespace internal
+} // namespace ceres
+
+#endif // CERES_INTERNAL_PAIR_HASH_H_
diff --git a/extern/ceres/internal/ceres/parallel_for.h b/extern/ceres/internal/ceres/parallel_for.h
new file mode 100644
index 00000000000..2da2320c137
--- /dev/null
+++ b/extern/ceres/internal/ceres/parallel_for.h
@@ -0,0 +1,67 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2018 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 (Michael Vitus)
+
+#ifndef CERES_INTERNAL_PARALLEL_FOR_
+#define CERES_INTERNAL_PARALLEL_FOR_
+
+#include <functional>
+
+#include "ceres/context_impl.h"
+
+namespace ceres {
+namespace internal {
+
+// Returns the maximum number of threads supported by the threading backend
+// Ceres was compiled with.
+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.
+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().
+void ParallelFor(ContextImpl* context,
+ int start,
+ int end,
+ int num_threads,
+ const std::function<void(int thread_id, int i)>& function);
+} // namespace internal
+} // namespace ceres
+
+#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
new file mode 100644
index 00000000000..8e358f5900b
--- /dev/null
+++ b/extern/ceres/internal/ceres/parallel_for_cxx.cc
@@ -0,0 +1,247 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2018 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 (Michael Vitus)
+
+// This include must come before any #ifndef check on Ceres compile options.
+#include "ceres/internal/port.h"
+
+#ifdef CERES_USE_CXX_THREADS
+
+#include "ceres/parallel_for.h"
+
+#include <cmath>
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+
+#include "ceres/concurrent_queue.h"
+#include "ceres/scoped_thread_token.h"
+#include "ceres/thread_token_provider.h"
+#include "glog/logging.h"
+
+namespace ceres {
+namespace internal {
+namespace {
+// This class creates a thread safe barrier which will block until a
+// pre-specified number of threads call Finished. This allows us to block the
+// main thread until all the parallel threads are finished processing all the
+// work.
+class BlockUntilFinished {
+ public:
+ explicit BlockUntilFinished(int num_total)
+ : num_finished_(0), num_total_(num_total) {}
+
+ // Increment the number of jobs that have finished and signal the blocking
+ // thread if all jobs have finished.
+ void Finished() {
+ std::lock_guard<std::mutex> lock(mutex_);
+ ++num_finished_;
+ CHECK_LE(num_finished_, num_total_);
+ if (num_finished_ == num_total_) {
+ condition_.notify_one();
+ }
+ }
+
+ // Block until all threads have signaled they are finished.
+ void Block() {
+ std::unique_lock<std::mutex> lock(mutex_);
+ condition_.wait(lock, [&]() { return num_finished_ == num_total_; });
+ }
+
+ private:
+ std::mutex mutex_;
+ std::condition_variable condition_;
+ // The current number of jobs finished.
+ int num_finished_;
+ // The total number of jobs.
+ int num_total_;
+};
+
+// Shared state between the parallel tasks. Each thread will use this
+// information to get the next block of work to be performed.
+struct SharedState {
+ SharedState(int start, int end, int num_work_items)
+ : start(start),
+ end(end),
+ num_work_items(num_work_items),
+ i(0),
+ thread_token_provider(num_work_items),
+ block_until_finished(num_work_items) {}
+
+ // The start and end index of the for loop.
+ const int start;
+ const int end;
+ // The number of blocks that need to be processed.
+ const int num_work_items;
+
+ // The next block of work to be assigned to a worker. The parallel for loop
+ // range is split into num_work_items blocks of work, i.e. a single block of
+ // work is:
+ // for (int j = start + i; j < end; j += num_work_items) { ... }.
+ int i;
+ std::mutex mutex_i;
+
+ // Provides a unique thread ID among all active threads working on the same
+ // group of tasks. Thread-safe.
+ ThreadTokenProvider thread_token_provider;
+
+ // Used to signal when all the work has been completed. Thread safe.
+ BlockUntilFinished block_until_finished;
+};
+
+} // namespace
+
+int MaxNumThreadsAvailable() {
+ return ThreadPool::MaxNumThreadsAvailable();
+}
+
+// See ParallelFor (below) for more details.
+void ParallelFor(ContextImpl* context,
+ int start,
+ int end,
+ int num_threads,
+ const std::function<void(int)>& function) {
+ CHECK_GT(num_threads, 0);
+ CHECK(context != NULL);
+ if (end <= start) {
+ return;
+ }
+
+ // Fast path for when it is single threaded.
+ if (num_threads == 1) {
+ for (int i = start; i < end; ++i) {
+ function(i);
+ }
+ return;
+ }
+
+ ParallelFor(context, start, end, num_threads,
+ [&function](int /*thread_id*/, int i) { function(i); });
+}
+
+// This implementation uses a fixed size max worker pool with a shared task
+// queue. The problem of executing the function for the interval of [start, end)
+// is broken up into at most num_threads blocks and added to the thread pool. To
+// avoid deadlocks, the calling thread is allowed to steal work from the worker
+// pool. This is implemented via a shared state between the tasks. In order for
+// the calling thread or thread pool to get a block of work, it will query the
+// shared state for the next block of work to be done. If there is nothing left,
+// it will return. We will exit the ParallelFor call when all of the work has
+// been done, not when all of the tasks have been popped off the task queue.
+//
+// A unique thread ID among all active tasks will be acquired once for each
+// block of work. This avoids the significant performance penalty for acquiring
+// it on every iteration of the for loop. The thread ID is guaranteed to be in
+// [0, num_threads).
+//
+// A performance analysis has shown this implementation is onpar with OpenMP and
+// TBB.
+void ParallelFor(ContextImpl* context,
+ int start,
+ int end,
+ int num_threads,
+ const std::function<void(int thread_id, int i)>& function) {
+ CHECK_GT(num_threads, 0);
+ CHECK(context != NULL);
+ if (end <= start) {
+ return;
+ }
+
+ // Fast path for when it is single threaded.
+ if (num_threads == 1) {
+ // Even though we only have one thread, use the thread token provider to
+ // guarantee the exact same behavior when running with multiple threads.
+ ThreadTokenProvider thread_token_provider(num_threads);
+ const ScopedThreadToken scoped_thread_token(&thread_token_provider);
+ const int thread_id = scoped_thread_token.token();
+ for (int i = start; i < end; ++i) {
+ function(thread_id, i);
+ }
+ return;
+ }
+
+ // We use a std::shared_ptr because the main thread can finish all
+ // the work before the tasks have been popped off the queue. So the
+ // shared state needs to exist for the duration of all the tasks.
+ const int num_work_items = std::min((end - start), num_threads);
+ std::shared_ptr<SharedState> shared_state(
+ new SharedState(start, end, num_work_items));
+
+ // A function which tries to perform a chunk of work. This returns false if
+ // there is no work to be done.
+ auto task_function = [shared_state, &function]() {
+ int i = 0;
+ {
+ // Get the next available chunk of work to be performed. If there is no
+ // work, return false.
+ std::lock_guard<std::mutex> lock(shared_state->mutex_i);
+ if (shared_state->i >= shared_state->num_work_items) {
+ return false;
+ }
+ i = shared_state->i;
+ ++shared_state->i;
+ }
+
+ const ScopedThreadToken scoped_thread_token(
+ &shared_state->thread_token_provider);
+ const int thread_id = scoped_thread_token.token();
+
+ // Perform each task.
+ for (int j = shared_state->start + i;
+ j < shared_state->end;
+ j += shared_state->num_work_items) {
+ function(thread_id, j);
+ }
+ shared_state->block_until_finished.Finished();
+ return true;
+ };
+
+ // Add all the tasks to the thread pool.
+ for (int i = 0; i < num_work_items; ++i) {
+ // Note we are taking the task_function as value so the shared_state
+ // shared pointer is copied and the ref count is increased. This is to
+ // prevent it from being deleted when the main thread finishes all the
+ // work and exits before the threads finish.
+ context->thread_pool.AddTask([task_function]() { task_function(); });
+ }
+
+ // Try to do any available work on the main thread. This may steal work from
+ // the thread pool, but when there is no work left the thread pool tasks
+ // will be no-ops.
+ while (task_function()) {
+ }
+
+ // Wait until all tasks have finished.
+ shared_state->block_until_finished.Block();
+}
+
+} // namespace internal
+} // namespace ceres
+
+#endif // CERES_USE_CXX_THREADS
diff --git a/extern/ceres/internal/ceres/parallel_for_nothreads.cc b/extern/ceres/internal/ceres/parallel_for_nothreads.cc
new file mode 100644
index 00000000000..e8f450a714d
--- /dev/null
+++ b/extern/ceres/internal/ceres/parallel_for_nothreads.cc
@@ -0,0 +1,78 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2018 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: alexs.mac@gmail.com (Alex Stewart)
+
+// This include must come before any #ifndef check on Ceres compile options.
+#include "ceres/internal/port.h"
+
+#ifdef CERES_NO_THREADS
+
+#include "ceres/parallel_for.h"
+#include "glog/logging.h"
+
+namespace ceres {
+namespace internal {
+
+int MaxNumThreadsAvailable() { return 1; }
+
+void ParallelFor(ContextImpl* context,
+ int start,
+ int end,
+ int num_threads,
+ const std::function<void(int)>& function) {
+ CHECK_GT(num_threads, 0);
+ CHECK(context != NULL);
+ if (end <= start) {
+ return;
+ }
+ for (int i = start; i < end; ++i) {
+ function(i);
+ }
+}
+
+void ParallelFor(ContextImpl* context,
+ int start,
+ int end,
+ int num_threads,
+ const std::function<void(int thread_id, int i)>& function) {
+ CHECK_GT(num_threads, 0);
+ CHECK(context != NULL);
+ if (end <= start) {
+ return;
+ }
+ const int thread_id = 0;
+ for (int i = start; i < end; ++i) {
+ function(thread_id, i);
+ }
+}
+
+}
+}
+
+#endif // CERES_NO_THREADS
diff --git a/extern/ceres/internal/ceres/parallel_for_openmp.cc b/extern/ceres/internal/ceres/parallel_for_openmp.cc
new file mode 100644
index 00000000000..8afe3b11f8d
--- /dev/null
+++ b/extern/ceres/internal/ceres/parallel_for_openmp.cc
@@ -0,0 +1,88 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2018 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 (Michael Vitus)
+
+// This include must come before any #ifndef check on Ceres compile options.
+#include "ceres/internal/port.h"
+
+#if defined(CERES_USE_OPENMP)
+
+#include "ceres/parallel_for.h"
+
+#include "ceres/scoped_thread_token.h"
+#include "ceres/thread_token_provider.h"
+#include "glog/logging.h"
+#include "omp.h"
+
+namespace ceres {
+namespace internal {
+
+int MaxNumThreadsAvailable() {
+ return omp_get_max_threads();
+}
+
+void ParallelFor(ContextImpl* context,
+ int start,
+ int end,
+ int num_threads,
+ const std::function<void(int)>& function) {
+ CHECK_GT(num_threads, 0);
+ CHECK(context != NULL);
+ if (end <= start) {
+ return;
+ }
+
+#ifdef CERES_USE_OPENMP
+#pragma omp parallel for num_threads(num_threads) \
+ schedule(dynamic) if (num_threads > 1)
+#endif // CERES_USE_OPENMP
+ for (int i = start; i < end; ++i) {
+ function(i);
+ }
+}
+
+void ParallelFor(ContextImpl* context,
+ int start,
+ int end,
+ int num_threads,
+ const std::function<void(int thread_id, int i)>& function) {
+ CHECK(context != NULL);
+
+ ThreadTokenProvider thread_token_provider(num_threads);
+ ParallelFor(context, start, end, num_threads, [&](int i) {
+ const ScopedThreadToken scoped_thread_token(&thread_token_provider);
+ const int thread_id = scoped_thread_token.token();
+ function(thread_id, i);
+ });
+}
+
+} // namespace internal
+} // namespace ceres
+
+#endif // defined(CERES_USE_OPENMP)
diff --git a/extern/ceres/internal/ceres/parallel_utils.cc b/extern/ceres/internal/ceres/parallel_utils.cc
new file mode 100644
index 00000000000..e1cb5f979ec
--- /dev/null
+++ b/extern/ceres/internal/ceres/parallel_utils.cc
@@ -0,0 +1,90 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2018 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: wjr@google.com (William Rucklidge)
+
+#include "ceres/parallel_utils.h"
+
+namespace ceres {
+namespace internal {
+
+void LinearIndexToUpperTriangularIndex(int k, int n, int* i, int* j) {
+ // This works by unfolding a rectangle into a triangle.
+ // Say n is even. 4 is a nice even number. The 10 i,j pairs that we
+ // want to produce are:
+ // 0,0 0,1 0,2 0,3
+ // 1,1 1,2 1,3
+ // 2,2 2,3
+ // 3,3
+ // This triangle can be folded into a 5x2 rectangle:
+ // 3,3 0,0 0,1 0,2 0,3
+ // 2,2 2,3 1,1 1,2 1,3
+
+ // If N is odd, say 5, then the 15 i,j pairs are:
+ // 0,0 0,1 0,2 0,3 0,4
+ // 1,1 1,2 1,3 1,4
+ // 2,2 2,3 2,3
+ // 3,3 3,4
+ // 4,4
+ // which folds to a 5x3 rectangle:
+ // 0,0 0,1 0,2 0,3 0,4
+ // 4,4 1,1 1,2 1,3 1,4
+ // 3,3 3,4 2,2 2,3 2,4
+
+ // All this function does is map the linear iteration position to a
+ // location in the rectangle and work out the appropriate (i, j) for that
+ // location.
+ if (n & 1) {
+ // Odd n. The tip of the triangle is on row 1.
+ int w = n; // Width of the rectangle to unfold
+ int i0 = k / w;
+ int j0 = k % w;
+ if (j0 >= i0) {
+ *i = i0;
+ *j = j0;
+ } else {
+ *i = n - i0;
+ *j = *i + j0;
+ }
+ } else {
+ // Even n. The tip of the triangle is on row 0, making it one wider.
+ int w = n + 1;
+ int i0 = k / w;
+ int j0 = k % w;
+ if (j0 > i0) {
+ *i = i0;
+ *j = j0 - 1;
+ } else {
+ *i = n - 1 - i0;
+ *j = *i + j0;
+ }
+ }
+}
+
+} // namespace internal
+} // namespace ceres
diff --git a/extern/ceres/internal/ceres/parallel_utils.h b/extern/ceres/internal/ceres/parallel_utils.h
new file mode 100644
index 00000000000..1291428228a
--- /dev/null
+++ b/extern/ceres/internal/ceres/parallel_utils.h
@@ -0,0 +1,67 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2018 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: wjr@google.com (William Rucklidge)
+
+#ifndef CERES_INTERNAL_PARALLEL_UTILS_H_
+#define CERES_INTERNAL_PARALLEL_UTILS_H_
+
+namespace ceres {
+namespace internal {
+
+// Converts a linear iteration order into a triangular iteration order.
+// Suppose you have nested loops that look like
+// for (int i = 0; i < n; i++) {
+// for (int j = i; j < n; j++) {
+// ... use i and j
+// }
+// }
+// Naively using ParallelFor to parallelise those loops might look like
+// ParallelFor(..., 0, n * n, num_threads,
+// [](int thread_id, int k) {
+// int i = k / n, j = k % n;
+// if (j < i) return;
+// ...
+// });
+// but these empty work items can lead to very unbalanced threading. Instead,
+// do this:
+// int actual_work_items = (n * (n + 1)) / 2;
+// ParallelFor(..., 0, actual_work_items, num_threads,
+// [](int thread_id, int k) {
+// int i, j;
+// UnfoldIteration(k, n, &i, &j);
+// ...
+// });
+// which in each iteration will produce i and j satisfying
+// 0 <= i <= j < n
+void LinearIndexToUpperTriangularIndex(int k, int n, int* i, int* j);
+
+} // namespace internal
+} // namespace ceres
+
+#endif // CERES_INTERNAL_PARALLEL_UTILS_H_
diff --git a/extern/ceres/internal/ceres/parameter_block.h b/extern/ceres/internal/ceres/parameter_block.h
index 8e21553c668..88943dfbcff 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 2015 Google Inc. All rights reserved.
+// Copyright 2019 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -32,15 +32,16 @@
#define CERES_INTERNAL_PARAMETER_BLOCK_H_
#include <algorithm>
+#include <cstdint>
#include <cstdlib>
#include <limits>
+#include <memory>
#include <string>
+#include <unordered_set>
+
#include "ceres/array_utils.h"
-#include "ceres/collections_port.h"
-#include "ceres/integral_types.h"
#include "ceres/internal/eigen.h"
#include "ceres/internal/port.h"
-#include "ceres/internal/scoped_ptr.h"
#include "ceres/local_parameterization.h"
#include "ceres/stringprintf.h"
#include "glog/logging.h"
@@ -62,29 +63,28 @@ class ResidualBlock;
// responsible for the proper disposal of the local parameterization.
class ParameterBlock {
public:
- // TODO(keir): Decide what data structure is best here. Should this be a set?
- // Probably not, because sets are memory inefficient. However, if it's a
- // vector, you can get into pathological linear performance when removing a
- // residual block from a problem where all the residual blocks depend on one
- // parameter; for example, shared focal length in a bundle adjustment
- // problem. It might be worth making a custom structure that is just an array
- // when it is small, but transitions to a hash set when it has more elements.
- //
- // For now, use a hash set.
- typedef HashSet<ResidualBlock*> ResidualBlockSet;
+ typedef std::unordered_set<ResidualBlock*> ResidualBlockSet;
// 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
// of the parameter block inside a Program (if any).
- ParameterBlock(double* user_state, int size, int index) {
- Init(user_state, size, index, NULL);
- }
+ ParameterBlock(double* user_state, int size, int index)
+ : user_state_(user_state),
+ size_(size),
+ state_(user_state),
+ index_(index) {}
ParameterBlock(double* user_state,
int size,
int index,
- LocalParameterization* local_parameterization) {
- Init(user_state, size, index, local_parameterization);
+ LocalParameterization* local_parameterization)
+ : user_state_(user_state),
+ size_(size),
+ state_(user_state),
+ index_(index) {
+ if (local_parameterization != nullptr) {
+ SetParameterization(local_parameterization);
+ }
}
// The size of the parameter block.
@@ -92,12 +92,10 @@ class ParameterBlock {
// Manipulate the parameter state.
bool SetState(const double* x) {
- CHECK(x != NULL)
- << "Tried to set the state of constant parameter "
- << "with user location " << user_state_;
- CHECK(!is_constant_)
- << "Tried to set the state of constant parameter "
- << "with user location " << user_state_;
+ CHECK(x != nullptr) << "Tried to set the state of constant parameter "
+ << "with user location " << user_state_;
+ CHECK(!IsConstant()) << "Tried to set the state of constant parameter "
+ << "with user location " << user_state_;
state_ = x;
return UpdateLocalParameterizationJacobian();
@@ -106,9 +104,9 @@ class ParameterBlock {
// Copy the current parameter state out to x. This is "GetState()" rather than
// simply "state()" since it is actively copying the data into the passed
// pointer.
- void GetState(double *x) const {
+ void GetState(double* x) const {
if (x != state_) {
- memcpy(x, state_, sizeof(*state_) * size_);
+ std::copy(state_, state_ + size_, x);
}
}
@@ -116,7 +114,7 @@ class ParameterBlock {
const double* state() const { return state_; }
const double* user_state() const { return user_state_; }
double* mutable_user_state() { return user_state_; }
- LocalParameterization* local_parameterization() const {
+ const LocalParameterization* local_parameterization() const {
return local_parameterization_;
}
LocalParameterization* mutable_local_parameterization() {
@@ -124,9 +122,22 @@ class ParameterBlock {
}
// Set this parameter block to vary or not.
- void SetConstant() { is_constant_ = true; }
- void SetVarying() { is_constant_ = false; }
- bool IsConstant() const { return is_constant_; }
+ void SetConstant() { is_set_constant_ = true; }
+ void SetVarying() { is_set_constant_ = false; }
+ bool IsConstant() const { return (is_set_constant_ || LocalSize() == 0); }
+
+ double UpperBound(int index) const {
+ return (upper_bounds_ ? upper_bounds_[index]
+ : std::numeric_limits<double>::max());
+ }
+
+ double LowerBound(int index) const {
+ return (lower_bounds_ ? lower_bounds_[index]
+ : -std::numeric_limits<double>::max());
+ }
+
+ bool IsUpperBounded() const { return (upper_bounds_ == nullptr); }
+ bool IsLowerBounded() const { return (lower_bounds_ == nullptr); }
// This parameter block's index in an array.
int index() const { return index_; }
@@ -142,7 +153,7 @@ class ParameterBlock {
// Methods relating to the parameter block's parameterization.
- // The local to global jacobian. Returns NULL if there is no local
+ // 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 {
@@ -150,27 +161,24 @@ class ParameterBlock {
}
int LocalSize() const {
- return (local_parameterization_ == NULL)
- ? size_
- : local_parameterization_->LocalSize();
+ return (local_parameterization_ == nullptr)
+ ? size_
+ : local_parameterization_->LocalSize();
}
- // Set the parameterization. The parameterization can be set exactly once;
- // multiple calls to set the parameterization to different values will crash.
- // It is an error to pass NULL for the parameterization. The parameter block
- // does not take ownership of the parameterization.
+ // Set the parameterization. The parameter block does not take
+ // ownership of the parameterization.
void SetParameterization(LocalParameterization* new_parameterization) {
- CHECK(new_parameterization != NULL) << "NULL parameterization invalid.";
// Nothing to do if the new parameterization is the same as the
// old parameterization.
if (new_parameterization == local_parameterization_) {
return;
}
- CHECK(local_parameterization_ == NULL)
- << "Can't re-set the local parameterization; it leads to "
- << "ambiguous ownership. Current local parameterization is: "
- << local_parameterization_;
+ if (new_parameterization == nullptr) {
+ local_parameterization_ = nullptr;
+ return;
+ }
CHECK(new_parameterization->GlobalSize() == size_)
<< "Invalid parameterization for parameter block. The parameter block "
@@ -178,9 +186,9 @@ class ParameterBlock {
<< "size of " << new_parameterization->GlobalSize() << ". Did you "
<< "accidentally use the wrong parameter block or parameterization?";
- CHECK_GT(new_parameterization->LocalSize(), 0)
- << "Invalid parameterization. Parameterizations must have a positive "
- << "dimensional tangent space.";
+ CHECK_GE(new_parameterization->LocalSize(), 0)
+ << "Invalid parameterization. Parameterizations must have a "
+ << "non-negative dimensional tangent space.";
local_parameterization_ = new_parameterization;
local_parameterization_jacobian_.reset(
@@ -194,7 +202,11 @@ class ParameterBlock {
void SetUpperBound(int index, double upper_bound) {
CHECK_LT(index, size_);
- if (upper_bounds_.get() == NULL) {
+ if (upper_bound >= std::numeric_limits<double>::max() && !upper_bounds_) {
+ return;
+ }
+
+ if (!upper_bounds_) {
upper_bounds_.reset(new double[size_]);
std::fill(upper_bounds_.get(),
upper_bounds_.get() + size_,
@@ -207,7 +219,11 @@ class ParameterBlock {
void SetLowerBound(int index, double lower_bound) {
CHECK_LT(index, size_);
- if (lower_bounds_.get() == NULL) {
+ if (lower_bound <= -std::numeric_limits<double>::max() && !lower_bounds_) {
+ return;
+ }
+
+ if (!lower_bounds_) {
lower_bounds_.reset(new double[size_]);
std::fill(lower_bounds_.get(),
lower_bounds_.get() + size_,
@@ -220,24 +236,24 @@ class ParameterBlock {
// Generalization of the addition operation. This is the same as
// LocalParameterization::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_ != NULL) {
+ 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)) {
return false;
}
} else {
- VectorRef(x_plus_delta, size_) = ConstVectorRef(x, size_) +
- ConstVectorRef(delta, size_);
+ VectorRef(x_plus_delta, size_) =
+ ConstVectorRef(x, size_) + ConstVectorRef(delta, size_);
}
// Project onto the box constraints.
- if (lower_bounds_.get() != NULL) {
+ if (lower_bounds_.get() != nullptr) {
for (int i = 0; i < size_; ++i) {
x_plus_delta[i] = std::max(x_plus_delta[i], lower_bounds_[i]);
}
}
- if (upper_bounds_.get() != NULL) {
+ if (upper_bounds_.get() != nullptr) {
for (int i = 0; i < size_; ++i) {
x_plus_delta[i] = std::min(x_plus_delta[i], upper_bounds_[i]);
}
@@ -247,35 +263,36 @@ class ParameterBlock {
}
std::string ToString() const {
- return StringPrintf("{ this=%p, user_state=%p, state=%p, size=%d, "
- "constant=%d, index=%d, state_offset=%d, "
- "delta_offset=%d }",
- this,
- user_state_,
- state_,
- size_,
- is_constant_,
- index_,
- state_offset_,
- delta_offset_);
+ return StringPrintf(
+ "{ this=%p, user_state=%p, state=%p, size=%d, "
+ "constant=%d, index=%d, state_offset=%d, "
+ "delta_offset=%d }",
+ this,
+ user_state_,
+ state_,
+ size_,
+ is_set_constant_,
+ index_,
+ state_offset_,
+ delta_offset_);
}
void EnableResidualBlockDependencies() {
- CHECK(residual_blocks_.get() == NULL)
+ CHECK(residual_blocks_.get() == nullptr)
<< "Ceres bug: There is already a residual block collection "
<< "for parameter block: " << ToString();
residual_blocks_.reset(new ResidualBlockSet);
}
void AddResidualBlock(ResidualBlock* residual_block) {
- CHECK(residual_blocks_.get() != NULL)
+ CHECK(residual_blocks_.get() != nullptr)
<< "Ceres bug: The residual block collection is null for parameter "
<< "block: " << ToString();
residual_blocks_->insert(residual_block);
}
void RemoveResidualBlock(ResidualBlock* residual_block) {
- CHECK(residual_blocks_.get() != NULL)
+ CHECK(residual_blocks_.get() != nullptr)
<< "Ceres bug: The residual block collection is null for parameter "
<< "block: " << ToString();
CHECK(residual_blocks_->find(residual_block) != residual_blocks_->end())
@@ -285,12 +302,10 @@ class ParameterBlock {
// This is only intended for iterating; perhaps this should only expose
// .begin() and .end().
- ResidualBlockSet* mutable_residual_blocks() {
- return residual_blocks_.get();
- }
+ ResidualBlockSet* mutable_residual_blocks() { return residual_blocks_.get(); }
double LowerBoundForParameter(int index) const {
- if (lower_bounds_.get() == NULL) {
+ if (lower_bounds_.get() == nullptr) {
return -std::numeric_limits<double>::max();
} else {
return lower_bounds_[index];
@@ -298,7 +313,7 @@ class ParameterBlock {
}
double UpperBoundForParameter(int index) const {
- if (upper_bounds_.get() == NULL) {
+ if (upper_bounds_.get() == nullptr) {
return std::numeric_limits<double>::max();
} else {
return upper_bounds_[index];
@@ -306,27 +321,8 @@ class ParameterBlock {
}
private:
- void Init(double* user_state,
- int size,
- int index,
- LocalParameterization* local_parameterization) {
- user_state_ = user_state;
- size_ = size;
- index_ = index;
- is_constant_ = false;
- state_ = user_state_;
-
- local_parameterization_ = NULL;
- if (local_parameterization != NULL) {
- SetParameterization(local_parameterization);
- }
-
- state_offset_ = -1;
- delta_offset_ = -1;
- }
-
bool UpdateLocalParameterizationJacobian() {
- if (local_parameterization_ == NULL) {
+ if (local_parameterization_ == nullptr) {
return true;
}
@@ -335,13 +331,12 @@ class ParameterBlock {
// at that time.
const int jacobian_size = Size() * LocalSize();
- InvalidateArray(jacobian_size,
- local_parameterization_jacobian_.get());
+ InvalidateArray(jacobian_size, local_parameterization_jacobian_.get());
if (!local_parameterization_->ComputeJacobian(
- state_,
- local_parameterization_jacobian_.get())) {
+ state_, local_parameterization_jacobian_.get())) {
LOG(WARNING) << "Local parameterization Jacobian computation failed"
- "for x: " << ConstVectorRef(state_, Size()).transpose();
+ "for x: "
+ << ConstVectorRef(state_, Size()).transpose();
return false;
}
@@ -358,30 +353,30 @@ class ParameterBlock {
return true;
}
- double* user_state_;
- int size_;
- bool is_constant_;
- LocalParameterization* local_parameterization_;
+ double* user_state_ = nullptr;
+ int size_ = -1;
+ bool is_set_constant_ = false;
+ LocalParameterization* local_parameterization_ = 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_;
- mutable scoped_array<double> local_parameterization_jacobian_;
+ mutable const double* state_ = nullptr;
+ mutable std::unique_ptr<double[]> local_parameterization_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.
- int32 index_;
+ int index_ = -1;
// The offset of this parameter block inside a larger state vector.
- int32 state_offset_;
+ int state_offset_ = -1;
// The offset of this parameter block inside a larger delta vector.
- int32 delta_offset_;
+ int delta_offset_ = -1;
// If non-null, contains the residual blocks this parameter block is in.
- scoped_ptr<ResidualBlockSet> residual_blocks_;
+ std::unique_ptr<ResidualBlockSet> residual_blocks_;
// Upper and lower bounds for the parameter block. SetUpperBound
// and SetLowerBound lazily initialize the upper_bounds_ and
@@ -394,8 +389,8 @@ class ParameterBlock {
// std::numeric_limits<double>::max() and
// -std::numeric_limits<double>::max() respectively which correspond
// to the parameter block being unconstrained.
- scoped_array<double> upper_bounds_;
- scoped_array<double> lower_bounds_;
+ std::unique_ptr<double[]> upper_bounds_;
+ std::unique_ptr<double[]> lower_bounds_;
// Necessary so ProblemImpl can clean up the parameterizations.
friend class ProblemImpl;
diff --git a/extern/ceres/internal/ceres/parameter_block_ordering.cc b/extern/ceres/internal/ceres/parameter_block_ordering.cc
index efba339977c..ef521c0e11b 100644
--- a/extern/ceres/internal/ceres/parameter_block_ordering.cc
+++ b/extern/ceres/internal/ceres/parameter_block_ordering.cc
@@ -30,9 +30,11 @@
#include "ceres/parameter_block_ordering.h"
+#include <memory>
+#include <unordered_set>
+
#include "ceres/graph.h"
#include "ceres/graph_algorithms.h"
-#include "ceres/internal/scoped_ptr.h"
#include "ceres/map_util.h"
#include "ceres/parameter_block.h"
#include "ceres/program.h"
@@ -49,13 +51,14 @@ using std::vector;
int ComputeStableSchurOrdering(const Program& program,
vector<ParameterBlock*>* ordering) {
- CHECK_NOTNULL(ordering)->clear();
+ CHECK(ordering != nullptr);
+ ordering->clear();
EventLogger event_logger("ComputeStableSchurOrdering");
- scoped_ptr<Graph< ParameterBlock*> > graph(CreateHessianGraph(program));
+ std::unique_ptr<Graph< ParameterBlock*> > graph(CreateHessianGraph(program));
event_logger.AddEvent("CreateHessianGraph");
const vector<ParameterBlock*>& parameter_blocks = program.parameter_blocks();
- const HashSet<ParameterBlock*>& vertices = graph->vertices();
+ 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]);
@@ -80,9 +83,10 @@ int ComputeStableSchurOrdering(const Program& program,
int ComputeSchurOrdering(const Program& program,
vector<ParameterBlock*>* ordering) {
- CHECK_NOTNULL(ordering)->clear();
+ CHECK(ordering != nullptr);
+ ordering->clear();
- scoped_ptr<Graph< ParameterBlock*> > graph(CreateHessianGraph(program));
+ std::unique_ptr<Graph< ParameterBlock*> > graph(CreateHessianGraph(program));
int independent_set_size = IndependentSetOrdering(*graph, ordering);
const vector<ParameterBlock*>& parameter_blocks = program.parameter_blocks();
@@ -99,9 +103,10 @@ int ComputeSchurOrdering(const Program& program,
void ComputeRecursiveIndependentSetOrdering(const Program& program,
ParameterBlockOrdering* ordering) {
- CHECK_NOTNULL(ordering)->Clear();
+ CHECK(ordering != nullptr);
+ ordering->Clear();
const vector<ParameterBlock*> parameter_blocks = program.parameter_blocks();
- scoped_ptr<Graph< ParameterBlock*> > graph(CreateHessianGraph(program));
+ std::unique_ptr<Graph< ParameterBlock*> > graph(CreateHessianGraph(program));
int num_covered = 0;
int round = 0;
@@ -120,7 +125,8 @@ void ComputeRecursiveIndependentSetOrdering(const Program& program,
}
Graph<ParameterBlock*>* CreateHessianGraph(const Program& program) {
- Graph<ParameterBlock*>* graph = CHECK_NOTNULL(new Graph<ParameterBlock*>);
+ Graph<ParameterBlock*>* graph = new 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];
@@ -155,17 +161,16 @@ Graph<ParameterBlock*>* CreateHessianGraph(const Program& program) {
void OrderingToGroupSizes(const ParameterBlockOrdering* ordering,
vector<int>* group_sizes) {
- CHECK_NOTNULL(group_sizes)->clear();
+ CHECK(group_sizes != nullptr);
+ group_sizes->clear();
if (ordering == NULL) {
return;
}
- const map<int, set<double*> >& group_to_elements =
+ const map<int, set<double*>>& group_to_elements =
ordering->group_to_elements();
- for (map<int, set<double*> >::const_iterator it = group_to_elements.begin();
- it != group_to_elements.end();
- ++it) {
- group_sizes->push_back(it->second.size());
+ for (const auto& g_t_e : group_to_elements) {
+ group_sizes->push_back(g_t_e.second.size());
}
}
diff --git a/extern/ceres/internal/ceres/partitioned_matrix_view.cc b/extern/ceres/internal/ceres/partitioned_matrix_view.cc
index 8054964e039..d7a998d68a3 100644
--- a/extern/ceres/internal/ceres/partitioned_matrix_view.cc
+++ b/extern/ceres/internal/ceres/partitioned_matrix_view.cc
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
//=========================================
//
-// This file is generated using generate_partitioned_matrix_view_specializations.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
#include "ceres/linear_solver.h"
#include "ceres/partitioned_matrix_view.h"
@@ -51,126 +50,105 @@ 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]);
- }
- 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]);
- }
- 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]);
- }
- if ((options.row_block_size == 2) &&
- (options.e_block_size == 2) &&
- (options.f_block_size == Eigen::Dynamic)) {
- return new 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]);
- }
- 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]);
- }
- 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]);
- }
- 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]);
- }
- if ((options.row_block_size == 2) &&
- (options.e_block_size == 3) &&
- (options.f_block_size == Eigen::Dynamic)) {
- return new 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]);
- }
- 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]);
- }
- 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]);
- }
- 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]);
- }
- if ((options.row_block_size == 2) &&
- (options.e_block_size == 4) &&
- (options.f_block_size == Eigen::Dynamic)) {
- return new PartitionedMatrixView<2, 4, Eigen::Dynamic>(
- matrix, options.elimination_groups[0]);
- }
- if ((options.row_block_size == 2) &&
- (options.e_block_size == Eigen::Dynamic) &&
- (options.f_block_size == Eigen::Dynamic)) {
- return new PartitionedMatrixView<2, Eigen::Dynamic, Eigen::Dynamic>(
- 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]);
- }
- 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]);
- }
- 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]);
- }
- if ((options.row_block_size == 4) &&
- (options.e_block_size == 4) &&
- (options.f_block_size == Eigen::Dynamic)) {
- return new PartitionedMatrixView<4, 4, Eigen::Dynamic>(
- matrix, options.elimination_groups[0]);
- }
- if ((options.row_block_size == Eigen::Dynamic) &&
- (options.e_block_size == Eigen::Dynamic) &&
- (options.f_block_size == Eigen::Dynamic)) {
- return new PartitionedMatrixView<Eigen::Dynamic, Eigen::Dynamic, Eigen::Dynamic>(
- matrix, options.elimination_groups[0]);
- }
+ 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]);
+ }
+ 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]);
+ }
+ 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]);
+ }
+ if ((options.row_block_size == 2) &&
+ (options.e_block_size == 2)) {
+ return new 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]);
+ }
+ 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]);
+ }
+ 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]);
+ }
+ 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]);
+ }
+ if ((options.row_block_size == 2) &&
+ (options.e_block_size == 3)) {
+ return new 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]);
+ }
+ 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]);
+ }
+ 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]);
+ }
+ 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]);
+ }
+ 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]);
+ }
+ if ((options.row_block_size == 2) &&
+ (options.e_block_size == 4)) {
+ return new 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]);
+ }
+ 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]);
+ }
+ 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]);
+ }
+ 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]);
+ }
+ 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]);
+ }
+ if ((options.row_block_size == 4) &&
+ (options.e_block_size == 4)) {
+ return new PartitionedMatrixView<4, 4, Eigen::Dynamic>(matrix, options.elimination_groups[0]);
+ }
#endif
VLOG(1) << "Template specializations not found for <"
diff --git a/extern/ceres/internal/ceres/partitioned_matrix_view.h b/extern/ceres/internal/ceres/partitioned_matrix_view.h
index 6e75060a47e..3853ea10dd6 100644
--- a/extern/ceres/internal/ceres/partitioned_matrix_view.h
+++ b/extern/ceres/internal/ceres/partitioned_matrix_view.h
@@ -119,20 +119,20 @@ class PartitionedMatrixView : public PartitionedMatrixViewBase {
PartitionedMatrixView(const BlockSparseMatrix& matrix, int num_col_blocks_e);
virtual ~PartitionedMatrixView();
- virtual void LeftMultiplyE(const double* x, double* y) const;
- virtual void LeftMultiplyF(const double* x, double* y) const;
- virtual void RightMultiplyE(const double* x, double* y) const;
- virtual void RightMultiplyF(const double* x, double* y) const;
- virtual BlockSparseMatrix* CreateBlockDiagonalEtE() const;
- virtual BlockSparseMatrix* CreateBlockDiagonalFtF() const;
- virtual void UpdateBlockDiagonalEtE(BlockSparseMatrix* block_diagonal) const;
- virtual void UpdateBlockDiagonalFtF(BlockSparseMatrix* block_diagonal) const;
- virtual int num_col_blocks_e() const { return num_col_blocks_e_; }
- virtual int num_col_blocks_f() const { return num_col_blocks_f_; }
- virtual int num_cols_e() const { return num_cols_e_; }
- virtual int num_cols_f() const { return num_cols_f_; }
- virtual int num_rows() const { return matrix_.num_rows(); }
- virtual int num_cols() const { return matrix_.num_cols(); }
+ 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;
+ void UpdateBlockDiagonalEtE(BlockSparseMatrix* block_diagonal) const final;
+ void UpdateBlockDiagonalFtF(BlockSparseMatrix* block_diagonal) const final;
+ int num_col_blocks_e() const final { return num_col_blocks_e_; }
+ int num_col_blocks_f() const final { return num_col_blocks_f_; }
+ int num_cols_e() const final { return num_cols_e_; }
+ int num_cols_f() const final { return num_cols_f_; }
+ int num_rows() const final { return matrix_.num_rows(); }
+ int num_cols() const final { return matrix_.num_cols(); }
private:
BlockSparseMatrix* CreateBlockDiagonalMatrixLayout(int start_col_block,
diff --git a/extern/ceres/internal/ceres/partitioned_matrix_view_impl.h b/extern/ceres/internal/ceres/partitioned_matrix_view_impl.h
index 86fb278fa27..f3f548c7a80 100644
--- a/extern/ceres/internal/ceres/partitioned_matrix_view_impl.h
+++ b/extern/ceres/internal/ceres/partitioned_matrix_view_impl.h
@@ -50,7 +50,7 @@ PartitionedMatrixView(
: matrix_(matrix),
num_col_blocks_e_(num_col_blocks_e) {
const CompressedRowBlockStructure* bs = matrix_.block_structure();
- CHECK_NOTNULL(bs);
+ CHECK(bs != nullptr);
num_col_blocks_f_ = bs->cols.size() - num_col_blocks_e_;
diff --git a/extern/ceres/internal/ceres/generate_partitioned_matrix_view_specializations.py b/extern/ceres/internal/ceres/partitioned_matrix_view_template.py
index c4ac3cf2332..7894523cdea 100644
--- a/extern/ceres/internal/ceres/generate_partitioned_matrix_view_specializations.py
+++ b/extern/ceres/internal/ceres/partitioned_matrix_view_template.py
@@ -46,29 +46,8 @@
# The list of tuples, specializations indicates the set of
# specializations that is generated.
-# Set of template specializations to generate
-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, 8),
- (2, 4, 9),
- (2, 4, "Eigen::Dynamic"),
- (2, "Eigen::Dynamic", "Eigen::Dynamic"),
- (4, 4, 2),
- (4, 4, 3),
- (4, 4, 4),
- (4, 4, "Eigen::Dynamic"),
- ("Eigen::Dynamic", "Eigen::Dynamic", "Eigen::Dynamic")]
HEADER = """// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -106,8 +85,7 @@ HEADER = """// Ceres Solver - A fast non-linear least squares minimizer
// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
//=========================================
//
-// This file is generated using generate_partitioned_matrix_view_specializations.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
"""
DYNAMIC_FILE = """
@@ -157,14 +135,7 @@ PartitionedMatrixViewBase::Create(const LinearSolver::Options& options,
const BlockSparseMatrix& matrix) {
#ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION
"""
-
-FACTORY_CONDITIONAL = """ if ((options.row_block_size == %s) &&
- (options.e_block_size == %s) &&
- (options.f_block_size == %s)) {
- return new PartitionedMatrixView<%s, %s, %s>(
- matrix, options.elimination_groups[0]);
- }
-"""
+FACTORY = """ return new PartitionedMatrixView<%s, %s, %s>(matrix, options.elimination_groups[0]);"""
FACTORY_FOOTER = """
#endif
@@ -179,54 +150,3 @@ FACTORY_FOOTER = """
} // namespace internal
} // namespace ceres
"""
-
-
-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 Specialize():
- """
- Generate specialization code and the conditionals to instantiate it.
- """
- f = open("partitioned_matrix_view.cc", "w")
- f.write(HEADER)
- f.write(FACTORY_FILE_HEADER)
-
- for row_block_size, e_block_size, f_block_size in SPECIALIZATIONS:
- output = SpecializationFilename("generated/partitioned_matrix_view",
- row_block_size,
- e_block_size,
- f_block_size) + ".cc"
- fptr = open(output, "w")
- fptr.write(HEADER)
-
- template = SPECIALIZATION_FILE
- if (row_block_size == "Eigen::Dynamic" and
- e_block_size == "Eigen::Dynamic" and
- f_block_size == "Eigen::Dynamic"):
- template = DYNAMIC_FILE
-
- fptr.write(template % (row_block_size, e_block_size, f_block_size))
- fptr.close()
-
- f.write(FACTORY_CONDITIONAL % (row_block_size,
- e_block_size,
- f_block_size,
- row_block_size,
- e_block_size,
- f_block_size))
- f.write(FACTORY_FOOTER)
- f.close()
-
-
-if __name__ == "__main__":
- Specialize()
diff --git a/extern/ceres/internal/ceres/polynomial.cc b/extern/ceres/internal/ceres/polynomial.cc
index 13bf8edeee6..20812f4de81 100644
--- a/extern/ceres/internal/ceres/polynomial.cc
+++ b/extern/ceres/internal/ceres/polynomial.cc
@@ -36,14 +36,13 @@
#include <vector>
#include "Eigen/Dense"
+#include "ceres/function_sample.h"
#include "ceres/internal/port.h"
-#include "ceres/stringprintf.h"
#include "glog/logging.h"
namespace ceres {
namespace internal {
-using std::string;
using std::vector;
namespace {
@@ -53,7 +52,7 @@ namespace {
// In: Numerische Mathematik, Volume 13, Number 4 (1969), 293-304,
// Springer Berlin / Heidelberg. DOI: 10.1007/BF02165404
void BalanceCompanionMatrix(Matrix* companion_matrix_ptr) {
- CHECK_NOTNULL(companion_matrix_ptr);
+ CHECK(companion_matrix_ptr != nullptr);
Matrix& companion_matrix = *companion_matrix_ptr;
Matrix companion_matrix_offdiagonal = companion_matrix;
companion_matrix_offdiagonal.diagonal().setZero();
@@ -105,7 +104,7 @@ void BalanceCompanionMatrix(Matrix* companion_matrix_ptr) {
void BuildCompanionMatrix(const Vector& polynomial,
Matrix* companion_matrix_ptr) {
- CHECK_NOTNULL(companion_matrix_ptr);
+ CHECK(companion_matrix_ptr != nullptr);
Matrix& companion_matrix = *companion_matrix_ptr;
const int degree = polynomial.size() - 1;
@@ -327,12 +326,6 @@ void MinimizePolynomial(const Vector& polynomial,
}
}
-string FunctionSample::ToDebugString() const {
- return StringPrintf("[x: %.8e, value: %.8e, gradient: %.8e, "
- "value_is_valid: %d, gradient_is_valid: %d]",
- x, value, gradient, value_is_valid, gradient_is_valid);
-}
-
Vector FindInterpolatingPolynomial(const vector<FunctionSample>& samples) {
const int num_samples = samples.size();
int num_constraints = 0;
@@ -370,7 +363,10 @@ Vector FindInterpolatingPolynomial(const vector<FunctionSample>& samples) {
}
}
- return lhs.fullPivLu().solve(rhs);
+ // TODO(sameeragarwal): This is a hack.
+ // https://github.com/ceres-solver/ceres-solver/issues/248
+ Eigen::FullPivLU<Matrix> lu(lhs);
+ return lu.setThreshold(0.0).solve(rhs);
}
void MinimizeInterpolatingPolynomial(const vector<FunctionSample>& samples,
diff --git a/extern/ceres/internal/ceres/polynomial.h b/extern/ceres/internal/ceres/polynomial.h
index 09a64c577f5..3e09bae3d0f 100644
--- a/extern/ceres/internal/ceres/polynomial.h
+++ b/extern/ceres/internal/ceres/polynomial.h
@@ -32,7 +32,6 @@
#ifndef CERES_INTERNAL_POLYNOMIAL_SOLVER_H_
#define CERES_INTERNAL_POLYNOMIAL_SOLVER_H_
-#include <string>
#include <vector>
#include "ceres/internal/eigen.h"
#include "ceres/internal/port.h"
@@ -40,6 +39,8 @@
namespace ceres {
namespace internal {
+struct FunctionSample;
+
// All polynomials are assumed to be the form
//
// sum_{i=0}^N polynomial(i) x^{N-i}.
@@ -84,27 +85,6 @@ void MinimizePolynomial(const Vector& polynomial,
double* optimal_x,
double* optimal_value);
-// Structure for storing sample values of a function.
-//
-// Clients can use this struct to communicate the value of the
-// function and or its gradient at a given point x.
-struct FunctionSample {
- FunctionSample()
- : x(0.0),
- value(0.0),
- value_is_valid(false),
- gradient(0.0),
- gradient_is_valid(false) {
- }
- std::string ToDebugString() const;
-
- double x;
- double value; // value = f(x)
- bool value_is_valid;
- double gradient; // gradient = f'(x)
- bool gradient_is_valid;
-};
-
// Given a set of function value and/or gradient samples, find a
// polynomial whose value and gradients are exactly equal to the ones
// in samples.
diff --git a/extern/ceres/internal/ceres/preconditioner.cc b/extern/ceres/internal/ceres/preconditioner.cc
index 82621dae50c..f98374e0cf8 100644
--- a/extern/ceres/internal/ceres/preconditioner.cc
+++ b/extern/ceres/internal/ceres/preconditioner.cc
@@ -49,7 +49,8 @@ PreconditionerType Preconditioner::PreconditionerForZeroEBlocks(
SparseMatrixPreconditionerWrapper::SparseMatrixPreconditionerWrapper(
const SparseMatrix* matrix)
- : matrix_(CHECK_NOTNULL(matrix)) {
+ : matrix_(matrix) {
+ CHECK(matrix != nullptr);
}
SparseMatrixPreconditionerWrapper::~SparseMatrixPreconditionerWrapper() {
diff --git a/extern/ceres/internal/ceres/preconditioner.h b/extern/ceres/internal/ceres/preconditioner.h
index a248eae060d..3e46ed83db2 100644
--- a/extern/ceres/internal/ceres/preconditioner.h
+++ b/extern/ceres/internal/ceres/preconditioner.h
@@ -34,6 +34,7 @@
#include <vector>
#include "ceres/casts.h"
#include "ceres/compressed_row_sparse_matrix.h"
+#include "ceres/context_impl.h"
#include "ceres/linear_operator.h"
#include "ceres/sparse_matrix.h"
#include "ceres/types.h"
@@ -47,22 +48,27 @@ class SparseMatrix;
class Preconditioner : public LinearOperator {
public:
struct Options {
- Options()
- : type(JACOBI),
- visibility_clustering_type(CANONICAL_VIEWS),
- sparse_linear_algebra_library_type(SUITE_SPARSE),
- num_threads(1),
- row_block_size(Eigen::Dynamic),
- e_block_size(Eigen::Dynamic),
- f_block_size(Eigen::Dynamic) {
- }
-
- PreconditionerType type;
- VisibilityClusteringType visibility_clustering_type;
- SparseLinearAlgebraLibraryType sparse_linear_algebra_library_type;
+ PreconditionerType type = JACOBI;
+ VisibilityClusteringType visibility_clustering_type = CANONICAL_VIEWS;
+ SparseLinearAlgebraLibraryType sparse_linear_algebra_library_type = SUITE_SPARSE;
+
+ // When using the subset preconditioner, all row blocks starting
+ // from this row block are used to construct the preconditioner.
+ //
+ // i.e., the Jacobian matrix A is horizontally partitioned as
+ //
+ // A = [P]
+ // [Q]
+ //
+ // where P has subset_preconditioner_start_row_block row blocks,
+ // and the preconditioner is the inverse of the matrix Q'Q.
+ int subset_preconditioner_start_row_block = -1;
+
+ // See solver.h for information about these flags.
+ bool use_postordering = false;
// If possible, how many threads the preconditioner can use.
- int num_threads;
+ int num_threads = 1;
// Hints about the order in which the parameter blocks should be
// eliminated by the linear solver.
@@ -91,9 +97,11 @@ class Preconditioner : public LinearOperator {
//
// Please see schur_complement_solver.h and schur_eliminator.h for
// more details.
- int row_block_size;
- int e_block_size;
- int f_block_size;
+ int row_block_size = Eigen::Dynamic;
+ int e_block_size = Eigen::Dynamic;
+ int f_block_size = Eigen::Dynamic;
+
+ ContextImpl* context = nullptr;
};
// If the optimization problem is such that there are no remaining
@@ -123,13 +131,13 @@ class Preconditioner : public LinearOperator {
// LeftMultiply and num_cols are just calls to RightMultiply and
// num_rows respectively. Update() must be called before
// RightMultiply can be called.
- virtual void RightMultiply(const double* x, double* y) const = 0;
- virtual void LeftMultiply(const double* x, double* y) const {
+ void RightMultiply(const double* x, double* y) const override = 0;
+ void LeftMultiply(const double* x, double* y) const override {
return RightMultiply(x, y);
}
- virtual int num_rows() const = 0;
- virtual int num_cols() const {
+ int num_rows() const override = 0;
+ int num_cols() const override {
return num_rows();
}
};
@@ -141,7 +149,7 @@ template <typename MatrixType>
class TypedPreconditioner : public Preconditioner {
public:
virtual ~TypedPreconditioner() {}
- virtual bool Update(const LinearOperator& A, const double* D) {
+ bool Update(const LinearOperator& A, const double* D) final {
return UpdateImpl(*down_cast<const MatrixType*>(&A), D);
}
@@ -149,7 +157,7 @@ class TypedPreconditioner : public Preconditioner {
virtual bool UpdateImpl(const MatrixType& A, const double* D) = 0;
};
-// Preconditioners that depend on acccess to the low level structure
+// Preconditioners that depend on access to the low level structure
// of a SparseMatrix.
typedef TypedPreconditioner<SparseMatrix> SparseMatrixPreconditioner; // NOLINT
typedef TypedPreconditioner<BlockSparseMatrix> BlockSparseMatrixPreconditioner; // NOLINT
diff --git a/extern/ceres/internal/ceres/preprocessor.cc b/extern/ceres/internal/ceres/preprocessor.cc
index 4aba6a39ce8..02219147d75 100644
--- a/extern/ceres/internal/ceres/preprocessor.cc
+++ b/extern/ceres/internal/ceres/preprocessor.cc
@@ -31,6 +31,7 @@
#include "ceres/callbacks.h"
#include "ceres/gradient_checking_cost_function.h"
#include "ceres/line_search_preprocessor.h"
+#include "ceres/parallel_for.h"
#include "ceres/preprocessor.h"
#include "ceres/problem_impl.h"
#include "ceres/solver.h"
@@ -56,25 +57,15 @@ Preprocessor::~Preprocessor() {
}
void ChangeNumThreadsIfNeeded(Solver::Options* options) {
-#ifndef CERES_USE_OPENMP
- if (options->num_threads > 1) {
+ const int num_threads_available = MaxNumThreadsAvailable();
+ if (options->num_threads > num_threads_available) {
LOG(WARNING)
- << "OpenMP support is not compiled into this binary; "
- << "only options.num_threads = 1 is supported. Switching "
- << "to single threaded mode.";
- options->num_threads = 1;
+ << "Specified options.num_threads: " << options->num_threads
+ << " exceeds maximum available from the threading model Ceres "
+ << "was compiled with: " << num_threads_available
+ << ". Bounding to maximum number available.";
+ options->num_threads = num_threads_available;
}
-
- // Only the Trust Region solver currently uses a linear solver.
- if (options->minimizer_type == TRUST_REGION &&
- options->num_linear_solver_threads > 1) {
- LOG(WARNING)
- << "OpenMP support is not compiled into this binary; "
- << "only options.num_linear_solver_threads=1 is supported. Switching "
- << "to single threaded mode.";
- options->num_linear_solver_threads = 1;
- }
-#endif // CERES_USE_OPENMP
}
void SetupCommonMinimizerOptions(PreprocessedProblem* pp) {
diff --git a/extern/ceres/internal/ceres/preprocessor.h b/extern/ceres/internal/ceres/preprocessor.h
index ff53d6f0d3f..99bd6c0c5dd 100644
--- a/extern/ceres/internal/ceres/preprocessor.h
+++ b/extern/ceres/internal/ceres/preprocessor.h
@@ -31,6 +31,7 @@
#ifndef CERES_INTERNAL_PREPROCESSOR_H_
#define CERES_INTERNAL_PREPROCESSOR_H_
+#include <memory>
#include <string>
#include <vector>
@@ -38,7 +39,6 @@
#include "ceres/evaluator.h"
#include "ceres/internal/eigen.h"
#include "ceres/internal/port.h"
-#include "ceres/internal/scoped_ptr.h"
#include "ceres/iteration_callback.h"
#include "ceres/linear_solver.h"
#include "ceres/minimizer.h"
@@ -91,14 +91,14 @@ struct PreprocessedProblem {
Minimizer::Options minimizer_options;
ProblemImpl* problem;
- scoped_ptr<ProblemImpl> gradient_checking_problem;
- scoped_ptr<Program> reduced_program;
- scoped_ptr<LinearSolver> linear_solver;
- scoped_ptr<IterationCallback> logging_callback;
- scoped_ptr<IterationCallback> state_updating_callback;
+ std::unique_ptr<ProblemImpl> gradient_checking_problem;
+ std::unique_ptr<Program> reduced_program;
+ std::unique_ptr<LinearSolver> linear_solver;
+ std::unique_ptr<IterationCallback> logging_callback;
+ std::unique_ptr<IterationCallback> state_updating_callback;
- shared_ptr<Evaluator> evaluator;
- shared_ptr<CoordinateDescentMinimizer> inner_iteration_minimizer;
+ std::shared_ptr<Evaluator> evaluator;
+ std::shared_ptr<CoordinateDescentMinimizer> inner_iteration_minimizer;
std::vector<double*> removed_parameter_blocks;
Vector reduced_parameters;
@@ -107,8 +107,9 @@ struct PreprocessedProblem {
// Common functions used by various preprocessors.
-// If OpenMP support is not available and user has requested more than
-// one thread, then set the *_num_threads options as needed to 1.
+// 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.
void ChangeNumThreadsIfNeeded(Solver::Options* options);
// Extract the effective parameter vector from the preprocessed
diff --git a/extern/ceres/internal/ceres/problem.cc b/extern/ceres/internal/ceres/problem.cc
index 730ce642036..767fe977296 100644
--- a/extern/ceres/internal/ceres/problem.cc
+++ b/extern/ceres/internal/ceres/problem.cc
@@ -32,6 +32,7 @@
#include "ceres/problem.h"
#include <vector>
+
#include "ceres/crs_matrix.h"
#include "ceres/problem_impl.h"
@@ -39,166 +40,90 @@ namespace ceres {
using std::vector;
-Problem::Problem() : problem_impl_(new internal::ProblemImpl) {}
+Problem::Problem() : impl_(new internal::ProblemImpl) {}
Problem::Problem(const Problem::Options& options)
- : problem_impl_(new internal::ProblemImpl(options)) {}
+ : impl_(new internal::ProblemImpl(options)) {}
+// Not inline defaulted in declaration due to use of std::unique_ptr.
+Problem::Problem(Problem&&) = default;
+Problem& Problem::operator=(Problem&&) = default;
Problem::~Problem() {}
ResidualBlockId Problem::AddResidualBlock(
CostFunction* cost_function,
LossFunction* loss_function,
const vector<double*>& parameter_blocks) {
- return problem_impl_->AddResidualBlock(cost_function,
- loss_function,
- parameter_blocks);
-}
-
-ResidualBlockId Problem::AddResidualBlock(
- CostFunction* cost_function,
- LossFunction* loss_function,
- double* x0) {
- return problem_impl_->AddResidualBlock(cost_function,
- loss_function,
- x0);
-}
-
-ResidualBlockId Problem::AddResidualBlock(
- CostFunction* cost_function,
- LossFunction* loss_function,
- double* x0, double* x1) {
- return problem_impl_->AddResidualBlock(cost_function,
- loss_function,
- x0, x1);
-}
-
-ResidualBlockId Problem::AddResidualBlock(
- CostFunction* cost_function,
- LossFunction* loss_function,
- double* x0, double* x1, double* x2) {
- return problem_impl_->AddResidualBlock(cost_function,
- loss_function,
- x0, x1, x2);
-}
-
-ResidualBlockId Problem::AddResidualBlock(
- CostFunction* cost_function,
- LossFunction* loss_function,
- double* x0, double* x1, double* x2, double* x3) {
- return problem_impl_->AddResidualBlock(cost_function,
- loss_function,
- x0, x1, x2, x3);
-}
-
-ResidualBlockId Problem::AddResidualBlock(
- CostFunction* cost_function,
- LossFunction* loss_function,
- double* x0, double* x1, double* x2, double* x3, double* x4) {
- return problem_impl_->AddResidualBlock(cost_function,
- loss_function,
- x0, x1, x2, x3, x4);
-}
-
-ResidualBlockId Problem::AddResidualBlock(
- CostFunction* cost_function,
- LossFunction* loss_function,
- double* x0, double* x1, double* x2, double* x3, double* x4, double* x5) {
- return problem_impl_->AddResidualBlock(cost_function,
- loss_function,
- x0, x1, x2, x3, x4, x5);
-}
-
-ResidualBlockId Problem::AddResidualBlock(
- CostFunction* cost_function,
- LossFunction* loss_function,
- double* x0, double* x1, double* x2, double* x3, double* x4, double* x5,
- double* x6) {
- return problem_impl_->AddResidualBlock(cost_function,
- loss_function,
- x0, x1, x2, x3, x4, x5, x6);
+ return impl_->AddResidualBlock(cost_function,
+ loss_function,
+ parameter_blocks.data(),
+ static_cast<int>(parameter_blocks.size()));
}
-ResidualBlockId Problem::AddResidualBlock(
- CostFunction* cost_function,
- LossFunction* loss_function,
- double* x0, double* x1, double* x2, double* x3, double* x4, double* x5,
- double* x6, double* x7) {
- return problem_impl_->AddResidualBlock(cost_function,
- loss_function,
- x0, x1, x2, x3, x4, x5, x6, x7);
-}
-
-ResidualBlockId Problem::AddResidualBlock(
- CostFunction* cost_function,
- LossFunction* loss_function,
- double* x0, double* x1, double* x2, double* x3, double* x4, double* x5,
- double* x6, double* x7, double* x8) {
- return problem_impl_->AddResidualBlock(cost_function,
- loss_function,
- x0, x1, x2, x3, x4, x5, x6, x7, x8);
-}
-
-ResidualBlockId Problem::AddResidualBlock(
- CostFunction* cost_function,
- LossFunction* loss_function,
- double* x0, double* x1, double* x2, double* x3, double* x4, double* x5,
- double* x6, double* x7, double* x8, double* x9) {
- return problem_impl_->AddResidualBlock(
- cost_function,
- loss_function,
- x0, x1, x2, x3, x4, x5, x6, x7, x8, x9);
+ResidualBlockId Problem::AddResidualBlock(CostFunction* cost_function,
+ LossFunction* loss_function,
+ double* const* const parameter_blocks,
+ int num_parameter_blocks) {
+ return impl_->AddResidualBlock(
+ cost_function, loss_function, parameter_blocks, num_parameter_blocks);
}
void Problem::AddParameterBlock(double* values, int size) {
- problem_impl_->AddParameterBlock(values, size);
+ impl_->AddParameterBlock(values, size);
}
void Problem::AddParameterBlock(double* values,
int size,
LocalParameterization* local_parameterization) {
- problem_impl_->AddParameterBlock(values, size, local_parameterization);
+ impl_->AddParameterBlock(values, size, local_parameterization);
}
void Problem::RemoveResidualBlock(ResidualBlockId residual_block) {
- problem_impl_->RemoveResidualBlock(residual_block);
+ impl_->RemoveResidualBlock(residual_block);
}
-void Problem::RemoveParameterBlock(double* values) {
- problem_impl_->RemoveParameterBlock(values);
+void Problem::RemoveParameterBlock(const double* values) {
+ impl_->RemoveParameterBlock(values);
}
-void Problem::SetParameterBlockConstant(double* values) {
- problem_impl_->SetParameterBlockConstant(values);
+void Problem::SetParameterBlockConstant(const double* values) {
+ impl_->SetParameterBlockConstant(values);
}
void Problem::SetParameterBlockVariable(double* values) {
- problem_impl_->SetParameterBlockVariable(values);
+ impl_->SetParameterBlockVariable(values);
}
-bool Problem::IsParameterBlockConstant(double* values) const {
- return problem_impl_->IsParameterBlockConstant(values);
+bool Problem::IsParameterBlockConstant(const double* values) const {
+ return impl_->IsParameterBlockConstant(values);
}
void Problem::SetParameterization(
- double* values,
- LocalParameterization* local_parameterization) {
- problem_impl_->SetParameterization(values, local_parameterization);
+ double* values, LocalParameterization* local_parameterization) {
+ impl_->SetParameterization(values, local_parameterization);
}
const LocalParameterization* Problem::GetParameterization(
- double* values) const {
- return problem_impl_->GetParameterization(values);
+ const double* values) const {
+ return impl_->GetParameterization(values);
}
void Problem::SetParameterLowerBound(double* values,
int index,
double lower_bound) {
- problem_impl_->SetParameterLowerBound(values, index, lower_bound);
+ impl_->SetParameterLowerBound(values, index, lower_bound);
}
void Problem::SetParameterUpperBound(double* values,
int index,
double upper_bound) {
- problem_impl_->SetParameterUpperBound(values, index, upper_bound);
+ impl_->SetParameterUpperBound(values, index, upper_bound);
+}
+
+double Problem::GetParameterUpperBound(const double* values, int index) const {
+ return impl_->GetParameterUpperBound(values, index);
+}
+
+double Problem::GetParameterLowerBound(const double* values, int index) const {
+ return impl_->GetParameterLowerBound(values, index);
}
bool Problem::Evaluate(const EvaluateOptions& evaluate_options,
@@ -206,72 +131,66 @@ bool Problem::Evaluate(const EvaluateOptions& evaluate_options,
vector<double>* residuals,
vector<double>* gradient,
CRSMatrix* jacobian) {
- return problem_impl_->Evaluate(evaluate_options,
- cost,
- residuals,
- gradient,
- jacobian);
+ return impl_->Evaluate(evaluate_options, cost, residuals, gradient, jacobian);
}
-int Problem::NumParameterBlocks() const {
- return problem_impl_->NumParameterBlocks();
+bool Problem::EvaluateResidualBlock(ResidualBlockId residual_block_id,
+ bool apply_loss_function,
+ double* cost,
+ double* residuals,
+ double** jacobians) const {
+ return impl_->EvaluateResidualBlock(
+ residual_block_id, apply_loss_function, cost, residuals, jacobians);
}
-int Problem::NumParameters() const {
- return problem_impl_->NumParameters();
-}
+int Problem::NumParameterBlocks() const { return impl_->NumParameterBlocks(); }
-int Problem::NumResidualBlocks() const {
- return problem_impl_->NumResidualBlocks();
-}
+int Problem::NumParameters() const { return impl_->NumParameters(); }
-int Problem::NumResiduals() const {
- return problem_impl_->NumResiduals();
-}
+int Problem::NumResidualBlocks() const { return impl_->NumResidualBlocks(); }
+
+int Problem::NumResiduals() const { return impl_->NumResiduals(); }
int Problem::ParameterBlockSize(const double* parameter_block) const {
- return problem_impl_->ParameterBlockSize(parameter_block);
+ return impl_->ParameterBlockSize(parameter_block);
}
int Problem::ParameterBlockLocalSize(const double* parameter_block) const {
- return problem_impl_->ParameterBlockLocalSize(parameter_block);
+ return impl_->ParameterBlockLocalSize(parameter_block);
}
bool Problem::HasParameterBlock(const double* values) const {
- return problem_impl_->HasParameterBlock(values);
+ return impl_->HasParameterBlock(values);
}
void Problem::GetParameterBlocks(vector<double*>* parameter_blocks) const {
- problem_impl_->GetParameterBlocks(parameter_blocks);
+ impl_->GetParameterBlocks(parameter_blocks);
}
void Problem::GetResidualBlocks(
vector<ResidualBlockId>* residual_blocks) const {
- problem_impl_->GetResidualBlocks(residual_blocks);
+ impl_->GetResidualBlocks(residual_blocks);
}
void Problem::GetParameterBlocksForResidualBlock(
const ResidualBlockId residual_block,
vector<double*>* parameter_blocks) const {
- problem_impl_->GetParameterBlocksForResidualBlock(residual_block,
- parameter_blocks);
+ impl_->GetParameterBlocksForResidualBlock(residual_block, parameter_blocks);
}
const CostFunction* Problem::GetCostFunctionForResidualBlock(
const ResidualBlockId residual_block) const {
- return problem_impl_->GetCostFunctionForResidualBlock(residual_block);
+ return impl_->GetCostFunctionForResidualBlock(residual_block);
}
const LossFunction* Problem::GetLossFunctionForResidualBlock(
const ResidualBlockId residual_block) const {
- return problem_impl_->GetLossFunctionForResidualBlock(residual_block);
+ return impl_->GetLossFunctionForResidualBlock(residual_block);
}
void Problem::GetResidualBlocksForParameterBlock(
- const double* values,
- vector<ResidualBlockId>* residual_blocks) const {
- problem_impl_->GetResidualBlocksForParameterBlock(values,
- residual_blocks);
+ const double* values, vector<ResidualBlockId>* residual_blocks) const {
+ impl_->GetResidualBlocksForParameterBlock(values, residual_blocks);
}
} // namespace ceres
diff --git a/extern/ceres/internal/ceres/problem_impl.cc b/extern/ceres/internal/ceres/problem_impl.cc
index 4abea8b33ee..6cc4d336c6a 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 2015 Google Inc. All rights reserved.
+// Copyright 2019 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -33,21 +33,31 @@
#include <algorithm>
#include <cstddef>
+#include <cstdint>
#include <iterator>
+#include <memory>
#include <set>
#include <string>
#include <utility>
#include <vector>
+
#include "ceres/casts.h"
+#include "ceres/compressed_row_jacobian_writer.h"
#include "ceres/compressed_row_sparse_matrix.h"
+#include "ceres/context_impl.h"
#include "ceres/cost_function.h"
#include "ceres/crs_matrix.h"
+#include "ceres/evaluation_callback.h"
#include "ceres/evaluator.h"
+#include "ceres/internal/fixed_array.h"
+#include "ceres/internal/port.h"
#include "ceres/loss_function.h"
#include "ceres/map_util.h"
#include "ceres/parameter_block.h"
#include "ceres/program.h"
+#include "ceres/program_evaluator.h"
#include "ceres/residual_block.h"
+#include "ceres/scratch_evaluate_preparer.h"
#include "ceres/stl_util.h"
#include "ceres/stringprintf.h"
#include "glog/logging.h"
@@ -58,36 +68,66 @@ namespace internal {
using std::map;
using std::string;
using std::vector;
-typedef std::map<double*, internal::ParameterBlock*> ParameterMap;
namespace {
// Returns true if two regions of memory, a and b, with sizes size_a and size_b
// respectively, overlap.
-bool RegionsAlias(const double* a, int size_a,
- const double* b, int size_b) {
- return (a < b) ? b < (a + size_a)
- : a < (b + size_b);
+bool RegionsAlias(const double* a, int size_a, const double* b, int size_b) {
+ return (a < b) ? b < (a + size_a) : a < (b + size_b);
}
void CheckForNoAliasing(double* existing_block,
int existing_block_size,
double* new_block,
int new_block_size) {
- CHECK(!RegionsAlias(existing_block, existing_block_size,
- new_block, new_block_size))
+ CHECK(!RegionsAlias(
+ existing_block, existing_block_size, new_block, new_block_size))
<< "Aliasing detected between existing parameter block at memory "
- << "location " << existing_block
- << " and has size " << existing_block_size << " with new parameter "
+ << "location " << existing_block << " and has size "
+ << existing_block_size << " with new parameter "
<< "block that has memory address " << new_block << " and would have "
<< "size " << new_block_size << ".";
}
+template <typename KeyType>
+void DecrementValueOrDeleteKey(const KeyType key,
+ std::map<KeyType, int>* container) {
+ auto it = container->find(key);
+ if (it->second == 1) {
+ delete key;
+ container->erase(it);
+ } else {
+ --it->second;
+ }
+}
+
+template <typename ForwardIterator>
+void STLDeleteContainerPairFirstPointers(ForwardIterator begin,
+ ForwardIterator end) {
+ while (begin != end) {
+ delete begin->first;
+ ++begin;
+ }
+}
+
+void InitializeContext(Context* context,
+ ContextImpl** context_impl,
+ bool* context_impl_owned) {
+ if (context == nullptr) {
+ *context_impl_owned = true;
+ *context_impl = new ContextImpl;
+ } else {
+ *context_impl_owned = false;
+ *context_impl = down_cast<ContextImpl*>(context);
+ }
+}
+
} // namespace
ParameterBlock* ProblemImpl::InternalAddParameterBlock(double* values,
int size) {
- CHECK(values != NULL) << "Null pointer passed to AddParameterBlock "
- << "for a parameter with size " << size;
+ CHECK(values != nullptr) << "Null pointer passed to AddParameterBlock "
+ << "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);
@@ -97,8 +137,7 @@ ParameterBlock* ProblemImpl::InternalAddParameterBlock(double* values,
CHECK(size == existing_size)
<< "Tried adding a parameter block with the same double pointer, "
<< values << ", twice, but with different block sizes. Original "
- << "size was " << existing_size << " but new size is "
- << size;
+ << "size was " << existing_size << " but new size is " << size;
}
return it->second;
}
@@ -113,18 +152,13 @@ ParameterBlock* ProblemImpl::InternalAddParameterBlock(double* values,
if (lb != parameter_block_map_.begin()) {
ParameterMap::iterator previous = lb;
--previous;
- CheckForNoAliasing(previous->first,
- previous->second->Size(),
- values,
- size);
+ CheckForNoAliasing(
+ previous->first, previous->second->Size(), values, size);
}
// If lb is not off the end, check lb for aliasing.
if (lb != parameter_block_map_.end()) {
- CheckForNoAliasing(lb->first,
- lb->second->Size(),
- values,
- size);
+ CheckForNoAliasing(lb->first, lb->second->Size(), values, size);
}
}
}
@@ -145,7 +179,7 @@ ParameterBlock* ProblemImpl::InternalAddParameterBlock(double* values,
}
void ProblemImpl::InternalRemoveResidualBlock(ResidualBlock* residual_block) {
- CHECK_NOTNULL(residual_block);
+ CHECK(residual_block != nullptr);
// Perform no check on the validity of residual_block, that is handled in
// the public method: RemoveResidualBlock().
@@ -154,8 +188,8 @@ void ProblemImpl::InternalRemoveResidualBlock(ResidualBlock* residual_block) {
const int num_parameter_blocks_for_residual =
residual_block->NumParameterBlocks();
for (int i = 0; i < num_parameter_blocks_for_residual; ++i) {
- residual_block->parameter_blocks()[i]
- ->RemoveResidualBlock(residual_block);
+ residual_block->parameter_blocks()[i]->RemoveResidualBlock(
+ residual_block);
}
ResidualBlockSet::iterator it = residual_block_set_.find(residual_block);
@@ -173,16 +207,19 @@ 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.
- if (options_.cost_function_ownership == TAKE_OWNERSHIP &&
- residual_block->cost_function() != NULL) {
- cost_functions_to_delete_.push_back(
- const_cast<CostFunction*>(residual_block->cost_function()));
+ CostFunction* 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 =
+ const_cast<LossFunction*>(residual_block->loss_function());
if (options_.loss_function_ownership == TAKE_OWNERSHIP &&
- residual_block->loss_function() != NULL) {
- loss_functions_to_delete_.push_back(
- const_cast<LossFunction*>(residual_block->loss_function()));
+ loss_function != nullptr) {
+ DecrementValueOrDeleteKey(loss_function, &loss_function_ref_count_);
}
+
delete residual_block;
}
@@ -193,7 +230,7 @@ void ProblemImpl::DeleteBlock(ResidualBlock* residual_block) {
// without doing a full scan.
void ProblemImpl::DeleteBlock(ParameterBlock* parameter_block) {
if (options_.local_parameterization_ownership == TAKE_OWNERSHIP &&
- parameter_block->local_parameterization() != NULL) {
+ parameter_block->local_parameterization() != nullptr) {
local_parameterizations_to_delete_.push_back(
parameter_block->mutable_local_parameterization());
}
@@ -201,18 +238,29 @@ void ProblemImpl::DeleteBlock(ParameterBlock* parameter_block) {
delete parameter_block;
}
-ProblemImpl::ProblemImpl() : program_(new internal::Program) {}
+ProblemImpl::ProblemImpl()
+ : options_(Problem::Options()), program_(new internal::Program) {
+ InitializeContext(options_.context, &context_impl_, &context_impl_owned_);
+}
+
ProblemImpl::ProblemImpl(const Problem::Options& options)
- : options_(options),
- program_(new internal::Program) {}
+ : options_(options), program_(new internal::Program) {
+ program_->evaluation_callback_ = options.evaluation_callback;
+ InitializeContext(options_.context, &context_impl_, &context_impl_owned_);
+}
ProblemImpl::~ProblemImpl() {
- // Collect the unique cost/loss functions and delete the residuals.
- const int num_residual_blocks = program_->residual_blocks_.size();
- cost_functions_to_delete_.reserve(num_residual_blocks);
- loss_functions_to_delete_.reserve(num_residual_blocks);
- for (int i = 0; i < program_->residual_blocks_.size(); ++i) {
- DeleteBlock(program_->residual_blocks_[i]);
+ STLDeleteContainerPointers(program_->residual_blocks_.begin(),
+ program_->residual_blocks_.end());
+
+ if (options_.cost_function_ownership == TAKE_OWNERSHIP) {
+ STLDeleteContainerPairFirstPointers(cost_function_ref_count_.begin(),
+ cost_function_ref_count_.end());
+ }
+
+ if (options_.loss_function_ownership == TAKE_OWNERSHIP) {
+ STLDeleteContainerPairFirstPointers(loss_function_ref_count_.begin(),
+ loss_function_ref_count_.end());
}
// Collect the unique parameterizations and delete the parameters.
@@ -220,57 +268,57 @@ ProblemImpl::~ProblemImpl() {
DeleteBlock(program_->parameter_blocks_[i]);
}
- // Delete the owned cost/loss functions and parameterizations.
+ // Delete the owned parameterizations.
STLDeleteUniqueContainerPointers(local_parameterizations_to_delete_.begin(),
local_parameterizations_to_delete_.end());
- STLDeleteUniqueContainerPointers(cost_functions_to_delete_.begin(),
- cost_functions_to_delete_.end());
- STLDeleteUniqueContainerPointers(loss_functions_to_delete_.begin(),
- loss_functions_to_delete_.end());
+
+ if (context_impl_owned_) {
+ delete context_impl_;
+ }
}
-ResidualBlock* ProblemImpl::AddResidualBlock(
+ResidualBlockId ProblemImpl::AddResidualBlock(
CostFunction* cost_function,
LossFunction* loss_function,
- const vector<double*>& parameter_blocks) {
- CHECK_NOTNULL(cost_function);
- CHECK_EQ(parameter_blocks.size(),
- cost_function->parameter_block_sizes().size());
+ double* const* const parameter_blocks,
+ int num_parameter_blocks) {
+ CHECK(cost_function != nullptr);
+ CHECK_EQ(num_parameter_blocks, cost_function->parameter_block_sizes().size());
// Check the sizes match.
- const vector<int32>& parameter_block_sizes =
+ const vector<int32_t>& parameter_block_sizes =
cost_function->parameter_block_sizes();
if (!options_.disable_all_safety_checks) {
- CHECK_EQ(parameter_block_sizes.size(), parameter_blocks.size())
+ CHECK_EQ(parameter_block_sizes.size(), num_parameter_blocks)
<< "Number of blocks input is different than the number of blocks "
<< "that the cost function expects.";
// Check for duplicate parameter blocks.
- vector<double*> sorted_parameter_blocks(parameter_blocks);
+ 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 =
(std::adjacent_find(sorted_parameter_blocks.begin(),
- sorted_parameter_blocks.end())
- != sorted_parameter_blocks.end());
+ sorted_parameter_blocks.end()) !=
+ sorted_parameter_blocks.end());
if (has_duplicate_items) {
string blocks;
- for (int i = 0; i < parameter_blocks.size(); ++i) {
+ for (int i = 0; i < num_parameter_blocks; ++i) {
blocks += StringPrintf(" %p ", parameter_blocks[i]);
}
LOG(FATAL) << "Duplicate parameter blocks in a residual parameter "
- << "are not allowed. Parameter block pointers: ["
- << blocks << "]";
+ << "are not allowed. Parameter block pointers: [" << blocks
+ << "]";
}
}
// Add parameter blocks and convert the double*'s to parameter blocks.
- vector<ParameterBlock*> parameter_block_ptrs(parameter_blocks.size());
- for (int i = 0; i < parameter_blocks.size(); ++i) {
- parameter_block_ptrs[i] =
- InternalAddParameterBlock(parameter_blocks[i],
- parameter_block_sizes[i]);
+ 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]);
}
if (!options_.disable_all_safety_checks) {
@@ -279,8 +327,8 @@ ResidualBlock* ProblemImpl::AddResidualBlock(
for (int i = 0; i < parameter_block_ptrs.size(); ++i) {
CHECK_EQ(cost_function->parameter_block_sizes()[i],
parameter_block_ptrs[i]->Size())
- << "The cost function expects parameter block " << i
- << " of size " << cost_function->parameter_block_sizes()[i]
+ << "The cost function expects parameter block " << i << " of size "
+ << cost_function->parameter_block_sizes()[i]
<< " but was given a block of size "
<< parameter_block_ptrs[i]->Size();
}
@@ -294,7 +342,7 @@ ResidualBlock* ProblemImpl::AddResidualBlock(
// Add dependencies on the residual to the parameter blocks.
if (options_.enable_fast_removal) {
- for (int i = 0; i < parameter_blocks.size(); ++i) {
+ for (int i = 0; i < num_parameter_blocks; ++i) {
parameter_block_ptrs[i]->AddResidualBlock(new_residual_block);
}
}
@@ -305,148 +353,19 @@ ResidualBlock* ProblemImpl::AddResidualBlock(
residual_block_set_.insert(new_residual_block);
}
- return new_residual_block;
-}
-
-// Unfortunately, macros don't help much to reduce this code, and var args don't
-// work because of the ambiguous case that there is no loss function.
-ResidualBlock* ProblemImpl::AddResidualBlock(
- CostFunction* cost_function,
- LossFunction* loss_function,
- double* x0) {
- vector<double*> residual_parameters;
- residual_parameters.push_back(x0);
- return AddResidualBlock(cost_function, loss_function, residual_parameters);
-}
-
-ResidualBlock* ProblemImpl::AddResidualBlock(
- CostFunction* cost_function,
- LossFunction* loss_function,
- double* x0, double* x1) {
- vector<double*> residual_parameters;
- residual_parameters.push_back(x0);
- residual_parameters.push_back(x1);
- return AddResidualBlock(cost_function, loss_function, residual_parameters);
-}
-
-ResidualBlock* ProblemImpl::AddResidualBlock(
- CostFunction* cost_function,
- LossFunction* loss_function,
- double* x0, double* x1, double* x2) {
- vector<double*> residual_parameters;
- residual_parameters.push_back(x0);
- residual_parameters.push_back(x1);
- residual_parameters.push_back(x2);
- return AddResidualBlock(cost_function, loss_function, residual_parameters);
-}
-
-ResidualBlock* ProblemImpl::AddResidualBlock(
- CostFunction* cost_function,
- LossFunction* loss_function,
- double* x0, double* x1, double* x2, double* x3) {
- vector<double*> residual_parameters;
- residual_parameters.push_back(x0);
- residual_parameters.push_back(x1);
- residual_parameters.push_back(x2);
- residual_parameters.push_back(x3);
- return AddResidualBlock(cost_function, loss_function, residual_parameters);
-}
+ if (options_.cost_function_ownership == TAKE_OWNERSHIP) {
+ // Increment the reference count, creating an entry in the table if
+ // needed. Note: C++ maps guarantee that new entries have default
+ // constructed values; this implies integers are zero initialized.
+ ++cost_function_ref_count_[cost_function];
+ }
-ResidualBlock* ProblemImpl::AddResidualBlock(
- CostFunction* cost_function,
- LossFunction* loss_function,
- double* x0, double* x1, double* x2, double* x3, double* x4) {
- vector<double*> residual_parameters;
- residual_parameters.push_back(x0);
- residual_parameters.push_back(x1);
- residual_parameters.push_back(x2);
- residual_parameters.push_back(x3);
- residual_parameters.push_back(x4);
- return AddResidualBlock(cost_function, loss_function, residual_parameters);
-}
+ if (options_.loss_function_ownership == TAKE_OWNERSHIP &&
+ loss_function != nullptr) {
+ ++loss_function_ref_count_[loss_function];
+ }
-ResidualBlock* ProblemImpl::AddResidualBlock(
- CostFunction* cost_function,
- LossFunction* loss_function,
- double* x0, double* x1, double* x2, double* x3, double* x4, double* x5) {
- vector<double*> residual_parameters;
- residual_parameters.push_back(x0);
- residual_parameters.push_back(x1);
- residual_parameters.push_back(x2);
- residual_parameters.push_back(x3);
- residual_parameters.push_back(x4);
- residual_parameters.push_back(x5);
- return AddResidualBlock(cost_function, loss_function, residual_parameters);
-}
-
-ResidualBlock* ProblemImpl::AddResidualBlock(
- CostFunction* cost_function,
- LossFunction* loss_function,
- double* x0, double* x1, double* x2, double* x3, double* x4, double* x5,
- double* x6) {
- vector<double*> residual_parameters;
- residual_parameters.push_back(x0);
- residual_parameters.push_back(x1);
- residual_parameters.push_back(x2);
- residual_parameters.push_back(x3);
- residual_parameters.push_back(x4);
- residual_parameters.push_back(x5);
- residual_parameters.push_back(x6);
- return AddResidualBlock(cost_function, loss_function, residual_parameters);
-}
-
-ResidualBlock* ProblemImpl::AddResidualBlock(
- CostFunction* cost_function,
- LossFunction* loss_function,
- double* x0, double* x1, double* x2, double* x3, double* x4, double* x5,
- double* x6, double* x7) {
- vector<double*> residual_parameters;
- residual_parameters.push_back(x0);
- residual_parameters.push_back(x1);
- residual_parameters.push_back(x2);
- residual_parameters.push_back(x3);
- residual_parameters.push_back(x4);
- residual_parameters.push_back(x5);
- residual_parameters.push_back(x6);
- residual_parameters.push_back(x7);
- return AddResidualBlock(cost_function, loss_function, residual_parameters);
-}
-
-ResidualBlock* ProblemImpl::AddResidualBlock(
- CostFunction* cost_function,
- LossFunction* loss_function,
- double* x0, double* x1, double* x2, double* x3, double* x4, double* x5,
- double* x6, double* x7, double* x8) {
- vector<double*> residual_parameters;
- residual_parameters.push_back(x0);
- residual_parameters.push_back(x1);
- residual_parameters.push_back(x2);
- residual_parameters.push_back(x3);
- residual_parameters.push_back(x4);
- residual_parameters.push_back(x5);
- residual_parameters.push_back(x6);
- residual_parameters.push_back(x7);
- residual_parameters.push_back(x8);
- return AddResidualBlock(cost_function, loss_function, residual_parameters);
-}
-
-ResidualBlock* ProblemImpl::AddResidualBlock(
- CostFunction* cost_function,
- LossFunction* loss_function,
- double* x0, double* x1, double* x2, double* x3, double* x4, double* x5,
- double* x6, double* x7, double* x8, double* x9) {
- vector<double*> residual_parameters;
- residual_parameters.push_back(x0);
- residual_parameters.push_back(x1);
- residual_parameters.push_back(x2);
- residual_parameters.push_back(x3);
- residual_parameters.push_back(x4);
- residual_parameters.push_back(x5);
- residual_parameters.push_back(x6);
- residual_parameters.push_back(x7);
- residual_parameters.push_back(x8);
- residual_parameters.push_back(x9);
- return AddResidualBlock(cost_function, loss_function, residual_parameters);
+ return new_residual_block;
}
void ProblemImpl::AddParameterBlock(double* values, int size) {
@@ -454,12 +373,9 @@ void ProblemImpl::AddParameterBlock(double* values, int size) {
}
void ProblemImpl::AddParameterBlock(
- double* values,
- int size,
- LocalParameterization* local_parameterization) {
- ParameterBlock* parameter_block =
- InternalAddParameterBlock(values, size);
- if (local_parameterization != NULL) {
+ double* values, int size, LocalParameterization* local_parameterization) {
+ ParameterBlock* parameter_block = InternalAddParameterBlock(values, size);
+ if (local_parameterization != nullptr) {
parameter_block->SetParameterization(local_parameterization);
}
}
@@ -468,13 +384,12 @@ void ProblemImpl::AddParameterBlock(
// This is done in constant time by moving an element from the end of the
// vector over the element to remove, then popping the last element. It
// destroys the ordering in the interest of speed.
-template<typename Block>
+template <typename Block>
void ProblemImpl::DeleteBlockInVector(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"
- << "Block requested: "
- << block_to_remove->ToString() << "\n"
+ << "Block requested: " << block_to_remove->ToString() << "\n"
<< "Block present: "
<< (*mutable_blocks)[block_to_remove->index()]->ToString();
@@ -493,24 +408,23 @@ void ProblemImpl::DeleteBlockInVector(vector<Block*>* mutable_blocks,
}
void ProblemImpl::RemoveResidualBlock(ResidualBlock* residual_block) {
- CHECK_NOTNULL(residual_block);
+ CHECK(residual_block != nullptr);
// Verify that residual_block identifies a residual in the current problem.
- const 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 "
- "area in memory.\n"
- " 2) residual_block represented a residual that was added to"
- " the problem, but referred to a parameter block which has "
- "since been removed, which removes all residuals which "
- "depend on that parameter block, and was thus removed.\n"
- " 3) residual_block referred to a residual that has already "
- "been removed from the problem (by the user).",
- residual_block);
+ const 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 "
+ "area in memory.\n"
+ " 2) residual_block represented a residual that was added to"
+ " the problem, but referred to a parameter block which has "
+ "since been removed, which removes all residuals which "
+ "depend on that parameter block, and was thus removed.\n"
+ " 3) residual_block referred to a residual that has already "
+ "been removed from the problem (by the user).",
+ residual_block);
if (options_.enable_fast_removal) {
- CHECK(residual_block_set_.find(residual_block) !=
- residual_block_set_.end())
+ CHECK(residual_block_set_.find(residual_block) != residual_block_set_.end())
<< residual_not_found_message;
} else {
// Perform a full search over all current residuals.
@@ -523,10 +437,10 @@ void ProblemImpl::RemoveResidualBlock(ResidualBlock* residual_block) {
InternalRemoveResidualBlock(residual_block);
}
-void ProblemImpl::RemoveParameterBlock(double* values) {
- ParameterBlock* parameter_block =
- FindWithDefault(parameter_block_map_, values, NULL);
- if (parameter_block == NULL) {
+void ProblemImpl::RemoveParameterBlock(const double* values) {
+ 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 "
<< "it can be removed.";
@@ -561,10 +475,10 @@ void ProblemImpl::RemoveParameterBlock(double* values) {
DeleteBlockInVector(program_->mutable_parameter_blocks(), parameter_block);
}
-void ProblemImpl::SetParameterBlockConstant(double* values) {
- ParameterBlock* parameter_block =
- FindWithDefault(parameter_block_map_, values, NULL);
- if (parameter_block == NULL) {
+void ProblemImpl::SetParameterBlockConstant(const double* values) {
+ 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 "
<< "it can be set constant.";
@@ -573,20 +487,19 @@ void ProblemImpl::SetParameterBlockConstant(double* values) {
parameter_block->SetConstant();
}
-bool ProblemImpl::IsParameterBlockConstant(double* values) const {
- const ParameterBlock* parameter_block =
- FindWithDefault(parameter_block_map_, values, NULL);
- CHECK(parameter_block != NULL)
- << "Parameter block not found: " << values << ". You must add the "
- << "parameter block to the problem before it can be queried.";
-
+bool ProblemImpl::IsParameterBlockConstant(const double* values) const {
+ const ParameterBlock* parameter_block = FindWithDefault(
+ parameter_block_map_, const_cast<double*>(values), nullptr);
+ CHECK(parameter_block != nullptr)
+ << "Parameter block not found: " << values << ". You must add the "
+ << "parameter block to the problem before it can be queried.";
return parameter_block->IsConstant();
}
void ProblemImpl::SetParameterBlockVariable(double* values) {
ParameterBlock* parameter_block =
- FindWithDefault(parameter_block_map_, values, NULL);
- if (parameter_block == NULL) {
+ 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 "
<< "it can be set varying.";
@@ -596,24 +509,32 @@ void ProblemImpl::SetParameterBlockVariable(double* values) {
}
void ProblemImpl::SetParameterization(
- double* values,
- LocalParameterization* local_parameterization) {
+ double* values, LocalParameterization* local_parameterization) {
ParameterBlock* parameter_block =
- FindWithDefault(parameter_block_map_, values, NULL);
- if (parameter_block == NULL) {
+ 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 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_);
+ }
+
parameter_block->SetParameterization(local_parameterization);
}
const LocalParameterization* ProblemImpl::GetParameterization(
- double* values) const {
- ParameterBlock* parameter_block =
- FindWithDefault(parameter_block_map_, values, NULL);
- if (parameter_block == NULL) {
+ 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 parameterization.";
@@ -626,8 +547,8 @@ void ProblemImpl::SetParameterLowerBound(double* values,
int index,
double lower_bound) {
ParameterBlock* parameter_block =
- FindWithDefault(parameter_block_map_, values, NULL);
- if (parameter_block == NULL) {
+ 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 a lower bound on one of its components.";
@@ -640,8 +561,8 @@ void ProblemImpl::SetParameterUpperBound(double* values,
int index,
double upper_bound) {
ParameterBlock* parameter_block =
- FindWithDefault(parameter_block_map_, values, NULL);
- if (parameter_block == NULL) {
+ 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 an upper bound on one of its components.";
@@ -649,15 +570,37 @@ void ProblemImpl::SetParameterUpperBound(double* values,
parameter_block->SetUpperBound(index, upper_bound);
}
+double ProblemImpl::GetParameterLowerBound(const double* values,
+ int index) 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 the lower bound on one of its components.";
+ }
+ return parameter_block->LowerBound(index);
+}
+
+double ProblemImpl::GetParameterUpperBound(const double* values,
+ int index) 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 set an upper bound on one of its components.";
+ }
+ return parameter_block->UpperBound(index);
+}
+
bool ProblemImpl::Evaluate(const Problem::EvaluateOptions& evaluate_options,
double* cost,
vector<double>* residuals,
vector<double>* gradient,
CRSMatrix* jacobian) {
- if (cost == NULL &&
- residuals == NULL &&
- gradient == NULL &&
- jacobian == NULL) {
+ if (cost == nullptr && residuals == nullptr && gradient == nullptr &&
+ jacobian == nullptr) {
LOG(INFO) << "Nothing to do.";
return true;
}
@@ -667,7 +610,8 @@ bool ProblemImpl::Evaluate(const Problem::EvaluateOptions& evaluate_options,
Program program;
*program.mutable_residual_blocks() =
((evaluate_options.residual_blocks.size() > 0)
- ? evaluate_options.residual_blocks : program_->residual_blocks());
+ ? evaluate_options.residual_blocks
+ : program_->residual_blocks());
const vector<double*>& parameter_block_ptrs =
evaluate_options.parameter_blocks;
@@ -688,10 +632,9 @@ bool ProblemImpl::Evaluate(const Problem::EvaluateOptions& evaluate_options,
// 1. Convert double* into ParameterBlock*
parameter_blocks.resize(parameter_block_ptrs.size());
for (int i = 0; i < parameter_block_ptrs.size(); ++i) {
- parameter_blocks[i] = FindWithDefault(parameter_block_map_,
- parameter_block_ptrs[i],
- NULL);
- if (parameter_blocks[i] == NULL) {
+ parameter_blocks[i] = FindWithDefault(
+ parameter_block_map_, parameter_block_ptrs[i], nullptr);
+ if (parameter_blocks[i] == nullptr) {
LOG(FATAL) << "No known parameter block for "
<< "Problem::Evaluate::Options.parameter_blocks[" << i << "]"
<< " = " << parameter_block_ptrs[i];
@@ -742,45 +685,36 @@ bool ProblemImpl::Evaluate(const Problem::EvaluateOptions& evaluate_options,
// the Evaluator decides the storage for the Jacobian based on the
// type of linear solver being used.
evaluator_options.linear_solver_type = SPARSE_NORMAL_CHOLESKY;
-#ifndef CERES_USE_OPENMP
+#ifdef CERES_NO_THREADS
LOG_IF(WARNING, evaluate_options.num_threads > 1)
- << "OpenMP support is not compiled into this binary; "
+ << "No threading support is compiled into this binary; "
<< "only evaluate_options.num_threads = 1 is supported. Switching "
<< "to single threaded mode.";
evaluator_options.num_threads = 1;
#else
evaluator_options.num_threads = evaluate_options.num_threads;
-#endif // CERES_USE_OPENMP
-
- string error;
- scoped_ptr<Evaluator> evaluator(
- Evaluator::Create(evaluator_options, &program, &error));
- if (evaluator.get() == NULL) {
- LOG(ERROR) << "Unable to create an Evaluator object. "
- << "Error: " << error
- << "This is a Ceres bug; please contact the developers!";
-
- // 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();
- }
-
- program_->SetParameterBlockStatePtrsToUserStatePtrs();
- program_->SetParameterOffsetsAndIndex();
- return false;
- }
-
- if (residuals !=NULL) {
+#endif // CERES_NO_THREADS
+
+ // The main thread also does work so we only need to launch num_threads - 1.
+ context_impl_->EnsureMinimumThreads(evaluator_options.num_threads - 1);
+ evaluator_options.context = context_impl_;
+ evaluator_options.evaluation_callback =
+ program_->mutable_evaluation_callback();
+ std::unique_ptr<Evaluator> evaluator(
+ new ProgramEvaluator<ScratchEvaluatePreparer,
+ CompressedRowJacobianWriter>(evaluator_options,
+ &program));
+
+ if (residuals != nullptr) {
residuals->resize(evaluator->NumResiduals());
}
- if (gradient != NULL) {
+ if (gradient != nullptr) {
gradient->resize(evaluator->NumEffectiveParameters());
}
- scoped_ptr<CompressedRowSparseMatrix> tmp_jacobian;
- if (jacobian != NULL) {
+ std::unique_ptr<CompressedRowSparseMatrix> tmp_jacobian;
+ if (jacobian != nullptr) {
tmp_jacobian.reset(
down_cast<CompressedRowSparseMatrix*>(evaluator->CreateJacobian()));
}
@@ -804,12 +738,13 @@ bool ProblemImpl::Evaluate(const Problem::EvaluateOptions& evaluate_options,
Evaluator::EvaluateOptions evaluator_evaluate_options;
evaluator_evaluate_options.apply_loss_function =
evaluate_options.apply_loss_function;
- bool status = evaluator->Evaluate(evaluator_evaluate_options,
- parameters.data(),
- &tmp_cost,
- residuals != NULL ? &(*residuals)[0] : NULL,
- gradient != NULL ? &(*gradient)[0] : NULL,
- tmp_jacobian.get());
+ bool status =
+ evaluator->Evaluate(evaluator_evaluate_options,
+ parameters.data(),
+ &tmp_cost,
+ residuals != nullptr ? &(*residuals)[0] : nullptr,
+ gradient != nullptr ? &(*gradient)[0] : nullptr,
+ tmp_jacobian.get());
// Make the parameter blocks that were temporarily marked constant,
// variable again.
@@ -818,10 +753,10 @@ bool ProblemImpl::Evaluate(const Problem::EvaluateOptions& evaluate_options,
}
if (status) {
- if (cost != NULL) {
+ if (cost != nullptr) {
*cost = tmp_cost;
}
- if (jacobian != NULL) {
+ if (jacobian != nullptr) {
tmp_jacobian->ToCRSMatrix(jacobian);
}
}
@@ -831,26 +766,53 @@ bool ProblemImpl::Evaluate(const Problem::EvaluateOptions& evaluate_options,
return status;
}
+bool ProblemImpl::EvaluateResidualBlock(ResidualBlock* residual_block,
+ bool apply_loss_function,
+ double* cost,
+ double* residuals,
+ double** jacobians) const {
+ ParameterBlock* const* parameter_blocks = residual_block->parameter_blocks();
+ const int num_parameter_blocks = residual_block->NumParameterBlocks();
+ for (int i = 0; i < num_parameter_blocks; ++i) {
+ ParameterBlock* parameter_block = parameter_blocks[i];
+ if (parameter_block->IsConstant()) {
+ if (jacobians != nullptr && jacobians[i] != nullptr) {
+ LOG(ERROR) << "Jacobian requested for parameter block : " << i
+ << ". But the parameter block is marked constant.";
+ return false;
+ }
+ } else {
+ CHECK(parameter_block->SetState(parameter_block->user_state()))
+ << "Congratulations, you found a Ceres bug! Please report this error "
+ << "to the developers.";
+ }
+ }
+
+ double dummy_cost = 0.0;
+ FixedArray<double> scratch(residual_block->NumScratchDoublesForEvaluate());
+ return residual_block->Evaluate(apply_loss_function,
+ cost ? cost : &dummy_cost,
+ residuals,
+ jacobians,
+ scratch.data());
+}
+
int ProblemImpl::NumParameterBlocks() const {
return program_->NumParameterBlocks();
}
-int ProblemImpl::NumParameters() const {
- return program_->NumParameters();
-}
+int ProblemImpl::NumParameters() const { return program_->NumParameters(); }
int ProblemImpl::NumResidualBlocks() const {
return program_->NumResidualBlocks();
}
-int ProblemImpl::NumResiduals() const {
- return program_->NumResiduals();
-}
+int ProblemImpl::NumResiduals() const { return program_->NumResiduals(); }
int ProblemImpl::ParameterBlockSize(const double* values) const {
- ParameterBlock* parameter_block =
- FindWithDefault(parameter_block_map_, const_cast<double*>(values), NULL);
- if (parameter_block == NULL) {
+ 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 size.";
@@ -860,9 +822,9 @@ int ProblemImpl::ParameterBlockSize(const double* values) const {
}
int ProblemImpl::ParameterBlockLocalSize(const double* values) const {
- ParameterBlock* parameter_block =
- FindWithDefault(parameter_block_map_, const_cast<double*>(values), NULL);
- if (parameter_block == NULL) {
+ 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.";
@@ -877,18 +839,17 @@ bool ProblemImpl::HasParameterBlock(const double* parameter_block) const {
}
void ProblemImpl::GetParameterBlocks(vector<double*>* parameter_blocks) const {
- CHECK_NOTNULL(parameter_blocks);
+ CHECK(parameter_blocks != nullptr);
parameter_blocks->resize(0);
- for (ParameterMap::const_iterator it = parameter_block_map_.begin();
- it != parameter_block_map_.end();
- ++it) {
- parameter_blocks->push_back(it->first);
+ parameter_blocks->reserve(parameter_block_map_.size());
+ for (const auto& entry : parameter_block_map_) {
+ parameter_blocks->push_back(entry.first);
}
}
void ProblemImpl::GetResidualBlocks(
vector<ResidualBlockId>* residual_blocks) const {
- CHECK_NOTNULL(residual_blocks);
+ CHECK(residual_blocks != nullptr);
*residual_blocks = program().residual_blocks();
}
@@ -896,7 +857,8 @@ void ProblemImpl::GetParameterBlocksForResidualBlock(
const ResidualBlockId residual_block,
vector<double*>* parameter_blocks) const {
int num_parameter_blocks = residual_block->NumParameterBlocks();
- CHECK_NOTNULL(parameter_blocks)->resize(num_parameter_blocks);
+ CHECK(parameter_blocks != nullptr);
+ parameter_blocks->resize(num_parameter_blocks);
for (int i = 0; i < num_parameter_blocks; ++i) {
(*parameter_blocks)[i] =
residual_block->parameter_blocks()[i]->mutable_user_state();
@@ -914,11 +876,10 @@ const LossFunction* ProblemImpl::GetLossFunctionForResidualBlock(
}
void ProblemImpl::GetResidualBlocksForParameterBlock(
- const double* values,
- vector<ResidualBlockId>* residual_blocks) const {
- ParameterBlock* parameter_block =
- FindWithDefault(parameter_block_map_, const_cast<double*>(values), NULL);
- if (parameter_block == NULL) {
+ const double* values, vector<ResidualBlockId>* residual_blocks) 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 the residual blocks that depend on it.";
@@ -927,8 +888,8 @@ void ProblemImpl::GetResidualBlocksForParameterBlock(
if (options_.enable_fast_removal) {
// In this case the residual blocks that depend on the parameter block are
// stored in the parameter block already, so just copy them out.
- CHECK_NOTNULL(residual_blocks)->resize(
- parameter_block->mutable_residual_blocks()->size());
+ CHECK(residual_blocks != nullptr);
+ residual_blocks->resize(parameter_block->mutable_residual_blocks()->size());
std::copy(parameter_block->mutable_residual_blocks()->begin(),
parameter_block->mutable_residual_blocks()->end(),
residual_blocks->begin());
@@ -936,11 +897,11 @@ void ProblemImpl::GetResidualBlocksForParameterBlock(
}
// Find residual blocks that depend on the parameter block.
- CHECK_NOTNULL(residual_blocks)->clear();
+ CHECK(residual_blocks != nullptr);
+ residual_blocks->clear();
const int num_residual_blocks = NumResidualBlocks();
for (int i = 0; i < num_residual_blocks; ++i) {
- ResidualBlock* residual_block =
- (*(program_->mutable_residual_blocks()))[i];
+ ResidualBlock* residual_block = (*(program_->mutable_residual_blocks()))[i];
const int num_parameter_blocks = residual_block->NumParameterBlocks();
for (int j = 0; j < num_parameter_blocks; ++j) {
if (residual_block->parameter_blocks()[j] == parameter_block) {
diff --git a/extern/ceres/internal/ceres/problem_impl.h b/extern/ceres/internal/ceres/problem_impl.h
index a4689c362f6..8bbe7238d27 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 2015 Google Inc. All rights reserved.
+// Copyright 2019 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -39,19 +39,21 @@
#ifndef CERES_PUBLIC_PROBLEM_IMPL_H_
#define CERES_PUBLIC_PROBLEM_IMPL_H_
+#include <array>
#include <map>
+#include <memory>
+#include <unordered_set>
#include <vector>
-#include "ceres/internal/macros.h"
+#include "ceres/context_impl.h"
#include "ceres/internal/port.h"
-#include "ceres/internal/scoped_ptr.h"
-#include "ceres/collections_port.h"
#include "ceres/problem.h"
#include "ceres/types.h"
namespace ceres {
class CostFunction;
+class EvaluationCallback;
class LossFunction;
class LocalParameterization;
struct CRSMatrix;
@@ -64,78 +66,55 @@ class ResidualBlock;
class ProblemImpl {
public:
typedef std::map<double*, ParameterBlock*> ParameterMap;
- typedef HashSet<ResidualBlock*> ResidualBlockSet;
+ typedef std::unordered_set<ResidualBlock*> ResidualBlockSet;
+ typedef std::map<CostFunction*, int> CostFunctionRefCount;
+ typedef std::map<LossFunction*, int> LossFunctionRefCount;
ProblemImpl();
explicit ProblemImpl(const Problem::Options& options);
+ ProblemImpl(const ProblemImpl&) = delete;
+ void operator=(const ProblemImpl&) = delete;
~ProblemImpl();
// See the public problem.h file for description of these methods.
- ResidualBlockId AddResidualBlock(
- CostFunction* cost_function,
- LossFunction* loss_function,
- const std::vector<double*>& parameter_blocks);
ResidualBlockId AddResidualBlock(CostFunction* cost_function,
LossFunction* loss_function,
- double* x0);
- ResidualBlockId AddResidualBlock(CostFunction* cost_function,
- LossFunction* loss_function,
- double* x0, double* x1);
- ResidualBlockId AddResidualBlock(CostFunction* cost_function,
- LossFunction* loss_function,
- double* x0, double* x1, double* x2);
- ResidualBlockId AddResidualBlock(CostFunction* cost_function,
- LossFunction* loss_function,
- double* x0, double* x1, double* x2,
- double* x3);
- ResidualBlockId AddResidualBlock(CostFunction* cost_function,
- LossFunction* loss_function,
- double* x0, double* x1, double* x2,
- double* x3, double* x4);
- ResidualBlockId AddResidualBlock(CostFunction* cost_function,
- LossFunction* loss_function,
- double* x0, double* x1, double* x2,
- double* x3, double* x4, double* x5);
- ResidualBlockId AddResidualBlock(CostFunction* cost_function,
- LossFunction* loss_function,
- double* x0, double* x1, double* x2,
- double* x3, double* x4, double* x5,
- double* x6);
- ResidualBlockId AddResidualBlock(CostFunction* cost_function,
- LossFunction* loss_function,
- double* x0, double* x1, double* x2,
- double* x3, double* x4, double* x5,
- double* x6, double* x7);
- ResidualBlockId AddResidualBlock(CostFunction* cost_function,
- LossFunction* loss_function,
- double* x0, double* x1, double* x2,
- double* x3, double* x4, double* x5,
- double* x6, double* x7, double* x8);
+ double* const* const parameter_blocks,
+ int num_parameter_blocks);
+
+ template <typename... Ts>
ResidualBlockId AddResidualBlock(CostFunction* cost_function,
LossFunction* loss_function,
- double* x0, double* x1, double* x2,
- double* x3, double* x4, double* x5,
- double* x6, double* x7, double* x8,
- double* x9);
+ double* x0,
+ Ts*... xs) {
+ const std::array<double*, sizeof...(Ts) + 1> parameter_blocks{{x0, xs...}};
+ return AddResidualBlock(cost_function,
+ loss_function,
+ parameter_blocks.data(),
+ static_cast<int>(parameter_blocks.size()));
+ }
+
void AddParameterBlock(double* values, int size);
void AddParameterBlock(double* values,
int size,
LocalParameterization* local_parameterization);
void RemoveResidualBlock(ResidualBlock* residual_block);
- void RemoveParameterBlock(double* values);
+ void RemoveParameterBlock(const double* values);
- void SetParameterBlockConstant(double* values);
+ void SetParameterBlockConstant(const double* values);
void SetParameterBlockVariable(double* values);
- bool IsParameterBlockConstant(double* values) const;
+ bool IsParameterBlockConstant(const double* values) const;
void SetParameterization(double* values,
LocalParameterization* local_parameterization);
- const LocalParameterization* GetParameterization(double* values) const;
+ const LocalParameterization* GetParameterization(const double* values) const;
void SetParameterLowerBound(double* values, int index, double lower_bound);
void SetParameterUpperBound(double* values, int index, double upper_bound);
+ double GetParameterLowerBound(const double* values, int index) const;
+ double GetParameterUpperBound(const double* values, int index) const;
bool Evaluate(const Problem::EvaluateOptions& options,
double* cost,
@@ -143,6 +122,12 @@ class ProblemImpl {
std::vector<double>* gradient,
CRSMatrix* jacobian);
+ bool EvaluateResidualBlock(ResidualBlock* residual_block,
+ bool apply_loss_function,
+ double* cost,
+ double* residuals,
+ double** jacobians) const;
+
int NumParameterBlocks() const;
int NumParameters() const;
int NumResidualBlocks() const;
@@ -179,20 +164,16 @@ class ProblemImpl {
return residual_block_set_;
}
+ ContextImpl* context() { return context_impl_; }
+
private:
ParameterBlock* InternalAddParameterBlock(double* values, int size);
void InternalRemoveResidualBlock(ResidualBlock* residual_block);
- bool InternalEvaluate(Program* program,
- double* cost,
- std::vector<double>* residuals,
- std::vector<double>* gradient,
- CRSMatrix* jacobian);
-
// Delete the arguments in question. These differ from the Remove* functions
// in that they do not clean up references to the block to delete; they
// merely delete them.
- template<typename Block>
+ template <typename Block>
void DeleteBlockInVector(std::vector<Block*>* mutable_blocks,
Block* block_to_remove);
void DeleteBlock(ResidualBlock* residual_block);
@@ -200,26 +181,32 @@ class ProblemImpl {
const Problem::Options options_;
+ bool context_impl_owned_;
+ ContextImpl* context_impl_;
+
// The mapping from user pointers to parameter blocks.
- std::map<double*, ParameterBlock*> parameter_block_map_;
+ ParameterMap parameter_block_map_;
// Iff enable_fast_removal is enabled, contains the current residual blocks.
ResidualBlockSet residual_block_set_;
// The actual parameter and residual blocks.
- internal::scoped_ptr<internal::Program> program_;
+ std::unique_ptr<internal::Program> program_;
- // When removing residual and parameter blocks, cost/loss functions and
- // parameterizations have ambiguous ownership. Instead of scanning the entire
- // problem to see if the cost/loss/parameterization is shared with other
- // residual or parameter blocks, buffer them until destruction.
+ // 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<CostFunction*> cost_functions_to_delete_;
- std::vector<LossFunction*> loss_functions_to_delete_;
std::vector<LocalParameterization*> local_parameterizations_to_delete_;
- CERES_DISALLOW_COPY_AND_ASSIGN(ProblemImpl);
+ // 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_;
};
} // namespace internal
diff --git a/extern/ceres/internal/ceres/program.cc b/extern/ceres/internal/ceres/program.cc
index 8e97f072113..f1ded2e5d5a 100644
--- a/extern/ceres/internal/ceres/program.cc
+++ b/extern/ceres/internal/ceres/program.cc
@@ -30,8 +30,11 @@
#include "ceres/program.h"
+#include <algorithm>
#include <map>
+#include <memory>
#include <vector>
+
#include "ceres/array_utils.h"
#include "ceres/casts.h"
#include "ceres/compressed_row_sparse_matrix.h"
@@ -59,8 +62,8 @@ Program::Program() {}
Program::Program(const Program& program)
: parameter_blocks_(program.parameter_blocks_),
- residual_blocks_(program.residual_blocks_) {
-}
+ residual_blocks_(program.residual_blocks_),
+ evaluation_callback_(program.evaluation_callback_) {}
const vector<ParameterBlock*>& Program::parameter_blocks() const {
return parameter_blocks_;
@@ -78,7 +81,11 @@ vector<ResidualBlock*>* Program::mutable_residual_blocks() {
return &residual_blocks_;
}
-bool Program::StateVectorToParameterBlocks(const double *state) {
+EvaluationCallback* Program::mutable_evaluation_callback() {
+ return 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)) {
@@ -89,7 +96,7 @@ bool Program::StateVectorToParameterBlocks(const double *state) {
return true;
}
-void Program::ParameterBlocksToStateVector(double *state) const {
+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();
@@ -178,7 +185,7 @@ bool Program::IsValid() const {
}
bool Program::ParameterBlocksAreFinite(string* message) const {
- CHECK_NOTNULL(message);
+ CHECK(message != nullptr);
for (int i = 0; i < parameter_blocks_.size(); ++i) {
const ParameterBlock* parameter_block = parameter_blocks_[i];
const double* array = parameter_block->user_state();
@@ -189,7 +196,9 @@ bool Program::ParameterBlocksAreFinite(string* message) const {
"ParameterBlock: %p with size %d has at least one invalid value.\n"
"First invalid value is at index: %d.\n"
"Parameter block values: ",
- array, size, invalid_index);
+ array,
+ size,
+ invalid_index);
AppendArrayToString(size, array, message);
return false;
}
@@ -217,7 +226,7 @@ bool Program::IsBoundsConstrained() const {
}
bool Program::IsFeasible(string* message) const {
- CHECK_NOTNULL(message);
+ CHECK(message != nullptr);
for (int i = 0; i < parameter_blocks_.size(); ++i) {
const ParameterBlock* parameter_block = parameter_blocks_[i];
const double* parameters = parameter_block->user_state();
@@ -236,7 +245,12 @@ bool Program::IsFeasible(string* message) const {
"\nFirst infeasible value is at index: %d."
"\nLower bound: %e, value: %e, upper bound: %e"
"\nParameter block values: ",
- parameters, size, j, lower_bound, parameters[j], upper_bound);
+ parameters,
+ size,
+ j,
+ lower_bound,
+ parameters[j],
+ upper_bound);
AppendArrayToString(size, parameters, message);
return false;
}
@@ -255,7 +269,11 @@ bool Program::IsFeasible(string* message) const {
"\nFirst infeasible bound is at index: %d."
"\nLower bound: %e, upper bound: %e"
"\nParameter block values: ",
- parameters, size, j, lower_bound, upper_bound);
+ parameters,
+ size,
+ j,
+ lower_bound,
+ upper_bound);
AppendArrayToString(size, parameters, message);
return false;
}
@@ -270,15 +288,14 @@ Program* Program::CreateReducedProgram(
vector<double*>* removed_parameter_blocks,
double* fixed_cost,
string* error) const {
- CHECK_NOTNULL(removed_parameter_blocks);
- CHECK_NOTNULL(fixed_cost);
- CHECK_NOTNULL(error);
-
- scoped_ptr<Program> reduced_program(new Program(*this));
- if (!reduced_program->RemoveFixedBlocks(removed_parameter_blocks,
- fixed_cost,
- error)) {
- return NULL;
+ CHECK(removed_parameter_blocks != nullptr);
+ CHECK(fixed_cost != nullptr);
+ CHECK(error != nullptr);
+
+ std::unique_ptr<Program> reduced_program(new Program(*this));
+ if (!reduced_program->RemoveFixedBlocks(
+ removed_parameter_blocks, fixed_cost, error)) {
+ return nullptr;
}
reduced_program->SetParameterOffsetsAndIndex();
@@ -288,15 +305,17 @@ Program* Program::CreateReducedProgram(
bool Program::RemoveFixedBlocks(vector<double*>* removed_parameter_blocks,
double* fixed_cost,
string* error) {
- CHECK_NOTNULL(removed_parameter_blocks);
- CHECK_NOTNULL(fixed_cost);
- CHECK_NOTNULL(error);
+ CHECK(removed_parameter_blocks != nullptr);
+ CHECK(fixed_cost != nullptr);
+ CHECK(error != nullptr);
- scoped_array<double> residual_block_evaluate_scratch;
+ std::unique_ptr<double[]> residual_block_evaluate_scratch;
residual_block_evaluate_scratch.reset(
new 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) {
@@ -326,18 +345,45 @@ bool Program::RemoveFixedBlocks(vector<double*>* removed_parameter_blocks,
continue;
}
+ // This is an exceedingly rare case, where the user has residual
+ // blocks which are effectively constant but they are also
+ // performance sensitive enough to add an EvaluationCallback.
+ //
+ // In this case before we evaluate the cost of the constant
+ // residual blocks, we must call
+ // EvaluationCallback::PrepareForEvaluation(). Because this call
+ // can be costly, we only call this if we actually encounter a
+ // residual block with all constant parameter blocks.
+ //
+ // It is worth nothing that there is a minor inefficiency here,
+ // that the iteration 0 of TrustRegionMinimizer will also cause
+ // PrepareForEvaluation to be called on the same point, but with
+ // evaluate_jacobians = true. We could try and optimize this here,
+ // but given the rarity of this case, the additional complexity
+ // and long range dependency is not worth it.
+ if (need_to_call_prepare_for_evaluation) {
+ constexpr bool kNewPoint = true;
+ constexpr bool kDoNotEvaluateJacobians = false;
+ evaluation_callback_->PrepareForEvaluation(kDoNotEvaluateJacobians,
+ kNewPoint);
+ need_to_call_prepare_for_evaluation = false;
+ }
+
// The residual is constant and will be removed, so its cost is
// added to the variable fixed_cost.
double cost = 0.0;
if (!residual_block->Evaluate(true,
&cost,
- NULL,
- NULL,
+ nullptr,
+ nullptr,
residual_block_evaluate_scratch.get())) {
- *error = StringPrintf("Evaluation of the residual %d failed during "
- "removal of fixed residual blocks.", i);
+ *error = StringPrintf(
+ "Evaluation of the residual %d failed during "
+ "removal of fixed residual blocks.",
+ i);
return false;
}
+
*fixed_cost += cost;
}
residual_blocks_.resize(num_active_residual_blocks);
@@ -356,11 +402,9 @@ bool Program::RemoveFixedBlocks(vector<double*>* removed_parameter_blocks,
}
parameter_blocks_.resize(num_active_parameter_blocks);
- if (!(((NumResidualBlocks() == 0) &&
- (NumParameterBlocks() == 0)) ||
- ((NumResidualBlocks() != 0) &&
- (NumParameterBlocks() != 0)))) {
- *error = "Congratulations, you found a bug in Ceres. Please report it.";
+ if (!(((NumResidualBlocks() == 0) && (NumParameterBlocks() == 0)) ||
+ ((NumResidualBlocks() != 0) && (NumParameterBlocks() != 0)))) {
+ *error = "Congratulations, you found a bug in Ceres. Please report it.";
return false;
}
@@ -373,15 +417,13 @@ bool Program::IsParameterBlockSetIndependent(
// blocks in the same residual block are part of
// parameter_block_ptrs as that would violate the assumption that it
// is an independent set in the Hessian matrix.
- for (vector<ResidualBlock*>::const_iterator it = residual_blocks_.begin();
- it != residual_blocks_.end();
- ++it) {
- ParameterBlock* const* parameter_blocks = (*it)->parameter_blocks();
- const int num_parameter_blocks = (*it)->NumParameterBlocks();
+ for (const ResidualBlock* residual_block : residual_blocks_) {
+ ParameterBlock* const* parameter_blocks =
+ residual_block->parameter_blocks();
+ const int num_parameter_blocks = residual_block->NumParameterBlocks();
int count = 0;
for (int i = 0; i < num_parameter_blocks; ++i) {
- count += independent_set.count(
- parameter_blocks[i]->mutable_user_state());
+ count += independent_set.count(parameter_blocks[i]->mutable_user_state());
}
if (count > 1) {
return false;
@@ -390,18 +432,20 @@ bool Program::IsParameterBlockSetIndependent(
return true;
}
-TripletSparseMatrix* Program::CreateJacobianBlockSparsityTranspose() const {
+std::unique_ptr<TripletSparseMatrix>
+Program::CreateJacobianBlockSparsityTranspose(int start_residual_block) const {
// Matrix to store the block sparsity structure of the Jacobian.
- TripletSparseMatrix* tsm =
- new TripletSparseMatrix(NumParameterBlocks(),
- NumResidualBlocks(),
- 10 * NumResidualBlocks());
+ const int num_rows = NumParameterBlocks();
+ const int num_cols = NumResidualBlocks() - start_residual_block;
+
+ std::unique_ptr<TripletSparseMatrix> tsm(
+ new TripletSparseMatrix(num_rows, num_cols, 10 * num_cols));
int num_nonzeros = 0;
int* rows = tsm->mutable_rows();
int* cols = tsm->mutable_cols();
double* values = tsm->mutable_values();
- for (int c = 0; c < residual_blocks_.size(); ++c) {
+ for (int c = start_residual_block; c < residual_blocks_.size(); ++c) {
const ResidualBlock* residual_block = residual_blocks_[c];
const int num_parameter_blocks = residual_block->NumParameterBlocks();
ParameterBlock* const* parameter_blocks =
@@ -423,7 +467,7 @@ TripletSparseMatrix* Program::CreateJacobianBlockSparsityTranspose() const {
const int r = parameter_blocks[j]->index();
rows[num_nonzeros] = r;
- cols[num_nonzeros] = c;
+ cols[num_nonzeros] = c - start_residual_block;
values[num_nonzeros] = 1.0;
++num_nonzeros;
}
@@ -433,13 +477,9 @@ TripletSparseMatrix* Program::CreateJacobianBlockSparsityTranspose() const {
return tsm;
}
-int Program::NumResidualBlocks() const {
- return residual_blocks_.size();
-}
+int Program::NumResidualBlocks() const { return residual_blocks_.size(); }
-int Program::NumParameterBlocks() const {
- return parameter_blocks_.size();
-}
+int Program::NumParameterBlocks() const { return parameter_blocks_.size(); }
int Program::NumResiduals() const {
int num_residuals = 0;
@@ -465,6 +505,9 @@ int Program::NumEffectiveParameters() const {
return num_parameters;
}
+// TODO(sameeragarwal): The following methods should just be updated
+// incrementally and the values cached, rather than the linear
+// complexity we have right now on every call.
int Program::MaxScratchDoublesNeededForEvaluate() const {
// Compute the scratch space needed for evaluate.
int max_scratch_bytes_for_evaluate = 0;
@@ -494,8 +537,8 @@ int Program::MaxDerivativesPerResidualBlock() const {
int Program::MaxParametersPerResidualBlock() const {
int max_parameters = 0;
for (int i = 0; i < residual_blocks_.size(); ++i) {
- max_parameters = max(max_parameters,
- residual_blocks_[i]->NumParameterBlocks());
+ max_parameters =
+ max(max_parameters, residual_blocks_[i]->NumParameterBlocks());
}
return max_parameters;
}
@@ -514,8 +557,8 @@ string Program::ToString() const {
ret += StringPrintf("Number of parameters: %d\n", NumParameters());
ret += "Parameters:\n";
for (int i = 0; i < parameter_blocks_.size(); ++i) {
- ret += StringPrintf("%d: %s\n",
- i, parameter_blocks_[i]->ToString().c_str());
+ ret +=
+ StringPrintf("%d: %s\n", i, parameter_blocks_[i]->ToString().c_str());
}
return ret;
}
diff --git a/extern/ceres/internal/ceres/program.h b/extern/ceres/internal/ceres/program.h
index 38c958fe34a..797129980e3 100644
--- a/extern/ceres/internal/ceres/program.h
+++ b/extern/ceres/internal/ceres/program.h
@@ -31,10 +31,13 @@
#ifndef CERES_INTERNAL_PROGRAM_H_
#define CERES_INTERNAL_PROGRAM_H_
+#include <memory>
#include <set>
#include <string>
#include <vector>
+
#include "ceres/internal/port.h"
+#include "ceres/evaluation_callback.h"
namespace ceres {
namespace internal {
@@ -64,6 +67,7 @@ class Program {
const std::vector<ResidualBlock*>& residual_blocks() const;
std::vector<ParameterBlock*>* mutable_parameter_blocks();
std::vector<ResidualBlock*>* mutable_residual_blocks();
+ EvaluationCallback* mutable_evaluation_callback();
// Serialize to/from the program and update states.
//
@@ -71,8 +75,8 @@ class Program {
// 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.
- bool StateVectorToParameterBlocks(const double *state);
- void ParameterBlocksToStateVector(double *state) const;
+ bool StateVectorToParameterBlocks(const double* state);
+ void ParameterBlocksToStateVector(double* state) const;
// Copy internal state to the user's parameters.
void CopyParameterBlockStateToUserState();
@@ -127,8 +131,10 @@ class Program {
// structure corresponding to the block sparsity of the transpose of
// the Jacobian matrix.
//
- // Caller owns the result.
- TripletSparseMatrix* CreateJacobianBlockSparsityTranspose() const;
+ // start_residual_block which allows the user to ignore the first
+ // start_residual_block residuals.
+ std::unique_ptr<TripletSparseMatrix> CreateJacobianBlockSparsityTranspose(
+ int start_residual_block = 0) const;
// Create a copy of this program and removes constant parameter
// blocks and residual blocks with no varying parameter blocks while
@@ -182,6 +188,7 @@ class Program {
// The Program does not own the ParameterBlock or ResidualBlock objects.
std::vector<ParameterBlock*> parameter_blocks_;
std::vector<ResidualBlock*> residual_blocks_;
+ EvaluationCallback* evaluation_callback_ = nullptr;
friend class ProblemImpl;
};
diff --git a/extern/ceres/internal/ceres/program_evaluator.h b/extern/ceres/internal/ceres/program_evaluator.h
index 74a812adeef..97ee590fbab 100644
--- a/extern/ceres/internal/ceres/program_evaluator.h
+++ b/extern/ceres/internal/ceres/program_evaluator.h
@@ -43,7 +43,7 @@
// residual jacobians are written directly into their final position in the
// block sparse matrix by the user's CostFunction; there is no copying.
//
-// The evaluation is threaded with OpenMP.
+// The evaluation is threaded with OpenMP or C++ threads.
//
// The EvaluatePreparer and JacobianWriter interfaces are as follows:
//
@@ -82,16 +82,16 @@
// This include must come before any #ifndef check on Ceres compile options.
#include "ceres/internal/port.h"
-#ifdef CERES_USE_OPENMP
-#include <omp.h>
-#endif
-
+#include <atomic>
#include <map>
+#include <memory>
#include <string>
#include <vector>
+
+#include "ceres/evaluation_callback.h"
#include "ceres/execution_summary.h"
#include "ceres/internal/eigen.h"
-#include "ceres/internal/scoped_ptr.h"
+#include "ceres/parallel_for.h"
#include "ceres/parameter_block.h"
#include "ceres/program.h"
#include "ceres/residual_block.h"
@@ -104,34 +104,33 @@ struct NullJacobianFinalizer {
void operator()(SparseMatrix* jacobian, int num_parameters) {}
};
-template<typename EvaluatePreparer,
- typename JacobianWriter,
- typename JacobianFinalizer = NullJacobianFinalizer>
+template <typename EvaluatePreparer,
+ typename JacobianWriter,
+ typename JacobianFinalizer = NullJacobianFinalizer>
class ProgramEvaluator : public Evaluator {
public:
- ProgramEvaluator(const Evaluator::Options &options, Program* program)
+ ProgramEvaluator(const Evaluator::Options& options, Program* program)
: options_(options),
program_(program),
jacobian_writer_(options, program),
evaluate_preparers_(
jacobian_writer_.CreateEvaluatePreparers(options.num_threads)) {
-#ifndef CERES_USE_OPENMP
+#ifdef CERES_NO_THREADS
if (options_.num_threads > 1) {
- LOG(WARNING)
- << "OpenMP support is not compiled into this binary; "
- << "only options.num_threads = 1 is supported. Switching "
- << "to single threaded mode.";
+ LOG(WARNING) << "No threading support is compiled into this binary; "
+ << "only options.num_threads = 1 is supported. Switching "
+ << "to single threaded mode.";
options_.num_threads = 1;
}
-#endif
+#endif // CERES_NO_THREADS
BuildResidualLayout(*program, &residual_layout_);
- evaluate_scratch_.reset(CreateEvaluatorScratch(*program,
- options.num_threads));
+ evaluate_scratch_.reset(
+ CreateEvaluatorScratch(*program, options.num_threads));
}
// Implementation of Evaluator interface.
- SparseMatrix* CreateJacobian() const {
+ SparseMatrix* CreateJacobian() const final {
return jacobian_writer_.CreateJacobian();
}
@@ -140,133 +139,133 @@ class ProgramEvaluator : public Evaluator {
double* cost,
double* residuals,
double* gradient,
- SparseMatrix* jacobian) {
+ SparseMatrix* jacobian) final {
ScopedExecutionTimer total_timer("Evaluator::Total", &execution_summary_);
- ScopedExecutionTimer call_type_timer(gradient == NULL && jacobian == NULL
- ? "Evaluator::Residual"
- : "Evaluator::Jacobian",
- &execution_summary_);
+ ScopedExecutionTimer call_type_timer(
+ gradient == nullptr && jacobian == nullptr ? "Evaluator::Residual"
+ : "Evaluator::Jacobian",
+ &execution_summary_);
// The parameters are stateful, so set the state before evaluating.
if (!program_->StateVectorToParameterBlocks(state)) {
return false;
}
- if (residuals != NULL) {
+ // Notify the user about a new evaluation point if they are interested.
+ if (options_.evaluation_callback != nullptr) {
+ program_->CopyParameterBlockStateToUserState();
+ options_.evaluation_callback->PrepareForEvaluation(
+ /*jacobians=*/(gradient != nullptr || jacobian != nullptr),
+ evaluate_options.new_evaluation_point);
+ }
+
+ if (residuals != nullptr) {
VectorRef(residuals, program_->NumResiduals()).setZero();
}
- if (jacobian != NULL) {
+ if (jacobian != nullptr) {
jacobian->SetZero();
}
// Each thread gets it's own cost and evaluate scratch space.
for (int i = 0; i < options_.num_threads; ++i) {
evaluate_scratch_[i].cost = 0.0;
- if (gradient != NULL) {
+ if (gradient != nullptr) {
VectorRef(evaluate_scratch_[i].gradient.get(),
- program_->NumEffectiveParameters()).setZero();
+ program_->NumEffectiveParameters())
+ .setZero();
}
}
- // This bool is used to disable the loop if an error is encountered
- // without breaking out of it. The remaining loop iterations are still run,
- // but with an empty body, and so will finish quickly.
- bool abort = false;
- int num_residual_blocks = program_->NumResidualBlocks();
-#pragma omp parallel for num_threads(options_.num_threads)
- for (int i = 0; i < num_residual_blocks; ++i) {
-// Disable the loop instead of breaking, as required by OpenMP.
-#pragma omp flush(abort)
- if (abort) {
- continue;
- }
+ const int num_residual_blocks = program_->NumResidualBlocks();
+ // This bool is used to disable the loop if an error is encountered without
+ // breaking out of it. The remaining loop iterations are still run, but with
+ // an empty body, and so will finish quickly.
+ std::atomic_bool abort(false);
+ ParallelFor(
+ options_.context,
+ 0,
+ num_residual_blocks,
+ options_.num_threads,
+ [&](int thread_id, int i) {
+ if (abort) {
+ return;
+ }
-#ifdef CERES_USE_OPENMP
- int thread_id = omp_get_thread_num();
-#else
- int thread_id = 0;
-#endif
- EvaluatePreparer* preparer = &evaluate_preparers_[thread_id];
- EvaluateScratch* scratch = &evaluate_scratch_[thread_id];
-
- // Prepare block residuals if requested.
- const ResidualBlock* residual_block = program_->residual_blocks()[i];
- double* block_residuals = NULL;
- if (residuals != NULL) {
- block_residuals = residuals + residual_layout_[i];
- } else if (gradient != NULL) {
- block_residuals = scratch->residual_block_residuals.get();
- }
+ EvaluatePreparer* preparer = &evaluate_preparers_[thread_id];
+ EvaluateScratch* scratch = &evaluate_scratch_[thread_id];
- // Prepare block jacobians if requested.
- double** block_jacobians = NULL;
- if (jacobian != NULL || gradient != NULL) {
- preparer->Prepare(residual_block,
- i,
- jacobian,
- scratch->jacobian_block_ptrs.get());
- block_jacobians = scratch->jacobian_block_ptrs.get();
- }
+ // Prepare block residuals if requested.
+ const ResidualBlock* residual_block = program_->residual_blocks()[i];
+ double* block_residuals = nullptr;
+ if (residuals != nullptr) {
+ block_residuals = residuals + residual_layout_[i];
+ } else if (gradient != nullptr) {
+ block_residuals = scratch->residual_block_residuals.get();
+ }
- // Evaluate the cost, residuals, and jacobians.
- double block_cost;
- if (!residual_block->Evaluate(
- evaluate_options.apply_loss_function,
- &block_cost,
- block_residuals,
- block_jacobians,
- scratch->residual_block_evaluate_scratch.get())) {
- abort = true;
-// This ensures that the OpenMP threads have a consistent view of 'abort'. Do
-// the flush inside the failure case so that there is usually only one
-// synchronization point per loop iteration instead of two.
-#pragma omp flush(abort)
- continue;
- }
+ // Prepare block jacobians if requested.
+ double** block_jacobians = nullptr;
+ if (jacobian != nullptr || gradient != nullptr) {
+ preparer->Prepare(residual_block,
+ i,
+ jacobian,
+ scratch->jacobian_block_ptrs.get());
+ block_jacobians = scratch->jacobian_block_ptrs.get();
+ }
- scratch->cost += block_cost;
+ // Evaluate the cost, residuals, and jacobians.
+ double block_cost;
+ if (!residual_block->Evaluate(
+ evaluate_options.apply_loss_function,
+ &block_cost,
+ block_residuals,
+ block_jacobians,
+ scratch->residual_block_evaluate_scratch.get())) {
+ abort = true;
+ return;
+ }
- // Store the jacobians, if they were requested.
- if (jacobian != NULL) {
- jacobian_writer_.Write(i,
- residual_layout_[i],
- block_jacobians,
- jacobian);
- }
+ scratch->cost += block_cost;
- // Compute and store the gradient, if it was requested.
- if (gradient != NULL) {
- int num_residuals = residual_block->NumResiduals();
- int num_parameter_blocks = residual_block->NumParameterBlocks();
- for (int j = 0; j < num_parameter_blocks; ++j) {
- const ParameterBlock* parameter_block =
- residual_block->parameter_blocks()[j];
- if (parameter_block->IsConstant()) {
- continue;
+ // Store the jacobians, if they were requested.
+ if (jacobian != nullptr) {
+ jacobian_writer_.Write(
+ i, residual_layout_[i], block_jacobians, jacobian);
}
- MatrixTransposeVectorMultiply<Eigen::Dynamic, Eigen::Dynamic, 1>(
- block_jacobians[j],
- num_residuals,
- parameter_block->LocalSize(),
- block_residuals,
- scratch->gradient.get() + parameter_block->delta_offset());
- }
- }
- }
+ // Compute and store the gradient, if it was requested.
+ if (gradient != nullptr) {
+ int num_residuals = residual_block->NumResiduals();
+ int num_parameter_blocks = residual_block->NumParameterBlocks();
+ for (int j = 0; j < num_parameter_blocks; ++j) {
+ const ParameterBlock* parameter_block =
+ residual_block->parameter_blocks()[j];
+ if (parameter_block->IsConstant()) {
+ continue;
+ }
+
+ MatrixTransposeVectorMultiply<Eigen::Dynamic, Eigen::Dynamic, 1>(
+ block_jacobians[j],
+ num_residuals,
+ parameter_block->LocalSize(),
+ block_residuals,
+ scratch->gradient.get() + parameter_block->delta_offset());
+ }
+ }
+ });
if (!abort) {
const int num_parameters = program_->NumEffectiveParameters();
// Sum the cost and gradient (if requested) from each thread.
(*cost) = 0.0;
- if (gradient != NULL) {
+ if (gradient != nullptr) {
VectorRef(gradient, num_parameters).setZero();
}
for (int i = 0; i < options_.num_threads; ++i) {
(*cost) += evaluate_scratch_[i].cost;
- if (gradient != NULL) {
+ if (gradient != nullptr) {
VectorRef(gradient, num_parameters) +=
VectorRef(evaluate_scratch_[i].gradient.get(), num_parameters);
}
@@ -276,7 +275,7 @@ class ProgramEvaluator : public Evaluator {
// `num_parameters` is passed to the finalizer so that additional
// storage can be reserved for additional diagonal elements if
// necessary.
- if (jacobian != NULL) {
+ if (jacobian != nullptr) {
JacobianFinalizer f;
f(jacobian, num_parameters);
}
@@ -286,27 +285,19 @@ class ProgramEvaluator : public Evaluator {
bool Plus(const double* state,
const double* delta,
- double* state_plus_delta) const {
+ double* state_plus_delta) const final {
return program_->Plus(state, delta, state_plus_delta);
}
- int NumParameters() const {
- return program_->NumParameters();
- }
- int NumEffectiveParameters() const {
+ int NumParameters() const final { return program_->NumParameters(); }
+ int NumEffectiveParameters() const final {
return program_->NumEffectiveParameters();
}
- int NumResiduals() const {
- return program_->NumResiduals();
- }
-
- virtual std::map<std::string, int> CallStatistics() const {
- return execution_summary_.calls();
- }
+ int NumResiduals() const final { return program_->NumResiduals(); }
- virtual std::map<std::string, double> TimeStatistics() const {
- return execution_summary_.times();
+ std::map<std::string, CallStatistics> Statistics() const final {
+ return execution_summary_.statistics();
}
private:
@@ -322,17 +313,16 @@ class ProgramEvaluator : public Evaluator {
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]);
+ jacobian_block_ptrs.reset(new double*[max_parameters_per_residual_block]);
}
double cost;
- scoped_array<double> residual_block_evaluate_scratch;
+ std::unique_ptr<double[]> residual_block_evaluate_scratch;
// The gradient in the local parameterization.
- scoped_array<double> gradient;
+ std::unique_ptr<double[]> gradient;
// Enough space to store the residual for the largest residual block.
- scoped_array<double> residual_block_residuals;
- scoped_array<double*> jacobian_block_ptrs;
+ std::unique_ptr<double[]> residual_block_residuals;
+ std::unique_ptr<double*[]> jacobian_block_ptrs;
};
static void BuildResidualLayout(const Program& program,
@@ -372,8 +362,8 @@ class ProgramEvaluator : public Evaluator {
Evaluator::Options options_;
Program* program_;
JacobianWriter jacobian_writer_;
- scoped_array<EvaluatePreparer> evaluate_preparers_;
- scoped_array<EvaluateScratch> evaluate_scratch_;
+ std::unique_ptr<EvaluatePreparer[]> evaluate_preparers_;
+ std::unique_ptr<EvaluateScratch[]> evaluate_scratch_;
std::vector<int> residual_layout_;
::ceres::internal::ExecutionSummary execution_summary_;
};
diff --git a/extern/ceres/internal/ceres/random.h b/extern/ceres/internal/ceres/random.h
index 2a025600609..87d9d77d90d 100644
--- a/extern/ceres/internal/ceres/random.h
+++ b/extern/ceres/internal/ceres/random.h
@@ -43,7 +43,11 @@ inline void SetRandomState(int state) {
}
inline int Uniform(int n) {
- return rand() % n;
+ if (n) {
+ return rand() % n;
+ } else {
+ return 0;
+ }
}
inline double RandDouble() {
diff --git a/extern/ceres/internal/ceres/reorder_program.cc b/extern/ceres/internal/ceres/reorder_program.cc
index a7c37107591..aa6032a9e9e 100644
--- a/extern/ceres/internal/ceres/reorder_program.cc
+++ b/extern/ceres/internal/ceres/reorder_program.cc
@@ -31,6 +31,7 @@
#include "ceres/reorder_program.h"
#include <algorithm>
+#include <memory>
#include <numeric>
#include <vector>
@@ -84,7 +85,7 @@ static int MinParameterBlock(const ResidualBlock* residual_block,
return min_parameter_block_position;
}
-#if EIGEN_VERSION_AT_LEAST(3, 2, 2) && defined(CERES_USE_EIGEN_SPARSE)
+#if defined(CERES_USE_EIGEN_SPARSE)
Eigen::SparseMatrix<int> CreateBlockJacobian(
const TripletSparseMatrix& block_jacobian_transpose) {
typedef Eigen::SparseMatrix<int> SparseMatrix;
@@ -178,7 +179,6 @@ void OrderingForSparseNormalCholeskyUsingCXSparse(
}
-#if EIGEN_VERSION_AT_LEAST(3, 2, 2)
void OrderingForSparseNormalCholeskyUsingEigenSparse(
const TripletSparseMatrix& tsm_block_jacobian_transpose,
int* ordering) {
@@ -211,7 +211,6 @@ void OrderingForSparseNormalCholeskyUsingEigenSparse(
}
#endif // CERES_USE_EIGEN_SPARSE
}
-#endif
} // namespace
@@ -233,24 +232,19 @@ bool ApplyOrdering(const ProblemImpl::ParameterMap& parameter_map,
program->mutable_parameter_blocks();
parameter_blocks->clear();
- const map<int, set<double*> >& groups = ordering.group_to_elements();
- for (map<int, set<double*> >::const_iterator group_it = groups.begin();
- group_it != groups.end();
- ++group_it) {
- const set<double*>& group = group_it->second;
- for (set<double*>::const_iterator parameter_block_ptr_it = group.begin();
- parameter_block_ptr_it != group.end();
- ++parameter_block_ptr_it) {
- ProblemImpl::ParameterMap::const_iterator parameter_block_it =
- parameter_map.find(*parameter_block_ptr_it);
- if (parameter_block_it == parameter_map.end()) {
+ const map<int, set<double*>>& groups = ordering.group_to_elements();
+ for (const auto& p : groups) {
+ const set<double*>& group = p.second;
+ for (double* parameter_block_ptr : group) {
+ auto it = parameter_map.find(parameter_block_ptr);
+ if (it == parameter_map.end()) {
*error = StringPrintf("User specified ordering contains a pointer "
"to a double that is not a parameter block in "
"the problem. The invalid double is in group: %d",
- group_it->first);
+ p.first);
return false;
}
- parameter_blocks->push_back(parameter_block_it->second);
+ parameter_blocks->push_back(it->second);
}
}
return true;
@@ -339,7 +333,7 @@ bool LexicographicallyOrderResidualBlocks(
// Pre-order the columns corresponding to the schur complement if
// possible.
-void MaybeReorderSchurComplementColumnsUsingSuiteSparse(
+static void MaybeReorderSchurComplementColumnsUsingSuiteSparse(
const ParameterBlockOrdering& parameter_block_ordering,
Program* program) {
#ifndef CERES_NO_SUITESPARSE
@@ -364,7 +358,7 @@ void MaybeReorderSchurComplementColumnsUsingSuiteSparse(
MapValuesToContiguousRange(constraints.size(), &constraints[0]);
// Compute a block sparse presentation of J'.
- scoped_ptr<TripletSparseMatrix> tsm_block_jacobian_transpose(
+ std::unique_ptr<TripletSparseMatrix> tsm_block_jacobian_transpose(
program->CreateJacobianBlockSparsityTranspose());
cholmod_sparse* block_jacobian_transpose =
@@ -385,15 +379,12 @@ void MaybeReorderSchurComplementColumnsUsingSuiteSparse(
#endif
}
-void MaybeReorderSchurComplementColumnsUsingEigen(
+static void MaybeReorderSchurComplementColumnsUsingEigen(
const int size_of_first_elimination_group,
const ProblemImpl::ParameterMap& parameter_map,
Program* program) {
-#if !EIGEN_VERSION_AT_LEAST(3, 2, 2) || !defined(CERES_USE_EIGEN_SPARSE)
- return;
-#else
-
- scoped_ptr<TripletSparseMatrix> tsm_block_jacobian_transpose(
+#if defined(CERES_USE_EIGEN_SPARSE)
+ std::unique_ptr<TripletSparseMatrix> tsm_block_jacobian_transpose(
program->CreateJacobianBlockSparsityTranspose());
typedef Eigen::SparseMatrix<int> SparseMatrix;
@@ -531,18 +522,14 @@ bool ReorderProgramForSchurTypeLinearSolver(
// Schur type solvers also require that their residual blocks be
// lexicographically ordered.
- if (!LexicographicallyOrderResidualBlocks(size_of_first_elimination_group,
- program,
- error)) {
- return false;
- }
-
- return true;
+ return LexicographicallyOrderResidualBlocks(
+ size_of_first_elimination_group, program, error);
}
-bool ReorderProgramForSparseNormalCholesky(
+bool ReorderProgramForSparseCholesky(
const SparseLinearAlgebraLibraryType sparse_linear_algebra_library_type,
const ParameterBlockOrdering& parameter_block_ordering,
+ int start_row_block,
Program* program,
string* error) {
if (parameter_block_ordering.NumElements() != program->NumParameterBlocks()) {
@@ -555,8 +542,8 @@ bool ReorderProgramForSparseNormalCholesky(
}
// Compute a block sparse presentation of J'.
- scoped_ptr<TripletSparseMatrix> tsm_block_jacobian_transpose(
- program->CreateJacobianBlockSparsityTranspose());
+ std::unique_ptr<TripletSparseMatrix> tsm_block_jacobian_transpose(
+ program->CreateJacobianBlockSparsityTranspose(start_row_block));
vector<int> ordering(program->NumParameterBlocks(), 0);
vector<ParameterBlock*>& parameter_blocks =
@@ -572,19 +559,19 @@ bool ReorderProgramForSparseNormalCholesky(
OrderingForSparseNormalCholeskyUsingCXSparse(
*tsm_block_jacobian_transpose,
&ordering[0]);
+ } else if (sparse_linear_algebra_library_type == ACCELERATE_SPARSE) {
+ // Accelerate does not provide a function to perform reordering without
+ // performing a full symbolic factorisation. As such, we have nothing
+ // to gain from trying to reorder the problem here, as it will happen
+ // in AppleAccelerateCholesky::Factorize() (once) and reordering here
+ // would involve performing two symbolic factorisations instead of one
+ // which would have a negative overall impact on performance.
+ return true;
+
} else if (sparse_linear_algebra_library_type == EIGEN_SPARSE) {
-#if EIGEN_VERSION_AT_LEAST(3, 2, 2)
- OrderingForSparseNormalCholeskyUsingEigenSparse(
+ OrderingForSparseNormalCholeskyUsingEigenSparse(
*tsm_block_jacobian_transpose,
&ordering[0]);
-#else
- // For Eigen versions less than 3.2.2, there is nothing to do as
- // older versions of Eigen do not expose a method for doing
- // symbolic analysis on pre-ordered matrices, so a block
- // pre-ordering is a bit pointless.
-
- return true;
-#endif
}
// Apply ordering.
@@ -597,5 +584,17 @@ bool ReorderProgramForSparseNormalCholesky(
return true;
}
+int ReorderResidualBlocksByPartition(
+ const std::unordered_set<ResidualBlockId>& bottom_residual_blocks,
+ Program* program) {
+ auto residual_blocks = program->mutable_residual_blocks();
+ auto it = std::partition(
+ residual_blocks->begin(), residual_blocks->end(),
+ [&bottom_residual_blocks](ResidualBlock* r) {
+ return bottom_residual_blocks.count(r) == 0;
+ });
+ return it - residual_blocks->begin();
+}
+
} // namespace internal
} // namespace ceres
diff --git a/extern/ceres/internal/ceres/reorder_program.h b/extern/ceres/internal/ceres/reorder_program.h
index 36e5d1637a9..88cbee3af21 100644
--- a/extern/ceres/internal/ceres/reorder_program.h
+++ b/extern/ceres/internal/ceres/reorder_program.h
@@ -89,12 +89,27 @@ 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.
-bool ReorderProgramForSparseNormalCholesky(
+bool ReorderProgramForSparseCholesky(
SparseLinearAlgebraLibraryType sparse_linear_algebra_library_type,
const ParameterBlockOrdering& parameter_block_ordering,
+ int start_row_block,
Program* program,
std::string* error);
+// Reorder the residual blocks in the program so that all the residual
+// blocks in bottom_residual_blocks are at the bottom. The return
+// value is the number of residual blocks in the program in "top" part
+// of the Program, i.e., the ones not included in
+// bottom_residual_blocks.
+//
+// This number can be different from program->NumResidualBlocks() -
+// bottom_residual_blocks.size() because we allow
+// bottom_residual_blocks to contain residual blocks not present in
+// the Program.
+int ReorderResidualBlocksByPartition(
+ const std::unordered_set<ResidualBlockId>& bottom_residual_blocks,
+ Program* program);
+
} // namespace internal
} // namespace ceres
diff --git a/extern/ceres/internal/ceres/residual_block.cc b/extern/ceres/internal/ceres/residual_block.cc
index 9a123cf132e..0bf30bcf446 100644
--- a/extern/ceres/internal/ceres/residual_block.cc
+++ b/extern/ceres/internal/ceres/residual_block.cc
@@ -35,13 +35,13 @@
#include <cstddef>
#include <vector>
#include "ceres/corrector.h"
-#include "ceres/parameter_block.h"
-#include "ceres/residual_block_utils.h"
#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/parameter_block.h"
+#include "ceres/residual_block_utils.h"
#include "ceres/small_blas.h"
using Eigen::Dynamic;
@@ -50,16 +50,14 @@ namespace ceres {
namespace internal {
ResidualBlock::ResidualBlock(
- const CostFunction* cost_function,
- const LossFunction* loss_function,
- const std::vector<ParameterBlock*>& parameter_blocks,
- int index)
+ const CostFunction* cost_function, const LossFunction* loss_function,
+ const std::vector<ParameterBlock*>& parameter_blocks, int index)
: cost_function_(cost_function),
loss_function_(loss_function),
parameter_blocks_(
- new ParameterBlock* [
- cost_function->parameter_block_sizes().size()]),
+ new ParameterBlock*[cost_function->parameter_block_sizes().size()]),
index_(index) {
+ CHECK(cost_function_ != nullptr);
std::copy(parameter_blocks.begin(),
parameter_blocks.end(),
parameter_blocks_.get());
@@ -82,11 +80,11 @@ bool ResidualBlock::Evaluate(const bool apply_loss_function,
// Put pointers into the scratch space into global_jacobians as appropriate.
FixedArray<double*, 8> global_jacobians(num_parameter_blocks);
- if (jacobians != NULL) {
+ if (jacobians != nullptr) {
for (int i = 0; i < num_parameter_blocks; ++i) {
const ParameterBlock* parameter_block = parameter_blocks_[i];
- if (jacobians[i] != NULL &&
- parameter_block->LocalParameterizationJacobian() != NULL) {
+ if (jacobians[i] != nullptr &&
+ parameter_block->LocalParameterizationJacobian() != nullptr) {
global_jacobians[i] = scratch;
scratch += num_residuals * parameter_block->Size();
} else {
@@ -96,7 +94,7 @@ bool ResidualBlock::Evaluate(const bool apply_loss_function,
}
// If the caller didn't request residuals, use the scratch space for them.
- bool outputting_residuals = (residuals != NULL);
+ bool outputting_residuals = (residuals != nullptr);
if (!outputting_residuals) {
residuals = scratch;
}
@@ -104,16 +102,17 @@ bool ResidualBlock::Evaluate(const bool apply_loss_function,
// Invalidate the evaluation buffers so that we can check them after
// the CostFunction::Evaluate call, to see if all the return values
// that were required were written to and that they are finite.
- double** eval_jacobians = (jacobians != NULL) ? global_jacobians.get() : NULL;
+ double** eval_jacobians =
+ (jacobians != nullptr) ? global_jacobians.data() : nullptr;
InvalidateEvaluation(*this, cost, residuals, eval_jacobians);
- if (!cost_function_->Evaluate(parameters.get(), residuals, eval_jacobians)) {
+ if (!cost_function_->Evaluate(parameters.data(), residuals, eval_jacobians)) {
return false;
}
if (!IsEvaluationValid(*this,
- parameters.get(),
+ parameters.data(),
cost,
residuals,
eval_jacobians)) {
@@ -124,7 +123,7 @@ bool ResidualBlock::Evaluate(const bool apply_loss_function,
"residual and jacobians that were requested or there was a non-finite value (nan/infinite)\n" // NOLINT
"generated during the or jacobian computation. \n\n" +
EvaluationToString(*this,
- parameters.get(),
+ parameters.data(),
cost,
residuals,
eval_jacobians);
@@ -135,13 +134,13 @@ bool ResidualBlock::Evaluate(const bool apply_loss_function,
double squared_norm = VectorRef(residuals, num_residuals).squaredNorm();
// Update the jacobians with the local parameterizations.
- if (jacobians != NULL) {
+ if (jacobians != nullptr) {
for (int i = 0; i < num_parameter_blocks; ++i) {
- if (jacobians[i] != NULL) {
+ if (jacobians[i] != nullptr) {
const ParameterBlock* parameter_block = parameter_blocks_[i];
// Apply local reparameterization to the jacobians.
- if (parameter_block->LocalParameterizationJacobian() != NULL) {
+ if (parameter_block->LocalParameterizationJacobian() != nullptr) {
// jacobians[i] = global_jacobians[i] * global_to_local_jacobian.
MatrixMatrixMultiply<Dynamic, Dynamic, Dynamic, Dynamic, 0>(
global_jacobians[i],
@@ -156,7 +155,7 @@ bool ResidualBlock::Evaluate(const bool apply_loss_function,
}
}
- if (loss_function_ == NULL || !apply_loss_function) {
+ if (loss_function_ == nullptr || !apply_loss_function) {
*cost = 0.5 * squared_norm;
return true;
}
@@ -167,16 +166,16 @@ bool ResidualBlock::Evaluate(const bool apply_loss_function,
// No jacobians and not outputting residuals? All done. Doing an early exit
// here avoids constructing the "Corrector" object below in a common case.
- if (jacobians == NULL && !outputting_residuals) {
+ if (jacobians == nullptr && !outputting_residuals) {
return true;
}
// Correct for the effects of the loss function. The jacobians need to be
// corrected before the residuals, since they use the uncorrected residuals.
Corrector correct(squared_norm, rho);
- if (jacobians != NULL) {
+ if (jacobians != nullptr) {
for (int i = 0; i < num_parameter_blocks; ++i) {
- if (jacobians[i] != NULL) {
+ if (jacobians[i] != nullptr) {
const ParameterBlock* parameter_block = parameter_blocks_[i];
// Correct the jacobians for the loss function.
@@ -206,8 +205,7 @@ int ResidualBlock::NumScratchDoublesForEvaluate() const {
int scratch_doubles = 1;
for (int i = 0; i < num_parameters; ++i) {
const ParameterBlock* parameter_block = parameter_blocks_[i];
- if (!parameter_block->IsConstant() &&
- parameter_block->LocalParameterizationJacobian() != NULL) {
+ if (parameter_block->LocalParameterizationJacobian() != 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 a32f1c36cd3..a2e4425b911 100644
--- a/extern/ceres/internal/ceres/residual_block.h
+++ b/extern/ceres/internal/ceres/residual_block.h
@@ -34,12 +34,13 @@
#ifndef CERES_INTERNAL_RESIDUAL_BLOCK_H_
#define CERES_INTERNAL_RESIDUAL_BLOCK_H_
+#include <cstdint>
+#include <memory>
#include <string>
#include <vector>
#include "ceres/cost_function.h"
#include "ceres/internal/port.h"
-#include "ceres/internal/scoped_ptr.h"
#include "ceres/stringprintf.h"
#include "ceres/types.h"
@@ -81,12 +82,14 @@ class ResidualBlock {
// computed. If jacobians[i] is NULL, then the jacobian for that parameter is
// not computed.
//
+ // cost must not be null.
+ //
// Evaluate needs scratch space which must be supplied by the caller via
// scratch. The array should have at least NumScratchDoublesForEvaluate()
// space available.
//
// The return value indicates the success or failure. If the function returns
- // false, the caller should expect the the output memory locations to have
+ // false, the caller should expect the output memory locations to have
// been modified.
//
// The returned cost and jacobians have had robustification and local
@@ -134,12 +137,12 @@ class ResidualBlock {
private:
const CostFunction* cost_function_;
const LossFunction* loss_function_;
- scoped_array<ParameterBlock*> parameter_blocks_;
+ std::unique_ptr<ParameterBlock*[]> parameter_blocks_;
// The index of the residual, typically in a Program. This is only to permit
// switching from a ResidualBlock* to an index in the Program's array, needed
// to do efficient removals.
- int32 index_;
+ int32_t index_;
};
} // namespace internal
diff --git a/extern/ceres/internal/ceres/residual_block_utils.cc b/extern/ceres/internal/ceres/residual_block_utils.cc
index dd2bd73a6ac..35e928bbcc1 100644
--- a/extern/ceres/internal/ceres/residual_block_utils.cc
+++ b/extern/ceres/internal/ceres/residual_block_utils.cc
@@ -68,8 +68,8 @@ string EvaluationToString(const ResidualBlock& block,
double* cost,
double* residuals,
double** jacobians) {
- CHECK_NOTNULL(cost);
- CHECK_NOTNULL(residuals);
+ CHECK(cost != nullptr);
+ CHECK(residuals != nullptr);
const int num_parameter_blocks = block.NumParameterBlocks();
const int num_residuals = block.NumResiduals();
diff --git a/extern/ceres/internal/ceres/schur_complement_solver.cc b/extern/ceres/internal/ceres/schur_complement_solver.cc
index 65449832c4c..0083300b036 100644
--- a/extern/ceres/internal/ceres/schur_complement_solver.cc
+++ b/extern/ceres/internal/ceres/schur_complement_solver.cc
@@ -28,33 +28,30 @@
//
// Author: sameeragarwal@google.com (Sameer Agarwal)
-#include "ceres/internal/port.h"
+#include "ceres/schur_complement_solver.h"
#include <algorithm>
#include <ctime>
+#include <memory>
#include <set>
-#include <sstream>
#include <vector>
+#include "Eigen/Dense"
+#include "Eigen/SparseCore"
#include "ceres/block_random_access_dense_matrix.h"
#include "ceres/block_random_access_matrix.h"
#include "ceres/block_random_access_sparse_matrix.h"
#include "ceres/block_sparse_matrix.h"
#include "ceres/block_structure.h"
#include "ceres/conjugate_gradients_solver.h"
-#include "ceres/cxsparse.h"
#include "ceres/detect_structure.h"
#include "ceres/internal/eigen.h"
-#include "ceres/internal/scoped_ptr.h"
#include "ceres/lapack.h"
#include "ceres/linear_solver.h"
-#include "ceres/schur_complement_solver.h"
-#include "ceres/suitesparse.h"
+#include "ceres/sparse_cholesky.h"
#include "ceres/triplet_sparse_matrix.h"
#include "ceres/types.h"
#include "ceres/wall_time.h"
-#include "Eigen/Dense"
-#include "Eigen/SparseCore"
namespace ceres {
namespace internal {
@@ -70,23 +67,22 @@ class BlockRandomAccessSparseMatrixAdapter : public LinearOperator {
public:
explicit BlockRandomAccessSparseMatrixAdapter(
const BlockRandomAccessSparseMatrix& m)
- : m_(m) {
- }
+ : m_(m) {}
virtual ~BlockRandomAccessSparseMatrixAdapter() {}
// y = y + Ax;
- virtual void RightMultiply(const double* x, double* y) const {
+ void RightMultiply(const double* x, double* y) const final {
m_.SymmetricRightMultiply(x, y);
}
// y = y + A'x;
- virtual void LeftMultiply(const double* x, double* y) const {
+ void LeftMultiply(const double* x, double* y) const final {
m_.SymmetricRightMultiply(x, y);
}
- virtual int num_rows() const { return m_.num_rows(); }
- virtual int num_cols() const { return m_.num_rows(); }
+ int num_rows() const final { return m_.num_rows(); }
+ int num_cols() const final { return m_.num_rows(); }
private:
const BlockRandomAccessSparseMatrix& m_;
@@ -96,29 +92,28 @@ class BlockRandomAccessDiagonalMatrixAdapter : public LinearOperator {
public:
explicit BlockRandomAccessDiagonalMatrixAdapter(
const BlockRandomAccessDiagonalMatrix& m)
- : m_(m) {
- }
+ : m_(m) {}
virtual ~BlockRandomAccessDiagonalMatrixAdapter() {}
// y = y + Ax;
- virtual void RightMultiply(const double* x, double* y) const {
+ void RightMultiply(const double* x, double* y) const final {
m_.RightMultiply(x, y);
}
// y = y + A'x;
- virtual void LeftMultiply(const double* x, double* y) const {
+ void LeftMultiply(const double* x, double* y) const final {
m_.RightMultiply(x, y);
}
- virtual int num_rows() const { return m_.num_rows(); }
- virtual int num_cols() const { return m_.num_rows(); }
+ int num_rows() const final { return m_.num_rows(); }
+ int num_cols() const final { return m_.num_rows(); }
private:
const BlockRandomAccessDiagonalMatrix& m_;
};
-} // namespace
+} // namespace
LinearSolver::Summary SchurComplementSolver::SolveImpl(
BlockSparseMatrix* A,
@@ -127,20 +122,45 @@ LinearSolver::Summary SchurComplementSolver::SolveImpl(
double* x) {
EventLogger event_logger("SchurComplementSolver::Solve");
+ const CompressedRowBlockStructure* bs = A->block_structure();
if (eliminator_.get() == NULL) {
- InitStorage(A->block_structure());
- DetectStructure(*A->block_structure(),
- options_.elimination_groups[0],
+ const int num_eliminate_blocks = options_.elimination_groups[0];
+ const int num_f_blocks = bs->cols.size() - num_eliminate_blocks;
+
+ InitStorage(bs);
+ DetectStructure(*bs,
+ num_eliminate_blocks,
&options_.row_block_size,
&options_.e_block_size,
&options_.f_block_size);
- eliminator_.reset(CHECK_NOTNULL(SchurEliminatorBase::Create(options_)));
- eliminator_->Init(options_.elimination_groups[0], A->block_structure());
- };
+
+ // For the special case of the static structure <2,3,6> with
+ // exactly one f block use the SchurEliminatorForOneFBlock.
+ //
+ // TODO(sameeragarwal): A more scalable template specialization
+ // 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>);
+ } else {
+ eliminator_.reset(SchurEliminatorBase::Create(options_));
+ }
+
+ CHECK(eliminator_);
+ const bool kFullRankETE = true;
+ eliminator_->Init(num_eliminate_blocks, kFullRankETE, bs);
+ }
+
std::fill(x, x + A->num_cols(), 0.0);
event_logger.AddEvent("Setup");
- eliminator_->Eliminate(A, b, per_solve_options.D, lhs_.get(), rhs_.get());
+ eliminator_->Eliminate(BlockSparseMatrixData(*A),
+ b,
+ per_solve_options.D,
+ lhs_.get(),
+ rhs_.get());
event_logger.AddEvent("Eliminate");
double* reduced_solution = x + A->num_cols() - lhs_->num_cols();
@@ -149,7 +169,8 @@ LinearSolver::Summary SchurComplementSolver::SolveImpl(
event_logger.AddEvent("ReducedSolve");
if (summary.termination_type == LINEAR_SOLVER_SUCCESS) {
- eliminator_->BackSubstitute(A, b, per_solve_options.D, reduced_solution, x);
+ eliminator_->BackSubstitute(
+ BlockSparseMatrixData(*A), b, per_solve_options.D, reduced_solution, x);
event_logger.AddEvent("BackSubstitute");
}
@@ -164,9 +185,7 @@ void DenseSchurComplementSolver::InitStorage(
const int num_col_blocks = bs->cols.size();
vector<int> blocks(num_col_blocks - num_eliminate_blocks, 0);
- for (int i = num_eliminate_blocks, j = 0;
- i < num_col_blocks;
- ++i, ++j) {
+ for (int i = num_eliminate_blocks, j = 0; i < num_col_blocks; ++i, ++j) {
blocks[j] = bs->cols[i].size;
}
@@ -177,10 +196,8 @@ void DenseSchurComplementSolver::InitStorage(
// Solve the system Sx = r, assuming that the matrix S is stored in a
// BlockRandomAccessDenseMatrix. The linear system is solved using
// Eigen's Cholesky factorization.
-LinearSolver::Summary
-DenseSchurComplementSolver::SolveReducedLinearSystem(
- const LinearSolver::PerSolveOptions& per_solve_options,
- double* solution) {
+LinearSolver::Summary DenseSchurComplementSolver::SolveReducedLinearSystem(
+ const LinearSolver::PerSolveOptions& per_solve_options, double* solution) {
LinearSolver::Summary summary;
summary.num_iterations = 0;
summary.termination_type = LINEAR_SOLVER_SUCCESS;
@@ -201,8 +218,8 @@ DenseSchurComplementSolver::SolveReducedLinearSystem(
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();
+ .selfadjointView<Eigen::Upper>()
+ .llt();
if (llt.info() != Eigen::Success) {
summary.termination_type = LINEAR_SOLVER_FAILURE;
summary.message =
@@ -213,11 +230,8 @@ DenseSchurComplementSolver::SolveReducedLinearSystem(
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 = LAPACK::SolveInPlaceUsingCholesky(
+ num_rows, m->values(), solution, &summary.message);
}
return summary;
@@ -225,23 +239,14 @@ DenseSchurComplementSolver::SolveReducedLinearSystem(
SparseSchurComplementSolver::SparseSchurComplementSolver(
const LinearSolver::Options& options)
- : SchurComplementSolver(options),
- factor_(NULL),
- cxsparse_factor_(NULL) {
-}
-
-SparseSchurComplementSolver::~SparseSchurComplementSolver() {
- if (factor_ != NULL) {
- ss_.Free(factor_);
- factor_ = NULL;
- }
-
- if (cxsparse_factor_ != NULL) {
- cxsparse_.Free(cxsparse_factor_);
- cxsparse_factor_ = NULL;
+ : SchurComplementSolver(options) {
+ if (options.type != ITERATIVE_SCHUR) {
+ sparse_cholesky_ = SparseCholesky::Create(options);
}
}
+SparseSchurComplementSolver::~SparseSchurComplementSolver() {}
+
// Determine the non-zero blocks in the Schur Complement matrix, and
// initialize a BlockRandomAccessSparseMatrix object.
void SparseSchurComplementSolver::InitStorage(
@@ -255,7 +260,7 @@ void SparseSchurComplementSolver::InitStorage(
blocks_[i - num_eliminate_blocks] = bs->cols[i].size;
}
- set<pair<int, int> > block_pairs;
+ set<pair<int, int>> block_pairs;
for (int i = 0; i < blocks_.size(); ++i) {
block_pairs.insert(make_pair(i, i));
}
@@ -293,7 +298,7 @@ void SparseSchurComplementSolver::InitStorage(
}
}
- // Remaing rows do not contribute to the chunks and directly go
+ // Remaining rows do not contribute to the chunks and directly go
// into the schur complement via an outer product.
for (; r < num_row_blocks; ++r) {
const CompressedRow& row = bs->rows[r];
@@ -313,296 +318,49 @@ void SparseSchurComplementSolver::InitStorage(
set_rhs(new double[lhs()->num_rows()]);
}
-LinearSolver::Summary
-SparseSchurComplementSolver::SolveReducedLinearSystem(
- const LinearSolver::PerSolveOptions& per_solve_options,
- double* solution) {
+LinearSolver::Summary SparseSchurComplementSolver::SolveReducedLinearSystem(
+ const LinearSolver::PerSolveOptions& per_solve_options, double* solution) {
if (options().type == ITERATIVE_SCHUR) {
- CHECK(options().use_explicit_schur_complement);
return SolveReducedLinearSystemUsingConjugateGradients(per_solve_options,
solution);
}
- switch (options().sparse_linear_algebra_library_type) {
- case SUITE_SPARSE:
- return SolveReducedLinearSystemUsingSuiteSparse(per_solve_options,
- solution);
- case CX_SPARSE:
- return SolveReducedLinearSystemUsingCXSparse(per_solve_options,
- solution);
- case EIGEN_SPARSE:
- return SolveReducedLinearSystemUsingEigen(per_solve_options,
- solution);
- default:
- LOG(FATAL) << "Unknown sparse linear algebra library : "
- << options().sparse_linear_algebra_library_type;
- }
-
- return LinearSolver::Summary();
-}
-
-// Solve the system Sx = r, assuming that the matrix S is stored in a
-// BlockRandomAccessSparseMatrix. The linear system is solved using
-// CHOLMOD's sparse cholesky factorization routines.
-LinearSolver::Summary
-SparseSchurComplementSolver::SolveReducedLinearSystemUsingSuiteSparse(
- const LinearSolver::PerSolveOptions& per_solve_options,
- double* solution) {
-#ifdef CERES_NO_SUITESPARSE
-
- LinearSolver::Summary summary;
- summary.num_iterations = 0;
- summary.termination_type = LINEAR_SOLVER_FATAL_ERROR;
- summary.message = "Ceres was not built with SuiteSparse support. "
- "Therefore, SPARSE_SCHUR cannot be used with SUITE_SPARSE";
- return summary;
-
-#else
-
LinearSolver::Summary summary;
summary.num_iterations = 0;
summary.termination_type = LINEAR_SOLVER_SUCCESS;
summary.message = "Success.";
- TripletSparseMatrix* tsm =
- const_cast<TripletSparseMatrix*>(
- down_cast<const BlockRandomAccessSparseMatrix*>(lhs())->matrix());
- const int num_rows = tsm->num_rows();
-
- // The case where there are no f blocks, and the system is block
- // diagonal.
- if (num_rows == 0) {
+ const TripletSparseMatrix* tsm =
+ down_cast<const BlockRandomAccessSparseMatrix*>(lhs())->matrix();
+ if (tsm->num_rows() == 0) {
return summary;
}
- summary.num_iterations = 1;
- cholmod_sparse* cholmod_lhs = NULL;
- if (options().use_postordering) {
- // If we are going to do a full symbolic analysis of the schur
- // complement matrix from scratch and not rely on the
- // pre-ordering, then the fastest path in cholmod_factorize is the
- // one corresponding to upper triangular matrices.
-
- // Create a upper triangular symmetric matrix.
- cholmod_lhs = ss_.CreateSparseMatrix(tsm);
- cholmod_lhs->stype = 1;
-
- if (factor_ == NULL) {
- factor_ = ss_.BlockAnalyzeCholesky(cholmod_lhs,
- blocks_,
- blocks_,
- &summary.message);
- }
+ std::unique_ptr<CompressedRowSparseMatrix> lhs;
+ const CompressedRowSparseMatrix::StorageType storage_type =
+ sparse_cholesky_->StorageType();
+ if (storage_type == CompressedRowSparseMatrix::UPPER_TRIANGULAR) {
+ lhs.reset(CompressedRowSparseMatrix::FromTripletSparseMatrix(*tsm));
+ lhs->set_storage_type(CompressedRowSparseMatrix::UPPER_TRIANGULAR);
} else {
- // If we are going to use the natural ordering (i.e. rely on the
- // pre-ordering computed by solver_impl.cc), then the fastest
- // path in cholmod_factorize is the one corresponding to lower
- // triangular matrices.
-
- // Create a upper triangular symmetric matrix.
- cholmod_lhs = ss_.CreateSparseMatrixTranspose(tsm);
- cholmod_lhs->stype = -1;
-
- if (factor_ == NULL) {
- factor_ = ss_.AnalyzeCholeskyWithNaturalOrdering(cholmod_lhs,
- &summary.message);
- }
- }
-
- if (factor_ == NULL) {
- ss_.Free(cholmod_lhs);
- summary.termination_type = LINEAR_SOLVER_FATAL_ERROR;
- // No need to set message as it has already been set by the
- // symbolic analysis routines above.
- return summary;
+ lhs.reset(
+ CompressedRowSparseMatrix::FromTripletSparseMatrixTransposed(*tsm));
+ lhs->set_storage_type(CompressedRowSparseMatrix::LOWER_TRIANGULAR);
}
- summary.termination_type =
- ss_.Cholesky(cholmod_lhs, factor_, &summary.message);
-
- ss_.Free(cholmod_lhs);
-
- if (summary.termination_type != LINEAR_SOLVER_SUCCESS) {
- // No need to set message as it has already been set by the
- // numeric factorization routine above.
- return summary;
- }
-
- cholmod_dense* cholmod_rhs =
- ss_.CreateDenseVector(const_cast<double*>(rhs()), num_rows, num_rows);
- cholmod_dense* cholmod_solution = ss_.Solve(factor_,
- cholmod_rhs,
- &summary.message);
- ss_.Free(cholmod_rhs);
-
- if (cholmod_solution == NULL) {
- summary.message =
- "SuiteSparse failure. Unable to perform triangular solve.";
- summary.termination_type = LINEAR_SOLVER_FAILURE;
- return summary;
- }
-
- VectorRef(solution, num_rows)
- = VectorRef(static_cast<double*>(cholmod_solution->x), num_rows);
- ss_.Free(cholmod_solution);
- return summary;
-#endif // CERES_NO_SUITESPARSE
-}
-
-// Solve the system Sx = r, assuming that the matrix S is stored in a
-// BlockRandomAccessSparseMatrix. The linear system is solved using
-// CXSparse's sparse cholesky factorization routines.
-LinearSolver::Summary
-SparseSchurComplementSolver::SolveReducedLinearSystemUsingCXSparse(
- const LinearSolver::PerSolveOptions& per_solve_options,
- double* solution) {
-#ifdef CERES_NO_CXSPARSE
-
- LinearSolver::Summary summary;
- summary.num_iterations = 0;
- summary.termination_type = LINEAR_SOLVER_FATAL_ERROR;
- summary.message = "Ceres was not built with CXSparse support. "
- "Therefore, SPARSE_SCHUR cannot be used with CX_SPARSE";
- return summary;
-
-#else
-
- LinearSolver::Summary summary;
- summary.num_iterations = 0;
- summary.termination_type = LINEAR_SOLVER_SUCCESS;
- summary.message = "Success.";
-
- // Extract the TripletSparseMatrix that is used for actually storing S.
- TripletSparseMatrix* tsm =
- const_cast<TripletSparseMatrix*>(
- down_cast<const BlockRandomAccessSparseMatrix*>(lhs())->matrix());
- const int num_rows = tsm->num_rows();
-
- // The case where there are no f blocks, and the system is block
- // diagonal.
- if (num_rows == 0) {
- return summary;
- }
-
- cs_di* lhs = CHECK_NOTNULL(cxsparse_.CreateSparseMatrix(tsm));
- VectorRef(solution, num_rows) = ConstVectorRef(rhs(), num_rows);
-
- // Compute symbolic factorization if not available.
- if (cxsparse_factor_ == NULL) {
- cxsparse_factor_ = cxsparse_.BlockAnalyzeCholesky(lhs, blocks_, blocks_);
- }
-
- if (cxsparse_factor_ == NULL) {
- summary.termination_type = LINEAR_SOLVER_FATAL_ERROR;
- summary.message =
- "CXSparse failure. Unable to find symbolic factorization.";
- } else if (!cxsparse_.SolveCholesky(lhs, cxsparse_factor_, solution)) {
- summary.termination_type = LINEAR_SOLVER_FAILURE;
- summary.message = "CXSparse::SolveCholesky failed.";
- }
-
- cxsparse_.Free(lhs);
- return summary;
-#endif // CERES_NO_CXPARSE
-}
-
-// Solve the system Sx = r, assuming that the matrix S is stored in a
-// BlockRandomAccessSparseMatrix. The linear system is solved using
-// Eigen's sparse cholesky factorization routines.
-LinearSolver::Summary
-SparseSchurComplementSolver::SolveReducedLinearSystemUsingEigen(
- const LinearSolver::PerSolveOptions& per_solve_options,
- double* solution) {
-#ifndef CERES_USE_EIGEN_SPARSE
-
- LinearSolver::Summary summary;
- summary.num_iterations = 0;
- summary.termination_type = LINEAR_SOLVER_FATAL_ERROR;
- summary.message =
- "SPARSE_SCHUR cannot be used with EIGEN_SPARSE. "
- "Ceres was not built with support for "
- "Eigen's SimplicialLDLT decomposition. "
- "This requires enabling building with -DEIGENSPARSE=ON.";
- return summary;
-
-#else
- EventLogger event_logger("SchurComplementSolver::EigenSolve");
- LinearSolver::Summary summary;
- summary.num_iterations = 0;
- summary.termination_type = LINEAR_SOLVER_SUCCESS;
- summary.message = "Success.";
-
- // Extract the TripletSparseMatrix that is used for actually storing S.
- TripletSparseMatrix* tsm =
- const_cast<TripletSparseMatrix*>(
- down_cast<const BlockRandomAccessSparseMatrix*>(lhs())->matrix());
- const int num_rows = tsm->num_rows();
-
- // The case where there are no f blocks, and the system is block
- // diagonal.
- if (num_rows == 0) {
- return summary;
- }
-
- // This is an upper triangular matrix.
- CompressedRowSparseMatrix crsm(*tsm);
- // Map this to a column major, lower triangular matrix.
- Eigen::MappedSparseMatrix<double, Eigen::ColMajor> eigen_lhs(
- crsm.num_rows(),
- crsm.num_rows(),
- crsm.num_nonzeros(),
- crsm.mutable_rows(),
- crsm.mutable_cols(),
- crsm.mutable_values());
- event_logger.AddEvent("ToCompressedRowSparseMatrix");
-
- // Compute symbolic factorization if one does not exist.
- if (simplicial_ldlt_.get() == NULL) {
- simplicial_ldlt_.reset(new SimplicialLDLT);
- // This ordering is quite bad. The scalar ordering produced by the
- // AMD algorithm is quite bad and can be an order of magnitude
- // worse than the one computed using the block version of the
- // algorithm.
- simplicial_ldlt_->analyzePattern(eigen_lhs);
- if (VLOG_IS_ON(2)) {
- std::stringstream ss;
- simplicial_ldlt_->dumpMemory(ss);
- VLOG(2) << "Symbolic Analysis\n"
- << ss.str();
- }
- event_logger.AddEvent("Analysis");
- if (simplicial_ldlt_->info() != Eigen::Success) {
- summary.termination_type = LINEAR_SOLVER_FATAL_ERROR;
- summary.message =
- "Eigen failure. Unable to find symbolic factorization.";
- return summary;
- }
- }
-
- simplicial_ldlt_->factorize(eigen_lhs);
- event_logger.AddEvent("Factorize");
- if (simplicial_ldlt_->info() != Eigen::Success) {
- summary.termination_type = LINEAR_SOLVER_FAILURE;
- summary.message = "Eigen failure. Unable to find numeric factoriztion.";
- return summary;
- }
-
- VectorRef(solution, num_rows) =
- simplicial_ldlt_->solve(ConstVectorRef(rhs(), num_rows));
- event_logger.AddEvent("Solve");
- if (simplicial_ldlt_->info() != Eigen::Success) {
- summary.termination_type = LINEAR_SOLVER_FAILURE;
- summary.message = "Eigen failure. Unable to do triangular solve.";
- }
+ *lhs->mutable_col_blocks() = blocks_;
+ *lhs->mutable_row_blocks() = blocks_;
+ summary.num_iterations = 1;
+ summary.termination_type = sparse_cholesky_->FactorAndSolve(
+ lhs.get(), rhs(), solution, &summary.message);
return summary;
-#endif // CERES_USE_EIGEN_SPARSE
}
LinearSolver::Summary
SparseSchurComplementSolver::SolveReducedLinearSystemUsingConjugateGradients(
- const LinearSolver::PerSolveOptions& per_solve_options,
- double* solution) {
+ const LinearSolver::PerSolveOptions& per_solve_options, double* solution) {
+ CHECK(options().use_explicit_schur_complement);
const int num_rows = lhs()->num_rows();
// The case where there are no f blocks, and the system is block
// diagonal.
@@ -621,27 +379,24 @@ SparseSchurComplementSolver::SolveReducedLinearSystemUsingConjugateGradients(
preconditioner_.reset(new BlockRandomAccessDiagonalMatrix(blocks_));
}
- BlockRandomAccessSparseMatrix* sc =
- down_cast<BlockRandomAccessSparseMatrix*>(
- const_cast<BlockRandomAccessMatrix*>(lhs()));
+ BlockRandomAccessSparseMatrix* sc = down_cast<BlockRandomAccessSparseMatrix*>(
+ const_cast<BlockRandomAccessMatrix*>(lhs()));
// Extract block diagonal from the Schur complement to construct the
// schur_jacobi preconditioner.
- for (int i = 0; i < blocks_.size(); ++i) {
+ for (int i = 0; i < blocks_.size(); ++i) {
const int block_size = blocks_[i];
int sc_r, sc_c, sc_row_stride, sc_col_stride;
CellInfo* sc_cell_info =
- CHECK_NOTNULL(sc->GetCell(i, i,
- &sc_r, &sc_c,
- &sc_row_stride, &sc_col_stride));
+ sc->GetCell(i, i, &sc_r, &sc_c, &sc_row_stride, &sc_col_stride);
+ CHECK(sc_cell_info != nullptr);
MatrixRef sc_m(sc_cell_info->values, sc_row_stride, sc_col_stride);
int pre_r, pre_c, pre_row_stride, pre_col_stride;
- CellInfo* pre_cell_info = CHECK_NOTNULL(
- preconditioner_->GetCell(i, i,
- &pre_r, &pre_c,
- &pre_row_stride, &pre_col_stride));
+ CellInfo* pre_cell_info = preconditioner_->GetCell(
+ i, i, &pre_r, &pre_c, &pre_row_stride, &pre_col_stride);
+ CHECK(pre_cell_info != nullptr);
MatrixRef pre_m(pre_cell_info->values, pre_row_stride, pre_col_stride);
pre_m.block(pre_r, pre_c, block_size, block_size) =
@@ -651,12 +406,11 @@ SparseSchurComplementSolver::SolveReducedLinearSystemUsingConjugateGradients(
VectorRef(solution, num_rows).setZero();
- scoped_ptr<LinearOperator> lhs_adapter(
+ std::unique_ptr<LinearOperator> lhs_adapter(
new BlockRandomAccessSparseMatrixAdapter(*sc));
- scoped_ptr<LinearOperator> preconditioner_adapter(
+ std::unique_ptr<LinearOperator> preconditioner_adapter(
new BlockRandomAccessDiagonalMatrixAdapter(*preconditioner_));
-
LinearSolver::Options cg_options;
cg_options.min_num_iterations = options().min_num_iterations;
cg_options.max_num_iterations = options().max_num_iterations;
@@ -667,10 +421,8 @@ SparseSchurComplementSolver::SolveReducedLinearSystemUsingConjugateGradients(
cg_per_solve_options.q_tolerance = per_solve_options.q_tolerance;
cg_per_solve_options.preconditioner = preconditioner_adapter.get();
- return cg_solver.Solve(lhs_adapter.get(),
- rhs(),
- cg_per_solve_options,
- solution);
+ return cg_solver.Solve(
+ lhs_adapter.get(), rhs(), cg_per_solve_options, solution);
}
} // namespace internal
diff --git a/extern/ceres/internal/ceres/schur_complement_solver.h b/extern/ceres/internal/ceres/schur_complement_solver.h
index 714dafc5b0c..87f04785794 100644
--- a/extern/ceres/internal/ceres/schur_complement_solver.h
+++ b/extern/ceres/internal/ceres/schur_complement_solver.h
@@ -31,22 +31,19 @@
#ifndef CERES_INTERNAL_SCHUR_COMPLEMENT_SOLVER_H_
#define CERES_INTERNAL_SCHUR_COMPLEMENT_SOLVER_H_
+#include <memory>
#include <set>
#include <utility>
#include <vector>
-#include "ceres/internal/port.h"
-
+#include "ceres/block_random_access_diagonal_matrix.h"
#include "ceres/block_random_access_matrix.h"
#include "ceres/block_sparse_matrix.h"
#include "ceres/block_structure.h"
-#include "ceres/cxsparse.h"
+#include "ceres/internal/port.h"
#include "ceres/linear_solver.h"
#include "ceres/schur_eliminator.h"
-#include "ceres/suitesparse.h"
-#include "ceres/internal/scoped_ptr.h"
#include "ceres/types.h"
-#include "ceres/block_random_access_diagonal_matrix.h"
#ifdef CERES_USE_EIGEN_SPARSE
#include "Eigen/SparseCholesky"
@@ -57,6 +54,7 @@ namespace ceres {
namespace internal {
class BlockSparseMatrix;
+class SparseCholesky;
// Base class for Schur complement based linear least squares
// solvers. It assumes that the input linear system Ax = b can be
@@ -96,33 +94,37 @@ class BlockSparseMatrix;
// be used for problems with upto a few hundred cameras.
//
// SparseSchurComplementSolver: For problems where the Schur
-// complement matrix is large and sparse. It requires that
-// CHOLMOD/SuiteSparse be installed, as it uses CHOLMOD to find a
-// sparse Cholesky factorization of the Schur complement. This solver
-// can be used for solving structure from motion problems with tens of
-// thousands of cameras, though depending on the exact sparsity
-// structure, it maybe better to use an iterative solver.
+// complement matrix is large and sparse. It requires that Ceres be
+// build with at least one sparse linear algebra library, as it
+// computes a sparse Cholesky factorization of the Schur complement.
+//
+// This solver can be used for solving structure from motion problems
+// with tens of thousands of cameras, though depending on the exact
+// sparsity structure, it maybe better to use an iterative solver.
//
// The two solvers can be instantiated by calling
// LinearSolver::CreateLinearSolver with LinearSolver::Options::type
// set to DENSE_SCHUR and SPARSE_SCHUR
-// respectively. LinearSolver::Options::elimination_groups[0] should be
-// at least 1.
+// respectively. LinearSolver::Options::elimination_groups[0] should
+// be at least 1.
class 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);
}
+ SchurComplementSolver(const SchurComplementSolver&) = delete;
+ void operator=(const SchurComplementSolver&) = delete;
// LinearSolver methods
virtual ~SchurComplementSolver() {}
- virtual LinearSolver::Summary SolveImpl(
+ LinearSolver::Summary SolveImpl(
BlockSparseMatrix* A,
const double* b,
const LinearSolver::PerSolveOptions& per_solve_options,
- double* x);
+ double* x) override;
protected:
const LinearSolver::Options& options() const { return options_; }
@@ -140,11 +142,9 @@ class SchurComplementSolver : public BlockSparseMatrixSolver {
LinearSolver::Options options_;
- scoped_ptr<SchurEliminatorBase> eliminator_;
- scoped_ptr<BlockRandomAccessMatrix> lhs_;
- scoped_array<double> rhs_;
-
- CERES_DISALLOW_COPY_AND_ASSIGN(SchurComplementSolver);
+ std::unique_ptr<SchurEliminatorBase> eliminator_;
+ std::unique_ptr<BlockRandomAccessMatrix> lhs_;
+ std::unique_ptr<double[]> rhs_;
};
// Dense Cholesky factorization based solver.
@@ -152,72 +152,40 @@ class DenseSchurComplementSolver : public SchurComplementSolver {
public:
explicit DenseSchurComplementSolver(const LinearSolver::Options& options)
: SchurComplementSolver(options) {}
+ DenseSchurComplementSolver(const DenseSchurComplementSolver&) = delete;
+ void operator=(const DenseSchurComplementSolver&) = delete;
+
virtual ~DenseSchurComplementSolver() {}
private:
- virtual void InitStorage(const CompressedRowBlockStructure* bs);
- virtual LinearSolver::Summary SolveReducedLinearSystem(
+ void InitStorage(const CompressedRowBlockStructure* bs) final;
+ LinearSolver::Summary SolveReducedLinearSystem(
const LinearSolver::PerSolveOptions& per_solve_options,
- double* solution);
-
- CERES_DISALLOW_COPY_AND_ASSIGN(DenseSchurComplementSolver);
+ double* solution) final;
};
// Sparse Cholesky factorization based solver.
class SparseSchurComplementSolver : public SchurComplementSolver {
public:
explicit SparseSchurComplementSolver(const LinearSolver::Options& options);
+ SparseSchurComplementSolver(const SparseSchurComplementSolver&) = delete;
+ void operator=(const SparseSchurComplementSolver&) = delete;
+
virtual ~SparseSchurComplementSolver();
private:
- virtual void InitStorage(const CompressedRowBlockStructure* bs);
- virtual LinearSolver::Summary SolveReducedLinearSystem(
- const LinearSolver::PerSolveOptions& per_solve_options,
- double* solution);
- LinearSolver::Summary SolveReducedLinearSystemUsingSuiteSparse(
+ void InitStorage(const CompressedRowBlockStructure* bs) final;
+ LinearSolver::Summary SolveReducedLinearSystem(
const LinearSolver::PerSolveOptions& per_solve_options,
- double* solution);
- LinearSolver::Summary SolveReducedLinearSystemUsingCXSparse(
- const LinearSolver::PerSolveOptions& per_solve_options,
- double* solution);
- LinearSolver::Summary SolveReducedLinearSystemUsingEigen(
- const LinearSolver::PerSolveOptions& per_solve_options,
- double* solution);
+ double* solution) final;
LinearSolver::Summary SolveReducedLinearSystemUsingConjugateGradients(
const LinearSolver::PerSolveOptions& per_solve_options,
double* solution);
// Size of the blocks in the Schur complement.
std::vector<int> blocks_;
-
- SuiteSparse ss_;
- // Symbolic factorization of the reduced linear system. Precomputed
- // once and reused in subsequent calls.
- cholmod_factor* factor_;
-
- CXSparse cxsparse_;
- // Cached factorization
- cs_dis* cxsparse_factor_;
-
-#ifdef CERES_USE_EIGEN_SPARSE
-
- // The preprocessor gymnastics here are dealing with the fact that
- // before version 3.2.2, Eigen did not support a third template
- // parameter to specify the ordering.
-#if EIGEN_VERSION_AT_LEAST(3,2,2)
- typedef Eigen::SimplicialLDLT<Eigen::SparseMatrix<double>, Eigen::Lower,
- Eigen::NaturalOrdering<int> >
- SimplicialLDLT;
-#else
- typedef Eigen::SimplicialLDLT<Eigen::SparseMatrix<double>, Eigen::Lower>
- SimplicialLDLT;
-#endif
-
- scoped_ptr<SimplicialLDLT> simplicial_ldlt_;
-#endif
-
- scoped_ptr<BlockRandomAccessDiagonalMatrix> preconditioner_;
- CERES_DISALLOW_COPY_AND_ASSIGN(SparseSchurComplementSolver);
+ std::unique_ptr<SparseCholesky> sparse_cholesky_;
+ std::unique_ptr<BlockRandomAccessDiagonalMatrix> preconditioner_;
};
} // namespace internal
diff --git a/extern/ceres/internal/ceres/schur_eliminator.cc b/extern/ceres/internal/ceres/schur_eliminator.cc
index ec0e2a020e5..bc8ced4bc7c 100644
--- a/extern/ceres/internal/ceres/schur_eliminator.cc
+++ b/extern/ceres/internal/ceres/schur_eliminator.cc
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
//=========================================
//
-// This file is generated using generate_eliminator_specialization.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
#include "ceres/linear_solver.h"
#include "ceres/schur_eliminator.h"
@@ -50,106 +49,105 @@ namespace internal {
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);
- }
- if ((options.row_block_size == 2) &&
- (options.e_block_size == 2) &&
- (options.f_block_size == 3)) {
- return new 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);
- }
- if ((options.row_block_size == 2) &&
- (options.e_block_size == 2) &&
- (options.f_block_size == Eigen::Dynamic)) {
- return new 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);
- }
- if ((options.row_block_size == 2) &&
- (options.e_block_size == 3) &&
- (options.f_block_size == 4)) {
- return new 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);
- }
- if ((options.row_block_size == 2) &&
- (options.e_block_size == 3) &&
- (options.f_block_size == 9)) {
- return new SchurEliminator<2, 3, 9>(options);
- }
- if ((options.row_block_size == 2) &&
- (options.e_block_size == 3) &&
- (options.f_block_size == Eigen::Dynamic)) {
- return new 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);
- }
- if ((options.row_block_size == 2) &&
- (options.e_block_size == 4) &&
- (options.f_block_size == 4)) {
- return new SchurEliminator<2, 4, 4>(options);
- }
- if ((options.row_block_size == 2) &&
- (options.e_block_size == 4) &&
- (options.f_block_size == 8)) {
- return new 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);
- }
- if ((options.row_block_size == 2) &&
- (options.e_block_size == 4) &&
- (options.f_block_size == Eigen::Dynamic)) {
- return new SchurEliminator<2, 4, Eigen::Dynamic>(options);
- }
- if ((options.row_block_size == 2) &&
- (options.e_block_size == Eigen::Dynamic) &&
- (options.f_block_size == Eigen::Dynamic)) {
- return new SchurEliminator<2, Eigen::Dynamic, Eigen::Dynamic>(options);
- }
- if ((options.row_block_size == 4) &&
- (options.e_block_size == 4) &&
- (options.f_block_size == 2)) {
- return new 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);
- }
- if ((options.row_block_size == 4) &&
- (options.e_block_size == 4) &&
- (options.f_block_size == 4)) {
- return new SchurEliminator<4, 4, 4>(options);
- }
- if ((options.row_block_size == 4) &&
- (options.e_block_size == 4) &&
- (options.f_block_size == Eigen::Dynamic)) {
- return new SchurEliminator<4, 4, Eigen::Dynamic>(options);
- }
- if ((options.row_block_size == Eigen::Dynamic) &&
- (options.e_block_size == Eigen::Dynamic) &&
- (options.f_block_size == Eigen::Dynamic)) {
- return new SchurEliminator<Eigen::Dynamic, Eigen::Dynamic, Eigen::Dynamic>(options);
- }
+ if ((options.row_block_size == 2) &&
+ (options.e_block_size == 2) &&
+ (options.f_block_size == 2)) {
+ return new 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);
+ }
+ if ((options.row_block_size == 2) &&
+ (options.e_block_size == 2) &&
+ (options.f_block_size == 4)) {
+ return new SchurEliminator<2, 2, 4>(options);
+ }
+ if ((options.row_block_size == 2) &&
+ (options.e_block_size == 2)) {
+ return new 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);
+ }
+ if ((options.row_block_size == 2) &&
+ (options.e_block_size == 3) &&
+ (options.f_block_size == 4)) {
+ return new 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);
+ }
+ if ((options.row_block_size == 2) &&
+ (options.e_block_size == 3) &&
+ (options.f_block_size == 9)) {
+ return new SchurEliminator<2, 3, 9>(options);
+ }
+ if ((options.row_block_size == 2) &&
+ (options.e_block_size == 3)) {
+ return new 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);
+ }
+ if ((options.row_block_size == 2) &&
+ (options.e_block_size == 4) &&
+ (options.f_block_size == 4)) {
+ return new 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);
+ }
+ if ((options.row_block_size == 2) &&
+ (options.e_block_size == 4) &&
+ (options.f_block_size == 8)) {
+ return new 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);
+ }
+ if ((options.row_block_size == 2) &&
+ (options.e_block_size == 4)) {
+ return new SchurEliminator<2, 4, Eigen::Dynamic>(options);
+ }
+ if (options.row_block_size == 2){
+ return new 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);
+ }
+ if ((options.row_block_size == 4) &&
+ (options.e_block_size == 4) &&
+ (options.f_block_size == 2)) {
+ return new 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);
+ }
+ if ((options.row_block_size == 4) &&
+ (options.e_block_size == 4) &&
+ (options.f_block_size == 4)) {
+ return new SchurEliminator<4, 4, 4>(options);
+ }
+ if ((options.row_block_size == 4) &&
+ (options.e_block_size == 4)) {
+ return new SchurEliminator<4, 4, Eigen::Dynamic>(options);
+ }
#endif
VLOG(1) << "Template specializations not found for <"
diff --git a/extern/ceres/internal/ceres/schur_eliminator.h b/extern/ceres/internal/ceres/schur_eliminator.h
index 761b58adc7f..66fcb4d58e8 100644
--- a/extern/ceres/internal/ceres/schur_eliminator.h
+++ b/extern/ceres/internal/ceres/schur_eliminator.h
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2019 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -32,14 +32,17 @@
#define CERES_INTERNAL_SCHUR_ELIMINATOR_H_
#include <map>
+#include <memory>
+#include <mutex>
#include <vector>
-#include "ceres/mutex.h"
+
+#include "Eigen/Dense"
#include "ceres/block_random_access_matrix.h"
#include "ceres/block_sparse_matrix.h"
#include "ceres/block_structure.h"
-#include "ceres/linear_solver.h"
#include "ceres/internal/eigen.h"
-#include "ceres/internal/scoped_ptr.h"
+#include "ceres/internal/port.h"
+#include "ceres/linear_solver.h"
namespace ceres {
namespace internal {
@@ -50,7 +53,7 @@ namespace internal {
//
// E y + F z = b
//
-// Where x = [y;z] is a partition of the variables. The paritioning
+// 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
@@ -147,7 +150,7 @@ namespace internal {
// Where the sum is over chunks and E_k'E_k is dense matrix of size y1
// x y1.
//
-// Advanced usage. Uptil now it has been assumed that the user would
+// Advanced usage. Until now it has been assumed that the user would
// be interested in all of the Schur Complement S. However, it is also
// possible to use this eliminator to obtain an arbitrary submatrix of
// the full Schur complement. When the eliminator is generating the
@@ -171,7 +174,14 @@ class SchurEliminatorBase {
// CompressedRowBlockStructure object passed to this method is the
// same one (or is equivalent to) the one associated with the
// BlockSparseMatrix objects below.
+ //
+ // assume_full_rank_ete controls how the eliminator inverts with the
+ // diagonal blocks corresponding to e blocks in A'A. If
+ // assume_full_rank_ete is true, then a Cholesky factorization is
+ // used to compute the inverse, otherwise a singular value
+ // decomposition is used to compute the pseudo inverse.
virtual void Init(int num_eliminate_blocks,
+ bool assume_full_rank_ete,
const CompressedRowBlockStructure* bs) = 0;
// Compute the Schur complement system from the augmented linear
@@ -185,7 +195,7 @@ class SchurEliminatorBase {
//
// Since the Schur complement is a symmetric matrix, only the upper
// triangular part of the Schur complement is computed.
- virtual void Eliminate(const BlockSparseMatrix* A,
+ virtual void Eliminate(const BlockSparseMatrixData& A,
const double* b,
const double* D,
BlockRandomAccessMatrix* lhs,
@@ -194,7 +204,7 @@ class SchurEliminatorBase {
// Given values for the variables z in the F block of A, solve for
// the optimal values of the variables y corresponding to the E
// block in A.
- virtual void BackSubstitute(const BlockSparseMatrix* A,
+ virtual void BackSubstitute(const BlockSparseMatrixData& A,
const double* b,
const double* D,
const double* z,
@@ -210,32 +220,31 @@ class SchurEliminatorBase {
// parameters as template arguments so that they are visible to the
// compiler and can be used for compile time optimization of the low
// level linear algebra routines.
-//
-// This implementation is mulithreaded using OpenMP. The level of
-// parallelism is controlled by LinearSolver::Options::num_threads.
template <int kRowBlockSize = Eigen::Dynamic,
int kEBlockSize = Eigen::Dynamic,
- int kFBlockSize = Eigen::Dynamic >
+ int kFBlockSize = Eigen::Dynamic>
class SchurEliminator : public SchurEliminatorBase {
public:
explicit SchurEliminator(const LinearSolver::Options& options)
- : num_threads_(options.num_threads) {
+ : num_threads_(options.num_threads), context_(options.context) {
+ CHECK(context_ != nullptr);
}
// SchurEliminatorBase Interface
virtual ~SchurEliminator();
- virtual void Init(int num_eliminate_blocks,
- const CompressedRowBlockStructure* bs);
- virtual void Eliminate(const BlockSparseMatrix* A,
- const double* b,
- const double* D,
- BlockRandomAccessMatrix* lhs,
- double* rhs);
- virtual void BackSubstitute(const BlockSparseMatrix* A,
- const double* b,
- const double* D,
- const double* z,
- double* y);
+ void Init(int num_eliminate_blocks,
+ bool assume_full_rank_ete,
+ const CompressedRowBlockStructure* bs) final;
+ void Eliminate(const BlockSparseMatrixData& A,
+ const double* b,
+ const double* D,
+ BlockRandomAccessMatrix* lhs,
+ double* rhs) final;
+ void BackSubstitute(const BlockSparseMatrixData& A,
+ const double* b,
+ const double* D,
+ const double* z,
+ double* y) final;
private:
// Chunk objects store combinatorial information needed to
@@ -273,7 +282,7 @@ class SchurEliminator : public SchurEliminatorBase {
void ChunkDiagonalBlockAndGradient(
const Chunk& chunk,
- const BlockSparseMatrix* A,
+ const BlockSparseMatrixData& A,
const double* b,
int row_block_counter,
typename EigenTypes<kEBlockSize, kEBlockSize>::Matrix* eet,
@@ -282,33 +291,36 @@ class SchurEliminator : public SchurEliminatorBase {
BlockRandomAccessMatrix* lhs);
void UpdateRhs(const Chunk& chunk,
- const BlockSparseMatrix* A,
+ const BlockSparseMatrixData& A,
const double* b,
int row_block_counter,
const double* inverse_ete_g,
double* rhs);
- void ChunkOuterProduct(const CompressedRowBlockStructure* bs,
+ void ChunkOuterProduct(int thread_id,
+ const CompressedRowBlockStructure* bs,
const Matrix& inverse_eet,
const double* buffer,
const BufferLayoutType& buffer_layout,
BlockRandomAccessMatrix* lhs);
- void EBlockRowOuterProduct(const BlockSparseMatrix* A,
+ void EBlockRowOuterProduct(const BlockSparseMatrixData& A,
int row_block_index,
BlockRandomAccessMatrix* lhs);
+ void NoEBlockRowsUpdate(const BlockSparseMatrixData& A,
+ const double* b,
+ int row_block_counter,
+ BlockRandomAccessMatrix* lhs,
+ double* rhs);
- void NoEBlockRowsUpdate(const BlockSparseMatrix* A,
- const double* b,
- int row_block_counter,
- BlockRandomAccessMatrix* lhs,
- double* rhs);
-
- void NoEBlockRowOuterProduct(const BlockSparseMatrix* A,
+ void NoEBlockRowOuterProduct(const BlockSparseMatrixData& A,
int row_block_index,
BlockRandomAccessMatrix* lhs);
+ int num_threads_;
+ ContextImpl* context_;
int num_eliminate_blocks_;
+ bool assume_full_rank_ete_;
// Block layout of the columns of the reduced linear system. Since
// the f blocks can be of varying size, this vector stores the
@@ -330,7 +342,7 @@ class SchurEliminator : public SchurEliminatorBase {
//
// [thread_id * buffer_size_ , (thread_id + 1) * buffer_size_]
//
- scoped_array<double> buffer_;
+ std::unique_ptr<double[]> buffer_;
// Buffer to store per thread matrix matrix products used by
// ChunkOuterProduct. Like buffer_ it is of size num_threads *
@@ -338,15 +350,275 @@ class SchurEliminator : public SchurEliminatorBase {
//
// [thread_id * buffer_size_ , (thread_id + 1) * buffer_size_ -1]
//
- scoped_array<double> chunk_outer_product_buffer_;
+ std::unique_ptr<double[]> chunk_outer_product_buffer_;
int buffer_size_;
- int num_threads_;
int uneliminated_row_begins_;
// Locks for the blocks in the right hand side of the reduced linear
// system.
- std::vector<Mutex*> rhs_locks_;
+ std::vector<std::mutex*> rhs_locks_;
+};
+
+// SchurEliminatorForOneFBlock specializes the SchurEliminatorBase interface for
+// the case where there is exactly one f-block and all three parameters
+// kRowBlockSize, kEBlockSize and KFBlockSize are known at compile time. This is
+// the case for some two view bundle adjustment problems which have very
+// stringent latency requirements.
+//
+// Under these assumptions, we can simplify the more general algorithm
+// implemented by SchurEliminatorImpl significantly. Two of the major
+// contributors to the increased performance are:
+//
+// 1. Simpler loop structure and less use of dynamic memory. Almost everything
+// is on the stack and benefits from aligned memory as well as fixed sized
+// vectorization. We are also able to reason about temporaries and control
+// their lifetimes better.
+// 2. Use of inverse() over llt().solve(Identity).
+template <int kRowBlockSize = Eigen::Dynamic,
+ int kEBlockSize = Eigen::Dynamic,
+ int kFBlockSize = Eigen::Dynamic>
+class SchurEliminatorForOneFBlock : public SchurEliminatorBase {
+ public:
+ virtual ~SchurEliminatorForOneFBlock() {}
+ void Init(int num_eliminate_blocks,
+ bool assume_full_rank_ete,
+ const CompressedRowBlockStructure* bs) override {
+ CHECK_GT(num_eliminate_blocks, 0)
+ << "SchurComplementSolver cannot be initialized with "
+ << "num_eliminate_blocks = 0.";
+ CHECK_EQ(bs->cols.size() - num_eliminate_blocks, 1);
+
+ num_eliminate_blocks_ = num_eliminate_blocks;
+ const int num_row_blocks = bs->rows.size();
+ chunks_.clear();
+ int r = 0;
+ // Iterate over the row blocks of A, and detect the chunks. The
+ // matrix should already have been ordered so that all rows
+ // containing the same y block are vertically contiguous.
+ while (r < num_row_blocks) {
+ const int e_block_id = bs->rows[r].cells.front().block_id;
+ if (e_block_id >= num_eliminate_blocks_) {
+ break;
+ }
+
+ chunks_.push_back(Chunk());
+ Chunk& chunk = chunks_.back();
+ chunk.num_rows = 0;
+ chunk.start = r;
+ // Add to the chunk until the first block in the row is
+ // different than the one in the first row for the chunk.
+ while (r + chunk.num_rows < num_row_blocks) {
+ const CompressedRow& row = bs->rows[r + chunk.num_rows];
+ if (row.cells.front().block_id != e_block_id) {
+ break;
+ }
+ ++chunk.num_rows;
+ }
+ r += chunk.num_rows;
+ }
+
+ const Chunk& last_chunk = chunks_.back();
+ uneliminated_row_begins_ = last_chunk.start + last_chunk.num_rows;
+ e_t_e_inverse_matrices_.resize(kEBlockSize * kEBlockSize * chunks_.size());
+ std::fill(
+ e_t_e_inverse_matrices_.begin(), e_t_e_inverse_matrices_.end(), 0.0);
+ }
+
+ void Eliminate(const BlockSparseMatrixData& A,
+ const double* b,
+ const double* D,
+ BlockRandomAccessMatrix* lhs_bram,
+ double* rhs_ptr) override {
+ // Since there is only one f-block, we can call GetCell once, and cache its
+ // output.
+ int r, c, row_stride, col_stride;
+ CellInfo* cell_info =
+ lhs_bram->GetCell(0, 0, &r, &c, &row_stride, &col_stride);
+ typename EigenTypes<kFBlockSize, kFBlockSize>::MatrixRef lhs(
+ cell_info->values, kFBlockSize, kFBlockSize);
+ typename EigenTypes<kFBlockSize>::VectorRef rhs(rhs_ptr, kFBlockSize);
+
+ lhs.setZero();
+ rhs.setZero();
+
+ const CompressedRowBlockStructure* bs = A.block_structure();
+ const double* values = A.values();
+
+ // Add the diagonal to the schur complement.
+ if (D != nullptr) {
+ typename EigenTypes<kFBlockSize>::ConstVectorRef diag(
+ D + bs->cols[num_eliminate_blocks_].position, kFBlockSize);
+ lhs.diagonal() = diag.array().square().matrix();
+ }
+
+ Eigen::Matrix<double, kEBlockSize, kFBlockSize> tmp;
+ Eigen::Matrix<double, kEBlockSize, 1> tmp2;
+
+ // The following loop works on a block matrix which looks as follows
+ // (number of rows can be anything):
+ //
+ // [e_1 | f_1] = [b1]
+ // [e_2 | f_2] = [b2]
+ // [e_3 | f_3] = [b3]
+ // [e_4 | f_4] = [b4]
+ //
+ // and computes the following
+ //
+ // e_t_e = sum_i e_i^T * e_i
+ // e_t_e_inverse = inverse(e_t_e)
+ // e_t_f = sum_i e_i^T f_i
+ // e_t_b = sum_i e_i^T b_i
+ // f_t_b = sum_i f_i^T b_i
+ //
+ // lhs += sum_i f_i^T * f_i - e_t_f^T * e_t_e_inverse * e_t_f
+ // rhs += f_t_b - e_t_f^T * e_t_e_inverse * e_t_b
+ for (int i = 0; i < chunks_.size(); ++i) {
+ const Chunk& chunk = chunks_[i];
+ const int e_block_id = bs->rows[chunk.start].cells.front().block_id;
+
+ // Naming covention, e_t_e = e_block.transpose() * e_block;
+ Eigen::Matrix<double, kEBlockSize, kEBlockSize> e_t_e;
+ Eigen::Matrix<double, kEBlockSize, kFBlockSize> e_t_f;
+ Eigen::Matrix<double, kEBlockSize, 1> e_t_b;
+ Eigen::Matrix<double, kFBlockSize, 1> f_t_b;
+
+ // Add the square of the diagonal to e_t_e.
+ if (D != NULL) {
+ const typename EigenTypes<kEBlockSize>::ConstVectorRef diag(
+ D + bs->cols[e_block_id].position, kEBlockSize);
+ e_t_e = diag.array().square().matrix().asDiagonal();
+ } else {
+ e_t_e.setZero();
+ }
+ e_t_f.setZero();
+ e_t_b.setZero();
+ f_t_b.setZero();
+
+ for (int j = 0; j < chunk.num_rows; ++j) {
+ const int row_id = chunk.start + j;
+ const auto& row = bs->rows[row_id];
+ const typename EigenTypes<kRowBlockSize, kEBlockSize>::ConstMatrixRef
+ e_block(values + row.cells[0].position, kRowBlockSize, kEBlockSize);
+ const typename EigenTypes<kRowBlockSize>::ConstVectorRef b_block(
+ b + row.block.position, kRowBlockSize);
+
+ e_t_e.noalias() += e_block.transpose() * e_block;
+ e_t_b.noalias() += e_block.transpose() * b_block;
+
+ if (row.cells.size() == 1) {
+ // There is no f block, so there is nothing more to do.
+ continue;
+ }
+
+ const typename EigenTypes<kRowBlockSize, kFBlockSize>::ConstMatrixRef
+ f_block(values + row.cells[1].position, kRowBlockSize, kFBlockSize);
+ e_t_f.noalias() += e_block.transpose() * f_block;
+ lhs.noalias() += f_block.transpose() * f_block;
+ f_t_b.noalias() += f_block.transpose() * b_block;
+ }
+
+ // BackSubstitute computes the same inverse, and this is the key workload
+ // there, so caching these inverses makes BackSubstitute essentially free.
+ typename EigenTypes<kEBlockSize, kEBlockSize>::MatrixRef e_t_e_inverse(
+ &e_t_e_inverse_matrices_[kEBlockSize * kEBlockSize * i],
+ kEBlockSize,
+ kEBlockSize);
+
+ // e_t_e is a symmetric positive definite matrix, so the standard way to
+ // compute its inverse is via the Cholesky factorization by calling
+ // e_t_e.llt().solve(Identity()). However, the inverse() method even
+ // though it is not optimized for symmetric matrices is significantly
+ // faster for small fixed size (up to 4x4) matrices.
+ //
+ // https://eigen.tuxfamily.org/dox/group__TutorialLinearAlgebra.html#title3
+ e_t_e_inverse.noalias() = e_t_e.inverse();
+
+ // The use of these two pre-allocated tmp vectors saves temporaries in the
+ // expressions for lhs and rhs updates below and has a significant impact
+ // on the performance of this method.
+ tmp.noalias() = e_t_e_inverse * e_t_f;
+ tmp2.noalias() = e_t_e_inverse * e_t_b;
+
+ lhs.noalias() -= e_t_f.transpose() * tmp;
+ rhs.noalias() += f_t_b - e_t_f.transpose() * tmp2;
+ }
+
+ // The rows without any e-blocks can have arbitrary size but only contain
+ // the f-block.
+ //
+ // lhs += f_i^T f_i
+ // rhs += f_i^T b_i
+ for (int row_id = uneliminated_row_begins_; row_id < bs->rows.size();
+ ++row_id) {
+ const auto& row = bs->rows[row_id];
+ const auto& cell = row.cells[0];
+ const typename EigenTypes<Eigen::Dynamic, kFBlockSize>::ConstMatrixRef
+ f_block(values + cell.position, row.block.size, kFBlockSize);
+ const typename EigenTypes<Eigen::Dynamic>::ConstVectorRef b_block(
+ b + row.block.position, row.block.size);
+ lhs.noalias() += f_block.transpose() * f_block;
+ rhs.noalias() += f_block.transpose() * b_block;
+ }
+ }
+
+ // This implementation of BackSubstitute depends on Eliminate being called
+ // before this. SchurComplementSolver always does this.
+ //
+ // y_i = e_t_e_inverse * sum_i e_i^T * (b_i - f_i * z);
+ void BackSubstitute(const BlockSparseMatrixData& A,
+ const double* b,
+ const double* D,
+ const double* z_ptr,
+ double* y) override {
+ typename EigenTypes<kFBlockSize>::ConstVectorRef z(z_ptr, kFBlockSize);
+ const CompressedRowBlockStructure* bs = A.block_structure();
+ const double* values = A.values();
+ Eigen::Matrix<double, kEBlockSize, 1> tmp;
+ for (int i = 0; i < chunks_.size(); ++i) {
+ const Chunk& chunk = chunks_[i];
+ const int e_block_id = bs->rows[chunk.start].cells.front().block_id;
+ tmp.setZero();
+ for (int j = 0; j < chunk.num_rows; ++j) {
+ const int row_id = chunk.start + j;
+ const auto& row = bs->rows[row_id];
+ const typename EigenTypes<kRowBlockSize, kEBlockSize>::ConstMatrixRef
+ e_block(values + row.cells[0].position, kRowBlockSize, kEBlockSize);
+ const typename EigenTypes<kRowBlockSize>::ConstVectorRef b_block(
+ b + row.block.position, kRowBlockSize);
+
+ if (row.cells.size() == 1) {
+ // There is no f block.
+ tmp += e_block.transpose() * b_block;
+ } else {
+ typename EigenTypes<kRowBlockSize, kFBlockSize>::ConstMatrixRef
+ f_block(
+ values + row.cells[1].position, kRowBlockSize, kFBlockSize);
+ tmp += e_block.transpose() * (b_block - f_block * z);
+ }
+ }
+
+ typename EigenTypes<kEBlockSize, kEBlockSize>::MatrixRef e_t_e_inverse(
+ &e_t_e_inverse_matrices_[kEBlockSize * kEBlockSize * i],
+ kEBlockSize,
+ kEBlockSize);
+
+ typename EigenTypes<kEBlockSize>::VectorRef y_block(
+ y + bs->cols[e_block_id].position, kEBlockSize);
+ y_block.noalias() = e_t_e_inverse * tmp;
+ }
+ }
+
+ private:
+ struct Chunk {
+ int start = 0;
+ int num_rows = 0;
+ };
+
+ std::vector<Chunk> chunks_;
+ int num_eliminate_blocks_;
+ int uneliminated_row_begins_;
+ std::vector<double> e_t_e_inverse_matrices_;
};
} // namespace internal
diff --git a/extern/ceres/internal/ceres/schur_eliminator_impl.h b/extern/ceres/internal/ceres/schur_eliminator_impl.h
index f2535880f15..bd0881eec1e 100644
--- a/extern/ceres/internal/ceres/schur_eliminator_impl.h
+++ b/extern/ceres/internal/ceres/schur_eliminator_impl.h
@@ -48,23 +48,23 @@
// This include must come before any #ifndef check on Ceres compile options.
#include "ceres/internal/port.h"
-#ifdef CERES_USE_OPENMP
-#include <omp.h>
-#endif
-
#include <algorithm>
#include <map>
+
+#include "Eigen/Dense"
#include "ceres/block_random_access_matrix.h"
#include "ceres/block_sparse_matrix.h"
#include "ceres/block_structure.h"
#include "ceres/internal/eigen.h"
#include "ceres/internal/fixed_array.h"
-#include "ceres/internal/scoped_ptr.h"
+#include "ceres/invert_psd_matrix.h"
#include "ceres/map_util.h"
+#include "ceres/parallel_for.h"
#include "ceres/schur_eliminator.h"
+#include "ceres/scoped_thread_token.h"
#include "ceres/small_blas.h"
#include "ceres/stl_util.h"
-#include "Eigen/Dense"
+#include "ceres/thread_token_provider.h"
#include "glog/logging.h"
namespace ceres {
@@ -76,14 +76,16 @@ SchurEliminator<kRowBlockSize, kEBlockSize, kFBlockSize>::~SchurEliminator() {
}
template <int kRowBlockSize, int kEBlockSize, int kFBlockSize>
-void
-SchurEliminator<kRowBlockSize, kEBlockSize, kFBlockSize>::
-Init(int num_eliminate_blocks, const CompressedRowBlockStructure* bs) {
+void SchurEliminator<kRowBlockSize, kEBlockSize, kFBlockSize>::Init(
+ int num_eliminate_blocks,
+ bool assume_full_rank_ete,
+ const CompressedRowBlockStructure* bs) {
CHECK_GT(num_eliminate_blocks, 0)
<< "SchurComplementSolver cannot be initialized with "
<< "num_eliminate_blocks = 0.";
num_eliminate_blocks_ = num_eliminate_blocks;
+ assume_full_rank_ete_ = assume_full_rank_ete;
const int num_col_blocks = bs->cols.size();
const int num_row_blocks = bs->rows.size();
@@ -102,6 +104,13 @@ Init(int num_eliminate_blocks, const CompressedRowBlockStructure* bs) {
lhs_num_rows += bs->cols[i].size;
}
+ // TODO(sameeragarwal): Now that we may have subset block structure,
+ // we need to make sure that we account for the fact that somep
+ // point blocks only have a "diagonal" row and nothing more.
+ //
+ // This likely requires a slightly different algorithm, which works
+ // off of the number of elimination blocks.
+
int r = 0;
// Iterate over the row blocks of A, and detect the chunks. The
// matrix should already have been ordered so that all rows
@@ -143,15 +152,12 @@ Init(int num_eliminate_blocks, const CompressedRowBlockStructure* bs) {
++chunk.size;
}
- CHECK_GT(chunk.size, 0);
+ CHECK_GT(chunk.size, 0); // This check will need to be resolved.
r += chunk.size;
}
const Chunk& chunk = chunks_.back();
uneliminated_row_begins_ = chunk.start + chunk.size;
- if (num_threads_ > 1) {
- random_shuffle(chunks_.begin(), chunks_.end());
- }
buffer_.reset(new double[buffer_size_ * num_threads_]);
@@ -163,46 +169,51 @@ Init(int num_eliminate_blocks, const CompressedRowBlockStructure* bs) {
STLDeleteElements(&rhs_locks_);
rhs_locks_.resize(num_col_blocks - num_eliminate_blocks_);
for (int i = 0; i < num_col_blocks - num_eliminate_blocks_; ++i) {
- rhs_locks_[i] = new Mutex;
+ rhs_locks_[i] = new std::mutex;
}
}
template <int kRowBlockSize, int kEBlockSize, int kFBlockSize>
void
SchurEliminator<kRowBlockSize, kEBlockSize, kFBlockSize>::
-Eliminate(const BlockSparseMatrix* A,
+Eliminate(const BlockSparseMatrixData& A,
const double* b,
const double* D,
BlockRandomAccessMatrix* lhs,
double* rhs) {
if (lhs->num_rows() > 0) {
lhs->SetZero();
- VectorRef(rhs, lhs->num_rows()).setZero();
+ if (rhs) {
+ VectorRef(rhs, lhs->num_rows()).setZero();
+ }
}
- const CompressedRowBlockStructure* bs = A->block_structure();
+ const CompressedRowBlockStructure* bs = A.block_structure();
const int num_col_blocks = bs->cols.size();
// Add the diagonal to the schur complement.
if (D != NULL) {
-#pragma omp parallel for num_threads(num_threads_) schedule(dynamic)
- for (int i = num_eliminate_blocks_; i < num_col_blocks; ++i) {
- const int block_id = i - num_eliminate_blocks_;
- 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) {
- const int block_size = bs->cols[i].size;
- typename EigenTypes<Eigen::Dynamic>::ConstVectorRef
- diag(D + bs->cols[i].position, block_size);
-
- CeresMutexLock l(&cell_info->m);
- MatrixRef m(cell_info->values, row_stride, col_stride);
- m.block(r, c, block_size, block_size).diagonal()
- += diag.array().square().matrix();
- }
- }
+ ParallelFor(
+ context_,
+ num_eliminate_blocks_,
+ num_col_blocks,
+ num_threads_,
+ [&](int i) {
+ const int block_id = i - num_eliminate_blocks_;
+ 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) {
+ const int block_size = bs->cols[i].size;
+ typename EigenTypes<Eigen::Dynamic>::ConstVectorRef diag(
+ D + bs->cols[i].position, block_size);
+
+ std::lock_guard<std::mutex> l(cell_info->m);
+ MatrixRef m(cell_info->values, row_stride, col_stride);
+ m.block(r, c, block_size, block_size).diagonal() +=
+ diag.array().square().matrix();
+ }
+ });
}
// Eliminate y blocks one chunk at a time. For each chunk, compute
@@ -218,79 +229,78 @@ Eliminate(const BlockSparseMatrix* A,
// z blocks that share a row block/residual term with the y
// block. EliminateRowOuterProduct does the corresponding operation
// for the lhs of the reduced linear system.
-#pragma omp parallel for num_threads(num_threads_) schedule(dynamic)
- for (int i = 0; i < chunks_.size(); ++i) {
-#ifdef CERES_USE_OPENMP
- int thread_id = omp_get_thread_num();
-#else
- int thread_id = 0;
-#endif
- double* buffer = buffer_.get() + thread_id * buffer_size_;
- const Chunk& chunk = chunks_[i];
- const int e_block_id = bs->rows[chunk.start].cells.front().block_id;
- const int e_block_size = bs->cols[e_block_id].size;
-
- VectorRef(buffer, buffer_size_).setZero();
-
- typename EigenTypes<kEBlockSize, kEBlockSize>::Matrix
- ete(e_block_size, e_block_size);
+ ParallelFor(
+ context_,
+ 0,
+ int(chunks_.size()),
+ num_threads_,
+ [&](int thread_id, int i) {
+ double* buffer = buffer_.get() + thread_id * buffer_size_;
+ const Chunk& chunk = chunks_[i];
+ const int e_block_id = bs->rows[chunk.start].cells.front().block_id;
+ const int e_block_size = bs->cols[e_block_id].size;
+
+ VectorRef(buffer, buffer_size_).setZero();
+
+ typename EigenTypes<kEBlockSize, kEBlockSize>::Matrix
+ ete(e_block_size, e_block_size);
+
+ if (D != NULL) {
+ const typename EigenTypes<kEBlockSize>::ConstVectorRef
+ diag(D + bs->cols[e_block_id].position, e_block_size);
+ ete = diag.array().square().matrix().asDiagonal();
+ } else {
+ ete.setZero();
+ }
- if (D != NULL) {
- const typename EigenTypes<kEBlockSize>::ConstVectorRef
- diag(D + bs->cols[e_block_id].position, e_block_size);
- ete = diag.array().square().matrix().asDiagonal();
- } else {
- ete.setZero();
- }
+ FixedArray<double, 8> g(e_block_size);
+ typename EigenTypes<kEBlockSize>::VectorRef gref(g.data(),
+ e_block_size);
+ gref.setZero();
+
+ // We are going to be computing
+ //
+ // S += F'F - F'E(E'E)^{-1}E'F
+ //
+ // for each Chunk. The computation is broken down into a number of
+ // function calls as below.
+
+ // Compute the outer product of the e_blocks with themselves (ete
+ // = E'E). Compute the product of the e_blocks with the
+ // corresponding f_blocks (buffer = E'F), the gradient of the terms
+ // in this chunk (g) and add the outer product of the f_blocks to
+ // Schur complement (S += F'F).
+ ChunkDiagonalBlockAndGradient(
+ chunk, A, b, chunk.start, &ete, g.data(), buffer, lhs);
+
+ // Normally one wouldn't compute the inverse explicitly, but
+ // e_block_size will typically be a small number like 3, in
+ // which case its much faster to compute the inverse once and
+ // use it to multiply other matrices/vectors instead of doing a
+ // Solve call over and over again.
+ typename EigenTypes<kEBlockSize, kEBlockSize>::Matrix inverse_ete =
+ InvertPSDMatrix<kEBlockSize>(assume_full_rank_ete_, ete);
+
+ // For the current chunk compute and update the rhs of the reduced
+ // linear system.
+ //
+ // rhs = F'b - F'E(E'E)^(-1) E'b
+
+ if (rhs) {
+ FixedArray<double, 8> inverse_ete_g(e_block_size);
+ MatrixVectorMultiply<kEBlockSize, kEBlockSize, 0>(
+ inverse_ete.data(),
+ e_block_size,
+ e_block_size,
+ g.data(),
+ inverse_ete_g.data());
+ UpdateRhs(chunk, A, b, chunk.start, inverse_ete_g.data(), rhs);
+ }
- FixedArray<double, 8> g(e_block_size);
- typename EigenTypes<kEBlockSize>::VectorRef gref(g.get(), e_block_size);
- gref.setZero();
-
- // We are going to be computing
- //
- // S += F'F - F'E(E'E)^{-1}E'F
- //
- // for each Chunk. The computation is broken down into a number of
- // function calls as below.
-
- // Compute the outer product of the e_blocks with themselves (ete
- // = E'E). Compute the product of the e_blocks with the
- // corresonding f_blocks (buffer = E'F), the gradient of the terms
- // in this chunk (g) and add the outer product of the f_blocks to
- // Schur complement (S += F'F).
- ChunkDiagonalBlockAndGradient(
- chunk, A, b, chunk.start, &ete, g.get(), buffer, lhs);
-
- // Normally one wouldn't compute the inverse explicitly, but
- // e_block_size will typically be a small number like 3, in
- // which case its much faster to compute the inverse once and
- // use it to multiply other matrices/vectors instead of doing a
- // Solve call over and over again.
- typename EigenTypes<kEBlockSize, kEBlockSize>::Matrix inverse_ete =
- ete
- .template selfadjointView<Eigen::Upper>()
- .llt()
- .solve(Matrix::Identity(e_block_size, e_block_size));
-
- // For the current chunk compute and update the rhs of the reduced
- // linear system.
- //
- // rhs = F'b - F'E(E'E)^(-1) E'b
-
- FixedArray<double, 8> inverse_ete_g(e_block_size);
- MatrixVectorMultiply<kEBlockSize, kEBlockSize, 0>(
- inverse_ete.data(),
- e_block_size,
- e_block_size,
- g.get(),
- inverse_ete_g.get());
-
- UpdateRhs(chunk, A, b, chunk.start, inverse_ete_g.get(), rhs);
-
- // S -= F'E(E'E)^{-1}E'F
- ChunkOuterProduct(bs, inverse_ete, buffer, chunk.buffer_layout, lhs);
- }
+ // S -= F'E(E'E)^{-1}E'F
+ ChunkOuterProduct(
+ thread_id, bs, inverse_ete, buffer, chunk.buffer_layout, lhs);
+ });
// For rows with no e_blocks, the schur complement update reduces to
// S += F'F.
@@ -300,19 +310,25 @@ Eliminate(const BlockSparseMatrix* A,
template <int kRowBlockSize, int kEBlockSize, int kFBlockSize>
void
SchurEliminator<kRowBlockSize, kEBlockSize, kFBlockSize>::
-BackSubstitute(const BlockSparseMatrix* A,
+BackSubstitute(const BlockSparseMatrixData& A,
const double* b,
const double* D,
const double* z,
double* y) {
- const CompressedRowBlockStructure* bs = A->block_structure();
-#pragma omp parallel for num_threads(num_threads_) schedule(dynamic)
- for (int i = 0; i < chunks_.size(); ++i) {
+ const CompressedRowBlockStructure* bs = A.block_structure();
+ const double* values = A.values();
+
+ ParallelFor(
+ context_,
+ 0,
+ int(chunks_.size()),
+ num_threads_,
+ [&](int i) {
const Chunk& chunk = chunks_[i];
const int e_block_id = bs->rows[chunk.start].cells.front().block_id;
const int e_block_size = bs->cols[e_block_id].size;
- double* y_ptr = y + bs->cols[e_block_id].position;
+ double* y_ptr = y + bs->cols[e_block_id].position;
typename EigenTypes<kEBlockSize>::VectorRef y_block(y_ptr, e_block_size);
typename EigenTypes<kEBlockSize, kEBlockSize>::Matrix
@@ -325,7 +341,6 @@ BackSubstitute(const BlockSparseMatrix* A,
ete.setZero();
}
- const double* values = A->values();
for (int j = 0; j < chunk.size; ++j) {
const CompressedRow& row = bs->rows[chunk.start + j];
const Cell& e_cell = row.cells.front();
@@ -333,9 +348,9 @@ BackSubstitute(const BlockSparseMatrix* A,
FixedArray<double, 8> sj(row.block.size);
- typename EigenTypes<kRowBlockSize>::VectorRef(sj.get(), row.block.size) =
- typename EigenTypes<kRowBlockSize>::ConstVectorRef
- (b + bs->rows[chunk.start + j].block.position, row.block.size);
+ typename EigenTypes<kRowBlockSize>::VectorRef(sj.data(), row.block.size) =
+ typename EigenTypes<kRowBlockSize>::ConstVectorRef(
+ b + bs->rows[chunk.start + j].block.position, row.block.size);
for (int c = 1; c < row.cells.size(); ++c) {
const int f_block_id = row.cells[c].block_id;
@@ -345,23 +360,24 @@ BackSubstitute(const BlockSparseMatrix* A,
MatrixVectorMultiply<kRowBlockSize, kFBlockSize, -1>(
values + row.cells[c].position, row.block.size, f_block_size,
z + lhs_row_layout_[r_block],
- sj.get());
+ sj.data());
}
MatrixTransposeVectorMultiply<kRowBlockSize, kEBlockSize, 1>(
values + e_cell.position, row.block.size, e_block_size,
- sj.get(),
+ sj.data(),
y_ptr);
MatrixTransposeMatrixMultiply
<kRowBlockSize, kEBlockSize, kRowBlockSize, kEBlockSize, 1>(
- values + e_cell.position, row.block.size, e_block_size,
- values + e_cell.position, row.block.size, e_block_size,
- ete.data(), 0, 0, e_block_size, e_block_size);
+ values + e_cell.position, row.block.size, e_block_size,
+ values + e_cell.position, row.block.size, e_block_size,
+ ete.data(), 0, 0, e_block_size, e_block_size);
}
- ete.llt().solveInPlace(y_block);
- }
+ y_block =
+ InvertPSDMatrix<kEBlockSize>(assume_full_rank_ete_, ete) * y_block;
+ });
}
// Update the rhs of the reduced linear system. Compute
@@ -371,17 +387,17 @@ template <int kRowBlockSize, int kEBlockSize, int kFBlockSize>
void
SchurEliminator<kRowBlockSize, kEBlockSize, kFBlockSize>::
UpdateRhs(const Chunk& chunk,
- const BlockSparseMatrix* A,
+ const BlockSparseMatrixData& A,
const double* b,
int row_block_counter,
const double* inverse_ete_g,
double* rhs) {
- const CompressedRowBlockStructure* bs = A->block_structure();
+ const CompressedRowBlockStructure* bs = A.block_structure();
+ const double* values = A.values();
+
const int e_block_id = bs->rows[chunk.start].cells.front().block_id;
const int e_block_size = bs->cols[e_block_id].size;
-
int b_pos = bs->rows[row_block_counter].block.position;
- const double* values = A->values();
for (int j = 0; j < chunk.size; ++j) {
const CompressedRow& row = bs->rows[row_block_counter + j];
const Cell& e_cell = row.cells.front();
@@ -398,7 +414,7 @@ UpdateRhs(const Chunk& chunk,
const int block_id = row.cells[c].block_id;
const int block_size = bs->cols[block_id].size;
const int block = block_id - num_eliminate_blocks_;
- CeresMutexLock l(rhs_locks_[block]);
+ std::lock_guard<std::mutex> l(*rhs_locks_[block]);
MatrixTransposeVectorMultiply<kRowBlockSize, kFBlockSize, 1>(
values + row.cells[c].position,
row.block.size, block_size,
@@ -432,14 +448,15 @@ void
SchurEliminator<kRowBlockSize, kEBlockSize, kFBlockSize>::
ChunkDiagonalBlockAndGradient(
const Chunk& chunk,
- const BlockSparseMatrix* A,
+ const BlockSparseMatrixData& A,
const double* b,
int row_block_counter,
typename EigenTypes<kEBlockSize, kEBlockSize>::Matrix* ete,
double* g,
double* buffer,
BlockRandomAccessMatrix* lhs) {
- const CompressedRowBlockStructure* bs = A->block_structure();
+ const CompressedRowBlockStructure* bs = A.block_structure();
+ const double* values = A.values();
int b_pos = bs->rows[row_block_counter].block.position;
const int e_block_size = ete->rows();
@@ -448,7 +465,6 @@ ChunkDiagonalBlockAndGradient(
// contribution of its F blocks to the Schur complement, the
// contribution of its E block to the matrix EE' (ete), and the
// corresponding block in the gradient vector.
- const double* values = A->values();
for (int j = 0; j < chunk.size; ++j) {
const CompressedRow& row = bs->rows[row_block_counter + j];
@@ -464,12 +480,13 @@ ChunkDiagonalBlockAndGradient(
values + e_cell.position, row.block.size, e_block_size,
ete->data(), 0, 0, e_block_size, e_block_size);
- // g += E_i' b_i
- MatrixTransposeVectorMultiply<kRowBlockSize, kEBlockSize, 1>(
- values + e_cell.position, row.block.size, e_block_size,
- b + b_pos,
- g);
-
+ if (b) {
+ // g += E_i' b_i
+ MatrixTransposeVectorMultiply<kRowBlockSize, kEBlockSize, 1>(
+ values + e_cell.position, row.block.size, e_block_size,
+ b + b_pos,
+ g);
+ }
// buffer = E'F. This computation is done by iterating over the
// f_blocks for each row in the chunk.
@@ -495,7 +512,8 @@ ChunkDiagonalBlockAndGradient(
template <int kRowBlockSize, int kEBlockSize, int kFBlockSize>
void
SchurEliminator<kRowBlockSize, kEBlockSize, kFBlockSize>::
-ChunkOuterProduct(const CompressedRowBlockStructure* bs,
+ChunkOuterProduct(int thread_id,
+ const CompressedRowBlockStructure* bs,
const Matrix& inverse_ete,
const double* buffer,
const BufferLayoutType& buffer_layout,
@@ -507,11 +525,6 @@ ChunkOuterProduct(const CompressedRowBlockStructure* bs,
const int e_block_size = inverse_ete.rows();
BufferLayoutType::const_iterator it1 = buffer_layout.begin();
-#ifdef CERES_USE_OPENMP
- int thread_id = omp_get_thread_num();
-#else
- int thread_id = 0;
-#endif
double* b1_transpose_inverse_ete =
chunk_outer_product_buffer_.get() + thread_id * buffer_size_;
@@ -535,7 +548,7 @@ ChunkOuterProduct(const CompressedRowBlockStructure* bs,
&row_stride, &col_stride);
if (cell_info != NULL) {
const int block2_size = bs->cols[it2->first].size;
- CeresMutexLock l(&cell_info->m);
+ std::lock_guard<std::mutex> l(cell_info->m);
MatrixMatrixMultiply
<kFBlockSize, kEBlockSize, kEBlockSize, kFBlockSize, -1>(
b1_transpose_inverse_ete, block1_size, e_block_size,
@@ -552,14 +565,18 @@ ChunkOuterProduct(const CompressedRowBlockStructure* bs,
template <int kRowBlockSize, int kEBlockSize, int kFBlockSize>
void
SchurEliminator<kRowBlockSize, kEBlockSize, kFBlockSize>::
-NoEBlockRowsUpdate(const BlockSparseMatrix* A,
+NoEBlockRowsUpdate(const BlockSparseMatrixData& A,
const double* b,
int row_block_counter,
BlockRandomAccessMatrix* lhs,
double* rhs) {
- const CompressedRowBlockStructure* bs = A->block_structure();
- const double* values = A->values();
+ const CompressedRowBlockStructure* bs = A.block_structure();
+ const double* values = A.values();
for (; row_block_counter < bs->rows.size(); ++row_block_counter) {
+ NoEBlockRowOuterProduct(A, row_block_counter, lhs);
+ if (!rhs) {
+ continue;
+ }
const CompressedRow& row = bs->rows[row_block_counter];
for (int c = 0; c < row.cells.size(); ++c) {
const int block_id = row.cells[c].block_id;
@@ -570,7 +587,6 @@ NoEBlockRowsUpdate(const BlockSparseMatrix* A,
b + row.block.position,
rhs + lhs_row_layout_[block]);
}
- NoEBlockRowOuterProduct(A, row_block_counter, lhs);
}
}
@@ -582,7 +598,7 @@ NoEBlockRowsUpdate(const BlockSparseMatrix* A,
// one difference. It does not use any of the template
// parameters. This is because the algorithm used for detecting the
// static structure of the matrix A only pays attention to rows with
-// e_blocks. This is becase rows without e_blocks are rare and
+// e_blocks. This is because rows without e_blocks are rare and
// typically arise from regularization terms in the original
// optimization problem, and have a very different structure than the
// rows with e_blocks. Including them in the static structure
@@ -592,12 +608,13 @@ NoEBlockRowsUpdate(const BlockSparseMatrix* A,
template <int kRowBlockSize, int kEBlockSize, int kFBlockSize>
void
SchurEliminator<kRowBlockSize, kEBlockSize, kFBlockSize>::
-NoEBlockRowOuterProduct(const BlockSparseMatrix* A,
+NoEBlockRowOuterProduct(const BlockSparseMatrixData& A,
int row_block_index,
BlockRandomAccessMatrix* lhs) {
- const CompressedRowBlockStructure* bs = A->block_structure();
+ const CompressedRowBlockStructure* bs = A.block_structure();
+ const double* values = A.values();
+
const CompressedRow& row = bs->rows[row_block_index];
- const double* values = A->values();
for (int i = 0; i < row.cells.size(); ++i) {
const int block1 = row.cells[i].block_id - num_eliminate_blocks_;
DCHECK_GE(block1, 0);
@@ -608,7 +625,7 @@ NoEBlockRowOuterProduct(const BlockSparseMatrix* A,
&r, &c,
&row_stride, &col_stride);
if (cell_info != NULL) {
- CeresMutexLock l(&cell_info->m);
+ std::lock_guard<std::mutex> l(cell_info->m);
// This multiply currently ignores the fact that this is a
// symmetric outer product.
MatrixTransposeMatrixMultiply
@@ -628,7 +645,7 @@ NoEBlockRowOuterProduct(const BlockSparseMatrix* A,
&row_stride, &col_stride);
if (cell_info != NULL) {
const int block2_size = bs->cols[row.cells[j].block_id].size;
- CeresMutexLock l(&cell_info->m);
+ std::lock_guard<std::mutex> l(cell_info->m);
MatrixTransposeMatrixMultiply
<Eigen::Dynamic, Eigen::Dynamic, Eigen::Dynamic, Eigen::Dynamic, 1>(
values + row.cells[i].position, row.block.size, block1_size,
@@ -639,18 +656,19 @@ NoEBlockRowOuterProduct(const BlockSparseMatrix* A,
}
}
-// For a row with an e_block, compute the contribition S += F'F. This
+// For a row with an e_block, compute the contribution S += F'F. This
// function has the same structure as NoEBlockRowOuterProduct, except
// that this function uses the template parameters.
template <int kRowBlockSize, int kEBlockSize, int kFBlockSize>
void
SchurEliminator<kRowBlockSize, kEBlockSize, kFBlockSize>::
-EBlockRowOuterProduct(const BlockSparseMatrix* A,
+EBlockRowOuterProduct(const BlockSparseMatrixData& A,
int row_block_index,
BlockRandomAccessMatrix* lhs) {
- const CompressedRowBlockStructure* bs = A->block_structure();
+ const CompressedRowBlockStructure* bs = A.block_structure();
+ const double* values = A.values();
+
const CompressedRow& row = bs->rows[row_block_index];
- const double* values = A->values();
for (int i = 1; i < row.cells.size(); ++i) {
const int block1 = row.cells[i].block_id - num_eliminate_blocks_;
DCHECK_GE(block1, 0);
@@ -661,7 +679,7 @@ EBlockRowOuterProduct(const BlockSparseMatrix* A,
&r, &c,
&row_stride, &col_stride);
if (cell_info != NULL) {
- CeresMutexLock l(&cell_info->m);
+ std::lock_guard<std::mutex> l(cell_info->m);
// block += b1.transpose() * b1;
MatrixTransposeMatrixMultiply
<kRowBlockSize, kFBlockSize, kRowBlockSize, kFBlockSize, 1>(
@@ -681,7 +699,7 @@ EBlockRowOuterProduct(const BlockSparseMatrix* A,
&row_stride, &col_stride);
if (cell_info != NULL) {
// block += b1.transpose() * b2;
- CeresMutexLock l(&cell_info->m);
+ std::lock_guard<std::mutex> l(cell_info->m);
MatrixTransposeMatrixMultiply
<kRowBlockSize, kFBlockSize, kRowBlockSize, kFBlockSize, 1>(
values + row.cells[i].position, row.block.size, block1_size,
diff --git a/extern/ceres/internal/ceres/generate_eliminator_specialization.py b/extern/ceres/internal/ceres/schur_eliminator_template.py
index e89e7a48c98..2f38cf5ad8f 100644
--- a/extern/ceres/internal/ceres/generate_eliminator_specialization.py
+++ b/extern/ceres/internal/ceres/schur_eliminator_template.py
@@ -1,5 +1,5 @@
# Ceres Solver - A fast non-linear least squares minimizer
-# Copyright 2015 Google Inc. All rights reserved.
+# Copyright 2017 Google Inc. All rights reserved.
# http://ceres-solver.org/
#
# Redistribution and use in source and binary forms, with or without
@@ -49,28 +49,9 @@
# specializations that is generated.
# Set of template specializations to generate
-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, 8),
- (2, 4, 9),
- (2, 4, "Eigen::Dynamic"),
- (2, "Eigen::Dynamic", "Eigen::Dynamic"),
- (4, 4, 2),
- (4, 4, 3),
- (4, 4, 4),
- (4, 4, "Eigen::Dynamic"),
- ("Eigen::Dynamic", "Eigen::Dynamic", "Eigen::Dynamic")]
+
HEADER = """// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -108,8 +89,7 @@ HEADER = """// Ceres Solver - A fast non-linear least squares minimizer
// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
//=========================================
//
-// This file is generated using generate_eliminator_specialization.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
"""
DYNAMIC_FILE = """
@@ -159,12 +139,7 @@ SchurEliminatorBase::Create(const LinearSolver::Options& options) {
#ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION
"""
-FACTORY_CONDITIONAL = """ if ((options.row_block_size == %s) &&
- (options.e_block_size == %s) &&
- (options.f_block_size == %s)) {
- return new SchurEliminator<%s, %s, %s>(options);
- }
-"""
+FACTORY = """ return new SchurEliminator<%s, %s, %s>(options);"""
FACTORY_FOOTER = """
#endif
@@ -178,54 +153,3 @@ FACTORY_FOOTER = """
} // namespace internal
} // namespace ceres
"""
-
-
-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 Specialize():
- """
- Generate specialization code and the conditionals to instantiate it.
- """
- f = open("schur_eliminator.cc", "w")
- f.write(HEADER)
- f.write(FACTORY_FILE_HEADER)
-
- for row_block_size, e_block_size, f_block_size in SPECIALIZATIONS:
- output = SpecializationFilename("generated/schur_eliminator",
- row_block_size,
- e_block_size,
- f_block_size) + ".cc"
- fptr = open(output, "w")
- fptr.write(HEADER)
-
- template = SPECIALIZATION_FILE
- if (row_block_size == "Eigen::Dynamic" and
- e_block_size == "Eigen::Dynamic" and
- f_block_size == "Eigen::Dynamic"):
- template = DYNAMIC_FILE
-
- fptr.write(template % (row_block_size, e_block_size, f_block_size))
- fptr.close()
-
- f.write(FACTORY_CONDITIONAL % (row_block_size,
- e_block_size,
- f_block_size,
- row_block_size,
- e_block_size,
- f_block_size))
- f.write(FACTORY_FOOTER)
- f.close()
-
-
-if __name__ == "__main__":
- Specialize()
diff --git a/extern/ceres/internal/ceres/schur_jacobi_preconditioner.cc b/extern/ceres/internal/ceres/schur_jacobi_preconditioner.cc
index 3e6cc90f63c..89d770b405a 100644
--- a/extern/ceres/internal/ceres/schur_jacobi_preconditioner.cc
+++ b/extern/ceres/internal/ceres/schur_jacobi_preconditioner.cc
@@ -32,10 +32,9 @@
#include <utility>
#include <vector>
+
#include "ceres/block_random_access_diagonal_matrix.h"
#include "ceres/block_sparse_matrix.h"
-#include "ceres/collections_port.h"
-#include "ceres/internal/scoped_ptr.h"
#include "ceres/linear_solver.h"
#include "ceres/schur_eliminator.h"
#include "glog/logging.h"
@@ -50,9 +49,9 @@ SchurJacobiPreconditioner::SchurJacobiPreconditioner(
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 atleast 1 f_block for "
- << "SCHUR_JACOBI preconditioner.";
+ CHECK_GT(num_blocks, 0) << "Jacobian should have at least 1 f_block for "
+ << "SCHUR_JACOBI preconditioner.";
+ CHECK(options_.context != NULL);
std::vector<int> blocks(num_blocks);
for (int i = 0; i < num_blocks; ++i) {
@@ -63,8 +62,7 @@ SchurJacobiPreconditioner::SchurJacobiPreconditioner(
InitEliminator(bs);
}
-SchurJacobiPreconditioner::~SchurJacobiPreconditioner() {
-}
+SchurJacobiPreconditioner::~SchurJacobiPreconditioner() {}
// Initialize the SchurEliminator.
void SchurJacobiPreconditioner::InitEliminator(
@@ -75,8 +73,11 @@ void SchurJacobiPreconditioner::InitEliminator(
eliminator_options.e_block_size = options_.e_block_size;
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_->Init(eliminator_options.elimination_groups[0], &bs);
+ const bool kFullRankETE = true;
+ eliminator_->Init(
+ eliminator_options.elimination_groups[0], kFullRankETE, &bs);
}
// Update the values of the preconditioner matrix and factorize it.
@@ -85,19 +86,9 @@ bool SchurJacobiPreconditioner::UpdateImpl(const BlockSparseMatrix& A,
const int num_rows = m_->num_rows();
CHECK_GT(num_rows, 0);
- // We need a dummy rhs vector and a dummy b vector since the Schur
- // eliminator combines the computation of the reduced camera matrix
- // with the computation of the right hand side of that linear
- // system.
- //
- // TODO(sameeragarwal): Perhaps its worth refactoring the
- // SchurEliminator::Eliminate function to allow NULL for the rhs. As
- // of now it does not seem to be worth the effort.
- Vector rhs = Vector::Zero(m_->num_rows());
- Vector b = Vector::Zero(A.num_rows());
-
// Compute a subset of the entries of the Schur complement.
- eliminator_->Eliminate(&A, b.data(), D, m_.get(), rhs.data());
+ eliminator_->Eliminate(
+ BlockSparseMatrixData(A), nullptr, D, m_.get(), nullptr);
m_->Invert();
return true;
}
@@ -107,9 +98,7 @@ void SchurJacobiPreconditioner::RightMultiply(const double* x,
m_->RightMultiply(x, y);
}
-int SchurJacobiPreconditioner::num_rows() const {
- return m_->num_rows();
-}
+int SchurJacobiPreconditioner::num_rows() const { return m_->num_rows(); }
} // namespace internal
} // namespace ceres
diff --git a/extern/ceres/internal/ceres/schur_jacobi_preconditioner.h b/extern/ceres/internal/ceres/schur_jacobi_preconditioner.h
index 5398f3ff35d..372b790b82f 100644
--- a/extern/ceres/internal/ceres/schur_jacobi_preconditioner.h
+++ b/extern/ceres/internal/ceres/schur_jacobi_preconditioner.h
@@ -38,12 +38,11 @@
#ifndef CERES_INTERNAL_SCHUR_JACOBI_PRECONDITIONER_H_
#define CERES_INTERNAL_SCHUR_JACOBI_PRECONDITIONER_H_
+#include <memory>
#include <set>
-#include <vector>
#include <utility>
-#include "ceres/collections_port.h"
-#include "ceres/internal/macros.h"
-#include "ceres/internal/scoped_ptr.h"
+#include <vector>
+
#include "ceres/preconditioner.h"
namespace ceres {
@@ -83,21 +82,23 @@ class SchurJacobiPreconditioner : public BlockSparseMatrixPreconditioner {
// based solvers. Please see schur_eliminator.h for more details.
SchurJacobiPreconditioner(const CompressedRowBlockStructure& bs,
const Preconditioner::Options& options);
+ SchurJacobiPreconditioner(const SchurJacobiPreconditioner&) = delete;
+ void operator=(const SchurJacobiPreconditioner&) = delete;
+
virtual ~SchurJacobiPreconditioner();
// Preconditioner interface.
- virtual void RightMultiply(const double* x, double* y) const;
- virtual int num_rows() const;
+ void RightMultiply(const double* x, double* y) const final;
+ int num_rows() const final;
private:
void InitEliminator(const CompressedRowBlockStructure& bs);
- virtual bool UpdateImpl(const BlockSparseMatrix& A, const double* D);
+ bool UpdateImpl(const BlockSparseMatrix& A, const double* D) final;
Preconditioner::Options options_;
- scoped_ptr<SchurEliminatorBase> eliminator_;
+ std::unique_ptr<SchurEliminatorBase> eliminator_;
// Preconditioner matrix.
- scoped_ptr<BlockRandomAccessDiagonalMatrix> m_;
- CERES_DISALLOW_COPY_AND_ASSIGN(SchurJacobiPreconditioner);
+ std::unique_ptr<BlockRandomAccessDiagonalMatrix> m_;
};
} // namespace internal
diff --git a/extern/ceres/internal/ceres/schur_templates.cc b/extern/ceres/internal/ceres/schur_templates.cc
new file mode 100644
index 00000000000..01528619b1b
--- /dev/null
+++ b/extern/ceres/internal/ceres/schur_templates.cc
@@ -0,0 +1,227 @@
+// 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.
+
+#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
+ if ((options.row_block_size == 2) &&
+ (options.e_block_size == 2) &&
+ (options.f_block_size == 2)) {
+ *row_block_size = 2;
+ *e_block_size = 2;
+ *f_block_size = 2;
+ return;
+ }
+ if ((options.row_block_size == 2) &&
+ (options.e_block_size == 2) &&
+ (options.f_block_size == 3)) {
+ *row_block_size = 2;
+ *e_block_size = 2;
+ *f_block_size = 3;
+ return;
+ }
+ if ((options.row_block_size == 2) &&
+ (options.e_block_size == 2) &&
+ (options.f_block_size == 4)) {
+ *row_block_size = 2;
+ *e_block_size = 2;
+ *f_block_size = 4;
+ return;
+ }
+ if ((options.row_block_size == 2) &&
+ (options.e_block_size == 2)) {
+ *row_block_size = 2;
+ *e_block_size = 2;
+ *f_block_size = Eigen::Dynamic;
+ return;
+ }
+ if ((options.row_block_size == 2) &&
+ (options.e_block_size == 3) &&
+ (options.f_block_size == 3)) {
+ *row_block_size = 2;
+ *e_block_size = 3;
+ *f_block_size = 3;
+ return;
+ }
+ if ((options.row_block_size == 2) &&
+ (options.e_block_size == 3) &&
+ (options.f_block_size == 4)) {
+ *row_block_size = 2;
+ *e_block_size = 3;
+ *f_block_size = 4;
+ return;
+ }
+ if ((options.row_block_size == 2) &&
+ (options.e_block_size == 3) &&
+ (options.f_block_size == 6)) {
+ *row_block_size = 2;
+ *e_block_size = 3;
+ *f_block_size = 6;
+ return;
+ }
+ if ((options.row_block_size == 2) &&
+ (options.e_block_size == 3) &&
+ (options.f_block_size == 9)) {
+ *row_block_size = 2;
+ *e_block_size = 3;
+ *f_block_size = 9;
+ return;
+ }
+ if ((options.row_block_size == 2) &&
+ (options.e_block_size == 3)) {
+ *row_block_size = 2;
+ *e_block_size = 3;
+ *f_block_size = Eigen::Dynamic;
+ return;
+ }
+ if ((options.row_block_size == 2) &&
+ (options.e_block_size == 4) &&
+ (options.f_block_size == 3)) {
+ *row_block_size = 2;
+ *e_block_size = 4;
+ *f_block_size = 3;
+ return;
+ }
+ if ((options.row_block_size == 2) &&
+ (options.e_block_size == 4) &&
+ (options.f_block_size == 4)) {
+ *row_block_size = 2;
+ *e_block_size = 4;
+ *f_block_size = 4;
+ return;
+ }
+ if ((options.row_block_size == 2) &&
+ (options.e_block_size == 4) &&
+ (options.f_block_size == 6)) {
+ *row_block_size = 2;
+ *e_block_size = 4;
+ *f_block_size = 6;
+ return;
+ }
+ if ((options.row_block_size == 2) &&
+ (options.e_block_size == 4) &&
+ (options.f_block_size == 8)) {
+ *row_block_size = 2;
+ *e_block_size = 4;
+ *f_block_size = 8;
+ return;
+ }
+ if ((options.row_block_size == 2) &&
+ (options.e_block_size == 4) &&
+ (options.f_block_size == 9)) {
+ *row_block_size = 2;
+ *e_block_size = 4;
+ *f_block_size = 9;
+ return;
+ }
+ if ((options.row_block_size == 2) &&
+ (options.e_block_size == 4)) {
+ *row_block_size = 2;
+ *e_block_size = 4;
+ *f_block_size = Eigen::Dynamic;
+ return;
+ }
+ if (options.row_block_size == 2){
+ *row_block_size = 2;
+ *e_block_size = Eigen::Dynamic;
+ *f_block_size = Eigen::Dynamic;
+ return;
+ }
+ if ((options.row_block_size == 3) &&
+ (options.e_block_size == 3) &&
+ (options.f_block_size == 3)) {
+ *row_block_size = 3;
+ *e_block_size = 3;
+ *f_block_size = 3;
+ return;
+ }
+ if ((options.row_block_size == 4) &&
+ (options.e_block_size == 4) &&
+ (options.f_block_size == 2)) {
+ *row_block_size = 4;
+ *e_block_size = 4;
+ *f_block_size = 2;
+ return;
+ }
+ if ((options.row_block_size == 4) &&
+ (options.e_block_size == 4) &&
+ (options.f_block_size == 3)) {
+ *row_block_size = 4;
+ *e_block_size = 4;
+ *f_block_size = 3;
+ return;
+ }
+ if ((options.row_block_size == 4) &&
+ (options.e_block_size == 4) &&
+ (options.f_block_size == 4)) {
+ *row_block_size = 4;
+ *e_block_size = 4;
+ *f_block_size = 4;
+ return;
+ }
+ if ((options.row_block_size == 4) &&
+ (options.e_block_size == 4)) {
+ *row_block_size = 4;
+ *e_block_size = 4;
+ *f_block_size = Eigen::Dynamic;
+ return;
+ }
+
+#endif
+ return;
+}
+
+} // namespace internal
+} // namespace ceres
diff --git a/extern/ceres/internal/ceres/schur_templates.h b/extern/ceres/internal/ceres/schur_templates.h
new file mode 100644
index 00000000000..90aee0a1afc
--- /dev/null
+++ b/extern/ceres/internal/ceres/schur_templates.h
@@ -0,0 +1,46 @@
+// 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)
+//
+
+#ifndef CERES_INTERNAL_SCHUR_TEMPLATES_H_
+#define CERES_INTERNAL_SCHUR_TEMPLATES_H_
+
+#include "ceres/linear_solver.h"
+
+namespace ceres {
+namespace internal {
+
+void GetBestSchurTemplateSpecialization(int* row_block_size,
+ int* e_block_size,
+ int* f_block_size);
+} // namespace internal
+} // namespace ceres
+
+#endif // CERES_INTERNAL_SCHUR_TEMPLATES_H_
diff --git a/extern/ceres/internal/ceres/scoped_thread_token.h b/extern/ceres/internal/ceres/scoped_thread_token.h
new file mode 100644
index 00000000000..c167397cce9
--- /dev/null
+++ b/extern/ceres/internal/ceres/scoped_thread_token.h
@@ -0,0 +1,61 @@
+// 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: yp@photonscore.de (Yury Prokazov)
+
+#ifndef CERES_INTERNAL_SCOPED_THREAD_TOKEN_H_
+#define CERES_INTERNAL_SCOPED_THREAD_TOKEN_H_
+
+#include "ceres/thread_token_provider.h"
+
+namespace ceres {
+namespace internal {
+
+// Helper class for ThreadTokenProvider. This object acquires a token in its
+// constructor and puts that token back with destruction.
+class ScopedThreadToken {
+ public:
+ ScopedThreadToken(ThreadTokenProvider* provider)
+ : provider_(provider), token_(provider->Acquire()) {}
+
+ ~ScopedThreadToken() { provider_->Release(token_); }
+
+ int token() const { return token_; }
+
+ private:
+ ThreadTokenProvider* provider_;
+ int token_;
+
+ ScopedThreadToken(ScopedThreadToken&);
+ ScopedThreadToken& operator=(ScopedThreadToken&);
+};
+
+} // namespace internal
+} // namespace ceres
+
+#endif // CERES_INTERNAL_SCOPED_THREAD_TOKEN_H_
diff --git a/extern/ceres/internal/ceres/scratch_evaluate_preparer.h b/extern/ceres/internal/ceres/scratch_evaluate_preparer.h
index fa9ebd0e50e..c8d9b937b47 100644
--- a/extern/ceres/internal/ceres/scratch_evaluate_preparer.h
+++ b/extern/ceres/internal/ceres/scratch_evaluate_preparer.h
@@ -35,7 +35,7 @@
#ifndef CERES_INTERNAL_SCRATCH_EVALUATE_PREPARER_H_
#define CERES_INTERNAL_SCRATCH_EVALUATE_PREPARER_H_
-#include "ceres/internal/scoped_ptr.h"
+#include <memory>
namespace ceres {
namespace internal {
@@ -60,7 +60,7 @@ class ScratchEvaluatePreparer {
private:
// Scratch space for the jacobians; each jacobian is packed one after another.
// There is enough scratch to hold all the jacobians for the largest residual.
- scoped_array<double> jacobian_scratch_;
+ std::unique_ptr<double[]> jacobian_scratch_;
};
} // namespace internal
diff --git a/extern/ceres/internal/ceres/single_linkage_clustering.cc b/extern/ceres/internal/ceres/single_linkage_clustering.cc
new file mode 100644
index 00000000000..394492cdf23
--- /dev/null
+++ b/extern/ceres/internal/ceres/single_linkage_clustering.cc
@@ -0,0 +1,94 @@
+// 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/single_linkage_clustering.h"
+
+#include <unordered_set>
+#include <unordered_map>
+#include "ceres/graph.h"
+#include "ceres/graph_algorithms.h"
+
+namespace ceres {
+namespace internal {
+
+int ComputeSingleLinkageClustering(
+ const SingleLinkageClusteringOptions& options,
+ const WeightedGraph<int>& graph,
+ std::unordered_map<int, int>* membership) {
+ CHECK(membership != nullptr);
+ membership->clear();
+
+ // Initially each vertex is in its own cluster.
+ const std::unordered_set<int>& vertices = graph.vertices();
+ for (const int v : vertices) {
+ (*membership)[v] = v;
+ }
+
+ for (const int vertex1 : vertices) {
+ const std::unordered_set<int>& neighbors = graph.Neighbors(vertex1);
+ for (const int vertex2 : neighbors) {
+ // Since the graph is undirected, only pay attention to one side
+ // of the edge and ignore weak edges.
+ if ((vertex1 > vertex2) ||
+ (graph.EdgeWeight(vertex1, vertex2) < options.min_similarity)) {
+ continue;
+ }
+
+ // Use a union-find algorithm to keep track of the clusters.
+ const int c1 = FindConnectedComponent(vertex1, membership);
+ const int c2 = FindConnectedComponent(vertex2, membership);
+
+ if (c1 == c2) {
+ continue;
+ }
+
+ if (c1 < c2) {
+ (*membership)[c2] = c1;
+ } else {
+ (*membership)[c1] = c2;
+ }
+ }
+ }
+
+ // Make sure that every vertex is connected directly to the vertex
+ // identifying the cluster.
+ int num_clusters = 0;
+ for (auto& m : *membership) {
+ m.second = FindConnectedComponent(m.first, membership);
+ if (m.first == m.second) {
+ ++num_clusters;
+ }
+ }
+
+ return num_clusters;
+}
+
+} // namespace internal
+} // namespace ceres
diff --git a/extern/ceres/include/ceres/fpclassify.h b/extern/ceres/internal/ceres/single_linkage_clustering.h
index bc2dc90026c..ccd6f8ea37d 100644
--- a/extern/ceres/include/ceres/fpclassify.h
+++ b/extern/ceres/internal/ceres/single_linkage_clustering.h
@@ -26,45 +26,39 @@
// 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)
-//
-// Portable floating point classification. The names are picked such that they
-// do not collide with macros. For example, "isnan" in C99 is a macro and hence
-// does not respect namespaces.
-//
-// TODO(keir): Finish porting!
+// Author: sameeragarwal@google.com (Sameer Agarwal)
-#ifndef CERES_PUBLIC_FPCLASSIFY_H_
-#define CERES_PUBLIC_FPCLASSIFY_H_
+#ifndef CERES_INTERNAL_SINGLE_LINKAGE_CLUSTERING_H_
+#define CERES_INTERNAL_SINGLE_LINKAGE_CLUSTERING_H_
-#if defined(_MSC_VER)
-#include <float.h>
-#endif
-
-#include <limits>
+#include <unordered_map>
+#include "ceres/graph.h"
namespace ceres {
+namespace internal {
-#if defined(_MSC_VER)
-
-inline bool IsFinite (double x) { return _finite(x) != 0; }
-inline bool IsInfinite(double x) { return _finite(x) == 0 && _isnan(x) == 0; }
-inline bool IsNaN (double x) { return _isnan(x) != 0; }
-inline bool IsNormal (double x) { // NOLINT
- const int classification = _fpclass(x);
- return (classification == _FPCLASS_NN || classification == _FPCLASS_PN);
-}
+struct SingleLinkageClusteringOptions {
+ // Graph edges with edge weight less than min_similarity are ignored
+ // during the clustering process.
+ double min_similarity = 0.99;
+};
-# else
-
-// These definitions are for the normal Unix suspects.
-inline bool IsFinite (double x) { return std::isfinite(x); }
-inline bool IsInfinite(double x) { return std::isinf(x); }
-inline bool IsNaN (double x) { return std::isnan(x); }
-inline bool IsNormal (double x) { return std::isnormal(x); }
-
-#endif
+// Compute a partitioning of the vertices of the graph using the
+// single linkage clustering algorithm. Edges with weight less than
+// SingleLinkageClusteringOptions::min_similarity will be ignored.
+//
+// membership upon return will contain a mapping from the vertices of
+// the graph to an integer indicating the identity of the cluster that
+// it belongs to.
+//
+// The return value of this function is the number of clusters
+// identified by the algorithm.
+int ComputeSingleLinkageClustering(
+ const SingleLinkageClusteringOptions& options,
+ const WeightedGraph<int>& graph,
+ std::unordered_map<int, int>* membership);
+} // namespace internal
} // namespace ceres
-#endif // CERES_PUBLIC_FPCLASSIFY_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 264ac53047d..81c58722d5b 100644
--- a/extern/ceres/internal/ceres/small_blas.h
+++ b/extern/ceres/internal/ceres/small_blas.h
@@ -38,6 +38,7 @@
#include "ceres/internal/port.h"
#include "ceres/internal/eigen.h"
#include "glog/logging.h"
+#include "small_blas_generic.h"
namespace ceres {
namespace internal {
@@ -89,6 +90,26 @@ namespace internal {
B, num_row_b, num_col_b, \
C, start_row_c, start_col_c, row_stride_c, col_stride_c);
+#define CERES_GEMM_STORE_SINGLE(p, index, value) \
+ if (kOperation > 0) { \
+ p[index] += value; \
+ } else if (kOperation < 0) { \
+ p[index] -= value; \
+ } else { \
+ p[index] = value; \
+ }
+
+#define CERES_GEMM_STORE_PAIR(p, index, v1, v2) \
+ if (kOperation > 0) { \
+ p[index] += v1; \
+ p[index + 1] += v2; \
+ } else if (kOperation < 0) { \
+ p[index] -= v1; \
+ p[index + 1] -= v2; \
+ } else { \
+ p[index] = v1; \
+ p[index + 1] = v2; \
+ }
// For the matrix-matrix functions below, there are three variants for
// each functionality. Foo, FooNaive and FooEigen. Foo is the one to
@@ -160,24 +181,64 @@ CERES_GEMM_BEGIN(MatrixMatrixMultiplyNaive) {
const int NUM_COL_C = NUM_COL_B;
DCHECK_LE(start_row_c + NUM_ROW_C, row_stride_c);
DCHECK_LE(start_col_c + NUM_COL_C, col_stride_c);
+ const int span = 4;
+
+ // Calculate the remainder part first.
- for (int row = 0; row < NUM_ROW_C; ++row) {
- for (int col = 0; col < NUM_COL_C; ++col) {
+ // Process the last odd column if present.
+ if (NUM_COL_C & 1) {
+ int col = NUM_COL_C - 1;
+ const double* pa = &A[0];
+ for (int row = 0; row < NUM_ROW_C; ++row, pa += NUM_COL_A) {
+ const double* pb = &B[col];
double tmp = 0.0;
- for (int k = 0; k < NUM_COL_A; ++k) {
- tmp += A[row * NUM_COL_A + k] * B[k * NUM_COL_B + col];
+ for (int k = 0; k < NUM_COL_A; ++k, pb += NUM_COL_B) {
+ tmp += pa[k] * pb[0];
}
const int index = (row + start_row_c) * col_stride_c + start_col_c + col;
- if (kOperation > 0) {
- C[index] += tmp;
- } else if (kOperation < 0) {
- C[index] -= tmp;
- } else {
- C[index] = tmp;
+ CERES_GEMM_STORE_SINGLE(C, index, tmp);
+ }
+
+ // Return directly for efficiency of extremely small matrix multiply.
+ if (NUM_COL_C == 1) {
+ return;
+ }
+ }
+
+ // Process the couple columns in remainder if present.
+ if (NUM_COL_C & 2) {
+ int col = NUM_COL_C & (int)(~(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];
+ double tmp1 = 0.0, tmp2 = 0.0;
+ for (int k = 0; k < NUM_COL_A; ++k, pb += NUM_COL_B) {
+ double av = pa[k];
+ tmp1 += av * pb[0];
+ tmp2 += av * pb[1];
}
+
+ const int index = (row + start_row_c) * col_stride_c + start_col_c + col;
+ CERES_GEMM_STORE_PAIR(C, index, tmp1, tmp2);
+ }
+
+ // Return directly for efficiency of extremely small matrix multiply.
+ if (NUM_COL_C < span) {
+ return;
}
}
+
+ // Calculate the main part with multiples of 4.
+ int col_m = NUM_COL_C & (int)(~(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;
+ MMM_mat1x4(NUM_COL_A, &A[row * NUM_COL_A],
+ &B[col], NUM_COL_B, &C[index], kOperation);
+ }
+ }
+
}
CERES_GEMM_BEGIN(MatrixMatrixMultiply) {
@@ -220,24 +281,68 @@ CERES_GEMM_BEGIN(MatrixTransposeMatrixMultiplyNaive) {
const int NUM_COL_C = NUM_COL_B;
DCHECK_LE(start_row_c + NUM_ROW_C, row_stride_c);
DCHECK_LE(start_col_c + NUM_COL_C, col_stride_c);
+ const int span = 4;
- for (int row = 0; row < NUM_ROW_C; ++row) {
- for (int col = 0; col < NUM_COL_C; ++col) {
+ // Process the remainder part first.
+
+ // Process the last odd column if present.
+ if (NUM_COL_C & 1) {
+ int col = NUM_COL_C - 1;
+ for (int row = 0; row < NUM_ROW_C; ++row) {
+ const double* pa = &A[row];
+ const double* pb = &B[col];
double tmp = 0.0;
for (int k = 0; k < NUM_ROW_A; ++k) {
- tmp += A[k * NUM_COL_A + row] * B[k * NUM_COL_B + col];
+ tmp += pa[0] * pb[0];
+ pa += NUM_COL_A;
+ pb += NUM_COL_B;
}
const int index = (row + start_row_c) * col_stride_c + start_col_c + col;
- if (kOperation > 0) {
- C[index]+= tmp;
- } else if (kOperation < 0) {
- C[index]-= tmp;
- } else {
- C[index]= tmp;
+ CERES_GEMM_STORE_SINGLE(C, index, tmp);
+ }
+
+ // Return directly for efficiency of extremely small matrix multiply.
+ if (NUM_COL_C == 1) {
+ return;
+ }
+ }
+
+ // Process the couple columns in remainder if present.
+ if (NUM_COL_C & 2) {
+ int col = NUM_COL_C & (int)(~(span - 1)) ;
+ for (int row = 0; row < NUM_ROW_C; ++row) {
+ const double* pa = &A[row];
+ const double* pb = &B[col];
+ double tmp1 = 0.0, tmp2 = 0.0;
+ for (int k = 0; k < NUM_ROW_A; ++k) {
+ double av = *pa;
+ tmp1 += av * pb[0];
+ tmp2 += av * pb[1];
+ pa += NUM_COL_A;
+ pb += NUM_COL_B;
}
+
+ const int index = (row + start_row_c) * col_stride_c + start_col_c + col;
+ CERES_GEMM_STORE_PAIR(C, index, tmp1, tmp2);
+ }
+
+ // Return directly for efficiency of extremely small matrix multiply.
+ if (NUM_COL_C < span) {
+ return;
}
}
+
+ // Process the main part with multiples of 4.
+ int col_m = NUM_COL_C & (int)(~(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;
+ MTM_mat1x4(NUM_ROW_A, &A[row], NUM_COL_A,
+ &B[col], NUM_COL_B, &C[index], kOperation);
+ }
+ }
+
}
CERES_GEMM_BEGIN(MatrixTransposeMatrixMultiply) {
@@ -301,21 +406,54 @@ inline void MatrixVectorMultiply(const double* A,
const int NUM_ROW_A = (kRowA != Eigen::Dynamic ? kRowA : num_row_a);
const int NUM_COL_A = (kColA != Eigen::Dynamic ? kColA : num_col_a);
+ const int span = 4;
- for (int row = 0; row < NUM_ROW_A; ++row) {
+ // Calculate the remainder part first.
+
+ // Process the last odd row if present.
+ if (NUM_ROW_A & 1) {
+ int row = NUM_ROW_A - 1;
+ const double* pa = &A[row * NUM_COL_A];
+ const double* pb = &b[0];
double tmp = 0.0;
for (int col = 0; col < NUM_COL_A; ++col) {
- tmp += A[row * NUM_COL_A + col] * b[col];
+ tmp += (*pa++) * (*pb++);
+ }
+ CERES_GEMM_STORE_SINGLE(c, row, tmp);
+
+ // Return directly for efficiency of extremely small matrix multiply.
+ if (NUM_ROW_A == 1) {
+ return;
+ }
+ }
+
+ // Process the couple rows in remainder if present.
+ if (NUM_ROW_A & 2) {
+ int row = NUM_ROW_A & (int)(~(span - 1));
+ const double* pa1 = &A[row * NUM_COL_A];
+ const double* pa2 = pa1 + NUM_COL_A;
+ const double* pb = &b[0];
+ double tmp1 = 0.0, tmp2 = 0.0;
+ for (int col = 0; col < NUM_COL_A; ++col) {
+ double bv = *pb++;
+ tmp1 += *(pa1++) * bv;
+ tmp2 += *(pa2++) * bv;
}
+ CERES_GEMM_STORE_PAIR(c, row, tmp1, tmp2);
- if (kOperation > 0) {
- c[row] += tmp;
- } else if (kOperation < 0) {
- c[row] -= tmp;
- } else {
- c[row] = tmp;
+ // Return directly for efficiency of extremely small matrix multiply.
+ if (NUM_ROW_A < span) {
+ return;
}
}
+
+ // Calculate the main part with multiples of 4.
+ int row_m = NUM_ROW_A & (int)(~(span - 1));
+ for (int row = 0; row < row_m; row += span) {
+ MVM_mat4x1(NUM_COL_A, &A[row * NUM_COL_A], NUM_COL_A,
+ &b[0], &c[row], kOperation);
+ }
+
#endif // CERES_NO_CUSTOM_BLAS
}
@@ -352,21 +490,55 @@ inline void MatrixTransposeVectorMultiply(const double* A,
const int NUM_ROW_A = (kRowA != Eigen::Dynamic ? kRowA : num_row_a);
const int NUM_COL_A = (kColA != Eigen::Dynamic ? kColA : num_col_a);
+ const int span = 4;
+
+ // Calculate the remainder part first.
- for (int row = 0; row < NUM_COL_A; ++row) {
+ // Process the last odd column if present.
+ if (NUM_COL_A & 1) {
+ int row = NUM_COL_A - 1;
+ const double* pa = &A[row];
+ const double* pb = &b[0];
double tmp = 0.0;
for (int col = 0; col < NUM_ROW_A; ++col) {
- tmp += A[col * NUM_COL_A + row] * b[col];
+ tmp += *pa * (*pb++);
+ pa += NUM_COL_A;
}
+ CERES_GEMM_STORE_SINGLE(c, row, tmp);
- if (kOperation > 0) {
- c[row] += tmp;
- } else if (kOperation < 0) {
- c[row] -= tmp;
- } else {
- c[row] = tmp;
+ // Return directly for efficiency of extremely small matrix multiply.
+ if (NUM_COL_A == 1) {
+ return;
}
}
+
+ // Process the couple columns in remainder if present.
+ if (NUM_COL_A & 2) {
+ int row = NUM_COL_A & (int)(~(span - 1));
+ const double* pa = &A[row];
+ const double* pb = &b[0];
+ double tmp1 = 0.0, tmp2 = 0.0;
+ for (int col = 0; col < NUM_ROW_A; ++col) {
+ double bv = *pb++;
+ tmp1 += *(pa ) * bv;
+ tmp2 += *(pa + 1) * bv;
+ pa += NUM_COL_A;
+ }
+ CERES_GEMM_STORE_PAIR(c, row, tmp1, tmp2);
+
+ // Return directly for efficiency of extremely small matrix multiply.
+ if (NUM_COL_A < span) {
+ return;
+ }
+ }
+
+ // Calculate the main part with multiples of 4.
+ int row_m = NUM_COL_A & (int)(~(span - 1));
+ for (int row = 0; row < row_m; row += span) {
+ MTV_mat4x1(NUM_ROW_A, &A[row], NUM_COL_A,
+ &b[0], &c[row], kOperation);
+ }
+
#endif // CERES_NO_CUSTOM_BLAS
}
@@ -374,6 +546,8 @@ inline void MatrixTransposeVectorMultiply(const double* A,
#undef CERES_GEMM_EIGEN_HEADER
#undef CERES_GEMM_NAIVE_HEADER
#undef CERES_CALL_GEMM
+#undef CERES_GEMM_STORE_SINGLE
+#undef CERES_GEMM_STORE_PAIR
} // namespace internal
} // namespace ceres
diff --git a/extern/ceres/internal/ceres/small_blas_generic.h b/extern/ceres/internal/ceres/small_blas_generic.h
new file mode 100644
index 00000000000..978c5d56a84
--- /dev/null
+++ b/extern/ceres/internal/ceres/small_blas_generic.h
@@ -0,0 +1,315 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2018 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: yangfan34@lenovo.com (Lenovo Research Device+ Lab - Shanghai)
+//
+// Optimization for simple blas functions used in the Schur Eliminator.
+// These are fairly basic implementations which already yield a significant
+// speedup in the eliminator performance.
+
+#ifndef CERES_INTERNAL_SMALL_BLAS_GENERIC_H_
+#define CERES_INTERNAL_SMALL_BLAS_GENERIC_H_
+
+namespace ceres {
+namespace internal {
+
+// The following macros are used to share code
+#define CERES_GEMM_OPT_NAIVE_HEADER \
+ double c0 = 0.0; \
+ double c1 = 0.0; \
+ double c2 = 0.0; \
+ double c3 = 0.0; \
+ const double* pa = a; \
+ const double* pb = b; \
+ const int span = 4; \
+ int col_r = col_a & (span - 1); \
+ int col_m = col_a - col_r;
+
+#define CERES_GEMM_OPT_STORE_MAT1X4 \
+ if (kOperation > 0) { \
+ *c++ += c0; \
+ *c++ += c1; \
+ *c++ += c2; \
+ *c++ += c3; \
+ } else if (kOperation < 0) { \
+ *c++ -= c0; \
+ *c++ -= c1; \
+ *c++ -= c2; \
+ *c++ -= c3; \
+ } else { \
+ *c++ = c0; \
+ *c++ = c1; \
+ *c++ = c2; \
+ *c++ = c3; \
+ }
+
+// Matrix-Matrix Multiplication
+// Figure out 1x4 of Matrix C in one batch
+//
+// c op a * B;
+// where op can be +=, -=, or =, indicated by kOperation.
+//
+// Matrix C Matrix A Matrix B
+//
+// C0, C1, C2, C3 op A0, A1, A2, A3, ... * B0, B1, B2, B3
+// B4, B5, B6, B7
+// B8, B9, Ba, Bb
+// Bc, Bd, Be, Bf
+// . , . , . , .
+// . , . , . , .
+// . , . , . , .
+//
+// unroll for loops
+// utilize the data resided in cache
+// NOTE: col_a means the columns of A
+static inline void MMM_mat1x4(const int col_a,
+ const double* a,
+ const double* b,
+ const int col_stride_b,
+ double* c,
+ const int kOperation) {
+ CERES_GEMM_OPT_NAIVE_HEADER
+ double av = 0.0;
+ int bi = 0;
+
+#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++; \
+ bi += col_stride_b; \
+ k++;
+
+ for (int k = 0; k < col_m;) {
+ CERES_GEMM_OPT_MMM_MAT1X4_MUL
+ CERES_GEMM_OPT_MMM_MAT1X4_MUL
+ CERES_GEMM_OPT_MMM_MAT1X4_MUL
+ CERES_GEMM_OPT_MMM_MAT1X4_MUL
+ }
+
+ for (int k = col_m; k < col_a;) {
+ CERES_GEMM_OPT_MMM_MAT1X4_MUL
+ }
+
+ CERES_GEMM_OPT_STORE_MAT1X4
+
+#undef CERES_GEMM_OPT_MMM_MAT1X4_MUL
+}
+
+// Matrix Transpose-Matrix multiplication
+// Figure out 1x4 of Matrix C in one batch
+//
+// c op a' * B;
+// where op can be +=, -=, or = indicated by kOperation.
+//
+// Matrix A
+//
+// A0
+// A1
+// A2
+// A3
+// .
+// .
+// .
+//
+// Matrix C Matrix A' Matrix B
+//
+// C0, C1, C2, C3 op A0, A1, A2, A3, ... * B0, B1, B2, B3
+// B4, B5, B6, B7
+// B8, B9, Ba, Bb
+// Bc, Bd, Be, Bf
+// . , . , . , .
+// . , . , . , .
+// . , . , . , .
+//
+// unroll for loops
+// utilize the data resided in cache
+// NOTE: col_a means the columns of A'
+static inline void MTM_mat1x4(const int col_a,
+ const double* a,
+ const int col_stride_a,
+ const double* b,
+ const int col_stride_b,
+ double* c,
+ const int kOperation) {
+ CERES_GEMM_OPT_NAIVE_HEADER
+ double av = 0.0;
+ int ai = 0;
+ int bi = 0;
+
+#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++; \
+ ai += col_stride_a; \
+ bi += col_stride_b;
+
+ for (int k = 0; k < col_m; k += span) {
+ CERES_GEMM_OPT_MTM_MAT1X4_MUL
+ CERES_GEMM_OPT_MTM_MAT1X4_MUL
+ CERES_GEMM_OPT_MTM_MAT1X4_MUL
+ CERES_GEMM_OPT_MTM_MAT1X4_MUL
+ }
+
+ for (int k = col_m; k < col_a; k++) {
+ CERES_GEMM_OPT_MTM_MAT1X4_MUL
+ }
+
+ CERES_GEMM_OPT_STORE_MAT1X4
+
+#undef CERES_GEMM_OPT_MTM_MAT1X4_MUL
+}
+
+// Matrix-Vector Multiplication
+// Figure out 4x1 of vector c in one batch
+//
+// c op A * b;
+// where op can be +=, -=, or =, indicated by kOperation.
+//
+// Vector c Matrix A Vector b
+//
+// C0 op A0, A1, A2, A3, ... * B0
+// C1 A4, A5, A6, A7, ... B1
+// C2 A8, A9, Aa, Ab, ... B2
+// C3 Ac, Ad, Ae, Af, ... B3
+// .
+// .
+// .
+//
+// unroll for loops
+// utilize the data resided in cache
+// NOTE: col_a means the columns of A
+static inline void MVM_mat4x1(const int col_a,
+ const double* a,
+ const int col_stride_a,
+ const double* b,
+ double* c,
+ const int kOperation) {
+ CERES_GEMM_OPT_NAIVE_HEADER
+ double bv = 0.0;
+
+#define CERES_GEMM_OPT_MVM_MAT4X1_MUL \
+ bv = *pb; \
+ c0 += *(pa ) * bv; \
+ c1 += *(pa + col_stride_a ) * bv; \
+ c2 += *(pa + col_stride_a * 2) * bv; \
+ c3 += *(pa + col_stride_a * 3) * bv; \
+ pa++; \
+ pb++;
+
+ for (int k = 0; k < col_m; k += span) {
+ CERES_GEMM_OPT_MVM_MAT4X1_MUL
+ CERES_GEMM_OPT_MVM_MAT4X1_MUL
+ CERES_GEMM_OPT_MVM_MAT4X1_MUL
+ CERES_GEMM_OPT_MVM_MAT4X1_MUL
+ }
+
+ for (int k = col_m; k < col_a; k++) {
+ CERES_GEMM_OPT_MVM_MAT4X1_MUL
+ }
+
+ CERES_GEMM_OPT_STORE_MAT1X4
+
+#undef CERES_GEMM_OPT_MVM_MAT4X1_MUL
+}
+
+// Matrix Transpose-Vector multiplication
+// Figure out 4x1 of vector c in one batch
+//
+// c op A' * b;
+// where op can be +=, -=, or =, indicated by kOperation.
+//
+// Matrix A
+//
+// A0, A4, A8, Ac
+// A1, A5, A9, Ad
+// A2, A6, Aa, Ae
+// A3, A7, Ab, Af
+// . , . , . , .
+// . , . , . , .
+// . , . , . , .
+//
+// Vector c Matrix A' Vector b
+//
+// C0 op A0, A1, A2, A3, ... * B0
+// C1 A4, A5, A6, A7, ... B1
+// C2 A8, A9, Aa, Ab, ... B2
+// C3 Ac, Ad, Ae, Af, ... B3
+// .
+// .
+// .
+//
+// unroll for loops
+// utilize the data resided in cache
+// NOTE: col_a means the columns of A'
+static inline void MTV_mat4x1(const int col_a,
+ const double* a,
+ const int col_stride_a,
+ const double* b,
+ double* c,
+ const int kOperation) {
+ CERES_GEMM_OPT_NAIVE_HEADER
+ double bv = 0.0;
+
+#define CERES_GEMM_OPT_MTV_MAT4X1_MUL \
+ bv = *pb; \
+ c0 += *(pa ) * bv; \
+ c1 += *(pa + 1) * bv; \
+ c2 += *(pa + 2) * bv; \
+ c3 += *(pa + 3) * bv; \
+ pa += col_stride_a; \
+ pb++;
+
+ for (int k = 0; k < col_m; k += span) {
+ CERES_GEMM_OPT_MTV_MAT4X1_MUL
+ CERES_GEMM_OPT_MTV_MAT4X1_MUL
+ CERES_GEMM_OPT_MTV_MAT4X1_MUL
+ CERES_GEMM_OPT_MTV_MAT4X1_MUL
+ }
+
+ for (int k = col_m; k < col_a; k++) {
+ CERES_GEMM_OPT_MTV_MAT4X1_MUL
+ }
+
+ CERES_GEMM_OPT_STORE_MAT1X4
+
+#undef CERES_GEMM_OPT_MTV_MAT4X1_MUL
+}
+
+#undef CERES_GEMM_OPT_NAIVE_HEADER
+#undef CERES_GEMM_OPT_STORE_MAT1X4
+
+} // namespace internal
+} // namespace ceres
+
+#endif // CERES_INTERNAL_SMALL_BLAS_GENERIC_H_
diff --git a/extern/ceres/internal/ceres/solver.cc b/extern/ceres/internal/ceres/solver.cc
index 8411350986a..861d8d30485 100644
--- a/extern/ceres/internal/ceres/solver.cc
+++ b/extern/ceres/internal/ceres/solver.cc
@@ -32,8 +32,14 @@
#include "ceres/solver.h"
#include <algorithm>
-#include <sstream> // NOLINT
+#include <memory>
+#include <sstream> // NOLINT
#include <vector>
+
+#include "ceres/casts.h"
+#include "ceres/context.h"
+#include "ceres/context_impl.h"
+#include "ceres/detect_structure.h"
#include "ceres/gradient_checking_cost_function.h"
#include "ceres/internal/port.h"
#include "ceres/parameter_block_ordering.h"
@@ -41,6 +47,7 @@
#include "ceres/problem.h"
#include "ceres/problem_impl.h"
#include "ceres/program.h"
+#include "ceres/schur_templates.h"
#include "ceres/solver_utils.h"
#include "ceres/stringprintf.h"
#include "ceres/types.h"
@@ -52,6 +59,8 @@ namespace {
using std::map;
using std::string;
using std::vector;
+using internal::StringAppendF;
+using internal::StringPrintf;
#define OPTION_OP(x, y, OP) \
if (!(options.x OP y)) { \
@@ -91,7 +100,6 @@ bool CommonOptionsAreValid(const Solver::Options& options, string* error) {
OPTION_GE(gradient_tolerance, 0.0);
OPTION_GE(parameter_tolerance, 0.0);
OPTION_GT(num_threads, 0);
- OPTION_GT(num_linear_solver_threads, 0);
if (options.check_gradients) {
OPTION_GT(gradient_check_relative_precision, 0.0);
OPTION_GT(gradient_check_numeric_derivative_relative_step_size, 0.0);
@@ -132,101 +140,51 @@ bool TrustRegionOptionsAreValid(const Solver::Options& options, string* error) {
return false;
}
- if (options.preconditioner_type == CLUSTER_JACOBI &&
- options.sparse_linear_algebra_library_type != SUITE_SPARSE) {
- *error = "CLUSTER_JACOBI requires "
- "Solver::Options::sparse_linear_algebra_library_type to be "
- "SUITE_SPARSE";
- return false;
- }
-
- if (options.preconditioner_type == CLUSTER_TRIDIAGONAL &&
- options.sparse_linear_algebra_library_type != SUITE_SPARSE) {
- *error = "CLUSTER_TRIDIAGONAL requires "
- "Solver::Options::sparse_linear_algebra_library_type to be "
- "SUITE_SPARSE";
- return false;
- }
-
-#ifdef CERES_NO_LAPACK
- if (options.dense_linear_algebra_library_type == LAPACK) {
- if (options.linear_solver_type == DENSE_NORMAL_CHOLESKY) {
- *error = "Can't use DENSE_NORMAL_CHOLESKY with LAPACK because "
- "LAPACK was not enabled when Ceres was built.";
- return false;
- } else if (options.linear_solver_type == DENSE_QR) {
- *error = "Can't use DENSE_QR with LAPACK because "
- "LAPACK was not enabled when Ceres was built.";
- return false;
- } else if (options.linear_solver_type == DENSE_SCHUR) {
- *error = "Can't use DENSE_SCHUR with LAPACK because "
- "LAPACK was not enabled when Ceres was built.";
- return false;
- }
- }
-#endif
-
-#ifdef CERES_NO_SUITESPARSE
- if (options.sparse_linear_algebra_library_type == SUITE_SPARSE) {
- if (options.linear_solver_type == SPARSE_NORMAL_CHOLESKY) {
- *error = "Can't use SPARSE_NORMAL_CHOLESKY with SUITESPARSE because "
- "SuiteSparse was not enabled when Ceres was built.";
- return false;
- } else if (options.linear_solver_type == SPARSE_SCHUR) {
- *error = "Can't use SPARSE_SCHUR with SUITESPARSE because "
- "SuiteSparse was not enabled when Ceres was built.";
- return false;
- } else if (options.preconditioner_type == CLUSTER_JACOBI) {
- *error = "CLUSTER_JACOBI preconditioner not supported. "
- "SuiteSparse was not enabled when Ceres was built.";
- return false;
- } else if (options.preconditioner_type == CLUSTER_TRIDIAGONAL) {
- *error = "CLUSTER_TRIDIAGONAL preconditioner not supported. "
- "SuiteSparse was not enabled when Ceres was built.";
+ if (options.dense_linear_algebra_library_type == LAPACK &&
+ !IsDenseLinearAlgebraLibraryTypeAvailable(LAPACK) &&
+ (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));
return false;
- }
}
-#endif
-#ifdef CERES_NO_CXSPARSE
- if (options.sparse_linear_algebra_library_type == CX_SPARSE) {
- if (options.linear_solver_type == SPARSE_NORMAL_CHOLESKY) {
- *error = "Can't use SPARSE_NORMAL_CHOLESKY with CX_SPARSE because "
- "CXSparse was not enabled when Ceres was built.";
- return false;
- } else if (options.linear_solver_type == SPARSE_SCHUR) {
- *error = "Can't use SPARSE_SCHUR with CX_SPARSE because "
- "CXSparse was not enabled when Ceres was built.";
- return false;
+ {
+ const char* sparse_linear_algebra_library_name =
+ SparseLinearAlgebraLibraryTypeToString(
+ options.sparse_linear_algebra_library_type);
+ const char* name = nullptr;
+ if (options.linear_solver_type == SPARSE_NORMAL_CHOLESKY ||
+ options.linear_solver_type == SPARSE_SCHUR) {
+ name = LinearSolverTypeToString(options.linear_solver_type);
+ } else if ((options.linear_solver_type == ITERATIVE_SCHUR &&
+ (options.preconditioner_type == CLUSTER_JACOBI ||
+ options.preconditioner_type == CLUSTER_TRIDIAGONAL)) ||
+ (options.linear_solver_type == CGNR &&
+ options.preconditioner_type == SUBSET)) {
+ name = PreconditionerTypeToString(options.preconditioner_type);
}
- }
-#endif
-#ifndef CERES_USE_EIGEN_SPARSE
- if (options.sparse_linear_algebra_library_type == EIGEN_SPARSE) {
- if (options.linear_solver_type == SPARSE_NORMAL_CHOLESKY) {
- *error = "Can't use SPARSE_NORMAL_CHOLESKY with EIGEN_SPARSE because "
- "Eigen's sparse linear algebra was not enabled when Ceres was "
- "built.";
- return false;
- } else if (options.linear_solver_type == SPARSE_SCHUR) {
- *error = "Can't use SPARSE_SCHUR with EIGEN_SPARSE because "
- "Eigen's sparse linear algebra was not enabled when Ceres was "
- "built.";
- return false;
- }
- }
-#endif
-
- if (options.sparse_linear_algebra_library_type == NO_SPARSE) {
- if (options.linear_solver_type == SPARSE_NORMAL_CHOLESKY) {
- *error = "Can't use SPARSE_NORMAL_CHOLESKY as "
- "sparse_linear_algebra_library_type is NO_SPARSE.";
- return false;
- } else if (options.linear_solver_type == SPARSE_SCHUR) {
- *error = "Can't use SPARSE_SCHUR as "
- "sparse_linear_algebra_library_type is NO_SPARSE.";
- return false;
+ if (name) {
+ if (options.sparse_linear_algebra_library_type == NO_SPARSE) {
+ *error = StringPrintf(
+ "Can't use %s with "
+ "Solver::Options::sparse_linear_algebra_library_type = %s.",
+ name, sparse_linear_algebra_library_name);
+ return false;
+ } else if (!IsSparseLinearAlgebraLibraryTypeAvailable(
+ options.sparse_linear_algebra_library_type)) {
+ *error = StringPrintf(
+ "Can't use %s with "
+ "Solver::Options::sparse_linear_algebra_library_type = %s, "
+ "because support was not enabled when Ceres Solver was built.",
+ name, sparse_linear_algebra_library_name);
+ return false;
+ }
}
}
@@ -240,16 +198,32 @@ bool TrustRegionOptionsAreValid(const Solver::Options& options, string* error) {
}
}
- if (options.trust_region_minimizer_iterations_to_dump.size() > 0 &&
+ if (!options.trust_region_minimizer_iterations_to_dump.empty() &&
options.trust_region_problem_dump_format_type != CONSOLE &&
options.trust_region_problem_dump_directory.empty()) {
*error = "Solver::Options::trust_region_problem_dump_directory is empty.";
return false;
}
- if (options.dynamic_sparsity &&
- options.linear_solver_type != SPARSE_NORMAL_CHOLESKY) {
- *error = "Dynamic sparsity is only supported with SPARSE_NORMAL_CHOLESKY.";
+ if (options.dynamic_sparsity) {
+ if (options.linear_solver_type != SPARSE_NORMAL_CHOLESKY) {
+ *error = "Dynamic sparsity is only supported with SPARSE_NORMAL_CHOLESKY.";
+ return false;
+ }
+ if (options.sparse_linear_algebra_library_type == ACCELERATE_SPARSE) {
+ *error = "ACCELERATE_SPARSE is not currently supported with dynamic "
+ "sparsity.";
+ return false;
+ }
+ }
+
+ if (options.linear_solver_type == CGNR &&
+ options.preconditioner_type == SUBSET &&
+ options.residual_blocks_for_subset_preconditioner.empty()) {
+ *error =
+ "When using SUBSET preconditioner, "
+ "Solver::Options::residual_blocks_for_subset_preconditioner cannot be "
+ "empty";
return false;
}
@@ -264,7 +238,8 @@ bool LineSearchOptionsAreValid(const Solver::Options& options, string* error) {
OPTION_LT_OPTION(max_line_search_step_contraction,
min_line_search_step_contraction);
OPTION_LE(min_line_search_step_contraction, 1.0);
- OPTION_GT(max_num_line_search_step_size_iterations, 0);
+ OPTION_GE(max_num_line_search_step_size_iterations,
+ (options.minimizer_type == ceres::TRUST_REGION ? 0 : 1));
OPTION_GT(line_search_sufficient_function_decrease, 0.0);
OPTION_LT_OPTION(line_search_sufficient_function_decrease,
line_search_sufficient_curvature_decrease);
@@ -310,13 +285,13 @@ bool LineSearchOptionsAreValid(const Solver::Options& options, string* error) {
#undef OPTION_LT_OPTION
void StringifyOrdering(const vector<int>& ordering, string* report) {
- if (ordering.size() == 0) {
+ if (ordering.empty()) {
internal::StringAppendF(report, "AUTOMATIC");
return;
}
for (int i = 0; i < ordering.size() - 1; ++i) {
- internal::StringAppendF(report, "%d, ", ordering[i]);
+ internal::StringAppendF(report, "%d,", ordering[i]);
}
internal::StringAppendF(report, "%d", ordering.back());
}
@@ -364,7 +339,6 @@ void PreSolveSummarize(const Solver::Options& options,
summary->max_lbfgs_rank = options.max_lbfgs_rank;
summary->minimizer_type = options.minimizer_type;
summary->nonlinear_conjugate_gradient_type = options.nonlinear_conjugate_gradient_type; // NOLINT
- summary->num_linear_solver_threads_given = options.num_linear_solver_threads; // NOLINT
summary->num_threads_given = options.num_threads;
summary->preconditioner_type_given = options.preconditioner_type;
summary->sparse_linear_algebra_library_type = options.sparse_linear_algebra_library_type; // NOLINT
@@ -380,10 +354,9 @@ void PostSolveSummarize(const internal::PreprocessedProblem& pp,
&(summary->inner_iteration_ordering_used));
summary->inner_iterations_used = pp.inner_iteration_minimizer.get() != NULL; // NOLINT
- summary->linear_solver_type_used = pp.options.linear_solver_type;
- summary->num_linear_solver_threads_used = pp.options.num_linear_solver_threads; // 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; // NOLINT
+ summary->preconditioner_type_used = pp.options.preconditioner_type;
internal::SetSummaryFinalCost(summary);
@@ -391,36 +364,47 @@ void PostSolveSummarize(const internal::PreprocessedProblem& pp,
SummarizeReducedProgram(*pp.reduced_program, summary);
}
+ using internal::CallStatistics;
+
// It is possible that no evaluator was created. This would be the
// 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) {
- const map<string, double>& evaluator_time_statistics =
- pp.evaluator->TimeStatistics();
- summary->residual_evaluation_time_in_seconds =
- FindWithDefault(evaluator_time_statistics, "Evaluator::Residual", 0.0);
- summary->jacobian_evaluation_time_in_seconds =
- FindWithDefault(evaluator_time_statistics, "Evaluator::Jacobian", 0.0);
+ const map<string, CallStatistics>& evaluator_statistics =
+ pp.evaluator->Statistics();
+ {
+ const CallStatistics& call_stats = FindWithDefault(
+ evaluator_statistics, "Evaluator::Residual", CallStatistics());
+
+ summary->residual_evaluation_time_in_seconds = call_stats.time;
+ summary->num_residual_evaluations = call_stats.calls;
+ }
+ {
+ const CallStatistics& call_stats = FindWithDefault(
+ evaluator_statistics, "Evaluator::Jacobian", CallStatistics());
+
+ summary->jacobian_evaluation_time_in_seconds = call_stats.time;
+ summary->num_jacobian_evaluations = call_stats.calls;
+ }
}
// 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) {
- const map<string, double>& linear_solver_time_statistics =
- pp.linear_solver->TimeStatistics();
- summary->linear_solver_time_in_seconds =
- FindWithDefault(linear_solver_time_statistics,
- "LinearSolver::Solve",
- 0.0);
+ const map<string, CallStatistics>& linear_solver_statistics =
+ pp.linear_solver->Statistics();
+ const CallStatistics& call_stats = FindWithDefault(
+ linear_solver_statistics, "LinearSolver::Solve", CallStatistics());
+ summary->num_linear_solves = call_stats.calls;
+ summary->linear_solver_time_in_seconds = call_stats.time;
}
}
void Minimize(internal::PreprocessedProblem* pp,
Solver::Summary* summary) {
using internal::Program;
- using internal::scoped_ptr;
using internal::Minimizer;
Program* program = pp->reduced_program.get();
@@ -434,16 +418,36 @@ void Minimize(internal::PreprocessedProblem* pp,
return;
}
- scoped_ptr<Minimizer> minimizer(
+ const Vector original_reduced_parameters = pp->reduced_parameters;
+ std::unique_ptr<Minimizer> minimizer(
Minimizer::Create(pp->options.minimizer_type));
minimizer->Minimize(pp->minimizer_options,
pp->reduced_parameters.data(),
summary);
- if (summary->IsSolutionUsable()) {
- program->StateVectorToParameterBlocks(pp->reduced_parameters.data());
- program->CopyParameterBlockStateToUserState();
- }
+ program->StateVectorToParameterBlocks(
+ summary->IsSolutionUsable()
+ ? pp->reduced_parameters.data()
+ : original_reduced_parameters.data());
+ program->CopyParameterBlockStateToUserState();
+}
+
+std::string SchurStructureToString(const int row_block_size,
+ const int e_block_size,
+ const int f_block_size) {
+ const std::string row =
+ (row_block_size == Eigen::Dynamic)
+ ? "d" : internal::StringPrintf("%d", row_block_size);
+
+ const std::string e =
+ (e_block_size == Eigen::Dynamic)
+ ? "d" : internal::StringPrintf("%d", e_block_size);
+
+ const std::string f =
+ (f_block_size == Eigen::Dynamic)
+ ? "d" : internal::StringPrintf("%d", f_block_size);
+
+ return internal::StringPrintf("%s,%s,%s", row.c_str(), e.c_str(), f.c_str());
}
} // namespace
@@ -475,11 +479,10 @@ void Solver::Solve(const Solver::Options& options,
using internal::Preprocessor;
using internal::ProblemImpl;
using internal::Program;
- using internal::scoped_ptr;
using internal::WallTimeInSeconds;
- CHECK_NOTNULL(problem);
- CHECK_NOTNULL(summary);
+ CHECK(problem != nullptr);
+ CHECK(summary != nullptr);
double start_time = WallTimeInSeconds();
*summary = Summary();
@@ -488,18 +491,14 @@ void Solver::Solve(const Solver::Options& options,
return;
}
- ProblemImpl* problem_impl = problem->problem_impl_.get();
+ ProblemImpl* problem_impl = problem->impl_.get();
Program* program = problem_impl->mutable_program();
PreSolveSummarize(options, problem_impl, summary);
- // Make sure that all the parameter blocks states are set to the
- // values provided by the user.
- program->SetParameterBlockStatePtrsToUserStatePtrs();
-
// If gradient_checking is enabled, wrap all cost functions in a
// gradient checker and install a callback that terminates if any gradient
// error is detected.
- scoped_ptr<internal::ProblemImpl> gradient_checking_problem;
+ std::unique_ptr<internal::ProblemImpl> gradient_checking_problem;
internal::GradientCheckingIterationCallback gradient_checking_callback;
Solver::Options modified_options = options;
if (options.check_gradients) {
@@ -514,10 +513,46 @@ void Solver::Solve(const Solver::Options& options,
program = problem_impl->mutable_program();
}
- scoped_ptr<Preprocessor> preprocessor(
+ // Make sure that all the parameter blocks states are set to the
+ // values provided by the user.
+ program->SetParameterBlockStatePtrsToUserStatePtrs();
+
+ // 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));
PreprocessedProblem pp;
+
const bool status = preprocessor->Preprocess(modified_options, problem_impl, &pp);
+
+ // We check the linear_solver_options.type rather than
+ // modified_options.linear_solver_type because, depending on the
+ // lack of a Schur structure, the preprocessor may change the linear
+ // solver type.
+ if (IsSchurType(pp.linear_solver_options.type)) {
+ // TODO(sameeragarwal): We can likely eliminate the duplicate call
+ // to DetectStructure here and inside the linear solver, by
+ // calling this in the preprocessor.
+ int row_block_size;
+ int e_block_size;
+ int f_block_size;
+ DetectStructure(*static_cast<internal::BlockSparseMatrix*>(
+ pp.minimizer_options.jacobian.get())
+ ->block_structure(),
+ pp.linear_solver_options.elimination_groups[0],
+ &row_block_size,
+ &e_block_size,
+ &f_block_size);
+ summary->schur_structure_given =
+ SchurStructureToString(row_block_size, e_block_size, f_block_size);
+ internal::GetBestSchurTemplateSpecialization(&row_block_size,
+ &e_block_size,
+ &f_block_size);
+ summary->schur_structure_used =
+ SchurStructureToString(row_block_size, e_block_size, f_block_size);
+ }
+
summary->fixed_cost = pp.fixed_cost;
summary->preprocessor_time_in_seconds = WallTimeInSeconds() - start_time;
@@ -531,7 +566,7 @@ void Solver::Solve(const Solver::Options& options,
}
const double postprocessor_start_time = WallTimeInSeconds();
- problem_impl = problem->problem_impl_.get();
+ problem_impl = problem->impl_.get();
program = problem_impl->mutable_program();
// On exit, ensure that the parameter blocks again point at the user
// provided values and the parameter blocks are numbered according
@@ -559,66 +594,6 @@ void Solve(const Solver::Options& options,
solver.Solve(options, problem, summary);
}
-Solver::Summary::Summary()
- // Invalid values for most fields, to ensure that we are not
- // accidentally reporting default values.
- : minimizer_type(TRUST_REGION),
- termination_type(FAILURE),
- message("ceres::Solve was not called."),
- initial_cost(-1.0),
- final_cost(-1.0),
- fixed_cost(-1.0),
- num_successful_steps(-1),
- num_unsuccessful_steps(-1),
- num_inner_iteration_steps(-1),
- num_line_search_steps(-1),
- preprocessor_time_in_seconds(-1.0),
- minimizer_time_in_seconds(-1.0),
- postprocessor_time_in_seconds(-1.0),
- total_time_in_seconds(-1.0),
- linear_solver_time_in_seconds(-1.0),
- residual_evaluation_time_in_seconds(-1.0),
- jacobian_evaluation_time_in_seconds(-1.0),
- inner_iteration_time_in_seconds(-1.0),
- line_search_cost_evaluation_time_in_seconds(-1.0),
- line_search_gradient_evaluation_time_in_seconds(-1.0),
- line_search_polynomial_minimization_time_in_seconds(-1.0),
- line_search_total_time_in_seconds(-1.0),
- num_parameter_blocks(-1),
- num_parameters(-1),
- num_effective_parameters(-1),
- num_residual_blocks(-1),
- num_residuals(-1),
- num_parameter_blocks_reduced(-1),
- num_parameters_reduced(-1),
- num_effective_parameters_reduced(-1),
- num_residual_blocks_reduced(-1),
- num_residuals_reduced(-1),
- is_constrained(false),
- num_threads_given(-1),
- num_threads_used(-1),
- num_linear_solver_threads_given(-1),
- num_linear_solver_threads_used(-1),
- linear_solver_type_given(SPARSE_NORMAL_CHOLESKY),
- linear_solver_type_used(SPARSE_NORMAL_CHOLESKY),
- inner_iterations_given(false),
- inner_iterations_used(false),
- preconditioner_type_given(IDENTITY),
- preconditioner_type_used(IDENTITY),
- visibility_clustering_type(CANONICAL_VIEWS),
- trust_region_strategy_type(LEVENBERG_MARQUARDT),
- dense_linear_algebra_library_type(EIGEN),
- sparse_linear_algebra_library_type(SUITE_SPARSE),
- line_search_direction_type(LBFGS),
- line_search_type(ARMIJO),
- line_search_interpolation_type(BISECTION),
- nonlinear_conjugate_gradient_type(FLETCHER_REEVES),
- max_lbfgs_rank(-1) {
-}
-
-using internal::StringAppendF;
-using internal::StringPrintf;
-
string Solver::Summary::BriefReport() const {
return StringPrintf("Ceres Solver Report: "
"Iterations: %d, "
@@ -647,7 +622,7 @@ string Solver::Summary::FullReport() const {
}
StringAppendF(&report, "Residual blocks % 25d% 25d\n",
num_residual_blocks, num_residual_blocks_reduced);
- StringAppendF(&report, "Residual % 25d% 25d\n",
+ StringAppendF(&report, "Residuals % 25d% 25d\n",
num_residuals, num_residuals_reduced);
if (minimizer_type == TRUST_REGION) {
@@ -708,9 +683,6 @@ string Solver::Summary::FullReport() const {
}
StringAppendF(&report, "Threads % 25d% 25d\n",
num_threads_given, num_threads_used);
- StringAppendF(&report, "Linear solver threads % 23d% 25d\n",
- num_linear_solver_threads_given,
- num_linear_solver_threads_used);
string given;
StringifyOrdering(linear_solver_ordering_given, &given);
@@ -720,6 +692,12 @@ string Solver::Summary::FullReport() const {
"Linear solver ordering %22s %24s\n",
given.c_str(),
used.c_str());
+ if (IsSchurType(linear_solver_type_used)) {
+ StringAppendF(&report,
+ "Schur structure %22s %24s\n",
+ schur_structure_given.c_str(),
+ schur_structure_used.c_str());
+ }
if (inner_iterations_given) {
StringAppendF(&report,
@@ -808,44 +786,44 @@ string Solver::Summary::FullReport() const {
}
StringAppendF(&report, "\nTime (in seconds):\n");
- StringAppendF(&report, "Preprocessor %25.4f\n",
+ StringAppendF(&report, "Preprocessor %25.6f\n",
preprocessor_time_in_seconds);
- StringAppendF(&report, "\n Residual evaluation %23.4f\n",
- residual_evaluation_time_in_seconds);
+ StringAppendF(&report, "\n Residual only evaluation %18.6f (%d)\n",
+ residual_evaluation_time_in_seconds, num_residual_evaluations);
if (line_search_used) {
- StringAppendF(&report, " Line search cost evaluation %10.4f\n",
+ StringAppendF(&report, " Line search cost evaluation %10.6f\n",
line_search_cost_evaluation_time_in_seconds);
}
- StringAppendF(&report, " Jacobian evaluation %23.4f\n",
- jacobian_evaluation_time_in_seconds);
+ StringAppendF(&report, " Jacobian & residual evaluation %12.6f (%d)\n",
+ jacobian_evaluation_time_in_seconds, num_jacobian_evaluations);
if (line_search_used) {
- StringAppendF(&report, " Line search gradient evaluation %6.4f\n",
+ StringAppendF(&report, " Line search gradient evaluation %6.6f\n",
line_search_gradient_evaluation_time_in_seconds);
}
if (minimizer_type == TRUST_REGION) {
- StringAppendF(&report, " Linear solver %23.4f\n",
- linear_solver_time_in_seconds);
+ StringAppendF(&report, " Linear solver %23.6f (%d)\n",
+ linear_solver_time_in_seconds, num_linear_solves);
}
if (inner_iterations_used) {
- StringAppendF(&report, " Inner iterations %23.4f\n",
+ StringAppendF(&report, " Inner iterations %23.6f\n",
inner_iteration_time_in_seconds);
}
if (line_search_used) {
- StringAppendF(&report, " Line search polynomial minimization %.4f\n",
+ StringAppendF(&report, " Line search polynomial minimization %.6f\n",
line_search_polynomial_minimization_time_in_seconds);
}
- StringAppendF(&report, "Minimizer %25.4f\n\n",
+ StringAppendF(&report, "Minimizer %25.6f\n\n",
minimizer_time_in_seconds);
- StringAppendF(&report, "Postprocessor %24.4f\n",
+ StringAppendF(&report, "Postprocessor %24.6f\n",
postprocessor_time_in_seconds);
- StringAppendF(&report, "Total %25.4f\n\n",
+ StringAppendF(&report, "Total %25.6f\n\n",
total_time_in_seconds);
StringAppendF(&report, "Termination: %25s (%s)\n",
diff --git a/extern/ceres/internal/ceres/solver_utils.cc b/extern/ceres/internal/ceres/solver_utils.cc
index 7f4ff7eb940..177a928e090 100644
--- a/extern/ceres/internal/ceres/solver_utils.cc
+++ b/extern/ceres/internal/ceres/solver_utils.cc
@@ -30,6 +30,8 @@
#include <string>
+#include "ceres/internal/config.h"
+
#include "Eigen/Core"
#include "ceres/internal/port.h"
#include "ceres/solver_utils.h"
@@ -61,6 +63,10 @@ std::string VersionString() {
value += "-cxsparse-(" + std::string(CERES_CXSPARSE_VERSION) + ")";
#endif
+#ifndef CERES_NO_ACCELERATE_SPARSE
+ value += "-acceleratesparse";
+#endif
+
#ifdef CERES_USE_EIGEN_SPARSE
value += "-eigensparse";
#endif
diff --git a/extern/ceres/internal/ceres/sparse_cholesky.cc b/extern/ceres/internal/ceres/sparse_cholesky.cc
new file mode 100644
index 00000000000..d9d2100d3f9
--- /dev/null
+++ b/extern/ceres/internal/ceres/sparse_cholesky.cc
@@ -0,0 +1,163 @@
+// 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)
+
+#include "ceres/sparse_cholesky.h"
+
+#include "ceres/accelerate_sparse.h"
+#include "ceres/cxsparse.h"
+#include "ceres/eigensparse.h"
+#include "ceres/float_cxsparse.h"
+#include "ceres/float_suitesparse.h"
+#include "ceres/iterative_refiner.h"
+#include "ceres/suitesparse.h"
+
+namespace ceres {
+namespace internal {
+
+std::unique_ptr<SparseCholesky> SparseCholesky::Create(
+ const LinearSolver::Options& options) {
+ const OrderingType ordering_type = options.use_postordering ? AMD : NATURAL;
+ std::unique_ptr<SparseCholesky> sparse_cholesky;
+
+ switch (options.sparse_linear_algebra_library_type) {
+ case SUITE_SPARSE:
+#ifndef CERES_NO_SUITESPARSE
+ if (options.use_mixed_precision_solves) {
+ sparse_cholesky = FloatSuiteSparseCholesky::Create(ordering_type);
+ } else {
+ sparse_cholesky = SuiteSparseCholesky::Create(ordering_type);
+ }
+ break;
+#else
+ (void)ordering_type;
+ LOG(FATAL) << "Ceres was compiled without support for SuiteSparse.";
+#endif
+
+ case EIGEN_SPARSE:
+#ifdef CERES_USE_EIGEN_SPARSE
+ if (options.use_mixed_precision_solves) {
+ sparse_cholesky = FloatEigenSparseCholesky::Create(ordering_type);
+ } else {
+ sparse_cholesky = EigenSparseCholesky::Create(ordering_type);
+ }
+ break;
+#else
+ LOG(FATAL) << "Ceres was compiled without support for "
+ << "Eigen's sparse Cholesky factorization routines.";
+#endif
+
+ case CX_SPARSE:
+#ifndef CERES_NO_CXSPARSE
+ if (options.use_mixed_precision_solves) {
+ sparse_cholesky = FloatCXSparseCholesky::Create(ordering_type);
+ } else {
+ sparse_cholesky = CXSparseCholesky::Create(ordering_type);
+ }
+ break;
+#else
+ LOG(FATAL) << "Ceres was compiled without support for CXSparse.";
+#endif
+
+ case ACCELERATE_SPARSE:
+#ifndef CERES_NO_ACCELERATE_SPARSE
+ if (options.use_mixed_precision_solves) {
+ sparse_cholesky = AppleAccelerateCholesky<float>::Create(ordering_type);
+ } else {
+ sparse_cholesky = AppleAccelerateCholesky<double>::Create(ordering_type);
+ }
+ break;
+#else
+ LOG(FATAL) << "Ceres was compiled without support for Apple's Accelerate "
+ << "framework solvers.";
+#endif
+
+ default:
+ LOG(FATAL) << "Unknown sparse linear algebra library type : "
+ << SparseLinearAlgebraLibraryTypeToString(
+ options.sparse_linear_algebra_library_type);
+ }
+
+ if (options.max_num_refinement_iterations > 0) {
+ std::unique_ptr<IterativeRefiner> refiner(
+ new IterativeRefiner(options.max_num_refinement_iterations));
+ sparse_cholesky = std::unique_ptr<SparseCholesky>(new RefinedSparseCholesky(
+ std::move(sparse_cholesky), std::move(refiner)));
+ }
+ return sparse_cholesky;
+}
+
+SparseCholesky::~SparseCholesky() {}
+
+LinearSolverTerminationType SparseCholesky::FactorAndSolve(
+ CompressedRowSparseMatrix* lhs,
+ const double* rhs,
+ double* solution,
+ std::string* message) {
+ LinearSolverTerminationType termination_type = Factorize(lhs, message);
+ if (termination_type == LINEAR_SOLVER_SUCCESS) {
+ termination_type = Solve(rhs, solution, message);
+ }
+ return termination_type;
+}
+
+RefinedSparseCholesky::RefinedSparseCholesky(
+ std::unique_ptr<SparseCholesky> sparse_cholesky,
+ std::unique_ptr<IterativeRefiner> iterative_refiner)
+ : sparse_cholesky_(std::move(sparse_cholesky)),
+ iterative_refiner_(std::move(iterative_refiner)) {}
+
+RefinedSparseCholesky::~RefinedSparseCholesky() {}
+
+CompressedRowSparseMatrix::StorageType RefinedSparseCholesky::StorageType()
+ const {
+ return sparse_cholesky_->StorageType();
+}
+
+LinearSolverTerminationType RefinedSparseCholesky::Factorize(
+ CompressedRowSparseMatrix* lhs, std::string* message) {
+ lhs_ = lhs;
+ return sparse_cholesky_->Factorize(lhs, message);
+}
+
+LinearSolverTerminationType RefinedSparseCholesky::Solve(const double* rhs,
+ double* solution,
+ std::string* message) {
+ CHECK(lhs_ != nullptr);
+ auto termination_type = sparse_cholesky_->Solve(rhs, solution, message);
+ if (termination_type != LINEAR_SOLVER_SUCCESS) {
+ return termination_type;
+ }
+
+ iterative_refiner_->Refine(*lhs_, rhs, sparse_cholesky_.get(), solution);
+ return LINEAR_SOLVER_SUCCESS;
+}
+
+} // namespace internal
+} // namespace ceres
diff --git a/extern/ceres/internal/ceres/sparse_cholesky.h b/extern/ceres/internal/ceres/sparse_cholesky.h
new file mode 100644
index 00000000000..bbe42370505
--- /dev/null
+++ b/extern/ceres/internal/ceres/sparse_cholesky.h
@@ -0,0 +1,138 @@
+// 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)
+
+#ifndef CERES_INTERNAL_SPARSE_CHOLESKY_H_
+#define CERES_INTERNAL_SPARSE_CHOLESKY_H_
+
+// This include must come before any #ifndef check on Ceres compile options.
+#include "ceres/internal/port.h"
+
+#include <memory>
+#include "ceres/linear_solver.h"
+#include "glog/logging.h"
+
+namespace ceres {
+namespace internal {
+
+// An interface that abstracts away the internal details of various
+// sparse linear algebra libraries and offers a simple API for solving
+// symmetric positive definite linear systems using a sparse Cholesky
+// factorization.
+//
+// Instances of SparseCholesky are expected to cache the symbolic
+// factorization of the linear system. They do this on the first call
+// to Factorize or FactorAndSolve. Subsequent calls to Factorize and
+// FactorAndSolve are expected to have the same sparsity structure.
+//
+// Example usage:
+//
+// std::unique_ptr<SparseCholesky>
+// sparse_cholesky(SparseCholesky::Create(SUITE_SPARSE, AMD));
+//
+// CompressedRowSparseMatrix lhs = ...;
+// std::string message;
+// CHECK_EQ(sparse_cholesky->Factorize(&lhs, &message), LINEAR_SOLVER_SUCCESS);
+// Vector rhs = ...;
+// Vector solution = ...;
+// CHECK_EQ(sparse_cholesky->Solve(rhs.data(), solution.data(), &message),
+// LINEAR_SOLVER_SUCCESS);
+
+class SparseCholesky {
+ public:
+ static std::unique_ptr<SparseCholesky> Create(
+ const LinearSolver::Options& options);
+
+ virtual ~SparseCholesky();
+
+ // Due to the symmetry of the linear system, sparse linear algebra
+ // libraries only use one half of the input matrix. Whether it is
+ // the upper or the lower triangular part of the matrix depends on
+ // the library and the re-ordering strategy being used. This
+ // function tells the user the storage type expected of the input
+ // matrix for the sparse linear algebra library and reordering
+ // strategy used.
+ virtual CompressedRowSparseMatrix::StorageType StorageType() const = 0;
+
+ // Computes the numeric factorization of the given matrix. If this
+ // is the first call to Factorize, first the symbolic factorization
+ // will be computed and cached and the numeric factorization will be
+ // computed based on that.
+ //
+ // Subsequent calls to Factorize will use that symbolic
+ // factorization assuming that the sparsity of the matrix has
+ // remained constant.
+ virtual LinearSolverTerminationType Factorize(
+ CompressedRowSparseMatrix* lhs, std::string* message) = 0;
+
+ // Computes the solution to the equation
+ //
+ // lhs * solution = rhs
+ 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.
+ virtual 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 RefinedSparseCholesky : public SparseCholesky {
+ public:
+ RefinedSparseCholesky(std::unique_ptr<SparseCholesky> sparse_cholesky,
+ std::unique_ptr<IterativeRefiner> iterative_refiner);
+ virtual ~RefinedSparseCholesky();
+
+ virtual CompressedRowSparseMatrix::StorageType StorageType() const;
+ virtual LinearSolverTerminationType Factorize(
+ CompressedRowSparseMatrix* lhs, std::string* message);
+ virtual LinearSolverTerminationType Solve(const double* rhs,
+ double* solution,
+ std::string* message);
+
+ private:
+ std::unique_ptr<SparseCholesky> sparse_cholesky_;
+ std::unique_ptr<IterativeRefiner> iterative_refiner_;
+ CompressedRowSparseMatrix* lhs_ = nullptr;
+};
+
+} // namespace internal
+} // namespace ceres
+
+#endif // CERES_INTERNAL_SPARSE_CHOLESKY_H_
diff --git a/extern/ceres/internal/ceres/sparse_matrix.h b/extern/ceres/internal/ceres/sparse_matrix.h
index b3af1d06440..074d847807e 100644
--- a/extern/ceres/internal/ceres/sparse_matrix.h
+++ b/extern/ceres/internal/ceres/sparse_matrix.h
@@ -90,7 +90,7 @@ class SparseMatrix : public LinearOperator {
virtual void ToTextFile(FILE* file) const = 0;
// Accessors for the values array that stores the entries of the
- // sparse matrix. The exact interpreptation of the values of this
+ // sparse matrix. The exact interpretation of the values of this
// array depends on the particular kind of SparseMatrix being
// accessed.
virtual double* mutable_values() = 0;
diff --git a/extern/ceres/internal/ceres/sparse_normal_cholesky_solver.cc b/extern/ceres/internal/ceres/sparse_normal_cholesky_solver.cc
index a4c2c766ddc..0f2e589d041 100644
--- a/extern/ceres/internal/ceres/sparse_normal_cholesky_solver.cc
+++ b/extern/ceres/internal/ceres/sparse_normal_cholesky_solver.cc
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -33,461 +33,82 @@
#include <algorithm>
#include <cstring>
#include <ctime>
-#include <sstream>
+#include <memory>
-#include "ceres/compressed_row_sparse_matrix.h"
-#include "ceres/cxsparse.h"
+#include "ceres/block_sparse_matrix.h"
+#include "ceres/inner_product_computer.h"
#include "ceres/internal/eigen.h"
-#include "ceres/internal/scoped_ptr.h"
+#include "ceres/iterative_refiner.h"
#include "ceres/linear_solver.h"
-#include "ceres/suitesparse.h"
+#include "ceres/sparse_cholesky.h"
#include "ceres/triplet_sparse_matrix.h"
#include "ceres/types.h"
#include "ceres/wall_time.h"
-#include "Eigen/SparseCore"
-
-#ifdef CERES_USE_EIGEN_SPARSE
-#include "Eigen/SparseCholesky"
-#endif
namespace ceres {
namespace internal {
-namespace {
-
-#ifdef CERES_USE_EIGEN_SPARSE
-// A templated factorized and solve function, which allows us to use
-// the same code independent of whether a AMD or a Natural ordering is
-// used.
-template <typename SimplicialCholeskySolver, typename SparseMatrixType>
-LinearSolver::Summary SimplicialLDLTSolve(
- const SparseMatrixType& lhs,
- const bool do_symbolic_analysis,
- SimplicialCholeskySolver* solver,
- double* rhs_and_solution,
- EventLogger* event_logger) {
- LinearSolver::Summary summary;
- summary.num_iterations = 1;
- summary.termination_type = LINEAR_SOLVER_SUCCESS;
- summary.message = "Success.";
-
- if (do_symbolic_analysis) {
- solver->analyzePattern(lhs);
- if (VLOG_IS_ON(2)) {
- std::stringstream ss;
- solver->dumpMemory(ss);
- VLOG(2) << "Symbolic Analysis\n"
- << ss.str();
- }
- event_logger->AddEvent("Analyze");
- if (solver->info() != Eigen::Success) {
- summary.termination_type = LINEAR_SOLVER_FATAL_ERROR;
- summary.message =
- "Eigen failure. Unable to find symbolic factorization.";
- return summary;
- }
- }
-
- solver->factorize(lhs);
- event_logger->AddEvent("Factorize");
- if (solver->info() != Eigen::Success) {
- summary.termination_type = LINEAR_SOLVER_FAILURE;
- summary.message = "Eigen failure. Unable to find numeric factorization.";
- return summary;
- }
-
- const Vector rhs = VectorRef(rhs_and_solution, lhs.cols());
-
- VectorRef(rhs_and_solution, lhs.cols()) = solver->solve(rhs);
- event_logger->AddEvent("Solve");
- if (solver->info() != Eigen::Success) {
- summary.termination_type = LINEAR_SOLVER_FAILURE;
- summary.message = "Eigen failure. Unable to do triangular solve.";
- return summary;
- }
-
- return summary;
-}
-
-#endif // CERES_USE_EIGEN_SPARSE
-
-#ifndef CERES_NO_CXSPARSE
-LinearSolver::Summary ComputeNormalEquationsAndSolveUsingCXSparse(
- CompressedRowSparseMatrix* A,
- double * rhs_and_solution,
- EventLogger* event_logger) {
- LinearSolver::Summary summary;
- summary.num_iterations = 1;
- summary.termination_type = LINEAR_SOLVER_SUCCESS;
- summary.message = "Success.";
-
- CXSparse cxsparse;
-
- // Wrap the augmented Jacobian in a compressed sparse column matrix.
- cs_di a_transpose = cxsparse.CreateSparseMatrixTransposeView(A);
-
- // Compute the normal equations. J'J delta = J'f and solve them
- // using a sparse Cholesky factorization. Notice that when compared
- // to SuiteSparse we have to explicitly compute the transpose of Jt,
- // and then the normal equations before they can be
- // factorized. CHOLMOD/SuiteSparse on the other hand can just work
- // off of Jt to compute the Cholesky factorization of the normal
- // equations.
- cs_di* a = cxsparse.TransposeMatrix(&a_transpose);
- cs_di* lhs = cxsparse.MatrixMatrixMultiply(&a_transpose, a);
- cxsparse.Free(a);
- event_logger->AddEvent("NormalEquations");
-
- cs_dis* factor = cxsparse.AnalyzeCholesky(lhs);
- event_logger->AddEvent("Analysis");
-
- if (factor == NULL) {
- summary.termination_type = LINEAR_SOLVER_FATAL_ERROR;
- summary.message = "CXSparse::AnalyzeCholesky failed.";
- } else if (!cxsparse.SolveCholesky(lhs, factor, rhs_and_solution)) {
- summary.termination_type = LINEAR_SOLVER_FAILURE;
- summary.message = "CXSparse::SolveCholesky failed.";
- }
- event_logger->AddEvent("Solve");
-
- cxsparse.Free(lhs);
- cxsparse.Free(factor);
- event_logger->AddEvent("TearDown");
- return summary;
-}
-
-#endif // CERES_NO_CXSPARSE
-
-} // namespace
SparseNormalCholeskySolver::SparseNormalCholeskySolver(
const LinearSolver::Options& options)
- : factor_(NULL),
- cxsparse_factor_(NULL),
- options_(options) {
+ : options_(options) {
+ sparse_cholesky_ = SparseCholesky::Create(options);
}
-void SparseNormalCholeskySolver::FreeFactorization() {
- if (factor_ != NULL) {
- ss_.Free(factor_);
- factor_ = NULL;
- }
-
- if (cxsparse_factor_ != NULL) {
- cxsparse_.Free(cxsparse_factor_);
- cxsparse_factor_ = NULL;
- }
-}
-
-SparseNormalCholeskySolver::~SparseNormalCholeskySolver() {
- FreeFactorization();
-}
+SparseNormalCholeskySolver::~SparseNormalCholeskySolver() {}
LinearSolver::Summary SparseNormalCholeskySolver::SolveImpl(
- CompressedRowSparseMatrix* A,
+ BlockSparseMatrix* A,
const double* b,
const LinearSolver::PerSolveOptions& per_solve_options,
- double * x) {
-
- const int num_cols = A->num_cols();
- VectorRef(x, num_cols).setZero();
- A->LeftMultiply(b, x);
-
- 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.
- scoped_ptr<CompressedRowSparseMatrix> regularizer;
- if (A->col_blocks().size() > 0) {
- regularizer.reset(CompressedRowSparseMatrix::CreateBlockDiagonalMatrix(
- per_solve_options.D, A->col_blocks()));
- } else {
- regularizer.reset(new CompressedRowSparseMatrix(
- per_solve_options.D, num_cols));
- }
- A->AppendRows(*regularizer);
- }
-
- LinearSolver::Summary summary;
- switch (options_.sparse_linear_algebra_library_type) {
- case SUITE_SPARSE:
- summary = SolveImplUsingSuiteSparse(A, x);
- break;
- case CX_SPARSE:
- summary = SolveImplUsingCXSparse(A, x);
- break;
- case EIGEN_SPARSE:
- summary = SolveImplUsingEigen(A, x);
- break;
- default:
- LOG(FATAL) << "Unknown sparse linear algebra library : "
- << options_.sparse_linear_algebra_library_type;
- }
-
- if (per_solve_options.D != NULL) {
- A->DeleteRows(num_cols);
- }
-
- return summary;
-}
-
-LinearSolver::Summary SparseNormalCholeskySolver::SolveImplUsingEigen(
- CompressedRowSparseMatrix* A,
- double * rhs_and_solution) {
-#ifndef CERES_USE_EIGEN_SPARSE
-
- LinearSolver::Summary summary;
- summary.num_iterations = 0;
- summary.termination_type = LINEAR_SOLVER_FATAL_ERROR;
- summary.message =
- "SPARSE_NORMAL_CHOLESKY cannot be used with EIGEN_SPARSE "
- "because Ceres was not built with support for "
- "Eigen's SimplicialLDLT decomposition. "
- "This requires enabling building with -DEIGENSPARSE=ON.";
- return summary;
-
-#else
-
- EventLogger event_logger("SparseNormalCholeskySolver::Eigen::Solve");
- // Compute the normal equations. J'J delta = J'f and solve them
- // using a sparse Cholesky factorization. Notice that when compared
- // to SuiteSparse we have to explicitly compute the normal equations
- // before they can be factorized. CHOLMOD/SuiteSparse on the other
- // hand can just work off of Jt to compute the Cholesky
- // factorization of the normal equations.
-
- if (options_.dynamic_sparsity) {
- // In the case where the problem has dynamic sparsity, it is not
- // worth using the ComputeOuterProduct routine, as the setup cost
- // is not amortized over multiple calls to 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::SparseMatrix<double> lhs = a.transpose() * a;
- Eigen::SimplicialLDLT<Eigen::SparseMatrix<double> > solver;
- return SimplicialLDLTSolve(lhs,
- true,
- &solver,
- rhs_and_solution,
- &event_logger);
- }
-
- if (outer_product_.get() == NULL) {
- outer_product_.reset(
- CompressedRowSparseMatrix::CreateOuterProductMatrixAndProgram(
- *A, &pattern_));
- }
-
- CompressedRowSparseMatrix::ComputeOuterProduct(
- *A, pattern_, outer_product_.get());
-
- // Map to an upper triangular column major matrix.
- //
- // outer_product_ is a compressed row sparse matrix and in lower
- // triangular form, when mapped to a compressed column sparse
- // matrix, it becomes an upper triangular matrix.
- Eigen::MappedSparseMatrix<double, Eigen::ColMajor> lhs(
- outer_product_->num_rows(),
- outer_product_->num_rows(),
- outer_product_->num_nonzeros(),
- outer_product_->mutable_rows(),
- outer_product_->mutable_cols(),
- outer_product_->mutable_values());
-
- bool do_symbolic_analysis = false;
-
- // If using post ordering or an old version of Eigen, we cannot
- // depend on a preordered jacobian, so we work with a SimplicialLDLT
- // decomposition with AMD ordering.
- if (options_.use_postordering ||
- !EIGEN_VERSION_AT_LEAST(3, 2, 2)) {
- if (amd_ldlt_.get() == NULL) {
- amd_ldlt_.reset(new SimplicialLDLTWithAMDOrdering);
- do_symbolic_analysis = true;
- }
-
- return SimplicialLDLTSolve(lhs,
- do_symbolic_analysis,
- amd_ldlt_.get(),
- rhs_and_solution,
- &event_logger);
- }
-
-#if EIGEN_VERSION_AT_LEAST(3,2,2)
- // The common case
- if (natural_ldlt_.get() == NULL) {
- natural_ldlt_.reset(new SimplicialLDLTWithNaturalOrdering);
- do_symbolic_analysis = true;
- }
-
- return SimplicialLDLTSolve(lhs,
- do_symbolic_analysis,
- natural_ldlt_.get(),
- rhs_and_solution,
- &event_logger);
-#endif
-
-#endif // EIGEN_USE_EIGEN_SPARSE
-}
-
-LinearSolver::Summary SparseNormalCholeskySolver::SolveImplUsingCXSparse(
- CompressedRowSparseMatrix* A,
- double * rhs_and_solution) {
-#ifdef CERES_NO_CXSPARSE
-
- LinearSolver::Summary summary;
- summary.num_iterations = 0;
- summary.termination_type = LINEAR_SOLVER_FATAL_ERROR;
- summary.message =
- "SPARSE_NORMAL_CHOLESKY cannot be used with CX_SPARSE "
- "because Ceres was not built with support for CXSparse. "
- "This requires enabling building with -DCXSPARSE=ON.";
-
- return summary;
-
-#else
-
- EventLogger event_logger("SparseNormalCholeskySolver::CXSparse::Solve");
- if (options_.dynamic_sparsity) {
- return ComputeNormalEquationsAndSolveUsingCXSparse(A,
- rhs_and_solution,
- &event_logger);
- }
-
+ double* x) {
+ EventLogger event_logger("SparseNormalCholeskySolver::Solve");
LinearSolver::Summary summary;
summary.num_iterations = 1;
summary.termination_type = LINEAR_SOLVER_SUCCESS;
summary.message = "Success.";
- // Compute the normal equations. J'J delta = J'f and solve them
- // using a sparse Cholesky factorization. Notice that when compared
- // to SuiteSparse we have to explicitly compute the normal equations
- // before they can be factorized. CHOLMOD/SuiteSparse on the other
- // hand can just work off of Jt to compute the Cholesky
- // factorization of the normal equations.
- if (outer_product_.get() == NULL) {
- outer_product_.reset(
- CompressedRowSparseMatrix::CreateOuterProductMatrixAndProgram(
- *A, &pattern_));
- }
-
- CompressedRowSparseMatrix::ComputeOuterProduct(
- *A, pattern_, outer_product_.get());
- cs_di lhs =
- cxsparse_.CreateSparseMatrixTransposeView(outer_product_.get());
-
- event_logger.AddEvent("Setup");
-
- // Compute symbolic factorization if not available.
- if (cxsparse_factor_ == NULL) {
- if (options_.use_postordering) {
- cxsparse_factor_ = cxsparse_.BlockAnalyzeCholesky(&lhs,
- A->col_blocks(),
- A->col_blocks());
- } else {
- cxsparse_factor_ = cxsparse_.AnalyzeCholeskyWithNaturalOrdering(&lhs);
- }
- }
- event_logger.AddEvent("Analysis");
-
- if (cxsparse_factor_ == NULL) {
- summary.termination_type = LINEAR_SOLVER_FATAL_ERROR;
- summary.message =
- "CXSparse failure. Unable to find symbolic factorization.";
- } else if (!cxsparse_.SolveCholesky(&lhs,
- cxsparse_factor_,
- rhs_and_solution)) {
- summary.termination_type = LINEAR_SOLVER_FAILURE;
- summary.message = "CXSparse::SolveCholesky failed.";
- }
- event_logger.AddEvent("Solve");
-
- return summary;
-#endif
-}
-
-LinearSolver::Summary SparseNormalCholeskySolver::SolveImplUsingSuiteSparse(
- CompressedRowSparseMatrix* A,
- double * rhs_and_solution) {
-#ifdef CERES_NO_SUITESPARSE
-
- LinearSolver::Summary summary;
- summary.num_iterations = 0;
- summary.termination_type = LINEAR_SOLVER_FATAL_ERROR;
- summary.message =
- "SPARSE_NORMAL_CHOLESKY cannot be used with SUITE_SPARSE "
- "because Ceres was not built with support for SuiteSparse. "
- "This requires enabling building with -DSUITESPARSE=ON.";
- return summary;
-
-#else
-
- EventLogger event_logger("SparseNormalCholeskySolver::SuiteSparse::Solve");
- LinearSolver::Summary summary;
- summary.termination_type = LINEAR_SOLVER_SUCCESS;
- summary.num_iterations = 1;
- summary.message = "Success.";
-
const int num_cols = A->num_cols();
- cholmod_sparse lhs = ss_.CreateSparseMatrixTransposeView(A);
- event_logger.AddEvent("Setup");
-
- if (options_.dynamic_sparsity) {
- FreeFactorization();
- }
+ VectorRef xref(x, num_cols);
+ xref.setZero();
+ rhs_.resize(num_cols);
+ rhs_.setZero();
+ A->LeftMultiply(b, rhs_.data());
+ event_logger.AddEvent("Compute RHS");
- if (factor_ == NULL) {
- if (options_.use_postordering) {
- factor_ = ss_.BlockAnalyzeCholesky(&lhs,
- A->col_blocks(),
- A->row_blocks(),
- &summary.message);
- } else {
- if (options_.dynamic_sparsity) {
- factor_ = ss_.AnalyzeCholesky(&lhs, &summary.message);
- } else {
- factor_ = ss_.AnalyzeCholeskyWithNaturalOrdering(&lhs,
- &summary.message);
- }
- }
+ 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.
+ std::unique_ptr<BlockSparseMatrix> regularizer;
+ regularizer.reset(BlockSparseMatrix::CreateDiagonalMatrix(
+ per_solve_options.D, A->block_structure()->cols));
+ event_logger.AddEvent("Diagonal");
+ A->AppendRows(*regularizer);
+ event_logger.AddEvent("Append");
}
- event_logger.AddEvent("Analysis");
+ event_logger.AddEvent("Append Rows");
- if (factor_ == NULL) {
- summary.termination_type = LINEAR_SOLVER_FATAL_ERROR;
- // No need to set message as it has already been set by the
- // symbolic analysis routines above.
- return summary;
- }
+ if (inner_product_computer_.get() == NULL) {
+ inner_product_computer_.reset(
+ InnerProductComputer::Create(*A, sparse_cholesky_->StorageType()));
- summary.termination_type = ss_.Cholesky(&lhs, factor_, &summary.message);
- if (summary.termination_type != LINEAR_SOLVER_SUCCESS) {
- return summary;
+ event_logger.AddEvent("InnerProductComputer::Create");
}
- cholmod_dense* rhs = ss_.CreateDenseVector(rhs_and_solution,
- num_cols,
- num_cols);
- cholmod_dense* solution = ss_.Solve(factor_, rhs, &summary.message);
- event_logger.AddEvent("Solve");
+ inner_product_computer_->Compute();
+ event_logger.AddEvent("InnerProductComputer::Compute");
- ss_.Free(rhs);
- if (solution != NULL) {
- memcpy(rhs_and_solution, solution->x, num_cols * sizeof(*rhs_and_solution));
- ss_.Free(solution);
- } else {
- // No need to set message as it has already been set by the
- // numeric factorization routine above.
- summary.termination_type = LINEAR_SOLVER_FAILURE;
+ if (per_solve_options.D != NULL) {
+ A->DeleteRowBlocks(A->block_structure()->cols.size());
}
- event_logger.AddEvent("Teardown");
+ summary.termination_type = sparse_cholesky_->FactorAndSolve(
+ inner_product_computer_->mutable_result(),
+ rhs_.data(),
+ x,
+ &summary.message);
+ event_logger.AddEvent("SparseCholesky::FactorAndSolve");
return summary;
-#endif
}
-} // namespace internal
-} // namespace ceres
+} // namespace internal
+} // namespace ceres
diff --git a/extern/ceres/internal/ceres/sparse_normal_cholesky_solver.h b/extern/ceres/internal/ceres/sparse_normal_cholesky_solver.h
index 2a93bc56d29..cbff2bdb3f6 100644
--- a/extern/ceres/internal/ceres/sparse_normal_cholesky_solver.h
+++ b/extern/ceres/internal/ceres/sparse_normal_cholesky_solver.h
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -34,91 +34,40 @@
#ifndef CERES_INTERNAL_SPARSE_NORMAL_CHOLESKY_SOLVER_H_
#define CERES_INTERNAL_SPARSE_NORMAL_CHOLESKY_SOLVER_H_
-#include <vector>
-
// This include must come before any #ifndef check on Ceres compile options.
#include "ceres/internal/port.h"
-#include "ceres/internal/macros.h"
+#include <vector>
#include "ceres/linear_solver.h"
-#include "ceres/suitesparse.h"
-#include "ceres/cxsparse.h"
-
-#ifdef CERES_USE_EIGEN_SPARSE
-#include "Eigen/SparseCholesky"
-#endif
namespace ceres {
namespace internal {
class CompressedRowSparseMatrix;
+class InnerProductComputer;
+class SparseCholesky;
-// Solves the normal equations (A'A + D'D) x = A'b, using the CHOLMOD sparse
-// cholesky solver.
-class SparseNormalCholeskySolver : public CompressedRowSparseMatrixSolver {
+// 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 {
public:
explicit SparseNormalCholeskySolver(const LinearSolver::Options& options);
+ SparseNormalCholeskySolver(const SparseNormalCholeskySolver&) = delete;
+ void operator=(const SparseNormalCholeskySolver&) = delete;
+
virtual ~SparseNormalCholeskySolver();
private:
- virtual LinearSolver::Summary SolveImpl(
- CompressedRowSparseMatrix* A,
+ LinearSolver::Summary SolveImpl(
+ BlockSparseMatrix* A,
const double* b,
const LinearSolver::PerSolveOptions& options,
- double* x);
-
- LinearSolver::Summary SolveImplUsingSuiteSparse(
- CompressedRowSparseMatrix* A,
- double* rhs_and_solution);
-
-
- LinearSolver::Summary SolveImplUsingCXSparse(
- CompressedRowSparseMatrix* A,
- double* rhs_and_solution);
-
- LinearSolver::Summary SolveImplUsingEigen(
- CompressedRowSparseMatrix* A,
- double* rhs_and_solution);
-
- void FreeFactorization();
-
- SuiteSparse ss_;
- // Cached factorization
- cholmod_factor* factor_;
-
- CXSparse cxsparse_;
- // Cached factorization
- cs_dis* cxsparse_factor_;
-
-#ifdef CERES_USE_EIGEN_SPARSE
-
- // The preprocessor gymnastics here are dealing with the fact that
- // before version 3.2.2, Eigen did not support a third template
- // parameter to specify the ordering.
-#if EIGEN_VERSION_AT_LEAST(3,2,2)
- typedef Eigen::SimplicialLDLT<Eigen::SparseMatrix<double>, Eigen::Upper,
- Eigen::NaturalOrdering<int> >
- SimplicialLDLTWithNaturalOrdering;
- scoped_ptr<SimplicialLDLTWithNaturalOrdering> natural_ldlt_;
-
- typedef Eigen::SimplicialLDLT<Eigen::SparseMatrix<double>, Eigen::Upper,
- Eigen::AMDOrdering<int> >
- SimplicialLDLTWithAMDOrdering;
- scoped_ptr<SimplicialLDLTWithAMDOrdering> amd_ldlt_;
-
-#else
- typedef Eigen::SimplicialLDLT<Eigen::SparseMatrix<double>, Eigen::Upper>
- SimplicialLDLTWithAMDOrdering;
-
- scoped_ptr<SimplicialLDLTWithAMDOrdering> amd_ldlt_;
-#endif
-
-#endif
+ double* x) final;
- scoped_ptr<CompressedRowSparseMatrix> outer_product_;
- std::vector<int> pattern_;
const LinearSolver::Options options_;
- CERES_DISALLOW_COPY_AND_ASSIGN(SparseNormalCholeskySolver);
+ Vector rhs_;
+ std::unique_ptr<SparseCholesky> sparse_cholesky_;
+ std::unique_ptr<InnerProductComputer> inner_product_computer_;
};
} // namespace internal
diff --git a/extern/ceres/internal/ceres/split.cc b/extern/ceres/internal/ceres/split.cc
index 296c09a6440..3a09e866839 100644
--- a/extern/ceres/internal/ceres/split.cc
+++ b/extern/ceres/internal/ceres/split.cc
@@ -115,7 +115,7 @@ 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);
+ std::back_insert_iterator<vector<string>> it(*result);
SplitStringToIteratorUsing(full, delim, it);
}
diff --git a/extern/ceres/internal/ceres/stringprintf.cc b/extern/ceres/internal/ceres/stringprintf.cc
index b3b7474d8f8..7a21f0e2118 100644
--- a/extern/ceres/internal/ceres/stringprintf.cc
+++ b/extern/ceres/internal/ceres/stringprintf.cc
@@ -43,28 +43,6 @@ namespace internal {
using std::string;
-// va_copy() was defined in the C99 standard. However, it did not appear in the
-// C++ standard until C++11. This means that if Ceres is being compiled with a
-// strict pre-C++11 standard (e.g. -std=c++03), va_copy() will NOT be defined,
-// as we are using the C++ compiler (it would however be defined if we were
-// using the C compiler). Note however that both GCC & Clang will in fact
-// define va_copy() when compiling for C++ if the C++ standard is not explicitly
-// specified (i.e. no -std=c++<XX> arg), even though it should not strictly be
-// defined unless -std=c++11 (or greater) was passed.
-#if !defined(va_copy)
-#if defined (__GNUC__)
-// On GCC/Clang, if va_copy() is not defined (C++ standard < C++11 explicitly
-// specified), use the internal __va_copy() version, which should be present
-// in even very old GCC versions.
-#define va_copy(d, s) __va_copy(d, s)
-#else
-// Some older versions of MSVC do not have va_copy(), in which case define it.
-// Although this is required for older MSVC versions, it should also work for
-// other non-GCC/Clang compilers which also do not defined va_copy().
-#define va_copy(d, s) ((d) = (s))
-#endif // defined (__GNUC__)
-#endif // !defined(va_copy)
-
void StringAppendV(string* dst, const char* format, va_list ap) {
// First try with a small fixed size buffer
char space[1024];
diff --git a/extern/ceres/internal/ceres/subset_preconditioner.cc b/extern/ceres/internal/ceres/subset_preconditioner.cc
new file mode 100644
index 00000000000..7c24ae9f288
--- /dev/null
+++ b/extern/ceres/internal/ceres/subset_preconditioner.cc
@@ -0,0 +1,117 @@
+// 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)
+
+#include "ceres/subset_preconditioner.h"
+
+#include <memory>
+#include <string>
+#include "ceres/compressed_row_sparse_matrix.h"
+#include "ceres/inner_product_computer.h"
+#include "ceres/linear_solver.h"
+#include "ceres/sparse_cholesky.h"
+#include "ceres/types.h"
+
+namespace ceres {
+namespace internal {
+
+SubsetPreconditioner::SubsetPreconditioner(
+ const Preconditioner::Options& options, const BlockSparseMatrix& A)
+ : options_(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.";
+
+ LinearSolver::Options sparse_cholesky_options;
+ sparse_cholesky_options.sparse_linear_algebra_library_type =
+ options_.sparse_linear_algebra_library_type;
+ sparse_cholesky_options.use_postordering =
+ options_.use_postordering;
+ sparse_cholesky_ = SparseCholesky::Create(sparse_cholesky_options);
+}
+
+SubsetPreconditioner::~SubsetPreconditioner() {}
+
+void SubsetPreconditioner::RightMultiply(const double* x, double* y) const {
+ CHECK(x != nullptr);
+ CHECK(y != nullptr);
+ std::string message;
+ sparse_cholesky_->Solve(x, y, &message);
+}
+
+bool SubsetPreconditioner::UpdateImpl(const BlockSparseMatrix& A,
+ const double* D) {
+ BlockSparseMatrix* m = const_cast<BlockSparseMatrix*>(&A);
+ const CompressedRowBlockStructure* bs = m->block_structure();
+
+ // A = [P]
+ // [Q]
+
+ // Now add D to A if needed.
+ if (D != NULL) {
+ // A = [P]
+ // [Q]
+ // [D]
+ std::unique_ptr<BlockSparseMatrix> regularizer(
+ BlockSparseMatrix::CreateDiagonalMatrix(D, bs->cols));
+ m->AppendRows(*regularizer);
+ }
+
+ if (inner_product_computer_.get() == NULL) {
+ inner_product_computer_.reset(InnerProductComputer::Create(
+ *m,
+ options_.subset_preconditioner_start_row_block,
+ bs->rows.size(),
+ sparse_cholesky_->StorageType()));
+ }
+
+ // Compute inner_product = [Q'*Q + D'*D]
+ inner_product_computer_->Compute();
+
+ // Unappend D if needed.
+ if (D != NULL) {
+ // A = [P]
+ // [Q]
+ m->DeleteRowBlocks(bs->cols.size());
+ }
+
+ std::string message;
+ // Compute L. s.t., LL' = Q'*Q + D'*D
+ const LinearSolverTerminationType termination_type =
+ sparse_cholesky_->Factorize(inner_product_computer_->mutable_result(),
+ &message);
+ if (termination_type != LINEAR_SOLVER_SUCCESS) {
+ LOG(ERROR) << "Preconditioner factorization failed: " << message;
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace internal
+} // namespace ceres
diff --git a/extern/ceres/internal/ceres/subset_preconditioner.h b/extern/ceres/internal/ceres/subset_preconditioner.h
new file mode 100644
index 00000000000..6f3c9ecd052
--- /dev/null
+++ b/extern/ceres/internal/ceres/subset_preconditioner.h
@@ -0,0 +1,91 @@
+// 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)
+
+#ifndef CERES_INTERNAL_SUBSET_PRECONDITIONER_H_
+#define CERES_INTERNAL_SUBSET_PRECONDITIONER_H_
+
+#include <memory>
+#include "ceres/preconditioner.h"
+
+namespace ceres {
+namespace internal {
+
+class BlockSparseMatrix;
+class SparseCholesky;
+class InnerProductComputer;
+
+// Subset preconditioning, uses a subset of the rows of the Jacobian
+// to construct a preconditioner for the normal equations.
+//
+// To keep the interface simple, we assume that the matrix A has
+// already been re-ordered that the user wishes to some subset of the
+// bottom row blocks of the matrix as the preconditioner. This is
+// controlled by
+// Preconditioner::Options::subset_preconditioner_start_row_block.
+//
+// When using the subset preconditioner, all row blocks starting
+// from this row block are used to construct the preconditioner.
+//
+// More precisely the matrix A is horizontally partitioned as
+//
+// A = [P]
+// [Q]
+//
+// where P has subset_preconditioner_start_row_block row blocks,
+// and the preconditioner is the inverse of the matrix Q'Q.
+//
+// Obviously, the smaller this number, the more accurate and
+// computationally expensive this preconditioner will be.
+//
+// See the tests for example usage.
+class SubsetPreconditioner : public BlockSparseMatrixPreconditioner {
+ public:
+ SubsetPreconditioner(const Preconditioner::Options& options,
+ const BlockSparseMatrix& A);
+ virtual ~SubsetPreconditioner();
+
+ // Preconditioner interface
+ void RightMultiply(const double* x, double* y) const final;
+ int num_rows() const final { return num_cols_; }
+ int num_cols() const final { return num_cols_; }
+
+ private:
+ bool UpdateImpl(const BlockSparseMatrix& A, const double* D) final;
+
+ const Preconditioner::Options options_;
+ const int num_cols_;
+ std::unique_ptr<SparseCholesky> sparse_cholesky_;
+ std::unique_ptr<InnerProductComputer> inner_product_computer_;
+};
+
+} // namespace internal
+} // namespace ceres
+
+#endif // CERES_INTERNAL_SUBSET_PRECONDITIONER_H_
diff --git a/extern/ceres/internal/ceres/suitesparse.cc b/extern/ceres/internal/ceres/suitesparse.cc
new file mode 100644
index 00000000000..190d1755add
--- /dev/null
+++ b/extern/ceres/internal/ceres/suitesparse.cc
@@ -0,0 +1,430 @@
+// 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)
+
+// This include must come before any #ifndef check on Ceres compile options.
+#include "ceres/internal/port.h"
+
+#ifndef CERES_NO_SUITESPARSE
+#include "ceres/suitesparse.h"
+
+#include <vector>
+
+#include "ceres/compressed_col_sparse_matrix_utils.h"
+#include "ceres/compressed_row_sparse_matrix.h"
+#include "ceres/linear_solver.h"
+#include "ceres/triplet_sparse_matrix.h"
+#include "cholmod.h"
+
+namespace ceres {
+namespace internal {
+
+using std::string;
+using std::vector;
+
+SuiteSparse::SuiteSparse() { cholmod_start(&cc_); }
+
+SuiteSparse::~SuiteSparse() { cholmod_finish(&cc_); }
+
+cholmod_sparse* SuiteSparse::CreateSparseMatrix(TripletSparseMatrix* A) {
+ cholmod_triplet triplet;
+
+ triplet.nrow = A->num_rows();
+ triplet.ncol = A->num_cols();
+ triplet.nzmax = A->max_num_nonzeros();
+ triplet.nnz = A->num_nonzeros();
+ triplet.i = reinterpret_cast<void*>(A->mutable_rows());
+ triplet.j = reinterpret_cast<void*>(A->mutable_cols());
+ triplet.x = reinterpret_cast<void*>(A->mutable_values());
+ triplet.stype = 0; // Matrix is not symmetric.
+ triplet.itype = CHOLMOD_INT;
+ triplet.xtype = CHOLMOD_REAL;
+ triplet.dtype = CHOLMOD_DOUBLE;
+
+ return cholmod_triplet_to_sparse(&triplet, triplet.nnz, &cc_);
+}
+
+cholmod_sparse* SuiteSparse::CreateSparseMatrixTranspose(
+ TripletSparseMatrix* A) {
+ cholmod_triplet triplet;
+
+ triplet.ncol = A->num_rows(); // swap row and columns
+ triplet.nrow = A->num_cols();
+ triplet.nzmax = A->max_num_nonzeros();
+ triplet.nnz = A->num_nonzeros();
+
+ // swap rows and columns
+ triplet.j = reinterpret_cast<void*>(A->mutable_rows());
+ triplet.i = reinterpret_cast<void*>(A->mutable_cols());
+ triplet.x = reinterpret_cast<void*>(A->mutable_values());
+ triplet.stype = 0; // Matrix is not symmetric.
+ triplet.itype = CHOLMOD_INT;
+ triplet.xtype = CHOLMOD_REAL;
+ triplet.dtype = CHOLMOD_DOUBLE;
+
+ return cholmod_triplet_to_sparse(&triplet, triplet.nnz, &cc_);
+}
+
+cholmod_sparse SuiteSparse::CreateSparseMatrixTransposeView(
+ CompressedRowSparseMatrix* A) {
+ cholmod_sparse m;
+ m.nrow = A->num_cols();
+ m.ncol = A->num_rows();
+ m.nzmax = A->num_nonzeros();
+ m.nz = nullptr;
+ m.p = reinterpret_cast<void*>(A->mutable_rows());
+ m.i = reinterpret_cast<void*>(A->mutable_cols());
+ m.x = reinterpret_cast<void*>(A->mutable_values());
+ m.z = nullptr;
+
+ if (A->storage_type() == CompressedRowSparseMatrix::LOWER_TRIANGULAR) {
+ m.stype = 1;
+ } else if (A->storage_type() == CompressedRowSparseMatrix::UPPER_TRIANGULAR) {
+ m.stype = -1;
+ } else {
+ m.stype = 0;
+ }
+
+ m.itype = CHOLMOD_INT;
+ m.xtype = CHOLMOD_REAL;
+ m.dtype = CHOLMOD_DOUBLE;
+ m.sorted = 1;
+ m.packed = 1;
+
+ return m;
+}
+
+cholmod_dense SuiteSparse::CreateDenseVectorView(const double* x, int size) {
+ cholmod_dense v;
+ v.nrow = size;
+ v.ncol = 1;
+ v.nzmax = size;
+ v.d = size;
+ v.x = const_cast<void*>(reinterpret_cast<const void*>(x));
+ v.xtype = CHOLMOD_REAL;
+ v.dtype = CHOLMOD_DOUBLE;
+ return v;
+}
+
+cholmod_dense* SuiteSparse::CreateDenseVector(const double* x,
+ int in_size,
+ int out_size) {
+ CHECK_LE(in_size, out_size);
+ cholmod_dense* v = cholmod_zeros(out_size, 1, CHOLMOD_REAL, &cc_);
+ if (x != nullptr) {
+ memcpy(v->x, x, in_size * sizeof(*x));
+ }
+ return v;
+}
+
+cholmod_factor* SuiteSparse::AnalyzeCholesky(cholmod_sparse* A,
+ string* message) {
+ // Cholmod can try multiple re-ordering strategies to find a fill
+ // reducing ordering. Here we just tell it use AMD with automatic
+ // matrix dependence choice of supernodal versus simplicial
+ // factorization.
+ cc_.nmethods = 1;
+ cc_.method[0].ordering = CHOLMOD_AMD;
+ cc_.supernodal = CHOLMOD_AUTO;
+
+ cholmod_factor* factor = cholmod_analyze(A, &cc_);
+ if (VLOG_IS_ON(2)) {
+ cholmod_print_common(const_cast<char*>("Symbolic Analysis"), &cc_);
+ }
+
+ if (cc_.status != CHOLMOD_OK) {
+ *message =
+ StringPrintf("cholmod_analyze failed. error code: %d", cc_.status);
+ return nullptr;
+ }
+
+ CHECK(factor != nullptr);
+ return factor;
+}
+
+cholmod_factor* SuiteSparse::BlockAnalyzeCholesky(cholmod_sparse* A,
+ const vector<int>& row_blocks,
+ const vector<int>& col_blocks,
+ string* message) {
+ vector<int> ordering;
+ if (!BlockAMDOrdering(A, row_blocks, col_blocks, &ordering)) {
+ return nullptr;
+ }
+ return AnalyzeCholeskyWithUserOrdering(A, ordering, message);
+}
+
+cholmod_factor* SuiteSparse::AnalyzeCholeskyWithUserOrdering(
+ cholmod_sparse* A, const vector<int>& ordering, string* message) {
+ CHECK_EQ(ordering.size(), A->nrow);
+
+ cc_.nmethods = 1;
+ cc_.method[0].ordering = CHOLMOD_GIVEN;
+
+ cholmod_factor* factor =
+ cholmod_analyze_p(A, const_cast<int*>(&ordering[0]), nullptr, 0, &cc_);
+ if (VLOG_IS_ON(2)) {
+ cholmod_print_common(const_cast<char*>("Symbolic Analysis"), &cc_);
+ }
+ if (cc_.status != CHOLMOD_OK) {
+ *message =
+ StringPrintf("cholmod_analyze failed. error code: %d", cc_.status);
+ return nullptr;
+ }
+
+ CHECK(factor != nullptr);
+ return factor;
+}
+
+cholmod_factor* SuiteSparse::AnalyzeCholeskyWithNaturalOrdering(
+ cholmod_sparse* A, string* message) {
+ cc_.nmethods = 1;
+ cc_.method[0].ordering = CHOLMOD_NATURAL;
+ cc_.postorder = 0;
+
+ cholmod_factor* factor = cholmod_analyze(A, &cc_);
+ if (VLOG_IS_ON(2)) {
+ cholmod_print_common(const_cast<char*>("Symbolic Analysis"), &cc_);
+ }
+ if (cc_.status != CHOLMOD_OK) {
+ *message =
+ StringPrintf("cholmod_analyze failed. error code: %d", cc_.status);
+ return nullptr;
+ }
+
+ CHECK(factor != nullptr);
+ return factor;
+}
+
+bool SuiteSparse::BlockAMDOrdering(const cholmod_sparse* A,
+ const vector<int>& row_blocks,
+ const vector<int>& col_blocks,
+ vector<int>* ordering) {
+ const int num_row_blocks = row_blocks.size();
+ const int num_col_blocks = col_blocks.size();
+
+ // Arrays storing the compressed column structure of the matrix
+ // incoding the block sparsity of A.
+ vector<int> block_cols;
+ vector<int> block_rows;
+
+ CompressedColumnScalarMatrixToBlockMatrix(reinterpret_cast<const int*>(A->i),
+ reinterpret_cast<const int*>(A->p),
+ row_blocks,
+ col_blocks,
+ &block_rows,
+ &block_cols);
+ cholmod_sparse_struct block_matrix;
+ block_matrix.nrow = num_row_blocks;
+ block_matrix.ncol = num_col_blocks;
+ block_matrix.nzmax = block_rows.size();
+ block_matrix.p = reinterpret_cast<void*>(&block_cols[0]);
+ block_matrix.i = reinterpret_cast<void*>(&block_rows[0]);
+ block_matrix.x = nullptr;
+ block_matrix.stype = A->stype;
+ block_matrix.itype = CHOLMOD_INT;
+ block_matrix.xtype = CHOLMOD_PATTERN;
+ block_matrix.dtype = CHOLMOD_DOUBLE;
+ block_matrix.sorted = 1;
+ block_matrix.packed = 1;
+
+ vector<int> block_ordering(num_row_blocks);
+ if (!cholmod_amd(&block_matrix, nullptr, 0, &block_ordering[0], &cc_)) {
+ return false;
+ }
+
+ BlockOrderingToScalarOrdering(row_blocks, block_ordering, ordering);
+ return true;
+}
+
+LinearSolverTerminationType SuiteSparse::Cholesky(cholmod_sparse* A,
+ cholmod_factor* L,
+ string* message) {
+ CHECK(A != nullptr);
+ CHECK(L != nullptr);
+
+ // Save the current print level and silence CHOLMOD, otherwise
+ // CHOLMOD is prone to dumping stuff to stderr, which can be
+ // distracting when the error (matrix is indefinite) is not a fatal
+ // failure.
+ const int old_print_level = cc_.print;
+ cc_.print = 0;
+
+ cc_.quick_return_if_not_posdef = 1;
+ int cholmod_status = cholmod_factorize(A, L, &cc_);
+ cc_.print = old_print_level;
+
+ switch (cc_.status) {
+ case CHOLMOD_NOT_INSTALLED:
+ *message = "CHOLMOD failure: Method not installed.";
+ return LINEAR_SOLVER_FATAL_ERROR;
+ case CHOLMOD_OUT_OF_MEMORY:
+ *message = "CHOLMOD failure: Out of memory.";
+ return LINEAR_SOLVER_FATAL_ERROR;
+ case CHOLMOD_TOO_LARGE:
+ *message = "CHOLMOD failure: Integer overflow occurred.";
+ return LINEAR_SOLVER_FATAL_ERROR;
+ case CHOLMOD_INVALID:
+ *message = "CHOLMOD failure: Invalid input.";
+ return LINEAR_SOLVER_FATAL_ERROR;
+ case CHOLMOD_NOT_POSDEF:
+ *message = "CHOLMOD warning: Matrix not positive definite.";
+ return LINEAR_SOLVER_FAILURE;
+ case CHOLMOD_DSMALL:
+ *message =
+ "CHOLMOD warning: D for LDL' or diag(L) or "
+ "LL' has tiny absolute value.";
+ return LINEAR_SOLVER_FAILURE;
+ case CHOLMOD_OK:
+ if (cholmod_status != 0) {
+ return LINEAR_SOLVER_SUCCESS;
+ }
+
+ *message =
+ "CHOLMOD failure: cholmod_factorize returned false "
+ "but cholmod_common::status is CHOLMOD_OK."
+ "Please report this to ceres-solver@googlegroups.com.";
+ return LINEAR_SOLVER_FATAL_ERROR;
+ default:
+ *message = StringPrintf(
+ "Unknown cholmod return code: %d. "
+ "Please report this to ceres-solver@googlegroups.com.",
+ cc_.status);
+ return LINEAR_SOLVER_FATAL_ERROR;
+ }
+
+ return LINEAR_SOLVER_FATAL_ERROR;
+}
+
+cholmod_dense* SuiteSparse::Solve(cholmod_factor* L,
+ cholmod_dense* b,
+ string* message) {
+ if (cc_.status != CHOLMOD_OK) {
+ *message = "cholmod_solve failed. CHOLMOD status is not CHOLMOD_OK";
+ return nullptr;
+ }
+
+ return cholmod_solve(CHOLMOD_A, L, b, &cc_);
+}
+
+bool SuiteSparse::ApproximateMinimumDegreeOrdering(cholmod_sparse* matrix,
+ int* ordering) {
+ return cholmod_amd(matrix, nullptr, 0, ordering, &cc_);
+}
+
+bool SuiteSparse::ConstrainedApproximateMinimumDegreeOrdering(
+ cholmod_sparse* matrix, int* constraints, int* ordering) {
+#ifndef CERES_NO_CAMD
+ return cholmod_camd(matrix, nullptr, 0, constraints, ordering, &cc_);
+#else
+ LOG(FATAL) << "Congratulations you have found a bug in Ceres."
+ << "Ceres Solver was compiled with SuiteSparse "
+ << "version 4.1.0 or less. Calling this function "
+ << "in that case is a bug. Please contact the"
+ << "the Ceres Solver developers.";
+ return false;
+#endif
+}
+
+std::unique_ptr<SparseCholesky> SuiteSparseCholesky::Create(
+ const OrderingType ordering_type) {
+ return std::unique_ptr<SparseCholesky>(new SuiteSparseCholesky(ordering_type));
+}
+
+SuiteSparseCholesky::SuiteSparseCholesky(const OrderingType ordering_type)
+ : ordering_type_(ordering_type), factor_(nullptr) {}
+
+SuiteSparseCholesky::~SuiteSparseCholesky() {
+ if (factor_ != nullptr) {
+ ss_.Free(factor_);
+ }
+}
+
+LinearSolverTerminationType SuiteSparseCholesky::Factorize(
+ CompressedRowSparseMatrix* lhs, string* message) {
+ if (lhs == nullptr) {
+ *message = "Failure: Input lhs is NULL.";
+ return LINEAR_SOLVER_FATAL_ERROR;
+ }
+
+ cholmod_sparse cholmod_lhs = ss_.CreateSparseMatrixTransposeView(lhs);
+
+ if (factor_ == nullptr) {
+ if (ordering_type_ == NATURAL) {
+ factor_ = ss_.AnalyzeCholeskyWithNaturalOrdering(&cholmod_lhs, message);
+ } else {
+ if (!lhs->col_blocks().empty() && !(lhs->row_blocks().empty())) {
+ factor_ = ss_.BlockAnalyzeCholesky(
+ &cholmod_lhs, lhs->col_blocks(), lhs->row_blocks(), message);
+ } else {
+ factor_ = ss_.AnalyzeCholesky(&cholmod_lhs, message);
+ }
+ }
+
+ if (factor_ == nullptr) {
+ return LINEAR_SOLVER_FATAL_ERROR;
+ }
+ }
+
+ return ss_.Cholesky(&cholmod_lhs, factor_, message);
+}
+
+CompressedRowSparseMatrix::StorageType SuiteSparseCholesky::StorageType()
+ const {
+ return ((ordering_type_ == NATURAL)
+ ? CompressedRowSparseMatrix::UPPER_TRIANGULAR
+ : CompressedRowSparseMatrix::LOWER_TRIANGULAR);
+}
+
+LinearSolverTerminationType SuiteSparseCholesky::Solve(const double* rhs,
+ double* solution,
+ string* message) {
+ // Error checking
+ if (factor_ == nullptr) {
+ *message = "Solve called without a call to Factorize first.";
+ return LINEAR_SOLVER_FATAL_ERROR;
+ }
+
+ const int num_cols = factor_->n;
+ cholmod_dense cholmod_rhs = ss_.CreateDenseVectorView(rhs, num_cols);
+ cholmod_dense* cholmod_dense_solution =
+ ss_.Solve(factor_, &cholmod_rhs, message);
+
+ if (cholmod_dense_solution == nullptr) {
+ return LINEAR_SOLVER_FAILURE;
+ }
+
+ memcpy(solution, cholmod_dense_solution->x, num_cols * sizeof(*solution));
+ ss_.Free(cholmod_dense_solution);
+ return LINEAR_SOLVER_SUCCESS;
+}
+
+} // namespace internal
+} // namespace ceres
+
+#endif // CERES_NO_SUITESPARSE
diff --git a/extern/ceres/internal/ceres/suitesparse.h b/extern/ceres/internal/ceres/suitesparse.h
index 380d76e003a..b77b296a66e 100644
--- a/extern/ceres/internal/ceres/suitesparse.h
+++ b/extern/ceres/internal/ceres/suitesparse.h
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -41,11 +41,11 @@
#include <cstring>
#include <string>
#include <vector>
-
+#include "SuiteSparseQR.hpp"
#include "ceres/linear_solver.h"
+#include "ceres/sparse_cholesky.h"
#include "cholmod.h"
#include "glog/logging.h"
-#include "SuiteSparseQR.hpp"
// Before SuiteSparse version 4.2.0, cholmod_camd was only enabled
// if SuiteSparse was compiled with Metis support. This makes
@@ -99,6 +99,11 @@ class SuiteSparse {
// use the SuiteSparse machinery to allocate memory.
cholmod_sparse CreateSparseMatrixTransposeView(CompressedRowSparseMatrix* A);
+ // Create a cholmod_dense vector around the contents of the array x.
+ // This is a shallow object, which refers to the contents of x and
+ // does not use the SuiteSparse machinery to allocate memory.
+ 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
// an all zeros vector is returned. Caller owns the result.
@@ -197,12 +202,12 @@ class SuiteSparse {
// doing sparse direct factorization of these matrices the
// fill-reducing ordering algorithms (in particular AMD) can either
// be run on the block or the scalar form of these matrices. The two
- // SuiteSparse::AnalyzeCholesky methods allows the the client to
+ // SuiteSparse::AnalyzeCholesky methods allows the client to
// compute the symbolic factorization of a matrix by either using
// AMD on the matrix or a user provided ordering of the rows.
//
// But since the underlying matrices are block oriented, it is worth
- // running AMD on just the block structre of these matrices and then
+ // running AMD on just the block structure of these matrices and then
// lifting these block orderings to a full scalar ordering. This
// preserves the block structure of the permuted matrix, and exposes
// more of the super-nodal structure of the matrix to the numerical
@@ -278,6 +283,27 @@ class SuiteSparse {
cholmod_common cc_;
};
+class SuiteSparseCholesky : public SparseCholesky {
+ public:
+ static std::unique_ptr<SparseCholesky> Create(
+ OrderingType ordering_type);
+
+ // SparseCholesky interface.
+ virtual ~SuiteSparseCholesky();
+ CompressedRowSparseMatrix::StorageType StorageType() const final;
+ LinearSolverTerminationType Factorize(
+ CompressedRowSparseMatrix* lhs, std::string* message) final;
+ LinearSolverTerminationType Solve(const double* rhs,
+ double* solution,
+ std::string* message) final;
+ private:
+ SuiteSparseCholesky(const OrderingType ordering_type);
+
+ const OrderingType ordering_type_;
+ SuiteSparse ss_;
+ cholmod_factor* factor_;
+};
+
} // namespace internal
} // namespace ceres
@@ -285,6 +311,9 @@ class SuiteSparse {
typedef void cholmod_factor;
+namespace ceres {
+namespace internal {
+
class SuiteSparse {
public:
// Defining this static function even when SuiteSparse is not
@@ -292,7 +321,7 @@ class SuiteSparse {
// without checking for the absence of the CERES_NO_CAMD symbol.
//
// This is safer because the symbol maybe missing due to a user
- // accidently not including suitesparse.h in their code when
+ // accidentally not including suitesparse.h in their code when
// checking for the symbol.
static bool IsConstrainedApproximateMinimumDegreeOrderingAvailable() {
return false;
@@ -301,6 +330,9 @@ class SuiteSparse {
void Free(void* arg) {}
};
+} // namespace internal
+} // namespace ceres
+
#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
new file mode 100644
index 00000000000..5a52c9d06a6
--- /dev/null
+++ b/extern/ceres/internal/ceres/thread_pool.cc
@@ -0,0 +1,116 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2018 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 (Michael Vitus)
+
+// This include must come before any #ifndef check on Ceres compile options.
+#include "ceres/internal/port.h"
+
+#ifdef CERES_USE_CXX_THREADS
+
+#include "ceres/thread_pool.h"
+
+#include <cmath>
+#include <limits>
+
+namespace ceres {
+namespace internal {
+namespace {
+
+// Constrain the total number of threads to the amount the hardware can support.
+int GetNumAllowedThreads(int requested_num_threads) {
+ return std::min(requested_num_threads, ThreadPool::MaxNumThreadsAvailable());
+}
+
+} // namespace
+
+int ThreadPool::MaxNumThreadsAvailable() {
+ const int num_hardware_threads = std::thread::hardware_concurrency();
+ // hardware_concurrency() can return 0 if the value is not well defined or not
+ // computable.
+ return num_hardware_threads == 0
+ ? std::numeric_limits<int>::max()
+ : num_hardware_threads;
+}
+
+ThreadPool::ThreadPool() { }
+
+ThreadPool::ThreadPool(int num_threads) {
+ Resize(num_threads);
+}
+
+ThreadPool::~ThreadPool() {
+ std::lock_guard<std::mutex> lock(thread_pool_mutex_);
+ // Signal the thread workers to stop and wait for them to finish all scheduled
+ // tasks.
+ Stop();
+ for (std::thread& thread : thread_pool_) {
+ thread.join();
+ }
+}
+
+void ThreadPool::Resize(int num_threads) {
+ std::lock_guard<std::mutex> lock(thread_pool_mutex_);
+
+ const int num_current_threads = thread_pool_.size();
+ if (num_current_threads >= num_threads) {
+ return;
+ }
+
+ const int create_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));
+ }
+}
+
+void ThreadPool::AddTask(const std::function<void()>& func) {
+ task_queue_.Push(func);
+}
+
+int ThreadPool::Size() {
+ std::lock_guard<std::mutex> lock(thread_pool_mutex_);
+ return thread_pool_.size();
+}
+
+void ThreadPool::ThreadMainLoop() {
+ std::function<void()> task;
+ while (task_queue_.Wait(&task)) {
+ task();
+ }
+}
+
+void ThreadPool::Stop() {
+ task_queue_.StopWaiters();
+}
+
+} // namespace internal
+} // namespace ceres
+
+#endif // CERES_USE_CXX_THREADS
diff --git a/extern/ceres/internal/ceres/thread_pool.h b/extern/ceres/internal/ceres/thread_pool.h
new file mode 100644
index 00000000000..1ebb52eb6b4
--- /dev/null
+++ b/extern/ceres/internal/ceres/thread_pool.h
@@ -0,0 +1,120 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2018 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 (Michael Vitus)
+
+#ifndef CERES_INTERNAL_THREAD_POOL_H_
+#define CERES_INTERNAL_THREAD_POOL_H_
+
+#include <functional>
+#include <mutex>
+#include <thread>
+#include <vector>
+
+#include "ceres/concurrent_queue.h"
+
+namespace ceres {
+namespace internal {
+
+// A thread-safe thread pool with an unbounded task queue and a resizable number
+// of workers. The size of the thread pool can be increased but never decreased
+// in order to support the largest number of threads requested. The ThreadPool
+// has three states:
+//
+// (1) The thread pool size is zero. Tasks may be added to the thread pool via
+// AddTask but they will not be executed until the thread pool is resized.
+//
+// (2) The thread pool size is greater than zero. Tasks may be added to the
+// thread pool and will be executed as soon as a worker is available. The
+// thread pool may be resized while the thread pool is running.
+//
+// (3) The thread pool is destructing. The thread pool will signal all the
+// workers to stop. The workers will finish all of the tasks that have already
+// been added to the thread pool.
+//
+class ThreadPool {
+ public:
+ // Returns the maximum number of hardware threads.
+ static int MaxNumThreadsAvailable();
+
+ // Default constructor with no active threads. We allow instantiating a
+ // thread pool with no threads to support the use case of single threaded
+ // Ceres where everything will be executed on the main thread. For single
+ // threaded execution this has two benefits: avoid any overhead as threads
+ // are expensive to create, and no unused threads shown in the debugger.
+ ThreadPool();
+
+ // Instantiates a thread pool with min(MaxNumThreadsAvailable, num_threads)
+ // number of threads.
+ explicit ThreadPool(int num_threads);
+
+ // Signals the workers to stop and waits for them to finish any tasks that
+ // have been scheduled.
+ ~ThreadPool();
+
+ // Resizes the thread pool if it is currently less than the requested number
+ // of threads. The thread pool will be resized to min(MaxNumThreadsAvailable,
+ // num_threads) number of threads. Resize does not support reducing the
+ // thread pool size. If a smaller number of threads is requested, the thread
+ // pool remains the same size. The thread pool is reused within Ceres with
+ // different number of threads, and we need to ensure we can support the
+ // largest number of threads requested. It is safe to resize the thread pool
+ // while the workers are executing tasks, and the resizing is guaranteed to
+ // complete upon return.
+ void Resize(int num_threads);
+
+ // Adds a task to the queue and wakes up a blocked thread. If the thread pool
+ // size is greater than zero, then the task will be executed by a currently
+ // idle thread or when a thread becomes available. If the thread pool has no
+ // threads, then the task will never be executed and the user should use
+ // Resize() to create a non-empty thread pool.
+ void AddTask(const std::function<void()>& func);
+
+ // Returns the current size of the thread pool.
+ int Size();
+
+ private:
+ // Main loop for the threads which blocks on the task queue until work becomes
+ // available. It will return if and only if Stop has been called.
+ void ThreadMainLoop();
+
+ // Signal all the threads to stop. It does not block until the threads are
+ // finished.
+ void Stop();
+
+ // The queue that stores the units of work available for the thread pool. The
+ // task queue maintains its own thread safety.
+ ConcurrentQueue<std::function<void()>> task_queue_;
+ std::vector<std::thread> thread_pool_;
+ std::mutex thread_pool_mutex_;
+};
+
+} // namespace internal
+} // namespace ceres
+
+#endif // CERES_INTERNAL_THREAD_POOL_H_
diff --git a/extern/ceres/internal/ceres/thread_token_provider.cc b/extern/ceres/internal/ceres/thread_token_provider.cc
new file mode 100644
index 00000000000..b04cf844488
--- /dev/null
+++ b/extern/ceres/internal/ceres/thread_token_provider.cc
@@ -0,0 +1,76 @@
+// 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: yp@photonscore.de (Yury Prokazov)
+
+#include "ceres/thread_token_provider.h"
+
+#ifdef CERES_USE_OPENMP
+#include <omp.h>
+#endif
+
+namespace ceres {
+namespace internal {
+
+ThreadTokenProvider::ThreadTokenProvider(int num_threads) {
+ (void)num_threads;
+#ifdef CERES_USE_CXX_THREADS
+ for (int i = 0; i < num_threads; i++) {
+ pool_.Push(i);
+ }
+#endif
+
+}
+
+int ThreadTokenProvider::Acquire() {
+#ifdef CERES_USE_OPENMP
+ return omp_get_thread_num();
+#endif
+
+#ifdef CERES_NO_THREADS
+ return 0;
+#endif
+
+#ifdef CERES_USE_CXX_THREADS
+ int thread_id;
+ CHECK(pool_.Wait(&thread_id));
+ return thread_id;
+#endif
+
+}
+
+void ThreadTokenProvider::Release(int thread_id) {
+ (void)thread_id;
+#ifdef CERES_USE_CXX_THREADS
+ pool_.Push(thread_id);
+#endif
+
+}
+
+} // namespace internal
+} // namespace ceres
diff --git a/extern/ceres/internal/ceres/thread_token_provider.h b/extern/ceres/internal/ceres/thread_token_provider.h
new file mode 100644
index 00000000000..06dc0438572
--- /dev/null
+++ b/extern/ceres/internal/ceres/thread_token_provider.h
@@ -0,0 +1,97 @@
+// 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: yp@photonscore.de (Yury Prokazov)
+
+#ifndef CERES_INTERNAL_THREAD_TOKEN_PROVIDER_H_
+#define CERES_INTERNAL_THREAD_TOKEN_PROVIDER_H_
+
+#include "ceres/internal/config.h"
+#include "ceres/internal/port.h"
+
+#ifdef CERES_USE_CXX_THREADS
+#include "ceres/concurrent_queue.h"
+#endif
+
+namespace ceres {
+namespace internal {
+
+// Helper for C++ thread number identification that is similar to
+// omp_get_thread_num() behaviour. This is necessary to support C++
+// threading with a sequential thread id. This is used to access preallocated
+// resources in the parallelized code parts. The sequence of tokens varies from
+// 0 to num_threads - 1 that can be acquired to identify the thread in a thread
+// pool.
+//
+// If CERES_NO_THREADS is defined, Acquire() always returns 0 and Release()
+// takes no action.
+//
+// If CERES_USE_OPENMP, omp_get_thread_num() is used to Acquire() with no action
+// in Release()
+//
+//
+// Example usage pseudocode:
+//
+// ThreadTokenProvider ttp(N); // allocate N tokens
+// Spawn N threads {
+// int token = ttp.Acquire(); // get unique token
+// ...
+// ... use token to access resources bound to the thread
+// ...
+// ttp.Release(token); // return token to the pool
+// }
+//
+class ThreadTokenProvider {
+ public:
+ ThreadTokenProvider(int num_threads);
+
+ // Returns the first token from the queue. The acquired value must be
+ // given back by Release().
+ int Acquire();
+
+ // Makes previously acquired token available for other threads.
+ void Release(int thread_id);
+
+ private:
+#ifdef CERES_USE_CXX_THREADS
+ // This queue initially holds a sequence from 0..num_threads-1. Every
+ // Acquire() call the first number is removed from here. When the token is not
+ // needed anymore it shall be given back with corresponding Release()
+ // call. This concurrent queue is more expensive than TBB's version, so you
+ // should not acquire the thread ID on every for loop iteration.
+ ConcurrentQueue<int> pool_;
+#endif
+
+ ThreadTokenProvider(ThreadTokenProvider&);
+ ThreadTokenProvider& operator=(ThreadTokenProvider&);
+};
+
+} // namespace internal
+} // namespace ceres
+
+#endif // CERES_INTERNAL_THREAD_TOKEN_PROVIDER_H_
diff --git a/extern/ceres/internal/ceres/triplet_sparse_matrix.cc b/extern/ceres/internal/ceres/triplet_sparse_matrix.cc
index 8df405ca115..54b588ba466 100644
--- a/extern/ceres/internal/ceres/triplet_sparse_matrix.cc
+++ b/extern/ceres/internal/ceres/triplet_sparse_matrix.cc
@@ -32,9 +32,10 @@
#include <algorithm>
#include <cstddef>
+
#include "ceres/internal/eigen.h"
#include "ceres/internal/port.h"
-#include "ceres/internal/scoped_ptr.h"
+#include "ceres/random.h"
#include "ceres/types.h"
#include "glog/logging.h"
@@ -45,10 +46,8 @@ TripletSparseMatrix::TripletSparseMatrix()
: num_rows_(0),
num_cols_(0),
max_num_nonzeros_(0),
- num_nonzeros_(0),
- rows_(NULL),
- cols_(NULL),
- values_(NULL) {}
+ num_nonzeros_(0) {}
+
TripletSparseMatrix::~TripletSparseMatrix() {}
@@ -58,10 +57,7 @@ TripletSparseMatrix::TripletSparseMatrix(int num_rows,
: num_rows_(num_rows),
num_cols_(num_cols),
max_num_nonzeros_(max_num_nonzeros),
- num_nonzeros_(0),
- rows_(NULL),
- cols_(NULL),
- values_(NULL) {
+ num_nonzeros_(0) {
// All the sizes should at least be zero
CHECK_GE(num_rows, 0);
CHECK_GE(num_cols, 0);
@@ -69,21 +65,41 @@ TripletSparseMatrix::TripletSparseMatrix(int num_rows,
AllocateMemory();
}
+TripletSparseMatrix::TripletSparseMatrix(const int num_rows,
+ const int num_cols,
+ const std::vector<int>& rows,
+ const std::vector<int>& cols,
+ const std::vector<double>& values)
+ : num_rows_(num_rows),
+ num_cols_(num_cols),
+ max_num_nonzeros_(values.size()),
+ num_nonzeros_(values.size()) {
+ // All the sizes should at least be zero
+ CHECK_GE(num_rows, 0);
+ CHECK_GE(num_cols, 0);
+ CHECK_EQ(rows.size(), cols.size());
+ CHECK_EQ(rows.size(), values.size());
+ AllocateMemory();
+ std::copy(rows.begin(), rows.end(), rows_.get());
+ std::copy(cols.begin(), cols.end(), cols_.get());
+ std::copy(values.begin(), values.end(), values_.get());
+}
+
TripletSparseMatrix::TripletSparseMatrix(const TripletSparseMatrix& orig)
: SparseMatrix(),
num_rows_(orig.num_rows_),
num_cols_(orig.num_cols_),
max_num_nonzeros_(orig.max_num_nonzeros_),
- num_nonzeros_(orig.num_nonzeros_),
- rows_(NULL),
- cols_(NULL),
- values_(NULL) {
+ num_nonzeros_(orig.num_nonzeros_) {
AllocateMemory();
CopyData(orig);
}
TripletSparseMatrix& TripletSparseMatrix::operator=(
const TripletSparseMatrix& rhs) {
+ if (this == &rhs) {
+ return *this;
+ }
num_rows_ = rhs.num_rows_;
num_cols_ = rhs.num_cols_;
num_nonzeros_ = rhs.num_nonzeros_;
@@ -165,7 +181,7 @@ void TripletSparseMatrix::LeftMultiply(const double* x, double* y) const {
}
void TripletSparseMatrix::SquaredColumnNorm(double* x) const {
- CHECK_NOTNULL(x);
+ CHECK(x != nullptr);
VectorRef(x, num_cols_).setZero();
for (int i = 0; i < num_nonzeros_; ++i) {
x[cols_[i]] += values_[i] * values_[i];
@@ -173,7 +189,7 @@ void TripletSparseMatrix::SquaredColumnNorm(double* x) const {
}
void TripletSparseMatrix::ScaleColumns(const double* scale) {
- CHECK_NOTNULL(scale);
+ CHECK(scale != nullptr);
for (int i = 0; i < num_nonzeros_; ++i) {
values_[i] = values_[i] * scale[cols_[i]];
}
@@ -254,11 +270,40 @@ TripletSparseMatrix* TripletSparseMatrix::CreateSparseDiagonalMatrix(
}
void TripletSparseMatrix::ToTextFile(FILE* file) const {
- CHECK_NOTNULL(file);
+ CHECK(file != nullptr);
for (int i = 0; i < num_nonzeros_; ++i) {
fprintf(file, "% 10d % 10d %17f\n", rows_[i], cols_[i], values_[i]);
}
}
+TripletSparseMatrix* TripletSparseMatrix::CreateRandomMatrix(
+ const TripletSparseMatrix::RandomMatrixOptions& options) {
+ CHECK_GT(options.num_rows, 0);
+ CHECK_GT(options.num_cols, 0);
+ CHECK_GT(options.density, 0.0);
+ CHECK_LE(options.density, 1.0);
+
+ std::vector<int> rows;
+ std::vector<int> cols;
+ std::vector<double> values;
+ while (rows.empty()) {
+ rows.clear();
+ cols.clear();
+ values.clear();
+ for (int r = 0; r < options.num_rows; ++r) {
+ for (int c = 0; c < options.num_cols; ++c) {
+ if (RandDouble() <= options.density) {
+ rows.push_back(r);
+ cols.push_back(c);
+ values.push_back(RandNormal());
+ }
+ }
+ }
+ }
+
+ return new TripletSparseMatrix(
+ options.num_rows, options.num_cols, rows, cols, values);
+}
+
} // namespace internal
} // namespace ceres
diff --git a/extern/ceres/internal/ceres/triplet_sparse_matrix.h b/extern/ceres/internal/ceres/triplet_sparse_matrix.h
index f3f5370df6f..2ee0fa992b5 100644
--- a/extern/ceres/internal/ceres/triplet_sparse_matrix.h
+++ b/extern/ceres/internal/ceres/triplet_sparse_matrix.h
@@ -31,9 +31,10 @@
#ifndef CERES_INTERNAL_TRIPLET_SPARSE_MATRIX_H_
#define CERES_INTERNAL_TRIPLET_SPARSE_MATRIX_H_
+#include <memory>
+#include <vector>
#include "ceres/sparse_matrix.h"
#include "ceres/internal/eigen.h"
-#include "ceres/internal/scoped_ptr.h"
#include "ceres/types.h"
namespace ceres {
@@ -47,26 +48,32 @@ class TripletSparseMatrix : public SparseMatrix {
public:
TripletSparseMatrix();
TripletSparseMatrix(int num_rows, int num_cols, int max_num_nonzeros);
+ TripletSparseMatrix(int num_rows,
+ int num_cols,
+ const std::vector<int>& rows,
+ const std::vector<int>& cols,
+ const std::vector<double>& values);
+
explicit TripletSparseMatrix(const TripletSparseMatrix& orig);
TripletSparseMatrix& operator=(const TripletSparseMatrix& rhs);
- ~TripletSparseMatrix();
+ virtual ~TripletSparseMatrix();
// Implementation of the SparseMatrix interface.
- virtual void SetZero();
- virtual void RightMultiply(const double* x, double* y) const;
- virtual void LeftMultiply(const double* x, double* y) const;
- virtual void SquaredColumnNorm(double* x) const;
- virtual void ScaleColumns(const double* scale);
- virtual void ToDenseMatrix(Matrix* dense_matrix) const;
- virtual void ToTextFile(FILE* file) const;
- virtual int num_rows() const { return num_rows_; }
- virtual int num_cols() const { return num_cols_; }
- virtual int num_nonzeros() const { return num_nonzeros_; }
- virtual const double* values() const { return values_.get(); }
- virtual double* mutable_values() { return values_.get(); }
- virtual void set_num_nonzeros(int num_nonzeros);
+ void SetZero() final;
+ void RightMultiply(const double* x, double* y) const final;
+ void LeftMultiply(const double* x, double* y) const final;
+ void SquaredColumnNorm(double* x) const final;
+ void ScaleColumns(const double* scale) final;
+ void ToDenseMatrix(Matrix* dense_matrix) const final;
+ void ToTextFile(FILE* file) const final;
+ int num_rows() const final { return num_rows_; }
+ int num_cols() const final { return num_cols_; }
+ int num_nonzeros() const final { return num_nonzeros_; }
+ const double* values() const final { return values_.get(); }
+ double* mutable_values() final { return values_.get(); }
+ void set_num_nonzeros(int num_nonzeros);
// Increase max_num_nonzeros and correspondingly increase the size
// of rows_, cols_ and values_. If new_max_num_nonzeros is smaller
@@ -105,6 +112,25 @@ class TripletSparseMatrix : public SparseMatrix {
static TripletSparseMatrix* CreateSparseDiagonalMatrix(const double* values,
int num_rows);
+ // Options struct to control the generation of random
+ // TripletSparseMatrix objects.
+ struct RandomMatrixOptions {
+ int num_rows;
+ int num_cols;
+ // 0 < density <= 1 is the probability of an entry being
+ // structurally non-zero. A given random matrix will not have
+ // precisely this density.
+ double density;
+ };
+
+ // Create a random CompressedRowSparseMatrix whose entries are
+ // normally distributed and whose structure is determined by
+ // RandomMatrixOptions.
+ //
+ // Caller owns the result.
+ static TripletSparseMatrix* CreateRandomMatrix(
+ const TripletSparseMatrix::RandomMatrixOptions& options);
+
private:
void AllocateMemory();
void CopyData(const TripletSparseMatrix& orig);
@@ -118,9 +144,9 @@ class TripletSparseMatrix : public SparseMatrix {
// stored at the location (rows_[i], cols_[i]). If the there are
// multiple entries with the same (rows_[i], cols_[i]), the values_
// entries corresponding to them are summed up.
- scoped_array<int> rows_;
- scoped_array<int> cols_;
- scoped_array<double> values_;
+ std::unique_ptr<int[]> rows_;
+ std::unique_ptr<int[]> cols_;
+ std::unique_ptr<double[]> values_;
};
} // namespace internal
diff --git a/extern/ceres/internal/ceres/trust_region_minimizer.cc b/extern/ceres/internal/ceres/trust_region_minimizer.cc
index d809906ab54..7065977ad77 100644
--- a/extern/ceres/internal/ceres/trust_region_minimizer.cc
+++ b/extern/ceres/internal/ceres/trust_region_minimizer.cc
@@ -35,6 +35,7 @@
#include <cstdlib>
#include <cstring>
#include <limits>
+#include <memory>
#include <string>
#include <vector>
@@ -92,7 +93,8 @@ void TrustRegionMinimizer::Minimize(const Minimizer::Options& options,
continue;
}
- if (options_.is_constrained) {
+ if (options_.is_constrained &&
+ options_.max_num_line_search_step_size_iterations > 0) {
// Use a projected line search to enforce the bounds constraints
// and improve the quality of the step.
DoLineSearch(x_, gradient_, x_cost_, &delta_);
@@ -135,13 +137,16 @@ void TrustRegionMinimizer::Init(const Minimizer::Options& options,
solver_summary_->num_unsuccessful_steps = 0;
solver_summary_->is_constrained = options.is_constrained;
- evaluator_ = CHECK_NOTNULL(options_.evaluator.get());
- jacobian_ = CHECK_NOTNULL(options_.jacobian.get());
- strategy_ = CHECK_NOTNULL(options_.trust_region_strategy.get());
+ CHECK(options_.evaluator != nullptr);
+ CHECK(options_.jacobian != nullptr);
+ CHECK(options_.trust_region_strategy != nullptr);
+ evaluator_ = options_.evaluator.get();
+ jacobian_ = options_.jacobian.get();
+ strategy_ = options_.trust_region_strategy.get();
is_not_silent_ = !options.is_silent;
inner_iterations_are_enabled_ =
- options.inner_iteration_minimizer.get() != NULL;
+ options.inner_iteration_minimizer.get() != nullptr;
inner_iterations_were_useful_ = false;
num_parameters_ = evaluator_->NumParameters();
@@ -201,7 +206,7 @@ bool TrustRegionMinimizer::IterationZero() {
x_norm_ = x_.norm();
}
- if (!EvaluateGradientAndJacobian()) {
+ if (!EvaluateGradientAndJacobian(/*new_evaluation_point=*/true)) {
return false;
}
@@ -223,8 +228,12 @@ bool TrustRegionMinimizer::IterationZero() {
// Returns true if all computations could be performed
// successfully. Any failures are considered fatal and the
// Solver::Summary is updated to indicate this.
-bool TrustRegionMinimizer::EvaluateGradientAndJacobian() {
- if (!evaluator_->Evaluate(x_.data(),
+bool TrustRegionMinimizer::EvaluateGradientAndJacobian(
+ bool new_evaluation_point) {
+ Evaluator::EvaluateOptions evaluate_options;
+ evaluate_options.new_evaluation_point = new_evaluation_point;
+ if (!evaluator_->Evaluate(evaluate_options,
+ x_.data(),
&x_cost_,
residuals_.data(),
gradient_.data(),
@@ -482,8 +491,11 @@ void TrustRegionMinimizer::DoInnerIterationsIfNeeded() {
options_.inner_iteration_minimizer->Minimize(
options_, inner_iteration_x_.data(), &inner_iteration_summary);
double inner_iteration_cost;
- if (!evaluator_->Evaluate(
- inner_iteration_x_.data(), &inner_iteration_cost, NULL, NULL, NULL)) {
+ if (!evaluator_->Evaluate(inner_iteration_x_.data(),
+ &inner_iteration_cost,
+ nullptr,
+ nullptr,
+ nullptr)) {
VLOG_IF(2, is_not_silent_) << "Inner iteration failed.";
return;
}
@@ -569,8 +581,8 @@ void TrustRegionMinimizer::DoLineSearch(const Vector& x,
line_search_options.function = &line_search_function;
std::string message;
- scoped_ptr<LineSearch> line_search(CHECK_NOTNULL(
- LineSearch::Create(ceres::ARMIJO, line_search_options, &message)));
+ std::unique_ptr<LineSearch> line_search(
+ LineSearch::Create(ceres::ARMIJO, line_search_options, &message));
LineSearch::Summary line_search_summary;
line_search_function.Init(x, *delta);
line_search->Search(1.0, cost, gradient.dot(*delta), &line_search_summary);
@@ -586,7 +598,7 @@ void TrustRegionMinimizer::DoLineSearch(const Vector& x,
line_search_summary.total_time_in_seconds;
if (line_search_summary.success) {
- *delta *= line_search_summary.optimal_step_size;
+ *delta *= line_search_summary.optimal_point.x;
}
}
@@ -601,10 +613,11 @@ bool TrustRegionMinimizer::MaxSolverTimeReached() {
return false;
}
- solver_summary_->message = StringPrintf("Maximum solver time reached. "
- "Total solver time: %e >= %e.",
- total_solver_time,
- options_.max_solver_time_in_seconds);
+ solver_summary_->message = StringPrintf(
+ "Maximum solver time reached. "
+ "Total solver time: %e >= %e.",
+ total_solver_time,
+ options_.max_solver_time_in_seconds);
solver_summary_->termination_type = NO_CONVERGENCE;
VLOG_IF(1, is_not_silent_) << "Terminating: " << solver_summary_->message;
return true;
@@ -618,10 +631,10 @@ bool TrustRegionMinimizer::MaxSolverIterationsReached() {
return false;
}
- solver_summary_->message =
- StringPrintf("Maximum number of iterations reached. "
- "Number of iterations: %d.",
- iteration_summary_.iteration);
+ solver_summary_->message = StringPrintf(
+ "Maximum number of iterations reached. "
+ "Number of iterations: %d.",
+ iteration_summary_.iteration);
solver_summary_->termination_type = NO_CONVERGENCE;
VLOG_IF(1, is_not_silent_) << "Terminating: " << solver_summary_->message;
@@ -653,11 +666,11 @@ bool TrustRegionMinimizer::MinTrustRegionRadiusReached() {
return false;
}
- solver_summary_->message =
- StringPrintf("Minimum trust region radius reached. "
- "Trust region radius: %e <= %e",
- iteration_summary_.trust_region_radius,
- options_.min_trust_region_radius);
+ solver_summary_->message = StringPrintf(
+ "Minimum trust region radius reached. "
+ "Trust region radius: %e <= %e",
+ iteration_summary_.trust_region_radius,
+ options_.min_trust_region_radius);
solver_summary_->termination_type = CONVERGENCE;
VLOG_IF(1, is_not_silent_) << "Terminating: " << solver_summary_->message;
return true;
@@ -725,7 +738,7 @@ void TrustRegionMinimizer::ComputeCandidatePointAndEvaluateCost() {
}
if (!evaluator_->Evaluate(
- candidate_x_.data(), &candidate_cost_, NULL, NULL, NULL)) {
+ candidate_x_.data(), &candidate_cost_, nullptr, nullptr, nullptr)) {
LOG_IF(WARNING, is_not_silent_)
<< "Step failed to evaluate. "
<< "Treating it as a step with infinite cost";
@@ -746,7 +759,7 @@ bool TrustRegionMinimizer::IsStepSuccessful() {
// small.
//
// This can cause the trust region loop to reject this step. To
- // get around this, we expicitly check if the inner iterations
+ // get around this, we explicitly check if the inner iterations
// led to a net decrease in the objective function value. If
// they did, we accept the step even if the trust region ratio
// is small.
@@ -768,7 +781,9 @@ bool TrustRegionMinimizer::HandleSuccessfulStep() {
x_ = candidate_x_;
x_norm_ = x_.norm();
- if (!EvaluateGradientAndJacobian()) {
+ // Since the step was successful, this point has already had the residual
+ // evaluated (but not the jacobian). So indicate that to the evaluator.
+ if (!EvaluateGradientAndJacobian(/*new_evaluation_point=*/false)) {
return false;
}
diff --git a/extern/ceres/internal/ceres/trust_region_minimizer.h b/extern/ceres/internal/ceres/trust_region_minimizer.h
index 43141da58a1..b5c41225ddc 100644
--- a/extern/ceres/internal/ceres/trust_region_minimizer.h
+++ b/extern/ceres/internal/ceres/trust_region_minimizer.h
@@ -31,8 +31,8 @@
#ifndef CERES_INTERNAL_TRUST_REGION_MINIMIZER_H_
#define CERES_INTERNAL_TRUST_REGION_MINIMIZER_H_
+#include <memory>
#include "ceres/internal/eigen.h"
-#include "ceres/internal/scoped_ptr.h"
#include "ceres/minimizer.h"
#include "ceres/solver.h"
#include "ceres/sparse_matrix.h"
@@ -51,9 +51,9 @@ class TrustRegionMinimizer : public Minimizer {
~TrustRegionMinimizer();
// This method is not thread safe.
- virtual void Minimize(const Minimizer::Options& options,
- double* parameters,
- Solver::Summary* solver_summary);
+ void Minimize(const Minimizer::Options& options,
+ double* parameters,
+ Solver::Summary* solver_summary) override;
private:
void Init(const Minimizer::Options& options,
@@ -63,7 +63,7 @@ class TrustRegionMinimizer : public Minimizer {
bool FinalizeIterationAndCheckIfMinimizerCanContinue();
bool ComputeTrustRegionStep();
- bool EvaluateGradientAndJacobian();
+ bool EvaluateGradientAndJacobian(bool new_evaluation_point);
void ComputeCandidatePointAndEvaluateCost();
void DoLineSearch(const Vector& x,
@@ -94,7 +94,7 @@ class TrustRegionMinimizer : public Minimizer {
SparseMatrix* jacobian_;
TrustRegionStrategy* strategy_;
- scoped_ptr<TrustRegionStepEvaluator> step_evaluator_;
+ std::unique_ptr<TrustRegionStepEvaluator> step_evaluator_;
bool is_not_silent_;
bool inner_iterations_are_enabled_;
diff --git a/extern/ceres/internal/ceres/trust_region_preprocessor.cc b/extern/ceres/internal/ceres/trust_region_preprocessor.cc
index 4020e4ca115..b8c6b49d1ca 100644
--- a/extern/ceres/internal/ceres/trust_region_preprocessor.cc
+++ b/extern/ceres/internal/ceres/trust_region_preprocessor.cc
@@ -32,7 +32,9 @@
#include <numeric>
#include <string>
+
#include "ceres/callbacks.h"
+#include "ceres/context_impl.h"
#include "ceres/evaluator.h"
#include "ceres/linear_solver.h"
#include "ceres/minimizer.h"
@@ -56,8 +58,7 @@ namespace {
ParameterBlockOrdering* CreateDefaultLinearSolverOrdering(
const Program& program) {
ParameterBlockOrdering* ordering = new ParameterBlockOrdering;
- const vector<ParameterBlock*>& parameter_blocks =
- program.parameter_blocks();
+ const vector<ParameterBlock*>& parameter_blocks = program.parameter_blocks();
for (int i = 0; i < parameter_blocks.size(); ++i) {
ordering->AddElementToGroup(
const_cast<double*>(parameter_blocks[i]->user_state()), 0);
@@ -68,8 +69,7 @@ ParameterBlockOrdering* CreateDefaultLinearSolverOrdering(
// Check if all the user supplied values in the parameter blocks are
// sane or not, and if the program is feasible or not.
bool IsProgramValid(const Program& program, std::string* error) {
- return (program.ParameterBlocksAreFinite(error) &&
- program.IsFeasible(error));
+ return (program.ParameterBlocksAreFinite(error) && program.IsFeasible(error));
}
void AlternateLinearSolverAndPreconditionerForSchurTypeLinearSolver(
@@ -81,36 +81,33 @@ void AlternateLinearSolverAndPreconditionerForSchurTypeLinearSolver(
const LinearSolverType linear_solver_type_given = options->linear_solver_type;
const PreconditionerType preconditioner_type_given =
options->preconditioner_type;
- options->linear_solver_type = LinearSolver::LinearSolverForZeroEBlocks(
- linear_solver_type_given);
+ options->linear_solver_type =
+ LinearSolver::LinearSolverForZeroEBlocks(linear_solver_type_given);
std::string message;
if (linear_solver_type_given == ITERATIVE_SCHUR) {
- options->preconditioner_type = Preconditioner::PreconditionerForZeroEBlocks(
- preconditioner_type_given);
+ options->preconditioner_type =
+ Preconditioner::PreconditionerForZeroEBlocks(preconditioner_type_given);
message =
- StringPrintf(
- "No E blocks. Switching from %s(%s) to %s(%s).",
- LinearSolverTypeToString(linear_solver_type_given),
- PreconditionerTypeToString(preconditioner_type_given),
- LinearSolverTypeToString(options->linear_solver_type),
- PreconditionerTypeToString(options->preconditioner_type));
+ StringPrintf("No E blocks. Switching from %s(%s) to %s(%s).",
+ LinearSolverTypeToString(linear_solver_type_given),
+ PreconditionerTypeToString(preconditioner_type_given),
+ LinearSolverTypeToString(options->linear_solver_type),
+ PreconditionerTypeToString(options->preconditioner_type));
} else {
message =
- StringPrintf(
- "No E blocks. Switching from %s to %s.",
- LinearSolverTypeToString(linear_solver_type_given),
- LinearSolverTypeToString(options->linear_solver_type));
+ StringPrintf("No E blocks. Switching from %s to %s.",
+ LinearSolverTypeToString(linear_solver_type_given),
+ LinearSolverTypeToString(options->linear_solver_type));
}
VLOG_IF(1, options->logging_type != SILENT) << message;
}
-// For Schur type and SPARSE_NORMAL_CHOLESKY linear solvers, reorder
-// the program to reduce fill-in and increase cache coherency.
+// Reorder the program to reduce fill-in and increase cache coherency.
bool ReorderProgram(PreprocessedProblem* pp) {
- Solver::Options& options = pp->options;
+ const Solver::Options& options = pp->options;
if (IsSchurType(options.linear_solver_type)) {
return ReorderProgramForSchurTypeLinearSolver(
options.linear_solver_type,
@@ -123,9 +120,25 @@ bool ReorderProgram(PreprocessedProblem* pp) {
if (options.linear_solver_type == SPARSE_NORMAL_CHOLESKY &&
!options.dynamic_sparsity) {
- return ReorderProgramForSparseNormalCholesky(
+ return ReorderProgramForSparseCholesky(
options.sparse_linear_algebra_library_type,
*options.linear_solver_ordering,
+ 0, /* use all the rows of the jacobian */
+ pp->reduced_program.get(),
+ &pp->error);
+ }
+
+ if (options.linear_solver_type == CGNR &&
+ options.preconditioner_type == SUBSET) {
+ pp->linear_solver_options.subset_preconditioner_start_row_block =
+ ReorderResidualBlocksByPartition(
+ options.residual_blocks_for_subset_preconditioner,
+ pp->reduced_program.get());
+
+ return ReorderProgramForSparseCholesky(
+ options.sparse_linear_algebra_library_type,
+ *options.linear_solver_ordering,
+ pp->linear_solver_options.subset_preconditioner_start_row_block,
pp->reduced_program.get(),
&pp->error);
}
@@ -139,7 +152,9 @@ bool ReorderProgram(PreprocessedProblem* pp) {
// too.
bool SetupLinearSolver(PreprocessedProblem* pp) {
Solver::Options& options = pp->options;
- if (options.linear_solver_ordering.get() == NULL) {
+ pp->linear_solver_options = LinearSolver::Options();
+
+ if (!options.linear_solver_ordering) {
// If the user has not supplied a linear solver ordering, then we
// assume that they are giving all the freedom to us in choosing
// the best possible ordering. This intent can be indicated by
@@ -163,8 +178,7 @@ bool SetupLinearSolver(PreprocessedProblem* pp) {
ordering->Remove(pp->removed_parameter_blocks);
if (IsSchurType(options.linear_solver_type) &&
min_group_id != ordering->MinNonZeroGroup()) {
- AlternateLinearSolverAndPreconditionerForSchurTypeLinearSolver(
- &options);
+ AlternateLinearSolverAndPreconditionerForSchurTypeLinearSolver(&options);
}
}
@@ -175,7 +189,6 @@ bool SetupLinearSolver(PreprocessedProblem* pp) {
}
// Configure the linear solver.
- pp->linear_solver_options = LinearSolver::Options();
pp->linear_solver_options.min_num_iterations =
options.min_linear_solver_iterations;
pp->linear_solver_options.max_num_iterations =
@@ -191,33 +204,50 @@ bool SetupLinearSolver(PreprocessedProblem* pp) {
pp->linear_solver_options.use_explicit_schur_complement =
options.use_explicit_schur_complement;
pp->linear_solver_options.dynamic_sparsity = options.dynamic_sparsity;
- pp->linear_solver_options.num_threads = options.num_linear_solver_threads;
-
- // Ignore user's postordering preferences and force it to be true if
- // cholmod_camd is not available. This ensures that the linear
- // solver does not assume that a fill-reducing pre-ordering has been
- // done.
+ pp->linear_solver_options.use_mixed_precision_solves =
+ options.use_mixed_precision_solves;
+ pp->linear_solver_options.max_num_refinement_iterations =
+ options.max_num_refinement_iterations;
+ pp->linear_solver_options.num_threads = options.num_threads;
pp->linear_solver_options.use_postordering = options.use_postordering;
- if (options.linear_solver_type == SPARSE_SCHUR &&
- options.sparse_linear_algebra_library_type == SUITE_SPARSE &&
- !SuiteSparse::IsConstrainedApproximateMinimumDegreeOrderingAvailable()) {
- pp->linear_solver_options.use_postordering = true;
- }
-
- OrderingToGroupSizes(options.linear_solver_ordering.get(),
- &pp->linear_solver_options.elimination_groups);
+ pp->linear_solver_options.context = pp->problem->context();
+
+ if (IsSchurType(pp->linear_solver_options.type)) {
+ OrderingToGroupSizes(options.linear_solver_ordering.get(),
+ &pp->linear_solver_options.elimination_groups);
+
+ // Schur type solvers expect at least two elimination groups. If
+ // there is only one elimination group, then it is guaranteed that
+ // this group only contains e_blocks. Thus we add a dummy
+ // elimination group with zero blocks in it.
+ if (pp->linear_solver_options.elimination_groups.size() == 1) {
+ pp->linear_solver_options.elimination_groups.push_back(0);
+ }
- // Schur type solvers expect at least two elimination groups. If
- // there is only one elimination group, then it is guaranteed that
- // this group only contains e_blocks. Thus we add a dummy
- // elimination group with zero blocks in it.
- if (IsSchurType(pp->linear_solver_options.type) &&
- pp->linear_solver_options.elimination_groups.size() == 1) {
- pp->linear_solver_options.elimination_groups.push_back(0);
+ if (options.linear_solver_type == SPARSE_SCHUR) {
+ // When using SPARSE_SCHUR, we ignore the user's postordering
+ // preferences in certain cases.
+ //
+ // 1. SUITE_SPARSE is the sparse linear algebra library requested
+ // but cholmod_camd is not available.
+ // 2. CX_SPARSE is the sparse linear algebra library requested.
+ //
+ // This ensures that the linear solver does not assume that a
+ // fill-reducing pre-ordering has been done.
+ //
+ // TODO(sameeragarwal): Implement the reordering of parameter
+ // blocks for CX_SPARSE.
+ if ((options.sparse_linear_algebra_library_type == SUITE_SPARSE &&
+ !SuiteSparse::
+ IsConstrainedApproximateMinimumDegreeOrderingAvailable()) ||
+ (options.sparse_linear_algebra_library_type == CX_SPARSE)) {
+ pp->linear_solver_options.use_postordering = true;
+ }
+ }
}
pp->linear_solver.reset(LinearSolver::Create(pp->linear_solver_options));
- return (pp->linear_solver.get() != NULL);
+ return (pp->linear_solver != nullptr);
}
// Configure and create the evaluator.
@@ -228,19 +258,20 @@ bool SetupEvaluator(PreprocessedProblem* pp) {
pp->evaluator_options.num_eliminate_blocks = 0;
if (IsSchurType(options.linear_solver_type)) {
pp->evaluator_options.num_eliminate_blocks =
- options
- .linear_solver_ordering
- ->group_to_elements().begin()
- ->second.size();
+ options.linear_solver_ordering->group_to_elements()
+ .begin()
+ ->second.size();
}
pp->evaluator_options.num_threads = options.num_threads;
pp->evaluator_options.dynamic_sparsity = options.dynamic_sparsity;
- pp->evaluator.reset(Evaluator::Create(pp->evaluator_options,
- pp->reduced_program.get(),
- &pp->error));
+ 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);
+ return (pp->evaluator != nullptr);
}
// If the user requested inner iterations, then find an inner
@@ -252,6 +283,11 @@ bool SetupInnerIterationMinimizer(PreprocessedProblem* pp) {
return true;
}
+ if (pp->reduced_program->mutable_evaluation_callback()) {
+ pp->error = "Inner iterations cannot be used with EvaluationCallbacks";
+ return false;
+ }
+
// With just one parameter block, the outer iteration of the trust
// region method and inner iterations are doing exactly the same
// thing, and thus inner iterations are not needed.
@@ -261,7 +297,7 @@ bool SetupInnerIterationMinimizer(PreprocessedProblem* pp) {
return true;
}
- if (options.inner_iteration_ordering.get() != NULL) {
+ if (options.inner_iteration_ordering != nullptr) {
// If the user supplied an ordering, then remove the set of
// inactive parameter blocks from it
options.inner_iteration_ordering->Remove(pp->removed_parameter_blocks);
@@ -283,7 +319,8 @@ bool SetupInnerIterationMinimizer(PreprocessedProblem* pp) {
CoordinateDescentMinimizer::CreateOrdering(*pp->reduced_program));
}
- pp->inner_iteration_minimizer.reset(new CoordinateDescentMinimizer);
+ pp->inner_iteration_minimizer.reset(
+ new CoordinateDescentMinimizer(pp->problem->context()));
return pp->inner_iteration_minimizer->Init(*pp->reduced_program,
pp->problem->parameter_map(),
*options.inner_iteration_ordering,
@@ -303,8 +340,7 @@ void SetupMinimizerOptions(PreprocessedProblem* pp) {
TrustRegionStrategy::Options strategy_options;
strategy_options.linear_solver = pp->linear_solver.get();
- strategy_options.initial_radius =
- options.initial_trust_region_radius;
+ strategy_options.initial_radius = options.initial_trust_region_radius;
strategy_options.max_radius = options.max_trust_region_radius;
strategy_options.min_lm_diagonal = options.min_lm_diagonal;
strategy_options.max_lm_diagonal = options.max_lm_diagonal;
@@ -312,18 +348,18 @@ void SetupMinimizerOptions(PreprocessedProblem* pp) {
options.trust_region_strategy_type;
strategy_options.dogleg_type = options.dogleg_type;
pp->minimizer_options.trust_region_strategy.reset(
- CHECK_NOTNULL(TrustRegionStrategy::Create(strategy_options)));
+ TrustRegionStrategy::Create(strategy_options));
+ CHECK(pp->minimizer_options.trust_region_strategy != nullptr);
}
} // namespace
-TrustRegionPreprocessor::~TrustRegionPreprocessor() {
-}
+TrustRegionPreprocessor::~TrustRegionPreprocessor() {}
bool TrustRegionPreprocessor::Preprocess(const Solver::Options& options,
ProblemImpl* problem,
PreprocessedProblem* pp) {
- CHECK_NOTNULL(pp);
+ CHECK(pp != nullptr);
pp->options = options;
ChangeNumThreadsIfNeeded(&pp->options);
@@ -333,10 +369,8 @@ 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.reset(program->CreateReducedProgram(
+ &pp->removed_parameter_blocks, &pp->fixed_cost, &pp->error));
if (pp->reduced_program.get() == NULL) {
return false;
@@ -348,8 +382,7 @@ bool TrustRegionPreprocessor::Preprocess(const Solver::Options& options,
return true;
}
- if (!SetupLinearSolver(pp) ||
- !SetupEvaluator(pp) ||
+ if (!SetupLinearSolver(pp) || !SetupEvaluator(pp) ||
!SetupInnerIterationMinimizer(pp)) {
return false;
}
diff --git a/extern/ceres/internal/ceres/trust_region_preprocessor.h b/extern/ceres/internal/ceres/trust_region_preprocessor.h
index a6631ab3d40..9597905ae9a 100644
--- a/extern/ceres/internal/ceres/trust_region_preprocessor.h
+++ b/extern/ceres/internal/ceres/trust_region_preprocessor.h
@@ -39,9 +39,9 @@ namespace internal {
class TrustRegionPreprocessor : public Preprocessor {
public:
virtual ~TrustRegionPreprocessor();
- virtual bool Preprocess(const Solver::Options& options,
- ProblemImpl* problem,
- PreprocessedProblem* preprocessed_problem);
+ bool Preprocess(const Solver::Options& options,
+ ProblemImpl* problem,
+ PreprocessedProblem* preprocessed_problem) override;
};
} // namespace internal
diff --git a/extern/ceres/internal/ceres/trust_region_step_evaluator.cc b/extern/ceres/internal/ceres/trust_region_step_evaluator.cc
index c9167e623ef..33b0c41ead6 100644
--- a/extern/ceres/internal/ceres/trust_region_step_evaluator.cc
+++ b/extern/ceres/internal/ceres/trust_region_step_evaluator.cc
@@ -29,6 +29,7 @@
// Author: sameeragarwal@google.com (Sameer Agarwal)
#include <algorithm>
+#include <limits>
#include "ceres/trust_region_step_evaluator.h"
#include "glog/logging.h"
@@ -51,6 +52,15 @@ TrustRegionStepEvaluator::TrustRegionStepEvaluator(
double TrustRegionStepEvaluator::StepQuality(
const double cost,
const double model_cost_change) const {
+ // If the function evaluation for this step was a failure, in which
+ // case the TrustRegionMinimizer would have set the cost to
+ // std::numeric_limits<double>::max(). In this case, the division by
+ // model_cost_change can result in an overflow. To prevent that from
+ // happening, we will deal with this case explicitly.
+ if (cost >= std::numeric_limits<double>::max()) {
+ return std::numeric_limits<double>::lowest();
+ }
+
const double relative_decrease = (current_cost_ - cost) / model_cost_change;
const double historical_relative_decrease =
(reference_cost_ - cost) /
diff --git a/extern/ceres/internal/ceres/trust_region_step_evaluator.h b/extern/ceres/internal/ceres/trust_region_step_evaluator.h
index 06df102a308..03c00362dac 100644
--- a/extern/ceres/internal/ceres/trust_region_step_evaluator.h
+++ b/extern/ceres/internal/ceres/trust_region_step_evaluator.h
@@ -56,7 +56,7 @@ namespace internal {
// The parameter max_consecutive_nonmonotonic_steps controls the
// window size used by the step selection algorithm to accept
// non-monotonic steps. Setting this parameter to zero, recovers the
-// classic montonic descent algorithm.
+// classic monotonic descent algorithm.
//
// Based on algorithm 10.1.2 (page 357) of "Trust Region
// Methods" by Conn Gould & Toint, or equations 33-40 of
@@ -82,7 +82,7 @@ class TrustRegionStepEvaluator {
// max_consecutive_nonmonotonic_steps controls the window size used
// by the step selection algorithm to accept non-monotonic
// steps. Setting this parameter to zero, recovers the classic
- // montonic descent algorithm.
+ // monotonic descent algorithm.
TrustRegionStepEvaluator(double initial_cost,
int max_consecutive_nonmonotonic_steps);
diff --git a/extern/ceres/internal/ceres/trust_region_strategy.h b/extern/ceres/internal/ceres/trust_region_strategy.h
index 36e8e981cc0..5751691e5ec 100644
--- a/extern/ceres/internal/ceres/trust_region_strategy.h
+++ b/extern/ceres/internal/ceres/trust_region_strategy.h
@@ -56,43 +56,34 @@ class SparseMatrix;
class TrustRegionStrategy {
public:
struct Options {
- Options()
- : trust_region_strategy_type(LEVENBERG_MARQUARDT),
- initial_radius(1e4),
- max_radius(1e32),
- min_lm_diagonal(1e-6),
- max_lm_diagonal(1e32),
- dogleg_type(TRADITIONAL_DOGLEG) {
- }
-
- TrustRegionStrategyType trust_region_strategy_type;
+ TrustRegionStrategyType trust_region_strategy_type = LEVENBERG_MARQUARDT;
// Linear solver used for actually solving the trust region step.
- LinearSolver* linear_solver;
- double initial_radius;
- double max_radius;
+ LinearSolver* linear_solver = nullptr;
+ double initial_radius = 1e4;
+ double max_radius = 1e32;
// Minimum and maximum values of the diagonal damping matrix used
// by LevenbergMarquardtStrategy. The DoglegStrategy also uses
// these bounds to construct a regularizing diagonal to ensure
// that the Gauss-Newton step computation is of full rank.
- double min_lm_diagonal;
- double max_lm_diagonal;
+ double min_lm_diagonal = 1e-6;
+ double max_lm_diagonal = 1e32;
// Further specify which dogleg method to use
- DoglegType dogleg_type;
+ DoglegType dogleg_type = TRADITIONAL_DOGLEG;
};
+ // Factory.
+ static TrustRegionStrategy* Create(const Options& options);
+
+ virtual ~TrustRegionStrategy();
+
// Per solve options.
struct PerSolveOptions {
- PerSolveOptions()
- : eta(0),
- dump_format_type(TEXTFILE) {
- }
-
// Forcing sequence for inexact solves.
- double eta;
+ double eta = 1e-1;
- DumpFormatType dump_format_type;
+ DumpFormatType dump_format_type = TEXTFILE;
// If non-empty and dump_format_type is not CONSOLE, the trust
// regions strategy will write the linear system to file(s) with
@@ -103,12 +94,6 @@ class TrustRegionStrategy {
};
struct Summary {
- Summary()
- : residual_norm(0.0),
- num_iterations(-1),
- termination_type(LINEAR_SOLVER_FAILURE) {
- }
-
// If the trust region problem is,
//
// 1/2 x'Ax + b'x + c,
@@ -116,19 +101,17 @@ class TrustRegionStrategy {
// then
//
// residual_norm = |Ax -b|
- double residual_norm;
+ double residual_norm = -1;
// Number of iterations used by the linear solver. If a linear
// solver was not called (e.g., DogLegStrategy after an
// unsuccessful step), then this would be zero.
- int num_iterations;
+ int num_iterations = -1;
// Status of the linear solver used to solve the Newton system.
- LinearSolverTerminationType termination_type;
+ LinearSolverTerminationType termination_type = LINEAR_SOLVER_FAILURE;
};
- virtual ~TrustRegionStrategy();
-
// Use the current radius to solve for the trust region step.
virtual Summary ComputeStep(const PerSolveOptions& per_solve_options,
SparseMatrix* jacobian,
@@ -153,9 +136,6 @@ class TrustRegionStrategy {
// Current trust region radius.
virtual double Radius() const = 0;
-
- // Factory.
- static TrustRegionStrategy* Create(const Options& options);
};
} // namespace internal
diff --git a/extern/ceres/internal/ceres/types.cc b/extern/ceres/internal/ceres/types.cc
index f86fb78eb8c..93c4cfcb027 100644
--- a/extern/ceres/internal/ceres/types.cc
+++ b/extern/ceres/internal/ceres/types.cc
@@ -78,6 +78,7 @@ const char* PreconditionerTypeToString(PreconditionerType type) {
CASESTR(SCHUR_JACOBI);
CASESTR(CLUSTER_JACOBI);
CASESTR(CLUSTER_TRIDIAGONAL);
+ CASESTR(SUBSET);
default:
return "UNKNOWN";
}
@@ -90,6 +91,7 @@ bool StringToPreconditionerType(string value, PreconditionerType* type) {
STRENUM(SCHUR_JACOBI);
STRENUM(CLUSTER_JACOBI);
STRENUM(CLUSTER_TRIDIAGONAL);
+ STRENUM(SUBSET);
return false;
}
@@ -99,6 +101,7 @@ const char* SparseLinearAlgebraLibraryTypeToString(
CASESTR(SUITE_SPARSE);
CASESTR(CX_SPARSE);
CASESTR(EIGEN_SPARSE);
+ CASESTR(ACCELERATE_SPARSE);
CASESTR(NO_SPARSE);
default:
return "UNKNOWN";
@@ -112,6 +115,7 @@ bool StringToSparseLinearAlgebraLibraryType(
STRENUM(SUITE_SPARSE);
STRENUM(CX_SPARSE);
STRENUM(EIGEN_SPARSE);
+ STRENUM(ACCELERATE_SPARSE);
STRENUM(NO_SPARSE);
return false;
}
@@ -267,8 +271,7 @@ const char* CovarianceAlgorithmTypeToString(
CovarianceAlgorithmType type) {
switch (type) {
CASESTR(DENSE_SVD);
- CASESTR(EIGEN_SPARSE_QR);
- CASESTR(SUITE_SPARSE_QR);
+ CASESTR(SPARSE_QR);
default:
return "UNKNOWN";
}
@@ -279,8 +282,7 @@ bool StringToCovarianceAlgorithmType(
CovarianceAlgorithmType* type) {
UpperCase(&value);
STRENUM(DENSE_SVD);
- STRENUM(EIGEN_SPARSE_QR);
- STRENUM(SUITE_SPARSE_QR);
+ STRENUM(SPARSE_QR);
return false;
}
@@ -336,6 +338,39 @@ const char* TerminationTypeToString(TerminationType type) {
}
}
+const char* LoggingTypeToString(LoggingType type) {
+ switch (type) {
+ CASESTR(SILENT);
+ CASESTR(PER_MINIMIZER_ITERATION);
+ default:
+ return "UNKNOWN";
+ }
+}
+
+bool StringtoLoggingType(std::string value, LoggingType* type) {
+ UpperCase(&value);
+ STRENUM(SILENT);
+ STRENUM(PER_MINIMIZER_ITERATION);
+ return false;
+}
+
+
+const char* DumpFormatTypeToString(DumpFormatType type) {
+ switch (type) {
+ CASESTR(CONSOLE);
+ CASESTR(TEXTFILE);
+ default:
+ return "UNKNOWN";
+ }
+}
+
+bool StringtoDumpFormatType(std::string value, DumpFormatType* type) {
+ UpperCase(&value);
+ STRENUM(CONSOLE);
+ STRENUM(TEXTFILE);
+ return false;
+}
+
#undef CASESTR
#undef STRENUM
@@ -363,6 +398,14 @@ bool IsSparseLinearAlgebraLibraryTypeAvailable(
#endif
}
+ if (type == ACCELERATE_SPARSE) {
+#ifdef CERES_NO_ACCELERATE_SPARSE
+ return false;
+#else
+ return true;
+#endif
+ }
+
if (type == EIGEN_SPARSE) {
#ifdef CERES_USE_EIGEN_SPARSE
return true;
diff --git a/extern/ceres/internal/ceres/visibility.cc b/extern/ceres/internal/ceres/visibility.cc
new file mode 100644
index 00000000000..0981eeddcbf
--- /dev/null
+++ b/extern/ceres/internal/ceres/visibility.cc
@@ -0,0 +1,152 @@
+// 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: kushalav@google.com (Avanish Kushal)
+
+#include "ceres/visibility.h"
+
+#include <cmath>
+#include <ctime>
+#include <algorithm>
+#include <set>
+#include <vector>
+#include <unordered_map>
+#include <utility>
+#include "ceres/block_structure.h"
+#include "ceres/graph.h"
+#include "ceres/pair_hash.h"
+#include "glog/logging.h"
+
+namespace ceres {
+namespace internal {
+
+using std::make_pair;
+using std::max;
+using std::pair;
+using std::set;
+using std::vector;
+
+void ComputeVisibility(const CompressedRowBlockStructure& block_structure,
+ const int num_eliminate_blocks,
+ vector<set<int>>* visibility) {
+ CHECK(visibility != nullptr);
+
+ // Clear the visibility vector and resize it to hold a
+ // vector for each camera.
+ 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;
+ 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) {
+ continue;
+ }
+
+ for (int j = 1; j < cells.size(); ++j) {
+ int camera_block_id = cells[j].block_id - num_eliminate_blocks;
+ DCHECK_GE(camera_block_id, 0);
+ DCHECK_LT(camera_block_id, visibility->size());
+ (*visibility)[camera_block_id].insert(block_id);
+ }
+ }
+}
+
+WeightedGraph<int>* CreateSchurComplementGraph(
+ const vector<set<int>>& visibility) {
+ const time_t start_time = time(NULL);
+ // 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);
+ }
+ }
+
+ // Invert the visibility. The input is a camera->point mapping,
+ // which tells us which points are visible in which
+ // cameras. However, to compute the sparsity structure of the Schur
+ // Complement efficiently, its better to have the point->camera
+ // mapping.
+ 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) {
+ inverse_visibility[v].insert(i);
+ }
+ }
+
+ // Map from camera pairs to number of points visible to both cameras
+ // in the pair.
+ std::unordered_map<pair<int, int>, int, pair_hash> camera_pairs;
+
+ // 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();
+ camera1 != inverse_visibility_set.end();
+ ++camera1) {
+ set<int>::const_iterator camera2 = camera1;
+ for (++camera2; camera2 != inverse_visibility_set.end(); ++camera2) {
+ ++(camera_pairs[make_pair(*camera1, *camera2)]);
+ }
+ }
+ }
+
+ WeightedGraph<int>* graph = new WeightedGraph<int>;
+
+ // Add vertices and initialize the pairs for self edges so that self
+ // edges are guaranteed. This is needed for the Canonical views
+ // algorithm to work correctly.
+ static constexpr double kSelfEdgeWeight = 1.0;
+ for (int i = 0; i < visibility.size(); ++i) {
+ graph->AddVertex(i);
+ graph->AddEdge(i, i, kSelfEdgeWeight);
+ }
+
+ // Add an edge for each camera pair.
+ for (const auto& camera_pair_count : camera_pairs) {
+ const int camera1 = camera_pair_count.first.first;
+ const int camera2 = camera_pair_count.first.second;
+ const int count = camera_pair_count.second;
+ DCHECK_NE(camera1, camera2);
+ // Static cast necessary for Windows.
+ const double weight = static_cast<double>(count) /
+ (sqrt(static_cast<double>(
+ visibility[camera1].size() * visibility[camera2].size())));
+ graph->AddEdge(camera1, camera2, weight);
+ }
+
+ VLOG(2) << "Schur complement graph time: " << (time(NULL) - start_time);
+ return graph;
+}
+
+} // namespace internal
+} // namespace ceres
diff --git a/extern/ceres/internal/ceres/visibility.h b/extern/ceres/internal/ceres/visibility.h
new file mode 100644
index 00000000000..115d45f7cf6
--- /dev/null
+++ b/extern/ceres/internal/ceres/visibility.h
@@ -0,0 +1,78 @@
+// 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: kushalav@google.com (Avanish Kushal)
+// sameeragarwal@google.com (Sameer Agarwal)
+//
+// Functions to manipulate visibility information from the block
+// structure of sparse matrices.
+
+#ifndef CERES_INTERNAL_VISIBILITY_H_
+#define CERES_INTERNAL_VISIBILITY_H_
+
+#include <set>
+#include <vector>
+#include "ceres/graph.h"
+
+namespace ceres {
+namespace internal {
+
+struct CompressedRowBlockStructure;
+
+// Given a compressed row block structure, computes the set of
+// e_blocks "visible" to each f_block. If an e_block co-occurs with an
+// f_block in a residual block, it is visible to the f_block. The
+// first num_eliminate_blocks columns blocks are e_blocks and the rest
+// f_blocks.
+//
+// In a structure from motion problem, e_blocks correspond to 3D
+// points and f_blocks correspond to cameras.
+void ComputeVisibility(const CompressedRowBlockStructure& block_structure,
+ int num_eliminate_blocks,
+ std::vector<std::set<int>>* visibility);
+
+// Given f_block visibility as computed by the ComputeVisibility
+// function above, construct and return a graph whose vertices are
+// f_blocks and an edge connects two vertices if they have at least one
+// e_block in common. The weight of this edge is normalized dot
+// product between the visibility vectors of the two
+// vertices/f_blocks.
+//
+// This graph reflects the sparsity structure of reduced camera
+// matrix/Schur complement matrix obtained by eliminating the e_blocks
+// from the normal equations.
+//
+// Caller acquires ownership of the returned WeightedGraph pointer
+// (heap-allocated).
+WeightedGraph<int>* CreateSchurComplementGraph(
+ const std::vector<std::set<int>>& visibility);
+
+} // namespace internal
+} // namespace ceres
+
+#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
new file mode 100644
index 00000000000..3372e82d1e1
--- /dev/null
+++ b/extern/ceres/internal/ceres/visibility_based_preconditioner.cc
@@ -0,0 +1,585 @@
+// 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/visibility_based_preconditioner.h"
+
+#include <algorithm>
+#include <functional>
+#include <iterator>
+#include <memory>
+#include <set>
+#include <utility>
+#include <vector>
+
+#include "Eigen/Dense"
+#include "ceres/block_random_access_sparse_matrix.h"
+#include "ceres/block_sparse_matrix.h"
+#include "ceres/canonical_views_clustering.h"
+#include "ceres/graph.h"
+#include "ceres/graph_algorithms.h"
+#include "ceres/linear_solver.h"
+#include "ceres/schur_eliminator.h"
+#include "ceres/single_linkage_clustering.h"
+#include "ceres/visibility.h"
+#include "glog/logging.h"
+
+namespace ceres {
+namespace internal {
+
+using std::make_pair;
+using std::pair;
+using std::set;
+using std::swap;
+using std::vector;
+
+// TODO(sameeragarwal): Currently these are magic weights for the
+// preconditioner construction. Move these higher up into the Options
+// struct and provide some guidelines for choosing them.
+//
+// This will require some more work on the clustering algorithm and
+// possibly some more refactoring of the code.
+static constexpr double kCanonicalViewsSizePenaltyWeight = 3.0;
+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) {
+ CHECK_GT(options_.elimination_groups.size(), 1);
+ CHECK_GT(options_.elimination_groups[0], 0);
+ CHECK(options_.type == CLUSTER_JACOBI || options_.type == CLUSTER_TRIDIAGONAL)
+ << "Unknown preconditioner type: " << options_.type;
+ 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);
+
+ // Vector of camera block sizes
+ block_size_.resize(num_blocks_);
+ for (int i = 0; i < num_blocks_; ++i) {
+ block_size_[i] = bs.cols[i + options_.elimination_groups[0]].size;
+ }
+
+ const time_t start_time = time(NULL);
+ switch (options_.type) {
+ case CLUSTER_JACOBI:
+ ComputeClusterJacobiSparsity(bs);
+ break;
+ case CLUSTER_TRIDIAGONAL:
+ ComputeClusterTridiagonalSparsity(bs);
+ break;
+ default:
+ LOG(FATAL) << "Unknown preconditioner type";
+ }
+ const time_t structure_time = time(NULL);
+ InitStorage(bs);
+ const time_t storage_time = time(NULL);
+ InitEliminator(bs);
+ const time_t eliminator_time = time(NULL);
+
+ LinearSolver::Options sparse_cholesky_options;
+ sparse_cholesky_options.sparse_linear_algebra_library_type =
+ options_.sparse_linear_algebra_library_type;
+
+ // The preconditioner's sparsity is not available in the
+ // preprocessor, so the columns of the Jacobian have not been
+ // reordered to minimize fill in when computing its sparse Cholesky
+ // factorization. So we must tell the SparseCholesky object to
+ // perform approximate minimum-degree reordering, which is done by
+ // setting use_postordering to true.
+ sparse_cholesky_options.use_postordering = true;
+ sparse_cholesky_ = SparseCholesky::Create(sparse_cholesky_options);
+
+ const time_t init_time = time(NULL);
+ 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() {}
+
+// Determine the sparsity structure of the CLUSTER_JACOBI
+// preconditioner. It clusters cameras using their scene
+// visibility. The clusters form the diagonal blocks of the
+// preconditioner matrix.
+void VisibilityBasedPreconditioner::ComputeClusterJacobiSparsity(
+ const CompressedRowBlockStructure& bs) {
+ vector<set<int>> visibility;
+ ComputeVisibility(bs, options_.elimination_groups[0], &visibility);
+ CHECK_EQ(num_blocks_, visibility.size());
+ ClusterCameras(visibility);
+ cluster_pairs_.clear();
+ for (int i = 0; i < num_clusters_; ++i) {
+ cluster_pairs_.insert(make_pair(i, i));
+ }
+}
+
+// Determine the sparsity structure of the CLUSTER_TRIDIAGONAL
+// preconditioner. It clusters cameras using using the scene
+// visibility and then finds the strongly interacting pairs of
+// clusters by constructing another graph with the clusters as
+// vertices and approximating it with a degree-2 maximum spanning
+// forest. The set of edges in this forest are the cluster pairs.
+void VisibilityBasedPreconditioner::ComputeClusterTridiagonalSparsity(
+ const CompressedRowBlockStructure& bs) {
+ vector<set<int>> visibility;
+ ComputeVisibility(bs, options_.elimination_groups[0], &visibility);
+ CHECK_EQ(num_blocks_, visibility.size());
+ ClusterCameras(visibility);
+
+ // Construct a weighted graph on the set of clusters, where the
+ // edges are the number of 3D points/e_blocks visible in both the
+ // clusters at the ends of the edge. Return an approximate degree-2
+ // 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));
+ CHECK(cluster_graph != nullptr);
+ std::unique_ptr<WeightedGraph<int>> forest(
+ Degree2MaximumSpanningForest(*cluster_graph));
+ CHECK(forest != nullptr);
+ ForestToClusterPairs(*forest, &cluster_pairs_);
+}
+
+// Allocate storage for the preconditioner matrix.
+void VisibilityBasedPreconditioner::InitStorage(
+ const CompressedRowBlockStructure& bs) {
+ ComputeBlockPairsInPreconditioner(bs);
+ m_.reset(new BlockRandomAccessSparseMatrix(block_size_, block_pairs_));
+}
+
+// Call the canonical views algorithm and cluster the cameras based on
+// their visibility sets. The visibility set of a camera is the set of
+// e_blocks/3D points in the scene that are seen by it.
+//
+// The cluster_membership_ vector is updated to indicate cluster
+// memberships for each camera block.
+void VisibilityBasedPreconditioner::ClusterCameras(
+ const vector<set<int>>& visibility) {
+ std::unique_ptr<WeightedGraph<int>> schur_complement_graph(
+ CreateSchurComplementGraph(visibility));
+ CHECK(schur_complement_graph != nullptr);
+
+ std::unordered_map<int, int> membership;
+
+ if (options_.visibility_clustering_type == CANONICAL_VIEWS) {
+ vector<int> centers;
+ CanonicalViewsClusteringOptions clustering_options;
+ clustering_options.size_penalty_weight = kCanonicalViewsSizePenaltyWeight;
+ clustering_options.similarity_penalty_weight =
+ kCanonicalViewsSimilarityPenaltyWeight;
+ ComputeCanonicalViewsClustering(
+ clustering_options, *schur_complement_graph, &centers, &membership);
+ num_clusters_ = centers.size();
+ } else if (options_.visibility_clustering_type == SINGLE_LINKAGE) {
+ SingleLinkageClusteringOptions clustering_options;
+ clustering_options.min_similarity = kSingleLinkageMinSimilarity;
+ num_clusters_ = ComputeSingleLinkageClustering(
+ clustering_options, *schur_complement_graph, &membership);
+ } else {
+ LOG(FATAL) << "Unknown visibility clustering algorithm.";
+ }
+
+ CHECK_GT(num_clusters_, 0);
+ VLOG(2) << "num_clusters: " << num_clusters_;
+ FlattenMembershipMap(membership, &cluster_membership_);
+}
+
+// Compute the block sparsity structure of the Schur complement
+// matrix. For each pair of cameras contributing a non-zero cell to
+// the schur complement, determine if that cell is present in the
+// preconditioner or not.
+//
+// A pair of cameras contribute a cell to the preconditioner if they
+// are part of the same cluster or if the two clusters that they
+// belong have an edge connecting them in the degree-2 maximum
+// spanning forest.
+//
+// For example, a camera pair (i,j) where i belongs to cluster1 and
+// j belongs to cluster2 (assume that cluster1 < cluster2).
+//
+// The cell corresponding to (i,j) is present in the preconditioner
+// if cluster1 == cluster2 or the pair (cluster1, cluster2) were
+// connected by an edge in the degree-2 maximum spanning forest.
+//
+// Since we have already expanded the forest into a set of camera
+// pairs/edges, including self edges, the check can be reduced to
+// checking membership of (cluster1, cluster2) in cluster_pairs_.
+void VisibilityBasedPreconditioner::ComputeBlockPairsInPreconditioner(
+ const CompressedRowBlockStructure& bs) {
+ block_pairs_.clear();
+ for (int i = 0; i < num_blocks_; ++i) {
+ block_pairs_.insert(make_pair(i, i));
+ }
+
+ int r = 0;
+ const int num_row_blocks = bs.rows.size();
+ const int num_eliminate_blocks = options_.elimination_groups[0];
+
+ // Iterate over each row of the matrix. The block structure of the
+ // matrix is assumed to be sorted in order of the e_blocks/point
+ // blocks. Thus all row blocks containing an e_block/point occur
+ // contiguously. Further, if present, an e_block is always the first
+ // parameter block in each row block. These structural assumptions
+ // are common to all Schur complement based solvers in Ceres.
+ //
+ // For each e_block/point block we identify the set of cameras
+ // seeing it. The cross product of this set with itself is the set
+ // of non-zero cells contributed by this e_block.
+ //
+ // The time complexity of this is O(nm^2) where, n is the number of
+ // 3d points and m is the maximum number of cameras seeing any
+ // point, which for most scenes is a fairly small number.
+ while (r < num_row_blocks) {
+ int e_block_id = bs.rows[r].cells.front().block_id;
+ if (e_block_id >= num_eliminate_blocks) {
+ // Skip the rows whose first block is an f_block.
+ break;
+ }
+
+ set<int> f_blocks;
+ for (; r < num_row_blocks; ++r) {
+ const CompressedRow& row = bs.rows[r];
+ if (row.cells.front().block_id != e_block_id) {
+ break;
+ }
+
+ // Iterate over the blocks in the row, ignoring the first block
+ // since it is the one to be eliminated and adding the rest to
+ // the list of f_blocks associated with this e_block.
+ for (int c = 1; c < row.cells.size(); ++c) {
+ const Cell& cell = row.cells[c];
+ const int f_block_id = cell.block_id - num_eliminate_blocks;
+ CHECK_GE(f_block_id, 0);
+ f_blocks.insert(f_block_id);
+ }
+ }
+
+ for (set<int>::const_iterator block1 = f_blocks.begin();
+ block1 != f_blocks.end();
+ ++block1) {
+ set<int>::const_iterator block2 = block1;
+ ++block2;
+ for (; block2 != f_blocks.end(); ++block2) {
+ if (IsBlockPairInPreconditioner(*block1, *block2)) {
+ block_pairs_.insert(make_pair(*block1, *block2));
+ }
+ }
+ }
+ }
+
+ // The remaining rows which do not contain any e_blocks.
+ for (; r < num_row_blocks; ++r) {
+ const CompressedRow& row = bs.rows[r];
+ 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;
+ if (block1 <= block2) {
+ if (IsBlockPairInPreconditioner(block1, block2)) {
+ block_pairs_.insert(make_pair(block1, block2));
+ }
+ }
+ }
+ }
+ }
+
+ VLOG(1) << "Block pair stats: " << block_pairs_.size();
+}
+
+// Initialize the SchurEliminator.
+void VisibilityBasedPreconditioner::InitEliminator(
+ const CompressedRowBlockStructure& bs) {
+ LinearSolver::Options eliminator_options;
+ eliminator_options.elimination_groups = options_.elimination_groups;
+ eliminator_options.num_threads = options_.num_threads;
+ eliminator_options.e_block_size = options_.e_block_size;
+ 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));
+ const bool kFullRankETE = true;
+ eliminator_->Init(
+ eliminator_options.elimination_groups[0], kFullRankETE, &bs);
+}
+
+// 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 int num_rows = m_->num_rows();
+ CHECK_GT(num_rows, 0);
+
+ // Compute a subset of the entries of the Schur complement.
+ eliminator_->Eliminate(
+ BlockSparseMatrixData(A), nullptr, D, m_.get(), nullptr);
+
+ // Try factorizing the matrix. For CLUSTER_JACOBI, this should
+ // always succeed modulo some numerical/conditioning problems. For
+ // CLUSTER_TRIDIAGONAL, in general the preconditioner matrix as
+ // constructed is not positive definite. However, we will go ahead
+ // and try factorizing it. If it works, great, otherwise we scale
+ // all the cells in the preconditioner corresponding to the edges in
+ // the degree-2 forest and that guarantees positive
+ // definiteness. The proof of this fact can be found in Lemma 1 in
+ // "Visibility Based Preconditioning for Bundle Adjustment".
+ //
+ // Doing the factorization like this saves us matrix mass when
+ // scaling is not needed, which is quite often in our experience.
+ LinearSolverTerminationType status = Factorize();
+
+ if (status == LINEAR_SOLVER_FATAL_ERROR) {
+ return false;
+ }
+
+ // The scaling only affects the tri-diagonal case, since
+ // ScaleOffDiagonalBlocks only pays attention to the cells that
+ // belong to the edges of the degree-2 forest. In the CLUSTER_JACOBI
+ // case, the preconditioner is guaranteed to be positive
+ // semidefinite.
+ if (status == LINEAR_SOLVER_FAILURE && options_.type == CLUSTER_TRIDIAGONAL) {
+ VLOG(1) << "Unscaled factorization failed. Retrying with off-diagonal "
+ << "scaling";
+ ScaleOffDiagonalCells();
+ status = Factorize();
+ }
+
+ VLOG(2) << "Compute time: " << time(NULL) - start_time;
+ return (status == LINEAR_SOLVER_SUCCESS);
+}
+
+// Consider the preconditioner matrix as meta-block matrix, whose
+// blocks correspond to the clusters. Then cluster pairs corresponding
+// to edges in the degree-2 forest are off diagonal entries of this
+// matrix. Scaling these off-diagonal entries by 1/2 forces this
+// matrix to be positive definite.
+void VisibilityBasedPreconditioner::ScaleOffDiagonalCells() {
+ for (const auto& block_pair : block_pairs_) {
+ const int block1 = block_pair.first;
+ const int block2 = block_pair.second;
+ if (!IsBlockPairOffDiagonal(block1, block2)) {
+ continue;
+ }
+
+ int r, c, row_stride, col_stride;
+ CellInfo* cell_info =
+ m_->GetCell(block1, block2, &r, &c, &row_stride, &col_stride);
+ CHECK(cell_info != NULL)
+ << "Cell missing for block pair (" << block1 << "," << block2 << ")"
+ << " cluster pair (" << cluster_membership_[block1] << " "
+ << cluster_membership_[block2] << ")";
+
+ // Ah the magic of tri-diagonal matrices and diagonal
+ // dominance. See Lemma 1 in "Visibility Based Preconditioning
+ // For Bundle Adjustment".
+ MatrixRef m(cell_info->values, row_stride, col_stride);
+ m.block(r, c, block_size_[block1], block_size_[block2]) *= 0.5;
+ }
+}
+
+// Compute the sparse Cholesky factorization of the preconditioner
+// matrix.
+LinearSolverTerminationType VisibilityBasedPreconditioner::Factorize() {
+ // Extract the TripletSparseMatrix that is used for actually storing
+ // S and convert it into a CompressedRowSparseMatrix.
+ const TripletSparseMatrix* tsm =
+ down_cast<BlockRandomAccessSparseMatrix*>(m_.get())->mutable_matrix();
+
+ std::unique_ptr<CompressedRowSparseMatrix> lhs;
+ const CompressedRowSparseMatrix::StorageType storage_type =
+ sparse_cholesky_->StorageType();
+ if (storage_type == CompressedRowSparseMatrix::UPPER_TRIANGULAR) {
+ lhs.reset(CompressedRowSparseMatrix::FromTripletSparseMatrix(*tsm));
+ lhs->set_storage_type(CompressedRowSparseMatrix::UPPER_TRIANGULAR);
+ } else {
+ lhs.reset(
+ CompressedRowSparseMatrix::FromTripletSparseMatrixTransposed(*tsm));
+ lhs->set_storage_type(CompressedRowSparseMatrix::LOWER_TRIANGULAR);
+ }
+
+ std::string message;
+ return sparse_cholesky_->Factorize(lhs.get(), &message);
+}
+
+void VisibilityBasedPreconditioner::RightMultiply(const double* x,
+ double* y) const {
+ CHECK(x != nullptr);
+ CHECK(y != nullptr);
+ CHECK(sparse_cholesky_ != nullptr);
+ std::string message;
+ sparse_cholesky_->Solve(x, y, &message);
+}
+
+int VisibilityBasedPreconditioner::num_rows() const { return m_->num_rows(); }
+
+// Classify camera/f_block pairs as in and out of the preconditioner,
+// based on whether the cluster pair that they belong to is in the
+// preconditioner or not.
+bool VisibilityBasedPreconditioner::IsBlockPairInPreconditioner(
+ const int block1, const int block2) const {
+ int cluster1 = cluster_membership_[block1];
+ int cluster2 = cluster_membership_[block2];
+ if (cluster1 > cluster2) {
+ swap(cluster1, cluster2);
+ }
+ return (cluster_pairs_.count(make_pair(cluster1, cluster2)) > 0);
+}
+
+bool VisibilityBasedPreconditioner::IsBlockPairOffDiagonal(
+ const int block1, const int block2) const {
+ return (cluster_membership_[block1] != cluster_membership_[block2]);
+}
+
+// Convert a graph into a list of edges that includes self edges for
+// each vertex.
+void VisibilityBasedPreconditioner::ForestToClusterPairs(
+ const WeightedGraph<int>& forest,
+ std::unordered_set<pair<int, int>, pair_hash>* cluster_pairs) const {
+ CHECK(cluster_pairs != nullptr);
+ cluster_pairs->clear();
+ const std::unordered_set<int>& vertices = forest.vertices();
+ CHECK_EQ(vertices.size(), num_clusters_);
+
+ // Add all the cluster pairs corresponding to the edges in the
+ // forest.
+ for (const int cluster1 : vertices) {
+ cluster_pairs->insert(make_pair(cluster1, cluster1));
+ const std::unordered_set<int>& neighbors = forest.Neighbors(cluster1);
+ for (const int cluster2 : neighbors) {
+ if (cluster1 < cluster2) {
+ cluster_pairs->insert(make_pair(cluster1, cluster2));
+ }
+ }
+ }
+}
+
+// The visibility set of a cluster is the union of the visibility sets
+// of all its cameras. In other words, the set of points visible to
+// any camera in the cluster.
+void VisibilityBasedPreconditioner::ComputeClusterVisibility(
+ const vector<set<int>>& visibility,
+ vector<set<int>>* cluster_visibility) const {
+ CHECK(cluster_visibility != nullptr);
+ cluster_visibility->resize(0);
+ cluster_visibility->resize(num_clusters_);
+ for (int i = 0; i < num_blocks_; ++i) {
+ const int cluster_id = cluster_membership_[i];
+ (*cluster_visibility)[cluster_id].insert(visibility[i].begin(),
+ visibility[i].end());
+ }
+}
+
+// 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(
+ const vector<set<int>>& cluster_visibility) const {
+ WeightedGraph<int>* cluster_graph = new WeightedGraph<int>;
+
+ for (int i = 0; i < num_clusters_; ++i) {
+ cluster_graph->AddVertex(i);
+ }
+
+ for (int i = 0; i < num_clusters_; ++i) {
+ const set<int>& cluster_i = cluster_visibility[i];
+ for (int j = i + 1; j < num_clusters_; ++j) {
+ vector<int> intersection;
+ const set<int>& cluster_j = cluster_visibility[j];
+ set_intersection(cluster_i.begin(),
+ cluster_i.end(),
+ cluster_j.begin(),
+ cluster_j.end(),
+ back_inserter(intersection));
+
+ if (intersection.size() > 0) {
+ // Clusters interact strongly when they share a large number
+ // of 3D points. The degree-2 maximum spanning forest
+ // algorithm, iterates on the edges in decreasing order of
+ // their weight, which is the number of points shared by the
+ // two cameras that it connects.
+ cluster_graph->AddEdge(i, j, intersection.size());
+ }
+ }
+ }
+ return cluster_graph;
+}
+
+// Canonical views clustering returns a std::unordered_map from vertices to
+// cluster ids. Convert this into a flat array for quick lookup. It is
+// possible that some of the vertices may not be associated with any
+// cluster. In that case, randomly assign them to one of the clusters.
+//
+// The cluster ids can be non-contiguous integers. So as we flatten
+// the membership_map, we also map the cluster ids to a contiguous set
+// of integers so that the cluster ids are in [0, num_clusters_).
+void VisibilityBasedPreconditioner::FlattenMembershipMap(
+ const std::unordered_map<int, int>& membership_map,
+ vector<int>* membership_vector) const {
+ CHECK(membership_vector != nullptr);
+ membership_vector->resize(0);
+ membership_vector->resize(num_blocks_, -1);
+
+ std::unordered_map<int, int> cluster_id_to_index;
+ // Iterate over the cluster membership map and update the
+ // cluster_membership_ vector assigning arbitrary cluster ids to
+ // the few cameras that have not been clustered.
+ for (const auto& m : membership_map) {
+ const int camera_id = m.first;
+ int cluster_id = m.second;
+
+ // If the view was not clustered, randomly assign it to one of the
+ // clusters. This preserves the mathematical correctness of the
+ // preconditioner. If there are too many views which are not
+ // clustered, it may lead to some quality degradation though.
+ //
+ // TODO(sameeragarwal): Check if a large number of views have not
+ // been clustered and deal with it?
+ if (cluster_id == -1) {
+ cluster_id = camera_id % num_clusters_;
+ }
+
+ const int index = FindWithDefault(
+ cluster_id_to_index, cluster_id, cluster_id_to_index.size());
+
+ if (index == cluster_id_to_index.size()) {
+ cluster_id_to_index[cluster_id] = index;
+ }
+
+ CHECK_LT(index, num_clusters_);
+ membership_vector->at(camera_id) = index;
+ }
+}
+
+} // namespace internal
+} // namespace ceres
diff --git a/extern/ceres/internal/ceres/visibility_based_preconditioner.h b/extern/ceres/internal/ceres/visibility_based_preconditioner.h
index a627c13523c..aa582d5e7ef 100644
--- a/extern/ceres/internal/ceres/visibility_based_preconditioner.h
+++ b/extern/ceres/internal/ceres/visibility_based_preconditioner.h
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -48,16 +48,18 @@
#ifndef CERES_INTERNAL_VISIBILITY_BASED_PRECONDITIONER_H_
#define CERES_INTERNAL_VISIBILITY_BASED_PRECONDITIONER_H_
+#include <memory>
#include <set>
-#include <vector>
+#include <unordered_map>
+#include <unordered_set>
#include <utility>
-#include "ceres/collections_port.h"
+#include <vector>
+
#include "ceres/graph.h"
-#include "ceres/internal/macros.h"
-#include "ceres/internal/scoped_ptr.h"
#include "ceres/linear_solver.h"
+#include "ceres/pair_hash.h"
#include "ceres/preconditioner.h"
-#include "ceres/suitesparse.h"
+#include "ceres/sparse_cholesky.h"
namespace ceres {
namespace internal {
@@ -122,8 +124,6 @@ class SchurEliminatorBase;
// *A.block_structure(), options);
// preconditioner.Update(A, NULL);
// preconditioner.RightMultiply(x, y);
-//
-#ifndef CERES_NO_SUITESPARSE
class VisibilityBasedPreconditioner : public BlockSparseMatrixPreconditioner {
public:
// Initialize the symbolic structure of the preconditioner. bs is
@@ -134,16 +134,19 @@ class VisibilityBasedPreconditioner : public BlockSparseMatrixPreconditioner {
// based solvers. Please see schur_eliminator.h for more details.
VisibilityBasedPreconditioner(const CompressedRowBlockStructure& bs,
const Preconditioner::Options& options);
+ VisibilityBasedPreconditioner(const VisibilityBasedPreconditioner&) = delete;
+ void operator=(const VisibilityBasedPreconditioner&) = delete;
+
virtual ~VisibilityBasedPreconditioner();
// Preconditioner interface
- virtual void RightMultiply(const double* x, double* y) const;
- virtual int num_rows() const;
+ void RightMultiply(const double* x, double* y) const final;
+ int num_rows() const final;
friend class VisibilityBasedPreconditionerTest;
private:
- virtual bool UpdateImpl(const BlockSparseMatrix& A, const double* D);
+ bool UpdateImpl(const BlockSparseMatrix& A, const double* D) final;
void ComputeClusterJacobiSparsity(const CompressedRowBlockStructure& bs);
void ComputeClusterTridiagonalSparsity(const CompressedRowBlockStructure& bs);
void InitStorage(const CompressedRowBlockStructure& bs);
@@ -151,16 +154,16 @@ class VisibilityBasedPreconditioner : public BlockSparseMatrixPreconditioner {
LinearSolverTerminationType Factorize();
void ScaleOffDiagonalCells();
- void ClusterCameras(const std::vector<std::set<int> >& visibility);
- void FlattenMembershipMap(const HashMap<int, int>& membership_map,
+ void ClusterCameras(const std::vector<std::set<int>>& visibility);
+ void FlattenMembershipMap(const std::unordered_map<int, int>& membership_map,
std::vector<int>* membership_vector) const;
void ComputeClusterVisibility(
- const std::vector<std::set<int> >& visibility,
- std::vector<std::set<int> >* cluster_visibility) const;
+ const std::vector<std::set<int>>& visibility,
+ std::vector<std::set<int>>* cluster_visibility) const;
WeightedGraph<int>* CreateClusterGraph(
- const std::vector<std::set<int> >& visibility) const;
+ const std::vector<std::set<int>>& visibility) const;
void ForestToClusterPairs(const WeightedGraph<int>& forest,
- HashSet<std::pair<int, int> >* cluster_pairs) const;
+ std::unordered_set<std::pair<int, int>, pair_hash>* cluster_pairs) const;
void ComputeBlockPairsInPreconditioner(const CompressedRowBlockStructure& bs);
bool IsBlockPairInPreconditioner(int block1, int block2) const;
bool IsBlockPairOffDiagonal(int block1, int block2) const;
@@ -180,52 +183,17 @@ class VisibilityBasedPreconditioner : public BlockSparseMatrixPreconditioner {
// Non-zero camera pairs from the schur complement matrix that are
// present in the preconditioner, sorted by row (first element of
// each pair), then column (second).
- std::set<std::pair<int, int> > block_pairs_;
+ std::set<std::pair<int, int>> block_pairs_;
// Set of cluster pairs (including self pairs (i,i)) in the
// preconditioner.
- HashSet<std::pair<int, int> > cluster_pairs_;
- scoped_ptr<SchurEliminatorBase> eliminator_;
+ std::unordered_set<std::pair<int, int>, pair_hash> cluster_pairs_;
+ std::unique_ptr<SchurEliminatorBase> eliminator_;
// Preconditioner matrix.
- scoped_ptr<BlockRandomAccessSparseMatrix> m_;
-
- // RightMultiply is a const method for LinearOperators. It is
- // implemented using CHOLMOD's sparse triangular matrix solve
- // function. This however requires non-const access to the
- // SuiteSparse context object, even though it does not result in any
- // of the state of the preconditioner being modified.
- SuiteSparse ss_;
-
- // Symbolic and numeric factorization of the preconditioner.
- cholmod_factor* factor_;
-
- // Temporary vector used by RightMultiply.
- cholmod_dense* tmp_rhs_;
- CERES_DISALLOW_COPY_AND_ASSIGN(VisibilityBasedPreconditioner);
-};
-#else // SuiteSparse
-// If SuiteSparse is not compiled in, the preconditioner is not
-// available.
-class VisibilityBasedPreconditioner : public BlockSparseMatrixPreconditioner {
- public:
- VisibilityBasedPreconditioner(const CompressedRowBlockStructure& bs,
- const Preconditioner::Options& options) {
- LOG(FATAL) << "Visibility based preconditioning is not available. Please "
- "build Ceres with SuiteSparse.";
- }
- virtual ~VisibilityBasedPreconditioner() {}
- virtual void RightMultiply(const double* x, double* y) const {}
- virtual void LeftMultiply(const double* x, double* y) const {}
- virtual int num_rows() const { return -1; }
- virtual int num_cols() const { return -1; }
-
- private:
- bool UpdateImpl(const BlockSparseMatrix& A, const double* D) {
- return false;
- }
+ std::unique_ptr<BlockRandomAccessSparseMatrix> m_;
+ std::unique_ptr<SparseCholesky> sparse_cholesky_;
};
-#endif // CERES_NO_SUITESPARSE
} // namespace internal
} // namespace ceres
diff --git a/extern/ceres/internal/ceres/wall_time.cc b/extern/ceres/internal/ceres/wall_time.cc
index c353973cc3e..716392741e9 100644
--- a/extern/ceres/internal/ceres/wall_time.cc
+++ b/extern/ceres/internal/ceres/wall_time.cc
@@ -50,7 +50,12 @@ double WallTimeInSeconds() {
return omp_get_wtime();
#else
#ifdef _WIN32
- return static_cast<double>(std::time(NULL));
+ LARGE_INTEGER count;
+ LARGE_INTEGER frequency;
+ QueryPerformanceCounter(&count);
+ QueryPerformanceFrequency(&frequency);
+ return static_cast<double>(count.QuadPart) /
+ static_cast<double>(frequency.QuadPart);
#else
timeval time_val;
gettimeofday(&time_val, NULL);
@@ -59,20 +64,24 @@ double WallTimeInSeconds() {
#endif
}
-EventLogger::EventLogger(const std::string& logger_name)
- : start_time_(WallTimeInSeconds()),
- last_event_time_(start_time_),
- events_("") {
- StringAppendF(&events_,
- "\n%s\n Delta Cumulative\n",
- logger_name.c_str());
+EventLogger::EventLogger(const std::string& logger_name) {
+ if (!VLOG_IS_ON(3)) {
+ return;
+ }
+
+ start_time_ = WallTimeInSeconds();
+ last_event_time_ = start_time_;
+ events_ = StringPrintf(
+ "\n%s\n Delta Cumulative\n",
+ logger_name.c_str());
}
EventLogger::~EventLogger() {
- if (VLOG_IS_ON(3)) {
- AddEvent("Total");
- VLOG(2) << "\n" << events_ << "\n";
+ if (!VLOG_IS_ON(3)) {
+ return;
}
+ AddEvent("Total");
+ VLOG(3) << "\n" << events_ << "\n";
}
void EventLogger::AddEvent(const std::string& event_name) {
diff --git a/extern/ceres/internal/ceres/wall_time.h b/extern/ceres/internal/ceres/wall_time.h
index 966aa67cab6..ed0610f27da 100644
--- a/extern/ceres/internal/ceres/wall_time.h
+++ b/extern/ceres/internal/ceres/wall_time.h
@@ -77,7 +77,7 @@ class EventLogger {
void AddEvent(const std::string& event_name);
private:
- const double start_time_;
+ double start_time_;
double last_event_time_;
std::string events_;
};
diff --git a/extern/ceres/patches/series b/extern/ceres/patches/series
index e69de29bb2d..7fa3673acac 100644
--- a/extern/ceres/patches/series
+++ b/extern/ceres/patches/series
@@ -0,0 +1,2 @@
+unused_parameter.patch
+unused_variable.patch
diff --git a/extern/ceres/patches/unused_parameter.patch b/extern/ceres/patches/unused_parameter.patch
new file mode 100644
index 00000000000..14969d6a19f
--- /dev/null
+++ b/extern/ceres/patches/unused_parameter.patch
@@ -0,0 +1,13 @@
+diff --git a/extern/ceres/include/ceres/internal/autodiff.h b/extern/ceres/include/ceres/internal/autodiff.h
+index 71b7bae4757..cb7b1aca5b9 100644
+--- a/include/ceres/internal/autodiff.h
++++ b/include/ceres/internal/autodiff.h
+@@ -198,7 +198,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* /*src*/, JetT* /*dst*/) {}
+ };
+
+ // Calls Make1stOrderPerturbation for every parameter block.
diff --git a/extern/ceres/patches/unused_variable.patch b/extern/ceres/patches/unused_variable.patch
new file mode 100644
index 00000000000..24a4f392962
--- /dev/null
+++ b/extern/ceres/patches/unused_variable.patch
@@ -0,0 +1,12 @@
+diff --git a/extern/ceres/internal/ceres/sparse_cholesky.cc b/extern/ceres/internal/ceres/sparse_cholesky.cc
+index 0639ea90664..d9d2100d3f9 100644
+--- a/internal/ceres/sparse_cholesky.cc
++++ b/internal/ceres/sparse_cholesky.cc
+@@ -56,6 +56,7 @@ std::unique_ptr<SparseCholesky> SparseCholesky::Create(
+ }
+ break;
+ #else
++ (void)ordering_type;
+ LOG(FATAL) << "Ceres was compiled without support for SuiteSparse.";
+ #endif
+
diff --git a/extern/draco/CMakeLists.txt b/extern/draco/CMakeLists.txt
index a3f9a8b9561..6961fa8a769 100644
--- a/extern/draco/CMakeLists.txt
+++ b/extern/draco/CMakeLists.txt
@@ -18,8 +18,6 @@
# All rights reserved.
# ***** END GPL LICENSE BLOCK *****
-set(CMAKE_CXX_STANDARD 14)
-
# Build Draco library.
add_subdirectory(dracoenc)
diff --git a/extern/glog/AUTHORS b/extern/glog/AUTHORS
index 8e654101b7c..e5111cea7dd 100644
--- a/extern/glog/AUTHORS
+++ b/extern/glog/AUTHORS
@@ -10,6 +10,7 @@
Abhishek Dasgupta <abhi2743@gmail.com>
Abhishek Parmar <abhishek@orng.net>
+Andrew Schwartzmeyer <andrew@schwartzmeyer.com>
Andy Ying <andy@trailofbits.com>
Brian Silverman <bsilver16384@gmail.com>
Google Inc.
@@ -17,6 +18,8 @@ Guillaume Dumont <dumont.guillaume@gmail.com>
Michael Tanner <michael@tannertaxpro.com>
MiniLight <MiniLightAR@Gmail.com>
romange <romange@users.noreply.github.com>
+Roman Perepelitsa <roman.perepelitsa@gmail.com>
Sergiu Deitsch <sergiu.deitsch@gmail.com>
tbennun <tbennun@gmail.com>
Teddy Reed <teddy@prosauce.org>
+Zhongming Qu <qzmfranklin@gmail.com>
diff --git a/extern/glog/CMakeLists.txt b/extern/glog/CMakeLists.txt
index bfe715d3463..2a9736425a8 100644
--- a/extern/glog/CMakeLists.txt
+++ b/extern/glog/CMakeLists.txt
@@ -27,11 +27,16 @@ set(INC_SYS
)
set(SRC
+ src/demangle.cc
src/logging.cc
src/raw_logging.cc
+ src/signalhandler.cc
+ src/symbolize.cc
src/utilities.cc
src/vlog_is_on.cc
+ src/demangle.h
+ src/symbolize.h
src/utilities.h
src/config.h
@@ -51,6 +56,7 @@ set(SRC
src/stacktrace_powerpc-inl.h
src/stacktrace_x86_64-inl.h
src/stacktrace_x86-inl.h
+ src/stacktrace_windows-inl.h
)
set(LIB
@@ -87,13 +93,6 @@ else()
include
)
list(APPEND SRC
- src/demangle.cc
- src/signalhandler.cc
- src/symbolize.cc
-
- src/demangle.h
- src/symbolize.h
-
include/glog/logging.h
include/glog/log_severity.h
include/glog/raw_logging.h
diff --git a/extern/glog/ChangeLog b/extern/glog/ChangeLog
index d1b42484416..f8e43f8b39d 100644
--- a/extern/glog/ChangeLog
+++ b/extern/glog/ChangeLog
@@ -1,3 +1,18 @@
+2019-01-22 Google Inc. <opensource@google.com>
+
+ * google-glog: version 0.4.0.
+ * See git log for the details.
+
+2017-05-09 Google Inc. <opensource@google.com>
+
+ * google-glog: version 0.3.5
+ * See git log for the details.
+
+2015-03-09 Google Inc. <opensource@google.com>
+
+ * google-glog: version 0.3.4
+ * See git log for the details.
+
2013-02-01 Google Inc. <opensource@google.com>
* google-glog: version 0.3.3
diff --git a/extern/glog/README.blender b/extern/glog/README.blender
index 38d5ff05c86..62cd065ef0f 100644
--- a/extern/glog/README.blender
+++ b/extern/glog/README.blender
@@ -1,7 +1,7 @@
Project: Google Logging
URL: https://github.com/google/glog
License: New BSD
-Upstream version: 0.3.5, a6a166db069
+Upstream version: 0.4.0, 96a2f23dca4
Local modifications:
* Added per-platform config.h files so no configuration-time
checks for functions and so are needed.
diff --git a/extern/glog/include/glog/logging.h b/extern/glog/include/glog/logging.h
index 8238ca9610f..7254b8dd62c 100644
--- a/extern/glog/include/glog/logging.h
+++ b/extern/glog/include/glog/logging.h
@@ -431,9 +431,15 @@ DECLARE_bool(stop_logging_if_full_disk);
#define LOG_TO_STRING_FATAL(message) google::NullStreamFatal()
#endif
+#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
+#define DCHECK_IS_ON() 0
+#else
+#define DCHECK_IS_ON() 1
+#endif
+
// For DFATAL, we want to use LogMessage (as opposed to
// LogMessageFatal), to be consistent with the original behavior.
-#ifdef NDEBUG
+#if !DCHECK_IS_ON()
#define COMPACT_GOOGLE_LOG_DFATAL COMPACT_GOOGLE_LOG_ERROR
#elif GOOGLE_STRIP_LOG <= 3
#define COMPACT_GOOGLE_LOG_DFATAL google::LogMessage( \
@@ -562,8 +568,10 @@ class LogSink; // defined below
LOG_TO_STRING_##severity(static_cast<std::vector<std::string>*>(outvec)).stream()
#define LOG_IF(severity, condition) \
+ static_cast<void>(0), \
!(condition) ? (void) 0 : google::LogMessageVoidify() & LOG(severity)
#define SYSLOG_IF(severity, condition) \
+ static_cast<void>(0), \
!(condition) ? (void) 0 : google::LogMessageVoidify() & SYSLOG(severity)
#define LOG_ASSERT(condition) \
@@ -572,7 +580,7 @@ class LogSink; // defined below
SYSLOG_IF(FATAL, !(condition)) << "Assert failed: " #condition
// CHECK dies with a fatal error if condition is not true. It is *not*
-// controlled by NDEBUG, so the check will be executed regardless of
+// controlled by DCHECK_IS_ON(), so the check will be executed regardless of
// compilation mode. Therefore, it is safe to do things like:
// CHECK(fp->Write(x) == 4)
#define CHECK(condition) \
@@ -644,7 +652,7 @@ void MakeCheckOpValueString(std::ostream* os, const unsigned char& v);
// Build the error message string. Specify no inlining for code size.
template <typename T1, typename T2>
std::string* MakeCheckOpString(const T1& v1, const T2& v2, const char* exprtext)
- __attribute__ ((noinline));
+ __attribute__((noinline));
namespace base {
namespace internal {
@@ -722,7 +730,7 @@ DEFINE_CHECK_OP_IMPL(Check_GT, > )
#if defined(STATIC_ANALYSIS)
// Only for static analysis tool to know that it is equivalent to assert
#define CHECK_OP_LOG(name, op, val1, val2, log) CHECK((val1) op (val2))
-#elif !defined(NDEBUG)
+#elif DCHECK_IS_ON()
// In debug mode, avoid constructing CheckOpStrings if possible,
// to reduce the overhead of CHECK statments by 2x.
// Real DCHECK-heavy tests have seen 1.5x speedups.
@@ -751,7 +759,7 @@ typedef std::string _Check_string;
google::GetReferenceableValue(val2), \
#val1 " " #op " " #val2)) \
log(__FILE__, __LINE__, _result).stream()
-#endif // STATIC_ANALYSIS, !NDEBUG
+#endif // STATIC_ANALYSIS, DCHECK_IS_ON()
#if GOOGLE_STRIP_LOG <= 3
#define CHECK_OP(name, op, val1, val2) \
@@ -853,6 +861,7 @@ DECLARE_CHECK_STROP_IMPL(strcasecmp, false)
&google::LogMessage::SendToLog)
#define PLOG_IF(severity, condition) \
+ static_cast<void>(0), \
!(condition) ? (void) 0 : google::LogMessageVoidify() & PLOG(severity)
// A CHECK() macro that postpends errno if the condition is false. E.g.
@@ -925,16 +934,11 @@ struct CompileAssert {
struct CrashReason;
// Returns true if FailureSignalHandler is installed.
-bool IsFailureSignalHandlerInstalled();
+// Needs to be exported since it's used by the signalhandler_unittest.
+GOOGLE_GLOG_DLL_DECL bool IsFailureSignalHandlerInstalled();
} // namespace glog_internal_namespace_
-#define GOOGLE_GLOG_COMPILE_ASSERT(expr, msg) \
- typedef google::glog_internal_namespace_::CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1]
-
#define LOG_EVERY_N(severity, n) \
- GOOGLE_GLOG_COMPILE_ASSERT(google::GLOG_ ## severity < \
- google::NUM_SEVERITIES, \
- INVALID_REQUESTED_LOG_SEVERITY); \
SOME_KIND_OF_LOG_EVERY_N(severity, (n), google::LogMessage::SendToLog)
#define SYSLOG_EVERY_N(severity, n) \
@@ -976,7 +980,7 @@ const LogSeverity GLOG_0 = GLOG_ERROR;
// Plus some debug-logging macros that get compiled to nothing for production
-#ifndef NDEBUG
+#if DCHECK_IS_ON()
#define DLOG(severity) LOG(severity)
#define DVLOG(verboselevel) VLOG(verboselevel)
@@ -986,7 +990,7 @@ const LogSeverity GLOG_0 = GLOG_ERROR;
LOG_IF_EVERY_N(severity, condition, n)
#define DLOG_ASSERT(condition) LOG_ASSERT(condition)
-// debug-only checking. not executed in NDEBUG mode.
+// debug-only checking. executed if DCHECK_IS_ON().
#define DCHECK(condition) CHECK(condition)
#define DCHECK_EQ(val1, val2) CHECK_EQ(val1, val2)
#define DCHECK_NE(val1, val2) CHECK_NE(val1, val2)
@@ -1000,25 +1004,31 @@ const LogSeverity GLOG_0 = GLOG_ERROR;
#define DCHECK_STRNE(str1, str2) CHECK_STRNE(str1, str2)
#define DCHECK_STRCASENE(str1, str2) CHECK_STRCASENE(str1, str2)
-#else // NDEBUG
+#else // !DCHECK_IS_ON()
-#define DLOG(severity) \
+#define DLOG(severity) \
+ static_cast<void>(0), \
true ? (void) 0 : google::LogMessageVoidify() & LOG(severity)
-#define DVLOG(verboselevel) \
- (true || !VLOG_IS_ON(verboselevel)) ?\
- (void) 0 : google::LogMessageVoidify() & LOG(INFO)
+#define DVLOG(verboselevel) \
+ static_cast<void>(0), \
+ (true || !VLOG_IS_ON(verboselevel)) ? \
+ (void) 0 : google::LogMessageVoidify() & LOG(INFO)
#define DLOG_IF(severity, condition) \
+ static_cast<void>(0), \
(true || !(condition)) ? (void) 0 : google::LogMessageVoidify() & LOG(severity)
#define DLOG_EVERY_N(severity, n) \
+ static_cast<void>(0), \
true ? (void) 0 : google::LogMessageVoidify() & LOG(severity)
#define DLOG_IF_EVERY_N(severity, condition, n) \
+ static_cast<void>(0), \
(true || !(condition))? (void) 0 : google::LogMessageVoidify() & LOG(severity)
#define DLOG_ASSERT(condition) \
+ static_cast<void>(0), \
true ? (void) 0 : LOG_ASSERT(condition)
// MSVC warning C4127: conditional expression is constant
@@ -1081,7 +1091,7 @@ const LogSeverity GLOG_0 = GLOG_ERROR;
while (false) \
GLOG_MSVC_POP_WARNING() CHECK_STRCASENE(str1, str2)
-#endif // NDEBUG
+#endif // DCHECK_IS_ON()
// Log only in verbose mode.
@@ -1103,10 +1113,11 @@ namespace base_logging {
// buffer to allow for a '\n' and '\0'.
class GOOGLE_GLOG_DLL_DECL LogStreamBuf : public std::streambuf {
public:
- // REQUIREMENTS: "len" must be >= 2 to account for the '\n' and '\n'.
+ // REQUIREMENTS: "len" must be >= 2 to account for the '\n' and '\0'.
LogStreamBuf(char *buf, int len) {
setp(buf, buf + len - 2);
}
+
// This effectively ignores overflow.
virtual int_type overflow(int_type ch) {
return ch;
@@ -1145,13 +1156,9 @@ public:
// 2005 if you are deriving from a type in the Standard C++ Library"
// http://msdn.microsoft.com/en-us/library/3tdb471s(VS.80).aspx
// Let's just ignore the warning.
-#ifdef _MSC_VER
-# pragma warning(disable: 4275)
-#endif
+GLOG_MSVC_PUSH_DISABLE_WARNING(4275)
class GOOGLE_GLOG_DLL_DECL LogStream : public std::ostream {
-#ifdef _MSC_VER
-# pragma warning(default: 4275)
-#endif
+GLOG_MSVC_POP_WARNING()
public:
LogStream(char *buf, int len, int ctr)
: std::ostream(NULL),
@@ -1240,7 +1247,7 @@ public:
void SendToSyslogAndLog(); // Actually dispatch to syslog and the logs
// Call abort() or similar to perform LOG(FATAL) crash.
- static void __attribute__ ((noreturn)) Fail();
+ static void __attribute__((noreturn)) Fail();
std::ostream& stream();
@@ -1288,7 +1295,7 @@ class GOOGLE_GLOG_DLL_DECL LogMessageFatal : public LogMessage {
public:
LogMessageFatal(const char* file, int line);
LogMessageFatal(const char* file, int line, const CheckOpString& result);
- __attribute__ ((noreturn)) ~LogMessageFatal();
+ __attribute__((noreturn)) ~LogMessageFatal();
};
// A non-macro interface to the log facility; (useful
@@ -1303,6 +1310,35 @@ inline void LogAtLevel(int const severity, std::string const &msg) {
// LOG macros, 2. this macro can be used as C++ stream.
#define LOG_AT_LEVEL(severity) google::LogMessage(__FILE__, __LINE__, severity).stream()
+// Check if it's compiled in C++11 mode.
+//
+// GXX_EXPERIMENTAL_CXX0X is defined by gcc and clang up to at least
+// gcc-4.7 and clang-3.1 (2011-12-13). __cplusplus was defined to 1
+// in gcc before 4.7 (Crosstool 16) and clang before 3.1, but is
+// defined according to the language version in effect thereafter.
+// Microsoft Visual Studio 14 (2015) sets __cplusplus==199711 despite
+// reasonably good C++11 support, so we set LANG_CXX for it and
+// newer versions (_MSC_VER >= 1900).
+#if (defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L || \
+ (defined(_MSC_VER) && _MSC_VER >= 1900))
+// Helper for CHECK_NOTNULL().
+//
+// In C++11, all cases can be handled by a single function. Since the value
+// category of the argument is preserved (also for rvalue references),
+// member initializer lists like the one below will compile correctly:
+//
+// Foo()
+// : x_(CHECK_NOTNULL(MethodReturningUniquePtr())) {}
+template <typename T>
+T CheckNotNull(const char* file, int line, const char* names, T&& t) {
+ if (t == nullptr) {
+ LogMessageFatal(file, line, new std::string(names));
+ }
+ return std::forward<T>(t);
+}
+
+#else
+
// A small helper for CHECK_NOTNULL().
template <typename T>
T* CheckNotNull(const char *file, int line, const char *names, T* t) {
@@ -1311,6 +1347,7 @@ T* CheckNotNull(const char *file, int line, const char *names, T* t) {
}
return t;
}
+#endif
// Allow folks to put a counter in the LOG_EVERY_X()'ed messages. This
// only works if ostream is a LogStream. If the ostream is not a
@@ -1592,7 +1629,7 @@ class GOOGLE_GLOG_DLL_DECL NullStreamFatal : public NullStream {
NullStreamFatal() { }
NullStreamFatal(const char* file, int line, const CheckOpString& result) :
NullStream(file, line, result) { }
- __attribute__ ((noreturn)) ~NullStreamFatal() throw () { _exit(1); }
+ __attribute__((noreturn)) ~NullStreamFatal() throw () { _exit(1); }
};
// Install a signal handler that will dump signal information and a stack
diff --git a/extern/glog/include/glog/raw_logging.h b/extern/glog/include/glog/raw_logging.h
index 65278f62803..cf3f27d9516 100644
--- a/extern/glog/include/glog/raw_logging.h
+++ b/extern/glog/include/glog/raw_logging.h
@@ -173,12 +173,7 @@ GOOGLE_GLOG_DLL_DECL void RawLog__(LogSeverity severity,
const char* file,
int line,
const char* format, ...)
- __attribute__((__format__ (__printf__, 4, 5)));
-
-// Hack to propagate time information into this module so that
-// this module does not have to directly call localtime_r(),
-// which could allocate memory.
-GOOGLE_GLOG_DLL_DECL void RawLog__SetLastTime(const struct tm& t, int usecs);
+ ;
}
diff --git a/extern/glog/src/config.h b/extern/glog/src/config.h
index 2703b7ba9dd..e4cde736d65 100644
--- a/extern/glog/src/config.h
+++ b/extern/glog/src/config.h
@@ -1,7 +1,3 @@
-/* src/config.h. Generated from config.h.in by configure. */
-/* src/config.h.in. Generated from configure.ac by autoheader. */
-
-/* Namespace for Google classes */
#if defined(__APPLE__)
#include "config_mac.h"
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
diff --git a/extern/glog/src/config_mac.h b/extern/glog/src/config_mac.h
index 4f008b5f67c..57cc64a0a74 100644
--- a/extern/glog/src/config_mac.h
+++ b/extern/glog/src/config_mac.h
@@ -5,7 +5,7 @@
#define GOOGLE_NAMESPACE google
/* Define if you have the `dladdr' function */
-/* #undef HAVE_DLADDR */
+#define HAVE_DLADDR
/* Define if you have the `snprintf' function */
#define HAVE_SNPRINTF
@@ -65,7 +65,7 @@
#define HAVE_PWRITE
/* define if the compiler implements pthread_rwlock_* */
-/* #undef HAVE_RWLOCK */
+#define HAVE_RWLOCK
/* Define if you have the 'sigaction' function */
#define HAVE_SIGACTION
@@ -104,7 +104,7 @@
#define HAVE_SYS_TYPES_H 1
/* Define to 1 if you have the <sys/ucontext.h> header file. */
-#define HAVE_SYS_UCONTEXT_H 1
+/* #undef HAVE_SYS_UCONTEXT_H */
/* Define to 1 if you have the <sys/utsname.h> header file. */
#define HAVE_SYS_UTSNAME_H
diff --git a/extern/glog/src/demangle.cc b/extern/glog/src/demangle.cc
index e858181a68f..9369f9a65c2 100644
--- a/extern/glog/src/demangle.cc
+++ b/extern/glog/src/demangle.cc
@@ -30,15 +30,22 @@
// Author: Satoru Takabayashi
//
// For reference check out:
-// http://www.codesourcery.com/public/cxx-abi/abi.html#mangling
+// http://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling
//
// Note that we only have partial C++0x support yet.
#include <stdio.h> // for NULL
+#include "utilities.h"
#include "demangle.h"
+#if defined(OS_WINDOWS)
+#include <dbghelp.h>
+#pragma comment(lib, "dbghelp")
+#endif
+
_START_GOOGLE_NAMESPACE_
+#if !defined(OS_WINDOWS)
typedef struct {
const char *abbrev;
const char *real_name;
@@ -416,6 +423,8 @@ static bool ParseNumber(State *state, int *number_out);
static bool ParseFloatNumber(State *state);
static bool ParseSeqId(State *state);
static bool ParseIdentifier(State *state, int length);
+static bool ParseAbiTags(State *state);
+static bool ParseAbiTag(State *state);
static bool ParseOperatorName(State *state);
static bool ParseSpecialName(State *state);
static bool ParseCallOffset(State *state);
@@ -587,13 +596,13 @@ static bool ParsePrefix(State *state) {
// <unqualified-name> ::= <operator-name>
// ::= <ctor-dtor-name>
-// ::= <source-name>
-// ::= <local-source-name>
+// ::= <source-name> [<abi-tags>]
+// ::= <local-source-name> [<abi-tags>]
static bool ParseUnqualifiedName(State *state) {
return (ParseOperatorName(state) ||
ParseCtorDtorName(state) ||
- ParseSourceName(state) ||
- ParseLocalSourceName(state));
+ (ParseSourceName(state) && Optional(ParseAbiTags(state))) ||
+ (ParseLocalSourceName(state) && Optional(ParseAbiTags(state))));
}
// <source-name> ::= <positive length number> <identifier>
@@ -696,6 +705,23 @@ static bool ParseIdentifier(State *state, int length) {
return true;
}
+// <abi-tags> ::= <abi-tag> [<abi-tags>]
+static bool ParseAbiTags(State *state) {
+ State copy = *state;
+ DisableAppend(state);
+ if (OneOrMore(ParseAbiTag, state)) {
+ RestoreAppend(state, copy.append);
+ return true;
+ }
+ *state = copy;
+ return false;
+}
+
+// <abi-tag> ::= B <source-name>
+static bool ParseAbiTag(State *state) {
+ return ParseOneCharToken(state, 'B') && ParseSourceName(state);
+}
+
// <operator-name> ::= nw, and other two letters cases
// ::= cv <type> # (cast)
// ::= v <digit> <source-name> # vendor extended operator
@@ -1090,10 +1116,11 @@ static bool ParseTemplateArgs(State *state) {
// <template-arg> ::= <type>
// ::= <expr-primary>
// ::= I <template-arg>* E # argument pack
+// ::= J <template-arg>* E # argument pack
// ::= X <expression> E
static bool ParseTemplateArg(State *state) {
State copy = *state;
- if (ParseOneCharToken(state, 'I') &&
+ if ((ParseOneCharToken(state, 'I') || ParseOneCharToken(state, 'J')) &&
ZeroOrMore(ParseTemplateArg, state) &&
ParseOneCharToken(state, 'E')) {
return true;
@@ -1293,12 +1320,37 @@ static bool ParseTopLevelMangledName(State *state) {
}
return false;
}
+#endif
// The demangler entry point.
bool Demangle(const char *mangled, char *out, int out_size) {
+#if defined(OS_WINDOWS)
+ // When built with incremental linking, the Windows debugger
+ // library provides a more complicated `Symbol->Name` with the
+ // Incremental Linking Table offset, which looks like
+ // `@ILT+1105(?func@Foo@@SAXH@Z)`. However, the demangler expects
+ // only the mangled symbol, `?func@Foo@@SAXH@Z`. Fortunately, the
+ // mangled symbol is guaranteed not to have parentheses,
+ // so we search for `(` and extract up to `)`.
+ //
+ // Since we may be in a signal handler here, we cannot use `std::string`.
+ char buffer[1024]; // Big enough for a sane symbol.
+ const char *lparen = strchr(mangled, '(');
+ if (lparen) {
+ // Extract the string `(?...)`
+ const char *rparen = strchr(lparen, ')');
+ size_t length = rparen - lparen - 1;
+ strncpy(buffer, lparen + 1, length);
+ buffer[length] = '\0';
+ mangled = buffer;
+ } // Else the symbol wasn't inside a set of parentheses
+ // We use the ANSI version to ensure the string type is always `char *`.
+ return UnDecorateSymbolName(mangled, out, out_size, UNDNAME_COMPLETE);
+#else
State state;
InitState(&state, mangled, out, out_size);
return ParseTopLevelMangledName(&state) && !state.overflowed;
+#endif
}
_END_GOOGLE_NAMESPACE_
diff --git a/extern/glog/src/logging.cc b/extern/glog/src/logging.cc
index 3e8f48e814d..0c86cf62296 100644
--- a/extern/glog/src/logging.cc
+++ b/extern/glog/src/logging.cc
@@ -114,11 +114,6 @@ GLOG_DEFINE_bool(drop_log_memory, true, "Drop in-memory buffers of log contents.
"Logs can grow very quickly and they are rarely read before they "
"need to be evicted from memory. Instead, drop them from memory "
"as soon as they are flushed to disk.");
-_START_GOOGLE_NAMESPACE_
-namespace logging {
-static const int64 kPageSize = getpagesize();
-}
-_END_GOOGLE_NAMESPACE_
#endif
// By default, errors (including fatal errors) get logged to stderr as
@@ -188,7 +183,7 @@ GLOG_DEFINE_string(log_backtrace_at, "",
#ifndef HAVE_PREAD
#if defined(OS_WINDOWS)
-#include <BaseTsd.h>
+#include <basetsd.h>
#define ssize_t SSIZE_T
#endif
static ssize_t pread(int fd, void* buf, size_t count, off_t offset) {
@@ -304,7 +299,7 @@ static GLogColor SeverityToColor(LogSeverity severity) {
#ifdef OS_WINDOWS
// Returns the character attribute for the given color.
-WORD GetColorAttribute(GLogColor color) {
+static WORD GetColorAttribute(GLogColor color) {
switch (color) {
case COLOR_RED: return FOREGROUND_RED;
case COLOR_GREEN: return FOREGROUND_GREEN;
@@ -316,7 +311,7 @@ WORD GetColorAttribute(GLogColor color) {
#else
// Returns the ANSI color code for the given color.
-const char* GetAnsiColorCode(GLogColor color) {
+static const char* GetAnsiColorCode(GLogColor color) {
switch (color) {
case COLOR_RED: return "1";
case COLOR_GREEN: return "2";
@@ -441,6 +436,7 @@ class LogFileObject : public base::Logger {
FILE* file_;
LogSeverity severity_;
uint32 bytes_since_flush_;
+ uint32 dropped_mem_length_;
uint32 file_length_;
unsigned int rollover_attempt_;
int64 next_flush_time_; // cycle count at which to flush log
@@ -842,6 +838,7 @@ LogFileObject::LogFileObject(LogSeverity severity,
file_(NULL),
severity_(severity),
bytes_since_flush_(0),
+ dropped_mem_length_(0),
file_length_(0),
rollover_attempt_(kRolloverAttemptFrequency-1),
next_flush_time_(0) {
@@ -979,7 +976,7 @@ void LogFileObject::Write(bool force_flush,
PidHasChanged()) {
if (file_ != NULL) fclose(file_);
file_ = NULL;
- file_length_ = bytes_since_flush_ = 0;
+ file_length_ = bytes_since_flush_ = dropped_mem_length_ = 0;
rollover_attempt_ = kRolloverAttemptFrequency-1;
}
@@ -1119,11 +1116,17 @@ void LogFileObject::Write(bool force_flush,
(CycleClock_Now() >= next_flush_time_) ) {
FlushUnlocked();
#ifdef OS_LINUX
- if (FLAGS_drop_log_memory) {
- if (file_length_ >= logging::kPageSize) {
- // don't evict the most recent page
- uint32 len = file_length_ & ~(logging::kPageSize - 1);
- posix_fadvise(fileno(file_), 0, len, POSIX_FADV_DONTNEED);
+ // Only consider files >= 3MiB
+ if (FLAGS_drop_log_memory && file_length_ >= (3 << 20)) {
+ // Don't evict the most recent 1-2MiB so as not to impact a tailer
+ // of the log file and to avoid page rounding issue on linux < 4.7
+ uint32 total_drop_length = (file_length_ & ~((1 << 20) - 1)) - (1 << 20);
+ uint32 this_drop_length = total_drop_length - dropped_mem_length_;
+ if (this_drop_length >= (2 << 20)) {
+ // Only advise when >= 2MiB to drop
+ posix_fadvise(fileno(file_), dropped_mem_length_, this_drop_length,
+ POSIX_FADV_DONTNEED);
+ dropped_mem_length_ = total_drop_length;
}
}
#endif
@@ -1145,6 +1148,22 @@ static bool fatal_msg_exclusive = true;
static LogMessage::LogMessageData fatal_msg_data_exclusive;
static LogMessage::LogMessageData fatal_msg_data_shared;
+#ifdef GLOG_THREAD_LOCAL_STORAGE
+// Static thread-local log data space to use, because typically at most one
+// LogMessageData object exists (in this case glog makes zero heap memory
+// allocations).
+static GLOG_THREAD_LOCAL_STORAGE bool thread_data_available = true;
+
+#ifdef HAVE_ALIGNED_STORAGE
+static GLOG_THREAD_LOCAL_STORAGE
+ std::aligned_storage<sizeof(LogMessage::LogMessageData),
+ alignof(LogMessage::LogMessageData)>::type thread_msg_data;
+#else
+static GLOG_THREAD_LOCAL_STORAGE
+ char thread_msg_data[sizeof(void*) + sizeof(LogMessage::LogMessageData)];
+#endif // HAVE_ALIGNED_STORAGE
+#endif // defined(GLOG_THREAD_LOCAL_STORAGE)
+
LogMessage::LogMessageData::LogMessageData()
: stream_(message_text_, LogMessage::kMaxLogMessageLen, 0) {
}
@@ -1201,8 +1220,28 @@ void LogMessage::Init(const char* file,
void (LogMessage::*send_method)()) {
allocated_ = NULL;
if (severity != GLOG_FATAL || !exit_on_dfatal) {
+#ifdef GLOG_THREAD_LOCAL_STORAGE
+ // No need for locking, because this is thread local.
+ if (thread_data_available) {
+ thread_data_available = false;
+#ifdef HAVE_ALIGNED_STORAGE
+ data_ = new (&thread_msg_data) LogMessageData;
+#else
+ const uintptr_t kAlign = sizeof(void*) - 1;
+
+ char* align_ptr =
+ reinterpret_cast<char*>(reinterpret_cast<uintptr_t>(thread_msg_data + kAlign) & ~kAlign);
+ data_ = new (align_ptr) LogMessageData;
+ assert(reinterpret_cast<uintptr_t>(align_ptr) % sizeof(void*) == 0);
+#endif
+ } else {
+ allocated_ = new LogMessageData();
+ data_ = allocated_;
+ }
+#else // !defined(GLOG_THREAD_LOCAL_STORAGE)
allocated_ = new LogMessageData();
data_ = allocated_;
+#endif // defined(GLOG_THREAD_LOCAL_STORAGE)
data_->first_fatal_ = false;
} else {
MutexLock l(&fatal_msg_lock);
@@ -1227,7 +1266,6 @@ void LogMessage::Init(const char* file,
data_->timestamp_ = static_cast<time_t>(now);
localtime_r(&data_->timestamp_, &data_->tm_time_);
int usecs = static_cast<int>((now - data_->timestamp_) * 1000000);
- RawLog__SetLastTime(data_->tm_time_, usecs);
data_->num_chars_to_log_ = 0;
data_->num_chars_to_syslog_ = 0;
@@ -1271,7 +1309,17 @@ void LogMessage::Init(const char* file,
LogMessage::~LogMessage() {
Flush();
+#ifdef GLOG_THREAD_LOCAL_STORAGE
+ if (data_ == static_cast<void*>(&thread_msg_data)) {
+ data_->~LogMessageData();
+ thread_data_available = true;
+ }
+ else {
+ delete allocated_;
+ }
+#else // !defined(GLOG_THREAD_LOCAL_STORAGE)
delete allocated_;
+#endif // defined(GLOG_THREAD_LOCAL_STORAGE)
}
int LogMessage::preserved_errno() const {
@@ -1466,16 +1514,13 @@ void LogMessage::RecordCrashReason(
# define ATTRIBUTE_NORETURN
#endif
+#if defined(OS_WINDOWS)
+__declspec(noreturn)
+#endif
static void logging_fail() ATTRIBUTE_NORETURN;
static void logging_fail() {
-#if defined(_DEBUG) && defined(_MSC_VER)
- // When debugging on windows, avoid the obnoxious dialog and make
- // it possible to continue past a LOG(FATAL) in the debugger
- __debugbreak();
-#else
abort();
-#endif
}
typedef void (*logging_fail_func_t)() ATTRIBUTE_NORETURN;
@@ -1681,6 +1726,7 @@ void LogToStderr() {
namespace base {
namespace internal {
+bool GetExitOnDFatal();
bool GetExitOnDFatal() {
MutexLock l(&log_mutex);
return exit_on_dfatal;
@@ -1696,6 +1742,7 @@ bool GetExitOnDFatal() {
// and the stack trace is not recorded. The LOG(FATAL) *will* still
// exit the program. Since this function is used only in testing,
// these differences are acceptable.
+void SetExitOnDFatal(bool value);
void SetExitOnDFatal(bool value) {
MutexLock l(&log_mutex);
exit_on_dfatal = value;
@@ -1704,6 +1751,42 @@ void SetExitOnDFatal(bool value) {
} // namespace internal
} // namespace base
+// Shell-escaping as we need to shell out ot /bin/mail.
+static const char kDontNeedShellEscapeChars[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789+-_.=/:,@";
+
+static string ShellEscape(const string& src) {
+ string result;
+ if (!src.empty() && // empty string needs quotes
+ src.find_first_not_of(kDontNeedShellEscapeChars) == string::npos) {
+ // only contains chars that don't need quotes; it's fine
+ result.assign(src);
+ } else if (src.find_first_of('\'') == string::npos) {
+ // no single quotes; just wrap it in single quotes
+ result.assign("'");
+ result.append(src);
+ result.append("'");
+ } else {
+ // needs double quote escaping
+ result.assign("\"");
+ for (size_t i = 0; i < src.size(); ++i) {
+ switch (src[i]) {
+ case '\\':
+ case '$':
+ case '"':
+ case '`':
+ result.append("\\");
+ }
+ result.append(src, i, 1);
+ }
+ result.append("\"");
+ }
+ return result;
+}
+
+
// use_logging controls whether the logging functions LOG/VLOG are used
// to log errors. It should be set to false when the caller holds the
// log_mutex.
@@ -1719,7 +1802,10 @@ static bool SendEmailInternal(const char*dest, const char *subject,
}
string cmd =
- FLAGS_logmailer + " -s\"" + subject + "\" " + dest;
+ FLAGS_logmailer + " -s" +
+ ShellEscape(subject) + " " + ShellEscape(dest);
+ VLOG(4) << "Mailing command: " << cmd;
+
FILE* pipe = popen(cmd.c_str(), "w");
if (pipe != NULL) {
// Add the body if we have one
diff --git a/extern/glog/src/raw_logging.cc b/extern/glog/src/raw_logging.cc
index 7a7409bbf34..3bbfda31868 100644
--- a/extern/glog/src/raw_logging.cc
+++ b/extern/glog/src/raw_logging.cc
@@ -68,17 +68,6 @@
_START_GOOGLE_NAMESPACE_
-// Data for RawLog__ below. We simply pick up the latest
-// time data created by a normal log message to avoid calling
-// localtime_r which can allocate memory.
-static struct ::tm last_tm_time_for_raw_log;
-static int last_usecs_for_raw_log;
-
-void RawLog__SetLastTime(const struct ::tm& t, int usecs) {
- memcpy(&last_tm_time_for_raw_log, &t, sizeof(last_tm_time_for_raw_log));
- last_usecs_for_raw_log = usecs;
-}
-
// CAVEAT: vsnprintf called from *DoRawLog below has some (exotic) code paths
// that invoke malloc() and getenv() that might acquire some locks.
// If this becomes a problem we should reimplement a subset of vsnprintf
@@ -120,16 +109,13 @@ void RawLog__(LogSeverity severity, const char* file, int line,
return; // this stderr log message is suppressed
}
// can't call localtime_r here: it can allocate
- struct ::tm& t = last_tm_time_for_raw_log;
char buffer[kLogBufSize];
char* buf = buffer;
int size = sizeof(buffer);
// NOTE: this format should match the specification in base/logging.h
- DoRawLog(&buf, &size, "%c%02d%02d %02d:%02d:%02d.%06d %5u %s:%d] RAW: ",
+ DoRawLog(&buf, &size, "%c0000 00:00:00.000000 %5u %s:%d] RAW: ",
LogSeverityNames[severity][0],
- 1 + t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec,
- last_usecs_for_raw_log,
static_cast<unsigned int>(GetTID()),
const_basename(const_cast<char *>(file)), line);
diff --git a/extern/glog/src/signalhandler.cc b/extern/glog/src/signalhandler.cc
index a7aef8b99d2..961ee965196 100644
--- a/extern/glog/src/signalhandler.cc
+++ b/extern/glog/src/signalhandler.cc
@@ -48,9 +48,6 @@
_START_GOOGLE_NAMESPACE_
-// TOOD(hamaji): Use signal instead of sigaction?
-#ifdef HAVE_SIGACTION
-
namespace {
// We'll install the failure signal handler for these signals. We could
@@ -66,10 +63,14 @@ const struct {
{ SIGILL, "SIGILL" },
{ SIGFPE, "SIGFPE" },
{ SIGABRT, "SIGABRT" },
+#if !defined(OS_WINDOWS)
{ SIGBUS, "SIGBUS" },
+#endif
{ SIGTERM, "SIGTERM" },
};
+static bool kFailureSignalHandlerInstalled = false;
+
// Returns the program counter from signal context, NULL if unknown.
void* GetPC(void* ucontext_in_void) {
#if (defined(HAVE_UCONTEXT_H) || defined(HAVE_SYS_UCONTEXT_H)) && defined(PC_FROM_UCONTEXT)
@@ -92,7 +93,7 @@ class MinimalFormatter {
}
// Returns the number of bytes written in the buffer.
- int num_bytes_written() const { return cursor_ - buffer_; }
+ int num_bytes_written() const { return (int) (cursor_ - buffer_); }
// Appends string from "str" and updates the internal cursor.
void AppendString(const char* str) {
@@ -168,6 +169,9 @@ void DumpTimeInfo() {
g_failure_writer(buf, formatter.num_bytes_written());
}
+// TOOD(hamaji): Use signal instead of sigaction?
+#ifdef HAVE_SIGACTION
+
// Dumps information about the signal to STDERR.
void DumpSignalInfo(int signal_number, siginfo_t *siginfo) {
// Get the signal name.
@@ -213,6 +217,8 @@ void DumpSignalInfo(int signal_number, siginfo_t *siginfo) {
g_failure_writer(buf, formatter.num_bytes_written());
}
+#endif // HAVE_SIGACTION
+
// Dumps information about the stack frame to STDERR.
void DumpStackFrameInfo(const char* prefix, void* pc) {
// Get the symbol name.
@@ -240,12 +246,17 @@ void DumpStackFrameInfo(const char* prefix, void* pc) {
// Invoke the default signal handler.
void InvokeDefaultSignalHandler(int signal_number) {
+#ifdef HAVE_SIGACTION
struct sigaction sig_action;
memset(&sig_action, 0, sizeof(sig_action));
sigemptyset(&sig_action.sa_mask);
sig_action.sa_handler = SIG_DFL;
sigaction(signal_number, &sig_action, NULL);
kill(getpid(), signal_number);
+#elif defined(OS_WINDOWS)
+ signal(signal_number, SIG_DFL);
+ raise(signal_number);
+#endif
}
// This variable is used for protecting FailureSignalHandler() from
@@ -256,9 +267,14 @@ static pthread_t* g_entered_thread_id_pointer = NULL;
// Dumps signal and stack frame information, and invokes the default
// signal handler once our job is done.
+#if defined(OS_WINDOWS)
+void FailureSignalHandler(int signal_number)
+#else
void FailureSignalHandler(int signal_number,
siginfo_t *signal_info,
- void *ucontext) {
+ void *ucontext)
+#endif
+{
// First check if we've already entered the function. We use an atomic
// compare and swap operation for platforms that support it. For other
// platforms, we use a naive method that could lead to a subtle race.
@@ -298,16 +314,20 @@ void FailureSignalHandler(int signal_number,
// First dump time info.
DumpTimeInfo();
+#if !defined(OS_WINDOWS)
// Get the program counter from ucontext.
void *pc = GetPC(ucontext);
DumpStackFrameInfo("PC: ", pc);
+#endif
#ifdef HAVE_STACKTRACE
// Get the stack traces.
void *stack[32];
// +1 to exclude this function.
const int depth = GetStackTrace(stack, ARRAYSIZE(stack), 1);
+# ifdef HAVE_SIGACTION
DumpSignalInfo(signal_number, signal_info);
+# endif
// Dump the stack traces.
for (int i = 0; i < depth; ++i) {
DumpStackFrameInfo(" ", stack[i]);
@@ -333,18 +353,19 @@ void FailureSignalHandler(int signal_number,
} // namespace
-#endif // HAVE_SIGACTION
-
namespace glog_internal_namespace_ {
bool IsFailureSignalHandlerInstalled() {
#ifdef HAVE_SIGACTION
+ // TODO(andschwa): Return kFailureSignalHandlerInstalled?
struct sigaction sig_action;
memset(&sig_action, 0, sizeof(sig_action));
sigemptyset(&sig_action.sa_mask);
sigaction(SIGABRT, NULL, &sig_action);
if (sig_action.sa_sigaction == &FailureSignalHandler)
return true;
+#elif defined(OS_WINDOWS)
+ return kFailureSignalHandlerInstalled;
#endif // HAVE_SIGACTION
return false;
}
@@ -363,11 +384,18 @@ void InstallFailureSignalHandler() {
for (size_t i = 0; i < ARRAYSIZE(kFailureSignals); ++i) {
CHECK_ERR(sigaction(kFailureSignals[i].number, &sig_action, NULL));
}
+ kFailureSignalHandlerInstalled = true;
+#elif defined(OS_WINDOWS)
+ for (size_t i = 0; i < ARRAYSIZE(kFailureSignals); ++i) {
+ CHECK_NE(signal(kFailureSignals[i].number, &FailureSignalHandler),
+ SIG_ERR);
+ }
+ kFailureSignalHandlerInstalled = true;
#endif // HAVE_SIGACTION
}
void InstallFailureWriter(void (*writer)(const char* data, int size)) {
-#ifdef HAVE_SIGACTION
+#if defined(HAVE_SIGACTION) || defined(OS_WINDOWS)
g_failure_writer = writer;
#endif // HAVE_SIGACTION
}
diff --git a/extern/glog/src/stacktrace.h b/extern/glog/src/stacktrace.h
index 8c3e8fe8f8d..cb64b33a688 100644
--- a/extern/glog/src/stacktrace.h
+++ b/extern/glog/src/stacktrace.h
@@ -34,6 +34,7 @@
#define BASE_STACKTRACE_H_
#include "config.h"
+#include "glog/logging.h"
_START_GOOGLE_NAMESPACE_
@@ -53,7 +54,7 @@ _START_GOOGLE_NAMESPACE_
// .... ...
//
// "result" must not be NULL.
-extern int GetStackTrace(void** result, int max_depth, int skip_count);
+GOOGLE_GLOG_DLL_DECL int GetStackTrace(void** result, int max_depth, int skip_count);
_END_GOOGLE_NAMESPACE_
diff --git a/extern/glog/src/stacktrace_windows-inl.h b/extern/glog/src/stacktrace_windows-inl.h
new file mode 100644
index 00000000000..726318879d8
--- /dev/null
+++ b/extern/glog/src/stacktrace_windows-inl.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2000 - 2007, Google Inc.
+// All rights reserved.
+//
+// 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: Andrew Schwartzmeyer
+//
+// Windows implementation - just use CaptureStackBackTrace
+
+#include "config.h"
+#include "port.h"
+#include "stacktrace.h"
+#include <DbgHelp.h>
+
+_START_GOOGLE_NAMESPACE_
+
+int GetStackTrace(void** result, int max_depth, int skip_count) {
+ if (max_depth > 64) {
+ max_depth = 64;
+ }
+ skip_count++; // we want to skip the current frame as well
+ // This API is thread-safe (moreover it walks only the current thread).
+ return CaptureStackBackTrace(skip_count, max_depth, result, NULL);
+}
+
+_END_GOOGLE_NAMESPACE_
diff --git a/extern/glog/src/stacktrace_x86-inl.h b/extern/glog/src/stacktrace_x86-inl.h
index cfd31f783e3..3b8d5a8282d 100644
--- a/extern/glog/src/stacktrace_x86-inl.h
+++ b/extern/glog/src/stacktrace_x86-inl.h
@@ -93,16 +93,23 @@ static void **NextStackFrame(void **old_sp) {
// If you change this function, also change GetStackFrames below.
int GetStackTrace(void** result, int max_depth, int skip_count) {
void **sp;
-#ifdef __i386__
+
+#ifdef __GNUC__
+#if __GNUC__ * 100 + __GNUC_MINOR__ >= 402
+#define USE_BUILTIN_FRAME_ADDRESS
+#endif
+#endif
+
+#ifdef USE_BUILTIN_FRAME_ADDRESS
+ sp = reinterpret_cast<void**>(__builtin_frame_address(0));
+#elif defined(__i386__)
// Stack frame format:
// sp[0] pointer to previous frame
// sp[1] caller address
// sp[2] first argument
// ...
sp = (void **)&result - 2;
-#endif
-
-#ifdef __x86_64__
+#elif defined(__x86_64__)
// __builtin_frame_address(0) can return the wrong address on gcc-4.1.0-k8
unsigned long rbp;
// Move the value of the register %rbp into the local variable rbp.
diff --git a/extern/glog/src/symbolize.cc b/extern/glog/src/symbolize.cc
index f83c309738e..1ffc6079a2a 100644
--- a/extern/glog/src/symbolize.cc
+++ b/extern/glog/src/symbolize.cc
@@ -56,6 +56,9 @@
#if defined(HAVE_SYMBOLIZE)
+#include <string.h>
+
+#include <algorithm>
#include <limits>
#include "symbolize.h"
@@ -134,17 +137,20 @@ _END_GOOGLE_NAMESPACE_
_START_GOOGLE_NAMESPACE_
-// Read up to "count" bytes from file descriptor "fd" into the buffer
-// starting at "buf" while handling short reads and EINTR. On
-// success, return the number of bytes read. Otherwise, return -1.
-static ssize_t ReadPersistent(const int fd, void *buf, const size_t count) {
+// Read up to "count" bytes from "offset" in the file pointed by file
+// descriptor "fd" into the buffer starting at "buf" while handling short reads
+// and EINTR. On success, return the number of bytes read. Otherwise, return
+// -1.
+static ssize_t ReadFromOffset(const int fd, void *buf, const size_t count,
+ const off_t offset) {
SAFE_ASSERT(fd >= 0);
SAFE_ASSERT(count <= std::numeric_limits<ssize_t>::max());
char *buf0 = reinterpret_cast<char *>(buf);
ssize_t num_bytes = 0;
while (num_bytes < count) {
ssize_t len;
- NO_INTR(len = read(fd, buf0 + num_bytes, count - num_bytes));
+ NO_INTR(len = pread(fd, buf0 + num_bytes, count - num_bytes,
+ offset + num_bytes));
if (len < 0) { // There was an error other than EINTR.
return -1;
}
@@ -157,18 +163,6 @@ static ssize_t ReadPersistent(const int fd, void *buf, const size_t count) {
return num_bytes;
}
-// Read up to "count" bytes from "offset" in the file pointed by file
-// descriptor "fd" into the buffer starting at "buf". On success,
-// return the number of bytes read. Otherwise, return -1.
-static ssize_t ReadFromOffset(const int fd, void *buf,
- const size_t count, const off_t offset) {
- off_t off = lseek(fd, offset, SEEK_SET);
- if (off == (off_t)-1) {
- return -1;
- }
- return ReadPersistent(fd, buf, count);
-}
-
// Try reading exactly "count" bytes from "offset" bytes in a file
// pointed by "fd" into the buffer starting at "buf" while handling
// short reads and EINTR. On success, return true. Otherwise, return
@@ -207,6 +201,9 @@ GetSectionHeaderByType(const int fd, ElfW(Half) sh_num, const off_t sh_offset,
(sizeof(buf) > num_bytes_left) ? num_bytes_left : sizeof(buf);
const ssize_t len = ReadFromOffset(fd, buf, num_bytes_to_read,
sh_offset + i * sizeof(buf[0]));
+ if (len == -1) {
+ return false;
+ }
SAFE_ASSERT(len % sizeof(buf[0]) == 0);
const ssize_t num_headers_in_buf = len / sizeof(buf[0]);
SAFE_ASSERT(num_headers_in_buf <= sizeof(buf) / sizeof(buf[0]));
@@ -296,10 +293,12 @@ FindSymbol(uint64_t pc, const int fd, char *out, int out_size,
// Read at most NUM_SYMBOLS symbols at once to save read() calls.
ElfW(Sym) buf[NUM_SYMBOLS];
- const ssize_t len = ReadFromOffset(fd, &buf, sizeof(buf), offset);
+ int num_symbols_to_read = std::min(NUM_SYMBOLS, num_symbols - i);
+ const ssize_t len =
+ ReadFromOffset(fd, &buf, sizeof(buf[0]) * num_symbols_to_read, offset);
SAFE_ASSERT(len % sizeof(buf[0]) == 0);
const ssize_t num_symbols_in_buf = len / sizeof(buf[0]);
- SAFE_ASSERT(num_symbols_in_buf <= sizeof(buf)/sizeof(buf[0]));
+ SAFE_ASSERT(num_symbols_in_buf <= num_symbols_to_read);
for (int j = 0; j < num_symbols_in_buf; ++j) {
const ElfW(Sym)& symbol = buf[j];
uint64_t start_address = symbol.st_value;
@@ -325,41 +324,17 @@ FindSymbol(uint64_t pc, const int fd, char *out, int out_size,
// both regular and dynamic symbol tables if necessary. On success,
// write the symbol name to "out" and return true. Otherwise, return
// false.
-static bool GetSymbolFromObjectFile(const int fd, uint64_t pc,
- char *out, int out_size,
- uint64_t map_base_address) {
+static bool GetSymbolFromObjectFile(const int fd,
+ uint64_t pc,
+ char* out,
+ int out_size,
+ uint64_t base_address) {
// Read the ELF header.
ElfW(Ehdr) elf_header;
if (!ReadFromOffsetExact(fd, &elf_header, sizeof(elf_header), 0)) {
return false;
}
- uint64_t symbol_offset = 0;
- if (elf_header.e_type == ET_DYN) { // DSO needs offset adjustment.
- ElfW(Phdr) phdr;
- // We need to find the PT_LOAD segment corresponding to the read-execute
- // file mapping in order to correctly perform the offset adjustment.
- for (unsigned i = 0; i != elf_header.e_phnum; ++i) {
- if (!ReadFromOffsetExact(fd, &phdr, sizeof(phdr),
- elf_header.e_phoff + i * sizeof(phdr)))
- return false;
- if (phdr.p_type == PT_LOAD &&
- (phdr.p_flags & (PF_R | PF_X)) == (PF_R | PF_X)) {
- // Find the mapped address corresponding to virtual address zero. We do
- // this by first adding p_offset. This gives us the mapped address of
- // the start of the segment, or in other words the mapped address
- // corresponding to the virtual address of the segment. (Note that this
- // is distinct from the start address, as p_offset is not guaranteed to
- // be page aligned.) We then subtract p_vaddr, which takes us to virtual
- // address zero.
- symbol_offset = map_base_address + phdr.p_offset - phdr.p_vaddr;
- break;
- }
- }
- if (symbol_offset == 0)
- return false;
- }
-
ElfW(Shdr) symtab, strtab;
// Consult a regular symbol table first.
@@ -369,8 +344,7 @@ static bool GetSymbolFromObjectFile(const int fd, uint64_t pc,
symtab.sh_link * sizeof(symtab))) {
return false;
}
- if (FindSymbol(pc, fd, out, out_size, symbol_offset,
- &strtab, &symtab)) {
+ if (FindSymbol(pc, fd, out, out_size, base_address, &strtab, &symtab)) {
return true; // Found the symbol in a regular symbol table.
}
}
@@ -382,8 +356,7 @@ static bool GetSymbolFromObjectFile(const int fd, uint64_t pc,
symtab.sh_link * sizeof(symtab))) {
return false;
}
- if (FindSymbol(pc, fd, out, out_size, symbol_offset,
- &strtab, &symtab)) {
+ if (FindSymbol(pc, fd, out, out_size, base_address, &strtab, &symtab)) {
return true; // Found the symbol in a dynamic symbol table.
}
}
@@ -416,9 +389,14 @@ struct FileDescriptor {
// and snprintf().
class LineReader {
public:
- explicit LineReader(int fd, char *buf, int buf_len) : fd_(fd),
- buf_(buf), buf_len_(buf_len), bol_(buf), eol_(buf), eod_(buf) {
- }
+ explicit LineReader(int fd, char *buf, int buf_len, off_t offset)
+ : fd_(fd),
+ buf_(buf),
+ buf_len_(buf_len),
+ offset_(offset),
+ bol_(buf),
+ eol_(buf),
+ eod_(buf) {}
// Read '\n'-terminated line from file. On success, modify "bol"
// and "eol", then return true. Otherwise, return false.
@@ -427,10 +405,11 @@ class LineReader {
// dropped. It's an intentional behavior to make the code simple.
bool ReadLine(const char **bol, const char **eol) {
if (BufferIsEmpty()) { // First time.
- const ssize_t num_bytes = ReadPersistent(fd_, buf_, buf_len_);
+ const ssize_t num_bytes = ReadFromOffset(fd_, buf_, buf_len_, offset_);
if (num_bytes <= 0) { // EOF or error.
return false;
}
+ offset_ += num_bytes;
eod_ = buf_ + num_bytes;
bol_ = buf_;
} else {
@@ -443,11 +422,12 @@ class LineReader {
// Read text from file and append it.
char * const append_pos = buf_ + incomplete_line_length;
const int capacity_left = buf_len_ - incomplete_line_length;
- const ssize_t num_bytes = ReadPersistent(fd_, append_pos,
- capacity_left);
+ const ssize_t num_bytes =
+ ReadFromOffset(fd_, append_pos, capacity_left, offset_);
if (num_bytes <= 0) { // EOF or error.
return false;
}
+ offset_ += num_bytes;
eod_ = append_pos + num_bytes;
bol_ = buf_;
}
@@ -492,6 +472,7 @@ class LineReader {
const int fd_;
char * const buf_;
const int buf_len_;
+ off_t offset_;
char *bol_;
char *eol_;
const char *eod_; // End of data in "buf_".
@@ -532,7 +513,6 @@ OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc,
int out_file_name_size) {
int object_fd;
- // Open /proc/self/maps.
int maps_fd;
NO_INTR(maps_fd = open("/proc/self/maps", O_RDONLY));
FileDescriptor wrapped_maps_fd(maps_fd);
@@ -540,11 +520,18 @@ OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc,
return -1;
}
+ int mem_fd;
+ NO_INTR(mem_fd = open("/proc/self/mem", O_RDONLY));
+ FileDescriptor wrapped_mem_fd(mem_fd);
+ if (wrapped_mem_fd.get() < 0) {
+ return -1;
+ }
+
// Iterate over maps and look for the map containing the pc. Then
// look into the symbol tables inside.
char buf[1024]; // Big enough for line of sane /proc/self/maps
int num_maps = 0;
- LineReader reader(wrapped_maps_fd.get(), buf, sizeof(buf));
+ LineReader reader(wrapped_maps_fd.get(), buf, sizeof(buf), 0);
while (true) {
num_maps++;
const char *cursor;
@@ -575,11 +562,6 @@ OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc,
}
++cursor; // Skip ' '.
- // Check start and end addresses.
- if (!(start_address <= pc && pc < end_address)) {
- continue; // We skip this map. PC isn't in this map.
- }
-
// Read flags. Skip flags until we encounter a space or eol.
const char * const flags_start = cursor;
while (cursor < eol && *cursor != ' ') {
@@ -590,6 +572,49 @@ OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc,
return -1; // Malformed line.
}
+ // Determine the base address by reading ELF headers in process memory.
+ ElfW(Ehdr) ehdr;
+ // Skip non-readable maps.
+ if (flags_start[0] == 'r' &&
+ ReadFromOffsetExact(mem_fd, &ehdr, sizeof(ElfW(Ehdr)), start_address) &&
+ memcmp(ehdr.e_ident, ELFMAG, SELFMAG) == 0) {
+ switch (ehdr.e_type) {
+ case ET_EXEC:
+ base_address = 0;
+ break;
+ case ET_DYN:
+ // Find the segment containing file offset 0. This will correspond
+ // to the ELF header that we just read. Normally this will have
+ // virtual address 0, but this is not guaranteed. We must subtract
+ // the virtual address from the address where the ELF header was
+ // mapped to get the base address.
+ //
+ // If we fail to find a segment for file offset 0, use the address
+ // of the ELF header as the base address.
+ base_address = start_address;
+ for (unsigned i = 0; i != ehdr.e_phnum; ++i) {
+ ElfW(Phdr) phdr;
+ if (ReadFromOffsetExact(
+ mem_fd, &phdr, sizeof(phdr),
+ start_address + ehdr.e_phoff + i * sizeof(phdr)) &&
+ phdr.p_type == PT_LOAD && phdr.p_offset == 0) {
+ base_address = start_address - phdr.p_vaddr;
+ break;
+ }
+ }
+ break;
+ default:
+ // ET_REL or ET_CORE. These aren't directly executable, so they don't
+ // affect the base address.
+ break;
+ }
+ }
+
+ // Check start and end addresses.
+ if (!(start_address <= pc && pc < end_address)) {
+ continue; // We skip this map. PC isn't in this map.
+ }
+
// Check flags. We are only interested in "r*x" maps.
if (flags_start[0] != 'r' || flags_start[2] != 'x') {
continue; // We skip this map.
@@ -604,19 +629,6 @@ OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc,
}
++cursor; // Skip ' '.
- // Don't subtract 'start_address' from the first entry:
- // * If a binary is compiled w/o -pie, then the first entry in
- // process maps is likely the binary itself (all dynamic libs
- // are mapped higher in address space). For such a binary,
- // instruction offset in binary coincides with the actual
- // instruction address in virtual memory (as code section
- // is mapped to a fixed memory range).
- // * If a binary is compiled with -pie, all the modules are
- // mapped high at address space (in particular, higher than
- // shadow memory of the tool), so the module can't be the
- // first entry.
- base_address = ((num_maps == 1) ? 0U : start_address) - file_offset;
-
// Skip to file name. "cursor" now points to dev. We need to
// skip at least two spaces for dev and inode.
int num_spaces = 0;
@@ -655,7 +667,7 @@ OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc,
// bytes. Output will be truncated as needed, and a NUL character is always
// appended.
// NOTE: code from sandbox/linux/seccomp-bpf/demo.cc.
-char *itoa_r(intptr_t i, char *buf, size_t sz, int base, size_t padding) {
+static char *itoa_r(intptr_t i, char *buf, size_t sz, int base, size_t padding) {
// Make sure we can write at least one NUL byte.
size_t n = 1;
if (n > sz)
@@ -672,7 +684,8 @@ char *itoa_r(intptr_t i, char *buf, size_t sz, int base, size_t padding) {
// Handle negative numbers (only for base 10).
if (i < 0 && base == 10) {
- j = -i;
+ // This does "j = -i" while avoiding integer overflow.
+ j = static_cast<uintptr_t>(-(i + 1)) + 1;
// Make sure we can write the '-' character.
if (++n > sz) {
@@ -717,7 +730,7 @@ char *itoa_r(intptr_t i, char *buf, size_t sz, int base, size_t padding) {
// Safely appends string |source| to string |dest|. Never writes past the
// buffer size |dest_size| and guarantees that |dest| is null-terminated.
-void SafeAppendString(const char* source, char* dest, int dest_size) {
+static void SafeAppendString(const char* source, char* dest, int dest_size) {
int dest_string_length = strlen(dest);
SAFE_ASSERT(dest_string_length < dest_size);
dest += dest_string_length;
@@ -730,7 +743,7 @@ void SafeAppendString(const char* source, char* dest, int dest_size) {
// Converts a 64-bit value into a hex string, and safely appends it to |dest|.
// Never writes past the buffer size |dest_size| and guarantees that |dest| is
// null-terminated.
-void SafeAppendHexNumber(uint64_t value, char* dest, int dest_size) {
+static void SafeAppendHexNumber(uint64_t value, char* dest, int dest_size) {
// 64-bit numbers in hex can have up to 16 digits.
char buf[17] = {'\0'};
SafeAppendString(itoa_r(value, buf, sizeof(buf), 16, 0), dest, dest_size);
@@ -768,8 +781,13 @@ static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void *pc, char *out,
out_size - 1);
}
+#if defined(PRINT_UNSYMBOLIZED_STACK_TRACES)
+ {
+ FileDescriptor wrapped_object_fd(object_fd);
+#else
// Check whether a file name was returned.
if (object_fd < 0) {
+#endif
if (out[1]) {
// The object file containing PC was determined successfully however the
// object file was not opened successfully. This is still considered
@@ -793,7 +811,7 @@ static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void *pc, char *out,
// Run the call back if it's installed.
// Note: relocation (and much of the rest of this code) will be
// wrong for prelinked shared libraries and PIE executables.
- uint64 relocation = (elf_type == ET_DYN) ? start_address : 0;
+ uint64_t relocation = (elf_type == ET_DYN) ? start_address : 0;
int num_bytes_written = g_symbolize_callback(wrapped_object_fd.get(),
pc, out, out_size,
relocation);
@@ -837,6 +855,71 @@ static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void *pc, char *out,
_END_GOOGLE_NAMESPACE_
+#elif defined(OS_WINDOWS) || defined(OS_CYGWIN)
+
+#include <windows.h>
+#include <dbghelp.h>
+
+#ifdef _MSC_VER
+#pragma comment(lib, "dbghelp")
+#endif
+
+_START_GOOGLE_NAMESPACE_
+
+class SymInitializer {
+public:
+ HANDLE process;
+ bool ready;
+ SymInitializer() : process(NULL), ready(false) {
+ // Initialize the symbol handler.
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/ms680344(v=vs.85).aspx
+ process = GetCurrentProcess();
+ // Defer symbol loading.
+ // We do not request undecorated symbols with SYMOPT_UNDNAME
+ // because the mangling library calls UnDecorateSymbolName.
+ SymSetOptions(SYMOPT_DEFERRED_LOADS);
+ if (SymInitialize(process, NULL, true)) {
+ ready = true;
+ }
+ }
+ ~SymInitializer() {
+ SymCleanup(process);
+ // We do not need to close `HANDLE process` because it's a "pseudo handle."
+ }
+private:
+ SymInitializer(const SymInitializer&);
+ SymInitializer& operator=(const SymInitializer&);
+};
+
+static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void *pc, char *out,
+ int out_size) {
+ const static SymInitializer symInitializer;
+ if (!symInitializer.ready) {
+ return false;
+ }
+ // Resolve symbol information from address.
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/ms680578(v=vs.85).aspx
+ char buf[sizeof(SYMBOL_INFO) + MAX_SYM_NAME];
+ SYMBOL_INFO *symbol = reinterpret_cast<SYMBOL_INFO *>(buf);
+ symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
+ symbol->MaxNameLen = MAX_SYM_NAME;
+ // We use the ANSI version to ensure the string type is always `char *`.
+ // This could break if a symbol has Unicode in it.
+ BOOL ret = SymFromAddr(symInitializer.process,
+ reinterpret_cast<DWORD64>(pc), 0, symbol);
+ if (ret == 1 && static_cast<int>(symbol->NameLen) < out_size) {
+ // `NameLen` does not include the null terminating character.
+ strncpy(out, symbol->Name, static_cast<size_t>(symbol->NameLen) + 1);
+ out[static_cast<size_t>(symbol->NameLen)] = '\0';
+ // Symbolization succeeded. Now we try to demangle the symbol.
+ DemangleInplace(out, out_size);
+ return true;
+ }
+ return false;
+}
+
+_END_GOOGLE_NAMESPACE_
+
#else
# error BUG: HAVE_SYMBOLIZE was wrongly set
#endif
diff --git a/extern/glog/src/symbolize.h b/extern/glog/src/symbolize.h
index f617184249c..c6f9ec4360e 100644
--- a/extern/glog/src/symbolize.h
+++ b/extern/glog/src/symbolize.h
@@ -116,8 +116,11 @@ _START_GOOGLE_NAMESPACE_
// counter "pc". The callback function should write output to "out"
// and return the size of the output written. On error, the callback
// function should return -1.
-typedef int (*SymbolizeCallback)(int fd, void *pc, char *out, size_t out_size,
- uint64 relocation);
+typedef int (*SymbolizeCallback)(int fd,
+ void* pc,
+ char* out,
+ size_t out_size,
+ uint64_t relocation);
void InstallSymbolizeCallback(SymbolizeCallback callback);
// Installs a callback function, which will be called instead of
@@ -131,9 +134,9 @@ void InstallSymbolizeCallback(SymbolizeCallback callback);
// returns -1. |out_file_name_size| is the size of the file name buffer
// (including the null-terminator).
typedef int (*SymbolizeOpenObjectFileCallback)(uint64_t pc,
- uint64_t &start_address,
- uint64_t &base_address,
- char *out_file_name,
+ uint64_t& start_address,
+ uint64_t& base_address,
+ char* out_file_name,
int out_file_name_size);
void InstallSymbolizeOpenObjectFileCallback(
SymbolizeOpenObjectFileCallback callback);
@@ -148,7 +151,7 @@ _START_GOOGLE_NAMESPACE_
// symbol name to "out". The symbol name is demangled if possible
// (supports symbols generated by GCC 3.x or newer). Otherwise,
// returns false.
-bool Symbolize(void *pc, char *out, int out_size);
+GOOGLE_GLOG_DLL_DECL bool Symbolize(void *pc, char *out, int out_size);
_END_GOOGLE_NAMESPACE_
diff --git a/extern/glog/src/utilities.cc b/extern/glog/src/utilities.cc
index 5c88e58d3c0..25c4b760f1c 100644
--- a/extern/glog/src/utilities.cc
+++ b/extern/glog/src/utilities.cc
@@ -47,6 +47,12 @@
#ifdef HAVE_SYSLOG_H
# include <syslog.h>
#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h> // For geteuid.
+#endif
+#ifdef HAVE_PWD_H
+# include <pwd.h>
+#endif
#include "base/googleinit.h"
@@ -84,7 +90,7 @@ static void DebugWriteToStderr(const char* data, void *) {
}
}
-void DebugWriteToString(const char* data, void *arg) {
+static void DebugWriteToString(const char* data, void *arg) {
reinterpret_cast<string*>(arg)->append(data);
}
@@ -137,17 +143,19 @@ static void DumpStackTraceAndExit() {
DumpStackTrace(1, DebugWriteToStderr, NULL);
// TOOD(hamaji): Use signal instead of sigaction?
-#ifdef HAVE_SIGACTION
if (IsFailureSignalHandlerInstalled()) {
// Set the default signal handler for SIGABRT, to avoid invoking our
// own signal handler installed by InstallFailureSignalHandler().
+#ifdef HAVE_SIGACTION
struct sigaction sig_action;
memset(&sig_action, 0, sizeof(sig_action));
sigemptyset(&sig_action.sa_mask);
sig_action.sa_handler = SIG_DFL;
sigaction(SIGABRT, &sig_action, NULL);
- }
+#elif defined(OS_WINDOWS)
+ signal(SIGABRT, SIG_DFL);
#endif // HAVE_SIGACTION
+ }
abort();
}
@@ -266,7 +274,7 @@ pid_t GetTID() {
// If gettid() could not be used, we use one of the following.
#if defined OS_LINUX
return getpid(); // Linux: getpid returns thread ID when gettid is absent
-#elif defined OS_WINDOWS || defined OS_CYGWIN
+#elif defined OS_WINDOWS && !defined OS_CYGWIN
return GetCurrentThreadId();
#else
// If none of the techniques above worked, we use pthread_self().
@@ -297,8 +305,24 @@ static void MyUserNameInitializer() {
if (user != NULL) {
g_my_user_name = user;
} else {
- g_my_user_name = "invalid-user";
+#if defined(HAVE_PWD_H) && defined(HAVE_UNISTD_H)
+ struct passwd pwd;
+ struct passwd* result = NULL;
+ char buffer[1024] = {'\0'};
+ uid_t uid = geteuid();
+ int pwuid_res = getpwuid_r(uid, &pwd, buffer, sizeof(buffer), &result);
+ if (pwuid_res == 0) {
+ g_my_user_name = pwd.pw_name;
+ } else {
+ snprintf(buffer, sizeof(buffer), "uid%d", uid);
+ g_my_user_name = buffer;
+ }
+#endif
+ if (g_my_user_name.empty()) {
+ g_my_user_name = "invalid-user";
+ }
}
+
}
REGISTER_MODULE_INITIALIZER(utilities, MyUserNameInitializer());
@@ -349,4 +373,12 @@ _END_GOOGLE_NAMESPACE_
// Make an implementation of stacktrace compiled.
#ifdef STACKTRACE_H
# include STACKTRACE_H
+# if 0
+// For include scanners which can't handle macro expansions.
+# include "stacktrace_libunwind-inl.h"
+# include "stacktrace_x86-inl.h"
+# include "stacktrace_x86_64-inl.h"
+# include "stacktrace_powerpc-inl.h"
+# include "stacktrace_generic-inl.h"
+# endif
#endif
diff --git a/extern/glog/src/utilities.h b/extern/glog/src/utilities.h
index 5f79968ef55..ca21cfb3884 100644
--- a/extern/glog/src/utilities.h
+++ b/extern/glog/src/utilities.h
@@ -39,7 +39,9 @@
#elif defined(__CYGWIN__) || defined(__CYGWIN32__)
# define OS_CYGWIN
#elif defined(linux) || defined(__linux) || defined(__linux__)
-# define OS_LINUX
+# ifndef OS_LINUX
+# define OS_LINUX
+# endif
#elif defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__)
# define OS_MACOSX
#elif defined(__FreeBSD__)
@@ -97,6 +99,8 @@
// malloc() from the unwinder. This is a problem because we're
// trying to use the unwinder to instrument malloc().
//
+// 4) The Windows API CaptureStackTrace.
+//
// Note: if you add a new implementation here, make sure it works
// correctly when GetStackTrace() is called with max_depth == 0.
// Some code may do that.
@@ -110,6 +114,8 @@
# define STACKTRACE_H "stacktrace_x86_64-inl.h"
# elif (defined(__ppc__) || defined(__PPC__)) && __GNUC__ >= 2
# define STACKTRACE_H "stacktrace_powerpc-inl.h"
+# elif defined(OS_WINDOWS)
+# define STACKTRACE_H "stacktrace_windows-inl.h"
# endif
#endif
@@ -121,13 +127,18 @@
# define HAVE_STACKTRACE
#endif
+#ifndef HAVE_SYMBOLIZE
// defined by gcc
#if defined(__ELF__) && defined(OS_LINUX)
# define HAVE_SYMBOLIZE
#elif defined(OS_MACOSX) && defined(HAVE_DLADDR)
// Use dladdr to symbolize.
# define HAVE_SYMBOLIZE
+#elif defined(OS_WINDOWS)
+// Use DbgHelp to symbolize
+# define HAVE_SYMBOLIZE
#endif
+#endif // !defined(HAVE_SYMBOLIZE)
#ifndef ARRAYSIZE
// There is a better way, but this is good enough for our purpose.
@@ -141,6 +152,9 @@ namespace glog_internal_namespace_ {
#ifdef HAVE___ATTRIBUTE__
# define ATTRIBUTE_NOINLINE __attribute__ ((noinline))
# define HAVE_ATTRIBUTE_NOINLINE
+#elif defined(OS_WINDOWS)
+# define ATTRIBUTE_NOINLINE __declspec(noinline)
+# define HAVE_ATTRIBUTE_NOINLINE
#else
# define ATTRIBUTE_NOINLINE
#endif
diff --git a/extern/glog/src/vlog_is_on.cc b/extern/glog/src/vlog_is_on.cc
index 4c95583b683..e8fdbae7dcb 100644
--- a/extern/glog/src/vlog_is_on.cc
+++ b/extern/glog/src/vlog_is_on.cc
@@ -62,6 +62,12 @@ _START_GOOGLE_NAMESPACE_
namespace glog_internal_namespace_ {
+// Used by logging_unittests.cc so can't make it static here.
+GOOGLE_GLOG_DLL_DECL bool SafeFNMatch_(const char* pattern,
+ size_t patt_len,
+ const char* str,
+ size_t str_len);
+
// Implementation of fnmatch that does not need 0-termination
// of arguments and does not allocate any memory,
// but we only support "*" and "?" wildcards, not the "[...]" patterns.
diff --git a/extern/glog/src/windows/glog/logging.h b/extern/glog/src/windows/glog/logging.h
index f521a2b9424..3681fa3fcc4 100644
--- a/extern/glog/src/windows/glog/logging.h
+++ b/extern/glog/src/windows/glog/logging.h
@@ -572,8 +572,10 @@ class LogSink; // defined below
LOG_TO_STRING_##severity(static_cast<std::vector<std::string>*>(outvec)).stream()
#define LOG_IF(severity, condition) \
+ static_cast<void>(0), \
!(condition) ? (void) 0 : google::LogMessageVoidify() & LOG(severity)
#define SYSLOG_IF(severity, condition) \
+ static_cast<void>(0), \
!(condition) ? (void) 0 : google::LogMessageVoidify() & SYSLOG(severity)
#define LOG_ASSERT(condition) \
@@ -863,6 +865,7 @@ DECLARE_CHECK_STROP_IMPL(strcasecmp, false)
&google::LogMessage::SendToLog)
#define PLOG_IF(severity, condition) \
+ static_cast<void>(0), \
!(condition) ? (void) 0 : google::LogMessageVoidify() & PLOG(severity)
// A CHECK() macro that postpends errno if the condition is false. E.g.
@@ -935,16 +938,11 @@ struct CompileAssert {
struct CrashReason;
// Returns true if FailureSignalHandler is installed.
-bool IsFailureSignalHandlerInstalled();
+// Needs to be exported since it's used by the signalhandler_unittest.
+GOOGLE_GLOG_DLL_DECL bool IsFailureSignalHandlerInstalled();
} // namespace glog_internal_namespace_
-#define GOOGLE_GLOG_COMPILE_ASSERT(expr, msg) \
- typedef google::glog_internal_namespace_::CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1]
-
#define LOG_EVERY_N(severity, n) \
- GOOGLE_GLOG_COMPILE_ASSERT(google::GLOG_ ## severity < \
- google::NUM_SEVERITIES, \
- INVALID_REQUESTED_LOG_SEVERITY); \
SOME_KIND_OF_LOG_EVERY_N(severity, (n), google::LogMessage::SendToLog)
#define SYSLOG_EVERY_N(severity, n) \
@@ -1012,23 +1010,29 @@ const LogSeverity GLOG_0 = GLOG_ERROR;
#else // !DCHECK_IS_ON()
-#define DLOG(severity) \
+#define DLOG(severity) \
+ static_cast<void>(0), \
true ? (void) 0 : google::LogMessageVoidify() & LOG(severity)
-#define DVLOG(verboselevel) \
- (true || !VLOG_IS_ON(verboselevel)) ?\
+#define DVLOG(verboselevel) \
+ static_cast<void>(0), \
+ (true || !VLOG_IS_ON(verboselevel)) ? \
(void) 0 : google::LogMessageVoidify() & LOG(INFO)
#define DLOG_IF(severity, condition) \
+ static_cast<void>(0), \
(true || !(condition)) ? (void) 0 : google::LogMessageVoidify() & LOG(severity)
#define DLOG_EVERY_N(severity, n) \
+ static_cast<void>(0), \
true ? (void) 0 : google::LogMessageVoidify() & LOG(severity)
#define DLOG_IF_EVERY_N(severity, condition, n) \
+ static_cast<void>(0), \
(true || !(condition))? (void) 0 : google::LogMessageVoidify() & LOG(severity)
#define DLOG_ASSERT(condition) \
+ static_cast<void>(0), \
true ? (void) 0 : LOG_ASSERT(condition)
// MSVC warning C4127: conditional expression is constant
@@ -1113,10 +1117,11 @@ namespace base_logging {
// buffer to allow for a '\n' and '\0'.
class GOOGLE_GLOG_DLL_DECL LogStreamBuf : public std::streambuf {
public:
- // REQUIREMENTS: "len" must be >= 2 to account for the '\n' and '\n'.
+ // REQUIREMENTS: "len" must be >= 2 to account for the '\n' and '\0'.
LogStreamBuf(char *buf, int len) {
setp(buf, buf + len - 2);
}
+
// This effectively ignores overflow.
virtual int_type overflow(int_type ch) {
return ch;
@@ -1155,13 +1160,9 @@ public:
// 2005 if you are deriving from a type in the Standard C++ Library"
// http://msdn.microsoft.com/en-us/library/3tdb471s(VS.80).aspx
// Let's just ignore the warning.
-#ifdef _MSC_VER
-# pragma warning(disable: 4275)
-#endif
+GLOG_MSVC_PUSH_DISABLE_WARNING(4275)
class GOOGLE_GLOG_DLL_DECL LogStream : public std::ostream {
-#ifdef _MSC_VER
-# pragma warning(default: 4275)
-#endif
+GLOG_MSVC_POP_WARNING()
public:
LogStream(char *buf, int len, int ctr)
: std::ostream(NULL),
diff --git a/extern/glog/src/windows/glog/raw_logging.h b/extern/glog/src/windows/glog/raw_logging.h
index 4757a719db7..e0e6d6f1a25 100644
--- a/extern/glog/src/windows/glog/raw_logging.h
+++ b/extern/glog/src/windows/glog/raw_logging.h
@@ -179,11 +179,6 @@ GOOGLE_GLOG_DLL_DECL void RawLog__(LogSeverity severity,
const char* format, ...)
;
-// Hack to propagate time information into this module so that
-// this module does not have to directly call localtime_r(),
-// which could allocate memory.
-GOOGLE_GLOG_DLL_DECL void RawLog__SetLastTime(const struct tm& t, int usecs);
-
}
#endif // BASE_RAW_LOGGING_H_
diff --git a/extern/glog/src/windows/port.cc b/extern/glog/src/windows/port.cc
index d9943254ee5..19bda367c62 100644
--- a/extern/glog/src/windows/port.cc
+++ b/extern/glog/src/windows/port.cc
@@ -38,15 +38,8 @@
#include "config.h"
#include <stdarg.h> // for va_list, va_start, va_end
-#include <string.h> // for strstr()
-#include <assert.h>
-#include <string>
-#include <vector>
#include "port.h"
-using std::string;
-using std::vector;
-
// These call the windows _vsnprintf, but always NUL-terminate.
int safe_vsnprintf(char *str, size_t size, const char *format, va_list ap) {
if (size == 0) // not even room for a \0?
@@ -55,6 +48,12 @@ int safe_vsnprintf(char *str, size_t size, const char *format, va_list ap) {
return _vsnprintf(str, size-1, format, ap);
}
+#ifndef HAVE_LOCALTIME_R
+struct tm* localtime_r(const time_t* timep, struct tm* result) {
+ localtime_s(result, timep);
+ return result;
+}
+#endif // not HAVE_LOCALTIME_R
#ifndef HAVE_SNPRINTF
int snprintf(char *str, size_t size, const char *format, ...) {
va_list ap;
diff --git a/extern/glog/src/windows/port.h b/extern/glog/src/windows/port.h
index 819846151b9..7b4b9c8545b 100644
--- a/extern/glog/src/windows/port.h
+++ b/extern/glog/src/windows/port.h
@@ -70,8 +70,11 @@
* 4715: for some reason VC++ stopped realizing you can't return after abort()
* 4800: we know we're casting ints/char*'s to bools, and we're ok with that
* 4996: Yes, we're ok using "unsafe" functions like fopen() and strerror()
+ * 4312: Converting uint32_t to a pointer when testing %p
+ * 4267: also subtracting two size_t to int
+ * 4722: Destructor never returns due to abort()
*/
-#pragma warning(disable:4244 4251 4355 4715 4800 4996)
+#pragma warning(disable:4244 4251 4355 4715 4800 4996 4267 4312 4722)
/* file I/O */
#define PATH_MAX 1024
@@ -86,6 +89,11 @@
#define pclose _pclose
#define R_OK 04 /* read-only (for access()) */
#define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR)
+
+#define O_WRONLY _O_WRONLY
+#define O_CREAT _O_CREAT
+#define O_EXCL _O_EXCL
+
#ifndef __MINGW32__
enum { STDIN_FILENO = 0, STDOUT_FILENO = 1, STDERR_FILENO = 2 };
#endif
@@ -136,19 +144,20 @@ typedef int pid_t;
#endif // _MSC_VER
// ----------------------------------- THREADS
-#ifndef __MINGW32__
+#if defined(HAVE_PTHREAD)
+# include <pthread.h>
+#else // no PTHREAD
typedef DWORD pthread_t;
typedef DWORD pthread_key_t;
typedef LONG pthread_once_t;
enum { PTHREAD_ONCE_INIT = 0 }; // important that this be 0! for SpinLock
#define pthread_self GetCurrentThreadId
#define pthread_equal(pthread_t_1, pthread_t_2) ((pthread_t_1)==(pthread_t_2))
+#endif // HAVE_PTHREAD
-inline struct tm* localtime_r(const time_t* timep, struct tm* result) {
- localtime_s(result, timep);
- return result;
-}
-#endif
+#ifndef HAVE_LOCALTIME_R
+extern GOOGLE_GLOG_DLL_DECL struct tm* localtime_r(const time_t* timep, struct tm* result);
+#endif // not HAVE_LOCALTIME_R
inline char* strerror_r(int errnum, char* buf, size_t buflen) {
strerror_s(buf, buflen, errnum);
diff --git a/extern/glog/src/windows/preprocess.sh b/extern/glog/src/windows/preprocess.sh
index 5398988e7ea..c35e92913b6 100755
--- a/extern/glog/src/windows/preprocess.sh
+++ b/extern/glog/src/windows/preprocess.sh
@@ -95,7 +95,7 @@ DLLDEF_DEFINES="\
-e "s!@ac_cv_have_libgflags@!0!g" \
-e "s!@ac_cv_have___builtin_expect@!0!g" \
-e "s!@ac_cv_cxx_using_operator@!1!g" \
- -e "s!@ac_cv___attribute___noreturn@!!g" \
+ -e "s!@ac_cv___attribute___noreturn@!__declspec(noreturn)!g" \
-e "s!@ac_cv___attribute___noinline@!!g" \
-e "s!@ac_cv___attribute___printf_4_5@!!g" \
-e "s!@ac_google_attribute@!${HAVE___ATTRIBUTE__:-0}!g" \
diff --git a/extern/gmock/CHANGES b/extern/gmock/CHANGES
deleted file mode 100644
index d6f2f760e34..00000000000
--- a/extern/gmock/CHANGES
+++ /dev/null
@@ -1,126 +0,0 @@
-Changes for 1.7.0:
-
-* All new improvements in Google Test 1.7.0.
-* New feature: matchers DoubleNear(), FloatNear(),
- NanSensitiveDoubleNear(), NanSensitiveFloatNear(),
- UnorderedElementsAre(), UnorderedElementsAreArray(), WhenSorted(),
- WhenSortedBy(), IsEmpty(), and SizeIs().
-* Improvement: Google Mock can now be built as a DLL.
-* Improvement: when compiled by a C++11 compiler, matchers AllOf()
- and AnyOf() can accept an arbitrary number of matchers.
-* Improvement: when compiled by a C++11 compiler, matchers
- ElementsAreArray() can accept an initializer list.
-* Improvement: when exceptions are enabled, a mock method with no
- default action now throws instead crashing the test.
-* Improvement: added class testing::StringMatchResultListener to aid
- definition of composite matchers.
-* Improvement: function return types used in MOCK_METHOD*() macros can
- now contain unprotected commas.
-* Improvement (potentially breaking): EXPECT_THAT() and ASSERT_THAT()
- are now more strict in ensuring that the value type and the matcher
- type are compatible, catching potential bugs in tests.
-* Improvement: Pointee() now works on an optional<T>.
-* Improvement: the ElementsAreArray() matcher can now take a vector or
- iterator range as input, and makes a copy of its input elements
- before the conversion to a Matcher.
-* Improvement: the Google Mock Generator can now generate mocks for
- some class templates.
-* Bug fix: mock object destruction triggerred by another mock object's
- destruction no longer hangs.
-* Improvement: Google Mock Doctor works better with newer Clang and
- GCC now.
-* Compatibility fixes.
-* Bug/warning fixes.
-
-Changes for 1.6.0:
-
-* Compilation is much faster and uses much less memory, especially
- when the constructor and destructor of a mock class are moved out of
- the class body.
-* New matchers: Pointwise(), Each().
-* New actions: ReturnPointee() and ReturnRefOfCopy().
-* CMake support.
-* Project files for Visual Studio 2010.
-* AllOf() and AnyOf() can handle up-to 10 arguments now.
-* Google Mock doctor understands Clang error messages now.
-* SetArgPointee<> now accepts string literals.
-* gmock_gen.py handles storage specifier macros and template return
- types now.
-* Compatibility fixes.
-* Bug fixes and implementation clean-ups.
-* Potentially incompatible changes: disables the harmful 'make install'
- command in autotools.
-
-Potentially breaking changes:
-
-* The description string for MATCHER*() changes from Python-style
- interpolation to an ordinary C++ string expression.
-* SetArgumentPointee is deprecated in favor of SetArgPointee.
-* Some non-essential project files for Visual Studio 2005 are removed.
-
-Changes for 1.5.0:
-
- * New feature: Google Mock can be safely used in multi-threaded tests
- on platforms having pthreads.
- * New feature: function for printing a value of arbitrary type.
- * New feature: function ExplainMatchResult() for easy definition of
- composite matchers.
- * The new matcher API lets user-defined matchers generate custom
- explanations more directly and efficiently.
- * Better failure messages all around.
- * NotNull() and IsNull() now work with smart pointers.
- * Field() and Property() now work when the matcher argument is a pointer
- passed by reference.
- * Regular expression matchers on all platforms.
- * Added GCC 4.0 support for Google Mock Doctor.
- * Added gmock_all_test.cc for compiling most Google Mock tests
- in a single file.
- * Significantly cleaned up compiler warnings.
- * Bug fixes, better test coverage, and implementation clean-ups.
-
- Potentially breaking changes:
-
- * Custom matchers defined using MatcherInterface or MakePolymorphicMatcher()
- need to be updated after upgrading to Google Mock 1.5.0; matchers defined
- using MATCHER or MATCHER_P* aren't affected.
- * Dropped support for 'make install'.
-
-Changes for 1.4.0 (we skipped 1.2.* and 1.3.* to match the version of
-Google Test):
-
- * Works in more environments: Symbian and minGW, Visual C++ 7.1.
- * Lighter weight: comes with our own implementation of TR1 tuple (no
- more dependency on Boost!).
- * New feature: --gmock_catch_leaked_mocks for detecting leaked mocks.
- * New feature: ACTION_TEMPLATE for defining templatized actions.
- * New feature: the .After() clause for specifying expectation order.
- * New feature: the .With() clause for for specifying inter-argument
- constraints.
- * New feature: actions ReturnArg<k>(), ReturnNew<T>(...), and
- DeleteArg<k>().
- * New feature: matchers Key(), Pair(), Args<...>(), AllArgs(), IsNull(),
- and Contains().
- * New feature: utility class MockFunction<F>, useful for checkpoints, etc.
- * New feature: functions Value(x, m) and SafeMatcherCast<T>(m).
- * New feature: copying a mock object is rejected at compile time.
- * New feature: a script for fusing all Google Mock and Google Test
- source files for easy deployment.
- * Improved the Google Mock doctor to diagnose more diseases.
- * Improved the Google Mock generator script.
- * Compatibility fixes for Mac OS X and gcc.
- * Bug fixes and implementation clean-ups.
-
-Changes for 1.1.0:
-
- * New feature: ability to use Google Mock with any testing framework.
- * New feature: macros for easily defining new matchers
- * New feature: macros for easily defining new actions.
- * New feature: more container matchers.
- * New feature: actions for accessing function arguments and throwing
- exceptions.
- * Improved the Google Mock doctor script for diagnosing compiler errors.
- * Bug fixes and implementation clean-ups.
-
-Changes for 1.0.0:
-
- * Initial Open Source release of Google Mock
diff --git a/extern/gmock/CMakeLists.txt b/extern/gmock/CMakeLists.txt
index 73e524d80cd..faa133265d3 100644
--- a/extern/gmock/CMakeLists.txt
+++ b/extern/gmock/CMakeLists.txt
@@ -29,33 +29,34 @@ set(INC_SYS
set(SRC
# src/gmock-all.cc
+ # src/gmock_main.cc
- src/gmock-cardinalities.cc
src/gmock.cc
+ src/gmock-cardinalities.cc
src/gmock-internal-utils.cc
- src/gmock_main.cc
src/gmock-matchers.cc
src/gmock-spec-builders.cc
)
set(SRC_HEADERS
+ include/gmock/gmock.h
include/gmock/gmock-actions.h
include/gmock/gmock-cardinalities.h
+ include/gmock/gmock-function-mocker.h
include/gmock/gmock-generated-actions.h
include/gmock/gmock-generated-function-mockers.h
include/gmock/gmock-generated-matchers.h
- include/gmock/gmock-generated-nice-strict.h
- include/gmock/gmock.h
include/gmock/gmock-matchers.h
include/gmock/gmock-more-actions.h
include/gmock/gmock-more-matchers.h
+ include/gmock/gmock-nice-strict.h
include/gmock/gmock-spec-builders.h
include/gmock/internal/custom/gmock-generated-actions.h
include/gmock/internal/custom/gmock-matchers.h
include/gmock/internal/custom/gmock-port.h
- include/gmock/internal/gmock-generated-internal-utils.h
include/gmock/internal/gmock-internal-utils.h
include/gmock/internal/gmock-port.h
+ include/gmock/internal/gmock-pp.h
)
include_directories(${INC})
diff --git a/extern/gmock/README.blender b/extern/gmock/README.blender
index 6165bd6f717..3f9d80362d8 100644
--- a/extern/gmock/README.blender
+++ b/extern/gmock/README.blender
@@ -1,7 +1,5 @@
Project: Google C++ Testing Framework
URL: https://github.com/google/googletest
License: New BSD
-Upstream version: 1.8.0 (ec44c6c1675)
-Local modifications:
-
-None.
+Upstream version: 1.10.0 (703bd9caab5)
+Local modifications: None
diff --git a/extern/gmock/README.md b/extern/gmock/README.md
index 332beab3881..183fdb81d9b 100644
--- a/extern/gmock/README.md
+++ b/extern/gmock/README.md
@@ -1,333 +1,44 @@
-## Google Mock ##
+# Googletest Mocking (gMock) Framework
-The Google C++ mocking framework.
+### Overview
-### Overview ###
-
-Google's framework for writing and using C++ mock classes.
-It can help you derive better designs of your system and write better tests.
+Google's framework for writing and using C++ mock classes. It can help you
+derive better designs of your system and write better tests.
It is inspired by:
- * [jMock](http://www.jmock.org/),
- * [EasyMock](http://www.easymock.org/), and
- * [Hamcrest](http://code.google.com/p/hamcrest/),
+* [jMock](http://www.jmock.org/),
+* [EasyMock](http://www.easymock.org/), and
+* [Hamcrest](http://code.google.com/p/hamcrest/),
and designed with C++'s specifics in mind.
-Google mock:
-
- * lets you create mock classes trivially using simple macros.
- * supports a rich set of matchers and actions.
- * handles unordered, partially ordered, or completely ordered expectations.
- * is extensible by users.
-
-We hope you find it useful!
-
-### Features ###
-
- * Provides a declarative syntax for defining mocks.
- * Can easily define partial (hybrid) mocks, which are a cross of real
- and mock objects.
- * Handles functions of arbitrary types and overloaded functions.
- * Comes with a rich set of matchers for validating function arguments.
- * Uses an intuitive syntax for controlling the behavior of a mock.
- * Does automatic verification of expectations (no record-and-replay needed).
- * Allows arbitrary (partial) ordering constraints on
- function calls to be expressed,.
- * Lets a user extend it by defining new matchers and actions.
- * Does not use exceptions.
- * Is easy to learn and use.
-
-Please see the project page above for more information as well as the
-mailing list for questions, discussions, and development. There is
-also an IRC channel on OFTC (irc.oftc.net) #gtest available. Please
-join us!
-
-Please note that code under [scripts/generator](scripts/generator/) is
-from [cppclean](http://code.google.com/p/cppclean/) and released under
-the Apache License, which is different from Google Mock's license.
-
-## Getting Started ##
-
-If you are new to the project, we suggest that you read the user
-documentation in the following order:
-
- * Learn the [basics](../googletest/docs/Primer.md) of
- Google Test, if you choose to use Google Mock with it (recommended).
- * Read [Google Mock for Dummies](docs/ForDummies.md).
- * Read the instructions below on how to build Google Mock.
-
-You can also watch Zhanyong's [talk](http://www.youtube.com/watch?v=sYpCyLI47rM) on Google Mock's usage and implementation.
-
-Once you understand the basics, check out the rest of the docs:
-
- * [CheatSheet](docs/CheatSheet.md) - all the commonly used stuff
- at a glance.
- * [CookBook](docs/CookBook.md) - recipes for getting things done,
- including advanced techniques.
-
-If you need help, please check the
-[KnownIssues](docs/KnownIssues.md) and
-[FrequentlyAskedQuestions](docs/FrequentlyAskedQuestions.md) before
-posting a question on the
-[discussion group](http://groups.google.com/group/googlemock).
-
-
-### Using Google Mock Without Google Test ###
-
-Google Mock is not a testing framework itself. Instead, it needs a
-testing framework for writing tests. Google Mock works seamlessly
-with [Google Test](http://code.google.com/p/googletest/), but
-you can also use it with [any C++ testing framework](googlemock/ForDummies.md#Using_Google_Mock_with_Any_Testing_Framework).
-
-### Requirements for End Users ###
-
-Google Mock is implemented on top of [Google Test](
-http://github.com/google/googletest/), and depends on it.
-You must use the bundled version of Google Test when using Google Mock.
-
-You can also easily configure Google Mock to work with another testing
-framework, although it will still need Google Test. Please read
-["Using_Google_Mock_with_Any_Testing_Framework"](
- docs/ForDummies.md#Using_Google_Mock_with_Any_Testing_Framework)
-for instructions.
-
-Google Mock depends on advanced C++ features and thus requires a more
-modern compiler. The following are needed to use Google Mock:
-
-#### Linux Requirements ####
-
- * GNU-compatible Make or "gmake"
- * POSIX-standard shell
- * POSIX(-2) Regular Expressions (regex.h)
- * C++98-standard-compliant compiler (e.g. GCC 3.4 or newer)
-
-#### Windows Requirements ####
-
- * Microsoft Visual C++ 8.0 SP1 or newer
-
-#### Mac OS X Requirements ####
-
- * Mac OS X 10.4 Tiger or newer
- * Developer Tools Installed
-
-### Requirements for Contributors ###
-
-We welcome patches. If you plan to contribute a patch, you need to
-build Google Mock and its tests, which has further requirements:
-
- * Automake version 1.9 or newer
- * Autoconf version 2.59 or newer
- * Libtool / Libtoolize
- * Python version 2.3 or newer (for running some of the tests and
- re-generating certain source files from templates)
-
-### Building Google Mock ###
-
-#### Preparing to Build (Unix only) ####
-
-If you are using a Unix system and plan to use the GNU Autotools build
-system to build Google Mock (described below), you'll need to
-configure it now.
-
-To prepare the Autotools build system:
-
- cd googlemock
- autoreconf -fvi
-
-To build Google Mock and your tests that use it, you need to tell your
-build system where to find its headers and source files. The exact
-way to do it depends on which build system you use, and is usually
-straightforward.
-
-This section shows how you can integrate Google Mock into your
-existing build system.
-
-Suppose you put Google Mock in directory `${GMOCK_DIR}` and Google Test
-in `${GTEST_DIR}` (the latter is `${GMOCK_DIR}/gtest` by default). To
-build Google Mock, create a library build target (or a project as
-called by Visual Studio and Xcode) to compile
-
- ${GTEST_DIR}/src/gtest-all.cc and ${GMOCK_DIR}/src/gmock-all.cc
-
-with
-
- ${GTEST_DIR}/include and ${GMOCK_DIR}/include
-
-in the system header search path, and
-
- ${GTEST_DIR} and ${GMOCK_DIR}
-
-in the normal header search path. Assuming a Linux-like system and gcc,
-something like the following will do:
-
- g++ -isystem ${GTEST_DIR}/include -I${GTEST_DIR} \
- -isystem ${GMOCK_DIR}/include -I${GMOCK_DIR} \
- -pthread -c ${GTEST_DIR}/src/gtest-all.cc
- g++ -isystem ${GTEST_DIR}/include -I${GTEST_DIR} \
- -isystem ${GMOCK_DIR}/include -I${GMOCK_DIR} \
- -pthread -c ${GMOCK_DIR}/src/gmock-all.cc
- ar -rv libgmock.a gtest-all.o gmock-all.o
-
-(We need -pthread as Google Test and Google Mock use threads.)
-
-Next, you should compile your test source file with
-${GTEST\_DIR}/include and ${GMOCK\_DIR}/include in the header search
-path, and link it with gmock and any other necessary libraries:
-
- g++ -isystem ${GTEST_DIR}/include -isystem ${GMOCK_DIR}/include \
- -pthread path/to/your_test.cc libgmock.a -o your_test
-
-As an example, the make/ directory contains a Makefile that you can
-use to build Google Mock on systems where GNU make is available
-(e.g. Linux, Mac OS X, and Cygwin). It doesn't try to build Google
-Mock's own tests. Instead, it just builds the Google Mock library and
-a sample test. You can use it as a starting point for your own build
-script.
-
-If the default settings are correct for your environment, the
-following commands should succeed:
-
- cd ${GMOCK_DIR}/make
- make
- ./gmock_test
-
-If you see errors, try to tweak the contents of
-[make/Makefile](make/Makefile) to make them go away.
-
-### Windows ###
-
-The msvc/2005 directory contains VC++ 2005 projects and the msvc/2010
-directory contains VC++ 2010 projects for building Google Mock and
-selected tests.
-
-Change to the appropriate directory and run "msbuild gmock.sln" to
-build the library and tests (or open the gmock.sln in the MSVC IDE).
-If you want to create your own project to use with Google Mock, you'll
-have to configure it to use the `gmock_config` propety sheet. For that:
-
- * Open the Property Manager window (View | Other Windows | Property Manager)
- * Right-click on your project and select "Add Existing Property Sheet..."
- * Navigate to `gmock_config.vsprops` or `gmock_config.props` and select it.
- * In Project Properties | Configuration Properties | General | Additional
- Include Directories, type <path to Google Mock>/include.
-
-### Tweaking Google Mock ###
-
-Google Mock can be used in diverse environments. The default
-configuration may not work (or may not work well) out of the box in
-some environments. However, you can easily tweak Google Mock by
-defining control macros on the compiler command line. Generally,
-these macros are named like `GTEST_XYZ` and you define them to either 1
-or 0 to enable or disable a certain feature.
-
-We list the most frequently used macros below. For a complete list,
-see file [${GTEST\_DIR}/include/gtest/internal/gtest-port.h](
-../googletest/include/gtest/internal/gtest-port.h).
-
-### Choosing a TR1 Tuple Library ###
-
-Google Mock uses the C++ Technical Report 1 (TR1) tuple library
-heavily. Unfortunately TR1 tuple is not yet widely available with all
-compilers. The good news is that Google Test 1.4.0+ implements a
-subset of TR1 tuple that's enough for Google Mock's need. Google Mock
-will automatically use that implementation when the compiler doesn't
-provide TR1 tuple.
-
-Usually you don't need to care about which tuple library Google Test
-and Google Mock use. However, if your project already uses TR1 tuple,
-you need to tell Google Test and Google Mock to use the same TR1 tuple
-library the rest of your project uses, or the two tuple
-implementations will clash. To do that, add
-
- -DGTEST_USE_OWN_TR1_TUPLE=0
-
-to the compiler flags while compiling Google Test, Google Mock, and
-your tests. If you want to force Google Test and Google Mock to use
-their own tuple library, just add
-
- -DGTEST_USE_OWN_TR1_TUPLE=1
-
-to the compiler flags instead.
-
-If you want to use Boost's TR1 tuple library with Google Mock, please
-refer to the Boost website (http://www.boost.org/) for how to obtain
-it and set it up.
-
-### As a Shared Library (DLL) ###
-
-Google Mock is compact, so most users can build and link it as a static
-library for the simplicity. Google Mock can be used as a DLL, but the
-same DLL must contain Google Test as well. See
-[Google Test's README][gtest_readme]
-for instructions on how to set up necessary compiler settings.
-
-### Tweaking Google Mock ###
-
-Most of Google Test's control macros apply to Google Mock as well.
-Please see [Google Test's README][gtest_readme] for how to tweak them.
-
-### Upgrading from an Earlier Version ###
-
-We strive to keep Google Mock releases backward compatible.
-Sometimes, though, we have to make some breaking changes for the
-users' long-term benefits. This section describes what you'll need to
-do if you are upgrading from an earlier version of Google Mock.
-
-#### Upgrading from 1.1.0 or Earlier ####
-
-You may need to explicitly enable or disable Google Test's own TR1
-tuple library. See the instructions in section "[Choosing a TR1 Tuple
-Library](../googletest/#choosing-a-tr1-tuple-library)".
-
-#### Upgrading from 1.4.0 or Earlier ####
-
-On platforms where the pthread library is available, Google Test and
-Google Mock use it in order to be thread-safe. For this to work, you
-may need to tweak your compiler and/or linker flags. Please see the
-"[Multi-threaded Tests](../googletest#multi-threaded-tests
-)" section in file Google Test's README for what you may need to do.
-
-If you have custom matchers defined using `MatcherInterface` or
-`MakePolymorphicMatcher()`, you'll need to update their definitions to
-use the new matcher API (
-[monomorphic](http://code.google.com/p/googlemock/wiki/CookBook#Writing_New_Monomorphic_Matchers),
-[polymorphic](http://code.google.com/p/googlemock/wiki/CookBook#Writing_New_Polymorphic_Matchers)).
-Matchers defined using `MATCHER()` or `MATCHER_P*()` aren't affected.
-
-### Developing Google Mock ###
-
-This section discusses how to make your own changes to Google Mock.
-
-#### Testing Google Mock Itself ####
-
-To make sure your changes work as intended and don't break existing
-functionality, you'll want to compile and run Google Test's own tests.
-For that you'll need Autotools. First, make sure you have followed
-the instructions above to configure Google Mock.
-Then, create a build output directory and enter it. Next,
-
- ${GMOCK_DIR}/configure # try --help for more info
-
-Once you have successfully configured Google Mock, the build steps are
-standard for GNU-style OSS packages.
-
- make # Standard makefile following GNU conventions
- make check # Builds and runs all tests - all should pass.
-
-Note that when building your project against Google Mock, you are building
-against Google Test as well. There is no need to configure Google Test
-separately.
-
-#### Contributing a Patch ####
-
-We welcome patches.
-Please read the [Developer's Guide](docs/DevGuide.md)
-for how you can contribute. In particular, make sure you have signed
-the Contributor License Agreement, or we won't be able to accept the
-patch.
-
-Happy testing!
-
-[gtest_readme]: ../googletest/README.md "googletest"
+gMock:
+
+- provides a declarative syntax for defining mocks,
+- can define partial (hybrid) mocks, which are a cross of real and mock
+ objects,
+- handles functions of arbitrary types and overloaded functions,
+- comes with a rich set of matchers for validating function arguments,
+- uses an intuitive syntax for controlling the behavior of a mock,
+- does automatic verification of expectations (no record-and-replay needed),
+- allows arbitrary (partial) ordering constraints on function calls to be
+ expressed,
+- lets a user extend it by defining new matchers and actions.
+- does not use exceptions, and
+- is easy to learn and use.
+
+Details and examples can be found here:
+
+* [gMock for Dummies](docs/for_dummies.md)
+* [Legacy gMock FAQ](docs/gmock_faq.md)
+* [gMock Cookbook](docs/cook_book.md)
+* [gMock Cheat Sheet](docs/cheat_sheet.md)
+
+Please note that code under scripts/generator/ is from the [cppclean
+project](http://code.google.com/p/cppclean/) and under the Apache
+License, which is different from Google Mock's license.
+
+Google Mock is a part of
+[Google Test C++ testing framework](http://github.com/google/googletest/) and a
+subject to the same requirements.
diff --git a/extern/gmock/include/gmock/gmock-actions.h b/extern/gmock/include/gmock/gmock-actions.h
index b3f654af348..f12d39be06a 100644
--- a/extern/gmock/include/gmock/gmock-actions.h
+++ b/extern/gmock/include/gmock/gmock-actions.h
@@ -26,13 +26,14 @@
// 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: wan@google.com (Zhanyong Wan)
+
// Google Mock - a framework for writing C++ mock classes.
//
// This file implements some commonly used actions.
+// GOOGLETEST_CM0002 DO NOT DELETE
+
#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_ACTIONS_H_
#define GMOCK_INCLUDE_GMOCK_GMOCK_ACTIONS_H_
@@ -41,13 +42,18 @@
#endif
#include <algorithm>
+#include <functional>
+#include <memory>
#include <string>
+#include <type_traits>
+#include <utility>
#include "gmock/internal/gmock-internal-utils.h"
#include "gmock/internal/gmock-port.h"
-#if GTEST_HAS_STD_TYPE_TRAITS_ // Defined by gtest-port.h via gmock-port.h.
-#include <type_traits>
+#ifdef _MSC_VER
+# pragma warning(push)
+# pragma warning(disable:4100)
#endif
namespace testing {
@@ -63,9 +69,6 @@ namespace testing {
namespace internal {
-template <typename F1, typename F2>
-class ActionAdaptor;
-
// BuiltInDefaultValueGetter<T, true>::Get() returns a
// default-constructed T value. BuiltInDefaultValueGetter<T,
// false>::Get() crashes with an error.
@@ -96,8 +99,8 @@ struct BuiltInDefaultValueGetter<T, false> {
template <typename T>
class BuiltInDefaultValue {
public:
-#if GTEST_HAS_STD_TYPE_TRAITS_
- // This function returns true iff type T has a built-in default value.
+ // This function returns true if and only if type T has a built-in default
+ // value.
static bool Exists() {
return ::std::is_default_constructible<T>::value;
}
@@ -106,18 +109,6 @@ class BuiltInDefaultValue {
return BuiltInDefaultValueGetter<
T, ::std::is_default_constructible<T>::value>::Get();
}
-
-#else // GTEST_HAS_STD_TYPE_TRAITS_
- // This function returns true iff type T has a built-in default value.
- static bool Exists() {
- return false;
- }
-
- static T Get() {
- return BuiltInDefaultValueGetter<T, false>::Get();
- }
-
-#endif // GTEST_HAS_STD_TYPE_TRAITS_
};
// This partial specialization says that we use the same built-in
@@ -135,7 +126,7 @@ template <typename T>
class BuiltInDefaultValue<T*> {
public:
static bool Exists() { return true; }
- static T* Get() { return NULL; }
+ static T* Get() { return nullptr; }
};
// The following specializations define the default values for
@@ -149,9 +140,6 @@ class BuiltInDefaultValue<T*> {
}
GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(void, ); // NOLINT
-#if GTEST_HAS_GLOBAL_STRING
-GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(::string, "");
-#endif // GTEST_HAS_GLOBAL_STRING
GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(::std::string, "");
GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(bool, false);
GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(unsigned char, '\0');
@@ -218,11 +206,11 @@ class DefaultValue {
// Unsets the default value for type T.
static void Clear() {
delete producer_;
- producer_ = NULL;
+ producer_ = nullptr;
}
- // Returns true iff the user has set the default value for type T.
- static bool IsSet() { return producer_ != NULL; }
+ // Returns true if and only if the user has set the default value for type T.
+ static bool IsSet() { return producer_ != nullptr; }
// Returns true if T has a default return value set by the user or there
// exists a built-in default value.
@@ -234,8 +222,8 @@ class DefaultValue {
// otherwise returns the built-in default value. Requires that Exists()
// is true, which ensures that the return value is well-defined.
static T Get() {
- return producer_ == NULL ?
- internal::BuiltInDefaultValue<T>::Get() : producer_->Produce();
+ return producer_ == nullptr ? internal::BuiltInDefaultValue<T>::Get()
+ : producer_->Produce();
}
private:
@@ -248,7 +236,7 @@ class DefaultValue {
class FixedValueProducer : public ValueProducer {
public:
explicit FixedValueProducer(T value) : value_(value) {}
- virtual T Produce() { return value_; }
+ T Produce() override { return value_; }
private:
const T value_;
@@ -259,7 +247,7 @@ class DefaultValue {
public:
explicit FactoryValueProducer(FactoryFunction factory)
: factory_(factory) {}
- virtual T Produce() { return factory_(); }
+ T Produce() override { return factory_(); }
private:
const FactoryFunction factory_;
@@ -280,12 +268,10 @@ class DefaultValue<T&> {
}
// Unsets the default value for type T&.
- static void Clear() {
- address_ = NULL;
- }
+ static void Clear() { address_ = nullptr; }
- // Returns true iff the user has set the default value for type T&.
- static bool IsSet() { return address_ != NULL; }
+ // Returns true if and only if the user has set the default value for type T&.
+ static bool IsSet() { return address_ != nullptr; }
// Returns true if T has a default return value set by the user or there
// exists a built-in default value.
@@ -297,8 +283,8 @@ class DefaultValue<T&> {
// otherwise returns the built-in default value if there is one;
// otherwise aborts the process.
static T& Get() {
- return address_ == NULL ?
- internal::BuiltInDefaultValue<T&>::Get() : *address_;
+ return address_ == nullptr ? internal::BuiltInDefaultValue<T&>::Get()
+ : *address_;
}
private:
@@ -316,11 +302,11 @@ class DefaultValue<void> {
// Points to the user-set default value for type T.
template <typename T>
-typename DefaultValue<T>::ValueProducer* DefaultValue<T>::producer_ = NULL;
+typename DefaultValue<T>::ValueProducer* DefaultValue<T>::producer_ = nullptr;
// Points to the user-set default value for type T&.
template <typename T>
-T* DefaultValue<T&>::address_ = NULL;
+T* DefaultValue<T&>::address_ = nullptr;
// Implement this interface to define an action for function type F.
template <typename F>
@@ -345,38 +331,53 @@ class ActionInterface {
// An Action<F> is a copyable and IMMUTABLE (except by assignment)
// object that represents an action to be taken when a mock function
// of type F is called. The implementation of Action<T> is just a
-// linked_ptr to const ActionInterface<T>, so copying is fairly cheap.
-// Don't inherit from Action!
-//
+// std::shared_ptr to const ActionInterface<T>. Don't inherit from Action!
// You can view an object implementing ActionInterface<F> as a
// concrete action (including its current state), and an Action<F>
// object as a handle to it.
template <typename F>
class Action {
+ // Adapter class to allow constructing Action from a legacy ActionInterface.
+ // New code should create Actions from functors instead.
+ struct ActionAdapter {
+ // Adapter must be copyable to satisfy std::function requirements.
+ ::std::shared_ptr<ActionInterface<F>> impl_;
+
+ template <typename... Args>
+ typename internal::Function<F>::Result operator()(Args&&... args) {
+ return impl_->Perform(
+ ::std::forward_as_tuple(::std::forward<Args>(args)...));
+ }
+ };
+
public:
typedef typename internal::Function<F>::Result Result;
typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
// Constructs a null Action. Needed for storing Action objects in
// STL containers.
- Action() : impl_(NULL) {}
+ Action() {}
- // Constructs an Action from its implementation. A NULL impl is
- // used to represent the "do-default" action.
- explicit Action(ActionInterface<F>* impl) : impl_(impl) {}
+ // Construct an Action from a specified callable.
+ // This cannot take std::function directly, because then Action would not be
+ // directly constructible from lambda (it would require two conversions).
+ template <typename G,
+ typename = typename ::std::enable_if<
+ ::std::is_constructible<::std::function<F>, G>::value>::type>
+ Action(G&& fun) : fun_(::std::forward<G>(fun)) {} // NOLINT
- // Copy constructor.
- Action(const Action& action) : impl_(action.impl_) {}
+ // Constructs an Action from its implementation.
+ explicit Action(ActionInterface<F>* impl)
+ : fun_(ActionAdapter{::std::shared_ptr<ActionInterface<F>>(impl)}) {}
// This constructor allows us to turn an Action<Func> object into an
// Action<F>, as long as F's arguments can be implicitly converted
- // to Func's and Func's return type can be implicitly converted to
- // F's.
+ // to Func's and Func's return type can be implicitly converted to F's.
template <typename Func>
- explicit Action(const Action<Func>& action);
+ explicit Action(const Action<Func>& action) : fun_(action.fun_) {}
- // Returns true iff this is the DoDefault() action.
- bool IsDoDefault() const { return impl_.get() == NULL; }
+ // Returns true if and only if this is the DoDefault() action.
+ bool IsDoDefault() const { return fun_ == nullptr; }
// Performs the action. Note that this method is const even though
// the corresponding method in ActionInterface is not. The reason
@@ -384,22 +385,19 @@ class Action {
// another concrete action, not that the concrete action it binds to
// cannot change state. (Think of the difference between a const
// pointer and a pointer to const.)
- Result Perform(const ArgumentTuple& args) const {
- internal::Assert(
- !IsDoDefault(), __FILE__, __LINE__,
- "You are using DoDefault() inside a composite action like "
- "DoAll() or WithArgs(). This is not supported for technical "
- "reasons. Please instead spell out the default action, or "
- "assign the default action to an Action variable and use "
- "the variable in various places.");
- return impl_->Perform(args);
+ Result Perform(ArgumentTuple args) const {
+ if (IsDoDefault()) {
+ internal::IllegalDoDefault(__FILE__, __LINE__);
+ }
+ return internal::Apply(fun_, ::std::move(args));
}
private:
- template <typename F1, typename F2>
- friend class internal::ActionAdaptor;
+ template <typename G>
+ friend class Action;
- internal::linked_ptr<ActionInterface<F> > impl_;
+ // fun_ is an empty function if and only if this is the DoDefault() action.
+ ::std::function<F> fun_;
};
// The PolymorphicAction class template makes it easy to implement a
@@ -414,7 +412,7 @@ class Action {
// template <typename Result, typename ArgumentTuple>
// Result Perform(const ArgumentTuple& args) const {
// // Processes the arguments and returns a result, using
-// // tr1::get<N>(args) to get the N-th (0-based) argument in the tuple.
+// // std::get<N>(args) to get the N-th (0-based) argument in the tuple.
// }
// ...
// };
@@ -442,7 +440,7 @@ class PolymorphicAction {
explicit MonomorphicImpl(const Impl& impl) : impl_(impl) {}
- virtual Result Perform(const ArgumentTuple& args) {
+ Result Perform(const ArgumentTuple& args) override {
return impl_.template Perform<Result>(args);
}
@@ -478,31 +476,11 @@ inline PolymorphicAction<Impl> MakePolymorphicAction(const Impl& impl) {
namespace internal {
-// Allows an Action<F2> object to pose as an Action<F1>, as long as F2
-// and F1 are compatible.
-template <typename F1, typename F2>
-class ActionAdaptor : public ActionInterface<F1> {
- public:
- typedef typename internal::Function<F1>::Result Result;
- typedef typename internal::Function<F1>::ArgumentTuple ArgumentTuple;
-
- explicit ActionAdaptor(const Action<F2>& from) : impl_(from.impl_) {}
-
- virtual Result Perform(const ArgumentTuple& args) {
- return impl_->Perform(args);
- }
-
- private:
- const internal::linked_ptr<ActionInterface<F2> > impl_;
-
- GTEST_DISALLOW_ASSIGN_(ActionAdaptor);
-};
-
// Helper struct to specialize ReturnAction to execute a move instead of a copy
// on return. Useful for move-only types, but could be used on any type.
template <typename T>
struct ByMoveWrapper {
- explicit ByMoveWrapper(T value) : payload(internal::move(value)) {}
+ explicit ByMoveWrapper(T value) : payload(std::move(value)) {}
T payload;
};
@@ -530,18 +508,21 @@ struct ByMoveWrapper {
// statement, and conversion of the result of Return to Action<T(U)> is a
// good place for that.
//
+// The real life example of the above scenario happens when an invocation
+// of gtl::Container() is passed into Return.
+//
template <typename R>
class ReturnAction {
public:
// Constructs a ReturnAction object from the value to be returned.
// 'value' is passed by value instead of by const reference in order
// to allow Return("string literal") to compile.
- explicit ReturnAction(R value) : value_(new R(internal::move(value))) {}
+ explicit ReturnAction(R value) : value_(new R(std::move(value))) {}
// This template type conversion operator allows Return(x) to be
// used in ANY function that returns x's type.
template <typename F>
- operator Action<F>() const {
+ operator Action<F>() const { // NOLINT
// Assert statement belongs here because this is the best place to verify
// conditions on F. It produces the clearest error messages
// in most compilers.
@@ -552,8 +533,10 @@ class ReturnAction {
// in the Impl class. But both definitions must be the same.
typedef typename Function<F>::Result Result;
GTEST_COMPILE_ASSERT_(
- !is_reference<Result>::value,
+ !std::is_reference<Result>::value,
use_ReturnRef_instead_of_Return_to_return_a_reference);
+ static_assert(!std::is_void<Result>::value,
+ "Can't use Return() on an action expected to return `void`.");
return Action<F>(new Impl<R, F>(value_));
}
@@ -572,14 +555,14 @@ class ReturnAction {
// Result to call. ImplicitCast_ forces the compiler to convert R to
// Result without considering explicit constructors, thus resolving the
// ambiguity. value_ is then initialized using its copy constructor.
- explicit Impl(const linked_ptr<R>& value)
+ explicit Impl(const std::shared_ptr<R>& value)
: value_before_cast_(*value),
value_(ImplicitCast_<Result>(value_before_cast_)) {}
- virtual Result Perform(const ArgumentTuple&) { return value_; }
+ Result Perform(const ArgumentTuple&) override { return value_; }
private:
- GTEST_COMPILE_ASSERT_(!is_reference<Result>::value,
+ GTEST_COMPILE_ASSERT_(!std::is_reference<Result>::value,
Result_cannot_be_a_reference_type);
// We save the value before casting just in case it is being cast to a
// wrapper type.
@@ -597,24 +580,24 @@ class ReturnAction {
typedef typename Function<F>::Result Result;
typedef typename Function<F>::ArgumentTuple ArgumentTuple;
- explicit Impl(const linked_ptr<R>& wrapper)
+ explicit Impl(const std::shared_ptr<R>& wrapper)
: performed_(false), wrapper_(wrapper) {}
- virtual Result Perform(const ArgumentTuple&) {
+ Result Perform(const ArgumentTuple&) override {
GTEST_CHECK_(!performed_)
<< "A ByMove() action should only be performed once.";
performed_ = true;
- return internal::move(wrapper_->payload);
+ return std::move(wrapper_->payload);
}
private:
bool performed_;
- const linked_ptr<R> wrapper_;
+ const std::shared_ptr<R> wrapper_;
GTEST_DISALLOW_ASSIGN_(Impl);
};
- const linked_ptr<R> value_;
+ const std::shared_ptr<R> value_;
GTEST_DISALLOW_ASSIGN_(ReturnAction);
};
@@ -627,13 +610,7 @@ class ReturnNullAction {
// pointer type on compile time.
template <typename Result, typename ArgumentTuple>
static Result Perform(const ArgumentTuple&) {
-#if GTEST_LANG_CXX11
return nullptr;
-#else
- GTEST_COMPILE_ASSERT_(internal::is_pointer<Result>::value,
- ReturnNull_can_be_used_to_return_a_pointer_only);
- return NULL;
-#endif // GTEST_LANG_CXX11
}
};
@@ -643,7 +620,7 @@ class ReturnVoidAction {
// Allows Return() to be used in any void-returning function.
template <typename Result, typename ArgumentTuple>
static void Perform(const ArgumentTuple&) {
- CompileAssertTypesEqual<void, Result>();
+ static_assert(std::is_void<Result>::value, "Result should be void.");
}
};
@@ -664,7 +641,7 @@ class ReturnRefAction {
// Asserts that the function return type is a reference. This
// catches the user error of using ReturnRef(x) when Return(x)
// should be used, and generates some helpful error message.
- GTEST_COMPILE_ASSERT_(internal::is_reference<Result>::value,
+ GTEST_COMPILE_ASSERT_(std::is_reference<Result>::value,
use_Return_instead_of_ReturnRef_to_return_a_value);
return Action<F>(new Impl<F>(ref_));
}
@@ -679,9 +656,7 @@ class ReturnRefAction {
explicit Impl(T& ref) : ref_(ref) {} // NOLINT
- virtual Result Perform(const ArgumentTuple&) {
- return ref_;
- }
+ Result Perform(const ArgumentTuple&) override { return ref_; }
private:
T& ref_;
@@ -713,7 +688,7 @@ class ReturnRefOfCopyAction {
// catches the user error of using ReturnRefOfCopy(x) when Return(x)
// should be used, and generates some helpful error message.
GTEST_COMPILE_ASSERT_(
- internal::is_reference<Result>::value,
+ std::is_reference<Result>::value,
use_Return_instead_of_ReturnRefOfCopy_to_return_a_value);
return Action<F>(new Impl<F>(value_));
}
@@ -728,9 +703,7 @@ class ReturnRefOfCopyAction {
explicit Impl(const T& value) : value_(value) {} // NOLINT
- virtual Result Perform(const ArgumentTuple&) {
- return value_;
- }
+ Result Perform(const ArgumentTuple&) override { return value_; }
private:
T value_;
@@ -749,7 +722,7 @@ class DoDefaultAction {
// This template type conversion operator allows DoDefault() to be
// used in any function.
template <typename F>
- operator Action<F>() const { return Action<F>(NULL); }
+ operator Action<F>() const { return Action<F>(); } // NOLINT
};
// Implements the Assign action to set a given pointer referent to a
@@ -797,92 +770,58 @@ class SetErrnoAndReturnAction {
#endif // !GTEST_OS_WINDOWS_MOBILE
// Implements the SetArgumentPointee<N>(x) action for any function
-// whose N-th argument (0-based) is a pointer to x's type. The
-// template parameter kIsProto is true iff type A is ProtocolMessage,
-// proto2::Message, or a sub-class of those.
-template <size_t N, typename A, bool kIsProto>
-class SetArgumentPointeeAction {
- public:
- // Constructs an action that sets the variable pointed to by the
- // N-th function argument to 'value'.
- explicit SetArgumentPointeeAction(const A& value) : value_(value) {}
-
- template <typename Result, typename ArgumentTuple>
- void Perform(const ArgumentTuple& args) const {
- CompileAssertTypesEqual<void, Result>();
- *::testing::get<N>(args) = value_;
+// whose N-th argument (0-based) is a pointer to x's type.
+template <size_t N, typename A, typename = void>
+struct SetArgumentPointeeAction {
+ A value;
+
+ template <typename... Args>
+ void operator()(const Args&... args) const {
+ *::std::get<N>(std::tie(args...)) = value;
}
-
- private:
- const A value_;
-
- GTEST_DISALLOW_ASSIGN_(SetArgumentPointeeAction);
};
-template <size_t N, typename Proto>
-class SetArgumentPointeeAction<N, Proto, true> {
- public:
- // Constructs an action that sets the variable pointed to by the
- // N-th function argument to 'proto'. Both ProtocolMessage and
- // proto2::Message have the CopyFrom() method, so the same
- // implementation works for both.
- explicit SetArgumentPointeeAction(const Proto& proto) : proto_(new Proto) {
- proto_->CopyFrom(proto);
- }
-
- template <typename Result, typename ArgumentTuple>
- void Perform(const ArgumentTuple& args) const {
- CompileAssertTypesEqual<void, Result>();
- ::testing::get<N>(args)->CopyFrom(*proto_);
+// Implements the Invoke(object_ptr, &Class::Method) action.
+template <class Class, typename MethodPtr>
+struct InvokeMethodAction {
+ Class* const obj_ptr;
+ const MethodPtr method_ptr;
+
+ template <typename... Args>
+ auto operator()(Args&&... args) const
+ -> decltype((obj_ptr->*method_ptr)(std::forward<Args>(args)...)) {
+ return (obj_ptr->*method_ptr)(std::forward<Args>(args)...);
}
-
- private:
- const internal::linked_ptr<Proto> proto_;
-
- GTEST_DISALLOW_ASSIGN_(SetArgumentPointeeAction);
};
// Implements the InvokeWithoutArgs(f) action. The template argument
// FunctionImpl is the implementation type of f, which can be either a
// function pointer or a functor. InvokeWithoutArgs(f) can be used as an
-// Action<F> as long as f's type is compatible with F (i.e. f can be
-// assigned to a tr1::function<F>).
+// Action<F> as long as f's type is compatible with F.
template <typename FunctionImpl>
-class InvokeWithoutArgsAction {
- public:
- // The c'tor makes a copy of function_impl (either a function
- // pointer or a functor).
- explicit InvokeWithoutArgsAction(FunctionImpl function_impl)
- : function_impl_(function_impl) {}
+struct InvokeWithoutArgsAction {
+ FunctionImpl function_impl;
// Allows InvokeWithoutArgs(f) to be used as any action whose type is
// compatible with f.
- template <typename Result, typename ArgumentTuple>
- Result Perform(const ArgumentTuple&) { return function_impl_(); }
-
- private:
- FunctionImpl function_impl_;
-
- GTEST_DISALLOW_ASSIGN_(InvokeWithoutArgsAction);
+ template <typename... Args>
+ auto operator()(const Args&...) -> decltype(function_impl()) {
+ return function_impl();
+ }
};
// Implements the InvokeWithoutArgs(object_ptr, &Class::Method) action.
template <class Class, typename MethodPtr>
-class InvokeMethodWithoutArgsAction {
- public:
- InvokeMethodWithoutArgsAction(Class* obj_ptr, MethodPtr method_ptr)
- : obj_ptr_(obj_ptr), method_ptr_(method_ptr) {}
+struct InvokeMethodWithoutArgsAction {
+ Class* const obj_ptr;
+ const MethodPtr method_ptr;
- template <typename Result, typename ArgumentTuple>
- Result Perform(const ArgumentTuple&) const {
- return (obj_ptr_->*method_ptr_)();
- }
+ using ReturnType = typename std::result_of<MethodPtr(Class*)>::type;
- private:
- Class* const obj_ptr_;
- const MethodPtr method_ptr_;
-
- GTEST_DISALLOW_ASSIGN_(InvokeMethodWithoutArgsAction);
+ template <typename... Args>
+ ReturnType operator()(const Args&...) const {
+ return (obj_ptr->*method_ptr)();
+ }
};
// Implements the IgnoreResult(action) action.
@@ -904,7 +843,7 @@ class IgnoreResultAction {
typedef typename internal::Function<F>::Result Result;
// Asserts at compile time that F returns void.
- CompileAssertTypesEqual<void, Result>();
+ static_assert(std::is_void<Result>::value, "Result type should be void.");
return Action<F>(new Impl<F>(action_));
}
@@ -918,7 +857,7 @@ class IgnoreResultAction {
explicit Impl(const A& action) : action_(action) {}
- virtual void Perform(const ArgumentTuple& args) {
+ void Perform(const ArgumentTuple& args) override {
// Performs the action and ignores its result.
action_.Perform(args);
}
@@ -939,76 +878,51 @@ class IgnoreResultAction {
GTEST_DISALLOW_ASSIGN_(IgnoreResultAction);
};
-// A ReferenceWrapper<T> object represents a reference to type T,
-// which can be either const or not. It can be explicitly converted
-// from, and implicitly converted to, a T&. Unlike a reference,
-// ReferenceWrapper<T> can be copied and can survive template type
-// inference. This is used to support by-reference arguments in the
-// InvokeArgument<N>(...) action. The idea was from "reference
-// wrappers" in tr1, which we don't have in our source tree yet.
-template <typename T>
-class ReferenceWrapper {
- public:
- // Constructs a ReferenceWrapper<T> object from a T&.
- explicit ReferenceWrapper(T& l_value) : pointer_(&l_value) {} // NOLINT
-
- // Allows a ReferenceWrapper<T> object to be implicitly converted to
- // a T&.
- operator T&() const { return *pointer_; }
- private:
- T* pointer_;
+template <typename InnerAction, size_t... I>
+struct WithArgsAction {
+ InnerAction action;
+
+ // The inner action could be anything convertible to Action<X>.
+ // We use the conversion operator to detect the signature of the inner Action.
+ template <typename R, typename... Args>
+ operator Action<R(Args...)>() const { // NOLINT
+ Action<R(typename std::tuple_element<I, std::tuple<Args...>>::type...)>
+ converted(action);
+
+ return [converted](Args... args) -> R {
+ return converted.Perform(std::forward_as_tuple(
+ std::get<I>(std::forward_as_tuple(std::forward<Args>(args)...))...));
+ };
+ }
};
-// Allows the expression ByRef(x) to be printed as a reference to x.
-template <typename T>
-void PrintTo(const ReferenceWrapper<T>& ref, ::std::ostream* os) {
- T& value = ref;
- UniversalPrinter<T&>::Print(value, os);
-}
+template <typename... Actions>
+struct DoAllAction {
+ private:
+ template <typename... Args, size_t... I>
+ std::vector<Action<void(Args...)>> Convert(IndexSequence<I...>) const {
+ return {std::get<I>(actions)...};
+ }
-// Does two actions sequentially. Used for implementing the DoAll(a1,
-// a2, ...) action.
-template <typename Action1, typename Action2>
-class DoBothAction {
public:
- DoBothAction(Action1 action1, Action2 action2)
- : action1_(action1), action2_(action2) {}
-
- // This template type conversion operator allows DoAll(a1, ..., a_n)
- // to be used in ANY function of compatible type.
- template <typename F>
- operator Action<F>() const {
- return Action<F>(new Impl<F>(action1_, action2_));
+ std::tuple<Actions...> actions;
+
+ template <typename R, typename... Args>
+ operator Action<R(Args...)>() const { // NOLINT
+ struct Op {
+ std::vector<Action<void(Args...)>> converted;
+ Action<R(Args...)> last;
+ R operator()(Args... args) const {
+ auto tuple_args = std::forward_as_tuple(std::forward<Args>(args)...);
+ for (auto& a : converted) {
+ a.Perform(tuple_args);
+ }
+ return last.Perform(tuple_args);
+ }
+ };
+ return Op{Convert<Args...>(MakeIndexSequence<sizeof...(Actions) - 1>()),
+ std::get<sizeof...(Actions) - 1>(actions)};
}
-
- private:
- // Implements the DoAll(...) action for a particular function type F.
- template <typename F>
- class Impl : public ActionInterface<F> {
- public:
- typedef typename Function<F>::Result Result;
- typedef typename Function<F>::ArgumentTuple ArgumentTuple;
- typedef typename Function<F>::MakeResultVoid VoidResult;
-
- Impl(const Action<VoidResult>& action1, const Action<F>& action2)
- : action1_(action1), action2_(action2) {}
-
- virtual Result Perform(const ArgumentTuple& args) {
- action1_.Perform(args);
- return action2_.Perform(args);
- }
-
- private:
- const Action<VoidResult> action1_;
- const Action<F> action2_;
-
- GTEST_DISALLOW_ASSIGN_(Impl);
- };
-
- Action1 action1_;
- Action2 action2_;
-
- GTEST_DISALLOW_ASSIGN_(DoBothAction);
};
} // namespace internal
@@ -1029,9 +943,9 @@ class DoBothAction {
// return sqrt(x*x + y*y);
// }
// ...
-// EXEPCT_CALL(mock, Foo("abc", _, _))
+// EXPECT_CALL(mock, Foo("abc", _, _))
// .WillOnce(Invoke(DistanceToOriginWithLabel));
-// EXEPCT_CALL(mock, Bar(5, _, _))
+// EXPECT_CALL(mock, Bar(5, _, _))
// .WillOnce(Invoke(DistanceToOriginWithIndex));
//
// you could write
@@ -1041,25 +955,55 @@ class DoBothAction {
// return sqrt(x*x + y*y);
// }
// ...
-// EXEPCT_CALL(mock, Foo("abc", _, _)).WillOnce(Invoke(DistanceToOrigin));
-// EXEPCT_CALL(mock, Bar(5, _, _)).WillOnce(Invoke(DistanceToOrigin));
+// EXPECT_CALL(mock, Foo("abc", _, _)).WillOnce(Invoke(DistanceToOrigin));
+// EXPECT_CALL(mock, Bar(5, _, _)).WillOnce(Invoke(DistanceToOrigin));
typedef internal::IgnoredValue Unused;
-// This constructor allows us to turn an Action<From> object into an
-// Action<To>, as long as To's arguments can be implicitly converted
-// to From's and From's return type cann be implicitly converted to
-// To's.
-template <typename To>
-template <typename From>
-Action<To>::Action(const Action<From>& from)
- : impl_(new internal::ActionAdaptor<To, From>(from)) {}
+// Creates an action that does actions a1, a2, ..., sequentially in
+// each invocation.
+template <typename... Action>
+internal::DoAllAction<typename std::decay<Action>::type...> DoAll(
+ Action&&... action) {
+ return {std::forward_as_tuple(std::forward<Action>(action)...)};
+}
+
+// WithArg<k>(an_action) creates an action that passes the k-th
+// (0-based) argument of the mock function to an_action and performs
+// it. It adapts an action accepting one argument to one that accepts
+// multiple arguments. For convenience, we also provide
+// WithArgs<k>(an_action) (defined below) as a synonym.
+template <size_t k, typename InnerAction>
+internal::WithArgsAction<typename std::decay<InnerAction>::type, k>
+WithArg(InnerAction&& action) {
+ return {std::forward<InnerAction>(action)};
+}
+
+// WithArgs<N1, N2, ..., Nk>(an_action) creates an action that passes
+// the selected arguments of the mock function to an_action and
+// performs it. It serves as an adaptor between actions with
+// different argument lists.
+template <size_t k, size_t... ks, typename InnerAction>
+internal::WithArgsAction<typename std::decay<InnerAction>::type, k, ks...>
+WithArgs(InnerAction&& action) {
+ return {std::forward<InnerAction>(action)};
+}
+
+// WithoutArgs(inner_action) can be used in a mock function with a
+// non-empty argument list to perform inner_action, which takes no
+// argument. In other words, it adapts an action accepting no
+// argument to one that accepts (and ignores) arguments.
+template <typename InnerAction>
+internal::WithArgsAction<typename std::decay<InnerAction>::type>
+WithoutArgs(InnerAction&& action) {
+ return {std::forward<InnerAction>(action)};
+}
// Creates an action that returns 'value'. 'value' is passed by value
// instead of const reference - otherwise Return("string literal")
// will trigger a compiler error about using array as initializer.
template <typename R>
internal::ReturnAction<R> Return(R value) {
- return internal::ReturnAction<R>(internal::move(value));
+ return internal::ReturnAction<R>(std::move(value));
}
// Creates an action that returns NULL.
@@ -1092,7 +1036,7 @@ inline internal::ReturnRefOfCopyAction<R> ReturnRefOfCopy(const R& x) {
// invariant.
template <typename R>
internal::ByMoveWrapper<R> ByMove(R x) {
- return internal::ByMoveWrapper<R>(internal::move(x));
+ return internal::ByMoveWrapper<R>(std::move(x));
}
// Creates an action that does the default action for the give mock function.
@@ -1103,43 +1047,14 @@ inline internal::DoDefaultAction DoDefault() {
// Creates an action that sets the variable pointed by the N-th
// (0-based) function argument to 'value'.
template <size_t N, typename T>
-PolymorphicAction<
- internal::SetArgumentPointeeAction<
- N, T, internal::IsAProtocolMessage<T>::value> >
-SetArgPointee(const T& x) {
- return MakePolymorphicAction(internal::SetArgumentPointeeAction<
- N, T, internal::IsAProtocolMessage<T>::value>(x));
-}
-
-#if !((GTEST_GCC_VER_ && GTEST_GCC_VER_ < 40000) || GTEST_OS_SYMBIAN)
-// This overload allows SetArgPointee() to accept a string literal.
-// GCC prior to the version 4.0 and Symbian C++ compiler cannot distinguish
-// this overload from the templated version and emit a compile error.
-template <size_t N>
-PolymorphicAction<
- internal::SetArgumentPointeeAction<N, const char*, false> >
-SetArgPointee(const char* p) {
- return MakePolymorphicAction(internal::SetArgumentPointeeAction<
- N, const char*, false>(p));
-}
-
-template <size_t N>
-PolymorphicAction<
- internal::SetArgumentPointeeAction<N, const wchar_t*, false> >
-SetArgPointee(const wchar_t* p) {
- return MakePolymorphicAction(internal::SetArgumentPointeeAction<
- N, const wchar_t*, false>(p));
+internal::SetArgumentPointeeAction<N, T> SetArgPointee(T x) {
+ return {std::move(x)};
}
-#endif
// The following version is DEPRECATED.
template <size_t N, typename T>
-PolymorphicAction<
- internal::SetArgumentPointeeAction<
- N, T, internal::IsAProtocolMessage<T>::value> >
-SetArgumentPointee(const T& x) {
- return MakePolymorphicAction(internal::SetArgumentPointeeAction<
- N, T, internal::IsAProtocolMessage<T>::value>(x));
+internal::SetArgumentPointeeAction<N, T> SetArgumentPointee(T x) {
+ return {std::move(x)};
}
// Creates an action that sets a pointer referent to a given value.
@@ -1160,24 +1075,38 @@ SetErrnoAndReturn(int errval, T result) {
#endif // !GTEST_OS_WINDOWS_MOBILE
-// Various overloads for InvokeWithoutArgs().
+// Various overloads for Invoke().
+
+// Legacy function.
+// Actions can now be implicitly constructed from callables. No need to create
+// wrapper objects.
+// This function exists for backwards compatibility.
+template <typename FunctionImpl>
+typename std::decay<FunctionImpl>::type Invoke(FunctionImpl&& function_impl) {
+ return std::forward<FunctionImpl>(function_impl);
+}
+
+// Creates an action that invokes the given method on the given object
+// with the mock function's arguments.
+template <class Class, typename MethodPtr>
+internal::InvokeMethodAction<Class, MethodPtr> Invoke(Class* obj_ptr,
+ MethodPtr method_ptr) {
+ return {obj_ptr, method_ptr};
+}
// Creates an action that invokes 'function_impl' with no argument.
template <typename FunctionImpl>
-PolymorphicAction<internal::InvokeWithoutArgsAction<FunctionImpl> >
+internal::InvokeWithoutArgsAction<typename std::decay<FunctionImpl>::type>
InvokeWithoutArgs(FunctionImpl function_impl) {
- return MakePolymorphicAction(
- internal::InvokeWithoutArgsAction<FunctionImpl>(function_impl));
+ return {std::move(function_impl)};
}
// Creates an action that invokes the given method on the given object
// with no argument.
template <class Class, typename MethodPtr>
-PolymorphicAction<internal::InvokeMethodWithoutArgsAction<Class, MethodPtr> >
-InvokeWithoutArgs(Class* obj_ptr, MethodPtr method_ptr) {
- return MakePolymorphicAction(
- internal::InvokeMethodWithoutArgsAction<Class, MethodPtr>(
- obj_ptr, method_ptr));
+internal::InvokeMethodWithoutArgsAction<Class, MethodPtr> InvokeWithoutArgs(
+ Class* obj_ptr, MethodPtr method_ptr) {
+ return {obj_ptr, method_ptr};
}
// Creates an action that performs an_action and throws away its
@@ -1195,11 +1124,19 @@ inline internal::IgnoreResultAction<A> IgnoreResult(const A& an_action) {
// where Base is a base class of Derived, just write:
//
// ByRef<const Base>(derived)
+//
+// N.B. ByRef is redundant with std::ref, std::cref and std::reference_wrapper.
+// However, it may still be used for consistency with ByMove().
template <typename T>
-inline internal::ReferenceWrapper<T> ByRef(T& l_value) { // NOLINT
- return internal::ReferenceWrapper<T>(l_value);
+inline ::std::reference_wrapper<T> ByRef(T& l_value) { // NOLINT
+ return ::std::reference_wrapper<T>(l_value);
}
} // namespace testing
+#ifdef _MSC_VER
+# pragma warning(pop)
+#endif
+
+
#endif // GMOCK_INCLUDE_GMOCK_GMOCK_ACTIONS_H_
diff --git a/extern/gmock/include/gmock/gmock-cardinalities.h b/extern/gmock/include/gmock/gmock-cardinalities.h
index fc315f92ab5..46e01e102d5 100644
--- a/extern/gmock/include/gmock/gmock-cardinalities.h
+++ b/extern/gmock/include/gmock/gmock-cardinalities.h
@@ -26,8 +26,7 @@
// 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: wan@google.com (Zhanyong Wan)
+
// Google Mock - a framework for writing C++ mock classes.
//
@@ -35,14 +34,20 @@
// cardinalities can be defined by the user implementing the
// CardinalityInterface interface if necessary.
+// GOOGLETEST_CM0002 DO NOT DELETE
+
#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_CARDINALITIES_H_
#define GMOCK_INCLUDE_GMOCK_GMOCK_CARDINALITIES_H_
#include <limits.h>
+#include <memory>
#include <ostream> // NOLINT
#include "gmock/internal/gmock-port.h"
#include "gtest/gtest.h"
+GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
+/* class A needs to have dll-interface to be used by clients of class B */)
+
namespace testing {
// To implement a cardinality Foo, define:
@@ -65,10 +70,12 @@ class CardinalityInterface {
virtual int ConservativeLowerBound() const { return 0; }
virtual int ConservativeUpperBound() const { return INT_MAX; }
- // Returns true iff call_count calls will satisfy this cardinality.
+ // Returns true if and only if call_count calls will satisfy this
+ // cardinality.
virtual bool IsSatisfiedByCallCount(int call_count) const = 0;
- // Returns true iff call_count calls will saturate this cardinality.
+ // Returns true if and only if call_count calls will saturate this
+ // cardinality.
virtual bool IsSaturatedByCallCount(int call_count) const = 0;
// Describes self to an ostream.
@@ -77,9 +84,8 @@ class CardinalityInterface {
// A Cardinality is a copyable and IMMUTABLE (except by assignment)
// object that specifies how many times a mock function is expected to
-// be called. The implementation of Cardinality is just a linked_ptr
-// to const CardinalityInterface, so copying is fairly cheap.
-// Don't inherit from Cardinality!
+// be called. The implementation of Cardinality is just a std::shared_ptr
+// to const CardinalityInterface. Don't inherit from Cardinality!
class GTEST_API_ Cardinality {
public:
// Constructs a null cardinality. Needed for storing Cardinality
@@ -94,17 +100,19 @@ class GTEST_API_ Cardinality {
int ConservativeLowerBound() const { return impl_->ConservativeLowerBound(); }
int ConservativeUpperBound() const { return impl_->ConservativeUpperBound(); }
- // Returns true iff call_count calls will satisfy this cardinality.
+ // Returns true if and only if call_count calls will satisfy this
+ // cardinality.
bool IsSatisfiedByCallCount(int call_count) const {
return impl_->IsSatisfiedByCallCount(call_count);
}
- // Returns true iff call_count calls will saturate this cardinality.
+ // Returns true if and only if call_count calls will saturate this
+ // cardinality.
bool IsSaturatedByCallCount(int call_count) const {
return impl_->IsSaturatedByCallCount(call_count);
}
- // Returns true iff call_count calls will over-saturate this
+ // Returns true if and only if call_count calls will over-saturate this
// cardinality, i.e. exceed the maximum number of allowed calls.
bool IsOverSaturatedByCallCount(int call_count) const {
return impl_->IsSaturatedByCallCount(call_count) &&
@@ -119,7 +127,7 @@ class GTEST_API_ Cardinality {
::std::ostream* os);
private:
- internal::linked_ptr<const CardinalityInterface> impl_;
+ std::shared_ptr<const CardinalityInterface> impl_;
};
// Creates a cardinality that allows at least n calls.
@@ -144,4 +152,6 @@ inline Cardinality MakeCardinality(const CardinalityInterface* c) {
} // namespace testing
+GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251
+
#endif // GMOCK_INCLUDE_GMOCK_GMOCK_CARDINALITIES_H_
diff --git a/extern/gmock/include/gmock/gmock-function-mocker.h b/extern/gmock/include/gmock/gmock-function-mocker.h
new file mode 100644
index 00000000000..cc1535c806b
--- /dev/null
+++ b/extern/gmock/include/gmock/gmock-function-mocker.h
@@ -0,0 +1,253 @@
+// Copyright 2007, Google Inc.
+// All rights reserved.
+//
+// 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.
+
+// Google Mock - a framework for writing C++ mock classes.
+//
+// This file implements MOCK_METHOD.
+
+// GOOGLETEST_CM0002 DO NOT DELETE
+
+#ifndef THIRD_PARTY_GOOGLETEST_GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_FUNCTION_MOCKER_H_ // NOLINT
+#define THIRD_PARTY_GOOGLETEST_GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_FUNCTION_MOCKER_H_ // NOLINT
+
+#include "gmock/gmock-generated-function-mockers.h" // NOLINT
+#include "gmock/internal/gmock-pp.h"
+
+#define MOCK_METHOD(...) \
+ GMOCK_PP_VARIADIC_CALL(GMOCK_INTERNAL_MOCK_METHOD_ARG_, __VA_ARGS__)
+
+#define GMOCK_INTERNAL_MOCK_METHOD_ARG_1(...) \
+ GMOCK_INTERNAL_WRONG_ARITY(__VA_ARGS__)
+
+#define GMOCK_INTERNAL_MOCK_METHOD_ARG_2(...) \
+ GMOCK_INTERNAL_WRONG_ARITY(__VA_ARGS__)
+
+#define GMOCK_INTERNAL_MOCK_METHOD_ARG_3(_Ret, _MethodName, _Args) \
+ GMOCK_INTERNAL_MOCK_METHOD_ARG_4(_Ret, _MethodName, _Args, ())
+
+#define GMOCK_INTERNAL_MOCK_METHOD_ARG_4(_Ret, _MethodName, _Args, _Spec) \
+ GMOCK_INTERNAL_ASSERT_PARENTHESIS(_Args); \
+ GMOCK_INTERNAL_ASSERT_PARENTHESIS(_Spec); \
+ GMOCK_INTERNAL_ASSERT_VALID_SIGNATURE( \
+ GMOCK_PP_NARG0 _Args, GMOCK_INTERNAL_SIGNATURE(_Ret, _Args)); \
+ GMOCK_INTERNAL_ASSERT_VALID_SPEC(_Spec) \
+ GMOCK_INTERNAL_MOCK_METHOD_IMPL( \
+ GMOCK_PP_NARG0 _Args, _MethodName, GMOCK_INTERNAL_HAS_CONST(_Spec), \
+ GMOCK_INTERNAL_HAS_OVERRIDE(_Spec), GMOCK_INTERNAL_HAS_FINAL(_Spec), \
+ GMOCK_INTERNAL_HAS_NOEXCEPT(_Spec), GMOCK_INTERNAL_GET_CALLTYPE(_Spec), \
+ (GMOCK_INTERNAL_SIGNATURE(_Ret, _Args)))
+
+#define GMOCK_INTERNAL_MOCK_METHOD_ARG_5(...) \
+ GMOCK_INTERNAL_WRONG_ARITY(__VA_ARGS__)
+
+#define GMOCK_INTERNAL_MOCK_METHOD_ARG_6(...) \
+ GMOCK_INTERNAL_WRONG_ARITY(__VA_ARGS__)
+
+#define GMOCK_INTERNAL_MOCK_METHOD_ARG_7(...) \
+ GMOCK_INTERNAL_WRONG_ARITY(__VA_ARGS__)
+
+#define GMOCK_INTERNAL_WRONG_ARITY(...) \
+ static_assert( \
+ false, \
+ "MOCK_METHOD must be called with 3 or 4 arguments. _Ret, " \
+ "_MethodName, _Args and optionally _Spec. _Args and _Spec must be " \
+ "enclosed in parentheses. If _Ret is a type with unprotected commas, " \
+ "it must also be enclosed in parentheses.")
+
+#define GMOCK_INTERNAL_ASSERT_PARENTHESIS(_Tuple) \
+ static_assert( \
+ GMOCK_PP_IS_ENCLOSED_PARENS(_Tuple), \
+ GMOCK_PP_STRINGIZE(_Tuple) " should be enclosed in parentheses.")
+
+#define GMOCK_INTERNAL_ASSERT_VALID_SIGNATURE(_N, ...) \
+ static_assert( \
+ std::is_function<__VA_ARGS__>::value, \
+ "Signature must be a function type, maybe return type contains " \
+ "unprotected comma."); \
+ static_assert( \
+ ::testing::tuple_size<typename ::testing::internal::Function< \
+ __VA_ARGS__>::ArgumentTuple>::value == _N, \
+ "This method does not take " GMOCK_PP_STRINGIZE( \
+ _N) " arguments. Parenthesize all types with unproctected commas.")
+
+#define GMOCK_INTERNAL_ASSERT_VALID_SPEC(_Spec) \
+ GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_ASSERT_VALID_SPEC_ELEMENT, ~, _Spec)
+
+#define GMOCK_INTERNAL_MOCK_METHOD_IMPL(_N, _MethodName, _Constness, \
+ _Override, _Final, _Noexcept, \
+ _CallType, _Signature) \
+ typename ::testing::internal::Function<GMOCK_PP_REMOVE_PARENS( \
+ _Signature)>::Result \
+ GMOCK_INTERNAL_EXPAND(_CallType) \
+ _MethodName(GMOCK_PP_REPEAT(GMOCK_INTERNAL_PARAMETER, _Signature, _N)) \
+ GMOCK_PP_IF(_Constness, const, ) GMOCK_PP_IF(_Noexcept, noexcept, ) \
+ GMOCK_PP_IF(_Override, override, ) \
+ GMOCK_PP_IF(_Final, final, ) { \
+ GMOCK_MOCKER_(_N, _Constness, _MethodName) \
+ .SetOwnerAndName(this, #_MethodName); \
+ return GMOCK_MOCKER_(_N, _Constness, _MethodName) \
+ .Invoke(GMOCK_PP_REPEAT(GMOCK_INTERNAL_FORWARD_ARG, _Signature, _N)); \
+ } \
+ ::testing::MockSpec<GMOCK_PP_REMOVE_PARENS(_Signature)> gmock_##_MethodName( \
+ GMOCK_PP_REPEAT(GMOCK_INTERNAL_MATCHER_PARAMETER, _Signature, _N)) \
+ GMOCK_PP_IF(_Constness, const, ) { \
+ GMOCK_MOCKER_(_N, _Constness, _MethodName).RegisterOwner(this); \
+ return GMOCK_MOCKER_(_N, _Constness, _MethodName) \
+ .With(GMOCK_PP_REPEAT(GMOCK_INTERNAL_MATCHER_ARGUMENT, , _N)); \
+ } \
+ ::testing::MockSpec<GMOCK_PP_REMOVE_PARENS(_Signature)> gmock_##_MethodName( \
+ const ::testing::internal::WithoutMatchers&, \
+ GMOCK_PP_IF(_Constness, const, )::testing::internal::Function< \
+ GMOCK_PP_REMOVE_PARENS(_Signature)>*) \
+ const GMOCK_PP_IF(_Noexcept, noexcept, ) { \
+ return GMOCK_PP_CAT(::testing::internal::AdjustConstness_, \
+ GMOCK_PP_IF(_Constness, const, ))(this) \
+ ->gmock_##_MethodName(GMOCK_PP_REPEAT( \
+ GMOCK_INTERNAL_A_MATCHER_ARGUMENT, _Signature, _N)); \
+ } \
+ mutable ::testing::FunctionMocker<GMOCK_PP_REMOVE_PARENS(_Signature)> \
+ GMOCK_MOCKER_(_N, _Constness, _MethodName)
+
+#define GMOCK_INTERNAL_EXPAND(...) __VA_ARGS__
+
+// Five Valid modifiers.
+#define GMOCK_INTERNAL_HAS_CONST(_Tuple) \
+ GMOCK_PP_HAS_COMMA(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_DETECT_CONST, ~, _Tuple))
+
+#define GMOCK_INTERNAL_HAS_OVERRIDE(_Tuple) \
+ GMOCK_PP_HAS_COMMA( \
+ GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_DETECT_OVERRIDE, ~, _Tuple))
+
+#define GMOCK_INTERNAL_HAS_FINAL(_Tuple) \
+ GMOCK_PP_HAS_COMMA(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_DETECT_FINAL, ~, _Tuple))
+
+#define GMOCK_INTERNAL_HAS_NOEXCEPT(_Tuple) \
+ GMOCK_PP_HAS_COMMA( \
+ GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_DETECT_NOEXCEPT, ~, _Tuple))
+
+#define GMOCK_INTERNAL_GET_CALLTYPE(_Tuple) \
+ GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_GET_CALLTYPE_IMPL, ~, _Tuple)
+
+#define GMOCK_INTERNAL_ASSERT_VALID_SPEC_ELEMENT(_i, _, _elem) \
+ static_assert( \
+ (GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_CONST(_i, _, _elem)) + \
+ GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_OVERRIDE(_i, _, _elem)) + \
+ GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_FINAL(_i, _, _elem)) + \
+ GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_NOEXCEPT(_i, _, _elem)) + \
+ GMOCK_INTERNAL_IS_CALLTYPE(_elem)) == 1, \
+ GMOCK_PP_STRINGIZE( \
+ _elem) " cannot be recognized as a valid specification modifier.");
+
+// Modifiers implementation.
+#define GMOCK_INTERNAL_DETECT_CONST(_i, _, _elem) \
+ GMOCK_PP_CAT(GMOCK_INTERNAL_DETECT_CONST_I_, _elem)
+
+#define GMOCK_INTERNAL_DETECT_CONST_I_const ,
+
+#define GMOCK_INTERNAL_DETECT_OVERRIDE(_i, _, _elem) \
+ GMOCK_PP_CAT(GMOCK_INTERNAL_DETECT_OVERRIDE_I_, _elem)
+
+#define GMOCK_INTERNAL_DETECT_OVERRIDE_I_override ,
+
+#define GMOCK_INTERNAL_DETECT_FINAL(_i, _, _elem) \
+ GMOCK_PP_CAT(GMOCK_INTERNAL_DETECT_FINAL_I_, _elem)
+
+#define GMOCK_INTERNAL_DETECT_FINAL_I_final ,
+
+// TODO(iserna): Maybe noexcept should accept an argument here as well.
+#define GMOCK_INTERNAL_DETECT_NOEXCEPT(_i, _, _elem) \
+ GMOCK_PP_CAT(GMOCK_INTERNAL_DETECT_NOEXCEPT_I_, _elem)
+
+#define GMOCK_INTERNAL_DETECT_NOEXCEPT_I_noexcept ,
+
+#define GMOCK_INTERNAL_GET_CALLTYPE_IMPL(_i, _, _elem) \
+ GMOCK_PP_IF(GMOCK_INTERNAL_IS_CALLTYPE(_elem), \
+ GMOCK_INTERNAL_GET_VALUE_CALLTYPE, GMOCK_PP_EMPTY) \
+ (_elem)
+
+// TODO(iserna): GMOCK_INTERNAL_IS_CALLTYPE and
+// GMOCK_INTERNAL_GET_VALUE_CALLTYPE needed more expansions to work on windows
+// maybe they can be simplified somehow.
+#define GMOCK_INTERNAL_IS_CALLTYPE(_arg) \
+ GMOCK_INTERNAL_IS_CALLTYPE_I( \
+ GMOCK_PP_CAT(GMOCK_INTERNAL_IS_CALLTYPE_HELPER_, _arg))
+#define GMOCK_INTERNAL_IS_CALLTYPE_I(_arg) GMOCK_PP_IS_ENCLOSED_PARENS(_arg)
+
+#define GMOCK_INTERNAL_GET_VALUE_CALLTYPE(_arg) \
+ GMOCK_INTERNAL_GET_VALUE_CALLTYPE_I( \
+ GMOCK_PP_CAT(GMOCK_INTERNAL_IS_CALLTYPE_HELPER_, _arg))
+#define GMOCK_INTERNAL_GET_VALUE_CALLTYPE_I(_arg) \
+ GMOCK_PP_CAT(GMOCK_PP_IDENTITY, _arg)
+
+#define GMOCK_INTERNAL_IS_CALLTYPE_HELPER_Calltype
+
+#define GMOCK_INTERNAL_SIGNATURE(_Ret, _Args) \
+ GMOCK_PP_IF(GMOCK_PP_IS_BEGIN_PARENS(_Ret), GMOCK_PP_REMOVE_PARENS, \
+ GMOCK_PP_IDENTITY) \
+ (_Ret)(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_GET_TYPE, _, _Args))
+
+#define GMOCK_INTERNAL_GET_TYPE(_i, _, _elem) \
+ GMOCK_PP_COMMA_IF(_i) \
+ GMOCK_PP_IF(GMOCK_PP_IS_BEGIN_PARENS(_elem), GMOCK_PP_REMOVE_PARENS, \
+ GMOCK_PP_IDENTITY) \
+ (_elem)
+
+#define GMOCK_INTERNAL_PARAMETER(_i, _Signature, _) \
+ GMOCK_PP_COMMA_IF(_i) \
+ GMOCK_INTERNAL_ARG_O(typename, GMOCK_PP_INC(_i), \
+ GMOCK_PP_REMOVE_PARENS(_Signature)) \
+ gmock_a##_i
+
+#define GMOCK_INTERNAL_FORWARD_ARG(_i, _Signature, _) \
+ GMOCK_PP_COMMA_IF(_i) \
+ ::std::forward<GMOCK_INTERNAL_ARG_O(typename, GMOCK_PP_INC(_i), \
+ GMOCK_PP_REMOVE_PARENS(_Signature))>( \
+ gmock_a##_i)
+
+#define GMOCK_INTERNAL_MATCHER_PARAMETER(_i, _Signature, _) \
+ GMOCK_PP_COMMA_IF(_i) \
+ GMOCK_INTERNAL_MATCHER_O(typename, GMOCK_PP_INC(_i), \
+ GMOCK_PP_REMOVE_PARENS(_Signature)) \
+ gmock_a##_i
+
+#define GMOCK_INTERNAL_MATCHER_ARGUMENT(_i, _1, _2) \
+ GMOCK_PP_COMMA_IF(_i) \
+ gmock_a##_i
+
+#define GMOCK_INTERNAL_A_MATCHER_ARGUMENT(_i, _Signature, _) \
+ GMOCK_PP_COMMA_IF(_i) \
+ ::testing::A<GMOCK_INTERNAL_ARG_O(typename, GMOCK_PP_INC(_i), \
+ GMOCK_PP_REMOVE_PARENS(_Signature))>()
+
+#define GMOCK_INTERNAL_ARG_O(_tn, _i, ...) GMOCK_ARG_(_tn, _i, __VA_ARGS__)
+
+#define GMOCK_INTERNAL_MATCHER_O(_tn, _i, ...) \
+ GMOCK_MATCHER_(_tn, _i, __VA_ARGS__)
+
+#endif // THIRD_PARTY_GOOGLETEST_GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_FUNCTION_MOCKER_H_
diff --git a/extern/gmock/include/gmock/gmock-generated-actions.h b/extern/gmock/include/gmock/gmock-generated-actions.h
index b5a889c0c3a..981af78ff9a 100644
--- a/extern/gmock/include/gmock/gmock-generated-actions.h
+++ b/extern/gmock/include/gmock/gmock-generated-actions.h
@@ -1,4 +1,6 @@
-// This file was GENERATED by a script. DO NOT EDIT BY HAND!!!
+// This file was GENERATED by command:
+// pump.py gmock-generated-actions.h.pump
+// DO NOT EDIT BY HAND!!!
// Copyright 2007, Google Inc.
// All rights reserved.
@@ -28,469 +30,26 @@
// 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: wan@google.com (Zhanyong Wan)
+
// Google Mock - a framework for writing C++ mock classes.
//
// This file implements some commonly used variadic actions.
+// GOOGLETEST_CM0002 DO NOT DELETE
+
#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_ACTIONS_H_
#define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_ACTIONS_H_
+#include <memory>
+#include <utility>
+
#include "gmock/gmock-actions.h"
#include "gmock/internal/gmock-port.h"
namespace testing {
namespace internal {
-// InvokeHelper<F> knows how to unpack an N-tuple and invoke an N-ary
-// function or method with the unpacked values, where F is a function
-// type that takes N arguments.
-template <typename Result, typename ArgumentTuple>
-class InvokeHelper;
-
-template <typename R>
-class InvokeHelper<R, ::testing::tuple<> > {
- public:
- template <typename Function>
- static R Invoke(Function function, const ::testing::tuple<>&) {
- return function();
- }
-
- template <class Class, typename MethodPtr>
- static R InvokeMethod(Class* obj_ptr,
- MethodPtr method_ptr,
- const ::testing::tuple<>&) {
- return (obj_ptr->*method_ptr)();
- }
-};
-
-template <typename R, typename A1>
-class InvokeHelper<R, ::testing::tuple<A1> > {
- public:
- template <typename Function>
- static R Invoke(Function function, const ::testing::tuple<A1>& args) {
- return function(get<0>(args));
- }
-
- template <class Class, typename MethodPtr>
- static R InvokeMethod(Class* obj_ptr,
- MethodPtr method_ptr,
- const ::testing::tuple<A1>& args) {
- return (obj_ptr->*method_ptr)(get<0>(args));
- }
-};
-
-template <typename R, typename A1, typename A2>
-class InvokeHelper<R, ::testing::tuple<A1, A2> > {
- public:
- template <typename Function>
- static R Invoke(Function function, const ::testing::tuple<A1, A2>& args) {
- return function(get<0>(args), get<1>(args));
- }
-
- template <class Class, typename MethodPtr>
- static R InvokeMethod(Class* obj_ptr,
- MethodPtr method_ptr,
- const ::testing::tuple<A1, A2>& args) {
- return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args));
- }
-};
-
-template <typename R, typename A1, typename A2, typename A3>
-class InvokeHelper<R, ::testing::tuple<A1, A2, A3> > {
- public:
- template <typename Function>
- static R Invoke(Function function, const ::testing::tuple<A1, A2, A3>& args) {
- return function(get<0>(args), get<1>(args), get<2>(args));
- }
-
- template <class Class, typename MethodPtr>
- static R InvokeMethod(Class* obj_ptr,
- MethodPtr method_ptr,
- const ::testing::tuple<A1, A2, A3>& args) {
- return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args),
- get<2>(args));
- }
-};
-
-template <typename R, typename A1, typename A2, typename A3, typename A4>
-class InvokeHelper<R, ::testing::tuple<A1, A2, A3, A4> > {
- public:
- template <typename Function>
- static R Invoke(Function function, const ::testing::tuple<A1, A2, A3,
- A4>& args) {
- return function(get<0>(args), get<1>(args), get<2>(args),
- get<3>(args));
- }
-
- template <class Class, typename MethodPtr>
- static R InvokeMethod(Class* obj_ptr,
- MethodPtr method_ptr,
- const ::testing::tuple<A1, A2, A3, A4>& args) {
- return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args),
- get<2>(args), get<3>(args));
- }
-};
-
-template <typename R, typename A1, typename A2, typename A3, typename A4,
- typename A5>
-class InvokeHelper<R, ::testing::tuple<A1, A2, A3, A4, A5> > {
- public:
- template <typename Function>
- static R Invoke(Function function, const ::testing::tuple<A1, A2, A3, A4,
- A5>& args) {
- return function(get<0>(args), get<1>(args), get<2>(args),
- get<3>(args), get<4>(args));
- }
-
- template <class Class, typename MethodPtr>
- static R InvokeMethod(Class* obj_ptr,
- MethodPtr method_ptr,
- const ::testing::tuple<A1, A2, A3, A4, A5>& args) {
- return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args),
- get<2>(args), get<3>(args), get<4>(args));
- }
-};
-
-template <typename R, typename A1, typename A2, typename A3, typename A4,
- typename A5, typename A6>
-class InvokeHelper<R, ::testing::tuple<A1, A2, A3, A4, A5, A6> > {
- public:
- template <typename Function>
- static R Invoke(Function function, const ::testing::tuple<A1, A2, A3, A4, A5,
- A6>& args) {
- return function(get<0>(args), get<1>(args), get<2>(args),
- get<3>(args), get<4>(args), get<5>(args));
- }
-
- template <class Class, typename MethodPtr>
- static R InvokeMethod(Class* obj_ptr,
- MethodPtr method_ptr,
- const ::testing::tuple<A1, A2, A3, A4, A5, A6>& args) {
- return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args),
- get<2>(args), get<3>(args), get<4>(args), get<5>(args));
- }
-};
-
-template <typename R, typename A1, typename A2, typename A3, typename A4,
- typename A5, typename A6, typename A7>
-class InvokeHelper<R, ::testing::tuple<A1, A2, A3, A4, A5, A6, A7> > {
- public:
- template <typename Function>
- static R Invoke(Function function, const ::testing::tuple<A1, A2, A3, A4, A5,
- A6, A7>& args) {
- return function(get<0>(args), get<1>(args), get<2>(args),
- get<3>(args), get<4>(args), get<5>(args), get<6>(args));
- }
-
- template <class Class, typename MethodPtr>
- static R InvokeMethod(Class* obj_ptr,
- MethodPtr method_ptr,
- const ::testing::tuple<A1, A2, A3, A4, A5, A6,
- A7>& args) {
- return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args),
- get<2>(args), get<3>(args), get<4>(args), get<5>(args),
- get<6>(args));
- }
-};
-
-template <typename R, typename A1, typename A2, typename A3, typename A4,
- typename A5, typename A6, typename A7, typename A8>
-class InvokeHelper<R, ::testing::tuple<A1, A2, A3, A4, A5, A6, A7, A8> > {
- public:
- template <typename Function>
- static R Invoke(Function function, const ::testing::tuple<A1, A2, A3, A4, A5,
- A6, A7, A8>& args) {
- return function(get<0>(args), get<1>(args), get<2>(args),
- get<3>(args), get<4>(args), get<5>(args), get<6>(args),
- get<7>(args));
- }
-
- template <class Class, typename MethodPtr>
- static R InvokeMethod(Class* obj_ptr,
- MethodPtr method_ptr,
- const ::testing::tuple<A1, A2, A3, A4, A5, A6, A7,
- A8>& args) {
- return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args),
- get<2>(args), get<3>(args), get<4>(args), get<5>(args),
- get<6>(args), get<7>(args));
- }
-};
-
-template <typename R, typename A1, typename A2, typename A3, typename A4,
- typename A5, typename A6, typename A7, typename A8, typename A9>
-class InvokeHelper<R, ::testing::tuple<A1, A2, A3, A4, A5, A6, A7, A8, A9> > {
- public:
- template <typename Function>
- static R Invoke(Function function, const ::testing::tuple<A1, A2, A3, A4, A5,
- A6, A7, A8, A9>& args) {
- return function(get<0>(args), get<1>(args), get<2>(args),
- get<3>(args), get<4>(args), get<5>(args), get<6>(args),
- get<7>(args), get<8>(args));
- }
-
- template <class Class, typename MethodPtr>
- static R InvokeMethod(Class* obj_ptr,
- MethodPtr method_ptr,
- const ::testing::tuple<A1, A2, A3, A4, A5, A6, A7, A8,
- A9>& args) {
- return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args),
- get<2>(args), get<3>(args), get<4>(args), get<5>(args),
- get<6>(args), get<7>(args), get<8>(args));
- }
-};
-
-template <typename R, typename A1, typename A2, typename A3, typename A4,
- typename A5, typename A6, typename A7, typename A8, typename A9,
- typename A10>
-class InvokeHelper<R, ::testing::tuple<A1, A2, A3, A4, A5, A6, A7, A8, A9,
- A10> > {
- public:
- template <typename Function>
- static R Invoke(Function function, const ::testing::tuple<A1, A2, A3, A4, A5,
- A6, A7, A8, A9, A10>& args) {
- return function(get<0>(args), get<1>(args), get<2>(args),
- get<3>(args), get<4>(args), get<5>(args), get<6>(args),
- get<7>(args), get<8>(args), get<9>(args));
- }
-
- template <class Class, typename MethodPtr>
- static R InvokeMethod(Class* obj_ptr,
- MethodPtr method_ptr,
- const ::testing::tuple<A1, A2, A3, A4, A5, A6, A7, A8,
- A9, A10>& args) {
- return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args),
- get<2>(args), get<3>(args), get<4>(args), get<5>(args),
- get<6>(args), get<7>(args), get<8>(args), get<9>(args));
- }
-};
-
-// An INTERNAL macro for extracting the type of a tuple field. It's
-// subject to change without notice - DO NOT USE IN USER CODE!
-#define GMOCK_FIELD_(Tuple, N) \
- typename ::testing::tuple_element<N, Tuple>::type
-
-// SelectArgs<Result, ArgumentTuple, k1, k2, ..., k_n>::type is the
-// type of an n-ary function whose i-th (1-based) argument type is the
-// k{i}-th (0-based) field of ArgumentTuple, which must be a tuple
-// type, and whose return type is Result. For example,
-// SelectArgs<int, ::testing::tuple<bool, char, double, long>, 0, 3>::type
-// is int(bool, long).
-//
-// SelectArgs<Result, ArgumentTuple, k1, k2, ..., k_n>::Select(args)
-// returns the selected fields (k1, k2, ..., k_n) of args as a tuple.
-// For example,
-// SelectArgs<int, tuple<bool, char, double>, 2, 0>::Select(
-// ::testing::make_tuple(true, 'a', 2.5))
-// returns tuple (2.5, true).
-//
-// The numbers in list k1, k2, ..., k_n must be >= 0, where n can be
-// in the range [0, 10]. Duplicates are allowed and they don't have
-// to be in an ascending or descending order.
-
-template <typename Result, typename ArgumentTuple, int k1, int k2, int k3,
- int k4, int k5, int k6, int k7, int k8, int k9, int k10>
-class SelectArgs {
- public:
- typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1),
- GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3),
- GMOCK_FIELD_(ArgumentTuple, k4), GMOCK_FIELD_(ArgumentTuple, k5),
- GMOCK_FIELD_(ArgumentTuple, k6), GMOCK_FIELD_(ArgumentTuple, k7),
- GMOCK_FIELD_(ArgumentTuple, k8), GMOCK_FIELD_(ArgumentTuple, k9),
- GMOCK_FIELD_(ArgumentTuple, k10));
- typedef typename Function<type>::ArgumentTuple SelectedArgs;
- static SelectedArgs Select(const ArgumentTuple& args) {
- return SelectedArgs(get<k1>(args), get<k2>(args), get<k3>(args),
- get<k4>(args), get<k5>(args), get<k6>(args), get<k7>(args),
- get<k8>(args), get<k9>(args), get<k10>(args));
- }
-};
-
-template <typename Result, typename ArgumentTuple>
-class SelectArgs<Result, ArgumentTuple,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1> {
- public:
- typedef Result type();
- typedef typename Function<type>::ArgumentTuple SelectedArgs;
- static SelectedArgs Select(const ArgumentTuple& /* args */) {
- return SelectedArgs();
- }
-};
-
-template <typename Result, typename ArgumentTuple, int k1>
-class SelectArgs<Result, ArgumentTuple,
- k1, -1, -1, -1, -1, -1, -1, -1, -1, -1> {
- public:
- typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1));
- typedef typename Function<type>::ArgumentTuple SelectedArgs;
- static SelectedArgs Select(const ArgumentTuple& args) {
- return SelectedArgs(get<k1>(args));
- }
-};
-
-template <typename Result, typename ArgumentTuple, int k1, int k2>
-class SelectArgs<Result, ArgumentTuple,
- k1, k2, -1, -1, -1, -1, -1, -1, -1, -1> {
- public:
- typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1),
- GMOCK_FIELD_(ArgumentTuple, k2));
- typedef typename Function<type>::ArgumentTuple SelectedArgs;
- static SelectedArgs Select(const ArgumentTuple& args) {
- return SelectedArgs(get<k1>(args), get<k2>(args));
- }
-};
-
-template <typename Result, typename ArgumentTuple, int k1, int k2, int k3>
-class SelectArgs<Result, ArgumentTuple,
- k1, k2, k3, -1, -1, -1, -1, -1, -1, -1> {
- public:
- typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1),
- GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3));
- typedef typename Function<type>::ArgumentTuple SelectedArgs;
- static SelectedArgs Select(const ArgumentTuple& args) {
- return SelectedArgs(get<k1>(args), get<k2>(args), get<k3>(args));
- }
-};
-
-template <typename Result, typename ArgumentTuple, int k1, int k2, int k3,
- int k4>
-class SelectArgs<Result, ArgumentTuple,
- k1, k2, k3, k4, -1, -1, -1, -1, -1, -1> {
- public:
- typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1),
- GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3),
- GMOCK_FIELD_(ArgumentTuple, k4));
- typedef typename Function<type>::ArgumentTuple SelectedArgs;
- static SelectedArgs Select(const ArgumentTuple& args) {
- return SelectedArgs(get<k1>(args), get<k2>(args), get<k3>(args),
- get<k4>(args));
- }
-};
-
-template <typename Result, typename ArgumentTuple, int k1, int k2, int k3,
- int k4, int k5>
-class SelectArgs<Result, ArgumentTuple,
- k1, k2, k3, k4, k5, -1, -1, -1, -1, -1> {
- public:
- typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1),
- GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3),
- GMOCK_FIELD_(ArgumentTuple, k4), GMOCK_FIELD_(ArgumentTuple, k5));
- typedef typename Function<type>::ArgumentTuple SelectedArgs;
- static SelectedArgs Select(const ArgumentTuple& args) {
- return SelectedArgs(get<k1>(args), get<k2>(args), get<k3>(args),
- get<k4>(args), get<k5>(args));
- }
-};
-
-template <typename Result, typename ArgumentTuple, int k1, int k2, int k3,
- int k4, int k5, int k6>
-class SelectArgs<Result, ArgumentTuple,
- k1, k2, k3, k4, k5, k6, -1, -1, -1, -1> {
- public:
- typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1),
- GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3),
- GMOCK_FIELD_(ArgumentTuple, k4), GMOCK_FIELD_(ArgumentTuple, k5),
- GMOCK_FIELD_(ArgumentTuple, k6));
- typedef typename Function<type>::ArgumentTuple SelectedArgs;
- static SelectedArgs Select(const ArgumentTuple& args) {
- return SelectedArgs(get<k1>(args), get<k2>(args), get<k3>(args),
- get<k4>(args), get<k5>(args), get<k6>(args));
- }
-};
-
-template <typename Result, typename ArgumentTuple, int k1, int k2, int k3,
- int k4, int k5, int k6, int k7>
-class SelectArgs<Result, ArgumentTuple,
- k1, k2, k3, k4, k5, k6, k7, -1, -1, -1> {
- public:
- typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1),
- GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3),
- GMOCK_FIELD_(ArgumentTuple, k4), GMOCK_FIELD_(ArgumentTuple, k5),
- GMOCK_FIELD_(ArgumentTuple, k6), GMOCK_FIELD_(ArgumentTuple, k7));
- typedef typename Function<type>::ArgumentTuple SelectedArgs;
- static SelectedArgs Select(const ArgumentTuple& args) {
- return SelectedArgs(get<k1>(args), get<k2>(args), get<k3>(args),
- get<k4>(args), get<k5>(args), get<k6>(args), get<k7>(args));
- }
-};
-
-template <typename Result, typename ArgumentTuple, int k1, int k2, int k3,
- int k4, int k5, int k6, int k7, int k8>
-class SelectArgs<Result, ArgumentTuple,
- k1, k2, k3, k4, k5, k6, k7, k8, -1, -1> {
- public:
- typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1),
- GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3),
- GMOCK_FIELD_(ArgumentTuple, k4), GMOCK_FIELD_(ArgumentTuple, k5),
- GMOCK_FIELD_(ArgumentTuple, k6), GMOCK_FIELD_(ArgumentTuple, k7),
- GMOCK_FIELD_(ArgumentTuple, k8));
- typedef typename Function<type>::ArgumentTuple SelectedArgs;
- static SelectedArgs Select(const ArgumentTuple& args) {
- return SelectedArgs(get<k1>(args), get<k2>(args), get<k3>(args),
- get<k4>(args), get<k5>(args), get<k6>(args), get<k7>(args),
- get<k8>(args));
- }
-};
-
-template <typename Result, typename ArgumentTuple, int k1, int k2, int k3,
- int k4, int k5, int k6, int k7, int k8, int k9>
-class SelectArgs<Result, ArgumentTuple,
- k1, k2, k3, k4, k5, k6, k7, k8, k9, -1> {
- public:
- typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1),
- GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3),
- GMOCK_FIELD_(ArgumentTuple, k4), GMOCK_FIELD_(ArgumentTuple, k5),
- GMOCK_FIELD_(ArgumentTuple, k6), GMOCK_FIELD_(ArgumentTuple, k7),
- GMOCK_FIELD_(ArgumentTuple, k8), GMOCK_FIELD_(ArgumentTuple, k9));
- typedef typename Function<type>::ArgumentTuple SelectedArgs;
- static SelectedArgs Select(const ArgumentTuple& args) {
- return SelectedArgs(get<k1>(args), get<k2>(args), get<k3>(args),
- get<k4>(args), get<k5>(args), get<k6>(args), get<k7>(args),
- get<k8>(args), get<k9>(args));
- }
-};
-
-#undef GMOCK_FIELD_
-
-// Implements the WithArgs action.
-template <typename InnerAction, int k1 = -1, int k2 = -1, int k3 = -1,
- int k4 = -1, int k5 = -1, int k6 = -1, int k7 = -1, int k8 = -1,
- int k9 = -1, int k10 = -1>
-class WithArgsAction {
- public:
- explicit WithArgsAction(const InnerAction& action) : action_(action) {}
-
- template <typename F>
- operator Action<F>() const { return MakeAction(new Impl<F>(action_)); }
-
- private:
- template <typename F>
- class Impl : public ActionInterface<F> {
- public:
- typedef typename Function<F>::Result Result;
- typedef typename Function<F>::ArgumentTuple ArgumentTuple;
-
- explicit Impl(const InnerAction& action) : action_(action) {}
-
- virtual Result Perform(const ArgumentTuple& args) {
- return action_.Perform(SelectArgs<Result, ArgumentTuple, k1, k2, k3, k4,
- k5, k6, k7, k8, k9, k10>::Select(args));
- }
-
- private:
- typedef typename SelectArgs<Result, ArgumentTuple,
- k1, k2, k3, k4, k5, k6, k7, k8, k9, k10>::type InnerFunctionType;
-
- Action<InnerFunctionType> action_;
- };
-
- const InnerAction action_;
-
- GTEST_DISALLOW_ASSIGN_(WithArgsAction);
-};
-
// A macro from the ACTION* family (defined later in this file)
// defines an action that can be used in a mock function. Typically,
// these actions only care about a subset of the arguments of the mock
@@ -511,7 +70,7 @@ struct ExcessiveArg {};
template <typename Result, class Impl>
class ActionHelper {
public:
- static Result Perform(Impl* impl, const ::testing::tuple<>& args) {
+ static Result Perform(Impl* impl, const ::std::tuple<>& args) {
return impl->template gmock_PerformImpl<>(args, ExcessiveArg(),
ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(),
ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(),
@@ -519,266 +78,100 @@ class ActionHelper {
}
template <typename A0>
- static Result Perform(Impl* impl, const ::testing::tuple<A0>& args) {
- return impl->template gmock_PerformImpl<A0>(args, get<0>(args),
+ static Result Perform(Impl* impl, const ::std::tuple<A0>& args) {
+ return impl->template gmock_PerformImpl<A0>(args, std::get<0>(args),
ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(),
ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(),
ExcessiveArg());
}
template <typename A0, typename A1>
- static Result Perform(Impl* impl, const ::testing::tuple<A0, A1>& args) {
- return impl->template gmock_PerformImpl<A0, A1>(args, get<0>(args),
- get<1>(args), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(),
+ static Result Perform(Impl* impl, const ::std::tuple<A0, A1>& args) {
+ return impl->template gmock_PerformImpl<A0, A1>(args, std::get<0>(args),
+ std::get<1>(args), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(),
ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(),
ExcessiveArg());
}
template <typename A0, typename A1, typename A2>
- static Result Perform(Impl* impl, const ::testing::tuple<A0, A1, A2>& args) {
- return impl->template gmock_PerformImpl<A0, A1, A2>(args, get<0>(args),
- get<1>(args), get<2>(args), ExcessiveArg(), ExcessiveArg(),
+ static Result Perform(Impl* impl, const ::std::tuple<A0, A1, A2>& args) {
+ return impl->template gmock_PerformImpl<A0, A1, A2>(args,
+ std::get<0>(args), std::get<1>(args), std::get<2>(args),
ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(),
- ExcessiveArg());
+ ExcessiveArg(), ExcessiveArg(), ExcessiveArg());
}
template <typename A0, typename A1, typename A2, typename A3>
- static Result Perform(Impl* impl, const ::testing::tuple<A0, A1, A2,
- A3>& args) {
- return impl->template gmock_PerformImpl<A0, A1, A2, A3>(args, get<0>(args),
- get<1>(args), get<2>(args), get<3>(args), ExcessiveArg(),
- ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(),
- ExcessiveArg());
+ static Result Perform(Impl* impl, const ::std::tuple<A0, A1, A2, A3>& args) {
+ return impl->template gmock_PerformImpl<A0, A1, A2, A3>(args,
+ std::get<0>(args), std::get<1>(args), std::get<2>(args),
+ std::get<3>(args), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(),
+ ExcessiveArg(), ExcessiveArg(), ExcessiveArg());
}
template <typename A0, typename A1, typename A2, typename A3, typename A4>
- static Result Perform(Impl* impl, const ::testing::tuple<A0, A1, A2, A3,
+ static Result Perform(Impl* impl, const ::std::tuple<A0, A1, A2, A3,
A4>& args) {
return impl->template gmock_PerformImpl<A0, A1, A2, A3, A4>(args,
- get<0>(args), get<1>(args), get<2>(args), get<3>(args), get<4>(args),
- ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(),
- ExcessiveArg());
+ std::get<0>(args), std::get<1>(args), std::get<2>(args),
+ std::get<3>(args), std::get<4>(args), ExcessiveArg(), ExcessiveArg(),
+ ExcessiveArg(), ExcessiveArg(), ExcessiveArg());
}
template <typename A0, typename A1, typename A2, typename A3, typename A4,
typename A5>
- static Result Perform(Impl* impl, const ::testing::tuple<A0, A1, A2, A3, A4,
+ static Result Perform(Impl* impl, const ::std::tuple<A0, A1, A2, A3, A4,
A5>& args) {
return impl->template gmock_PerformImpl<A0, A1, A2, A3, A4, A5>(args,
- get<0>(args), get<1>(args), get<2>(args), get<3>(args), get<4>(args),
- get<5>(args), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(),
- ExcessiveArg());
+ std::get<0>(args), std::get<1>(args), std::get<2>(args),
+ std::get<3>(args), std::get<4>(args), std::get<5>(args),
+ ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg());
}
template <typename A0, typename A1, typename A2, typename A3, typename A4,
typename A5, typename A6>
- static Result Perform(Impl* impl, const ::testing::tuple<A0, A1, A2, A3, A4,
- A5, A6>& args) {
+ static Result Perform(Impl* impl, const ::std::tuple<A0, A1, A2, A3, A4, A5,
+ A6>& args) {
return impl->template gmock_PerformImpl<A0, A1, A2, A3, A4, A5, A6>(args,
- get<0>(args), get<1>(args), get<2>(args), get<3>(args), get<4>(args),
- get<5>(args), get<6>(args), ExcessiveArg(), ExcessiveArg(),
- ExcessiveArg());
+ std::get<0>(args), std::get<1>(args), std::get<2>(args),
+ std::get<3>(args), std::get<4>(args), std::get<5>(args),
+ std::get<6>(args), ExcessiveArg(), ExcessiveArg(), ExcessiveArg());
}
template <typename A0, typename A1, typename A2, typename A3, typename A4,
typename A5, typename A6, typename A7>
- static Result Perform(Impl* impl, const ::testing::tuple<A0, A1, A2, A3, A4,
- A5, A6, A7>& args) {
+ static Result Perform(Impl* impl, const ::std::tuple<A0, A1, A2, A3, A4, A5,
+ A6, A7>& args) {
return impl->template gmock_PerformImpl<A0, A1, A2, A3, A4, A5, A6,
- A7>(args, get<0>(args), get<1>(args), get<2>(args), get<3>(args),
- get<4>(args), get<5>(args), get<6>(args), get<7>(args), ExcessiveArg(),
- ExcessiveArg());
+ A7>(args, std::get<0>(args), std::get<1>(args), std::get<2>(args),
+ std::get<3>(args), std::get<4>(args), std::get<5>(args),
+ std::get<6>(args), std::get<7>(args), ExcessiveArg(), ExcessiveArg());
}
template <typename A0, typename A1, typename A2, typename A3, typename A4,
typename A5, typename A6, typename A7, typename A8>
- static Result Perform(Impl* impl, const ::testing::tuple<A0, A1, A2, A3, A4,
- A5, A6, A7, A8>& args) {
+ static Result Perform(Impl* impl, const ::std::tuple<A0, A1, A2, A3, A4, A5,
+ A6, A7, A8>& args) {
return impl->template gmock_PerformImpl<A0, A1, A2, A3, A4, A5, A6, A7,
- A8>(args, get<0>(args), get<1>(args), get<2>(args), get<3>(args),
- get<4>(args), get<5>(args), get<6>(args), get<7>(args), get<8>(args),
+ A8>(args, std::get<0>(args), std::get<1>(args), std::get<2>(args),
+ std::get<3>(args), std::get<4>(args), std::get<5>(args),
+ std::get<6>(args), std::get<7>(args), std::get<8>(args),
ExcessiveArg());
}
template <typename A0, typename A1, typename A2, typename A3, typename A4,
typename A5, typename A6, typename A7, typename A8, typename A9>
- static Result Perform(Impl* impl, const ::testing::tuple<A0, A1, A2, A3, A4,
- A5, A6, A7, A8, A9>& args) {
+ static Result Perform(Impl* impl, const ::std::tuple<A0, A1, A2, A3, A4, A5,
+ A6, A7, A8, A9>& args) {
return impl->template gmock_PerformImpl<A0, A1, A2, A3, A4, A5, A6, A7, A8,
- A9>(args, get<0>(args), get<1>(args), get<2>(args), get<3>(args),
- get<4>(args), get<5>(args), get<6>(args), get<7>(args), get<8>(args),
- get<9>(args));
+ A9>(args, std::get<0>(args), std::get<1>(args), std::get<2>(args),
+ std::get<3>(args), std::get<4>(args), std::get<5>(args),
+ std::get<6>(args), std::get<7>(args), std::get<8>(args),
+ std::get<9>(args));
}
};
} // namespace internal
-
-// Various overloads for Invoke().
-
-// WithArgs<N1, N2, ..., Nk>(an_action) creates an action that passes
-// the selected arguments of the mock function to an_action and
-// performs it. It serves as an adaptor between actions with
-// different argument lists. C++ doesn't support default arguments for
-// function templates, so we have to overload it.
-template <int k1, typename InnerAction>
-inline internal::WithArgsAction<InnerAction, k1>
-WithArgs(const InnerAction& action) {
- return internal::WithArgsAction<InnerAction, k1>(action);
-}
-
-template <int k1, int k2, typename InnerAction>
-inline internal::WithArgsAction<InnerAction, k1, k2>
-WithArgs(const InnerAction& action) {
- return internal::WithArgsAction<InnerAction, k1, k2>(action);
-}
-
-template <int k1, int k2, int k3, typename InnerAction>
-inline internal::WithArgsAction<InnerAction, k1, k2, k3>
-WithArgs(const InnerAction& action) {
- return internal::WithArgsAction<InnerAction, k1, k2, k3>(action);
-}
-
-template <int k1, int k2, int k3, int k4, typename InnerAction>
-inline internal::WithArgsAction<InnerAction, k1, k2, k3, k4>
-WithArgs(const InnerAction& action) {
- return internal::WithArgsAction<InnerAction, k1, k2, k3, k4>(action);
-}
-
-template <int k1, int k2, int k3, int k4, int k5, typename InnerAction>
-inline internal::WithArgsAction<InnerAction, k1, k2, k3, k4, k5>
-WithArgs(const InnerAction& action) {
- return internal::WithArgsAction<InnerAction, k1, k2, k3, k4, k5>(action);
-}
-
-template <int k1, int k2, int k3, int k4, int k5, int k6, typename InnerAction>
-inline internal::WithArgsAction<InnerAction, k1, k2, k3, k4, k5, k6>
-WithArgs(const InnerAction& action) {
- return internal::WithArgsAction<InnerAction, k1, k2, k3, k4, k5, k6>(action);
-}
-
-template <int k1, int k2, int k3, int k4, int k5, int k6, int k7,
- typename InnerAction>
-inline internal::WithArgsAction<InnerAction, k1, k2, k3, k4, k5, k6, k7>
-WithArgs(const InnerAction& action) {
- return internal::WithArgsAction<InnerAction, k1, k2, k3, k4, k5, k6,
- k7>(action);
-}
-
-template <int k1, int k2, int k3, int k4, int k5, int k6, int k7, int k8,
- typename InnerAction>
-inline internal::WithArgsAction<InnerAction, k1, k2, k3, k4, k5, k6, k7, k8>
-WithArgs(const InnerAction& action) {
- return internal::WithArgsAction<InnerAction, k1, k2, k3, k4, k5, k6, k7,
- k8>(action);
-}
-
-template <int k1, int k2, int k3, int k4, int k5, int k6, int k7, int k8,
- int k9, typename InnerAction>
-inline internal::WithArgsAction<InnerAction, k1, k2, k3, k4, k5, k6, k7, k8, k9>
-WithArgs(const InnerAction& action) {
- return internal::WithArgsAction<InnerAction, k1, k2, k3, k4, k5, k6, k7, k8,
- k9>(action);
-}
-
-template <int k1, int k2, int k3, int k4, int k5, int k6, int k7, int k8,
- int k9, int k10, typename InnerAction>
-inline internal::WithArgsAction<InnerAction, k1, k2, k3, k4, k5, k6, k7, k8,
- k9, k10>
-WithArgs(const InnerAction& action) {
- return internal::WithArgsAction<InnerAction, k1, k2, k3, k4, k5, k6, k7, k8,
- k9, k10>(action);
-}
-
-// Creates an action that does actions a1, a2, ..., sequentially in
-// each invocation.
-template <typename Action1, typename Action2>
-inline internal::DoBothAction<Action1, Action2>
-DoAll(Action1 a1, Action2 a2) {
- return internal::DoBothAction<Action1, Action2>(a1, a2);
-}
-
-template <typename Action1, typename Action2, typename Action3>
-inline internal::DoBothAction<Action1, internal::DoBothAction<Action2,
- Action3> >
-DoAll(Action1 a1, Action2 a2, Action3 a3) {
- return DoAll(a1, DoAll(a2, a3));
-}
-
-template <typename Action1, typename Action2, typename Action3,
- typename Action4>
-inline internal::DoBothAction<Action1, internal::DoBothAction<Action2,
- internal::DoBothAction<Action3, Action4> > >
-DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4) {
- return DoAll(a1, DoAll(a2, a3, a4));
-}
-
-template <typename Action1, typename Action2, typename Action3,
- typename Action4, typename Action5>
-inline internal::DoBothAction<Action1, internal::DoBothAction<Action2,
- internal::DoBothAction<Action3, internal::DoBothAction<Action4,
- Action5> > > >
-DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5) {
- return DoAll(a1, DoAll(a2, a3, a4, a5));
-}
-
-template <typename Action1, typename Action2, typename Action3,
- typename Action4, typename Action5, typename Action6>
-inline internal::DoBothAction<Action1, internal::DoBothAction<Action2,
- internal::DoBothAction<Action3, internal::DoBothAction<Action4,
- internal::DoBothAction<Action5, Action6> > > > >
-DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6) {
- return DoAll(a1, DoAll(a2, a3, a4, a5, a6));
-}
-
-template <typename Action1, typename Action2, typename Action3,
- typename Action4, typename Action5, typename Action6, typename Action7>
-inline internal::DoBothAction<Action1, internal::DoBothAction<Action2,
- internal::DoBothAction<Action3, internal::DoBothAction<Action4,
- internal::DoBothAction<Action5, internal::DoBothAction<Action6,
- Action7> > > > > >
-DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6,
- Action7 a7) {
- return DoAll(a1, DoAll(a2, a3, a4, a5, a6, a7));
-}
-
-template <typename Action1, typename Action2, typename Action3,
- typename Action4, typename Action5, typename Action6, typename Action7,
- typename Action8>
-inline internal::DoBothAction<Action1, internal::DoBothAction<Action2,
- internal::DoBothAction<Action3, internal::DoBothAction<Action4,
- internal::DoBothAction<Action5, internal::DoBothAction<Action6,
- internal::DoBothAction<Action7, Action8> > > > > > >
-DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6,
- Action7 a7, Action8 a8) {
- return DoAll(a1, DoAll(a2, a3, a4, a5, a6, a7, a8));
-}
-
-template <typename Action1, typename Action2, typename Action3,
- typename Action4, typename Action5, typename Action6, typename Action7,
- typename Action8, typename Action9>
-inline internal::DoBothAction<Action1, internal::DoBothAction<Action2,
- internal::DoBothAction<Action3, internal::DoBothAction<Action4,
- internal::DoBothAction<Action5, internal::DoBothAction<Action6,
- internal::DoBothAction<Action7, internal::DoBothAction<Action8,
- Action9> > > > > > > >
-DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6,
- Action7 a7, Action8 a8, Action9 a9) {
- return DoAll(a1, DoAll(a2, a3, a4, a5, a6, a7, a8, a9));
-}
-
-template <typename Action1, typename Action2, typename Action3,
- typename Action4, typename Action5, typename Action6, typename Action7,
- typename Action8, typename Action9, typename Action10>
-inline internal::DoBothAction<Action1, internal::DoBothAction<Action2,
- internal::DoBothAction<Action3, internal::DoBothAction<Action4,
- internal::DoBothAction<Action5, internal::DoBothAction<Action6,
- internal::DoBothAction<Action7, internal::DoBothAction<Action8,
- internal::DoBothAction<Action9, Action10> > > > > > > > >
-DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6,
- Action7 a7, Action8 a8, Action9 a9, Action10 a10) {
- return DoAll(a1, DoAll(a2, a3, a4, a5, a6, a7, a8, a9, a10));
-}
-
} // namespace testing
// The ACTION* family of macros can be used in a namespace scope to
@@ -866,30 +259,29 @@ DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6,
//
// CAVEAT:
//
-// ACTION*() can only be used in a namespace scope. The reason is
-// that C++ doesn't yet allow function-local types to be used to
-// instantiate templates. The up-coming C++0x standard will fix this.
-// Once that's done, we'll consider supporting using ACTION*() inside
-// a function.
+// ACTION*() can only be used in a namespace scope as templates cannot be
+// declared inside of a local class.
+// Users can, however, define any local functors (e.g. a lambda) that
+// can be used as actions.
//
// MORE INFORMATION:
//
-// To learn more about using these macros, please search for 'ACTION'
-// on http://code.google.com/p/googlemock/wiki/CookBook.
+// To learn more about using these macros, please search for 'ACTION' on
+// https://github.com/google/googletest/blob/master/googlemock/docs/cook_book.md
// An internal macro needed for implementing ACTION*().
#define GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_\
const args_type& args GTEST_ATTRIBUTE_UNUSED_, \
- arg0_type arg0 GTEST_ATTRIBUTE_UNUSED_, \
- arg1_type arg1 GTEST_ATTRIBUTE_UNUSED_, \
- arg2_type arg2 GTEST_ATTRIBUTE_UNUSED_, \
- arg3_type arg3 GTEST_ATTRIBUTE_UNUSED_, \
- arg4_type arg4 GTEST_ATTRIBUTE_UNUSED_, \
- arg5_type arg5 GTEST_ATTRIBUTE_UNUSED_, \
- arg6_type arg6 GTEST_ATTRIBUTE_UNUSED_, \
- arg7_type arg7 GTEST_ATTRIBUTE_UNUSED_, \
- arg8_type arg8 GTEST_ATTRIBUTE_UNUSED_, \
- arg9_type arg9 GTEST_ATTRIBUTE_UNUSED_
+ const arg0_type& arg0 GTEST_ATTRIBUTE_UNUSED_, \
+ const arg1_type& arg1 GTEST_ATTRIBUTE_UNUSED_, \
+ const arg2_type& arg2 GTEST_ATTRIBUTE_UNUSED_, \
+ const arg3_type& arg3 GTEST_ATTRIBUTE_UNUSED_, \
+ const arg4_type& arg4 GTEST_ATTRIBUTE_UNUSED_, \
+ const arg5_type& arg5 GTEST_ATTRIBUTE_UNUSED_, \
+ const arg6_type& arg6 GTEST_ATTRIBUTE_UNUSED_, \
+ const arg7_type& arg7 GTEST_ATTRIBUTE_UNUSED_, \
+ const arg8_type& arg8 GTEST_ATTRIBUTE_UNUSED_, \
+ const arg9_type& arg9 GTEST_ATTRIBUTE_UNUSED_
// Sometimes you want to give an action explicit template parameters
// that cannot be inferred from its value parameters. ACTION() and
@@ -915,7 +307,7 @@ DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6,
// ACTION_TEMPLATE(DuplicateArg,
// HAS_2_TEMPLATE_PARAMS(int, k, typename, T),
// AND_1_VALUE_PARAMS(output)) {
-// *output = T(::testing::get<k>(args));
+// *output = T(::std::get<k>(args));
// }
// ...
// int n;
@@ -1073,52 +465,67 @@ DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6,
#define GMOCK_INTERNAL_INIT_AND_0_VALUE_PARAMS()\
()
#define GMOCK_INTERNAL_INIT_AND_1_VALUE_PARAMS(p0)\
- (p0##_type gmock_p0) : p0(gmock_p0)
+ (p0##_type gmock_p0) : p0(::std::move(gmock_p0))
#define GMOCK_INTERNAL_INIT_AND_2_VALUE_PARAMS(p0, p1)\
- (p0##_type gmock_p0, p1##_type gmock_p1) : p0(gmock_p0), p1(gmock_p1)
+ (p0##_type gmock_p0, p1##_type gmock_p1) : p0(::std::move(gmock_p0)), \
+ p1(::std::move(gmock_p1))
#define GMOCK_INTERNAL_INIT_AND_3_VALUE_PARAMS(p0, p1, p2)\
(p0##_type gmock_p0, p1##_type gmock_p1, \
- p2##_type gmock_p2) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2)
+ p2##_type gmock_p2) : p0(::std::move(gmock_p0)), \
+ p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2))
#define GMOCK_INTERNAL_INIT_AND_4_VALUE_PARAMS(p0, p1, p2, p3)\
(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
- p3##_type gmock_p3) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
- p3(gmock_p3)
+ p3##_type gmock_p3) : p0(::std::move(gmock_p0)), \
+ p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \
+ p3(::std::move(gmock_p3))
#define GMOCK_INTERNAL_INIT_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4)\
(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
- p3##_type gmock_p3, p4##_type gmock_p4) : p0(gmock_p0), p1(gmock_p1), \
- p2(gmock_p2), p3(gmock_p3), p4(gmock_p4)
+ p3##_type gmock_p3, p4##_type gmock_p4) : p0(::std::move(gmock_p0)), \
+ p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \
+ p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4))
#define GMOCK_INTERNAL_INIT_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5)\
(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
p3##_type gmock_p3, p4##_type gmock_p4, \
- p5##_type gmock_p5) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
- p3(gmock_p3), p4(gmock_p4), p5(gmock_p5)
+ p5##_type gmock_p5) : p0(::std::move(gmock_p0)), \
+ p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \
+ p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)), \
+ p5(::std::move(gmock_p5))
#define GMOCK_INTERNAL_INIT_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6)\
(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
- p6##_type gmock_p6) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
- p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6)
+ p6##_type gmock_p6) : p0(::std::move(gmock_p0)), \
+ p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \
+ p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)), \
+ p5(::std::move(gmock_p5)), p6(::std::move(gmock_p6))
#define GMOCK_INTERNAL_INIT_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7)\
(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
- p6##_type gmock_p6, p7##_type gmock_p7) : p0(gmock_p0), p1(gmock_p1), \
- p2(gmock_p2), p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), \
- p7(gmock_p7)
+ p6##_type gmock_p6, p7##_type gmock_p7) : p0(::std::move(gmock_p0)), \
+ p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \
+ p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)), \
+ p5(::std::move(gmock_p5)), p6(::std::move(gmock_p6)), \
+ p7(::std::move(gmock_p7))
#define GMOCK_INTERNAL_INIT_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
p7, p8)\
(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
p6##_type gmock_p6, p7##_type gmock_p7, \
- p8##_type gmock_p8) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
- p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), p7(gmock_p7), \
- p8(gmock_p8)
+ p8##_type gmock_p8) : p0(::std::move(gmock_p0)), \
+ p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \
+ p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)), \
+ p5(::std::move(gmock_p5)), p6(::std::move(gmock_p6)), \
+ p7(::std::move(gmock_p7)), p8(::std::move(gmock_p8))
#define GMOCK_INTERNAL_INIT_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
p7, p8, p9)\
(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
p6##_type gmock_p6, p7##_type gmock_p7, p8##_type gmock_p8, \
- p9##_type gmock_p9) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
- p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), p7(gmock_p7), \
- p8(gmock_p8), p9(gmock_p9)
+ p9##_type gmock_p9) : p0(::std::move(gmock_p0)), \
+ p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \
+ p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)), \
+ p5(::std::move(gmock_p5)), p6(::std::move(gmock_p6)), \
+ p7(::std::move(gmock_p7)), p8(::std::move(gmock_p8)), \
+ p9(::std::move(gmock_p9))
// Declares the fields for storing the value parameters.
#define GMOCK_INTERNAL_DEFN_AND_0_VALUE_PARAMS()
@@ -1264,10 +671,12 @@ DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6,
typename arg3_type, typename arg4_type, typename arg5_type, \
typename arg6_type, typename arg7_type, typename arg8_type, \
typename arg9_type>\
- return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \
- arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \
- arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \
- arg9_type arg9) const;\
+ return_type gmock_PerformImpl(const args_type& args, \
+ const arg0_type& arg0, const arg1_type& arg1, \
+ const arg2_type& arg2, const arg3_type& arg3, \
+ const arg4_type& arg4, const arg5_type& arg5, \
+ const arg6_type& arg6, const arg7_type& arg7, \
+ const arg8_type& arg8, const arg9_type& arg9) const;\
GMOCK_INTERNAL_DEFN_##value_params\
private:\
GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
@@ -1325,10 +734,12 @@ DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6,
typename arg3_type, typename arg4_type, typename arg5_type, \
typename arg6_type, typename arg7_type, typename arg8_type, \
typename arg9_type>\
- return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \
- arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \
- arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \
- arg9_type arg9) const;\
+ return_type gmock_PerformImpl(const args_type& args, \
+ const arg0_type& arg0, const arg1_type& arg1, \
+ const arg2_type& arg2, const arg3_type& arg3, \
+ const arg4_type& arg4, const arg5_type& arg5, \
+ const arg6_type& arg6, const arg7_type& arg7, \
+ const arg8_type& arg8, const arg9_type& arg9) const;\
private:\
GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
};\
@@ -1354,7 +765,8 @@ DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6,
template <typename p0##_type>\
class name##ActionP {\
public:\
- explicit name##ActionP(p0##_type gmock_p0) : p0(gmock_p0) {}\
+ explicit name##ActionP(p0##_type gmock_p0) : \
+ p0(::std::forward<p0##_type>(gmock_p0)) {}\
template <typename F>\
class gmock_Impl : public ::testing::ActionInterface<F> {\
public:\
@@ -1362,7 +774,8 @@ DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6,
typedef typename ::testing::internal::Function<F>::Result return_type;\
typedef typename ::testing::internal::Function<F>::ArgumentTuple\
args_type;\
- explicit gmock_Impl(p0##_type gmock_p0) : p0(gmock_p0) {}\
+ explicit gmock_Impl(p0##_type gmock_p0) : \
+ p0(::std::forward<p0##_type>(gmock_p0)) {}\
virtual return_type Perform(const args_type& args) {\
return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
Perform(this, args);\
@@ -1371,10 +784,12 @@ DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6,
typename arg3_type, typename arg4_type, typename arg5_type, \
typename arg6_type, typename arg7_type, typename arg8_type, \
typename arg9_type>\
- return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \
- arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \
- arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \
- arg9_type arg9) const;\
+ return_type gmock_PerformImpl(const args_type& args, \
+ const arg0_type& arg0, const arg1_type& arg1, \
+ const arg2_type& arg2, const arg3_type& arg3, \
+ const arg4_type& arg4, const arg5_type& arg5, \
+ const arg6_type& arg6, const arg7_type& arg7, \
+ const arg8_type& arg8, const arg9_type& arg9) const;\
p0##_type p0;\
private:\
GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
@@ -1404,8 +819,9 @@ DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6,
template <typename p0##_type, typename p1##_type>\
class name##ActionP2 {\
public:\
- name##ActionP2(p0##_type gmock_p0, p1##_type gmock_p1) : p0(gmock_p0), \
- p1(gmock_p1) {}\
+ name##ActionP2(p0##_type gmock_p0, \
+ p1##_type gmock_p1) : p0(::std::forward<p0##_type>(gmock_p0)), \
+ p1(::std::forward<p1##_type>(gmock_p1)) {}\
template <typename F>\
class gmock_Impl : public ::testing::ActionInterface<F> {\
public:\
@@ -1413,8 +829,9 @@ DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6,
typedef typename ::testing::internal::Function<F>::Result return_type;\
typedef typename ::testing::internal::Function<F>::ArgumentTuple\
args_type;\
- gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1) : p0(gmock_p0), \
- p1(gmock_p1) {}\
+ gmock_Impl(p0##_type gmock_p0, \
+ p1##_type gmock_p1) : p0(::std::forward<p0##_type>(gmock_p0)), \
+ p1(::std::forward<p1##_type>(gmock_p1)) {}\
virtual return_type Perform(const args_type& args) {\
return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
Perform(this, args);\
@@ -1423,10 +840,12 @@ DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6,
typename arg3_type, typename arg4_type, typename arg5_type, \
typename arg6_type, typename arg7_type, typename arg8_type, \
typename arg9_type>\
- return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \
- arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \
- arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \
- arg9_type arg9) const;\
+ return_type gmock_PerformImpl(const args_type& args, \
+ const arg0_type& arg0, const arg1_type& arg1, \
+ const arg2_type& arg2, const arg3_type& arg3, \
+ const arg4_type& arg4, const arg5_type& arg5, \
+ const arg6_type& arg6, const arg7_type& arg7, \
+ const arg8_type& arg8, const arg9_type& arg9) const;\
p0##_type p0;\
p1##_type p1;\
private:\
@@ -1460,7 +879,9 @@ DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6,
class name##ActionP3 {\
public:\
name##ActionP3(p0##_type gmock_p0, p1##_type gmock_p1, \
- p2##_type gmock_p2) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2) {}\
+ p2##_type gmock_p2) : p0(::std::forward<p0##_type>(gmock_p0)), \
+ p1(::std::forward<p1##_type>(gmock_p1)), \
+ p2(::std::forward<p2##_type>(gmock_p2)) {}\
template <typename F>\
class gmock_Impl : public ::testing::ActionInterface<F> {\
public:\
@@ -1469,7 +890,9 @@ DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6,
typedef typename ::testing::internal::Function<F>::ArgumentTuple\
args_type;\
gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, \
- p2##_type gmock_p2) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2) {}\
+ p2##_type gmock_p2) : p0(::std::forward<p0##_type>(gmock_p0)), \
+ p1(::std::forward<p1##_type>(gmock_p1)), \
+ p2(::std::forward<p2##_type>(gmock_p2)) {}\
virtual return_type Perform(const args_type& args) {\
return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
Perform(this, args);\
@@ -1478,10 +901,12 @@ DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6,
typename arg3_type, typename arg4_type, typename arg5_type, \
typename arg6_type, typename arg7_type, typename arg8_type, \
typename arg9_type>\
- return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \
- arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \
- arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \
- arg9_type arg9) const;\
+ return_type gmock_PerformImpl(const args_type& args, \
+ const arg0_type& arg0, const arg1_type& arg1, \
+ const arg2_type& arg2, const arg3_type& arg3, \
+ const arg4_type& arg4, const arg5_type& arg5, \
+ const arg6_type& arg6, const arg7_type& arg7, \
+ const arg8_type& arg8, const arg9_type& arg9) const;\
p0##_type p0;\
p1##_type p1;\
p2##_type p2;\
@@ -1519,8 +944,11 @@ DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6,
class name##ActionP4 {\
public:\
name##ActionP4(p0##_type gmock_p0, p1##_type gmock_p1, \
- p2##_type gmock_p2, p3##_type gmock_p3) : p0(gmock_p0), p1(gmock_p1), \
- p2(gmock_p2), p3(gmock_p3) {}\
+ p2##_type gmock_p2, \
+ p3##_type gmock_p3) : p0(::std::forward<p0##_type>(gmock_p0)), \
+ p1(::std::forward<p1##_type>(gmock_p1)), \
+ p2(::std::forward<p2##_type>(gmock_p2)), \
+ p3(::std::forward<p3##_type>(gmock_p3)) {}\
template <typename F>\
class gmock_Impl : public ::testing::ActionInterface<F> {\
public:\
@@ -1529,8 +957,10 @@ DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6,
typedef typename ::testing::internal::Function<F>::ArgumentTuple\
args_type;\
gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
- p3##_type gmock_p3) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
- p3(gmock_p3) {}\
+ p3##_type gmock_p3) : p0(::std::forward<p0##_type>(gmock_p0)), \
+ p1(::std::forward<p1##_type>(gmock_p1)), \
+ p2(::std::forward<p2##_type>(gmock_p2)), \
+ p3(::std::forward<p3##_type>(gmock_p3)) {}\
virtual return_type Perform(const args_type& args) {\
return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
Perform(this, args);\
@@ -1539,10 +969,12 @@ DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6,
typename arg3_type, typename arg4_type, typename arg5_type, \
typename arg6_type, typename arg7_type, typename arg8_type, \
typename arg9_type>\
- return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \
- arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \
- arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \
- arg9_type arg9) const;\
+ return_type gmock_PerformImpl(const args_type& args, \
+ const arg0_type& arg0, const arg1_type& arg1, \
+ const arg2_type& arg2, const arg3_type& arg3, \
+ const arg4_type& arg4, const arg5_type& arg5, \
+ const arg6_type& arg6, const arg7_type& arg7, \
+ const arg8_type& arg8, const arg9_type& arg9) const;\
p0##_type p0;\
p1##_type p1;\
p2##_type p2;\
@@ -1587,8 +1019,11 @@ DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6,
public:\
name##ActionP5(p0##_type gmock_p0, p1##_type gmock_p1, \
p2##_type gmock_p2, p3##_type gmock_p3, \
- p4##_type gmock_p4) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
- p3(gmock_p3), p4(gmock_p4) {}\
+ p4##_type gmock_p4) : p0(::std::forward<p0##_type>(gmock_p0)), \
+ p1(::std::forward<p1##_type>(gmock_p1)), \
+ p2(::std::forward<p2##_type>(gmock_p2)), \
+ p3(::std::forward<p3##_type>(gmock_p3)), \
+ p4(::std::forward<p4##_type>(gmock_p4)) {}\
template <typename F>\
class gmock_Impl : public ::testing::ActionInterface<F> {\
public:\
@@ -1597,8 +1032,12 @@ DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6,
typedef typename ::testing::internal::Function<F>::ArgumentTuple\
args_type;\
gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
- p3##_type gmock_p3, p4##_type gmock_p4) : p0(gmock_p0), \
- p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), p4(gmock_p4) {}\
+ p3##_type gmock_p3, \
+ p4##_type gmock_p4) : p0(::std::forward<p0##_type>(gmock_p0)), \
+ p1(::std::forward<p1##_type>(gmock_p1)), \
+ p2(::std::forward<p2##_type>(gmock_p2)), \
+ p3(::std::forward<p3##_type>(gmock_p3)), \
+ p4(::std::forward<p4##_type>(gmock_p4)) {}\
virtual return_type Perform(const args_type& args) {\
return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
Perform(this, args);\
@@ -1607,10 +1046,12 @@ DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6,
typename arg3_type, typename arg4_type, typename arg5_type, \
typename arg6_type, typename arg7_type, typename arg8_type, \
typename arg9_type>\
- return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \
- arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \
- arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \
- arg9_type arg9) const;\
+ return_type gmock_PerformImpl(const args_type& args, \
+ const arg0_type& arg0, const arg1_type& arg1, \
+ const arg2_type& arg2, const arg3_type& arg3, \
+ const arg4_type& arg4, const arg5_type& arg5, \
+ const arg6_type& arg6, const arg7_type& arg7, \
+ const arg8_type& arg8, const arg9_type& arg9) const;\
p0##_type p0;\
p1##_type p1;\
p2##_type p2;\
@@ -1657,8 +1098,12 @@ DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6,
public:\
name##ActionP6(p0##_type gmock_p0, p1##_type gmock_p1, \
p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \
- p5##_type gmock_p5) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
- p3(gmock_p3), p4(gmock_p4), p5(gmock_p5) {}\
+ p5##_type gmock_p5) : p0(::std::forward<p0##_type>(gmock_p0)), \
+ p1(::std::forward<p1##_type>(gmock_p1)), \
+ p2(::std::forward<p2##_type>(gmock_p2)), \
+ p3(::std::forward<p3##_type>(gmock_p3)), \
+ p4(::std::forward<p4##_type>(gmock_p4)), \
+ p5(::std::forward<p5##_type>(gmock_p5)) {}\
template <typename F>\
class gmock_Impl : public ::testing::ActionInterface<F> {\
public:\
@@ -1668,8 +1113,12 @@ DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6,
args_type;\
gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
p3##_type gmock_p3, p4##_type gmock_p4, \
- p5##_type gmock_p5) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
- p3(gmock_p3), p4(gmock_p4), p5(gmock_p5) {}\
+ p5##_type gmock_p5) : p0(::std::forward<p0##_type>(gmock_p0)), \
+ p1(::std::forward<p1##_type>(gmock_p1)), \
+ p2(::std::forward<p2##_type>(gmock_p2)), \
+ p3(::std::forward<p3##_type>(gmock_p3)), \
+ p4(::std::forward<p4##_type>(gmock_p4)), \
+ p5(::std::forward<p5##_type>(gmock_p5)) {}\
virtual return_type Perform(const args_type& args) {\
return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
Perform(this, args);\
@@ -1678,10 +1127,12 @@ DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6,
typename arg3_type, typename arg4_type, typename arg5_type, \
typename arg6_type, typename arg7_type, typename arg8_type, \
typename arg9_type>\
- return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \
- arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \
- arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \
- arg9_type arg9) const;\
+ return_type gmock_PerformImpl(const args_type& args, \
+ const arg0_type& arg0, const arg1_type& arg1, \
+ const arg2_type& arg2, const arg3_type& arg3, \
+ const arg4_type& arg4, const arg5_type& arg5, \
+ const arg6_type& arg6, const arg7_type& arg7, \
+ const arg8_type& arg8, const arg9_type& arg9) const;\
p0##_type p0;\
p1##_type p1;\
p2##_type p2;\
@@ -1731,9 +1182,14 @@ DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6,
public:\
name##ActionP7(p0##_type gmock_p0, p1##_type gmock_p1, \
p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \
- p5##_type gmock_p5, p6##_type gmock_p6) : p0(gmock_p0), p1(gmock_p1), \
- p2(gmock_p2), p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), \
- p6(gmock_p6) {}\
+ p5##_type gmock_p5, \
+ p6##_type gmock_p6) : p0(::std::forward<p0##_type>(gmock_p0)), \
+ p1(::std::forward<p1##_type>(gmock_p1)), \
+ p2(::std::forward<p2##_type>(gmock_p2)), \
+ p3(::std::forward<p3##_type>(gmock_p3)), \
+ p4(::std::forward<p4##_type>(gmock_p4)), \
+ p5(::std::forward<p5##_type>(gmock_p5)), \
+ p6(::std::forward<p6##_type>(gmock_p6)) {}\
template <typename F>\
class gmock_Impl : public ::testing::ActionInterface<F> {\
public:\
@@ -1743,8 +1199,13 @@ DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6,
args_type;\
gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
- p6##_type gmock_p6) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
- p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6) {}\
+ p6##_type gmock_p6) : p0(::std::forward<p0##_type>(gmock_p0)), \
+ p1(::std::forward<p1##_type>(gmock_p1)), \
+ p2(::std::forward<p2##_type>(gmock_p2)), \
+ p3(::std::forward<p3##_type>(gmock_p3)), \
+ p4(::std::forward<p4##_type>(gmock_p4)), \
+ p5(::std::forward<p5##_type>(gmock_p5)), \
+ p6(::std::forward<p6##_type>(gmock_p6)) {}\
virtual return_type Perform(const args_type& args) {\
return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
Perform(this, args);\
@@ -1753,10 +1214,12 @@ DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6,
typename arg3_type, typename arg4_type, typename arg5_type, \
typename arg6_type, typename arg7_type, typename arg8_type, \
typename arg9_type>\
- return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \
- arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \
- arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \
- arg9_type arg9) const;\
+ return_type gmock_PerformImpl(const args_type& args, \
+ const arg0_type& arg0, const arg1_type& arg1, \
+ const arg2_type& arg2, const arg3_type& arg3, \
+ const arg4_type& arg4, const arg5_type& arg5, \
+ const arg6_type& arg6, const arg7_type& arg7, \
+ const arg8_type& arg8, const arg9_type& arg9) const;\
p0##_type p0;\
p1##_type p1;\
p2##_type p2;\
@@ -1813,9 +1276,14 @@ DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6,
name##ActionP8(p0##_type gmock_p0, p1##_type gmock_p1, \
p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \
p5##_type gmock_p5, p6##_type gmock_p6, \
- p7##_type gmock_p7) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
- p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), \
- p7(gmock_p7) {}\
+ p7##_type gmock_p7) : p0(::std::forward<p0##_type>(gmock_p0)), \
+ p1(::std::forward<p1##_type>(gmock_p1)), \
+ p2(::std::forward<p2##_type>(gmock_p2)), \
+ p3(::std::forward<p3##_type>(gmock_p3)), \
+ p4(::std::forward<p4##_type>(gmock_p4)), \
+ p5(::std::forward<p5##_type>(gmock_p5)), \
+ p6(::std::forward<p6##_type>(gmock_p6)), \
+ p7(::std::forward<p7##_type>(gmock_p7)) {}\
template <typename F>\
class gmock_Impl : public ::testing::ActionInterface<F> {\
public:\
@@ -1825,9 +1293,15 @@ DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6,
args_type;\
gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
- p6##_type gmock_p6, p7##_type gmock_p7) : p0(gmock_p0), \
- p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), p4(gmock_p4), \
- p5(gmock_p5), p6(gmock_p6), p7(gmock_p7) {}\
+ p6##_type gmock_p6, \
+ p7##_type gmock_p7) : p0(::std::forward<p0##_type>(gmock_p0)), \
+ p1(::std::forward<p1##_type>(gmock_p1)), \
+ p2(::std::forward<p2##_type>(gmock_p2)), \
+ p3(::std::forward<p3##_type>(gmock_p3)), \
+ p4(::std::forward<p4##_type>(gmock_p4)), \
+ p5(::std::forward<p5##_type>(gmock_p5)), \
+ p6(::std::forward<p6##_type>(gmock_p6)), \
+ p7(::std::forward<p7##_type>(gmock_p7)) {}\
virtual return_type Perform(const args_type& args) {\
return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
Perform(this, args);\
@@ -1836,10 +1310,12 @@ DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6,
typename arg3_type, typename arg4_type, typename arg5_type, \
typename arg6_type, typename arg7_type, typename arg8_type, \
typename arg9_type>\
- return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \
- arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \
- arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \
- arg9_type arg9) const;\
+ return_type gmock_PerformImpl(const args_type& args, \
+ const arg0_type& arg0, const arg1_type& arg1, \
+ const arg2_type& arg2, const arg3_type& arg3, \
+ const arg4_type& arg4, const arg5_type& arg5, \
+ const arg6_type& arg6, const arg7_type& arg7, \
+ const arg8_type& arg8, const arg9_type& arg9) const;\
p0##_type p0;\
p1##_type p1;\
p2##_type p2;\
@@ -1900,9 +1376,15 @@ DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6,
name##ActionP9(p0##_type gmock_p0, p1##_type gmock_p1, \
p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \
p5##_type gmock_p5, p6##_type gmock_p6, p7##_type gmock_p7, \
- p8##_type gmock_p8) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
- p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), p7(gmock_p7), \
- p8(gmock_p8) {}\
+ p8##_type gmock_p8) : p0(::std::forward<p0##_type>(gmock_p0)), \
+ p1(::std::forward<p1##_type>(gmock_p1)), \
+ p2(::std::forward<p2##_type>(gmock_p2)), \
+ p3(::std::forward<p3##_type>(gmock_p3)), \
+ p4(::std::forward<p4##_type>(gmock_p4)), \
+ p5(::std::forward<p5##_type>(gmock_p5)), \
+ p6(::std::forward<p6##_type>(gmock_p6)), \
+ p7(::std::forward<p7##_type>(gmock_p7)), \
+ p8(::std::forward<p8##_type>(gmock_p8)) {}\
template <typename F>\
class gmock_Impl : public ::testing::ActionInterface<F> {\
public:\
@@ -1913,9 +1395,15 @@ DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6,
gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
p6##_type gmock_p6, p7##_type gmock_p7, \
- p8##_type gmock_p8) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
- p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), \
- p7(gmock_p7), p8(gmock_p8) {}\
+ p8##_type gmock_p8) : p0(::std::forward<p0##_type>(gmock_p0)), \
+ p1(::std::forward<p1##_type>(gmock_p1)), \
+ p2(::std::forward<p2##_type>(gmock_p2)), \
+ p3(::std::forward<p3##_type>(gmock_p3)), \
+ p4(::std::forward<p4##_type>(gmock_p4)), \
+ p5(::std::forward<p5##_type>(gmock_p5)), \
+ p6(::std::forward<p6##_type>(gmock_p6)), \
+ p7(::std::forward<p7##_type>(gmock_p7)), \
+ p8(::std::forward<p8##_type>(gmock_p8)) {}\
virtual return_type Perform(const args_type& args) {\
return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
Perform(this, args);\
@@ -1924,10 +1412,12 @@ DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6,
typename arg3_type, typename arg4_type, typename arg5_type, \
typename arg6_type, typename arg7_type, typename arg8_type, \
typename arg9_type>\
- return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \
- arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \
- arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \
- arg9_type arg9) const;\
+ return_type gmock_PerformImpl(const args_type& args, \
+ const arg0_type& arg0, const arg1_type& arg1, \
+ const arg2_type& arg2, const arg3_type& arg3, \
+ const arg4_type& arg4, const arg5_type& arg5, \
+ const arg6_type& arg6, const arg7_type& arg7, \
+ const arg8_type& arg8, const arg9_type& arg9) const;\
p0##_type p0;\
p1##_type p1;\
p2##_type p2;\
@@ -1992,9 +1482,17 @@ DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6,
name##ActionP10(p0##_type gmock_p0, p1##_type gmock_p1, \
p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \
p5##_type gmock_p5, p6##_type gmock_p6, p7##_type gmock_p7, \
- p8##_type gmock_p8, p9##_type gmock_p9) : p0(gmock_p0), p1(gmock_p1), \
- p2(gmock_p2), p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), \
- p7(gmock_p7), p8(gmock_p8), p9(gmock_p9) {}\
+ p8##_type gmock_p8, \
+ p9##_type gmock_p9) : p0(::std::forward<p0##_type>(gmock_p0)), \
+ p1(::std::forward<p1##_type>(gmock_p1)), \
+ p2(::std::forward<p2##_type>(gmock_p2)), \
+ p3(::std::forward<p3##_type>(gmock_p3)), \
+ p4(::std::forward<p4##_type>(gmock_p4)), \
+ p5(::std::forward<p5##_type>(gmock_p5)), \
+ p6(::std::forward<p6##_type>(gmock_p6)), \
+ p7(::std::forward<p7##_type>(gmock_p7)), \
+ p8(::std::forward<p8##_type>(gmock_p8)), \
+ p9(::std::forward<p9##_type>(gmock_p9)) {}\
template <typename F>\
class gmock_Impl : public ::testing::ActionInterface<F> {\
public:\
@@ -2005,9 +1503,16 @@ DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6,
gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
p6##_type gmock_p6, p7##_type gmock_p7, p8##_type gmock_p8, \
- p9##_type gmock_p9) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
- p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), \
- p7(gmock_p7), p8(gmock_p8), p9(gmock_p9) {}\
+ p9##_type gmock_p9) : p0(::std::forward<p0##_type>(gmock_p0)), \
+ p1(::std::forward<p1##_type>(gmock_p1)), \
+ p2(::std::forward<p2##_type>(gmock_p2)), \
+ p3(::std::forward<p3##_type>(gmock_p3)), \
+ p4(::std::forward<p4##_type>(gmock_p4)), \
+ p5(::std::forward<p5##_type>(gmock_p5)), \
+ p6(::std::forward<p6##_type>(gmock_p6)), \
+ p7(::std::forward<p7##_type>(gmock_p7)), \
+ p8(::std::forward<p8##_type>(gmock_p8)), \
+ p9(::std::forward<p9##_type>(gmock_p9)) {}\
virtual return_type Perform(const args_type& args) {\
return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
Perform(this, args);\
@@ -2016,10 +1521,12 @@ DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6,
typename arg3_type, typename arg4_type, typename arg5_type, \
typename arg6_type, typename arg7_type, typename arg8_type, \
typename arg9_type>\
- return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \
- arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \
- arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \
- arg9_type arg9) const;\
+ return_type gmock_PerformImpl(const args_type& args, \
+ const arg0_type& arg0, const arg1_type& arg1, \
+ const arg2_type& arg2, const arg3_type& arg3, \
+ const arg4_type& arg4, const arg5_type& arg5, \
+ const arg6_type& arg6, const arg7_type& arg7, \
+ const arg8_type& arg8, const arg9_type& arg9) const;\
p0##_type p0;\
p1##_type p1;\
p2##_type p2;\
@@ -2199,7 +1706,7 @@ ACTION_TEMPLATE(InvokeArgument,
using internal::invoke_argument::InvokeArgumentAdl;
return InvokeArgumentAdl<return_type>(
internal::invoke_argument::AdlTag(),
- ::testing::get<k>(args));
+ ::std::get<k>(args));
}
ACTION_TEMPLATE(InvokeArgument,
@@ -2208,7 +1715,7 @@ ACTION_TEMPLATE(InvokeArgument,
using internal::invoke_argument::InvokeArgumentAdl;
return InvokeArgumentAdl<return_type>(
internal::invoke_argument::AdlTag(),
- ::testing::get<k>(args), p0);
+ ::std::get<k>(args), p0);
}
ACTION_TEMPLATE(InvokeArgument,
@@ -2217,7 +1724,7 @@ ACTION_TEMPLATE(InvokeArgument,
using internal::invoke_argument::InvokeArgumentAdl;
return InvokeArgumentAdl<return_type>(
internal::invoke_argument::AdlTag(),
- ::testing::get<k>(args), p0, p1);
+ ::std::get<k>(args), p0, p1);
}
ACTION_TEMPLATE(InvokeArgument,
@@ -2226,7 +1733,7 @@ ACTION_TEMPLATE(InvokeArgument,
using internal::invoke_argument::InvokeArgumentAdl;
return InvokeArgumentAdl<return_type>(
internal::invoke_argument::AdlTag(),
- ::testing::get<k>(args), p0, p1, p2);
+ ::std::get<k>(args), p0, p1, p2);
}
ACTION_TEMPLATE(InvokeArgument,
@@ -2235,7 +1742,7 @@ ACTION_TEMPLATE(InvokeArgument,
using internal::invoke_argument::InvokeArgumentAdl;
return InvokeArgumentAdl<return_type>(
internal::invoke_argument::AdlTag(),
- ::testing::get<k>(args), p0, p1, p2, p3);
+ ::std::get<k>(args), p0, p1, p2, p3);
}
ACTION_TEMPLATE(InvokeArgument,
@@ -2244,7 +1751,7 @@ ACTION_TEMPLATE(InvokeArgument,
using internal::invoke_argument::InvokeArgumentAdl;
return InvokeArgumentAdl<return_type>(
internal::invoke_argument::AdlTag(),
- ::testing::get<k>(args), p0, p1, p2, p3, p4);
+ ::std::get<k>(args), p0, p1, p2, p3, p4);
}
ACTION_TEMPLATE(InvokeArgument,
@@ -2253,7 +1760,7 @@ ACTION_TEMPLATE(InvokeArgument,
using internal::invoke_argument::InvokeArgumentAdl;
return InvokeArgumentAdl<return_type>(
internal::invoke_argument::AdlTag(),
- ::testing::get<k>(args), p0, p1, p2, p3, p4, p5);
+ ::std::get<k>(args), p0, p1, p2, p3, p4, p5);
}
ACTION_TEMPLATE(InvokeArgument,
@@ -2262,7 +1769,7 @@ ACTION_TEMPLATE(InvokeArgument,
using internal::invoke_argument::InvokeArgumentAdl;
return InvokeArgumentAdl<return_type>(
internal::invoke_argument::AdlTag(),
- ::testing::get<k>(args), p0, p1, p2, p3, p4, p5, p6);
+ ::std::get<k>(args), p0, p1, p2, p3, p4, p5, p6);
}
ACTION_TEMPLATE(InvokeArgument,
@@ -2271,7 +1778,7 @@ ACTION_TEMPLATE(InvokeArgument,
using internal::invoke_argument::InvokeArgumentAdl;
return InvokeArgumentAdl<return_type>(
internal::invoke_argument::AdlTag(),
- ::testing::get<k>(args), p0, p1, p2, p3, p4, p5, p6, p7);
+ ::std::get<k>(args), p0, p1, p2, p3, p4, p5, p6, p7);
}
ACTION_TEMPLATE(InvokeArgument,
@@ -2280,7 +1787,7 @@ ACTION_TEMPLATE(InvokeArgument,
using internal::invoke_argument::InvokeArgumentAdl;
return InvokeArgumentAdl<return_type>(
internal::invoke_argument::AdlTag(),
- ::testing::get<k>(args), p0, p1, p2, p3, p4, p5, p6, p7, p8);
+ ::std::get<k>(args), p0, p1, p2, p3, p4, p5, p6, p7, p8);
}
ACTION_TEMPLATE(InvokeArgument,
@@ -2289,7 +1796,7 @@ ACTION_TEMPLATE(InvokeArgument,
using internal::invoke_argument::InvokeArgumentAdl;
return InvokeArgumentAdl<return_type>(
internal::invoke_argument::AdlTag(),
- ::testing::get<k>(args), p0, p1, p2, p3, p4, p5, p6, p7, p8, p9);
+ ::std::get<k>(args), p0, p1, p2, p3, p4, p5, p6, p7, p8, p9);
}
// Various overloads for ReturnNew<T>().
@@ -2369,7 +1876,7 @@ ACTION_TEMPLATE(ReturnNew,
} // namespace testing
-// Include any custom actions added by the local installation.
+// Include any custom callback actions added by the local installation.
// We must include this header at the end to make sure it can use the
// declarations from this file.
#include "gmock/internal/custom/gmock-generated-actions.h"
diff --git a/extern/gmock/include/gmock/gmock-generated-function-mockers.h b/extern/gmock/include/gmock/gmock-generated-function-mockers.h
index 4fa5ca94849..cd9578178c0 100644
--- a/extern/gmock/include/gmock/gmock-generated-function-mockers.h
+++ b/extern/gmock/include/gmock/gmock-generated-function-mockers.h
@@ -30,295 +30,76 @@
// 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: wan@google.com (Zhanyong Wan)
+
// Google Mock - a framework for writing C++ mock classes.
//
// This file implements function mockers of various arities.
+// GOOGLETEST_CM0002 DO NOT DELETE
+
#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_FUNCTION_MOCKERS_H_
#define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_FUNCTION_MOCKERS_H_
+#include <functional>
+#include <utility>
+
#include "gmock/gmock-spec-builders.h"
#include "gmock/internal/gmock-internal-utils.h"
-#if GTEST_HAS_STD_FUNCTION_
-# include <functional>
-#endif
-
namespace testing {
namespace internal {
-
-template <typename F>
-class FunctionMockerBase;
-
-// Note: class FunctionMocker really belongs to the ::testing
-// namespace. However if we define it in ::testing, MSVC will
-// complain when classes in ::testing::internal declare it as a
-// friend class template. To workaround this compiler bug, we define
-// FunctionMocker in ::testing::internal and import it into ::testing.
-template <typename F>
-class FunctionMocker;
-
-template <typename R>
-class FunctionMocker<R()> : public
- internal::FunctionMockerBase<R()> {
- public:
- typedef R F();
- typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
-
- MockSpec<F>& With() {
- return this->current_spec();
- }
-
- R Invoke() {
- // Even though gcc and MSVC don't enforce it, 'this->' is required
- // by the C++ standard [14.6.4] here, as the base class type is
- // dependent on the template argument (and thus shouldn't be
- // looked into when resolving InvokeWith).
- return this->InvokeWith(ArgumentTuple());
- }
-};
-
-template <typename R, typename A1>
-class FunctionMocker<R(A1)> : public
- internal::FunctionMockerBase<R(A1)> {
- public:
- typedef R F(A1);
- typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
-
- MockSpec<F>& With(const Matcher<A1>& m1) {
- this->current_spec().SetMatchers(::testing::make_tuple(m1));
- return this->current_spec();
- }
-
- R Invoke(A1 a1) {
- // Even though gcc and MSVC don't enforce it, 'this->' is required
- // by the C++ standard [14.6.4] here, as the base class type is
- // dependent on the template argument (and thus shouldn't be
- // looked into when resolving InvokeWith).
- return this->InvokeWith(ArgumentTuple(a1));
- }
-};
-
-template <typename R, typename A1, typename A2>
-class FunctionMocker<R(A1, A2)> : public
- internal::FunctionMockerBase<R(A1, A2)> {
- public:
- typedef R F(A1, A2);
- typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
-
- MockSpec<F>& With(const Matcher<A1>& m1, const Matcher<A2>& m2) {
- this->current_spec().SetMatchers(::testing::make_tuple(m1, m2));
- return this->current_spec();
- }
-
- R Invoke(A1 a1, A2 a2) {
- // Even though gcc and MSVC don't enforce it, 'this->' is required
- // by the C++ standard [14.6.4] here, as the base class type is
- // dependent on the template argument (and thus shouldn't be
- // looked into when resolving InvokeWith).
- return this->InvokeWith(ArgumentTuple(a1, a2));
- }
-};
-
-template <typename R, typename A1, typename A2, typename A3>
-class FunctionMocker<R(A1, A2, A3)> : public
- internal::FunctionMockerBase<R(A1, A2, A3)> {
- public:
- typedef R F(A1, A2, A3);
- typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
-
- MockSpec<F>& With(const Matcher<A1>& m1, const Matcher<A2>& m2,
- const Matcher<A3>& m3) {
- this->current_spec().SetMatchers(::testing::make_tuple(m1, m2, m3));
- return this->current_spec();
- }
-
- R Invoke(A1 a1, A2 a2, A3 a3) {
- // Even though gcc and MSVC don't enforce it, 'this->' is required
- // by the C++ standard [14.6.4] here, as the base class type is
- // dependent on the template argument (and thus shouldn't be
- // looked into when resolving InvokeWith).
- return this->InvokeWith(ArgumentTuple(a1, a2, a3));
- }
-};
-
-template <typename R, typename A1, typename A2, typename A3, typename A4>
-class FunctionMocker<R(A1, A2, A3, A4)> : public
- internal::FunctionMockerBase<R(A1, A2, A3, A4)> {
- public:
- typedef R F(A1, A2, A3, A4);
- typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
-
- MockSpec<F>& With(const Matcher<A1>& m1, const Matcher<A2>& m2,
- const Matcher<A3>& m3, const Matcher<A4>& m4) {
- this->current_spec().SetMatchers(::testing::make_tuple(m1, m2, m3, m4));
- return this->current_spec();
- }
-
- R Invoke(A1 a1, A2 a2, A3 a3, A4 a4) {
- // Even though gcc and MSVC don't enforce it, 'this->' is required
- // by the C++ standard [14.6.4] here, as the base class type is
- // dependent on the template argument (and thus shouldn't be
- // looked into when resolving InvokeWith).
- return this->InvokeWith(ArgumentTuple(a1, a2, a3, a4));
- }
-};
-
-template <typename R, typename A1, typename A2, typename A3, typename A4,
- typename A5>
-class FunctionMocker<R(A1, A2, A3, A4, A5)> : public
- internal::FunctionMockerBase<R(A1, A2, A3, A4, A5)> {
- public:
- typedef R F(A1, A2, A3, A4, A5);
- typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
-
- MockSpec<F>& With(const Matcher<A1>& m1, const Matcher<A2>& m2,
- const Matcher<A3>& m3, const Matcher<A4>& m4, const Matcher<A5>& m5) {
- this->current_spec().SetMatchers(::testing::make_tuple(m1, m2, m3, m4, m5));
- return this->current_spec();
- }
-
- R Invoke(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) {
- // Even though gcc and MSVC don't enforce it, 'this->' is required
- // by the C++ standard [14.6.4] here, as the base class type is
- // dependent on the template argument (and thus shouldn't be
- // looked into when resolving InvokeWith).
- return this->InvokeWith(ArgumentTuple(a1, a2, a3, a4, a5));
- }
-};
-
-template <typename R, typename A1, typename A2, typename A3, typename A4,
- typename A5, typename A6>
-class FunctionMocker<R(A1, A2, A3, A4, A5, A6)> : public
- internal::FunctionMockerBase<R(A1, A2, A3, A4, A5, A6)> {
- public:
- typedef R F(A1, A2, A3, A4, A5, A6);
- typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
-
- MockSpec<F>& With(const Matcher<A1>& m1, const Matcher<A2>& m2,
- const Matcher<A3>& m3, const Matcher<A4>& m4, const Matcher<A5>& m5,
- const Matcher<A6>& m6) {
- this->current_spec().SetMatchers(::testing::make_tuple(m1, m2, m3, m4, m5,
- m6));
- return this->current_spec();
- }
-
- R Invoke(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) {
- // Even though gcc and MSVC don't enforce it, 'this->' is required
- // by the C++ standard [14.6.4] here, as the base class type is
- // dependent on the template argument (and thus shouldn't be
- // looked into when resolving InvokeWith).
- return this->InvokeWith(ArgumentTuple(a1, a2, a3, a4, a5, a6));
- }
-};
-
-template <typename R, typename A1, typename A2, typename A3, typename A4,
- typename A5, typename A6, typename A7>
-class FunctionMocker<R(A1, A2, A3, A4, A5, A6, A7)> : public
- internal::FunctionMockerBase<R(A1, A2, A3, A4, A5, A6, A7)> {
- public:
- typedef R F(A1, A2, A3, A4, A5, A6, A7);
- typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
-
- MockSpec<F>& With(const Matcher<A1>& m1, const Matcher<A2>& m2,
- const Matcher<A3>& m3, const Matcher<A4>& m4, const Matcher<A5>& m5,
- const Matcher<A6>& m6, const Matcher<A7>& m7) {
- this->current_spec().SetMatchers(::testing::make_tuple(m1, m2, m3, m4, m5,
- m6, m7));
- return this->current_spec();
- }
-
- R Invoke(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) {
- // Even though gcc and MSVC don't enforce it, 'this->' is required
- // by the C++ standard [14.6.4] here, as the base class type is
- // dependent on the template argument (and thus shouldn't be
- // looked into when resolving InvokeWith).
- return this->InvokeWith(ArgumentTuple(a1, a2, a3, a4, a5, a6, a7));
- }
-};
-
-template <typename R, typename A1, typename A2, typename A3, typename A4,
- typename A5, typename A6, typename A7, typename A8>
-class FunctionMocker<R(A1, A2, A3, A4, A5, A6, A7, A8)> : public
- internal::FunctionMockerBase<R(A1, A2, A3, A4, A5, A6, A7, A8)> {
- public:
- typedef R F(A1, A2, A3, A4, A5, A6, A7, A8);
- typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
-
- MockSpec<F>& With(const Matcher<A1>& m1, const Matcher<A2>& m2,
- const Matcher<A3>& m3, const Matcher<A4>& m4, const Matcher<A5>& m5,
- const Matcher<A6>& m6, const Matcher<A7>& m7, const Matcher<A8>& m8) {
- this->current_spec().SetMatchers(::testing::make_tuple(m1, m2, m3, m4, m5,
- m6, m7, m8));
- return this->current_spec();
- }
-
- R Invoke(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) {
- // Even though gcc and MSVC don't enforce it, 'this->' is required
- // by the C++ standard [14.6.4] here, as the base class type is
- // dependent on the template argument (and thus shouldn't be
- // looked into when resolving InvokeWith).
- return this->InvokeWith(ArgumentTuple(a1, a2, a3, a4, a5, a6, a7, a8));
- }
-};
-
-template <typename R, typename A1, typename A2, typename A3, typename A4,
- typename A5, typename A6, typename A7, typename A8, typename A9>
-class FunctionMocker<R(A1, A2, A3, A4, A5, A6, A7, A8, A9)> : public
- internal::FunctionMockerBase<R(A1, A2, A3, A4, A5, A6, A7, A8, A9)> {
- public:
- typedef R F(A1, A2, A3, A4, A5, A6, A7, A8, A9);
- typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
-
- MockSpec<F>& With(const Matcher<A1>& m1, const Matcher<A2>& m2,
- const Matcher<A3>& m3, const Matcher<A4>& m4, const Matcher<A5>& m5,
- const Matcher<A6>& m6, const Matcher<A7>& m7, const Matcher<A8>& m8,
- const Matcher<A9>& m9) {
- this->current_spec().SetMatchers(::testing::make_tuple(m1, m2, m3, m4, m5,
- m6, m7, m8, m9));
- return this->current_spec();
- }
-
- R Invoke(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) {
- // Even though gcc and MSVC don't enforce it, 'this->' is required
- // by the C++ standard [14.6.4] here, as the base class type is
- // dependent on the template argument (and thus shouldn't be
- // looked into when resolving InvokeWith).
- return this->InvokeWith(ArgumentTuple(a1, a2, a3, a4, a5, a6, a7, a8, a9));
- }
-};
-
-template <typename R, typename A1, typename A2, typename A3, typename A4,
- typename A5, typename A6, typename A7, typename A8, typename A9,
- typename A10>
-class FunctionMocker<R(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10)> : public
- internal::FunctionMockerBase<R(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10)> {
- public:
- typedef R F(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10);
- typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
-
- MockSpec<F>& With(const Matcher<A1>& m1, const Matcher<A2>& m2,
- const Matcher<A3>& m3, const Matcher<A4>& m4, const Matcher<A5>& m5,
- const Matcher<A6>& m6, const Matcher<A7>& m7, const Matcher<A8>& m8,
- const Matcher<A9>& m9, const Matcher<A10>& m10) {
- this->current_spec().SetMatchers(::testing::make_tuple(m1, m2, m3, m4, m5,
- m6, m7, m8, m9, m10));
- return this->current_spec();
- }
-
- R Invoke(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9,
- A10 a10) {
- // Even though gcc and MSVC don't enforce it, 'this->' is required
- // by the C++ standard [14.6.4] here, as the base class type is
- // dependent on the template argument (and thus shouldn't be
- // looked into when resolving InvokeWith).
- return this->InvokeWith(ArgumentTuple(a1, a2, a3, a4, a5, a6, a7, a8, a9,
- a10));
- }
-};
+// Removes the given pointer; this is a helper for the expectation setter method
+// for parameterless matchers.
+//
+// We want to make sure that the user cannot set a parameterless expectation on
+// overloaded methods, including methods which are overloaded on const. Example:
+//
+// class MockClass {
+// MOCK_METHOD0(GetName, string&());
+// MOCK_CONST_METHOD0(GetName, const string&());
+// };
+//
+// TEST() {
+// // This should be an error, as it's not clear which overload is expected.
+// EXPECT_CALL(mock, GetName).WillOnce(ReturnRef(value));
+// }
+//
+// Here are the generated expectation-setter methods:
+//
+// class MockClass {
+// // Overload 1
+// MockSpec<string&()> gmock_GetName() { ... }
+// // Overload 2. Declared const so that the compiler will generate an
+// // error when trying to resolve between this and overload 4 in
+// // 'gmock_GetName(WithoutMatchers(), nullptr)'.
+// MockSpec<string&()> gmock_GetName(
+// const WithoutMatchers&, const Function<string&()>*) const {
+// // Removes const from this, calls overload 1
+// return AdjustConstness_(this)->gmock_GetName();
+// }
+//
+// // Overload 3
+// const string& gmock_GetName() const { ... }
+// // Overload 4
+// MockSpec<const string&()> gmock_GetName(
+// const WithoutMatchers&, const Function<const string&()>*) const {
+// // Does not remove const, calls overload 3
+// return AdjustConstness_const(this)->gmock_GetName();
+// }
+// }
+//
+template <typename MockType>
+const MockType* AdjustConstness_const(const MockType* mock) {
+ return mock;
+}
+
+// Removes const from and returns the given pointer; this is a helper for the
+// expectation setter method for parameterless matchers.
+template <typename MockType>
+MockType* AdjustConstness_(const MockType* mock) {
+ return const_cast<MockType*>(mock);
+}
} // namespace internal
@@ -340,7 +121,7 @@ using internal::FunctionMocker;
// The type of argument N of the given function type.
// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
#define GMOCK_ARG_(tn, N, ...) \
- tn ::testing::internal::Function<__VA_ARGS__>::Argument##N
+ tn ::testing::internal::Function<__VA_ARGS__>::template Arg<N-1>::type
// The matcher type for argument N of the given function type.
// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
@@ -354,78 +135,101 @@ using internal::FunctionMocker;
// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
#define GMOCK_METHOD0_(tn, constness, ct, Method, ...) \
+ static_assert(0 == \
+ ::testing::internal::Function<__VA_ARGS__>::ArgumentCount, \
+ "MOCK_METHOD<N> must match argument count.");\
GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \
) constness { \
- GTEST_COMPILE_ASSERT_((::testing::tuple_size< \
- tn ::testing::internal::Function<__VA_ARGS__>::ArgumentTuple>::value \
- == 0), \
- this_method_does_not_take_0_arguments); \
GMOCK_MOCKER_(0, constness, Method).SetOwnerAndName(this, #Method); \
return GMOCK_MOCKER_(0, constness, Method).Invoke(); \
} \
- ::testing::MockSpec<__VA_ARGS__>& \
+ ::testing::MockSpec<__VA_ARGS__> \
gmock_##Method() constness { \
GMOCK_MOCKER_(0, constness, Method).RegisterOwner(this); \
return GMOCK_MOCKER_(0, constness, Method).With(); \
} \
+ ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \
+ const ::testing::internal::WithoutMatchers&, \
+ constness ::testing::internal::Function<__VA_ARGS__>* ) const { \
+ return ::testing::internal::AdjustConstness_##constness(this)-> \
+ gmock_##Method(); \
+ } \
mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(0, constness, \
Method)
// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
#define GMOCK_METHOD1_(tn, constness, ct, Method, ...) \
+ static_assert(1 == \
+ ::testing::internal::Function<__VA_ARGS__>::ArgumentCount, \
+ "MOCK_METHOD<N> must match argument count.");\
GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \
GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1) constness { \
- GTEST_COMPILE_ASSERT_((::testing::tuple_size< \
- tn ::testing::internal::Function<__VA_ARGS__>::ArgumentTuple>::value \
- == 1), \
- this_method_does_not_take_1_argument); \
GMOCK_MOCKER_(1, constness, Method).SetOwnerAndName(this, #Method); \
- return GMOCK_MOCKER_(1, constness, Method).Invoke(gmock_a1); \
+ return GMOCK_MOCKER_(1, constness, \
+ Method).Invoke(::std::forward<GMOCK_ARG_(tn, 1, \
+ __VA_ARGS__)>(gmock_a1)); \
} \
- ::testing::MockSpec<__VA_ARGS__>& \
+ ::testing::MockSpec<__VA_ARGS__> \
gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1) constness { \
GMOCK_MOCKER_(1, constness, Method).RegisterOwner(this); \
return GMOCK_MOCKER_(1, constness, Method).With(gmock_a1); \
} \
+ ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \
+ const ::testing::internal::WithoutMatchers&, \
+ constness ::testing::internal::Function<__VA_ARGS__>* ) const { \
+ return ::testing::internal::AdjustConstness_##constness(this)-> \
+ gmock_##Method(::testing::A<GMOCK_ARG_(tn, 1, __VA_ARGS__)>()); \
+ } \
mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(1, constness, \
Method)
// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
#define GMOCK_METHOD2_(tn, constness, ct, Method, ...) \
+ static_assert(2 == \
+ ::testing::internal::Function<__VA_ARGS__>::ArgumentCount, \
+ "MOCK_METHOD<N> must match argument count.");\
GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \
- GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, \
- GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2) constness { \
- GTEST_COMPILE_ASSERT_((::testing::tuple_size< \
- tn ::testing::internal::Function<__VA_ARGS__>::ArgumentTuple>::value \
- == 2), \
- this_method_does_not_take_2_arguments); \
+ GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, GMOCK_ARG_(tn, 2, \
+ __VA_ARGS__) gmock_a2) constness { \
GMOCK_MOCKER_(2, constness, Method).SetOwnerAndName(this, #Method); \
- return GMOCK_MOCKER_(2, constness, Method).Invoke(gmock_a1, gmock_a2); \
+ return GMOCK_MOCKER_(2, constness, \
+ Method).Invoke(::std::forward<GMOCK_ARG_(tn, 1, \
+ __VA_ARGS__)>(gmock_a1), \
+ ::std::forward<GMOCK_ARG_(tn, 2, __VA_ARGS__)>(gmock_a2)); \
} \
- ::testing::MockSpec<__VA_ARGS__>& \
+ ::testing::MockSpec<__VA_ARGS__> \
gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \
GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2) constness { \
GMOCK_MOCKER_(2, constness, Method).RegisterOwner(this); \
return GMOCK_MOCKER_(2, constness, Method).With(gmock_a1, gmock_a2); \
} \
+ ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \
+ const ::testing::internal::WithoutMatchers&, \
+ constness ::testing::internal::Function<__VA_ARGS__>* ) const { \
+ return ::testing::internal::AdjustConstness_##constness(this)-> \
+ gmock_##Method(::testing::A<GMOCK_ARG_(tn, 1, __VA_ARGS__)>(), \
+ ::testing::A<GMOCK_ARG_(tn, 2, __VA_ARGS__)>()); \
+ } \
mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(2, constness, \
Method)
// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
#define GMOCK_METHOD3_(tn, constness, ct, Method, ...) \
+ static_assert(3 == \
+ ::testing::internal::Function<__VA_ARGS__>::ArgumentCount, \
+ "MOCK_METHOD<N> must match argument count.");\
GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \
- GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, \
- GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2, \
- GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3) constness { \
- GTEST_COMPILE_ASSERT_((::testing::tuple_size< \
- tn ::testing::internal::Function<__VA_ARGS__>::ArgumentTuple>::value \
- == 3), \
- this_method_does_not_take_3_arguments); \
+ GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, GMOCK_ARG_(tn, 2, \
+ __VA_ARGS__) gmock_a2, GMOCK_ARG_(tn, 3, \
+ __VA_ARGS__) gmock_a3) constness { \
GMOCK_MOCKER_(3, constness, Method).SetOwnerAndName(this, #Method); \
- return GMOCK_MOCKER_(3, constness, Method).Invoke(gmock_a1, gmock_a2, \
- gmock_a3); \
+ return GMOCK_MOCKER_(3, constness, \
+ Method).Invoke(::std::forward<GMOCK_ARG_(tn, 1, \
+ __VA_ARGS__)>(gmock_a1), \
+ ::std::forward<GMOCK_ARG_(tn, 2, __VA_ARGS__)>(gmock_a2), \
+ ::std::forward<GMOCK_ARG_(tn, 3, __VA_ARGS__)>(gmock_a3)); \
} \
- ::testing::MockSpec<__VA_ARGS__>& \
+ ::testing::MockSpec<__VA_ARGS__> \
gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \
GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \
GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3) constness { \
@@ -433,25 +237,35 @@ using internal::FunctionMocker;
return GMOCK_MOCKER_(3, constness, Method).With(gmock_a1, gmock_a2, \
gmock_a3); \
} \
+ ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \
+ const ::testing::internal::WithoutMatchers&, \
+ constness ::testing::internal::Function<__VA_ARGS__>* ) const { \
+ return ::testing::internal::AdjustConstness_##constness(this)-> \
+ gmock_##Method(::testing::A<GMOCK_ARG_(tn, 1, __VA_ARGS__)>(), \
+ ::testing::A<GMOCK_ARG_(tn, 2, __VA_ARGS__)>(), \
+ ::testing::A<GMOCK_ARG_(tn, 3, __VA_ARGS__)>()); \
+ } \
mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(3, constness, \
Method)
// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
#define GMOCK_METHOD4_(tn, constness, ct, Method, ...) \
+ static_assert(4 == \
+ ::testing::internal::Function<__VA_ARGS__>::ArgumentCount, \
+ "MOCK_METHOD<N> must match argument count.");\
GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \
- GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, \
- GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2, \
- GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \
- GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4) constness { \
- GTEST_COMPILE_ASSERT_((::testing::tuple_size< \
- tn ::testing::internal::Function<__VA_ARGS__>::ArgumentTuple>::value \
- == 4), \
- this_method_does_not_take_4_arguments); \
+ GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, GMOCK_ARG_(tn, 2, \
+ __VA_ARGS__) gmock_a2, GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \
+ GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4) constness { \
GMOCK_MOCKER_(4, constness, Method).SetOwnerAndName(this, #Method); \
- return GMOCK_MOCKER_(4, constness, Method).Invoke(gmock_a1, gmock_a2, \
- gmock_a3, gmock_a4); \
+ return GMOCK_MOCKER_(4, constness, \
+ Method).Invoke(::std::forward<GMOCK_ARG_(tn, 1, \
+ __VA_ARGS__)>(gmock_a1), \
+ ::std::forward<GMOCK_ARG_(tn, 2, __VA_ARGS__)>(gmock_a2), \
+ ::std::forward<GMOCK_ARG_(tn, 3, __VA_ARGS__)>(gmock_a3), \
+ ::std::forward<GMOCK_ARG_(tn, 4, __VA_ARGS__)>(gmock_a4)); \
} \
- ::testing::MockSpec<__VA_ARGS__>& \
+ ::testing::MockSpec<__VA_ARGS__> \
gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \
GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \
GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \
@@ -460,26 +274,38 @@ using internal::FunctionMocker;
return GMOCK_MOCKER_(4, constness, Method).With(gmock_a1, gmock_a2, \
gmock_a3, gmock_a4); \
} \
+ ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \
+ const ::testing::internal::WithoutMatchers&, \
+ constness ::testing::internal::Function<__VA_ARGS__>* ) const { \
+ return ::testing::internal::AdjustConstness_##constness(this)-> \
+ gmock_##Method(::testing::A<GMOCK_ARG_(tn, 1, __VA_ARGS__)>(), \
+ ::testing::A<GMOCK_ARG_(tn, 2, __VA_ARGS__)>(), \
+ ::testing::A<GMOCK_ARG_(tn, 3, __VA_ARGS__)>(), \
+ ::testing::A<GMOCK_ARG_(tn, 4, __VA_ARGS__)>()); \
+ } \
mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(4, constness, \
Method)
// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
#define GMOCK_METHOD5_(tn, constness, ct, Method, ...) \
+ static_assert(5 == \
+ ::testing::internal::Function<__VA_ARGS__>::ArgumentCount, \
+ "MOCK_METHOD<N> must match argument count.");\
GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \
- GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, \
- GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2, \
- GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \
- GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, \
- GMOCK_ARG_(tn, 5, __VA_ARGS__) gmock_a5) constness { \
- GTEST_COMPILE_ASSERT_((::testing::tuple_size< \
- tn ::testing::internal::Function<__VA_ARGS__>::ArgumentTuple>::value \
- == 5), \
- this_method_does_not_take_5_arguments); \
+ GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, GMOCK_ARG_(tn, 2, \
+ __VA_ARGS__) gmock_a2, GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \
+ GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, GMOCK_ARG_(tn, 5, \
+ __VA_ARGS__) gmock_a5) constness { \
GMOCK_MOCKER_(5, constness, Method).SetOwnerAndName(this, #Method); \
- return GMOCK_MOCKER_(5, constness, Method).Invoke(gmock_a1, gmock_a2, \
- gmock_a3, gmock_a4, gmock_a5); \
+ return GMOCK_MOCKER_(5, constness, \
+ Method).Invoke(::std::forward<GMOCK_ARG_(tn, 1, \
+ __VA_ARGS__)>(gmock_a1), \
+ ::std::forward<GMOCK_ARG_(tn, 2, __VA_ARGS__)>(gmock_a2), \
+ ::std::forward<GMOCK_ARG_(tn, 3, __VA_ARGS__)>(gmock_a3), \
+ ::std::forward<GMOCK_ARG_(tn, 4, __VA_ARGS__)>(gmock_a4), \
+ ::std::forward<GMOCK_ARG_(tn, 5, __VA_ARGS__)>(gmock_a5)); \
} \
- ::testing::MockSpec<__VA_ARGS__>& \
+ ::testing::MockSpec<__VA_ARGS__> \
gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \
GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \
GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \
@@ -489,27 +315,41 @@ using internal::FunctionMocker;
return GMOCK_MOCKER_(5, constness, Method).With(gmock_a1, gmock_a2, \
gmock_a3, gmock_a4, gmock_a5); \
} \
+ ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \
+ const ::testing::internal::WithoutMatchers&, \
+ constness ::testing::internal::Function<__VA_ARGS__>* ) const { \
+ return ::testing::internal::AdjustConstness_##constness(this)-> \
+ gmock_##Method(::testing::A<GMOCK_ARG_(tn, 1, __VA_ARGS__)>(), \
+ ::testing::A<GMOCK_ARG_(tn, 2, __VA_ARGS__)>(), \
+ ::testing::A<GMOCK_ARG_(tn, 3, __VA_ARGS__)>(), \
+ ::testing::A<GMOCK_ARG_(tn, 4, __VA_ARGS__)>(), \
+ ::testing::A<GMOCK_ARG_(tn, 5, __VA_ARGS__)>()); \
+ } \
mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(5, constness, \
Method)
// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
#define GMOCK_METHOD6_(tn, constness, ct, Method, ...) \
+ static_assert(6 == \
+ ::testing::internal::Function<__VA_ARGS__>::ArgumentCount, \
+ "MOCK_METHOD<N> must match argument count.");\
GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \
- GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, \
- GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2, \
- GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \
- GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, \
- GMOCK_ARG_(tn, 5, __VA_ARGS__) gmock_a5, \
- GMOCK_ARG_(tn, 6, __VA_ARGS__) gmock_a6) constness { \
- GTEST_COMPILE_ASSERT_((::testing::tuple_size< \
- tn ::testing::internal::Function<__VA_ARGS__>::ArgumentTuple>::value \
- == 6), \
- this_method_does_not_take_6_arguments); \
+ GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, GMOCK_ARG_(tn, 2, \
+ __VA_ARGS__) gmock_a2, GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \
+ GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, GMOCK_ARG_(tn, 5, \
+ __VA_ARGS__) gmock_a5, GMOCK_ARG_(tn, 6, \
+ __VA_ARGS__) gmock_a6) constness { \
GMOCK_MOCKER_(6, constness, Method).SetOwnerAndName(this, #Method); \
- return GMOCK_MOCKER_(6, constness, Method).Invoke(gmock_a1, gmock_a2, \
- gmock_a3, gmock_a4, gmock_a5, gmock_a6); \
+ return GMOCK_MOCKER_(6, constness, \
+ Method).Invoke(::std::forward<GMOCK_ARG_(tn, 1, \
+ __VA_ARGS__)>(gmock_a1), \
+ ::std::forward<GMOCK_ARG_(tn, 2, __VA_ARGS__)>(gmock_a2), \
+ ::std::forward<GMOCK_ARG_(tn, 3, __VA_ARGS__)>(gmock_a3), \
+ ::std::forward<GMOCK_ARG_(tn, 4, __VA_ARGS__)>(gmock_a4), \
+ ::std::forward<GMOCK_ARG_(tn, 5, __VA_ARGS__)>(gmock_a5), \
+ ::std::forward<GMOCK_ARG_(tn, 6, __VA_ARGS__)>(gmock_a6)); \
} \
- ::testing::MockSpec<__VA_ARGS__>& \
+ ::testing::MockSpec<__VA_ARGS__> \
gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \
GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \
GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \
@@ -520,28 +360,43 @@ using internal::FunctionMocker;
return GMOCK_MOCKER_(6, constness, Method).With(gmock_a1, gmock_a2, \
gmock_a3, gmock_a4, gmock_a5, gmock_a6); \
} \
+ ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \
+ const ::testing::internal::WithoutMatchers&, \
+ constness ::testing::internal::Function<__VA_ARGS__>* ) const { \
+ return ::testing::internal::AdjustConstness_##constness(this)-> \
+ gmock_##Method(::testing::A<GMOCK_ARG_(tn, 1, __VA_ARGS__)>(), \
+ ::testing::A<GMOCK_ARG_(tn, 2, __VA_ARGS__)>(), \
+ ::testing::A<GMOCK_ARG_(tn, 3, __VA_ARGS__)>(), \
+ ::testing::A<GMOCK_ARG_(tn, 4, __VA_ARGS__)>(), \
+ ::testing::A<GMOCK_ARG_(tn, 5, __VA_ARGS__)>(), \
+ ::testing::A<GMOCK_ARG_(tn, 6, __VA_ARGS__)>()); \
+ } \
mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(6, constness, \
Method)
// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
#define GMOCK_METHOD7_(tn, constness, ct, Method, ...) \
+ static_assert(7 == \
+ ::testing::internal::Function<__VA_ARGS__>::ArgumentCount, \
+ "MOCK_METHOD<N> must match argument count.");\
GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \
- GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, \
- GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2, \
- GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \
- GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, \
- GMOCK_ARG_(tn, 5, __VA_ARGS__) gmock_a5, \
- GMOCK_ARG_(tn, 6, __VA_ARGS__) gmock_a6, \
- GMOCK_ARG_(tn, 7, __VA_ARGS__) gmock_a7) constness { \
- GTEST_COMPILE_ASSERT_((::testing::tuple_size< \
- tn ::testing::internal::Function<__VA_ARGS__>::ArgumentTuple>::value \
- == 7), \
- this_method_does_not_take_7_arguments); \
+ GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, GMOCK_ARG_(tn, 2, \
+ __VA_ARGS__) gmock_a2, GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \
+ GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, GMOCK_ARG_(tn, 5, \
+ __VA_ARGS__) gmock_a5, GMOCK_ARG_(tn, 6, __VA_ARGS__) gmock_a6, \
+ GMOCK_ARG_(tn, 7, __VA_ARGS__) gmock_a7) constness { \
GMOCK_MOCKER_(7, constness, Method).SetOwnerAndName(this, #Method); \
- return GMOCK_MOCKER_(7, constness, Method).Invoke(gmock_a1, gmock_a2, \
- gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7); \
+ return GMOCK_MOCKER_(7, constness, \
+ Method).Invoke(::std::forward<GMOCK_ARG_(tn, 1, \
+ __VA_ARGS__)>(gmock_a1), \
+ ::std::forward<GMOCK_ARG_(tn, 2, __VA_ARGS__)>(gmock_a2), \
+ ::std::forward<GMOCK_ARG_(tn, 3, __VA_ARGS__)>(gmock_a3), \
+ ::std::forward<GMOCK_ARG_(tn, 4, __VA_ARGS__)>(gmock_a4), \
+ ::std::forward<GMOCK_ARG_(tn, 5, __VA_ARGS__)>(gmock_a5), \
+ ::std::forward<GMOCK_ARG_(tn, 6, __VA_ARGS__)>(gmock_a6), \
+ ::std::forward<GMOCK_ARG_(tn, 7, __VA_ARGS__)>(gmock_a7)); \
} \
- ::testing::MockSpec<__VA_ARGS__>& \
+ ::testing::MockSpec<__VA_ARGS__> \
gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \
GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \
GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \
@@ -553,29 +408,46 @@ using internal::FunctionMocker;
return GMOCK_MOCKER_(7, constness, Method).With(gmock_a1, gmock_a2, \
gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7); \
} \
+ ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \
+ const ::testing::internal::WithoutMatchers&, \
+ constness ::testing::internal::Function<__VA_ARGS__>* ) const { \
+ return ::testing::internal::AdjustConstness_##constness(this)-> \
+ gmock_##Method(::testing::A<GMOCK_ARG_(tn, 1, __VA_ARGS__)>(), \
+ ::testing::A<GMOCK_ARG_(tn, 2, __VA_ARGS__)>(), \
+ ::testing::A<GMOCK_ARG_(tn, 3, __VA_ARGS__)>(), \
+ ::testing::A<GMOCK_ARG_(tn, 4, __VA_ARGS__)>(), \
+ ::testing::A<GMOCK_ARG_(tn, 5, __VA_ARGS__)>(), \
+ ::testing::A<GMOCK_ARG_(tn, 6, __VA_ARGS__)>(), \
+ ::testing::A<GMOCK_ARG_(tn, 7, __VA_ARGS__)>()); \
+ } \
mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(7, constness, \
Method)
// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
#define GMOCK_METHOD8_(tn, constness, ct, Method, ...) \
+ static_assert(8 == \
+ ::testing::internal::Function<__VA_ARGS__>::ArgumentCount, \
+ "MOCK_METHOD<N> must match argument count.");\
GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \
- GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, \
- GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2, \
- GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \
- GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, \
- GMOCK_ARG_(tn, 5, __VA_ARGS__) gmock_a5, \
- GMOCK_ARG_(tn, 6, __VA_ARGS__) gmock_a6, \
- GMOCK_ARG_(tn, 7, __VA_ARGS__) gmock_a7, \
- GMOCK_ARG_(tn, 8, __VA_ARGS__) gmock_a8) constness { \
- GTEST_COMPILE_ASSERT_((::testing::tuple_size< \
- tn ::testing::internal::Function<__VA_ARGS__>::ArgumentTuple>::value \
- == 8), \
- this_method_does_not_take_8_arguments); \
+ GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, GMOCK_ARG_(tn, 2, \
+ __VA_ARGS__) gmock_a2, GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \
+ GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, GMOCK_ARG_(tn, 5, \
+ __VA_ARGS__) gmock_a5, GMOCK_ARG_(tn, 6, __VA_ARGS__) gmock_a6, \
+ GMOCK_ARG_(tn, 7, __VA_ARGS__) gmock_a7, GMOCK_ARG_(tn, 8, \
+ __VA_ARGS__) gmock_a8) constness { \
GMOCK_MOCKER_(8, constness, Method).SetOwnerAndName(this, #Method); \
- return GMOCK_MOCKER_(8, constness, Method).Invoke(gmock_a1, gmock_a2, \
- gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7, gmock_a8); \
+ return GMOCK_MOCKER_(8, constness, \
+ Method).Invoke(::std::forward<GMOCK_ARG_(tn, 1, \
+ __VA_ARGS__)>(gmock_a1), \
+ ::std::forward<GMOCK_ARG_(tn, 2, __VA_ARGS__)>(gmock_a2), \
+ ::std::forward<GMOCK_ARG_(tn, 3, __VA_ARGS__)>(gmock_a3), \
+ ::std::forward<GMOCK_ARG_(tn, 4, __VA_ARGS__)>(gmock_a4), \
+ ::std::forward<GMOCK_ARG_(tn, 5, __VA_ARGS__)>(gmock_a5), \
+ ::std::forward<GMOCK_ARG_(tn, 6, __VA_ARGS__)>(gmock_a6), \
+ ::std::forward<GMOCK_ARG_(tn, 7, __VA_ARGS__)>(gmock_a7), \
+ ::std::forward<GMOCK_ARG_(tn, 8, __VA_ARGS__)>(gmock_a8)); \
} \
- ::testing::MockSpec<__VA_ARGS__>& \
+ ::testing::MockSpec<__VA_ARGS__> \
gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \
GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \
GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \
@@ -588,31 +460,49 @@ using internal::FunctionMocker;
return GMOCK_MOCKER_(8, constness, Method).With(gmock_a1, gmock_a2, \
gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7, gmock_a8); \
} \
+ ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \
+ const ::testing::internal::WithoutMatchers&, \
+ constness ::testing::internal::Function<__VA_ARGS__>* ) const { \
+ return ::testing::internal::AdjustConstness_##constness(this)-> \
+ gmock_##Method(::testing::A<GMOCK_ARG_(tn, 1, __VA_ARGS__)>(), \
+ ::testing::A<GMOCK_ARG_(tn, 2, __VA_ARGS__)>(), \
+ ::testing::A<GMOCK_ARG_(tn, 3, __VA_ARGS__)>(), \
+ ::testing::A<GMOCK_ARG_(tn, 4, __VA_ARGS__)>(), \
+ ::testing::A<GMOCK_ARG_(tn, 5, __VA_ARGS__)>(), \
+ ::testing::A<GMOCK_ARG_(tn, 6, __VA_ARGS__)>(), \
+ ::testing::A<GMOCK_ARG_(tn, 7, __VA_ARGS__)>(), \
+ ::testing::A<GMOCK_ARG_(tn, 8, __VA_ARGS__)>()); \
+ } \
mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(8, constness, \
Method)
// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
#define GMOCK_METHOD9_(tn, constness, ct, Method, ...) \
+ static_assert(9 == \
+ ::testing::internal::Function<__VA_ARGS__>::ArgumentCount, \
+ "MOCK_METHOD<N> must match argument count.");\
GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \
- GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, \
- GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2, \
- GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \
- GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, \
- GMOCK_ARG_(tn, 5, __VA_ARGS__) gmock_a5, \
- GMOCK_ARG_(tn, 6, __VA_ARGS__) gmock_a6, \
- GMOCK_ARG_(tn, 7, __VA_ARGS__) gmock_a7, \
- GMOCK_ARG_(tn, 8, __VA_ARGS__) gmock_a8, \
- GMOCK_ARG_(tn, 9, __VA_ARGS__) gmock_a9) constness { \
- GTEST_COMPILE_ASSERT_((::testing::tuple_size< \
- tn ::testing::internal::Function<__VA_ARGS__>::ArgumentTuple>::value \
- == 9), \
- this_method_does_not_take_9_arguments); \
+ GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, GMOCK_ARG_(tn, 2, \
+ __VA_ARGS__) gmock_a2, GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \
+ GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, GMOCK_ARG_(tn, 5, \
+ __VA_ARGS__) gmock_a5, GMOCK_ARG_(tn, 6, __VA_ARGS__) gmock_a6, \
+ GMOCK_ARG_(tn, 7, __VA_ARGS__) gmock_a7, GMOCK_ARG_(tn, 8, \
+ __VA_ARGS__) gmock_a8, GMOCK_ARG_(tn, 9, \
+ __VA_ARGS__) gmock_a9) constness { \
GMOCK_MOCKER_(9, constness, Method).SetOwnerAndName(this, #Method); \
- return GMOCK_MOCKER_(9, constness, Method).Invoke(gmock_a1, gmock_a2, \
- gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7, gmock_a8, \
- gmock_a9); \
+ return GMOCK_MOCKER_(9, constness, \
+ Method).Invoke(::std::forward<GMOCK_ARG_(tn, 1, \
+ __VA_ARGS__)>(gmock_a1), \
+ ::std::forward<GMOCK_ARG_(tn, 2, __VA_ARGS__)>(gmock_a2), \
+ ::std::forward<GMOCK_ARG_(tn, 3, __VA_ARGS__)>(gmock_a3), \
+ ::std::forward<GMOCK_ARG_(tn, 4, __VA_ARGS__)>(gmock_a4), \
+ ::std::forward<GMOCK_ARG_(tn, 5, __VA_ARGS__)>(gmock_a5), \
+ ::std::forward<GMOCK_ARG_(tn, 6, __VA_ARGS__)>(gmock_a6), \
+ ::std::forward<GMOCK_ARG_(tn, 7, __VA_ARGS__)>(gmock_a7), \
+ ::std::forward<GMOCK_ARG_(tn, 8, __VA_ARGS__)>(gmock_a8), \
+ ::std::forward<GMOCK_ARG_(tn, 9, __VA_ARGS__)>(gmock_a9)); \
} \
- ::testing::MockSpec<__VA_ARGS__>& \
+ ::testing::MockSpec<__VA_ARGS__> \
gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \
GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \
GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \
@@ -627,32 +517,51 @@ using internal::FunctionMocker;
gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7, gmock_a8, \
gmock_a9); \
} \
+ ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \
+ const ::testing::internal::WithoutMatchers&, \
+ constness ::testing::internal::Function<__VA_ARGS__>* ) const { \
+ return ::testing::internal::AdjustConstness_##constness(this)-> \
+ gmock_##Method(::testing::A<GMOCK_ARG_(tn, 1, __VA_ARGS__)>(), \
+ ::testing::A<GMOCK_ARG_(tn, 2, __VA_ARGS__)>(), \
+ ::testing::A<GMOCK_ARG_(tn, 3, __VA_ARGS__)>(), \
+ ::testing::A<GMOCK_ARG_(tn, 4, __VA_ARGS__)>(), \
+ ::testing::A<GMOCK_ARG_(tn, 5, __VA_ARGS__)>(), \
+ ::testing::A<GMOCK_ARG_(tn, 6, __VA_ARGS__)>(), \
+ ::testing::A<GMOCK_ARG_(tn, 7, __VA_ARGS__)>(), \
+ ::testing::A<GMOCK_ARG_(tn, 8, __VA_ARGS__)>(), \
+ ::testing::A<GMOCK_ARG_(tn, 9, __VA_ARGS__)>()); \
+ } \
mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(9, constness, \
Method)
// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
#define GMOCK_METHOD10_(tn, constness, ct, Method, ...) \
+ static_assert(10 == \
+ ::testing::internal::Function<__VA_ARGS__>::ArgumentCount, \
+ "MOCK_METHOD<N> must match argument count.");\
GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \
- GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, \
- GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2, \
- GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \
- GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, \
- GMOCK_ARG_(tn, 5, __VA_ARGS__) gmock_a5, \
- GMOCK_ARG_(tn, 6, __VA_ARGS__) gmock_a6, \
- GMOCK_ARG_(tn, 7, __VA_ARGS__) gmock_a7, \
- GMOCK_ARG_(tn, 8, __VA_ARGS__) gmock_a8, \
- GMOCK_ARG_(tn, 9, __VA_ARGS__) gmock_a9, \
- GMOCK_ARG_(tn, 10, __VA_ARGS__) gmock_a10) constness { \
- GTEST_COMPILE_ASSERT_((::testing::tuple_size< \
- tn ::testing::internal::Function<__VA_ARGS__>::ArgumentTuple>::value \
- == 10), \
- this_method_does_not_take_10_arguments); \
+ GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, GMOCK_ARG_(tn, 2, \
+ __VA_ARGS__) gmock_a2, GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \
+ GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, GMOCK_ARG_(tn, 5, \
+ __VA_ARGS__) gmock_a5, GMOCK_ARG_(tn, 6, __VA_ARGS__) gmock_a6, \
+ GMOCK_ARG_(tn, 7, __VA_ARGS__) gmock_a7, GMOCK_ARG_(tn, 8, \
+ __VA_ARGS__) gmock_a8, GMOCK_ARG_(tn, 9, __VA_ARGS__) gmock_a9, \
+ GMOCK_ARG_(tn, 10, __VA_ARGS__) gmock_a10) constness { \
GMOCK_MOCKER_(10, constness, Method).SetOwnerAndName(this, #Method); \
- return GMOCK_MOCKER_(10, constness, Method).Invoke(gmock_a1, gmock_a2, \
- gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7, gmock_a8, gmock_a9, \
- gmock_a10); \
+ return GMOCK_MOCKER_(10, constness, \
+ Method).Invoke(::std::forward<GMOCK_ARG_(tn, 1, \
+ __VA_ARGS__)>(gmock_a1), \
+ ::std::forward<GMOCK_ARG_(tn, 2, __VA_ARGS__)>(gmock_a2), \
+ ::std::forward<GMOCK_ARG_(tn, 3, __VA_ARGS__)>(gmock_a3), \
+ ::std::forward<GMOCK_ARG_(tn, 4, __VA_ARGS__)>(gmock_a4), \
+ ::std::forward<GMOCK_ARG_(tn, 5, __VA_ARGS__)>(gmock_a5), \
+ ::std::forward<GMOCK_ARG_(tn, 6, __VA_ARGS__)>(gmock_a6), \
+ ::std::forward<GMOCK_ARG_(tn, 7, __VA_ARGS__)>(gmock_a7), \
+ ::std::forward<GMOCK_ARG_(tn, 8, __VA_ARGS__)>(gmock_a8), \
+ ::std::forward<GMOCK_ARG_(tn, 9, __VA_ARGS__)>(gmock_a9), \
+ ::std::forward<GMOCK_ARG_(tn, 10, __VA_ARGS__)>(gmock_a10)); \
} \
- ::testing::MockSpec<__VA_ARGS__>& \
+ ::testing::MockSpec<__VA_ARGS__> \
gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \
GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \
GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \
@@ -669,6 +578,21 @@ using internal::FunctionMocker;
gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7, gmock_a8, gmock_a9, \
gmock_a10); \
} \
+ ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \
+ const ::testing::internal::WithoutMatchers&, \
+ constness ::testing::internal::Function<__VA_ARGS__>* ) const { \
+ return ::testing::internal::AdjustConstness_##constness(this)-> \
+ gmock_##Method(::testing::A<GMOCK_ARG_(tn, 1, __VA_ARGS__)>(), \
+ ::testing::A<GMOCK_ARG_(tn, 2, __VA_ARGS__)>(), \
+ ::testing::A<GMOCK_ARG_(tn, 3, __VA_ARGS__)>(), \
+ ::testing::A<GMOCK_ARG_(tn, 4, __VA_ARGS__)>(), \
+ ::testing::A<GMOCK_ARG_(tn, 5, __VA_ARGS__)>(), \
+ ::testing::A<GMOCK_ARG_(tn, 6, __VA_ARGS__)>(), \
+ ::testing::A<GMOCK_ARG_(tn, 7, __VA_ARGS__)>(), \
+ ::testing::A<GMOCK_ARG_(tn, 8, __VA_ARGS__)>(), \
+ ::testing::A<GMOCK_ARG_(tn, 9, __VA_ARGS__)>(), \
+ ::testing::A<GMOCK_ARG_(tn, 10, __VA_ARGS__)>()); \
+ } \
mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(10, constness, \
Method)
@@ -823,273 +747,6 @@ using internal::FunctionMocker;
#define MOCK_CONST_METHOD10_T_WITH_CALLTYPE(ct, m, ...) \
GMOCK_METHOD10_(typename, const, ct, m, __VA_ARGS__)
-// A MockFunction<F> class has one mock method whose type is F. It is
-// useful when you just want your test code to emit some messages and
-// have Google Mock verify the right messages are sent (and perhaps at
-// the right times). For example, if you are exercising code:
-//
-// Foo(1);
-// Foo(2);
-// Foo(3);
-//
-// and want to verify that Foo(1) and Foo(3) both invoke
-// mock.Bar("a"), but Foo(2) doesn't invoke anything, you can write:
-//
-// TEST(FooTest, InvokesBarCorrectly) {
-// MyMock mock;
-// MockFunction<void(string check_point_name)> check;
-// {
-// InSequence s;
-//
-// EXPECT_CALL(mock, Bar("a"));
-// EXPECT_CALL(check, Call("1"));
-// EXPECT_CALL(check, Call("2"));
-// EXPECT_CALL(mock, Bar("a"));
-// }
-// Foo(1);
-// check.Call("1");
-// Foo(2);
-// check.Call("2");
-// Foo(3);
-// }
-//
-// The expectation spec says that the first Bar("a") must happen
-// before check point "1", the second Bar("a") must happen after check
-// point "2", and nothing should happen between the two check
-// points. The explicit check points make it easy to tell which
-// Bar("a") is called by which call to Foo().
-//
-// MockFunction<F> can also be used to exercise code that accepts
-// std::function<F> callbacks. To do so, use AsStdFunction() method
-// to create std::function proxy forwarding to original object's Call.
-// Example:
-//
-// TEST(FooTest, RunsCallbackWithBarArgument) {
-// MockFunction<int(string)> callback;
-// EXPECT_CALL(callback, Call("bar")).WillOnce(Return(1));
-// Foo(callback.AsStdFunction());
-// }
-template <typename F>
-class MockFunction;
-
-template <typename R>
-class MockFunction<R()> {
- public:
- MockFunction() {}
-
- MOCK_METHOD0_T(Call, R());
-
-#if GTEST_HAS_STD_FUNCTION_
- std::function<R()> AsStdFunction() {
- return [this]() -> R {
- return this->Call();
- };
- }
-#endif // GTEST_HAS_STD_FUNCTION_
-
- private:
- GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction);
-};
-
-template <typename R, typename A0>
-class MockFunction<R(A0)> {
- public:
- MockFunction() {}
-
- MOCK_METHOD1_T(Call, R(A0));
-
-#if GTEST_HAS_STD_FUNCTION_
- std::function<R(A0)> AsStdFunction() {
- return [this](A0 a0) -> R {
- return this->Call(a0);
- };
- }
-#endif // GTEST_HAS_STD_FUNCTION_
-
- private:
- GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction);
-};
-
-template <typename R, typename A0, typename A1>
-class MockFunction<R(A0, A1)> {
- public:
- MockFunction() {}
-
- MOCK_METHOD2_T(Call, R(A0, A1));
-
-#if GTEST_HAS_STD_FUNCTION_
- std::function<R(A0, A1)> AsStdFunction() {
- return [this](A0 a0, A1 a1) -> R {
- return this->Call(a0, a1);
- };
- }
-#endif // GTEST_HAS_STD_FUNCTION_
-
- private:
- GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction);
-};
-
-template <typename R, typename A0, typename A1, typename A2>
-class MockFunction<R(A0, A1, A2)> {
- public:
- MockFunction() {}
-
- MOCK_METHOD3_T(Call, R(A0, A1, A2));
-
-#if GTEST_HAS_STD_FUNCTION_
- std::function<R(A0, A1, A2)> AsStdFunction() {
- return [this](A0 a0, A1 a1, A2 a2) -> R {
- return this->Call(a0, a1, a2);
- };
- }
-#endif // GTEST_HAS_STD_FUNCTION_
-
- private:
- GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction);
-};
-
-template <typename R, typename A0, typename A1, typename A2, typename A3>
-class MockFunction<R(A0, A1, A2, A3)> {
- public:
- MockFunction() {}
-
- MOCK_METHOD4_T(Call, R(A0, A1, A2, A3));
-
-#if GTEST_HAS_STD_FUNCTION_
- std::function<R(A0, A1, A2, A3)> AsStdFunction() {
- return [this](A0 a0, A1 a1, A2 a2, A3 a3) -> R {
- return this->Call(a0, a1, a2, a3);
- };
- }
-#endif // GTEST_HAS_STD_FUNCTION_
-
- private:
- GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction);
-};
-
-template <typename R, typename A0, typename A1, typename A2, typename A3,
- typename A4>
-class MockFunction<R(A0, A1, A2, A3, A4)> {
- public:
- MockFunction() {}
-
- MOCK_METHOD5_T(Call, R(A0, A1, A2, A3, A4));
-
-#if GTEST_HAS_STD_FUNCTION_
- std::function<R(A0, A1, A2, A3, A4)> AsStdFunction() {
- return [this](A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) -> R {
- return this->Call(a0, a1, a2, a3, a4);
- };
- }
-#endif // GTEST_HAS_STD_FUNCTION_
-
- private:
- GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction);
-};
-
-template <typename R, typename A0, typename A1, typename A2, typename A3,
- typename A4, typename A5>
-class MockFunction<R(A0, A1, A2, A3, A4, A5)> {
- public:
- MockFunction() {}
-
- MOCK_METHOD6_T(Call, R(A0, A1, A2, A3, A4, A5));
-
-#if GTEST_HAS_STD_FUNCTION_
- std::function<R(A0, A1, A2, A3, A4, A5)> AsStdFunction() {
- return [this](A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) -> R {
- return this->Call(a0, a1, a2, a3, a4, a5);
- };
- }
-#endif // GTEST_HAS_STD_FUNCTION_
-
- private:
- GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction);
-};
-
-template <typename R, typename A0, typename A1, typename A2, typename A3,
- typename A4, typename A5, typename A6>
-class MockFunction<R(A0, A1, A2, A3, A4, A5, A6)> {
- public:
- MockFunction() {}
-
- MOCK_METHOD7_T(Call, R(A0, A1, A2, A3, A4, A5, A6));
-
-#if GTEST_HAS_STD_FUNCTION_
- std::function<R(A0, A1, A2, A3, A4, A5, A6)> AsStdFunction() {
- return [this](A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) -> R {
- return this->Call(a0, a1, a2, a3, a4, a5, a6);
- };
- }
-#endif // GTEST_HAS_STD_FUNCTION_
-
- private:
- GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction);
-};
-
-template <typename R, typename A0, typename A1, typename A2, typename A3,
- typename A4, typename A5, typename A6, typename A7>
-class MockFunction<R(A0, A1, A2, A3, A4, A5, A6, A7)> {
- public:
- MockFunction() {}
-
- MOCK_METHOD8_T(Call, R(A0, A1, A2, A3, A4, A5, A6, A7));
-
-#if GTEST_HAS_STD_FUNCTION_
- std::function<R(A0, A1, A2, A3, A4, A5, A6, A7)> AsStdFunction() {
- return [this](A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) -> R {
- return this->Call(a0, a1, a2, a3, a4, a5, a6, a7);
- };
- }
-#endif // GTEST_HAS_STD_FUNCTION_
-
- private:
- GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction);
-};
-
-template <typename R, typename A0, typename A1, typename A2, typename A3,
- typename A4, typename A5, typename A6, typename A7, typename A8>
-class MockFunction<R(A0, A1, A2, A3, A4, A5, A6, A7, A8)> {
- public:
- MockFunction() {}
-
- MOCK_METHOD9_T(Call, R(A0, A1, A2, A3, A4, A5, A6, A7, A8));
-
-#if GTEST_HAS_STD_FUNCTION_
- std::function<R(A0, A1, A2, A3, A4, A5, A6, A7, A8)> AsStdFunction() {
- return [this](A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7,
- A8 a8) -> R {
- return this->Call(a0, a1, a2, a3, a4, a5, a6, a7, a8);
- };
- }
-#endif // GTEST_HAS_STD_FUNCTION_
-
- private:
- GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction);
-};
-
-template <typename R, typename A0, typename A1, typename A2, typename A3,
- typename A4, typename A5, typename A6, typename A7, typename A8,
- typename A9>
-class MockFunction<R(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9)> {
- public:
- MockFunction() {}
-
- MOCK_METHOD10_T(Call, R(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9));
-
-#if GTEST_HAS_STD_FUNCTION_
- std::function<R(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9)> AsStdFunction() {
- return [this](A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7,
- A8 a8, A9 a9) -> R {
- return this->Call(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
- };
- }
-#endif // GTEST_HAS_STD_FUNCTION_
-
- private:
- GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction);
-};
-
} // namespace testing
#endif // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_FUNCTION_MOCKERS_H_
diff --git a/extern/gmock/include/gmock/gmock-generated-matchers.h b/extern/gmock/include/gmock/gmock-generated-matchers.h
index 57056fd91d2..690a57f1c9f 100644
--- a/extern/gmock/include/gmock/gmock-generated-matchers.h
+++ b/extern/gmock/include/gmock/gmock-generated-matchers.h
@@ -35,1134 +35,18 @@
//
// This file implements some commonly used variadic matchers.
+// GOOGLETEST_CM0002 DO NOT DELETE
+
#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_MATCHERS_H_
#define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_MATCHERS_H_
#include <iterator>
#include <sstream>
#include <string>
+#include <utility>
#include <vector>
#include "gmock/gmock-matchers.h"
-namespace testing {
-namespace internal {
-
-// The type of the i-th (0-based) field of Tuple.
-#define GMOCK_FIELD_TYPE_(Tuple, i) \
- typename ::testing::tuple_element<i, Tuple>::type
-
-// TupleFields<Tuple, k0, ..., kn> is for selecting fields from a
-// tuple of type Tuple. It has two members:
-//
-// type: a tuple type whose i-th field is the ki-th field of Tuple.
-// GetSelectedFields(t): returns fields k0, ..., and kn of t as a tuple.
-//
-// For example, in class TupleFields<tuple<bool, char, int>, 2, 0>, we have:
-//
-// type is tuple<int, bool>, and
-// GetSelectedFields(make_tuple(true, 'a', 42)) is (42, true).
-
-template <class Tuple, int k0 = -1, int k1 = -1, int k2 = -1, int k3 = -1,
- int k4 = -1, int k5 = -1, int k6 = -1, int k7 = -1, int k8 = -1,
- int k9 = -1>
-class TupleFields;
-
-// This generic version is used when there are 10 selectors.
-template <class Tuple, int k0, int k1, int k2, int k3, int k4, int k5, int k6,
- int k7, int k8, int k9>
-class TupleFields {
- public:
- typedef ::testing::tuple<GMOCK_FIELD_TYPE_(Tuple, k0),
- GMOCK_FIELD_TYPE_(Tuple, k1), GMOCK_FIELD_TYPE_(Tuple, k2),
- GMOCK_FIELD_TYPE_(Tuple, k3), GMOCK_FIELD_TYPE_(Tuple, k4),
- GMOCK_FIELD_TYPE_(Tuple, k5), GMOCK_FIELD_TYPE_(Tuple, k6),
- GMOCK_FIELD_TYPE_(Tuple, k7), GMOCK_FIELD_TYPE_(Tuple, k8),
- GMOCK_FIELD_TYPE_(Tuple, k9)> type;
- static type GetSelectedFields(const Tuple& t) {
- return type(get<k0>(t), get<k1>(t), get<k2>(t), get<k3>(t), get<k4>(t),
- get<k5>(t), get<k6>(t), get<k7>(t), get<k8>(t), get<k9>(t));
- }
-};
-
-// The following specialization is used for 0 ~ 9 selectors.
-
-template <class Tuple>
-class TupleFields<Tuple, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1> {
- public:
- typedef ::testing::tuple<> type;
- static type GetSelectedFields(const Tuple& /* t */) {
- return type();
- }
-};
-
-template <class Tuple, int k0>
-class TupleFields<Tuple, k0, -1, -1, -1, -1, -1, -1, -1, -1, -1> {
- public:
- typedef ::testing::tuple<GMOCK_FIELD_TYPE_(Tuple, k0)> type;
- static type GetSelectedFields(const Tuple& t) {
- return type(get<k0>(t));
- }
-};
-
-template <class Tuple, int k0, int k1>
-class TupleFields<Tuple, k0, k1, -1, -1, -1, -1, -1, -1, -1, -1> {
- public:
- typedef ::testing::tuple<GMOCK_FIELD_TYPE_(Tuple, k0),
- GMOCK_FIELD_TYPE_(Tuple, k1)> type;
- static type GetSelectedFields(const Tuple& t) {
- return type(get<k0>(t), get<k1>(t));
- }
-};
-
-template <class Tuple, int k0, int k1, int k2>
-class TupleFields<Tuple, k0, k1, k2, -1, -1, -1, -1, -1, -1, -1> {
- public:
- typedef ::testing::tuple<GMOCK_FIELD_TYPE_(Tuple, k0),
- GMOCK_FIELD_TYPE_(Tuple, k1), GMOCK_FIELD_TYPE_(Tuple, k2)> type;
- static type GetSelectedFields(const Tuple& t) {
- return type(get<k0>(t), get<k1>(t), get<k2>(t));
- }
-};
-
-template <class Tuple, int k0, int k1, int k2, int k3>
-class TupleFields<Tuple, k0, k1, k2, k3, -1, -1, -1, -1, -1, -1> {
- public:
- typedef ::testing::tuple<GMOCK_FIELD_TYPE_(Tuple, k0),
- GMOCK_FIELD_TYPE_(Tuple, k1), GMOCK_FIELD_TYPE_(Tuple, k2),
- GMOCK_FIELD_TYPE_(Tuple, k3)> type;
- static type GetSelectedFields(const Tuple& t) {
- return type(get<k0>(t), get<k1>(t), get<k2>(t), get<k3>(t));
- }
-};
-
-template <class Tuple, int k0, int k1, int k2, int k3, int k4>
-class TupleFields<Tuple, k0, k1, k2, k3, k4, -1, -1, -1, -1, -1> {
- public:
- typedef ::testing::tuple<GMOCK_FIELD_TYPE_(Tuple, k0),
- GMOCK_FIELD_TYPE_(Tuple, k1), GMOCK_FIELD_TYPE_(Tuple, k2),
- GMOCK_FIELD_TYPE_(Tuple, k3), GMOCK_FIELD_TYPE_(Tuple, k4)> type;
- static type GetSelectedFields(const Tuple& t) {
- return type(get<k0>(t), get<k1>(t), get<k2>(t), get<k3>(t), get<k4>(t));
- }
-};
-
-template <class Tuple, int k0, int k1, int k2, int k3, int k4, int k5>
-class TupleFields<Tuple, k0, k1, k2, k3, k4, k5, -1, -1, -1, -1> {
- public:
- typedef ::testing::tuple<GMOCK_FIELD_TYPE_(Tuple, k0),
- GMOCK_FIELD_TYPE_(Tuple, k1), GMOCK_FIELD_TYPE_(Tuple, k2),
- GMOCK_FIELD_TYPE_(Tuple, k3), GMOCK_FIELD_TYPE_(Tuple, k4),
- GMOCK_FIELD_TYPE_(Tuple, k5)> type;
- static type GetSelectedFields(const Tuple& t) {
- return type(get<k0>(t), get<k1>(t), get<k2>(t), get<k3>(t), get<k4>(t),
- get<k5>(t));
- }
-};
-
-template <class Tuple, int k0, int k1, int k2, int k3, int k4, int k5, int k6>
-class TupleFields<Tuple, k0, k1, k2, k3, k4, k5, k6, -1, -1, -1> {
- public:
- typedef ::testing::tuple<GMOCK_FIELD_TYPE_(Tuple, k0),
- GMOCK_FIELD_TYPE_(Tuple, k1), GMOCK_FIELD_TYPE_(Tuple, k2),
- GMOCK_FIELD_TYPE_(Tuple, k3), GMOCK_FIELD_TYPE_(Tuple, k4),
- GMOCK_FIELD_TYPE_(Tuple, k5), GMOCK_FIELD_TYPE_(Tuple, k6)> type;
- static type GetSelectedFields(const Tuple& t) {
- return type(get<k0>(t), get<k1>(t), get<k2>(t), get<k3>(t), get<k4>(t),
- get<k5>(t), get<k6>(t));
- }
-};
-
-template <class Tuple, int k0, int k1, int k2, int k3, int k4, int k5, int k6,
- int k7>
-class TupleFields<Tuple, k0, k1, k2, k3, k4, k5, k6, k7, -1, -1> {
- public:
- typedef ::testing::tuple<GMOCK_FIELD_TYPE_(Tuple, k0),
- GMOCK_FIELD_TYPE_(Tuple, k1), GMOCK_FIELD_TYPE_(Tuple, k2),
- GMOCK_FIELD_TYPE_(Tuple, k3), GMOCK_FIELD_TYPE_(Tuple, k4),
- GMOCK_FIELD_TYPE_(Tuple, k5), GMOCK_FIELD_TYPE_(Tuple, k6),
- GMOCK_FIELD_TYPE_(Tuple, k7)> type;
- static type GetSelectedFields(const Tuple& t) {
- return type(get<k0>(t), get<k1>(t), get<k2>(t), get<k3>(t), get<k4>(t),
- get<k5>(t), get<k6>(t), get<k7>(t));
- }
-};
-
-template <class Tuple, int k0, int k1, int k2, int k3, int k4, int k5, int k6,
- int k7, int k8>
-class TupleFields<Tuple, k0, k1, k2, k3, k4, k5, k6, k7, k8, -1> {
- public:
- typedef ::testing::tuple<GMOCK_FIELD_TYPE_(Tuple, k0),
- GMOCK_FIELD_TYPE_(Tuple, k1), GMOCK_FIELD_TYPE_(Tuple, k2),
- GMOCK_FIELD_TYPE_(Tuple, k3), GMOCK_FIELD_TYPE_(Tuple, k4),
- GMOCK_FIELD_TYPE_(Tuple, k5), GMOCK_FIELD_TYPE_(Tuple, k6),
- GMOCK_FIELD_TYPE_(Tuple, k7), GMOCK_FIELD_TYPE_(Tuple, k8)> type;
- static type GetSelectedFields(const Tuple& t) {
- return type(get<k0>(t), get<k1>(t), get<k2>(t), get<k3>(t), get<k4>(t),
- get<k5>(t), get<k6>(t), get<k7>(t), get<k8>(t));
- }
-};
-
-#undef GMOCK_FIELD_TYPE_
-
-// Implements the Args() matcher.
-template <class ArgsTuple, int k0 = -1, int k1 = -1, int k2 = -1, int k3 = -1,
- int k4 = -1, int k5 = -1, int k6 = -1, int k7 = -1, int k8 = -1,
- int k9 = -1>
-class ArgsMatcherImpl : public MatcherInterface<ArgsTuple> {
- public:
- // ArgsTuple may have top-level const or reference modifiers.
- typedef GTEST_REMOVE_REFERENCE_AND_CONST_(ArgsTuple) RawArgsTuple;
- typedef typename internal::TupleFields<RawArgsTuple, k0, k1, k2, k3, k4, k5,
- k6, k7, k8, k9>::type SelectedArgs;
- typedef Matcher<const SelectedArgs&> MonomorphicInnerMatcher;
-
- template <typename InnerMatcher>
- explicit ArgsMatcherImpl(const InnerMatcher& inner_matcher)
- : inner_matcher_(SafeMatcherCast<const SelectedArgs&>(inner_matcher)) {}
-
- virtual bool MatchAndExplain(ArgsTuple args,
- MatchResultListener* listener) const {
- const SelectedArgs& selected_args = GetSelectedArgs(args);
- if (!listener->IsInterested())
- return inner_matcher_.Matches(selected_args);
-
- PrintIndices(listener->stream());
- *listener << "are " << PrintToString(selected_args);
-
- StringMatchResultListener inner_listener;
- const bool match = inner_matcher_.MatchAndExplain(selected_args,
- &inner_listener);
- PrintIfNotEmpty(inner_listener.str(), listener->stream());
- return match;
- }
-
- virtual void DescribeTo(::std::ostream* os) const {
- *os << "are a tuple ";
- PrintIndices(os);
- inner_matcher_.DescribeTo(os);
- }
-
- virtual void DescribeNegationTo(::std::ostream* os) const {
- *os << "are a tuple ";
- PrintIndices(os);
- inner_matcher_.DescribeNegationTo(os);
- }
-
- private:
- static SelectedArgs GetSelectedArgs(ArgsTuple args) {
- return TupleFields<RawArgsTuple, k0, k1, k2, k3, k4, k5, k6, k7, k8,
- k9>::GetSelectedFields(args);
- }
-
- // Prints the indices of the selected fields.
- static void PrintIndices(::std::ostream* os) {
- *os << "whose fields (";
- const int indices[10] = { k0, k1, k2, k3, k4, k5, k6, k7, k8, k9 };
- for (int i = 0; i < 10; i++) {
- if (indices[i] < 0)
- break;
-
- if (i >= 1)
- *os << ", ";
-
- *os << "#" << indices[i];
- }
- *os << ") ";
- }
-
- const MonomorphicInnerMatcher inner_matcher_;
-
- GTEST_DISALLOW_ASSIGN_(ArgsMatcherImpl);
-};
-
-template <class InnerMatcher, int k0 = -1, int k1 = -1, int k2 = -1,
- int k3 = -1, int k4 = -1, int k5 = -1, int k6 = -1, int k7 = -1,
- int k8 = -1, int k9 = -1>
-class ArgsMatcher {
- public:
- explicit ArgsMatcher(const InnerMatcher& inner_matcher)
- : inner_matcher_(inner_matcher) {}
-
- template <typename ArgsTuple>
- operator Matcher<ArgsTuple>() const {
- return MakeMatcher(new ArgsMatcherImpl<ArgsTuple, k0, k1, k2, k3, k4, k5,
- k6, k7, k8, k9>(inner_matcher_));
- }
-
- private:
- const InnerMatcher inner_matcher_;
-
- GTEST_DISALLOW_ASSIGN_(ArgsMatcher);
-};
-
-// A set of metafunctions for computing the result type of AllOf.
-// AllOf(m1, ..., mN) returns
-// AllOfResultN<decltype(m1), ..., decltype(mN)>::type.
-
-// Although AllOf isn't defined for one argument, AllOfResult1 is defined
-// to simplify the implementation.
-template <typename M1>
-struct AllOfResult1 {
- typedef M1 type;
-};
-
-template <typename M1, typename M2>
-struct AllOfResult2 {
- typedef BothOfMatcher<
- typename AllOfResult1<M1>::type,
- typename AllOfResult1<M2>::type
- > type;
-};
-
-template <typename M1, typename M2, typename M3>
-struct AllOfResult3 {
- typedef BothOfMatcher<
- typename AllOfResult1<M1>::type,
- typename AllOfResult2<M2, M3>::type
- > type;
-};
-
-template <typename M1, typename M2, typename M3, typename M4>
-struct AllOfResult4 {
- typedef BothOfMatcher<
- typename AllOfResult2<M1, M2>::type,
- typename AllOfResult2<M3, M4>::type
- > type;
-};
-
-template <typename M1, typename M2, typename M3, typename M4, typename M5>
-struct AllOfResult5 {
- typedef BothOfMatcher<
- typename AllOfResult2<M1, M2>::type,
- typename AllOfResult3<M3, M4, M5>::type
- > type;
-};
-
-template <typename M1, typename M2, typename M3, typename M4, typename M5,
- typename M6>
-struct AllOfResult6 {
- typedef BothOfMatcher<
- typename AllOfResult3<M1, M2, M3>::type,
- typename AllOfResult3<M4, M5, M6>::type
- > type;
-};
-
-template <typename M1, typename M2, typename M3, typename M4, typename M5,
- typename M6, typename M7>
-struct AllOfResult7 {
- typedef BothOfMatcher<
- typename AllOfResult3<M1, M2, M3>::type,
- typename AllOfResult4<M4, M5, M6, M7>::type
- > type;
-};
-
-template <typename M1, typename M2, typename M3, typename M4, typename M5,
- typename M6, typename M7, typename M8>
-struct AllOfResult8 {
- typedef BothOfMatcher<
- typename AllOfResult4<M1, M2, M3, M4>::type,
- typename AllOfResult4<M5, M6, M7, M8>::type
- > type;
-};
-
-template <typename M1, typename M2, typename M3, typename M4, typename M5,
- typename M6, typename M7, typename M8, typename M9>
-struct AllOfResult9 {
- typedef BothOfMatcher<
- typename AllOfResult4<M1, M2, M3, M4>::type,
- typename AllOfResult5<M5, M6, M7, M8, M9>::type
- > type;
-};
-
-template <typename M1, typename M2, typename M3, typename M4, typename M5,
- typename M6, typename M7, typename M8, typename M9, typename M10>
-struct AllOfResult10 {
- typedef BothOfMatcher<
- typename AllOfResult5<M1, M2, M3, M4, M5>::type,
- typename AllOfResult5<M6, M7, M8, M9, M10>::type
- > type;
-};
-
-// A set of metafunctions for computing the result type of AnyOf.
-// AnyOf(m1, ..., mN) returns
-// AnyOfResultN<decltype(m1), ..., decltype(mN)>::type.
-
-// Although AnyOf isn't defined for one argument, AnyOfResult1 is defined
-// to simplify the implementation.
-template <typename M1>
-struct AnyOfResult1 {
- typedef M1 type;
-};
-
-template <typename M1, typename M2>
-struct AnyOfResult2 {
- typedef EitherOfMatcher<
- typename AnyOfResult1<M1>::type,
- typename AnyOfResult1<M2>::type
- > type;
-};
-
-template <typename M1, typename M2, typename M3>
-struct AnyOfResult3 {
- typedef EitherOfMatcher<
- typename AnyOfResult1<M1>::type,
- typename AnyOfResult2<M2, M3>::type
- > type;
-};
-
-template <typename M1, typename M2, typename M3, typename M4>
-struct AnyOfResult4 {
- typedef EitherOfMatcher<
- typename AnyOfResult2<M1, M2>::type,
- typename AnyOfResult2<M3, M4>::type
- > type;
-};
-
-template <typename M1, typename M2, typename M3, typename M4, typename M5>
-struct AnyOfResult5 {
- typedef EitherOfMatcher<
- typename AnyOfResult2<M1, M2>::type,
- typename AnyOfResult3<M3, M4, M5>::type
- > type;
-};
-
-template <typename M1, typename M2, typename M3, typename M4, typename M5,
- typename M6>
-struct AnyOfResult6 {
- typedef EitherOfMatcher<
- typename AnyOfResult3<M1, M2, M3>::type,
- typename AnyOfResult3<M4, M5, M6>::type
- > type;
-};
-
-template <typename M1, typename M2, typename M3, typename M4, typename M5,
- typename M6, typename M7>
-struct AnyOfResult7 {
- typedef EitherOfMatcher<
- typename AnyOfResult3<M1, M2, M3>::type,
- typename AnyOfResult4<M4, M5, M6, M7>::type
- > type;
-};
-
-template <typename M1, typename M2, typename M3, typename M4, typename M5,
- typename M6, typename M7, typename M8>
-struct AnyOfResult8 {
- typedef EitherOfMatcher<
- typename AnyOfResult4<M1, M2, M3, M4>::type,
- typename AnyOfResult4<M5, M6, M7, M8>::type
- > type;
-};
-
-template <typename M1, typename M2, typename M3, typename M4, typename M5,
- typename M6, typename M7, typename M8, typename M9>
-struct AnyOfResult9 {
- typedef EitherOfMatcher<
- typename AnyOfResult4<M1, M2, M3, M4>::type,
- typename AnyOfResult5<M5, M6, M7, M8, M9>::type
- > type;
-};
-
-template <typename M1, typename M2, typename M3, typename M4, typename M5,
- typename M6, typename M7, typename M8, typename M9, typename M10>
-struct AnyOfResult10 {
- typedef EitherOfMatcher<
- typename AnyOfResult5<M1, M2, M3, M4, M5>::type,
- typename AnyOfResult5<M6, M7, M8, M9, M10>::type
- > type;
-};
-
-} // namespace internal
-
-// Args<N1, N2, ..., Nk>(a_matcher) matches a tuple if the selected
-// fields of it matches a_matcher. C++ doesn't support default
-// arguments for function templates, so we have to overload it.
-template <typename InnerMatcher>
-inline internal::ArgsMatcher<InnerMatcher>
-Args(const InnerMatcher& matcher) {
- return internal::ArgsMatcher<InnerMatcher>(matcher);
-}
-
-template <int k1, typename InnerMatcher>
-inline internal::ArgsMatcher<InnerMatcher, k1>
-Args(const InnerMatcher& matcher) {
- return internal::ArgsMatcher<InnerMatcher, k1>(matcher);
-}
-
-template <int k1, int k2, typename InnerMatcher>
-inline internal::ArgsMatcher<InnerMatcher, k1, k2>
-Args(const InnerMatcher& matcher) {
- return internal::ArgsMatcher<InnerMatcher, k1, k2>(matcher);
-}
-
-template <int k1, int k2, int k3, typename InnerMatcher>
-inline internal::ArgsMatcher<InnerMatcher, k1, k2, k3>
-Args(const InnerMatcher& matcher) {
- return internal::ArgsMatcher<InnerMatcher, k1, k2, k3>(matcher);
-}
-
-template <int k1, int k2, int k3, int k4, typename InnerMatcher>
-inline internal::ArgsMatcher<InnerMatcher, k1, k2, k3, k4>
-Args(const InnerMatcher& matcher) {
- return internal::ArgsMatcher<InnerMatcher, k1, k2, k3, k4>(matcher);
-}
-
-template <int k1, int k2, int k3, int k4, int k5, typename InnerMatcher>
-inline internal::ArgsMatcher<InnerMatcher, k1, k2, k3, k4, k5>
-Args(const InnerMatcher& matcher) {
- return internal::ArgsMatcher<InnerMatcher, k1, k2, k3, k4, k5>(matcher);
-}
-
-template <int k1, int k2, int k3, int k4, int k5, int k6, typename InnerMatcher>
-inline internal::ArgsMatcher<InnerMatcher, k1, k2, k3, k4, k5, k6>
-Args(const InnerMatcher& matcher) {
- return internal::ArgsMatcher<InnerMatcher, k1, k2, k3, k4, k5, k6>(matcher);
-}
-
-template <int k1, int k2, int k3, int k4, int k5, int k6, int k7,
- typename InnerMatcher>
-inline internal::ArgsMatcher<InnerMatcher, k1, k2, k3, k4, k5, k6, k7>
-Args(const InnerMatcher& matcher) {
- return internal::ArgsMatcher<InnerMatcher, k1, k2, k3, k4, k5, k6,
- k7>(matcher);
-}
-
-template <int k1, int k2, int k3, int k4, int k5, int k6, int k7, int k8,
- typename InnerMatcher>
-inline internal::ArgsMatcher<InnerMatcher, k1, k2, k3, k4, k5, k6, k7, k8>
-Args(const InnerMatcher& matcher) {
- return internal::ArgsMatcher<InnerMatcher, k1, k2, k3, k4, k5, k6, k7,
- k8>(matcher);
-}
-
-template <int k1, int k2, int k3, int k4, int k5, int k6, int k7, int k8,
- int k9, typename InnerMatcher>
-inline internal::ArgsMatcher<InnerMatcher, k1, k2, k3, k4, k5, k6, k7, k8, k9>
-Args(const InnerMatcher& matcher) {
- return internal::ArgsMatcher<InnerMatcher, k1, k2, k3, k4, k5, k6, k7, k8,
- k9>(matcher);
-}
-
-template <int k1, int k2, int k3, int k4, int k5, int k6, int k7, int k8,
- int k9, int k10, typename InnerMatcher>
-inline internal::ArgsMatcher<InnerMatcher, k1, k2, k3, k4, k5, k6, k7, k8, k9,
- k10>
-Args(const InnerMatcher& matcher) {
- return internal::ArgsMatcher<InnerMatcher, k1, k2, k3, k4, k5, k6, k7, k8,
- k9, k10>(matcher);
-}
-
-// ElementsAre(e_1, e_2, ... e_n) matches an STL-style container with
-// n elements, where the i-th element in the container must
-// match the i-th argument in the list. Each argument of
-// ElementsAre() can be either a value or a matcher. We support up to
-// 10 arguments.
-//
-// The use of DecayArray in the implementation allows ElementsAre()
-// to accept string literals, whose type is const char[N], but we
-// want to treat them as const char*.
-//
-// NOTE: Since ElementsAre() cares about the order of the elements, it
-// must not be used with containers whose elements's order is
-// undefined (e.g. hash_map).
-
-inline internal::ElementsAreMatcher<
- ::testing::tuple<> >
-ElementsAre() {
- typedef ::testing::tuple<> Args;
- return internal::ElementsAreMatcher<Args>(Args());
-}
-
-template <typename T1>
-inline internal::ElementsAreMatcher<
- ::testing::tuple<
- typename internal::DecayArray<T1>::type> >
-ElementsAre(const T1& e1) {
- typedef ::testing::tuple<
- typename internal::DecayArray<T1>::type> Args;
- return internal::ElementsAreMatcher<Args>(Args(e1));
-}
-
-template <typename T1, typename T2>
-inline internal::ElementsAreMatcher<
- ::testing::tuple<
- typename internal::DecayArray<T1>::type,
- typename internal::DecayArray<T2>::type> >
-ElementsAre(const T1& e1, const T2& e2) {
- typedef ::testing::tuple<
- typename internal::DecayArray<T1>::type,
- typename internal::DecayArray<T2>::type> Args;
- return internal::ElementsAreMatcher<Args>(Args(e1, e2));
-}
-
-template <typename T1, typename T2, typename T3>
-inline internal::ElementsAreMatcher<
- ::testing::tuple<
- typename internal::DecayArray<T1>::type,
- typename internal::DecayArray<T2>::type,
- typename internal::DecayArray<T3>::type> >
-ElementsAre(const T1& e1, const T2& e2, const T3& e3) {
- typedef ::testing::tuple<
- typename internal::DecayArray<T1>::type,
- typename internal::DecayArray<T2>::type,
- typename internal::DecayArray<T3>::type> Args;
- return internal::ElementsAreMatcher<Args>(Args(e1, e2, e3));
-}
-
-template <typename T1, typename T2, typename T3, typename T4>
-inline internal::ElementsAreMatcher<
- ::testing::tuple<
- typename internal::DecayArray<T1>::type,
- typename internal::DecayArray<T2>::type,
- typename internal::DecayArray<T3>::type,
- typename internal::DecayArray<T4>::type> >
-ElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4) {
- typedef ::testing::tuple<
- typename internal::DecayArray<T1>::type,
- typename internal::DecayArray<T2>::type,
- typename internal::DecayArray<T3>::type,
- typename internal::DecayArray<T4>::type> Args;
- return internal::ElementsAreMatcher<Args>(Args(e1, e2, e3, e4));
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5>
-inline internal::ElementsAreMatcher<
- ::testing::tuple<
- typename internal::DecayArray<T1>::type,
- typename internal::DecayArray<T2>::type,
- typename internal::DecayArray<T3>::type,
- typename internal::DecayArray<T4>::type,
- typename internal::DecayArray<T5>::type> >
-ElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4,
- const T5& e5) {
- typedef ::testing::tuple<
- typename internal::DecayArray<T1>::type,
- typename internal::DecayArray<T2>::type,
- typename internal::DecayArray<T3>::type,
- typename internal::DecayArray<T4>::type,
- typename internal::DecayArray<T5>::type> Args;
- return internal::ElementsAreMatcher<Args>(Args(e1, e2, e3, e4, e5));
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6>
-inline internal::ElementsAreMatcher<
- ::testing::tuple<
- typename internal::DecayArray<T1>::type,
- typename internal::DecayArray<T2>::type,
- typename internal::DecayArray<T3>::type,
- typename internal::DecayArray<T4>::type,
- typename internal::DecayArray<T5>::type,
- typename internal::DecayArray<T6>::type> >
-ElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4,
- const T5& e5, const T6& e6) {
- typedef ::testing::tuple<
- typename internal::DecayArray<T1>::type,
- typename internal::DecayArray<T2>::type,
- typename internal::DecayArray<T3>::type,
- typename internal::DecayArray<T4>::type,
- typename internal::DecayArray<T5>::type,
- typename internal::DecayArray<T6>::type> Args;
- return internal::ElementsAreMatcher<Args>(Args(e1, e2, e3, e4, e5, e6));
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7>
-inline internal::ElementsAreMatcher<
- ::testing::tuple<
- typename internal::DecayArray<T1>::type,
- typename internal::DecayArray<T2>::type,
- typename internal::DecayArray<T3>::type,
- typename internal::DecayArray<T4>::type,
- typename internal::DecayArray<T5>::type,
- typename internal::DecayArray<T6>::type,
- typename internal::DecayArray<T7>::type> >
-ElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4,
- const T5& e5, const T6& e6, const T7& e7) {
- typedef ::testing::tuple<
- typename internal::DecayArray<T1>::type,
- typename internal::DecayArray<T2>::type,
- typename internal::DecayArray<T3>::type,
- typename internal::DecayArray<T4>::type,
- typename internal::DecayArray<T5>::type,
- typename internal::DecayArray<T6>::type,
- typename internal::DecayArray<T7>::type> Args;
- return internal::ElementsAreMatcher<Args>(Args(e1, e2, e3, e4, e5, e6, e7));
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8>
-inline internal::ElementsAreMatcher<
- ::testing::tuple<
- typename internal::DecayArray<T1>::type,
- typename internal::DecayArray<T2>::type,
- typename internal::DecayArray<T3>::type,
- typename internal::DecayArray<T4>::type,
- typename internal::DecayArray<T5>::type,
- typename internal::DecayArray<T6>::type,
- typename internal::DecayArray<T7>::type,
- typename internal::DecayArray<T8>::type> >
-ElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4,
- const T5& e5, const T6& e6, const T7& e7, const T8& e8) {
- typedef ::testing::tuple<
- typename internal::DecayArray<T1>::type,
- typename internal::DecayArray<T2>::type,
- typename internal::DecayArray<T3>::type,
- typename internal::DecayArray<T4>::type,
- typename internal::DecayArray<T5>::type,
- typename internal::DecayArray<T6>::type,
- typename internal::DecayArray<T7>::type,
- typename internal::DecayArray<T8>::type> Args;
- return internal::ElementsAreMatcher<Args>(Args(e1, e2, e3, e4, e5, e6, e7,
- e8));
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9>
-inline internal::ElementsAreMatcher<
- ::testing::tuple<
- typename internal::DecayArray<T1>::type,
- typename internal::DecayArray<T2>::type,
- typename internal::DecayArray<T3>::type,
- typename internal::DecayArray<T4>::type,
- typename internal::DecayArray<T5>::type,
- typename internal::DecayArray<T6>::type,
- typename internal::DecayArray<T7>::type,
- typename internal::DecayArray<T8>::type,
- typename internal::DecayArray<T9>::type> >
-ElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4,
- const T5& e5, const T6& e6, const T7& e7, const T8& e8, const T9& e9) {
- typedef ::testing::tuple<
- typename internal::DecayArray<T1>::type,
- typename internal::DecayArray<T2>::type,
- typename internal::DecayArray<T3>::type,
- typename internal::DecayArray<T4>::type,
- typename internal::DecayArray<T5>::type,
- typename internal::DecayArray<T6>::type,
- typename internal::DecayArray<T7>::type,
- typename internal::DecayArray<T8>::type,
- typename internal::DecayArray<T9>::type> Args;
- return internal::ElementsAreMatcher<Args>(Args(e1, e2, e3, e4, e5, e6, e7,
- e8, e9));
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10>
-inline internal::ElementsAreMatcher<
- ::testing::tuple<
- typename internal::DecayArray<T1>::type,
- typename internal::DecayArray<T2>::type,
- typename internal::DecayArray<T3>::type,
- typename internal::DecayArray<T4>::type,
- typename internal::DecayArray<T5>::type,
- typename internal::DecayArray<T6>::type,
- typename internal::DecayArray<T7>::type,
- typename internal::DecayArray<T8>::type,
- typename internal::DecayArray<T9>::type,
- typename internal::DecayArray<T10>::type> >
-ElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4,
- const T5& e5, const T6& e6, const T7& e7, const T8& e8, const T9& e9,
- const T10& e10) {
- typedef ::testing::tuple<
- typename internal::DecayArray<T1>::type,
- typename internal::DecayArray<T2>::type,
- typename internal::DecayArray<T3>::type,
- typename internal::DecayArray<T4>::type,
- typename internal::DecayArray<T5>::type,
- typename internal::DecayArray<T6>::type,
- typename internal::DecayArray<T7>::type,
- typename internal::DecayArray<T8>::type,
- typename internal::DecayArray<T9>::type,
- typename internal::DecayArray<T10>::type> Args;
- return internal::ElementsAreMatcher<Args>(Args(e1, e2, e3, e4, e5, e6, e7,
- e8, e9, e10));
-}
-
-// UnorderedElementsAre(e_1, e_2, ..., e_n) is an ElementsAre extension
-// that matches n elements in any order. We support up to n=10 arguments.
-
-inline internal::UnorderedElementsAreMatcher<
- ::testing::tuple<> >
-UnorderedElementsAre() {
- typedef ::testing::tuple<> Args;
- return internal::UnorderedElementsAreMatcher<Args>(Args());
-}
-
-template <typename T1>
-inline internal::UnorderedElementsAreMatcher<
- ::testing::tuple<
- typename internal::DecayArray<T1>::type> >
-UnorderedElementsAre(const T1& e1) {
- typedef ::testing::tuple<
- typename internal::DecayArray<T1>::type> Args;
- return internal::UnorderedElementsAreMatcher<Args>(Args(e1));
-}
-
-template <typename T1, typename T2>
-inline internal::UnorderedElementsAreMatcher<
- ::testing::tuple<
- typename internal::DecayArray<T1>::type,
- typename internal::DecayArray<T2>::type> >
-UnorderedElementsAre(const T1& e1, const T2& e2) {
- typedef ::testing::tuple<
- typename internal::DecayArray<T1>::type,
- typename internal::DecayArray<T2>::type> Args;
- return internal::UnorderedElementsAreMatcher<Args>(Args(e1, e2));
-}
-
-template <typename T1, typename T2, typename T3>
-inline internal::UnorderedElementsAreMatcher<
- ::testing::tuple<
- typename internal::DecayArray<T1>::type,
- typename internal::DecayArray<T2>::type,
- typename internal::DecayArray<T3>::type> >
-UnorderedElementsAre(const T1& e1, const T2& e2, const T3& e3) {
- typedef ::testing::tuple<
- typename internal::DecayArray<T1>::type,
- typename internal::DecayArray<T2>::type,
- typename internal::DecayArray<T3>::type> Args;
- return internal::UnorderedElementsAreMatcher<Args>(Args(e1, e2, e3));
-}
-
-template <typename T1, typename T2, typename T3, typename T4>
-inline internal::UnorderedElementsAreMatcher<
- ::testing::tuple<
- typename internal::DecayArray<T1>::type,
- typename internal::DecayArray<T2>::type,
- typename internal::DecayArray<T3>::type,
- typename internal::DecayArray<T4>::type> >
-UnorderedElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4) {
- typedef ::testing::tuple<
- typename internal::DecayArray<T1>::type,
- typename internal::DecayArray<T2>::type,
- typename internal::DecayArray<T3>::type,
- typename internal::DecayArray<T4>::type> Args;
- return internal::UnorderedElementsAreMatcher<Args>(Args(e1, e2, e3, e4));
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5>
-inline internal::UnorderedElementsAreMatcher<
- ::testing::tuple<
- typename internal::DecayArray<T1>::type,
- typename internal::DecayArray<T2>::type,
- typename internal::DecayArray<T3>::type,
- typename internal::DecayArray<T4>::type,
- typename internal::DecayArray<T5>::type> >
-UnorderedElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4,
- const T5& e5) {
- typedef ::testing::tuple<
- typename internal::DecayArray<T1>::type,
- typename internal::DecayArray<T2>::type,
- typename internal::DecayArray<T3>::type,
- typename internal::DecayArray<T4>::type,
- typename internal::DecayArray<T5>::type> Args;
- return internal::UnorderedElementsAreMatcher<Args>(Args(e1, e2, e3, e4, e5));
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6>
-inline internal::UnorderedElementsAreMatcher<
- ::testing::tuple<
- typename internal::DecayArray<T1>::type,
- typename internal::DecayArray<T2>::type,
- typename internal::DecayArray<T3>::type,
- typename internal::DecayArray<T4>::type,
- typename internal::DecayArray<T5>::type,
- typename internal::DecayArray<T6>::type> >
-UnorderedElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4,
- const T5& e5, const T6& e6) {
- typedef ::testing::tuple<
- typename internal::DecayArray<T1>::type,
- typename internal::DecayArray<T2>::type,
- typename internal::DecayArray<T3>::type,
- typename internal::DecayArray<T4>::type,
- typename internal::DecayArray<T5>::type,
- typename internal::DecayArray<T6>::type> Args;
- return internal::UnorderedElementsAreMatcher<Args>(Args(e1, e2, e3, e4, e5,
- e6));
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7>
-inline internal::UnorderedElementsAreMatcher<
- ::testing::tuple<
- typename internal::DecayArray<T1>::type,
- typename internal::DecayArray<T2>::type,
- typename internal::DecayArray<T3>::type,
- typename internal::DecayArray<T4>::type,
- typename internal::DecayArray<T5>::type,
- typename internal::DecayArray<T6>::type,
- typename internal::DecayArray<T7>::type> >
-UnorderedElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4,
- const T5& e5, const T6& e6, const T7& e7) {
- typedef ::testing::tuple<
- typename internal::DecayArray<T1>::type,
- typename internal::DecayArray<T2>::type,
- typename internal::DecayArray<T3>::type,
- typename internal::DecayArray<T4>::type,
- typename internal::DecayArray<T5>::type,
- typename internal::DecayArray<T6>::type,
- typename internal::DecayArray<T7>::type> Args;
- return internal::UnorderedElementsAreMatcher<Args>(Args(e1, e2, e3, e4, e5,
- e6, e7));
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8>
-inline internal::UnorderedElementsAreMatcher<
- ::testing::tuple<
- typename internal::DecayArray<T1>::type,
- typename internal::DecayArray<T2>::type,
- typename internal::DecayArray<T3>::type,
- typename internal::DecayArray<T4>::type,
- typename internal::DecayArray<T5>::type,
- typename internal::DecayArray<T6>::type,
- typename internal::DecayArray<T7>::type,
- typename internal::DecayArray<T8>::type> >
-UnorderedElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4,
- const T5& e5, const T6& e6, const T7& e7, const T8& e8) {
- typedef ::testing::tuple<
- typename internal::DecayArray<T1>::type,
- typename internal::DecayArray<T2>::type,
- typename internal::DecayArray<T3>::type,
- typename internal::DecayArray<T4>::type,
- typename internal::DecayArray<T5>::type,
- typename internal::DecayArray<T6>::type,
- typename internal::DecayArray<T7>::type,
- typename internal::DecayArray<T8>::type> Args;
- return internal::UnorderedElementsAreMatcher<Args>(Args(e1, e2, e3, e4, e5,
- e6, e7, e8));
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9>
-inline internal::UnorderedElementsAreMatcher<
- ::testing::tuple<
- typename internal::DecayArray<T1>::type,
- typename internal::DecayArray<T2>::type,
- typename internal::DecayArray<T3>::type,
- typename internal::DecayArray<T4>::type,
- typename internal::DecayArray<T5>::type,
- typename internal::DecayArray<T6>::type,
- typename internal::DecayArray<T7>::type,
- typename internal::DecayArray<T8>::type,
- typename internal::DecayArray<T9>::type> >
-UnorderedElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4,
- const T5& e5, const T6& e6, const T7& e7, const T8& e8, const T9& e9) {
- typedef ::testing::tuple<
- typename internal::DecayArray<T1>::type,
- typename internal::DecayArray<T2>::type,
- typename internal::DecayArray<T3>::type,
- typename internal::DecayArray<T4>::type,
- typename internal::DecayArray<T5>::type,
- typename internal::DecayArray<T6>::type,
- typename internal::DecayArray<T7>::type,
- typename internal::DecayArray<T8>::type,
- typename internal::DecayArray<T9>::type> Args;
- return internal::UnorderedElementsAreMatcher<Args>(Args(e1, e2, e3, e4, e5,
- e6, e7, e8, e9));
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10>
-inline internal::UnorderedElementsAreMatcher<
- ::testing::tuple<
- typename internal::DecayArray<T1>::type,
- typename internal::DecayArray<T2>::type,
- typename internal::DecayArray<T3>::type,
- typename internal::DecayArray<T4>::type,
- typename internal::DecayArray<T5>::type,
- typename internal::DecayArray<T6>::type,
- typename internal::DecayArray<T7>::type,
- typename internal::DecayArray<T8>::type,
- typename internal::DecayArray<T9>::type,
- typename internal::DecayArray<T10>::type> >
-UnorderedElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4,
- const T5& e5, const T6& e6, const T7& e7, const T8& e8, const T9& e9,
- const T10& e10) {
- typedef ::testing::tuple<
- typename internal::DecayArray<T1>::type,
- typename internal::DecayArray<T2>::type,
- typename internal::DecayArray<T3>::type,
- typename internal::DecayArray<T4>::type,
- typename internal::DecayArray<T5>::type,
- typename internal::DecayArray<T6>::type,
- typename internal::DecayArray<T7>::type,
- typename internal::DecayArray<T8>::type,
- typename internal::DecayArray<T9>::type,
- typename internal::DecayArray<T10>::type> Args;
- return internal::UnorderedElementsAreMatcher<Args>(Args(e1, e2, e3, e4, e5,
- e6, e7, e8, e9, e10));
-}
-
-// AllOf(m1, m2, ..., mk) matches any value that matches all of the given
-// sub-matchers. AllOf is called fully qualified to prevent ADL from firing.
-
-template <typename M1, typename M2>
-inline typename internal::AllOfResult2<M1, M2>::type
-AllOf(M1 m1, M2 m2) {
- return typename internal::AllOfResult2<M1, M2>::type(
- m1,
- m2);
-}
-
-template <typename M1, typename M2, typename M3>
-inline typename internal::AllOfResult3<M1, M2, M3>::type
-AllOf(M1 m1, M2 m2, M3 m3) {
- return typename internal::AllOfResult3<M1, M2, M3>::type(
- m1,
- ::testing::AllOf(m2, m3));
-}
-
-template <typename M1, typename M2, typename M3, typename M4>
-inline typename internal::AllOfResult4<M1, M2, M3, M4>::type
-AllOf(M1 m1, M2 m2, M3 m3, M4 m4) {
- return typename internal::AllOfResult4<M1, M2, M3, M4>::type(
- ::testing::AllOf(m1, m2),
- ::testing::AllOf(m3, m4));
-}
-
-template <typename M1, typename M2, typename M3, typename M4, typename M5>
-inline typename internal::AllOfResult5<M1, M2, M3, M4, M5>::type
-AllOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5) {
- return typename internal::AllOfResult5<M1, M2, M3, M4, M5>::type(
- ::testing::AllOf(m1, m2),
- ::testing::AllOf(m3, m4, m5));
-}
-
-template <typename M1, typename M2, typename M3, typename M4, typename M5,
- typename M6>
-inline typename internal::AllOfResult6<M1, M2, M3, M4, M5, M6>::type
-AllOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6) {
- return typename internal::AllOfResult6<M1, M2, M3, M4, M5, M6>::type(
- ::testing::AllOf(m1, m2, m3),
- ::testing::AllOf(m4, m5, m6));
-}
-
-template <typename M1, typename M2, typename M3, typename M4, typename M5,
- typename M6, typename M7>
-inline typename internal::AllOfResult7<M1, M2, M3, M4, M5, M6, M7>::type
-AllOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7) {
- return typename internal::AllOfResult7<M1, M2, M3, M4, M5, M6, M7>::type(
- ::testing::AllOf(m1, m2, m3),
- ::testing::AllOf(m4, m5, m6, m7));
-}
-
-template <typename M1, typename M2, typename M3, typename M4, typename M5,
- typename M6, typename M7, typename M8>
-inline typename internal::AllOfResult8<M1, M2, M3, M4, M5, M6, M7, M8>::type
-AllOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8) {
- return typename internal::AllOfResult8<M1, M2, M3, M4, M5, M6, M7, M8>::type(
- ::testing::AllOf(m1, m2, m3, m4),
- ::testing::AllOf(m5, m6, m7, m8));
-}
-
-template <typename M1, typename M2, typename M3, typename M4, typename M5,
- typename M6, typename M7, typename M8, typename M9>
-inline typename internal::AllOfResult9<M1, M2, M3, M4, M5, M6, M7, M8, M9>::type
-AllOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9) {
- return typename internal::AllOfResult9<M1, M2, M3, M4, M5, M6, M7, M8,
- M9>::type(
- ::testing::AllOf(m1, m2, m3, m4),
- ::testing::AllOf(m5, m6, m7, m8, m9));
-}
-
-template <typename M1, typename M2, typename M3, typename M4, typename M5,
- typename M6, typename M7, typename M8, typename M9, typename M10>
-inline typename internal::AllOfResult10<M1, M2, M3, M4, M5, M6, M7, M8, M9,
- M10>::type
-AllOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9, M10 m10) {
- return typename internal::AllOfResult10<M1, M2, M3, M4, M5, M6, M7, M8, M9,
- M10>::type(
- ::testing::AllOf(m1, m2, m3, m4, m5),
- ::testing::AllOf(m6, m7, m8, m9, m10));
-}
-
-// AnyOf(m1, m2, ..., mk) matches any value that matches any of the given
-// sub-matchers. AnyOf is called fully qualified to prevent ADL from firing.
-
-template <typename M1, typename M2>
-inline typename internal::AnyOfResult2<M1, M2>::type
-AnyOf(M1 m1, M2 m2) {
- return typename internal::AnyOfResult2<M1, M2>::type(
- m1,
- m2);
-}
-
-template <typename M1, typename M2, typename M3>
-inline typename internal::AnyOfResult3<M1, M2, M3>::type
-AnyOf(M1 m1, M2 m2, M3 m3) {
- return typename internal::AnyOfResult3<M1, M2, M3>::type(
- m1,
- ::testing::AnyOf(m2, m3));
-}
-
-template <typename M1, typename M2, typename M3, typename M4>
-inline typename internal::AnyOfResult4<M1, M2, M3, M4>::type
-AnyOf(M1 m1, M2 m2, M3 m3, M4 m4) {
- return typename internal::AnyOfResult4<M1, M2, M3, M4>::type(
- ::testing::AnyOf(m1, m2),
- ::testing::AnyOf(m3, m4));
-}
-
-template <typename M1, typename M2, typename M3, typename M4, typename M5>
-inline typename internal::AnyOfResult5<M1, M2, M3, M4, M5>::type
-AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5) {
- return typename internal::AnyOfResult5<M1, M2, M3, M4, M5>::type(
- ::testing::AnyOf(m1, m2),
- ::testing::AnyOf(m3, m4, m5));
-}
-
-template <typename M1, typename M2, typename M3, typename M4, typename M5,
- typename M6>
-inline typename internal::AnyOfResult6<M1, M2, M3, M4, M5, M6>::type
-AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6) {
- return typename internal::AnyOfResult6<M1, M2, M3, M4, M5, M6>::type(
- ::testing::AnyOf(m1, m2, m3),
- ::testing::AnyOf(m4, m5, m6));
-}
-
-template <typename M1, typename M2, typename M3, typename M4, typename M5,
- typename M6, typename M7>
-inline typename internal::AnyOfResult7<M1, M2, M3, M4, M5, M6, M7>::type
-AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7) {
- return typename internal::AnyOfResult7<M1, M2, M3, M4, M5, M6, M7>::type(
- ::testing::AnyOf(m1, m2, m3),
- ::testing::AnyOf(m4, m5, m6, m7));
-}
-
-template <typename M1, typename M2, typename M3, typename M4, typename M5,
- typename M6, typename M7, typename M8>
-inline typename internal::AnyOfResult8<M1, M2, M3, M4, M5, M6, M7, M8>::type
-AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8) {
- return typename internal::AnyOfResult8<M1, M2, M3, M4, M5, M6, M7, M8>::type(
- ::testing::AnyOf(m1, m2, m3, m4),
- ::testing::AnyOf(m5, m6, m7, m8));
-}
-
-template <typename M1, typename M2, typename M3, typename M4, typename M5,
- typename M6, typename M7, typename M8, typename M9>
-inline typename internal::AnyOfResult9<M1, M2, M3, M4, M5, M6, M7, M8, M9>::type
-AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9) {
- return typename internal::AnyOfResult9<M1, M2, M3, M4, M5, M6, M7, M8,
- M9>::type(
- ::testing::AnyOf(m1, m2, m3, m4),
- ::testing::AnyOf(m5, m6, m7, m8, m9));
-}
-
-template <typename M1, typename M2, typename M3, typename M4, typename M5,
- typename M6, typename M7, typename M8, typename M9, typename M10>
-inline typename internal::AnyOfResult10<M1, M2, M3, M4, M5, M6, M7, M8, M9,
- M10>::type
-AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9, M10 m10) {
- return typename internal::AnyOfResult10<M1, M2, M3, M4, M5, M6, M7, M8, M9,
- M10>::type(
- ::testing::AnyOf(m1, m2, m3, m4, m5),
- ::testing::AnyOf(m6, m7, m8, m9, m10));
-}
-
-} // namespace testing
-
-
// The MATCHER* family of macros can be used in a namespace scope to
// define custom matchers easily.
//
@@ -1268,7 +152,7 @@ AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9, M10 m10) {
// using testing::PrintToString;
//
// MATCHER_P2(InClosedRange, low, hi,
-// string(negation ? "is not" : "is") + " in range [" +
+// std::string(negation ? "is not" : "is") + " in range [" +
// PrintToString(low) + ", " + PrintToString(hi) + "]") {
// return low <= arg && arg <= hi;
// }
@@ -1366,28 +250,28 @@ AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9, M10 m10) {
// overloading matchers based on parameter types (as opposed to just
// based on the number of parameters).
//
-// MATCHER*() can only be used in a namespace scope. The reason is
-// that C++ doesn't yet allow function-local types to be used to
-// instantiate templates. The up-coming C++0x standard will fix this.
-// Once that's done, we'll consider supporting using MATCHER*() inside
-// a function.
+// MATCHER*() can only be used in a namespace scope as templates cannot be
+// declared inside of a local class.
//
// More Information
// ================
//
// To learn more about using these macros, please search for 'MATCHER'
-// on http://code.google.com/p/googlemock/wiki/CookBook.
+// on
+// https://github.com/google/googletest/blob/master/googlemock/docs/cook_book.md
#define MATCHER(name, description)\
class name##Matcher {\
public:\
template <typename arg_type>\
- class gmock_Impl : public ::testing::MatcherInterface<arg_type> {\
+ class gmock_Impl : public ::testing::MatcherInterface<\
+ GTEST_REFERENCE_TO_CONST_(arg_type)> {\
public:\
gmock_Impl()\
{}\
virtual bool MatchAndExplain(\
- arg_type arg, ::testing::MatchResultListener* result_listener) const;\
+ GTEST_REFERENCE_TO_CONST_(arg_type) arg,\
+ ::testing::MatchResultListener* result_listener) const;\
virtual void DescribeTo(::std::ostream* gmock_os) const {\
*gmock_os << FormatDescription(false);\
}\
@@ -1395,16 +279,16 @@ AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9, M10 m10) {
*gmock_os << FormatDescription(true);\
}\
private:\
- ::testing::internal::string FormatDescription(bool negation) const {\
- const ::testing::internal::string gmock_description = (description);\
- if (!gmock_description.empty())\
+ ::std::string FormatDescription(bool negation) const {\
+ ::std::string gmock_description = (description);\
+ if (!gmock_description.empty()) {\
return gmock_description;\
+ }\
return ::testing::internal::FormatMatcherDescription(\
negation, #name, \
::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
- ::testing::tuple<>()));\
+ ::std::tuple<>()));\
}\
- GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
};\
template <typename arg_type>\
operator ::testing::Matcher<arg_type>() const {\
@@ -1414,14 +298,13 @@ AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9, M10 m10) {
name##Matcher() {\
}\
private:\
- GTEST_DISALLOW_ASSIGN_(name##Matcher);\
};\
inline name##Matcher name() {\
return name##Matcher();\
}\
template <typename arg_type>\
bool name##Matcher::gmock_Impl<arg_type>::MatchAndExplain(\
- arg_type arg, \
+ GTEST_REFERENCE_TO_CONST_(arg_type) arg,\
::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\
const
@@ -1430,41 +313,42 @@ AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9, M10 m10) {
class name##MatcherP {\
public:\
template <typename arg_type>\
- class gmock_Impl : public ::testing::MatcherInterface<arg_type> {\
+ class gmock_Impl : public ::testing::MatcherInterface<\
+ GTEST_REFERENCE_TO_CONST_(arg_type)> {\
public:\
explicit gmock_Impl(p0##_type gmock_p0)\
- : p0(gmock_p0) {}\
+ : p0(::std::move(gmock_p0)) {}\
virtual bool MatchAndExplain(\
- arg_type arg, ::testing::MatchResultListener* result_listener) const;\
+ GTEST_REFERENCE_TO_CONST_(arg_type) arg,\
+ ::testing::MatchResultListener* result_listener) const;\
virtual void DescribeTo(::std::ostream* gmock_os) const {\
*gmock_os << FormatDescription(false);\
}\
virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\
*gmock_os << FormatDescription(true);\
}\
- p0##_type p0;\
+ p0##_type const p0;\
private:\
- ::testing::internal::string FormatDescription(bool negation) const {\
- const ::testing::internal::string gmock_description = (description);\
- if (!gmock_description.empty())\
+ ::std::string FormatDescription(bool negation) const {\
+ ::std::string gmock_description = (description);\
+ if (!gmock_description.empty()) {\
return gmock_description;\
+ }\
return ::testing::internal::FormatMatcherDescription(\
negation, #name, \
::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
- ::testing::tuple<p0##_type>(p0)));\
+ ::std::tuple<p0##_type>(p0)));\
}\
- GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
};\
template <typename arg_type>\
operator ::testing::Matcher<arg_type>() const {\
return ::testing::Matcher<arg_type>(\
new gmock_Impl<arg_type>(p0));\
}\
- explicit name##MatcherP(p0##_type gmock_p0) : p0(gmock_p0) {\
+ explicit name##MatcherP(p0##_type gmock_p0) : p0(::std::move(gmock_p0)) {\
}\
- p0##_type p0;\
+ p0##_type const p0;\
private:\
- GTEST_DISALLOW_ASSIGN_(name##MatcherP);\
};\
template <typename p0##_type>\
inline name##MatcherP<p0##_type> name(p0##_type p0) {\
@@ -1473,7 +357,7 @@ AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9, M10 m10) {
template <typename p0##_type>\
template <typename arg_type>\
bool name##MatcherP<p0##_type>::gmock_Impl<arg_type>::MatchAndExplain(\
- arg_type arg, \
+ GTEST_REFERENCE_TO_CONST_(arg_type) arg,\
::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\
const
@@ -1482,44 +366,46 @@ AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9, M10 m10) {
class name##MatcherP2 {\
public:\
template <typename arg_type>\
- class gmock_Impl : public ::testing::MatcherInterface<arg_type> {\
+ class gmock_Impl : public ::testing::MatcherInterface<\
+ GTEST_REFERENCE_TO_CONST_(arg_type)> {\
public:\
gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1)\
- : p0(gmock_p0), p1(gmock_p1) {}\
+ : p0(::std::move(gmock_p0)), p1(::std::move(gmock_p1)) {}\
virtual bool MatchAndExplain(\
- arg_type arg, ::testing::MatchResultListener* result_listener) const;\
+ GTEST_REFERENCE_TO_CONST_(arg_type) arg,\
+ ::testing::MatchResultListener* result_listener) const;\
virtual void DescribeTo(::std::ostream* gmock_os) const {\
*gmock_os << FormatDescription(false);\
}\
virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\
*gmock_os << FormatDescription(true);\
}\
- p0##_type p0;\
- p1##_type p1;\
+ p0##_type const p0;\
+ p1##_type const p1;\
private:\
- ::testing::internal::string FormatDescription(bool negation) const {\
- const ::testing::internal::string gmock_description = (description);\
- if (!gmock_description.empty())\
+ ::std::string FormatDescription(bool negation) const {\
+ ::std::string gmock_description = (description);\
+ if (!gmock_description.empty()) {\
return gmock_description;\
+ }\
return ::testing::internal::FormatMatcherDescription(\
negation, #name, \
::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
- ::testing::tuple<p0##_type, p1##_type>(p0, p1)));\
+ ::std::tuple<p0##_type, p1##_type>(p0, p1)));\
}\
- GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
};\
template <typename arg_type>\
operator ::testing::Matcher<arg_type>() const {\
return ::testing::Matcher<arg_type>(\
new gmock_Impl<arg_type>(p0, p1));\
}\
- name##MatcherP2(p0##_type gmock_p0, p1##_type gmock_p1) : p0(gmock_p0), \
- p1(gmock_p1) {\
+ name##MatcherP2(p0##_type gmock_p0, \
+ p1##_type gmock_p1) : p0(::std::move(gmock_p0)), \
+ p1(::std::move(gmock_p1)) {\
}\
- p0##_type p0;\
- p1##_type p1;\
+ p0##_type const p0;\
+ p1##_type const p1;\
private:\
- GTEST_DISALLOW_ASSIGN_(name##MatcherP2);\
};\
template <typename p0##_type, typename p1##_type>\
inline name##MatcherP2<p0##_type, p1##_type> name(p0##_type p0, \
@@ -1530,7 +416,7 @@ AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9, M10 m10) {
template <typename arg_type>\
bool name##MatcherP2<p0##_type, \
p1##_type>::gmock_Impl<arg_type>::MatchAndExplain(\
- arg_type arg, \
+ GTEST_REFERENCE_TO_CONST_(arg_type) arg,\
::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\
const
@@ -1539,33 +425,35 @@ AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9, M10 m10) {
class name##MatcherP3 {\
public:\
template <typename arg_type>\
- class gmock_Impl : public ::testing::MatcherInterface<arg_type> {\
+ class gmock_Impl : public ::testing::MatcherInterface<\
+ GTEST_REFERENCE_TO_CONST_(arg_type)> {\
public:\
gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2)\
- : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2) {}\
+ : p0(::std::move(gmock_p0)), p1(::std::move(gmock_p1)), \
+ p2(::std::move(gmock_p2)) {}\
virtual bool MatchAndExplain(\
- arg_type arg, ::testing::MatchResultListener* result_listener) const;\
+ GTEST_REFERENCE_TO_CONST_(arg_type) arg,\
+ ::testing::MatchResultListener* result_listener) const;\
virtual void DescribeTo(::std::ostream* gmock_os) const {\
*gmock_os << FormatDescription(false);\
}\
virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\
*gmock_os << FormatDescription(true);\
}\
- p0##_type p0;\
- p1##_type p1;\
- p2##_type p2;\
+ p0##_type const p0;\
+ p1##_type const p1;\
+ p2##_type const p2;\
private:\
- ::testing::internal::string FormatDescription(bool negation) const {\
- const ::testing::internal::string gmock_description = (description);\
- if (!gmock_description.empty())\
+ ::std::string FormatDescription(bool negation) const {\
+ ::std::string gmock_description = (description);\
+ if (!gmock_description.empty()) {\
return gmock_description;\
+ }\
return ::testing::internal::FormatMatcherDescription(\
negation, #name, \
::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
- ::testing::tuple<p0##_type, p1##_type, p2##_type>(p0, p1, \
- p2)));\
+ ::std::tuple<p0##_type, p1##_type, p2##_type>(p0, p1, p2)));\
}\
- GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
};\
template <typename arg_type>\
operator ::testing::Matcher<arg_type>() const {\
@@ -1573,13 +461,13 @@ AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9, M10 m10) {
new gmock_Impl<arg_type>(p0, p1, p2));\
}\
name##MatcherP3(p0##_type gmock_p0, p1##_type gmock_p1, \
- p2##_type gmock_p2) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2) {\
+ p2##_type gmock_p2) : p0(::std::move(gmock_p0)), \
+ p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)) {\
}\
- p0##_type p0;\
- p1##_type p1;\
- p2##_type p2;\
+ p0##_type const p0;\
+ p1##_type const p1;\
+ p2##_type const p2;\
private:\
- GTEST_DISALLOW_ASSIGN_(name##MatcherP3);\
};\
template <typename p0##_type, typename p1##_type, typename p2##_type>\
inline name##MatcherP3<p0##_type, p1##_type, p2##_type> name(p0##_type p0, \
@@ -1590,7 +478,7 @@ AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9, M10 m10) {
template <typename arg_type>\
bool name##MatcherP3<p0##_type, p1##_type, \
p2##_type>::gmock_Impl<arg_type>::MatchAndExplain(\
- arg_type arg, \
+ GTEST_REFERENCE_TO_CONST_(arg_type) arg,\
::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\
const
@@ -1600,35 +488,38 @@ AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9, M10 m10) {
class name##MatcherP4 {\
public:\
template <typename arg_type>\
- class gmock_Impl : public ::testing::MatcherInterface<arg_type> {\
+ class gmock_Impl : public ::testing::MatcherInterface<\
+ GTEST_REFERENCE_TO_CONST_(arg_type)> {\
public:\
gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
p3##_type gmock_p3)\
- : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), p3(gmock_p3) {}\
+ : p0(::std::move(gmock_p0)), p1(::std::move(gmock_p1)), \
+ p2(::std::move(gmock_p2)), p3(::std::move(gmock_p3)) {}\
virtual bool MatchAndExplain(\
- arg_type arg, ::testing::MatchResultListener* result_listener) const;\
+ GTEST_REFERENCE_TO_CONST_(arg_type) arg,\
+ ::testing::MatchResultListener* result_listener) const;\
virtual void DescribeTo(::std::ostream* gmock_os) const {\
*gmock_os << FormatDescription(false);\
}\
virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\
*gmock_os << FormatDescription(true);\
}\
- p0##_type p0;\
- p1##_type p1;\
- p2##_type p2;\
- p3##_type p3;\
+ p0##_type const p0;\
+ p1##_type const p1;\
+ p2##_type const p2;\
+ p3##_type const p3;\
private:\
- ::testing::internal::string FormatDescription(bool negation) const {\
- const ::testing::internal::string gmock_description = (description);\
- if (!gmock_description.empty())\
+ ::std::string FormatDescription(bool negation) const {\
+ ::std::string gmock_description = (description);\
+ if (!gmock_description.empty()) {\
return gmock_description;\
+ }\
return ::testing::internal::FormatMatcherDescription(\
negation, #name, \
::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
- ::testing::tuple<p0##_type, p1##_type, p2##_type, \
- p3##_type>(p0, p1, p2, p3)));\
+ ::std::tuple<p0##_type, p1##_type, p2##_type, p3##_type>(p0, \
+ p1, p2, p3)));\
}\
- GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
};\
template <typename arg_type>\
operator ::testing::Matcher<arg_type>() const {\
@@ -1636,15 +527,15 @@ AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9, M10 m10) {
new gmock_Impl<arg_type>(p0, p1, p2, p3));\
}\
name##MatcherP4(p0##_type gmock_p0, p1##_type gmock_p1, \
- p2##_type gmock_p2, p3##_type gmock_p3) : p0(gmock_p0), p1(gmock_p1), \
- p2(gmock_p2), p3(gmock_p3) {\
+ p2##_type gmock_p2, p3##_type gmock_p3) : p0(::std::move(gmock_p0)), \
+ p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \
+ p3(::std::move(gmock_p3)) {\
}\
- p0##_type p0;\
- p1##_type p1;\
- p2##_type p2;\
- p3##_type p3;\
+ p0##_type const p0;\
+ p1##_type const p1;\
+ p2##_type const p2;\
+ p3##_type const p3;\
private:\
- GTEST_DISALLOW_ASSIGN_(name##MatcherP4);\
};\
template <typename p0##_type, typename p1##_type, typename p2##_type, \
typename p3##_type>\
@@ -1659,7 +550,7 @@ AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9, M10 m10) {
template <typename arg_type>\
bool name##MatcherP4<p0##_type, p1##_type, p2##_type, \
p3##_type>::gmock_Impl<arg_type>::MatchAndExplain(\
- arg_type arg, \
+ GTEST_REFERENCE_TO_CONST_(arg_type) arg,\
::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\
const
@@ -1669,37 +560,40 @@ AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9, M10 m10) {
class name##MatcherP5 {\
public:\
template <typename arg_type>\
- class gmock_Impl : public ::testing::MatcherInterface<arg_type> {\
+ class gmock_Impl : public ::testing::MatcherInterface<\
+ GTEST_REFERENCE_TO_CONST_(arg_type)> {\
public:\
gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
p3##_type gmock_p3, p4##_type gmock_p4)\
- : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), \
- p4(gmock_p4) {}\
+ : p0(::std::move(gmock_p0)), p1(::std::move(gmock_p1)), \
+ p2(::std::move(gmock_p2)), p3(::std::move(gmock_p3)), \
+ p4(::std::move(gmock_p4)) {}\
virtual bool MatchAndExplain(\
- arg_type arg, ::testing::MatchResultListener* result_listener) const;\
+ GTEST_REFERENCE_TO_CONST_(arg_type) arg,\
+ ::testing::MatchResultListener* result_listener) const;\
virtual void DescribeTo(::std::ostream* gmock_os) const {\
*gmock_os << FormatDescription(false);\
}\
virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\
*gmock_os << FormatDescription(true);\
}\
- p0##_type p0;\
- p1##_type p1;\
- p2##_type p2;\
- p3##_type p3;\
- p4##_type p4;\
+ p0##_type const p0;\
+ p1##_type const p1;\
+ p2##_type const p2;\
+ p3##_type const p3;\
+ p4##_type const p4;\
private:\
- ::testing::internal::string FormatDescription(bool negation) const {\
- const ::testing::internal::string gmock_description = (description);\
- if (!gmock_description.empty())\
+ ::std::string FormatDescription(bool negation) const {\
+ ::std::string gmock_description = (description);\
+ if (!gmock_description.empty()) {\
return gmock_description;\
+ }\
return ::testing::internal::FormatMatcherDescription(\
negation, #name, \
::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
- ::testing::tuple<p0##_type, p1##_type, p2##_type, p3##_type, \
+ ::std::tuple<p0##_type, p1##_type, p2##_type, p3##_type, \
p4##_type>(p0, p1, p2, p3, p4)));\
}\
- GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
};\
template <typename arg_type>\
operator ::testing::Matcher<arg_type>() const {\
@@ -1708,16 +602,16 @@ AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9, M10 m10) {
}\
name##MatcherP5(p0##_type gmock_p0, p1##_type gmock_p1, \
p2##_type gmock_p2, p3##_type gmock_p3, \
- p4##_type gmock_p4) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
- p3(gmock_p3), p4(gmock_p4) {\
+ p4##_type gmock_p4) : p0(::std::move(gmock_p0)), \
+ p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \
+ p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)) {\
}\
- p0##_type p0;\
- p1##_type p1;\
- p2##_type p2;\
- p3##_type p3;\
- p4##_type p4;\
+ p0##_type const p0;\
+ p1##_type const p1;\
+ p2##_type const p2;\
+ p3##_type const p3;\
+ p4##_type const p4;\
private:\
- GTEST_DISALLOW_ASSIGN_(name##MatcherP5);\
};\
template <typename p0##_type, typename p1##_type, typename p2##_type, \
typename p3##_type, typename p4##_type>\
@@ -1732,7 +626,7 @@ AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9, M10 m10) {
template <typename arg_type>\
bool name##MatcherP5<p0##_type, p1##_type, p2##_type, p3##_type, \
p4##_type>::gmock_Impl<arg_type>::MatchAndExplain(\
- arg_type arg, \
+ GTEST_REFERENCE_TO_CONST_(arg_type) arg,\
::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\
const
@@ -1742,38 +636,41 @@ AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9, M10 m10) {
class name##MatcherP6 {\
public:\
template <typename arg_type>\
- class gmock_Impl : public ::testing::MatcherInterface<arg_type> {\
+ class gmock_Impl : public ::testing::MatcherInterface<\
+ GTEST_REFERENCE_TO_CONST_(arg_type)> {\
public:\
gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5)\
- : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), \
- p4(gmock_p4), p5(gmock_p5) {}\
+ : p0(::std::move(gmock_p0)), p1(::std::move(gmock_p1)), \
+ p2(::std::move(gmock_p2)), p3(::std::move(gmock_p3)), \
+ p4(::std::move(gmock_p4)), p5(::std::move(gmock_p5)) {}\
virtual bool MatchAndExplain(\
- arg_type arg, ::testing::MatchResultListener* result_listener) const;\
+ GTEST_REFERENCE_TO_CONST_(arg_type) arg,\
+ ::testing::MatchResultListener* result_listener) const;\
virtual void DescribeTo(::std::ostream* gmock_os) const {\
*gmock_os << FormatDescription(false);\
}\
virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\
*gmock_os << FormatDescription(true);\
}\
- p0##_type p0;\
- p1##_type p1;\
- p2##_type p2;\
- p3##_type p3;\
- p4##_type p4;\
- p5##_type p5;\
+ p0##_type const p0;\
+ p1##_type const p1;\
+ p2##_type const p2;\
+ p3##_type const p3;\
+ p4##_type const p4;\
+ p5##_type const p5;\
private:\
- ::testing::internal::string FormatDescription(bool negation) const {\
- const ::testing::internal::string gmock_description = (description);\
- if (!gmock_description.empty())\
+ ::std::string FormatDescription(bool negation) const {\
+ ::std::string gmock_description = (description);\
+ if (!gmock_description.empty()) {\
return gmock_description;\
+ }\
return ::testing::internal::FormatMatcherDescription(\
negation, #name, \
::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
- ::testing::tuple<p0##_type, p1##_type, p2##_type, p3##_type, \
+ ::std::tuple<p0##_type, p1##_type, p2##_type, p3##_type, \
p4##_type, p5##_type>(p0, p1, p2, p3, p4, p5)));\
}\
- GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
};\
template <typename arg_type>\
operator ::testing::Matcher<arg_type>() const {\
@@ -1782,17 +679,18 @@ AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9, M10 m10) {
}\
name##MatcherP6(p0##_type gmock_p0, p1##_type gmock_p1, \
p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \
- p5##_type gmock_p5) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
- p3(gmock_p3), p4(gmock_p4), p5(gmock_p5) {\
+ p5##_type gmock_p5) : p0(::std::move(gmock_p0)), \
+ p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \
+ p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)), \
+ p5(::std::move(gmock_p5)) {\
}\
- p0##_type p0;\
- p1##_type p1;\
- p2##_type p2;\
- p3##_type p3;\
- p4##_type p4;\
- p5##_type p5;\
+ p0##_type const p0;\
+ p1##_type const p1;\
+ p2##_type const p2;\
+ p3##_type const p3;\
+ p4##_type const p4;\
+ p5##_type const p5;\
private:\
- GTEST_DISALLOW_ASSIGN_(name##MatcherP6);\
};\
template <typename p0##_type, typename p1##_type, typename p2##_type, \
typename p3##_type, typename p4##_type, typename p5##_type>\
@@ -1807,7 +705,7 @@ AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9, M10 m10) {
template <typename arg_type>\
bool name##MatcherP6<p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \
p5##_type>::gmock_Impl<arg_type>::MatchAndExplain(\
- arg_type arg, \
+ GTEST_REFERENCE_TO_CONST_(arg_type) arg,\
::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\
const
@@ -1818,41 +716,45 @@ AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9, M10 m10) {
class name##MatcherP7 {\
public:\
template <typename arg_type>\
- class gmock_Impl : public ::testing::MatcherInterface<arg_type> {\
+ class gmock_Impl : public ::testing::MatcherInterface<\
+ GTEST_REFERENCE_TO_CONST_(arg_type)> {\
public:\
gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
p6##_type gmock_p6)\
- : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), \
- p4(gmock_p4), p5(gmock_p5), p6(gmock_p6) {}\
+ : p0(::std::move(gmock_p0)), p1(::std::move(gmock_p1)), \
+ p2(::std::move(gmock_p2)), p3(::std::move(gmock_p3)), \
+ p4(::std::move(gmock_p4)), p5(::std::move(gmock_p5)), \
+ p6(::std::move(gmock_p6)) {}\
virtual bool MatchAndExplain(\
- arg_type arg, ::testing::MatchResultListener* result_listener) const;\
+ GTEST_REFERENCE_TO_CONST_(arg_type) arg,\
+ ::testing::MatchResultListener* result_listener) const;\
virtual void DescribeTo(::std::ostream* gmock_os) const {\
*gmock_os << FormatDescription(false);\
}\
virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\
*gmock_os << FormatDescription(true);\
}\
- p0##_type p0;\
- p1##_type p1;\
- p2##_type p2;\
- p3##_type p3;\
- p4##_type p4;\
- p5##_type p5;\
- p6##_type p6;\
+ p0##_type const p0;\
+ p1##_type const p1;\
+ p2##_type const p2;\
+ p3##_type const p3;\
+ p4##_type const p4;\
+ p5##_type const p5;\
+ p6##_type const p6;\
private:\
- ::testing::internal::string FormatDescription(bool negation) const {\
- const ::testing::internal::string gmock_description = (description);\
- if (!gmock_description.empty())\
+ ::std::string FormatDescription(bool negation) const {\
+ ::std::string gmock_description = (description);\
+ if (!gmock_description.empty()) {\
return gmock_description;\
+ }\
return ::testing::internal::FormatMatcherDescription(\
negation, #name, \
::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
- ::testing::tuple<p0##_type, p1##_type, p2##_type, p3##_type, \
+ ::std::tuple<p0##_type, p1##_type, p2##_type, p3##_type, \
p4##_type, p5##_type, p6##_type>(p0, p1, p2, p3, p4, p5, \
p6)));\
}\
- GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
};\
template <typename arg_type>\
operator ::testing::Matcher<arg_type>() const {\
@@ -1861,19 +763,19 @@ AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9, M10 m10) {
}\
name##MatcherP7(p0##_type gmock_p0, p1##_type gmock_p1, \
p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \
- p5##_type gmock_p5, p6##_type gmock_p6) : p0(gmock_p0), p1(gmock_p1), \
- p2(gmock_p2), p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), \
- p6(gmock_p6) {\
+ p5##_type gmock_p5, p6##_type gmock_p6) : p0(::std::move(gmock_p0)), \
+ p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \
+ p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)), \
+ p5(::std::move(gmock_p5)), p6(::std::move(gmock_p6)) {\
}\
- p0##_type p0;\
- p1##_type p1;\
- p2##_type p2;\
- p3##_type p3;\
- p4##_type p4;\
- p5##_type p5;\
- p6##_type p6;\
+ p0##_type const p0;\
+ p1##_type const p1;\
+ p2##_type const p2;\
+ p3##_type const p3;\
+ p4##_type const p4;\
+ p5##_type const p5;\
+ p6##_type const p6;\
private:\
- GTEST_DISALLOW_ASSIGN_(name##MatcherP7);\
};\
template <typename p0##_type, typename p1##_type, typename p2##_type, \
typename p3##_type, typename p4##_type, typename p5##_type, \
@@ -1891,7 +793,7 @@ AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9, M10 m10) {
template <typename arg_type>\
bool name##MatcherP7<p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \
p5##_type, p6##_type>::gmock_Impl<arg_type>::MatchAndExplain(\
- arg_type arg, \
+ GTEST_REFERENCE_TO_CONST_(arg_type) arg,\
::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\
const
@@ -1902,42 +804,46 @@ AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9, M10 m10) {
class name##MatcherP8 {\
public:\
template <typename arg_type>\
- class gmock_Impl : public ::testing::MatcherInterface<arg_type> {\
+ class gmock_Impl : public ::testing::MatcherInterface<\
+ GTEST_REFERENCE_TO_CONST_(arg_type)> {\
public:\
gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
p6##_type gmock_p6, p7##_type gmock_p7)\
- : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), \
- p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), p7(gmock_p7) {}\
+ : p0(::std::move(gmock_p0)), p1(::std::move(gmock_p1)), \
+ p2(::std::move(gmock_p2)), p3(::std::move(gmock_p3)), \
+ p4(::std::move(gmock_p4)), p5(::std::move(gmock_p5)), \
+ p6(::std::move(gmock_p6)), p7(::std::move(gmock_p7)) {}\
virtual bool MatchAndExplain(\
- arg_type arg, ::testing::MatchResultListener* result_listener) const;\
+ GTEST_REFERENCE_TO_CONST_(arg_type) arg,\
+ ::testing::MatchResultListener* result_listener) const;\
virtual void DescribeTo(::std::ostream* gmock_os) const {\
*gmock_os << FormatDescription(false);\
}\
virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\
*gmock_os << FormatDescription(true);\
}\
- p0##_type p0;\
- p1##_type p1;\
- p2##_type p2;\
- p3##_type p3;\
- p4##_type p4;\
- p5##_type p5;\
- p6##_type p6;\
- p7##_type p7;\
+ p0##_type const p0;\
+ p1##_type const p1;\
+ p2##_type const p2;\
+ p3##_type const p3;\
+ p4##_type const p4;\
+ p5##_type const p5;\
+ p6##_type const p6;\
+ p7##_type const p7;\
private:\
- ::testing::internal::string FormatDescription(bool negation) const {\
- const ::testing::internal::string gmock_description = (description);\
- if (!gmock_description.empty())\
+ ::std::string FormatDescription(bool negation) const {\
+ ::std::string gmock_description = (description);\
+ if (!gmock_description.empty()) {\
return gmock_description;\
+ }\
return ::testing::internal::FormatMatcherDescription(\
negation, #name, \
::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
- ::testing::tuple<p0##_type, p1##_type, p2##_type, p3##_type, \
+ ::std::tuple<p0##_type, p1##_type, p2##_type, p3##_type, \
p4##_type, p5##_type, p6##_type, p7##_type>(p0, p1, p2, \
p3, p4, p5, p6, p7)));\
}\
- GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
};\
template <typename arg_type>\
operator ::testing::Matcher<arg_type>() const {\
@@ -1947,20 +853,21 @@ AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9, M10 m10) {
name##MatcherP8(p0##_type gmock_p0, p1##_type gmock_p1, \
p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \
p5##_type gmock_p5, p6##_type gmock_p6, \
- p7##_type gmock_p7) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
- p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), \
- p7(gmock_p7) {\
+ p7##_type gmock_p7) : p0(::std::move(gmock_p0)), \
+ p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \
+ p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)), \
+ p5(::std::move(gmock_p5)), p6(::std::move(gmock_p6)), \
+ p7(::std::move(gmock_p7)) {\
}\
- p0##_type p0;\
- p1##_type p1;\
- p2##_type p2;\
- p3##_type p3;\
- p4##_type p4;\
- p5##_type p5;\
- p6##_type p6;\
- p7##_type p7;\
+ p0##_type const p0;\
+ p1##_type const p1;\
+ p2##_type const p2;\
+ p3##_type const p3;\
+ p4##_type const p4;\
+ p5##_type const p5;\
+ p6##_type const p6;\
+ p7##_type const p7;\
private:\
- GTEST_DISALLOW_ASSIGN_(name##MatcherP8);\
};\
template <typename p0##_type, typename p1##_type, typename p2##_type, \
typename p3##_type, typename p4##_type, typename p5##_type, \
@@ -1980,7 +887,7 @@ AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9, M10 m10) {
bool name##MatcherP8<p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \
p5##_type, p6##_type, \
p7##_type>::gmock_Impl<arg_type>::MatchAndExplain(\
- arg_type arg, \
+ GTEST_REFERENCE_TO_CONST_(arg_type) arg,\
::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\
const
@@ -1991,44 +898,48 @@ AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9, M10 m10) {
class name##MatcherP9 {\
public:\
template <typename arg_type>\
- class gmock_Impl : public ::testing::MatcherInterface<arg_type> {\
+ class gmock_Impl : public ::testing::MatcherInterface<\
+ GTEST_REFERENCE_TO_CONST_(arg_type)> {\
public:\
gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
p6##_type gmock_p6, p7##_type gmock_p7, p8##_type gmock_p8)\
- : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), \
- p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), p7(gmock_p7), \
- p8(gmock_p8) {}\
+ : p0(::std::move(gmock_p0)), p1(::std::move(gmock_p1)), \
+ p2(::std::move(gmock_p2)), p3(::std::move(gmock_p3)), \
+ p4(::std::move(gmock_p4)), p5(::std::move(gmock_p5)), \
+ p6(::std::move(gmock_p6)), p7(::std::move(gmock_p7)), \
+ p8(::std::move(gmock_p8)) {}\
virtual bool MatchAndExplain(\
- arg_type arg, ::testing::MatchResultListener* result_listener) const;\
+ GTEST_REFERENCE_TO_CONST_(arg_type) arg,\
+ ::testing::MatchResultListener* result_listener) const;\
virtual void DescribeTo(::std::ostream* gmock_os) const {\
*gmock_os << FormatDescription(false);\
}\
virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\
*gmock_os << FormatDescription(true);\
}\
- p0##_type p0;\
- p1##_type p1;\
- p2##_type p2;\
- p3##_type p3;\
- p4##_type p4;\
- p5##_type p5;\
- p6##_type p6;\
- p7##_type p7;\
- p8##_type p8;\
+ p0##_type const p0;\
+ p1##_type const p1;\
+ p2##_type const p2;\
+ p3##_type const p3;\
+ p4##_type const p4;\
+ p5##_type const p5;\
+ p6##_type const p6;\
+ p7##_type const p7;\
+ p8##_type const p8;\
private:\
- ::testing::internal::string FormatDescription(bool negation) const {\
- const ::testing::internal::string gmock_description = (description);\
- if (!gmock_description.empty())\
+ ::std::string FormatDescription(bool negation) const {\
+ ::std::string gmock_description = (description);\
+ if (!gmock_description.empty()) {\
return gmock_description;\
+ }\
return ::testing::internal::FormatMatcherDescription(\
negation, #name, \
::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
- ::testing::tuple<p0##_type, p1##_type, p2##_type, p3##_type, \
+ ::std::tuple<p0##_type, p1##_type, p2##_type, p3##_type, \
p4##_type, p5##_type, p6##_type, p7##_type, \
p8##_type>(p0, p1, p2, p3, p4, p5, p6, p7, p8)));\
}\
- GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
};\
template <typename arg_type>\
operator ::testing::Matcher<arg_type>() const {\
@@ -2038,21 +949,22 @@ AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9, M10 m10) {
name##MatcherP9(p0##_type gmock_p0, p1##_type gmock_p1, \
p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \
p5##_type gmock_p5, p6##_type gmock_p6, p7##_type gmock_p7, \
- p8##_type gmock_p8) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
- p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), p7(gmock_p7), \
- p8(gmock_p8) {\
+ p8##_type gmock_p8) : p0(::std::move(gmock_p0)), \
+ p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \
+ p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)), \
+ p5(::std::move(gmock_p5)), p6(::std::move(gmock_p6)), \
+ p7(::std::move(gmock_p7)), p8(::std::move(gmock_p8)) {\
}\
- p0##_type p0;\
- p1##_type p1;\
- p2##_type p2;\
- p3##_type p3;\
- p4##_type p4;\
- p5##_type p5;\
- p6##_type p6;\
- p7##_type p7;\
- p8##_type p8;\
+ p0##_type const p0;\
+ p1##_type const p1;\
+ p2##_type const p2;\
+ p3##_type const p3;\
+ p4##_type const p4;\
+ p5##_type const p5;\
+ p6##_type const p6;\
+ p7##_type const p7;\
+ p8##_type const p8;\
private:\
- GTEST_DISALLOW_ASSIGN_(name##MatcherP9);\
};\
template <typename p0##_type, typename p1##_type, typename p2##_type, \
typename p3##_type, typename p4##_type, typename p5##_type, \
@@ -2073,7 +985,7 @@ AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9, M10 m10) {
bool name##MatcherP9<p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \
p5##_type, p6##_type, p7##_type, \
p8##_type>::gmock_Impl<arg_type>::MatchAndExplain(\
- arg_type arg, \
+ GTEST_REFERENCE_TO_CONST_(arg_type) arg,\
::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\
const
@@ -2085,46 +997,50 @@ AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9, M10 m10) {
class name##MatcherP10 {\
public:\
template <typename arg_type>\
- class gmock_Impl : public ::testing::MatcherInterface<arg_type> {\
+ class gmock_Impl : public ::testing::MatcherInterface<\
+ GTEST_REFERENCE_TO_CONST_(arg_type)> {\
public:\
gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
p6##_type gmock_p6, p7##_type gmock_p7, p8##_type gmock_p8, \
p9##_type gmock_p9)\
- : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), \
- p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), p7(gmock_p7), \
- p8(gmock_p8), p9(gmock_p9) {}\
+ : p0(::std::move(gmock_p0)), p1(::std::move(gmock_p1)), \
+ p2(::std::move(gmock_p2)), p3(::std::move(gmock_p3)), \
+ p4(::std::move(gmock_p4)), p5(::std::move(gmock_p5)), \
+ p6(::std::move(gmock_p6)), p7(::std::move(gmock_p7)), \
+ p8(::std::move(gmock_p8)), p9(::std::move(gmock_p9)) {}\
virtual bool MatchAndExplain(\
- arg_type arg, ::testing::MatchResultListener* result_listener) const;\
+ GTEST_REFERENCE_TO_CONST_(arg_type) arg,\
+ ::testing::MatchResultListener* result_listener) const;\
virtual void DescribeTo(::std::ostream* gmock_os) const {\
*gmock_os << FormatDescription(false);\
}\
virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\
*gmock_os << FormatDescription(true);\
}\
- p0##_type p0;\
- p1##_type p1;\
- p2##_type p2;\
- p3##_type p3;\
- p4##_type p4;\
- p5##_type p5;\
- p6##_type p6;\
- p7##_type p7;\
- p8##_type p8;\
- p9##_type p9;\
+ p0##_type const p0;\
+ p1##_type const p1;\
+ p2##_type const p2;\
+ p3##_type const p3;\
+ p4##_type const p4;\
+ p5##_type const p5;\
+ p6##_type const p6;\
+ p7##_type const p7;\
+ p8##_type const p8;\
+ p9##_type const p9;\
private:\
- ::testing::internal::string FormatDescription(bool negation) const {\
- const ::testing::internal::string gmock_description = (description);\
- if (!gmock_description.empty())\
+ ::std::string FormatDescription(bool negation) const {\
+ ::std::string gmock_description = (description);\
+ if (!gmock_description.empty()) {\
return gmock_description;\
+ }\
return ::testing::internal::FormatMatcherDescription(\
negation, #name, \
::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
- ::testing::tuple<p0##_type, p1##_type, p2##_type, p3##_type, \
+ ::std::tuple<p0##_type, p1##_type, p2##_type, p3##_type, \
p4##_type, p5##_type, p6##_type, p7##_type, p8##_type, \
p9##_type>(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9)));\
}\
- GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
};\
template <typename arg_type>\
operator ::testing::Matcher<arg_type>() const {\
@@ -2134,22 +1050,24 @@ AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9, M10 m10) {
name##MatcherP10(p0##_type gmock_p0, p1##_type gmock_p1, \
p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \
p5##_type gmock_p5, p6##_type gmock_p6, p7##_type gmock_p7, \
- p8##_type gmock_p8, p9##_type gmock_p9) : p0(gmock_p0), p1(gmock_p1), \
- p2(gmock_p2), p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), \
- p7(gmock_p7), p8(gmock_p8), p9(gmock_p9) {\
+ p8##_type gmock_p8, p9##_type gmock_p9) : p0(::std::move(gmock_p0)), \
+ p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \
+ p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)), \
+ p5(::std::move(gmock_p5)), p6(::std::move(gmock_p6)), \
+ p7(::std::move(gmock_p7)), p8(::std::move(gmock_p8)), \
+ p9(::std::move(gmock_p9)) {\
}\
- p0##_type p0;\
- p1##_type p1;\
- p2##_type p2;\
- p3##_type p3;\
- p4##_type p4;\
- p5##_type p5;\
- p6##_type p6;\
- p7##_type p7;\
- p8##_type p8;\
- p9##_type p9;\
+ p0##_type const p0;\
+ p1##_type const p1;\
+ p2##_type const p2;\
+ p3##_type const p3;\
+ p4##_type const p4;\
+ p5##_type const p5;\
+ p6##_type const p6;\
+ p7##_type const p7;\
+ p8##_type const p8;\
+ p9##_type const p9;\
private:\
- GTEST_DISALLOW_ASSIGN_(name##MatcherP10);\
};\
template <typename p0##_type, typename p1##_type, typename p2##_type, \
typename p3##_type, typename p4##_type, typename p5##_type, \
@@ -2172,7 +1090,7 @@ AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9, M10 m10) {
bool name##MatcherP10<p0##_type, p1##_type, p2##_type, p3##_type, \
p4##_type, p5##_type, p6##_type, p7##_type, p8##_type, \
p9##_type>::gmock_Impl<arg_type>::MatchAndExplain(\
- arg_type arg, \
+ GTEST_REFERENCE_TO_CONST_(arg_type) arg,\
::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\
const
diff --git a/extern/gmock/include/gmock/gmock-generated-nice-strict.h b/extern/gmock/include/gmock/gmock-generated-nice-strict.h
deleted file mode 100644
index 4095f4d5bc7..00000000000
--- a/extern/gmock/include/gmock/gmock-generated-nice-strict.h
+++ /dev/null
@@ -1,397 +0,0 @@
-// This file was GENERATED by command:
-// pump.py gmock-generated-nice-strict.h.pump
-// DO NOT EDIT BY HAND!!!
-
-// Copyright 2008, Google Inc.
-// All rights reserved.
-//
-// 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: wan@google.com (Zhanyong Wan)
-
-// Implements class templates NiceMock, NaggyMock, and StrictMock.
-//
-// Given a mock class MockFoo that is created using Google Mock,
-// NiceMock<MockFoo> is a subclass of MockFoo that allows
-// uninteresting calls (i.e. calls to mock methods that have no
-// EXPECT_CALL specs), NaggyMock<MockFoo> is a subclass of MockFoo
-// that prints a warning when an uninteresting call occurs, and
-// StrictMock<MockFoo> is a subclass of MockFoo that treats all
-// uninteresting calls as errors.
-//
-// Currently a mock is naggy by default, so MockFoo and
-// NaggyMock<MockFoo> behave like the same. However, we will soon
-// switch the default behavior of mocks to be nice, as that in general
-// leads to more maintainable tests. When that happens, MockFoo will
-// stop behaving like NaggyMock<MockFoo> and start behaving like
-// NiceMock<MockFoo>.
-//
-// NiceMock, NaggyMock, and StrictMock "inherit" the constructors of
-// their respective base class, with up-to 10 arguments. Therefore
-// you can write NiceMock<MockFoo>(5, "a") to construct a nice mock
-// where MockFoo has a constructor that accepts (int, const char*),
-// for example.
-//
-// A known limitation is that NiceMock<MockFoo>, NaggyMock<MockFoo>,
-// and StrictMock<MockFoo> only works for mock methods defined using
-// the MOCK_METHOD* family of macros DIRECTLY in the MockFoo class.
-// If a mock method is defined in a base class of MockFoo, the "nice"
-// or "strict" modifier may not affect it, depending on the compiler.
-// In particular, nesting NiceMock, NaggyMock, and StrictMock is NOT
-// supported.
-//
-// Another known limitation is that the constructors of the base mock
-// cannot have arguments passed by non-const reference, which are
-// banned by the Google C++ style guide anyway.
-
-#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_NICE_STRICT_H_
-#define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_NICE_STRICT_H_
-
-#include "gmock/gmock-spec-builders.h"
-#include "gmock/internal/gmock-port.h"
-
-namespace testing {
-
-template <class MockClass>
-class NiceMock : public MockClass {
- public:
- // We don't factor out the constructor body to a common method, as
- // we have to avoid a possible clash with members of MockClass.
- NiceMock() {
- ::testing::Mock::AllowUninterestingCalls(
- internal::ImplicitCast_<MockClass*>(this));
- }
-
- // C++ doesn't (yet) allow inheritance of constructors, so we have
- // to define it for each arity.
- template <typename A1>
- explicit NiceMock(const A1& a1) : MockClass(a1) {
- ::testing::Mock::AllowUninterestingCalls(
- internal::ImplicitCast_<MockClass*>(this));
- }
- template <typename A1, typename A2>
- NiceMock(const A1& a1, const A2& a2) : MockClass(a1, a2) {
- ::testing::Mock::AllowUninterestingCalls(
- internal::ImplicitCast_<MockClass*>(this));
- }
-
- template <typename A1, typename A2, typename A3>
- NiceMock(const A1& a1, const A2& a2, const A3& a3) : MockClass(a1, a2, a3) {
- ::testing::Mock::AllowUninterestingCalls(
- internal::ImplicitCast_<MockClass*>(this));
- }
-
- template <typename A1, typename A2, typename A3, typename A4>
- NiceMock(const A1& a1, const A2& a2, const A3& a3,
- const A4& a4) : MockClass(a1, a2, a3, a4) {
- ::testing::Mock::AllowUninterestingCalls(
- internal::ImplicitCast_<MockClass*>(this));
- }
-
- template <typename A1, typename A2, typename A3, typename A4, typename A5>
- NiceMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
- const A5& a5) : MockClass(a1, a2, a3, a4, a5) {
- ::testing::Mock::AllowUninterestingCalls(
- internal::ImplicitCast_<MockClass*>(this));
- }
-
- template <typename A1, typename A2, typename A3, typename A4, typename A5,
- typename A6>
- NiceMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
- const A5& a5, const A6& a6) : MockClass(a1, a2, a3, a4, a5, a6) {
- ::testing::Mock::AllowUninterestingCalls(
- internal::ImplicitCast_<MockClass*>(this));
- }
-
- template <typename A1, typename A2, typename A3, typename A4, typename A5,
- typename A6, typename A7>
- NiceMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
- const A5& a5, const A6& a6, const A7& a7) : MockClass(a1, a2, a3, a4, a5,
- a6, a7) {
- ::testing::Mock::AllowUninterestingCalls(
- internal::ImplicitCast_<MockClass*>(this));
- }
-
- template <typename A1, typename A2, typename A3, typename A4, typename A5,
- typename A6, typename A7, typename A8>
- NiceMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
- const A5& a5, const A6& a6, const A7& a7, const A8& a8) : MockClass(a1,
- a2, a3, a4, a5, a6, a7, a8) {
- ::testing::Mock::AllowUninterestingCalls(
- internal::ImplicitCast_<MockClass*>(this));
- }
-
- template <typename A1, typename A2, typename A3, typename A4, typename A5,
- typename A6, typename A7, typename A8, typename A9>
- NiceMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
- const A5& a5, const A6& a6, const A7& a7, const A8& a8,
- const A9& a9) : MockClass(a1, a2, a3, a4, a5, a6, a7, a8, a9) {
- ::testing::Mock::AllowUninterestingCalls(
- internal::ImplicitCast_<MockClass*>(this));
- }
-
- template <typename A1, typename A2, typename A3, typename A4, typename A5,
- typename A6, typename A7, typename A8, typename A9, typename A10>
- NiceMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
- const A5& a5, const A6& a6, const A7& a7, const A8& a8, const A9& a9,
- const A10& a10) : MockClass(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) {
- ::testing::Mock::AllowUninterestingCalls(
- internal::ImplicitCast_<MockClass*>(this));
- }
-
- virtual ~NiceMock() {
- ::testing::Mock::UnregisterCallReaction(
- internal::ImplicitCast_<MockClass*>(this));
- }
-
- private:
- GTEST_DISALLOW_COPY_AND_ASSIGN_(NiceMock);
-};
-
-template <class MockClass>
-class NaggyMock : public MockClass {
- public:
- // We don't factor out the constructor body to a common method, as
- // we have to avoid a possible clash with members of MockClass.
- NaggyMock() {
- ::testing::Mock::WarnUninterestingCalls(
- internal::ImplicitCast_<MockClass*>(this));
- }
-
- // C++ doesn't (yet) allow inheritance of constructors, so we have
- // to define it for each arity.
- template <typename A1>
- explicit NaggyMock(const A1& a1) : MockClass(a1) {
- ::testing::Mock::WarnUninterestingCalls(
- internal::ImplicitCast_<MockClass*>(this));
- }
- template <typename A1, typename A2>
- NaggyMock(const A1& a1, const A2& a2) : MockClass(a1, a2) {
- ::testing::Mock::WarnUninterestingCalls(
- internal::ImplicitCast_<MockClass*>(this));
- }
-
- template <typename A1, typename A2, typename A3>
- NaggyMock(const A1& a1, const A2& a2, const A3& a3) : MockClass(a1, a2, a3) {
- ::testing::Mock::WarnUninterestingCalls(
- internal::ImplicitCast_<MockClass*>(this));
- }
-
- template <typename A1, typename A2, typename A3, typename A4>
- NaggyMock(const A1& a1, const A2& a2, const A3& a3,
- const A4& a4) : MockClass(a1, a2, a3, a4) {
- ::testing::Mock::WarnUninterestingCalls(
- internal::ImplicitCast_<MockClass*>(this));
- }
-
- template <typename A1, typename A2, typename A3, typename A4, typename A5>
- NaggyMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
- const A5& a5) : MockClass(a1, a2, a3, a4, a5) {
- ::testing::Mock::WarnUninterestingCalls(
- internal::ImplicitCast_<MockClass*>(this));
- }
-
- template <typename A1, typename A2, typename A3, typename A4, typename A5,
- typename A6>
- NaggyMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
- const A5& a5, const A6& a6) : MockClass(a1, a2, a3, a4, a5, a6) {
- ::testing::Mock::WarnUninterestingCalls(
- internal::ImplicitCast_<MockClass*>(this));
- }
-
- template <typename A1, typename A2, typename A3, typename A4, typename A5,
- typename A6, typename A7>
- NaggyMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
- const A5& a5, const A6& a6, const A7& a7) : MockClass(a1, a2, a3, a4, a5,
- a6, a7) {
- ::testing::Mock::WarnUninterestingCalls(
- internal::ImplicitCast_<MockClass*>(this));
- }
-
- template <typename A1, typename A2, typename A3, typename A4, typename A5,
- typename A6, typename A7, typename A8>
- NaggyMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
- const A5& a5, const A6& a6, const A7& a7, const A8& a8) : MockClass(a1,
- a2, a3, a4, a5, a6, a7, a8) {
- ::testing::Mock::WarnUninterestingCalls(
- internal::ImplicitCast_<MockClass*>(this));
- }
-
- template <typename A1, typename A2, typename A3, typename A4, typename A5,
- typename A6, typename A7, typename A8, typename A9>
- NaggyMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
- const A5& a5, const A6& a6, const A7& a7, const A8& a8,
- const A9& a9) : MockClass(a1, a2, a3, a4, a5, a6, a7, a8, a9) {
- ::testing::Mock::WarnUninterestingCalls(
- internal::ImplicitCast_<MockClass*>(this));
- }
-
- template <typename A1, typename A2, typename A3, typename A4, typename A5,
- typename A6, typename A7, typename A8, typename A9, typename A10>
- NaggyMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
- const A5& a5, const A6& a6, const A7& a7, const A8& a8, const A9& a9,
- const A10& a10) : MockClass(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) {
- ::testing::Mock::WarnUninterestingCalls(
- internal::ImplicitCast_<MockClass*>(this));
- }
-
- virtual ~NaggyMock() {
- ::testing::Mock::UnregisterCallReaction(
- internal::ImplicitCast_<MockClass*>(this));
- }
-
- private:
- GTEST_DISALLOW_COPY_AND_ASSIGN_(NaggyMock);
-};
-
-template <class MockClass>
-class StrictMock : public MockClass {
- public:
- // We don't factor out the constructor body to a common method, as
- // we have to avoid a possible clash with members of MockClass.
- StrictMock() {
- ::testing::Mock::FailUninterestingCalls(
- internal::ImplicitCast_<MockClass*>(this));
- }
-
- // C++ doesn't (yet) allow inheritance of constructors, so we have
- // to define it for each arity.
- template <typename A1>
- explicit StrictMock(const A1& a1) : MockClass(a1) {
- ::testing::Mock::FailUninterestingCalls(
- internal::ImplicitCast_<MockClass*>(this));
- }
- template <typename A1, typename A2>
- StrictMock(const A1& a1, const A2& a2) : MockClass(a1, a2) {
- ::testing::Mock::FailUninterestingCalls(
- internal::ImplicitCast_<MockClass*>(this));
- }
-
- template <typename A1, typename A2, typename A3>
- StrictMock(const A1& a1, const A2& a2, const A3& a3) : MockClass(a1, a2, a3) {
- ::testing::Mock::FailUninterestingCalls(
- internal::ImplicitCast_<MockClass*>(this));
- }
-
- template <typename A1, typename A2, typename A3, typename A4>
- StrictMock(const A1& a1, const A2& a2, const A3& a3,
- const A4& a4) : MockClass(a1, a2, a3, a4) {
- ::testing::Mock::FailUninterestingCalls(
- internal::ImplicitCast_<MockClass*>(this));
- }
-
- template <typename A1, typename A2, typename A3, typename A4, typename A5>
- StrictMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
- const A5& a5) : MockClass(a1, a2, a3, a4, a5) {
- ::testing::Mock::FailUninterestingCalls(
- internal::ImplicitCast_<MockClass*>(this));
- }
-
- template <typename A1, typename A2, typename A3, typename A4, typename A5,
- typename A6>
- StrictMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
- const A5& a5, const A6& a6) : MockClass(a1, a2, a3, a4, a5, a6) {
- ::testing::Mock::FailUninterestingCalls(
- internal::ImplicitCast_<MockClass*>(this));
- }
-
- template <typename A1, typename A2, typename A3, typename A4, typename A5,
- typename A6, typename A7>
- StrictMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
- const A5& a5, const A6& a6, const A7& a7) : MockClass(a1, a2, a3, a4, a5,
- a6, a7) {
- ::testing::Mock::FailUninterestingCalls(
- internal::ImplicitCast_<MockClass*>(this));
- }
-
- template <typename A1, typename A2, typename A3, typename A4, typename A5,
- typename A6, typename A7, typename A8>
- StrictMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
- const A5& a5, const A6& a6, const A7& a7, const A8& a8) : MockClass(a1,
- a2, a3, a4, a5, a6, a7, a8) {
- ::testing::Mock::FailUninterestingCalls(
- internal::ImplicitCast_<MockClass*>(this));
- }
-
- template <typename A1, typename A2, typename A3, typename A4, typename A5,
- typename A6, typename A7, typename A8, typename A9>
- StrictMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
- const A5& a5, const A6& a6, const A7& a7, const A8& a8,
- const A9& a9) : MockClass(a1, a2, a3, a4, a5, a6, a7, a8, a9) {
- ::testing::Mock::FailUninterestingCalls(
- internal::ImplicitCast_<MockClass*>(this));
- }
-
- template <typename A1, typename A2, typename A3, typename A4, typename A5,
- typename A6, typename A7, typename A8, typename A9, typename A10>
- StrictMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
- const A5& a5, const A6& a6, const A7& a7, const A8& a8, const A9& a9,
- const A10& a10) : MockClass(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) {
- ::testing::Mock::FailUninterestingCalls(
- internal::ImplicitCast_<MockClass*>(this));
- }
-
- virtual ~StrictMock() {
- ::testing::Mock::UnregisterCallReaction(
- internal::ImplicitCast_<MockClass*>(this));
- }
-
- private:
- GTEST_DISALLOW_COPY_AND_ASSIGN_(StrictMock);
-};
-
-// The following specializations catch some (relatively more common)
-// user errors of nesting nice and strict mocks. They do NOT catch
-// all possible errors.
-
-// These specializations are declared but not defined, as NiceMock,
-// NaggyMock, and StrictMock cannot be nested.
-
-template <typename MockClass>
-class NiceMock<NiceMock<MockClass> >;
-template <typename MockClass>
-class NiceMock<NaggyMock<MockClass> >;
-template <typename MockClass>
-class NiceMock<StrictMock<MockClass> >;
-
-template <typename MockClass>
-class NaggyMock<NiceMock<MockClass> >;
-template <typename MockClass>
-class NaggyMock<NaggyMock<MockClass> >;
-template <typename MockClass>
-class NaggyMock<StrictMock<MockClass> >;
-
-template <typename MockClass>
-class StrictMock<NiceMock<MockClass> >;
-template <typename MockClass>
-class StrictMock<NaggyMock<MockClass> >;
-template <typename MockClass>
-class StrictMock<StrictMock<MockClass> >;
-
-} // namespace testing
-
-#endif // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_NICE_STRICT_H_
diff --git a/extern/gmock/include/gmock/gmock-matchers.h b/extern/gmock/include/gmock/gmock-matchers.h
index 33b37a7a5d6..28e188bb813 100644
--- a/extern/gmock/include/gmock/gmock-matchers.h
+++ b/extern/gmock/include/gmock/gmock-matchers.h
@@ -26,36 +26,50 @@
// 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: wan@google.com (Zhanyong Wan)
+
// Google Mock - a framework for writing C++ mock classes.
//
// This file implements some commonly used argument matchers. More
// matchers can be defined by the user implementing the
// MatcherInterface<T> interface if necessary.
+//
+// See googletest/include/gtest/gtest-matchers.h for the definition of class
+// Matcher, class MatcherInterface, and others.
+
+// GOOGLETEST_CM0002 DO NOT DELETE
#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_MATCHERS_H_
#define GMOCK_INCLUDE_GMOCK_GMOCK_MATCHERS_H_
#include <math.h>
#include <algorithm>
+#include <initializer_list>
#include <iterator>
#include <limits>
+#include <memory>
#include <ostream> // NOLINT
#include <sstream>
#include <string>
+#include <type_traits>
#include <utility>
#include <vector>
-
#include "gmock/internal/gmock-internal-utils.h"
#include "gmock/internal/gmock-port.h"
#include "gtest/gtest.h"
-#if GTEST_HAS_STD_INITIALIZER_LIST_
-# include <initializer_list> // NOLINT -- must be after gtest.h
+// MSVC warning C5046 is new as of VS2017 version 15.8.
+#if defined(_MSC_VER) && _MSC_VER >= 1915
+#define GMOCK_MAYBE_5046_ 5046
+#else
+#define GMOCK_MAYBE_5046_
#endif
+GTEST_DISABLE_MSC_WARNINGS_PUSH_(
+ 4251 GMOCK_MAYBE_5046_ /* class A needs to have dll-interface to be used by
+ clients of class B */
+ /* Symbol involving type with internal linkage not defined */)
+
namespace testing {
// To implement a matcher Foo for type T, define:
@@ -70,123 +84,13 @@ namespace testing {
// ownership management as Matcher objects can now be copied like
// plain values.
-// MatchResultListener is an abstract class. Its << operator can be
-// used by a matcher to explain why a value matches or doesn't match.
-//
-// TODO(wan@google.com): add method
-// bool InterestedInWhy(bool result) const;
-// to indicate whether the listener is interested in why the match
-// result is 'result'.
-class MatchResultListener {
- public:
- // Creates a listener object with the given underlying ostream. The
- // listener does not own the ostream, and does not dereference it
- // in the constructor or destructor.
- explicit MatchResultListener(::std::ostream* os) : stream_(os) {}
- virtual ~MatchResultListener() = 0; // Makes this class abstract.
-
- // Streams x to the underlying ostream; does nothing if the ostream
- // is NULL.
- template <typename T>
- MatchResultListener& operator<<(const T& x) {
- if (stream_ != NULL)
- *stream_ << x;
- return *this;
- }
-
- // Returns the underlying ostream.
- ::std::ostream* stream() { return stream_; }
-
- // Returns true iff the listener is interested in an explanation of
- // the match result. A matcher's MatchAndExplain() method can use
- // this information to avoid generating the explanation when no one
- // intends to hear it.
- bool IsInterested() const { return stream_ != NULL; }
-
- private:
- ::std::ostream* const stream_;
-
- GTEST_DISALLOW_COPY_AND_ASSIGN_(MatchResultListener);
-};
-
-inline MatchResultListener::~MatchResultListener() {
-}
-
-// An instance of a subclass of this knows how to describe itself as a
-// matcher.
-class MatcherDescriberInterface {
- public:
- virtual ~MatcherDescriberInterface() {}
-
- // Describes this matcher to an ostream. The function should print
- // a verb phrase that describes the property a value matching this
- // matcher should have. The subject of the verb phrase is the value
- // being matched. For example, the DescribeTo() method of the Gt(7)
- // matcher prints "is greater than 7".
- virtual void DescribeTo(::std::ostream* os) const = 0;
-
- // Describes the negation of this matcher to an ostream. For
- // example, if the description of this matcher is "is greater than
- // 7", the negated description could be "is not greater than 7".
- // You are not required to override this when implementing
- // MatcherInterface, but it is highly advised so that your matcher
- // can produce good error messages.
- virtual void DescribeNegationTo(::std::ostream* os) const {
- *os << "not (";
- DescribeTo(os);
- *os << ")";
- }
-};
-
-// The implementation of a matcher.
-template <typename T>
-class MatcherInterface : public MatcherDescriberInterface {
- public:
- // Returns true iff the matcher matches x; also explains the match
- // result to 'listener' if necessary (see the next paragraph), in
- // the form of a non-restrictive relative clause ("which ...",
- // "whose ...", etc) that describes x. For example, the
- // MatchAndExplain() method of the Pointee(...) matcher should
- // generate an explanation like "which points to ...".
- //
- // Implementations of MatchAndExplain() should add an explanation of
- // the match result *if and only if* they can provide additional
- // information that's not already present (or not obvious) in the
- // print-out of x and the matcher's description. Whether the match
- // succeeds is not a factor in deciding whether an explanation is
- // needed, as sometimes the caller needs to print a failure message
- // when the match succeeds (e.g. when the matcher is used inside
- // Not()).
- //
- // For example, a "has at least 10 elements" matcher should explain
- // what the actual element count is, regardless of the match result,
- // as it is useful information to the reader; on the other hand, an
- // "is empty" matcher probably only needs to explain what the actual
- // size is when the match fails, as it's redundant to say that the
- // size is 0 when the value is already known to be empty.
- //
- // You should override this method when defining a new matcher.
- //
- // It's the responsibility of the caller (Google Mock) to guarantee
- // that 'listener' is not NULL. This helps to simplify a matcher's
- // implementation when it doesn't care about the performance, as it
- // can talk to 'listener' without checking its validity first.
- // However, in order to implement dummy listeners efficiently,
- // listener->stream() may be NULL.
- virtual bool MatchAndExplain(T x, MatchResultListener* listener) const = 0;
-
- // Inherits these methods from MatcherDescriberInterface:
- // virtual void DescribeTo(::std::ostream* os) const = 0;
- // virtual void DescribeNegationTo(::std::ostream* os) const;
-};
-
// A match result listener that stores the explanation in a string.
class StringMatchResultListener : public MatchResultListener {
public:
StringMatchResultListener() : MatchResultListener(&ss_) {}
// Returns the explanation accumulated so far.
- internal::string str() const { return ss_.str(); }
+ std::string str() const { return ss_.str(); }
// Clears the explanation accumulated so far.
void Clear() { ss_.str(""); }
@@ -197,306 +101,6 @@ class StringMatchResultListener : public MatchResultListener {
GTEST_DISALLOW_COPY_AND_ASSIGN_(StringMatchResultListener);
};
-namespace internal {
-
-struct AnyEq {
- template <typename A, typename B>
- bool operator()(const A& a, const B& b) const { return a == b; }
-};
-struct AnyNe {
- template <typename A, typename B>
- bool operator()(const A& a, const B& b) const { return a != b; }
-};
-struct AnyLt {
- template <typename A, typename B>
- bool operator()(const A& a, const B& b) const { return a < b; }
-};
-struct AnyGt {
- template <typename A, typename B>
- bool operator()(const A& a, const B& b) const { return a > b; }
-};
-struct AnyLe {
- template <typename A, typename B>
- bool operator()(const A& a, const B& b) const { return a <= b; }
-};
-struct AnyGe {
- template <typename A, typename B>
- bool operator()(const A& a, const B& b) const { return a >= b; }
-};
-
-// A match result listener that ignores the explanation.
-class DummyMatchResultListener : public MatchResultListener {
- public:
- DummyMatchResultListener() : MatchResultListener(NULL) {}
-
- private:
- GTEST_DISALLOW_COPY_AND_ASSIGN_(DummyMatchResultListener);
-};
-
-// A match result listener that forwards the explanation to a given
-// ostream. The difference between this and MatchResultListener is
-// that the former is concrete.
-class StreamMatchResultListener : public MatchResultListener {
- public:
- explicit StreamMatchResultListener(::std::ostream* os)
- : MatchResultListener(os) {}
-
- private:
- GTEST_DISALLOW_COPY_AND_ASSIGN_(StreamMatchResultListener);
-};
-
-// An internal class for implementing Matcher<T>, which will derive
-// from it. We put functionalities common to all Matcher<T>
-// specializations here to avoid code duplication.
-template <typename T>
-class MatcherBase {
- public:
- // Returns true iff the matcher matches x; also explains the match
- // result to 'listener'.
- bool MatchAndExplain(T x, MatchResultListener* listener) const {
- return impl_->MatchAndExplain(x, listener);
- }
-
- // Returns true iff this matcher matches x.
- bool Matches(T x) const {
- DummyMatchResultListener dummy;
- return MatchAndExplain(x, &dummy);
- }
-
- // Describes this matcher to an ostream.
- void DescribeTo(::std::ostream* os) const { impl_->DescribeTo(os); }
-
- // Describes the negation of this matcher to an ostream.
- void DescribeNegationTo(::std::ostream* os) const {
- impl_->DescribeNegationTo(os);
- }
-
- // Explains why x matches, or doesn't match, the matcher.
- void ExplainMatchResultTo(T x, ::std::ostream* os) const {
- StreamMatchResultListener listener(os);
- MatchAndExplain(x, &listener);
- }
-
- // Returns the describer for this matcher object; retains ownership
- // of the describer, which is only guaranteed to be alive when
- // this matcher object is alive.
- const MatcherDescriberInterface* GetDescriber() const {
- return impl_.get();
- }
-
- protected:
- MatcherBase() {}
-
- // Constructs a matcher from its implementation.
- explicit MatcherBase(const MatcherInterface<T>* impl)
- : impl_(impl) {}
-
- virtual ~MatcherBase() {}
-
- private:
- // shared_ptr (util/gtl/shared_ptr.h) and linked_ptr have similar
- // interfaces. The former dynamically allocates a chunk of memory
- // to hold the reference count, while the latter tracks all
- // references using a circular linked list without allocating
- // memory. It has been observed that linked_ptr performs better in
- // typical scenarios. However, shared_ptr can out-perform
- // linked_ptr when there are many more uses of the copy constructor
- // than the default constructor.
- //
- // If performance becomes a problem, we should see if using
- // shared_ptr helps.
- ::testing::internal::linked_ptr<const MatcherInterface<T> > impl_;
-};
-
-} // namespace internal
-
-// A Matcher<T> is a copyable and IMMUTABLE (except by assignment)
-// object that can check whether a value of type T matches. The
-// implementation of Matcher<T> is just a linked_ptr to const
-// MatcherInterface<T>, so copying is fairly cheap. Don't inherit
-// from Matcher!
-template <typename T>
-class Matcher : public internal::MatcherBase<T> {
- public:
- // Constructs a null matcher. Needed for storing Matcher objects in STL
- // containers. A default-constructed matcher is not yet initialized. You
- // cannot use it until a valid value has been assigned to it.
- explicit Matcher() {} // NOLINT
-
- // Constructs a matcher from its implementation.
- explicit Matcher(const MatcherInterface<T>* impl)
- : internal::MatcherBase<T>(impl) {}
-
- // Implicit constructor here allows people to write
- // EXPECT_CALL(foo, Bar(5)) instead of EXPECT_CALL(foo, Bar(Eq(5))) sometimes
- Matcher(T value); // NOLINT
-};
-
-// The following two specializations allow the user to write str
-// instead of Eq(str) and "foo" instead of Eq("foo") when a string
-// matcher is expected.
-template <>
-class GTEST_API_ Matcher<const internal::string&>
- : public internal::MatcherBase<const internal::string&> {
- public:
- Matcher() {}
-
- explicit Matcher(const MatcherInterface<const internal::string&>* impl)
- : internal::MatcherBase<const internal::string&>(impl) {}
-
- // Allows the user to write str instead of Eq(str) sometimes, where
- // str is a string object.
- Matcher(const internal::string& s); // NOLINT
-
- // Allows the user to write "foo" instead of Eq("foo") sometimes.
- Matcher(const char* s); // NOLINT
-};
-
-template <>
-class GTEST_API_ Matcher<internal::string>
- : public internal::MatcherBase<internal::string> {
- public:
- Matcher() {}
-
- explicit Matcher(const MatcherInterface<internal::string>* impl)
- : internal::MatcherBase<internal::string>(impl) {}
-
- // Allows the user to write str instead of Eq(str) sometimes, where
- // str is a string object.
- Matcher(const internal::string& s); // NOLINT
-
- // Allows the user to write "foo" instead of Eq("foo") sometimes.
- Matcher(const char* s); // NOLINT
-};
-
-#if GTEST_HAS_STRING_PIECE_
-// The following two specializations allow the user to write str
-// instead of Eq(str) and "foo" instead of Eq("foo") when a StringPiece
-// matcher is expected.
-template <>
-class GTEST_API_ Matcher<const StringPiece&>
- : public internal::MatcherBase<const StringPiece&> {
- public:
- Matcher() {}
-
- explicit Matcher(const MatcherInterface<const StringPiece&>* impl)
- : internal::MatcherBase<const StringPiece&>(impl) {}
-
- // Allows the user to write str instead of Eq(str) sometimes, where
- // str is a string object.
- Matcher(const internal::string& s); // NOLINT
-
- // Allows the user to write "foo" instead of Eq("foo") sometimes.
- Matcher(const char* s); // NOLINT
-
- // Allows the user to pass StringPieces directly.
- Matcher(StringPiece s); // NOLINT
-};
-
-template <>
-class GTEST_API_ Matcher<StringPiece>
- : public internal::MatcherBase<StringPiece> {
- public:
- Matcher() {}
-
- explicit Matcher(const MatcherInterface<StringPiece>* impl)
- : internal::MatcherBase<StringPiece>(impl) {}
-
- // Allows the user to write str instead of Eq(str) sometimes, where
- // str is a string object.
- Matcher(const internal::string& s); // NOLINT
-
- // Allows the user to write "foo" instead of Eq("foo") sometimes.
- Matcher(const char* s); // NOLINT
-
- // Allows the user to pass StringPieces directly.
- Matcher(StringPiece s); // NOLINT
-};
-#endif // GTEST_HAS_STRING_PIECE_
-
-// The PolymorphicMatcher class template makes it easy to implement a
-// polymorphic matcher (i.e. a matcher that can match values of more
-// than one type, e.g. Eq(n) and NotNull()).
-//
-// To define a polymorphic matcher, a user should provide an Impl
-// class that has a DescribeTo() method and a DescribeNegationTo()
-// method, and define a member function (or member function template)
-//
-// bool MatchAndExplain(const Value& value,
-// MatchResultListener* listener) const;
-//
-// See the definition of NotNull() for a complete example.
-template <class Impl>
-class PolymorphicMatcher {
- public:
- explicit PolymorphicMatcher(const Impl& an_impl) : impl_(an_impl) {}
-
- // Returns a mutable reference to the underlying matcher
- // implementation object.
- Impl& mutable_impl() { return impl_; }
-
- // Returns an immutable reference to the underlying matcher
- // implementation object.
- const Impl& impl() const { return impl_; }
-
- template <typename T>
- operator Matcher<T>() const {
- return Matcher<T>(new MonomorphicImpl<T>(impl_));
- }
-
- private:
- template <typename T>
- class MonomorphicImpl : public MatcherInterface<T> {
- public:
- explicit MonomorphicImpl(const Impl& impl) : impl_(impl) {}
-
- virtual void DescribeTo(::std::ostream* os) const {
- impl_.DescribeTo(os);
- }
-
- virtual void DescribeNegationTo(::std::ostream* os) const {
- impl_.DescribeNegationTo(os);
- }
-
- virtual bool MatchAndExplain(T x, MatchResultListener* listener) const {
- return impl_.MatchAndExplain(x, listener);
- }
-
- private:
- const Impl impl_;
-
- GTEST_DISALLOW_ASSIGN_(MonomorphicImpl);
- };
-
- Impl impl_;
-
- GTEST_DISALLOW_ASSIGN_(PolymorphicMatcher);
-};
-
-// Creates a matcher from its implementation. This is easier to use
-// than the Matcher<T> constructor as it doesn't require you to
-// explicitly write the template argument, e.g.
-//
-// MakeMatcher(foo);
-// vs
-// Matcher<const string&>(foo);
-template <typename T>
-inline Matcher<T> MakeMatcher(const MatcherInterface<T>* impl) {
- return Matcher<T>(impl);
-}
-
-// Creates a polymorphic matcher from its implementation. This is
-// easier to use than the PolymorphicMatcher<Impl> constructor as it
-// doesn't require you to explicitly write the template argument, e.g.
-//
-// MakePolymorphicMatcher(foo);
-// vs
-// PolymorphicMatcher<TypeOfFoo>(foo);
-template <class Impl>
-inline PolymorphicMatcher<Impl> MakePolymorphicMatcher(const Impl& impl) {
- return PolymorphicMatcher<Impl>(impl);
-}
-
// Anything inside the 'internal' namespace IS INTERNAL IMPLEMENTATION
// and MUST NOT BE USED IN USER CODE!!!
namespace internal {
@@ -515,7 +119,7 @@ template <typename T, typename M>
class MatcherCastImpl {
public:
static Matcher<T> Cast(const M& polymorphic_matcher_or_value) {
- // M can be a polymorhic matcher, in which case we want to use
+ // M can be a polymorphic matcher, in which case we want to use
// its conversion operator to create Matcher<T>. Or it can be a value
// that should be passed to the Matcher<T>'s constructor.
//
@@ -528,24 +132,18 @@ class MatcherCastImpl {
// polymorphic_matcher_or_value to Matcher<T> because it won't trigger
// a user-defined conversion from M to T if one exists (assuming M is
// a value).
- return CastImpl(
- polymorphic_matcher_or_value,
- BooleanConstant<
- internal::ImplicitlyConvertible<M, Matcher<T> >::value>());
+ return CastImpl(polymorphic_matcher_or_value,
+ std::is_convertible<M, Matcher<T>>{},
+ std::is_convertible<M, T>{});
}
private:
- static Matcher<T> CastImpl(const M& value, BooleanConstant<false>) {
- // M can't be implicitly converted to Matcher<T>, so M isn't a polymorphic
- // matcher. It must be a value then. Use direct initialization to create
- // a matcher.
- return Matcher<T>(ImplicitCast_<T>(value));
- }
-
+ template <bool Ignore>
static Matcher<T> CastImpl(const M& polymorphic_matcher_or_value,
- BooleanConstant<true>) {
+ std::true_type /* convertible_to_matcher */,
+ bool_constant<Ignore>) {
// M is implicitly convertible to Matcher<T>, which means that either
- // M is a polymorhpic matcher or Matcher<T> has an implicit constructor
+ // M is a polymorphic matcher or Matcher<T> has an implicit constructor
// from M. In both cases using the implicit conversion will produce a
// matcher.
//
@@ -554,6 +152,29 @@ class MatcherCastImpl {
// (first to create T from M and then to create Matcher<T> from T).
return polymorphic_matcher_or_value;
}
+
+ // M can't be implicitly converted to Matcher<T>, so M isn't a polymorphic
+ // matcher. It's a value of a type implicitly convertible to T. Use direct
+ // initialization to create a matcher.
+ static Matcher<T> CastImpl(const M& value,
+ std::false_type /* convertible_to_matcher */,
+ std::true_type /* convertible_to_T */) {
+ return Matcher<T>(ImplicitCast_<T>(value));
+ }
+
+ // M can't be implicitly converted to either Matcher<T> or T. Attempt to use
+ // polymorphic matcher Eq(value) in this case.
+ //
+ // Note that we first attempt to perform an implicit cast on the value and
+ // only fall back to the polymorphic Eq() matcher afterwards because the
+ // latter calls bool operator==(const Lhs& lhs, const Rhs& rhs) in the end
+ // which might be undefined even when Rhs is implicitly convertible to Lhs
+ // (e.g. std::pair<const int, int> vs. std::pair<int, int>).
+ //
+ // We don't define this method inline as we need the declaration of Eq().
+ static Matcher<T> CastImpl(const M& value,
+ std::false_type /* convertible_to_matcher */,
+ std::false_type /* convertible_to_T */);
};
// This more specialized version is used when MatcherCast()'s argument
@@ -573,15 +194,29 @@ class MatcherCastImpl<T, Matcher<U> > {
: source_matcher_(source_matcher) {}
// We delegate the matching logic to the source matcher.
- virtual bool MatchAndExplain(T x, MatchResultListener* listener) const {
+ bool MatchAndExplain(T x, MatchResultListener* listener) const override {
+ using FromType = typename std::remove_cv<typename std::remove_pointer<
+ typename std::remove_reference<T>::type>::type>::type;
+ using ToType = typename std::remove_cv<typename std::remove_pointer<
+ typename std::remove_reference<U>::type>::type>::type;
+ // Do not allow implicitly converting base*/& to derived*/&.
+ static_assert(
+ // Do not trigger if only one of them is a pointer. That implies a
+ // regular conversion and not a down_cast.
+ (std::is_pointer<typename std::remove_reference<T>::type>::value !=
+ std::is_pointer<typename std::remove_reference<U>::type>::value) ||
+ std::is_same<FromType, ToType>::value ||
+ !std::is_base_of<FromType, ToType>::value,
+ "Can't implicitly convert from <base> to <derived>");
+
return source_matcher_.MatchAndExplain(static_cast<U>(x), listener);
}
- virtual void DescribeTo(::std::ostream* os) const {
+ void DescribeTo(::std::ostream* os) const override {
source_matcher_.DescribeTo(os);
}
- virtual void DescribeNegationTo(::std::ostream* os) const {
+ void DescribeNegationTo(::std::ostream* os) const override {
source_matcher_.DescribeNegationTo(os);
}
@@ -613,11 +248,8 @@ inline Matcher<T> MatcherCast(const M& matcher) {
// Implements SafeMatcherCast().
//
-// We use an intermediate class to do the actual safe casting as Nokia's
-// Symbian compiler cannot decide between
-// template <T, M> ... (M) and
-// template <T, U> ... (const Matcher<U>&)
-// for function templates but can for member function templates.
+// FIXME: The intermediate SafeMatcherCastImpl class was introduced as a
+// workaround for a compiler bug, and can now be removed.
template <typename T>
class SafeMatcherCastImpl {
public:
@@ -640,13 +272,13 @@ class SafeMatcherCastImpl {
template <typename U>
static inline Matcher<T> Cast(const Matcher<U>& matcher) {
// Enforce that T can be implicitly converted to U.
- GTEST_COMPILE_ASSERT_((internal::ImplicitlyConvertible<T, U>::value),
- T_must_be_implicitly_convertible_to_U);
+ GTEST_COMPILE_ASSERT_((std::is_convertible<T, U>::value),
+ "T must be implicitly convertible to U");
// Enforce that we are not converting a non-reference type T to a reference
// type U.
GTEST_COMPILE_ASSERT_(
- internal::is_reference<T>::value || !internal::is_reference<U>::value,
- cannot_convert_non_referentce_arg_to_reference);
+ std::is_reference<T>::value || !std::is_reference<U>::value,
+ cannot_convert_non_reference_arg_to_reference);
// In case both T and U are arithmetic types, enforce that the
// conversion is not lossy.
typedef GTEST_REMOVE_REFERENCE_AND_CONST_(T) RawT;
@@ -675,9 +307,9 @@ Matcher<T> A();
namespace internal {
// If the explanation is not empty, prints it to the ostream.
-inline void PrintIfNotEmpty(const internal::string& explanation,
+inline void PrintIfNotEmpty(const std::string& explanation,
::std::ostream* os) {
- if (explanation != "" && os != NULL) {
+ if (explanation != "" && os != nullptr) {
*os << ", " << explanation;
}
}
@@ -685,11 +317,11 @@ inline void PrintIfNotEmpty(const internal::string& explanation,
// Returns true if the given type name is easy to read by a human.
// This is used to decide whether printing the type of a value might
// be helpful.
-inline bool IsReadableTypeName(const string& type_name) {
+inline bool IsReadableTypeName(const std::string& type_name) {
// We consider a type name readable if it's short or doesn't contain
// a template or function type.
return (type_name.length() <= 20 ||
- type_name.find_first_of("<(") == string::npos);
+ type_name.find_first_of("<(") == std::string::npos);
}
// Matches the value against the given matcher, prints the value and explains
@@ -711,7 +343,7 @@ bool MatchPrintAndExplain(Value& value, const Matcher<T>& matcher,
UniversalPrint(value, listener->stream());
#if GTEST_HAS_RTTI
- const string& type_name = GetTypeName<Value>();
+ const std::string& type_name = GetTypeName<Value>();
if (IsReadableTypeName(type_name))
*listener->stream() << " (of type " << type_name << ")";
#endif
@@ -726,13 +358,13 @@ template <size_t N>
class TuplePrefix {
public:
// TuplePrefix<N>::Matches(matcher_tuple, value_tuple) returns true
- // iff the first N fields of matcher_tuple matches the first N
- // fields of value_tuple, respectively.
+ // if and only if the first N fields of matcher_tuple matches
+ // the first N fields of value_tuple, respectively.
template <typename MatcherTuple, typename ValueTuple>
static bool Matches(const MatcherTuple& matcher_tuple,
const ValueTuple& value_tuple) {
- return TuplePrefix<N - 1>::Matches(matcher_tuple, value_tuple)
- && get<N - 1>(matcher_tuple).Matches(get<N - 1>(value_tuple));
+ return TuplePrefix<N - 1>::Matches(matcher_tuple, value_tuple) &&
+ std::get<N - 1>(matcher_tuple).Matches(std::get<N - 1>(value_tuple));
}
// TuplePrefix<N>::ExplainMatchFailuresTo(matchers, values, os)
@@ -748,16 +380,14 @@ class TuplePrefix {
// Then describes the failure (if any) in the (N - 1)-th (0-based)
// field.
- typename tuple_element<N - 1, MatcherTuple>::type matcher =
- get<N - 1>(matchers);
- typedef typename tuple_element<N - 1, ValueTuple>::type Value;
- Value value = get<N - 1>(values);
+ typename std::tuple_element<N - 1, MatcherTuple>::type matcher =
+ std::get<N - 1>(matchers);
+ typedef typename std::tuple_element<N - 1, ValueTuple>::type Value;
+ const Value& value = std::get<N - 1>(values);
StringMatchResultListener listener;
if (!matcher.MatchAndExplain(value, &listener)) {
- // TODO(wan): include in the message the name of the parameter
- // as used in MOCK_METHOD*() when possible.
*os << " Expected arg #" << N - 1 << ": ";
- get<N - 1>(matchers).DescribeTo(os);
+ std::get<N - 1>(matchers).DescribeTo(os);
*os << "\n Actual: ";
// We remove the reference in type Value to prevent the
// universal printer from printing the address of value, which
@@ -787,8 +417,8 @@ class TuplePrefix<0> {
::std::ostream* /* os */) {}
};
-// TupleMatches(matcher_tuple, value_tuple) returns true iff all
-// matchers in matcher_tuple match the corresponding fields in
+// TupleMatches(matcher_tuple, value_tuple) returns true if and only if
+// all matchers in matcher_tuple match the corresponding fields in
// value_tuple. It is a compiler error if matcher_tuple and
// value_tuple have different number of fields or incompatible field
// types.
@@ -797,11 +427,11 @@ bool TupleMatches(const MatcherTuple& matcher_tuple,
const ValueTuple& value_tuple) {
// Makes sure that matcher_tuple and value_tuple have the same
// number of fields.
- GTEST_COMPILE_ASSERT_(tuple_size<MatcherTuple>::value ==
- tuple_size<ValueTuple>::value,
+ GTEST_COMPILE_ASSERT_(std::tuple_size<MatcherTuple>::value ==
+ std::tuple_size<ValueTuple>::value,
matcher_and_value_have_different_numbers_of_fields);
- return TuplePrefix<tuple_size<ValueTuple>::value>::
- Matches(matcher_tuple, value_tuple);
+ return TuplePrefix<std::tuple_size<ValueTuple>::value>::Matches(matcher_tuple,
+ value_tuple);
}
// Describes failures in matching matchers against values. If there
@@ -810,7 +440,7 @@ template <typename MatcherTuple, typename ValueTuple>
void ExplainMatchFailureTupleTo(const MatcherTuple& matchers,
const ValueTuple& values,
::std::ostream* os) {
- TuplePrefix<tuple_size<MatcherTuple>::value>::ExplainMatchFailuresTo(
+ TuplePrefix<std::tuple_size<MatcherTuple>::value>::ExplainMatchFailuresTo(
matchers, values, os);
}
@@ -821,7 +451,7 @@ void ExplainMatchFailureTupleTo(const MatcherTuple& matchers,
template <typename Tuple, typename Func, typename OutIter>
class TransformTupleValuesHelper {
private:
- typedef ::testing::tuple_size<Tuple> TupleSize;
+ typedef ::std::tuple_size<Tuple> TupleSize;
public:
// For each member of tuple 't', taken in order, evaluates '*out++ = f(t)'.
@@ -834,7 +464,7 @@ class TransformTupleValuesHelper {
template <typename Tup, size_t kRemainingSize>
struct IterateOverTuple {
OutIter operator() (Func f, const Tup& t, OutIter out) const {
- *out++ = f(::testing::get<TupleSize::value - kRemainingSize>(t));
+ *out++ = f(::std::get<TupleSize::value - kRemainingSize>(t));
return IterateOverTuple<Tup, kRemainingSize - 1>()(f, t, out);
}
};
@@ -856,12 +486,14 @@ OutIter TransformTupleValues(Func f, const Tuple& t, OutIter out) {
// Implements A<T>().
template <typename T>
-class AnyMatcherImpl : public MatcherInterface<T> {
+class AnyMatcherImpl : public MatcherInterface<const T&> {
public:
- virtual bool MatchAndExplain(
- T /* x */, MatchResultListener* /* listener */) const { return true; }
- virtual void DescribeTo(::std::ostream* os) const { *os << "is anything"; }
- virtual void DescribeNegationTo(::std::ostream* os) const {
+ bool MatchAndExplain(const T& /* x */,
+ MatchResultListener* /* listener */) const override {
+ return true;
+ }
+ void DescribeTo(::std::ostream* os) const override { *os << "is anything"; }
+ void DescribeNegationTo(::std::ostream* os) const override {
// This is mostly for completeness' safe, as it's not very useful
// to write Not(A<bool>()). However we cannot completely rule out
// such a possibility, and it doesn't hurt to be prepared.
@@ -879,99 +511,6 @@ class AnythingMatcher {
operator Matcher<T>() const { return A<T>(); }
};
-// Implements a matcher that compares a given value with a
-// pre-supplied value using one of the ==, <=, <, etc, operators. The
-// two values being compared don't have to have the same type.
-//
-// The matcher defined here is polymorphic (for example, Eq(5) can be
-// used to match an int, a short, a double, etc). Therefore we use
-// a template type conversion operator in the implementation.
-//
-// The following template definition assumes that the Rhs parameter is
-// a "bare" type (i.e. neither 'const T' nor 'T&').
-template <typename D, typename Rhs, typename Op>
-class ComparisonBase {
- public:
- explicit ComparisonBase(const Rhs& rhs) : rhs_(rhs) {}
- template <typename Lhs>
- operator Matcher<Lhs>() const {
- return MakeMatcher(new Impl<Lhs>(rhs_));
- }
-
- private:
- template <typename Lhs>
- class Impl : public MatcherInterface<Lhs> {
- public:
- explicit Impl(const Rhs& rhs) : rhs_(rhs) {}
- virtual bool MatchAndExplain(
- Lhs lhs, MatchResultListener* /* listener */) const {
- return Op()(lhs, rhs_);
- }
- virtual void DescribeTo(::std::ostream* os) const {
- *os << D::Desc() << " ";
- UniversalPrint(rhs_, os);
- }
- virtual void DescribeNegationTo(::std::ostream* os) const {
- *os << D::NegatedDesc() << " ";
- UniversalPrint(rhs_, os);
- }
- private:
- Rhs rhs_;
- GTEST_DISALLOW_ASSIGN_(Impl);
- };
- Rhs rhs_;
- GTEST_DISALLOW_ASSIGN_(ComparisonBase);
-};
-
-template <typename Rhs>
-class EqMatcher : public ComparisonBase<EqMatcher<Rhs>, Rhs, AnyEq> {
- public:
- explicit EqMatcher(const Rhs& rhs)
- : ComparisonBase<EqMatcher<Rhs>, Rhs, AnyEq>(rhs) { }
- static const char* Desc() { return "is equal to"; }
- static const char* NegatedDesc() { return "isn't equal to"; }
-};
-template <typename Rhs>
-class NeMatcher : public ComparisonBase<NeMatcher<Rhs>, Rhs, AnyNe> {
- public:
- explicit NeMatcher(const Rhs& rhs)
- : ComparisonBase<NeMatcher<Rhs>, Rhs, AnyNe>(rhs) { }
- static const char* Desc() { return "isn't equal to"; }
- static const char* NegatedDesc() { return "is equal to"; }
-};
-template <typename Rhs>
-class LtMatcher : public ComparisonBase<LtMatcher<Rhs>, Rhs, AnyLt> {
- public:
- explicit LtMatcher(const Rhs& rhs)
- : ComparisonBase<LtMatcher<Rhs>, Rhs, AnyLt>(rhs) { }
- static const char* Desc() { return "is <"; }
- static const char* NegatedDesc() { return "isn't <"; }
-};
-template <typename Rhs>
-class GtMatcher : public ComparisonBase<GtMatcher<Rhs>, Rhs, AnyGt> {
- public:
- explicit GtMatcher(const Rhs& rhs)
- : ComparisonBase<GtMatcher<Rhs>, Rhs, AnyGt>(rhs) { }
- static const char* Desc() { return "is >"; }
- static const char* NegatedDesc() { return "isn't >"; }
-};
-template <typename Rhs>
-class LeMatcher : public ComparisonBase<LeMatcher<Rhs>, Rhs, AnyLe> {
- public:
- explicit LeMatcher(const Rhs& rhs)
- : ComparisonBase<LeMatcher<Rhs>, Rhs, AnyLe>(rhs) { }
- static const char* Desc() { return "is <="; }
- static const char* NegatedDesc() { return "isn't <="; }
-};
-template <typename Rhs>
-class GeMatcher : public ComparisonBase<GeMatcher<Rhs>, Rhs, AnyGe> {
- public:
- explicit GeMatcher(const Rhs& rhs)
- : ComparisonBase<GeMatcher<Rhs>, Rhs, AnyGe>(rhs) { }
- static const char* Desc() { return "is >="; }
- static const char* NegatedDesc() { return "isn't >="; }
-};
-
// Implements the polymorphic IsNull() matcher, which matches any raw or smart
// pointer that is NULL.
class IsNullMatcher {
@@ -979,11 +518,7 @@ class IsNullMatcher {
template <typename Pointer>
bool MatchAndExplain(const Pointer& p,
MatchResultListener* /* listener */) const {
-#if GTEST_LANG_CXX11
return p == nullptr;
-#else // GTEST_LANG_CXX11
- return GetRawPointer(p) == NULL;
-#endif // GTEST_LANG_CXX11
}
void DescribeTo(::std::ostream* os) const { *os << "is NULL"; }
@@ -999,11 +534,7 @@ class NotNullMatcher {
template <typename Pointer>
bool MatchAndExplain(const Pointer& p,
MatchResultListener* /* listener */) const {
-#if GTEST_LANG_CXX11
return p != nullptr;
-#else // GTEST_LANG_CXX11
- return GetRawPointer(p) != NULL;
-#endif // GTEST_LANG_CXX11
}
void DescribeTo(::std::ostream* os) const { *os << "isn't NULL"; }
@@ -1059,18 +590,18 @@ class RefMatcher<T&> {
// MatchAndExplain() takes a Super& (as opposed to const Super&)
// in order to match the interface MatcherInterface<Super&>.
- virtual bool MatchAndExplain(
- Super& x, MatchResultListener* listener) const {
+ bool MatchAndExplain(Super& x,
+ MatchResultListener* listener) const override {
*listener << "which is located @" << static_cast<const void*>(&x);
return &x == &object_;
}
- virtual void DescribeTo(::std::ostream* os) const {
+ void DescribeTo(::std::ostream* os) const override {
*os << "references the variable ";
UniversalPrinter<Super&>::Print(object_, os);
}
- virtual void DescribeNegationTo(::std::ostream* os) const {
+ void DescribeNegationTo(::std::ostream* os) const override {
*os << "does not reference the variable ";
UniversalPrinter<Super&>::Print(object_, os);
}
@@ -1129,6 +660,16 @@ class StrEqualityMatcher {
bool case_sensitive)
: string_(str), expect_eq_(expect_eq), case_sensitive_(case_sensitive) {}
+#if GTEST_HAS_ABSL
+ bool MatchAndExplain(const absl::string_view& s,
+ MatchResultListener* listener) const {
+ // This should fail to compile if absl::string_view is used with wide
+ // strings.
+ const StringType& str = std::string(s);
+ return MatchAndExplain(str, listener);
+ }
+#endif // GTEST_HAS_ABSL
+
// Accepts pointer types, particularly:
// const char*
// char*
@@ -1136,7 +677,7 @@ class StrEqualityMatcher {
// wchar_t*
template <typename CharType>
bool MatchAndExplain(CharType* s, MatchResultListener* listener) const {
- if (s == NULL) {
+ if (s == nullptr) {
return !expect_eq_;
}
return MatchAndExplain(StringType(s), listener);
@@ -1145,7 +686,7 @@ class StrEqualityMatcher {
// Matches anything that can convert to StringType.
//
// This is a template, not just a plain function with const StringType&,
- // because StringPiece has some interfering non-explicit constructors.
+ // because absl::string_view has some interfering non-explicit constructors.
template <typename MatcheeStringType>
bool MatchAndExplain(const MatcheeStringType& s,
MatchResultListener* /* listener */) const {
@@ -1189,6 +730,16 @@ class HasSubstrMatcher {
explicit HasSubstrMatcher(const StringType& substring)
: substring_(substring) {}
+#if GTEST_HAS_ABSL
+ bool MatchAndExplain(const absl::string_view& s,
+ MatchResultListener* listener) const {
+ // This should fail to compile if absl::string_view is used with wide
+ // strings.
+ const StringType& str = std::string(s);
+ return MatchAndExplain(str, listener);
+ }
+#endif // GTEST_HAS_ABSL
+
// Accepts pointer types, particularly:
// const char*
// char*
@@ -1196,13 +747,13 @@ class HasSubstrMatcher {
// wchar_t*
template <typename CharType>
bool MatchAndExplain(CharType* s, MatchResultListener* listener) const {
- return s != NULL && MatchAndExplain(StringType(s), listener);
+ return s != nullptr && MatchAndExplain(StringType(s), listener);
}
// Matches anything that can convert to StringType.
//
// This is a template, not just a plain function with const StringType&,
- // because StringPiece has some interfering non-explicit constructors.
+ // because absl::string_view has some interfering non-explicit constructors.
template <typename MatcheeStringType>
bool MatchAndExplain(const MatcheeStringType& s,
MatchResultListener* /* listener */) const {
@@ -1236,6 +787,16 @@ class StartsWithMatcher {
explicit StartsWithMatcher(const StringType& prefix) : prefix_(prefix) {
}
+#if GTEST_HAS_ABSL
+ bool MatchAndExplain(const absl::string_view& s,
+ MatchResultListener* listener) const {
+ // This should fail to compile if absl::string_view is used with wide
+ // strings.
+ const StringType& str = std::string(s);
+ return MatchAndExplain(str, listener);
+ }
+#endif // GTEST_HAS_ABSL
+
// Accepts pointer types, particularly:
// const char*
// char*
@@ -1243,13 +804,13 @@ class StartsWithMatcher {
// wchar_t*
template <typename CharType>
bool MatchAndExplain(CharType* s, MatchResultListener* listener) const {
- return s != NULL && MatchAndExplain(StringType(s), listener);
+ return s != nullptr && MatchAndExplain(StringType(s), listener);
}
// Matches anything that can convert to StringType.
//
// This is a template, not just a plain function with const StringType&,
- // because StringPiece has some interfering non-explicit constructors.
+ // because absl::string_view has some interfering non-explicit constructors.
template <typename MatcheeStringType>
bool MatchAndExplain(const MatcheeStringType& s,
MatchResultListener* /* listener */) const {
@@ -1282,6 +843,16 @@ class EndsWithMatcher {
public:
explicit EndsWithMatcher(const StringType& suffix) : suffix_(suffix) {}
+#if GTEST_HAS_ABSL
+ bool MatchAndExplain(const absl::string_view& s,
+ MatchResultListener* listener) const {
+ // This should fail to compile if absl::string_view is used with wide
+ // strings.
+ const StringType& str = std::string(s);
+ return MatchAndExplain(str, listener);
+ }
+#endif // GTEST_HAS_ABSL
+
// Accepts pointer types, particularly:
// const char*
// char*
@@ -1289,13 +860,13 @@ class EndsWithMatcher {
// wchar_t*
template <typename CharType>
bool MatchAndExplain(CharType* s, MatchResultListener* listener) const {
- return s != NULL && MatchAndExplain(StringType(s), listener);
+ return s != nullptr && MatchAndExplain(StringType(s), listener);
}
// Matches anything that can convert to StringType.
//
// This is a template, not just a plain function with const StringType&,
- // because StringPiece has some interfering non-explicit constructors.
+ // because absl::string_view has some interfering non-explicit constructors.
template <typename MatcheeStringType>
bool MatchAndExplain(const MatcheeStringType& s,
MatchResultListener* /* listener */) const {
@@ -1320,73 +891,24 @@ class EndsWithMatcher {
GTEST_DISALLOW_ASSIGN_(EndsWithMatcher);
};
-// Implements polymorphic matchers MatchesRegex(regex) and
-// ContainsRegex(regex), which can be used as a Matcher<T> as long as
-// T can be converted to a string.
-class MatchesRegexMatcher {
- public:
- MatchesRegexMatcher(const RE* regex, bool full_match)
- : regex_(regex), full_match_(full_match) {}
-
- // Accepts pointer types, particularly:
- // const char*
- // char*
- // const wchar_t*
- // wchar_t*
- template <typename CharType>
- bool MatchAndExplain(CharType* s, MatchResultListener* listener) const {
- return s != NULL && MatchAndExplain(internal::string(s), listener);
- }
-
- // Matches anything that can convert to internal::string.
- //
- // This is a template, not just a plain function with const internal::string&,
- // because StringPiece has some interfering non-explicit constructors.
- template <class MatcheeStringType>
- bool MatchAndExplain(const MatcheeStringType& s,
- MatchResultListener* /* listener */) const {
- const internal::string& s2(s);
- return full_match_ ? RE::FullMatch(s2, *regex_) :
- RE::PartialMatch(s2, *regex_);
- }
-
- void DescribeTo(::std::ostream* os) const {
- *os << (full_match_ ? "matches" : "contains")
- << " regular expression ";
- UniversalPrinter<internal::string>::Print(regex_->pattern(), os);
- }
-
- void DescribeNegationTo(::std::ostream* os) const {
- *os << "doesn't " << (full_match_ ? "match" : "contain")
- << " regular expression ";
- UniversalPrinter<internal::string>::Print(regex_->pattern(), os);
- }
-
- private:
- const internal::linked_ptr<const RE> regex_;
- const bool full_match_;
-
- GTEST_DISALLOW_ASSIGN_(MatchesRegexMatcher);
-};
-
// Implements a matcher that compares the two fields of a 2-tuple
// using one of the ==, <=, <, etc, operators. The two fields being
// compared don't have to have the same type.
//
// The matcher defined here is polymorphic (for example, Eq() can be
-// used to match a tuple<int, short>, a tuple<const long&, double>,
+// used to match a std::tuple<int, short>, a std::tuple<const long&, double>,
// etc). Therefore we use a template type conversion operator in the
// implementation.
template <typename D, typename Op>
class PairMatchBase {
public:
template <typename T1, typename T2>
- operator Matcher< ::testing::tuple<T1, T2> >() const {
- return MakeMatcher(new Impl< ::testing::tuple<T1, T2> >);
+ operator Matcher<::std::tuple<T1, T2>>() const {
+ return Matcher<::std::tuple<T1, T2>>(new Impl<const ::std::tuple<T1, T2>&>);
}
template <typename T1, typename T2>
- operator Matcher<const ::testing::tuple<T1, T2>&>() const {
- return MakeMatcher(new Impl<const ::testing::tuple<T1, T2>&>);
+ operator Matcher<const ::std::tuple<T1, T2>&>() const {
+ return MakeMatcher(new Impl<const ::std::tuple<T1, T2>&>);
}
private:
@@ -1397,15 +919,14 @@ class PairMatchBase {
template <typename Tuple>
class Impl : public MatcherInterface<Tuple> {
public:
- virtual bool MatchAndExplain(
- Tuple args,
- MatchResultListener* /* listener */) const {
- return Op()(::testing::get<0>(args), ::testing::get<1>(args));
+ bool MatchAndExplain(Tuple args,
+ MatchResultListener* /* listener */) const override {
+ return Op()(::std::get<0>(args), ::std::get<1>(args));
}
- virtual void DescribeTo(::std::ostream* os) const {
+ void DescribeTo(::std::ostream* os) const override {
*os << "are " << GetDesc;
}
- virtual void DescribeNegationTo(::std::ostream* os) const {
+ void DescribeNegationTo(::std::ostream* os) const override {
*os << "aren't " << GetDesc;
}
};
@@ -1441,20 +962,21 @@ class Ge2Matcher : public PairMatchBase<Ge2Matcher, AnyGe> {
// will prevent different instantiations of NotMatcher from sharing
// the same NotMatcherImpl<T> class.
template <typename T>
-class NotMatcherImpl : public MatcherInterface<T> {
+class NotMatcherImpl : public MatcherInterface<const T&> {
public:
explicit NotMatcherImpl(const Matcher<T>& matcher)
: matcher_(matcher) {}
- virtual bool MatchAndExplain(T x, MatchResultListener* listener) const {
+ bool MatchAndExplain(const T& x,
+ MatchResultListener* listener) const override {
return !matcher_.MatchAndExplain(x, listener);
}
- virtual void DescribeTo(::std::ostream* os) const {
+ void DescribeTo(::std::ostream* os) const override {
matcher_.DescribeNegationTo(os);
}
- virtual void DescribeNegationTo(::std::ostream* os) const {
+ void DescribeNegationTo(::std::ostream* os) const override {
matcher_.DescribeTo(os);
}
@@ -1489,115 +1011,62 @@ class NotMatcher {
// that will prevent different instantiations of BothOfMatcher from
// sharing the same BothOfMatcherImpl<T> class.
template <typename T>
-class BothOfMatcherImpl : public MatcherInterface<T> {
+class AllOfMatcherImpl : public MatcherInterface<const T&> {
public:
- BothOfMatcherImpl(const Matcher<T>& matcher1, const Matcher<T>& matcher2)
- : matcher1_(matcher1), matcher2_(matcher2) {}
+ explicit AllOfMatcherImpl(std::vector<Matcher<T> > matchers)
+ : matchers_(std::move(matchers)) {}
- virtual void DescribeTo(::std::ostream* os) const {
+ void DescribeTo(::std::ostream* os) const override {
*os << "(";
- matcher1_.DescribeTo(os);
- *os << ") and (";
- matcher2_.DescribeTo(os);
+ for (size_t i = 0; i < matchers_.size(); ++i) {
+ if (i != 0) *os << ") and (";
+ matchers_[i].DescribeTo(os);
+ }
*os << ")";
}
- virtual void DescribeNegationTo(::std::ostream* os) const {
+ void DescribeNegationTo(::std::ostream* os) const override {
*os << "(";
- matcher1_.DescribeNegationTo(os);
- *os << ") or (";
- matcher2_.DescribeNegationTo(os);
+ for (size_t i = 0; i < matchers_.size(); ++i) {
+ if (i != 0) *os << ") or (";
+ matchers_[i].DescribeNegationTo(os);
+ }
*os << ")";
}
- virtual bool MatchAndExplain(T x, MatchResultListener* listener) const {
+ bool MatchAndExplain(const T& x,
+ MatchResultListener* listener) const override {
// If either matcher1_ or matcher2_ doesn't match x, we only need
// to explain why one of them fails.
- StringMatchResultListener listener1;
- if (!matcher1_.MatchAndExplain(x, &listener1)) {
- *listener << listener1.str();
- return false;
- }
+ std::string all_match_result;
- StringMatchResultListener listener2;
- if (!matcher2_.MatchAndExplain(x, &listener2)) {
- *listener << listener2.str();
- return false;
+ for (size_t i = 0; i < matchers_.size(); ++i) {
+ StringMatchResultListener slistener;
+ if (matchers_[i].MatchAndExplain(x, &slistener)) {
+ if (all_match_result.empty()) {
+ all_match_result = slistener.str();
+ } else {
+ std::string result = slistener.str();
+ if (!result.empty()) {
+ all_match_result += ", and ";
+ all_match_result += result;
+ }
+ }
+ } else {
+ *listener << slistener.str();
+ return false;
+ }
}
// Otherwise we need to explain why *both* of them match.
- const internal::string s1 = listener1.str();
- const internal::string s2 = listener2.str();
-
- if (s1 == "") {
- *listener << s2;
- } else {
- *listener << s1;
- if (s2 != "") {
- *listener << ", and " << s2;
- }
- }
+ *listener << all_match_result;
return true;
}
private:
- const Matcher<T> matcher1_;
- const Matcher<T> matcher2_;
+ const std::vector<Matcher<T> > matchers_;
- GTEST_DISALLOW_ASSIGN_(BothOfMatcherImpl);
-};
-
-#if GTEST_LANG_CXX11
-// MatcherList provides mechanisms for storing a variable number of matchers in
-// a list structure (ListType) and creating a combining matcher from such a
-// list.
-// The template is defined recursively using the following template paramters:
-// * kSize is the length of the MatcherList.
-// * Head is the type of the first matcher of the list.
-// * Tail denotes the types of the remaining matchers of the list.
-template <int kSize, typename Head, typename... Tail>
-struct MatcherList {
- typedef MatcherList<kSize - 1, Tail...> MatcherListTail;
- typedef ::std::pair<Head, typename MatcherListTail::ListType> ListType;
-
- // BuildList stores variadic type values in a nested pair structure.
- // Example:
- // MatcherList<3, int, string, float>::BuildList(5, "foo", 2.0) will return
- // the corresponding result of type pair<int, pair<string, float>>.
- static ListType BuildList(const Head& matcher, const Tail&... tail) {
- return ListType(matcher, MatcherListTail::BuildList(tail...));
- }
-
- // CreateMatcher<T> creates a Matcher<T> from a given list of matchers (built
- // by BuildList()). CombiningMatcher<T> is used to combine the matchers of the
- // list. CombiningMatcher<T> must implement MatcherInterface<T> and have a
- // constructor taking two Matcher<T>s as input.
- template <typename T, template <typename /* T */> class CombiningMatcher>
- static Matcher<T> CreateMatcher(const ListType& matchers) {
- return Matcher<T>(new CombiningMatcher<T>(
- SafeMatcherCast<T>(matchers.first),
- MatcherListTail::template CreateMatcher<T, CombiningMatcher>(
- matchers.second)));
- }
-};
-
-// The following defines the base case for the recursive definition of
-// MatcherList.
-template <typename Matcher1, typename Matcher2>
-struct MatcherList<2, Matcher1, Matcher2> {
- typedef ::std::pair<Matcher1, Matcher2> ListType;
-
- static ListType BuildList(const Matcher1& matcher1,
- const Matcher2& matcher2) {
- return ::std::pair<Matcher1, Matcher2>(matcher1, matcher2);
- }
-
- template <typename T, template <typename /* T */> class CombiningMatcher>
- static Matcher<T> CreateMatcher(const ListType& matchers) {
- return Matcher<T>(new CombiningMatcher<T>(
- SafeMatcherCast<T>(matchers.first),
- SafeMatcherCast<T>(matchers.second)));
- }
+ GTEST_DISALLOW_ASSIGN_(AllOfMatcherImpl);
};
// VariadicMatcher is used for the variadic implementation of
@@ -1608,149 +1077,139 @@ template <template <typename T> class CombiningMatcher, typename... Args>
class VariadicMatcher {
public:
VariadicMatcher(const Args&... matchers) // NOLINT
- : matchers_(MatcherListType::BuildList(matchers...)) {}
+ : matchers_(matchers...) {
+ static_assert(sizeof...(Args) > 0, "Must have at least one matcher.");
+ }
// This template type conversion operator allows an
// VariadicMatcher<Matcher1, Matcher2...> object to match any type that
// all of the provided matchers (Matcher1, Matcher2, ...) can match.
template <typename T>
operator Matcher<T>() const {
- return MatcherListType::template CreateMatcher<T, CombiningMatcher>(
- matchers_);
+ std::vector<Matcher<T> > values;
+ CreateVariadicMatcher<T>(&values, std::integral_constant<size_t, 0>());
+ return Matcher<T>(new CombiningMatcher<T>(std::move(values)));
}
private:
- typedef MatcherList<sizeof...(Args), Args...> MatcherListType;
+ template <typename T, size_t I>
+ void CreateVariadicMatcher(std::vector<Matcher<T> >* values,
+ std::integral_constant<size_t, I>) const {
+ values->push_back(SafeMatcherCast<T>(std::get<I>(matchers_)));
+ CreateVariadicMatcher<T>(values, std::integral_constant<size_t, I + 1>());
+ }
- const typename MatcherListType::ListType matchers_;
+ template <typename T>
+ void CreateVariadicMatcher(
+ std::vector<Matcher<T> >*,
+ std::integral_constant<size_t, sizeof...(Args)>) const {}
+
+ std::tuple<Args...> matchers_;
GTEST_DISALLOW_ASSIGN_(VariadicMatcher);
};
template <typename... Args>
-using AllOfMatcher = VariadicMatcher<BothOfMatcherImpl, Args...>;
-
-#endif // GTEST_LANG_CXX11
-
-// Used for implementing the AllOf(m_1, ..., m_n) matcher, which
-// matches a value that matches all of the matchers m_1, ..., and m_n.
-template <typename Matcher1, typename Matcher2>
-class BothOfMatcher {
- public:
- BothOfMatcher(Matcher1 matcher1, Matcher2 matcher2)
- : matcher1_(matcher1), matcher2_(matcher2) {}
-
- // This template type conversion operator allows a
- // BothOfMatcher<Matcher1, Matcher2> object to match any type that
- // both Matcher1 and Matcher2 can match.
- template <typename T>
- operator Matcher<T>() const {
- return Matcher<T>(new BothOfMatcherImpl<T>(SafeMatcherCast<T>(matcher1_),
- SafeMatcherCast<T>(matcher2_)));
- }
-
- private:
- Matcher1 matcher1_;
- Matcher2 matcher2_;
-
- GTEST_DISALLOW_ASSIGN_(BothOfMatcher);
-};
+using AllOfMatcher = VariadicMatcher<AllOfMatcherImpl, Args...>;
// Implements the AnyOf(m1, m2) matcher for a particular argument type
// T. We do not nest it inside the AnyOfMatcher class template, as
// that will prevent different instantiations of AnyOfMatcher from
// sharing the same EitherOfMatcherImpl<T> class.
template <typename T>
-class EitherOfMatcherImpl : public MatcherInterface<T> {
+class AnyOfMatcherImpl : public MatcherInterface<const T&> {
public:
- EitherOfMatcherImpl(const Matcher<T>& matcher1, const Matcher<T>& matcher2)
- : matcher1_(matcher1), matcher2_(matcher2) {}
+ explicit AnyOfMatcherImpl(std::vector<Matcher<T> > matchers)
+ : matchers_(std::move(matchers)) {}
- virtual void DescribeTo(::std::ostream* os) const {
+ void DescribeTo(::std::ostream* os) const override {
*os << "(";
- matcher1_.DescribeTo(os);
- *os << ") or (";
- matcher2_.DescribeTo(os);
+ for (size_t i = 0; i < matchers_.size(); ++i) {
+ if (i != 0) *os << ") or (";
+ matchers_[i].DescribeTo(os);
+ }
*os << ")";
}
- virtual void DescribeNegationTo(::std::ostream* os) const {
+ void DescribeNegationTo(::std::ostream* os) const override {
*os << "(";
- matcher1_.DescribeNegationTo(os);
- *os << ") and (";
- matcher2_.DescribeNegationTo(os);
+ for (size_t i = 0; i < matchers_.size(); ++i) {
+ if (i != 0) *os << ") and (";
+ matchers_[i].DescribeNegationTo(os);
+ }
*os << ")";
}
- virtual bool MatchAndExplain(T x, MatchResultListener* listener) const {
+ bool MatchAndExplain(const T& x,
+ MatchResultListener* listener) const override {
+ std::string no_match_result;
+
// If either matcher1_ or matcher2_ matches x, we just need to
// explain why *one* of them matches.
- StringMatchResultListener listener1;
- if (matcher1_.MatchAndExplain(x, &listener1)) {
- *listener << listener1.str();
- return true;
- }
-
- StringMatchResultListener listener2;
- if (matcher2_.MatchAndExplain(x, &listener2)) {
- *listener << listener2.str();
- return true;
+ for (size_t i = 0; i < matchers_.size(); ++i) {
+ StringMatchResultListener slistener;
+ if (matchers_[i].MatchAndExplain(x, &slistener)) {
+ *listener << slistener.str();
+ return true;
+ } else {
+ if (no_match_result.empty()) {
+ no_match_result = slistener.str();
+ } else {
+ std::string result = slistener.str();
+ if (!result.empty()) {
+ no_match_result += ", and ";
+ no_match_result += result;
+ }
+ }
+ }
}
// Otherwise we need to explain why *both* of them fail.
- const internal::string s1 = listener1.str();
- const internal::string s2 = listener2.str();
-
- if (s1 == "") {
- *listener << s2;
- } else {
- *listener << s1;
- if (s2 != "") {
- *listener << ", and " << s2;
- }
- }
+ *listener << no_match_result;
return false;
}
private:
- const Matcher<T> matcher1_;
- const Matcher<T> matcher2_;
+ const std::vector<Matcher<T> > matchers_;
- GTEST_DISALLOW_ASSIGN_(EitherOfMatcherImpl);
+ GTEST_DISALLOW_ASSIGN_(AnyOfMatcherImpl);
};
-#if GTEST_LANG_CXX11
// AnyOfMatcher is used for the variadic implementation of AnyOf(m_1, m_2, ...).
template <typename... Args>
-using AnyOfMatcher = VariadicMatcher<EitherOfMatcherImpl, Args...>;
-
-#endif // GTEST_LANG_CXX11
+using AnyOfMatcher = VariadicMatcher<AnyOfMatcherImpl, Args...>;
-// Used for implementing the AnyOf(m_1, ..., m_n) matcher, which
-// matches a value that matches at least one of the matchers m_1, ...,
-// and m_n.
-template <typename Matcher1, typename Matcher2>
-class EitherOfMatcher {
+// Wrapper for implementation of Any/AllOfArray().
+template <template <class> class MatcherImpl, typename T>
+class SomeOfArrayMatcher {
public:
- EitherOfMatcher(Matcher1 matcher1, Matcher2 matcher2)
- : matcher1_(matcher1), matcher2_(matcher2) {}
+ // Constructs the matcher from a sequence of element values or
+ // element matchers.
+ template <typename Iter>
+ SomeOfArrayMatcher(Iter first, Iter last) : matchers_(first, last) {}
- // This template type conversion operator allows a
- // EitherOfMatcher<Matcher1, Matcher2> object to match any type that
- // both Matcher1 and Matcher2 can match.
- template <typename T>
- operator Matcher<T>() const {
- return Matcher<T>(new EitherOfMatcherImpl<T>(
- SafeMatcherCast<T>(matcher1_), SafeMatcherCast<T>(matcher2_)));
+ template <typename U>
+ operator Matcher<U>() const { // NOLINT
+ using RawU = typename std::decay<U>::type;
+ std::vector<Matcher<RawU>> matchers;
+ for (const auto& matcher : matchers_) {
+ matchers.push_back(MatcherCast<RawU>(matcher));
+ }
+ return Matcher<U>(new MatcherImpl<RawU>(std::move(matchers)));
}
private:
- Matcher1 matcher1_;
- Matcher2 matcher2_;
+ const ::std::vector<T> matchers_;
- GTEST_DISALLOW_ASSIGN_(EitherOfMatcher);
+ GTEST_DISALLOW_ASSIGN_(SomeOfArrayMatcher);
};
+template <typename T>
+using AllOfArrayMatcher = SomeOfArrayMatcher<AllOfMatcherImpl, T>;
+
+template <typename T>
+using AnyOfArrayMatcher = SomeOfArrayMatcher<AnyOfMatcherImpl, T>;
+
// Used for implementing Truly(pred), which turns a predicate into a
// matcher.
template <typename Predicate>
@@ -1833,7 +1292,7 @@ class MatcherAsPredicate {
template <typename M>
class PredicateFormatterFromMatcher {
public:
- explicit PredicateFormatterFromMatcher(M m) : matcher_(internal::move(m)) {}
+ explicit PredicateFormatterFromMatcher(M m) : matcher_(std::move(m)) {}
// This template () operator allows a PredicateFormatterFromMatcher
// object to act as a predicate-formatter suitable for using with
@@ -1852,14 +1311,24 @@ class PredicateFormatterFromMatcher {
// We don't write MatcherCast<const T&> either, as that allows
// potentially unsafe downcasting of the matcher argument.
const Matcher<const T&> matcher = SafeMatcherCast<const T&>(matcher_);
- StringMatchResultListener listener;
- if (MatchPrintAndExplain(x, matcher, &listener))
+
+ // The expected path here is that the matcher should match (i.e. that most
+ // tests pass) so optimize for this case.
+ if (matcher.Matches(x)) {
return AssertionSuccess();
+ }
::std::stringstream ss;
ss << "Value of: " << value_text << "\n"
<< "Expected: ";
matcher.DescribeTo(&ss);
+
+ // Rerun the matcher to "PrintAndExain" the failure.
+ StringMatchResultListener listener;
+ if (MatchPrintAndExplain(x, matcher, &listener)) {
+ ss << "\n The matcher failed on the initial attempt; but passed when "
+ "rerun to generate the explanation.";
+ }
ss << "\n Actual: " << listener.str();
return AssertionFailure() << ss.str();
}
@@ -1877,7 +1346,7 @@ class PredicateFormatterFromMatcher {
template <typename M>
inline PredicateFormatterFromMatcher<M>
MakePredicateFormatterFromMatcher(M matcher) {
- return PredicateFormatterFromMatcher<M>(internal::move(matcher));
+ return PredicateFormatterFromMatcher<M>(std::move(matcher));
}
// Implements the polymorphic floating point equality matcher, which matches
@@ -1918,8 +1387,8 @@ class FloatingEqMatcher {
nan_eq_nan_(nan_eq_nan),
max_abs_error_(max_abs_error) {}
- virtual bool MatchAndExplain(T value,
- MatchResultListener* listener) const {
+ bool MatchAndExplain(T value,
+ MatchResultListener* listener) const override {
const FloatingPoint<FloatType> actual(value), expected(expected_);
// Compares NaNs first, if nan_eq_nan_ is true.
@@ -1953,7 +1422,7 @@ class FloatingEqMatcher {
}
}
- virtual void DescribeTo(::std::ostream* os) const {
+ void DescribeTo(::std::ostream* os) const override {
// os->precision() returns the previously set precision, which we
// store to restore the ostream to its original configuration
// after outputting.
@@ -1974,7 +1443,7 @@ class FloatingEqMatcher {
os->precision(old_precision);
}
- virtual void DescribeNegationTo(::std::ostream* os) const {
+ void DescribeNegationTo(::std::ostream* os) const override {
// As before, get original precision.
const ::std::streamsize old_precision = os->precision(
::std::numeric_limits<FloatType>::digits10 + 2);
@@ -2037,6 +1506,82 @@ class FloatingEqMatcher {
GTEST_DISALLOW_ASSIGN_(FloatingEqMatcher);
};
+// A 2-tuple ("binary") wrapper around FloatingEqMatcher:
+// FloatingEq2Matcher() matches (x, y) by matching FloatingEqMatcher(x, false)
+// against y, and FloatingEq2Matcher(e) matches FloatingEqMatcher(x, false, e)
+// against y. The former implements "Eq", the latter "Near". At present, there
+// is no version that compares NaNs as equal.
+template <typename FloatType>
+class FloatingEq2Matcher {
+ public:
+ FloatingEq2Matcher() { Init(-1, false); }
+
+ explicit FloatingEq2Matcher(bool nan_eq_nan) { Init(-1, nan_eq_nan); }
+
+ explicit FloatingEq2Matcher(FloatType max_abs_error) {
+ Init(max_abs_error, false);
+ }
+
+ FloatingEq2Matcher(FloatType max_abs_error, bool nan_eq_nan) {
+ Init(max_abs_error, nan_eq_nan);
+ }
+
+ template <typename T1, typename T2>
+ operator Matcher<::std::tuple<T1, T2>>() const {
+ return MakeMatcher(
+ new Impl<::std::tuple<T1, T2>>(max_abs_error_, nan_eq_nan_));
+ }
+ template <typename T1, typename T2>
+ operator Matcher<const ::std::tuple<T1, T2>&>() const {
+ return MakeMatcher(
+ new Impl<const ::std::tuple<T1, T2>&>(max_abs_error_, nan_eq_nan_));
+ }
+
+ private:
+ static ::std::ostream& GetDesc(::std::ostream& os) { // NOLINT
+ return os << "an almost-equal pair";
+ }
+
+ template <typename Tuple>
+ class Impl : public MatcherInterface<Tuple> {
+ public:
+ Impl(FloatType max_abs_error, bool nan_eq_nan) :
+ max_abs_error_(max_abs_error),
+ nan_eq_nan_(nan_eq_nan) {}
+
+ bool MatchAndExplain(Tuple args,
+ MatchResultListener* listener) const override {
+ if (max_abs_error_ == -1) {
+ FloatingEqMatcher<FloatType> fm(::std::get<0>(args), nan_eq_nan_);
+ return static_cast<Matcher<FloatType>>(fm).MatchAndExplain(
+ ::std::get<1>(args), listener);
+ } else {
+ FloatingEqMatcher<FloatType> fm(::std::get<0>(args), nan_eq_nan_,
+ max_abs_error_);
+ return static_cast<Matcher<FloatType>>(fm).MatchAndExplain(
+ ::std::get<1>(args), listener);
+ }
+ }
+ void DescribeTo(::std::ostream* os) const override {
+ *os << "are " << GetDesc;
+ }
+ void DescribeNegationTo(::std::ostream* os) const override {
+ *os << "aren't " << GetDesc;
+ }
+
+ private:
+ FloatType max_abs_error_;
+ const bool nan_eq_nan_;
+ };
+
+ void Init(FloatType max_abs_error_val, bool nan_eq_nan_val) {
+ max_abs_error_ = max_abs_error_val;
+ nan_eq_nan_ = nan_eq_nan_val;
+ }
+ FloatType max_abs_error_;
+ bool nan_eq_nan_;
+};
+
// Implements the Pointee(m) matcher for matching a pointer whose
// pointee matches matcher m. The pointer can be either raw or smart.
template <typename InnerMatcher>
@@ -2054,7 +1599,7 @@ class PointeeMatcher {
// enough for implementing the DescribeTo() method of Pointee().
template <typename Pointer>
operator Matcher<Pointer>() const {
- return MakeMatcher(new Impl<Pointer>(matcher_));
+ return Matcher<Pointer>(new Impl<const Pointer&>(matcher_));
}
private:
@@ -2062,26 +1607,25 @@ class PointeeMatcher {
template <typename Pointer>
class Impl : public MatcherInterface<Pointer> {
public:
- typedef typename PointeeOf<GTEST_REMOVE_CONST_( // NOLINT
- GTEST_REMOVE_REFERENCE_(Pointer))>::type Pointee;
+ typedef typename PointeeOf<typename std::remove_const<
+ typename std::remove_reference<Pointer>::type>::type>::type Pointee;
explicit Impl(const InnerMatcher& matcher)
: matcher_(MatcherCast<const Pointee&>(matcher)) {}
- virtual void DescribeTo(::std::ostream* os) const {
+ void DescribeTo(::std::ostream* os) const override {
*os << "points to a value that ";
matcher_.DescribeTo(os);
}
- virtual void DescribeNegationTo(::std::ostream* os) const {
+ void DescribeNegationTo(::std::ostream* os) const override {
*os << "does not point to a value that ";
matcher_.DescribeTo(os);
}
- virtual bool MatchAndExplain(Pointer pointer,
- MatchResultListener* listener) const {
- if (GetRawPointer(pointer) == NULL)
- return false;
+ bool MatchAndExplain(Pointer pointer,
+ MatchResultListener* listener) const override {
+ if (GetRawPointer(pointer) == nullptr) return false;
*listener << "which points to ";
return MatchPrintAndExplain(*pointer, matcher_, listener);
@@ -2098,6 +1642,7 @@ class PointeeMatcher {
GTEST_DISALLOW_ASSIGN_(PointeeMatcher);
};
+#if GTEST_HAS_RTTI
// Implements the WhenDynamicCastTo<T>(m) matcher that matches a pointer or
// reference that matches inner_matcher when dynamic_cast<T> is applied.
// The result of dynamic_cast<To> is forwarded to the inner matcher.
@@ -2123,12 +1668,8 @@ class WhenDynamicCastToMatcherBase {
protected:
const Matcher<To> matcher_;
- static string GetToName() {
-#if GTEST_HAS_RTTI
+ static std::string GetToName() {
return GetTypeName<To>();
-#else // GTEST_HAS_RTTI
- return "the target type";
-#endif // GTEST_HAS_RTTI
}
private:
@@ -2149,7 +1690,6 @@ class WhenDynamicCastToMatcher : public WhenDynamicCastToMatcherBase<To> {
template <typename From>
bool MatchAndExplain(From from, MatchResultListener* listener) const {
- // TODO(sbenza): Add more detail on failures. ie did the dyn_cast fail?
To to = dynamic_cast<To>(from);
return MatchPrintAndExplain(to, this->matcher_, listener);
}
@@ -2167,13 +1707,14 @@ class WhenDynamicCastToMatcher<To&> : public WhenDynamicCastToMatcherBase<To&> {
bool MatchAndExplain(From& from, MatchResultListener* listener) const {
// We don't want an std::bad_cast here, so do the cast with pointers.
To* to = dynamic_cast<To*>(&from);
- if (to == NULL) {
+ if (to == nullptr) {
*listener << "which cannot be dynamic_cast to " << this->GetToName();
return false;
}
return MatchPrintAndExplain(*to, this->matcher_, listener);
}
};
+#endif // GTEST_HAS_RTTI
// Implements the Field() matcher for matching a field (i.e. member
// variable) of an object.
@@ -2182,137 +1723,144 @@ class FieldMatcher {
public:
FieldMatcher(FieldType Class::*field,
const Matcher<const FieldType&>& matcher)
- : field_(field), matcher_(matcher) {}
+ : field_(field), matcher_(matcher), whose_field_("whose given field ") {}
+
+ FieldMatcher(const std::string& field_name, FieldType Class::*field,
+ const Matcher<const FieldType&>& matcher)
+ : field_(field),
+ matcher_(matcher),
+ whose_field_("whose field `" + field_name + "` ") {}
void DescribeTo(::std::ostream* os) const {
- *os << "is an object whose given field ";
+ *os << "is an object " << whose_field_;
matcher_.DescribeTo(os);
}
void DescribeNegationTo(::std::ostream* os) const {
- *os << "is an object whose given field ";
+ *os << "is an object " << whose_field_;
matcher_.DescribeNegationTo(os);
}
template <typename T>
bool MatchAndExplain(const T& value, MatchResultListener* listener) const {
+ // FIXME: The dispatch on std::is_pointer was introduced as a workaround for
+ // a compiler bug, and can now be removed.
return MatchAndExplainImpl(
- typename ::testing::internal::
- is_pointer<GTEST_REMOVE_CONST_(T)>::type(),
+ typename std::is_pointer<typename std::remove_const<T>::type>::type(),
value, listener);
}
private:
- // The first argument of MatchAndExplainImpl() is needed to help
- // Symbian's C++ compiler choose which overload to use. Its type is
- // true_type iff the Field() matcher is used to match a pointer.
- bool MatchAndExplainImpl(false_type /* is_not_pointer */, const Class& obj,
+ bool MatchAndExplainImpl(std::false_type /* is_not_pointer */,
+ const Class& obj,
MatchResultListener* listener) const {
- *listener << "whose given field is ";
+ *listener << whose_field_ << "is ";
return MatchPrintAndExplain(obj.*field_, matcher_, listener);
}
- bool MatchAndExplainImpl(true_type /* is_pointer */, const Class* p,
+ bool MatchAndExplainImpl(std::true_type /* is_pointer */, const Class* p,
MatchResultListener* listener) const {
- if (p == NULL)
- return false;
+ if (p == nullptr) return false;
*listener << "which points to an object ";
// Since *p has a field, it must be a class/struct/union type and
// thus cannot be a pointer. Therefore we pass false_type() as
// the first argument.
- return MatchAndExplainImpl(false_type(), *p, listener);
+ return MatchAndExplainImpl(std::false_type(), *p, listener);
}
const FieldType Class::*field_;
const Matcher<const FieldType&> matcher_;
+ // Contains either "whose given field " if the name of the field is unknown
+ // or "whose field `name_of_field` " if the name is known.
+ const std::string whose_field_;
+
GTEST_DISALLOW_ASSIGN_(FieldMatcher);
};
// Implements the Property() matcher for matching a property
// (i.e. return value of a getter method) of an object.
-template <typename Class, typename PropertyType>
+//
+// Property is a const-qualified member function of Class returning
+// PropertyType.
+template <typename Class, typename PropertyType, typename Property>
class PropertyMatcher {
public:
- // The property may have a reference type, so 'const PropertyType&'
- // may cause double references and fail to compile. That's why we
- // need GTEST_REFERENCE_TO_CONST, which works regardless of
- // PropertyType being a reference or not.
- typedef GTEST_REFERENCE_TO_CONST_(PropertyType) RefToConstProperty;
+ typedef const PropertyType& RefToConstProperty;
- PropertyMatcher(PropertyType (Class::*property)() const,
+ PropertyMatcher(Property property, const Matcher<RefToConstProperty>& matcher)
+ : property_(property),
+ matcher_(matcher),
+ whose_property_("whose given property ") {}
+
+ PropertyMatcher(const std::string& property_name, Property property,
const Matcher<RefToConstProperty>& matcher)
- : property_(property), matcher_(matcher) {}
+ : property_(property),
+ matcher_(matcher),
+ whose_property_("whose property `" + property_name + "` ") {}
void DescribeTo(::std::ostream* os) const {
- *os << "is an object whose given property ";
+ *os << "is an object " << whose_property_;
matcher_.DescribeTo(os);
}
void DescribeNegationTo(::std::ostream* os) const {
- *os << "is an object whose given property ";
+ *os << "is an object " << whose_property_;
matcher_.DescribeNegationTo(os);
}
template <typename T>
bool MatchAndExplain(const T&value, MatchResultListener* listener) const {
return MatchAndExplainImpl(
- typename ::testing::internal::
- is_pointer<GTEST_REMOVE_CONST_(T)>::type(),
+ typename std::is_pointer<typename std::remove_const<T>::type>::type(),
value, listener);
}
private:
- // The first argument of MatchAndExplainImpl() is needed to help
- // Symbian's C++ compiler choose which overload to use. Its type is
- // true_type iff the Property() matcher is used to match a pointer.
- bool MatchAndExplainImpl(false_type /* is_not_pointer */, const Class& obj,
+ bool MatchAndExplainImpl(std::false_type /* is_not_pointer */,
+ const Class& obj,
MatchResultListener* listener) const {
- *listener << "whose given property is ";
+ *listener << whose_property_ << "is ";
// Cannot pass the return value (for example, int) to MatchPrintAndExplain,
// which takes a non-const reference as argument.
-#if defined(_PREFAST_ ) && _MSC_VER == 1800
- // Workaround bug in VC++ 2013's /analyze parser.
- // https://connect.microsoft.com/VisualStudio/feedback/details/1106363/internal-compiler-error-with-analyze-due-to-failure-to-infer-move
- posix::Abort(); // To make sure it is never run.
- return false;
-#else
RefToConstProperty result = (obj.*property_)();
return MatchPrintAndExplain(result, matcher_, listener);
-#endif
}
- bool MatchAndExplainImpl(true_type /* is_pointer */, const Class* p,
+ bool MatchAndExplainImpl(std::true_type /* is_pointer */, const Class* p,
MatchResultListener* listener) const {
- if (p == NULL)
- return false;
+ if (p == nullptr) return false;
*listener << "which points to an object ";
// Since *p has a property method, it must be a class/struct/union
// type and thus cannot be a pointer. Therefore we pass
// false_type() as the first argument.
- return MatchAndExplainImpl(false_type(), *p, listener);
+ return MatchAndExplainImpl(std::false_type(), *p, listener);
}
- PropertyType (Class::*property_)() const;
+ Property property_;
const Matcher<RefToConstProperty> matcher_;
+ // Contains either "whose given property " if the name of the property is
+ // unknown or "whose property `name_of_property` " if the name is known.
+ const std::string whose_property_;
+
GTEST_DISALLOW_ASSIGN_(PropertyMatcher);
};
// Type traits specifying various features of different functors for ResultOf.
// The default template specifies features for functor objects.
-// Functor classes have to typedef argument_type and result_type
-// to be compatible with ResultOf.
template <typename Functor>
struct CallableTraits {
- typedef typename Functor::result_type ResultType;
typedef Functor StorageType;
static void CheckIsValid(Functor /* functor */) {}
+
template <typename T>
- static ResultType Invoke(Functor f, T arg) { return f(arg); }
+ static auto Invoke(Functor f, const T& arg) -> decltype(f(arg)) {
+ return f(arg);
+ }
};
// Specialization for function pointers.
@@ -2322,7 +1870,7 @@ struct CallableTraits<ResType(*)(ArgType)> {
typedef ResType(*StorageType)(ArgType);
static void CheckIsValid(ResType(*f)(ArgType)) {
- GTEST_CHECK_(f != NULL)
+ GTEST_CHECK_(f != nullptr)
<< "NULL function pointer is passed into ResultOf().";
}
template <typename T>
@@ -2333,19 +1881,17 @@ struct CallableTraits<ResType(*)(ArgType)> {
// Implements the ResultOf() matcher for matching a return value of a
// unary function of an object.
-template <typename Callable>
+template <typename Callable, typename InnerMatcher>
class ResultOfMatcher {
public:
- typedef typename CallableTraits<Callable>::ResultType ResultType;
-
- ResultOfMatcher(Callable callable, const Matcher<ResultType>& matcher)
- : callable_(callable), matcher_(matcher) {
+ ResultOfMatcher(Callable callable, InnerMatcher matcher)
+ : callable_(std::move(callable)), matcher_(std::move(matcher)) {
CallableTraits<Callable>::CheckIsValid(callable_);
}
template <typename T>
operator Matcher<T>() const {
- return Matcher<T>(new Impl<T>(callable_, matcher_));
+ return Matcher<T>(new Impl<const T&>(callable_, matcher_));
}
private:
@@ -2353,24 +1899,30 @@ class ResultOfMatcher {
template <typename T>
class Impl : public MatcherInterface<T> {
+ using ResultType = decltype(CallableTraits<Callable>::template Invoke<T>(
+ std::declval<CallableStorageType>(), std::declval<T>()));
+
public:
- Impl(CallableStorageType callable, const Matcher<ResultType>& matcher)
- : callable_(callable), matcher_(matcher) {}
+ template <typename M>
+ Impl(const CallableStorageType& callable, const M& matcher)
+ : callable_(callable), matcher_(MatcherCast<ResultType>(matcher)) {}
- virtual void DescribeTo(::std::ostream* os) const {
+ void DescribeTo(::std::ostream* os) const override {
*os << "is mapped by the given callable to a value that ";
matcher_.DescribeTo(os);
}
- virtual void DescribeNegationTo(::std::ostream* os) const {
+ void DescribeNegationTo(::std::ostream* os) const override {
*os << "is mapped by the given callable to a value that ";
matcher_.DescribeNegationTo(os);
}
- virtual bool MatchAndExplain(T obj, MatchResultListener* listener) const {
+ bool MatchAndExplain(T obj, MatchResultListener* listener) const override {
*listener << "which is mapped by the given callable to ";
- // Cannot pass the return value (for example, int) to
- // MatchPrintAndExplain, which takes a non-const reference as argument.
+ // Cannot pass the return value directly to MatchPrintAndExplain, which
+ // takes a non-const reference as argument.
+ // Also, specifying template argument explicitly is needed because T could
+ // be a non-const reference (e.g. Matcher<Uncopyable&>).
ResultType result =
CallableTraits<Callable>::template Invoke<T>(callable_, obj);
return MatchPrintAndExplain(result, matcher_, listener);
@@ -2378,9 +1930,9 @@ class ResultOfMatcher {
private:
// Functors often define operator() as non-const method even though
- // they are actualy stateless. But we need to use them even when
+ // they are actually stateless. But we need to use them even when
// 'this' is a const pointer. It's the user's responsibility not to
- // use stateful callables with ResultOf(), which does't guarantee
+ // use stateful callables with ResultOf(), which doesn't guarantee
// how many times the callable will be invoked.
mutable CallableStorageType callable_;
const Matcher<ResultType> matcher_;
@@ -2389,7 +1941,7 @@ class ResultOfMatcher {
}; // class Impl
const CallableStorageType callable_;
- const Matcher<ResultType> matcher_;
+ const InnerMatcher matcher_;
GTEST_DISALLOW_ASSIGN_(ResultOfMatcher);
};
@@ -2404,29 +1956,27 @@ class SizeIsMatcher {
template <typename Container>
operator Matcher<Container>() const {
- return MakeMatcher(new Impl<Container>(size_matcher_));
+ return Matcher<Container>(new Impl<const Container&>(size_matcher_));
}
template <typename Container>
class Impl : public MatcherInterface<Container> {
public:
- typedef internal::StlContainerView<
- GTEST_REMOVE_REFERENCE_AND_CONST_(Container)> ContainerView;
- typedef typename ContainerView::type::size_type SizeType;
+ using SizeType = decltype(std::declval<Container>().size());
explicit Impl(const SizeMatcher& size_matcher)
: size_matcher_(MatcherCast<SizeType>(size_matcher)) {}
- virtual void DescribeTo(::std::ostream* os) const {
+ void DescribeTo(::std::ostream* os) const override {
*os << "size ";
size_matcher_.DescribeTo(os);
}
- virtual void DescribeNegationTo(::std::ostream* os) const {
+ void DescribeNegationTo(::std::ostream* os) const override {
*os << "size ";
size_matcher_.DescribeNegationTo(os);
}
- virtual bool MatchAndExplain(Container container,
- MatchResultListener* listener) const {
+ bool MatchAndExplain(Container container,
+ MatchResultListener* listener) const override {
SizeType size = container.size();
StringMatchResultListener size_listener;
const bool result = size_matcher_.MatchAndExplain(size, &size_listener);
@@ -2456,7 +2006,7 @@ class BeginEndDistanceIsMatcher {
template <typename Container>
operator Matcher<Container>() const {
- return MakeMatcher(new Impl<Container>(distance_matcher_));
+ return Matcher<Container>(new Impl<const Container&>(distance_matcher_));
}
template <typename Container>
@@ -2470,24 +2020,20 @@ class BeginEndDistanceIsMatcher {
explicit Impl(const DistanceMatcher& distance_matcher)
: distance_matcher_(MatcherCast<DistanceType>(distance_matcher)) {}
- virtual void DescribeTo(::std::ostream* os) const {
+ void DescribeTo(::std::ostream* os) const override {
*os << "distance between begin() and end() ";
distance_matcher_.DescribeTo(os);
}
- virtual void DescribeNegationTo(::std::ostream* os) const {
+ void DescribeNegationTo(::std::ostream* os) const override {
*os << "distance between begin() and end() ";
distance_matcher_.DescribeNegationTo(os);
}
- virtual bool MatchAndExplain(Container container,
- MatchResultListener* listener) const {
-#if GTEST_HAS_STD_BEGIN_AND_END_
+ bool MatchAndExplain(Container container,
+ MatchResultListener* listener) const override {
using std::begin;
using std::end;
DistanceType distance = std::distance(begin(container), end(container));
-#else
- DistanceType distance = std::distance(container.begin(), container.end());
-#endif
StringMatchResultListener distance_listener;
const bool result =
distance_matcher_.MatchAndExplain(distance, &distance_listener);
@@ -2524,15 +2070,15 @@ class ContainerEqMatcher {
typedef typename View::type StlContainer;
typedef typename View::const_reference StlContainerReference;
+ static_assert(!std::is_const<Container>::value,
+ "Container type must not be const");
+ static_assert(!std::is_reference<Container>::value,
+ "Container type must not be a reference");
+
// We make a copy of expected in case the elements in it are modified
// after this matcher is created.
explicit ContainerEqMatcher(const Container& expected)
- : expected_(View::Copy(expected)) {
- // Makes sure the user doesn't instantiate this class template
- // with a const or reference type.
- (void)testing::StaticAssertTypeEq<Container,
- GTEST_REMOVE_REFERENCE_AND_CONST_(Container)>();
- }
+ : expected_(View::Copy(expected)) {}
void DescribeTo(::std::ostream* os) const {
*os << "equals ";
@@ -2546,9 +2092,8 @@ class ContainerEqMatcher {
template <typename LhsContainer>
bool MatchAndExplain(const LhsContainer& lhs,
MatchResultListener* listener) const {
- // GTEST_REMOVE_CONST_() is needed to work around an MSVC 8.0 bug
- // that causes LhsContainer to be a const type sometimes.
- typedef internal::StlContainerView<GTEST_REMOVE_CONST_(LhsContainer)>
+ typedef internal::StlContainerView<
+ typename std::remove_const<LhsContainer>::type>
LhsView;
typedef typename LhsView::type LhsStlContainer;
StlContainerReference lhs_stl_container = LhsView::ConstReference(lhs);
@@ -2556,7 +2101,7 @@ class ContainerEqMatcher {
return true;
::std::ostream* const os = listener->stream();
- if (os != NULL) {
+ if (os != nullptr) {
// Something is different. Check for extra values first.
bool printed_header = false;
for (typename LhsStlContainer::const_iterator it =
@@ -2636,18 +2181,18 @@ class WhenSortedByMatcher {
Impl(const Comparator& comparator, const ContainerMatcher& matcher)
: comparator_(comparator), matcher_(matcher) {}
- virtual void DescribeTo(::std::ostream* os) const {
+ void DescribeTo(::std::ostream* os) const override {
*os << "(when sorted) ";
matcher_.DescribeTo(os);
}
- virtual void DescribeNegationTo(::std::ostream* os) const {
+ void DescribeNegationTo(::std::ostream* os) const override {
*os << "(when sorted) ";
matcher_.DescribeNegationTo(os);
}
- virtual bool MatchAndExplain(LhsContainer lhs,
- MatchResultListener* listener) const {
+ bool MatchAndExplain(LhsContainer lhs,
+ MatchResultListener* listener) const override {
LhsStlContainerReference lhs_stl_container = LhsView::ConstReference(lhs);
::std::vector<LhsValue> sorted_container(lhs_stl_container.begin(),
lhs_stl_container.end());
@@ -2686,29 +2231,38 @@ class WhenSortedByMatcher {
};
// Implements Pointwise(tuple_matcher, rhs_container). tuple_matcher
-// must be able to be safely cast to Matcher<tuple<const T1&, const
+// must be able to be safely cast to Matcher<std::tuple<const T1&, const
// T2&> >, where T1 and T2 are the types of elements in the LHS
// container and the RHS container respectively.
template <typename TupleMatcher, typename RhsContainer>
class PointwiseMatcher {
+ GTEST_COMPILE_ASSERT_(
+ !IsHashTable<GTEST_REMOVE_REFERENCE_AND_CONST_(RhsContainer)>::value,
+ use_UnorderedPointwise_with_hash_tables);
+
public:
typedef internal::StlContainerView<RhsContainer> RhsView;
typedef typename RhsView::type RhsStlContainer;
typedef typename RhsStlContainer::value_type RhsValue;
+ static_assert(!std::is_const<RhsContainer>::value,
+ "RhsContainer type must not be const");
+ static_assert(!std::is_reference<RhsContainer>::value,
+ "RhsContainer type must not be a reference");
+
// Like ContainerEq, we make a copy of rhs in case the elements in
// it are modified after this matcher is created.
PointwiseMatcher(const TupleMatcher& tuple_matcher, const RhsContainer& rhs)
- : tuple_matcher_(tuple_matcher), rhs_(RhsView::Copy(rhs)) {
- // Makes sure the user doesn't instantiate this class template
- // with a const or reference type.
- (void)testing::StaticAssertTypeEq<RhsContainer,
- GTEST_REMOVE_REFERENCE_AND_CONST_(RhsContainer)>();
- }
+ : tuple_matcher_(tuple_matcher), rhs_(RhsView::Copy(rhs)) {}
template <typename LhsContainer>
operator Matcher<LhsContainer>() const {
- return MakeMatcher(new Impl<LhsContainer>(tuple_matcher_, rhs_));
+ GTEST_COMPILE_ASSERT_(
+ !IsHashTable<GTEST_REMOVE_REFERENCE_AND_CONST_(LhsContainer)>::value,
+ use_UnorderedPointwise_with_hash_tables);
+
+ return Matcher<LhsContainer>(
+ new Impl<const LhsContainer&>(tuple_matcher_, rhs_));
}
template <typename LhsContainer>
@@ -2723,21 +2277,21 @@ class PointwiseMatcher {
// reference, as they may be expensive to copy. We must use tuple
// instead of pair here, as a pair cannot hold references (C++ 98,
// 20.2.2 [lib.pairs]).
- typedef ::testing::tuple<const LhsValue&, const RhsValue&> InnerMatcherArg;
+ typedef ::std::tuple<const LhsValue&, const RhsValue&> InnerMatcherArg;
Impl(const TupleMatcher& tuple_matcher, const RhsStlContainer& rhs)
// mono_tuple_matcher_ holds a monomorphic version of the tuple matcher.
: mono_tuple_matcher_(SafeMatcherCast<InnerMatcherArg>(tuple_matcher)),
rhs_(rhs) {}
- virtual void DescribeTo(::std::ostream* os) const {
+ void DescribeTo(::std::ostream* os) const override {
*os << "contains " << rhs_.size()
<< " values, where each value and its corresponding value in ";
UniversalPrinter<RhsStlContainer>::Print(rhs_, os);
*os << " ";
mono_tuple_matcher_.DescribeTo(os);
}
- virtual void DescribeNegationTo(::std::ostream* os) const {
+ void DescribeNegationTo(::std::ostream* os) const override {
*os << "doesn't contain exactly " << rhs_.size()
<< " values, or contains a value x at some index i"
<< " where x and the i-th value of ";
@@ -2746,8 +2300,8 @@ class PointwiseMatcher {
mono_tuple_matcher_.DescribeNegationTo(os);
}
- virtual bool MatchAndExplain(LhsContainer lhs,
- MatchResultListener* listener) const {
+ bool MatchAndExplain(LhsContainer lhs,
+ MatchResultListener* listener) const override {
LhsStlContainerReference lhs_stl_container = LhsView::ConstReference(lhs);
const size_t actual_size = lhs_stl_container.size();
if (actual_size != rhs_.size()) {
@@ -2758,12 +2312,15 @@ class PointwiseMatcher {
typename LhsStlContainer::const_iterator left = lhs_stl_container.begin();
typename RhsStlContainer::const_iterator right = rhs_.begin();
for (size_t i = 0; i != actual_size; ++i, ++left, ++right) {
- const InnerMatcherArg value_pair(*left, *right);
-
if (listener->IsInterested()) {
StringMatchResultListener inner_listener;
+ // Create InnerMatcherArg as a temporarily object to avoid it outlives
+ // *left and *right. Dereference or the conversion to `const T&` may
+ // return temp objects, e.g for vector<bool>.
if (!mono_tuple_matcher_.MatchAndExplain(
- value_pair, &inner_listener)) {
+ InnerMatcherArg(ImplicitCast_<const LhsValue&>(*left),
+ ImplicitCast_<const RhsValue&>(*right)),
+ &inner_listener)) {
*listener << "where the value pair (";
UniversalPrint(*left, listener->stream());
*listener << ", ";
@@ -2773,7 +2330,9 @@ class PointwiseMatcher {
return false;
}
} else {
- if (!mono_tuple_matcher_.Matches(value_pair))
+ if (!mono_tuple_matcher_.Matches(
+ InnerMatcherArg(ImplicitCast_<const LhsValue&>(*left),
+ ImplicitCast_<const RhsValue&>(*right))))
return false;
}
}
@@ -2849,18 +2408,18 @@ class ContainsMatcherImpl : public QuantifierMatcherImpl<Container> {
: QuantifierMatcherImpl<Container>(inner_matcher) {}
// Describes what this matcher does.
- virtual void DescribeTo(::std::ostream* os) const {
+ void DescribeTo(::std::ostream* os) const override {
*os << "contains at least one element that ";
this->inner_matcher_.DescribeTo(os);
}
- virtual void DescribeNegationTo(::std::ostream* os) const {
+ void DescribeNegationTo(::std::ostream* os) const override {
*os << "doesn't contain any element that ";
this->inner_matcher_.DescribeTo(os);
}
- virtual bool MatchAndExplain(Container container,
- MatchResultListener* listener) const {
+ bool MatchAndExplain(Container container,
+ MatchResultListener* listener) const override {
return this->MatchAndExplainImpl(false, container, listener);
}
@@ -2878,18 +2437,18 @@ class EachMatcherImpl : public QuantifierMatcherImpl<Container> {
: QuantifierMatcherImpl<Container>(inner_matcher) {}
// Describes what this matcher does.
- virtual void DescribeTo(::std::ostream* os) const {
+ void DescribeTo(::std::ostream* os) const override {
*os << "only contains elements that ";
this->inner_matcher_.DescribeTo(os);
}
- virtual void DescribeNegationTo(::std::ostream* os) const {
+ void DescribeNegationTo(::std::ostream* os) const override {
*os << "contains some element that ";
this->inner_matcher_.DescribeNegationTo(os);
}
- virtual bool MatchAndExplain(Container container,
- MatchResultListener* listener) const {
+ bool MatchAndExplain(Container container,
+ MatchResultListener* listener) const override {
return this->MatchAndExplainImpl(true, container, listener);
}
@@ -2905,7 +2464,8 @@ class ContainsMatcher {
template <typename Container>
operator Matcher<Container>() const {
- return MakeMatcher(new ContainsMatcherImpl<Container>(inner_matcher_));
+ return Matcher<Container>(
+ new ContainsMatcherImpl<const Container&>(inner_matcher_));
}
private:
@@ -2922,7 +2482,8 @@ class EachMatcher {
template <typename Container>
operator Matcher<Container>() const {
- return MakeMatcher(new EachMatcherImpl<Container>(inner_matcher_));
+ return Matcher<Container>(
+ new EachMatcherImpl<const Container&>(inner_matcher_));
}
private:
@@ -2931,6 +2492,30 @@ class EachMatcher {
GTEST_DISALLOW_ASSIGN_(EachMatcher);
};
+struct Rank1 {};
+struct Rank0 : Rank1 {};
+
+namespace pair_getters {
+using std::get;
+template <typename T>
+auto First(T& x, Rank1) -> decltype(get<0>(x)) { // NOLINT
+ return get<0>(x);
+}
+template <typename T>
+auto First(T& x, Rank0) -> decltype((x.first)) { // NOLINT
+ return x.first;
+}
+
+template <typename T>
+auto Second(T& x, Rank1) -> decltype(get<1>(x)) { // NOLINT
+ return get<1>(x);
+}
+template <typename T>
+auto Second(T& x, Rank0) -> decltype((x.second)) { // NOLINT
+ return x.second;
+}
+} // namespace pair_getters
+
// Implements Key(inner_matcher) for the given argument pair type.
// Key(inner_matcher) matches an std::pair whose 'first' field matches
// inner_matcher. For example, Contains(Key(Ge(5))) can be used to match an
@@ -2947,13 +2532,14 @@ class KeyMatcherImpl : public MatcherInterface<PairType> {
testing::SafeMatcherCast<const KeyType&>(inner_matcher)) {
}
- // Returns true iff 'key_value.first' (the key) matches the inner matcher.
- virtual bool MatchAndExplain(PairType key_value,
- MatchResultListener* listener) const {
+ // Returns true if and only if 'key_value.first' (the key) matches the inner
+ // matcher.
+ bool MatchAndExplain(PairType key_value,
+ MatchResultListener* listener) const override {
StringMatchResultListener inner_listener;
- const bool match = inner_matcher_.MatchAndExplain(key_value.first,
- &inner_listener);
- const internal::string explanation = inner_listener.str();
+ const bool match = inner_matcher_.MatchAndExplain(
+ pair_getters::First(key_value, Rank0()), &inner_listener);
+ const std::string explanation = inner_listener.str();
if (explanation != "") {
*listener << "whose first field is a value " << explanation;
}
@@ -2961,13 +2547,13 @@ class KeyMatcherImpl : public MatcherInterface<PairType> {
}
// Describes what this matcher does.
- virtual void DescribeTo(::std::ostream* os) const {
+ void DescribeTo(::std::ostream* os) const override {
*os << "has a key that ";
inner_matcher_.DescribeTo(os);
}
// Describes what the negation of this matcher does.
- virtual void DescribeNegationTo(::std::ostream* os) const {
+ void DescribeNegationTo(::std::ostream* os) const override {
*os << "doesn't have a key that ";
inner_matcher_.DescribeTo(os);
}
@@ -2986,7 +2572,8 @@ class KeyMatcher {
template <typename PairType>
operator Matcher<PairType>() const {
- return MakeMatcher(new KeyMatcherImpl<PairType>(matcher_for_key_));
+ return Matcher<PairType>(
+ new KeyMatcherImpl<const PairType&>(matcher_for_key_));
}
private:
@@ -3013,7 +2600,7 @@ class PairMatcherImpl : public MatcherInterface<PairType> {
}
// Describes what this matcher does.
- virtual void DescribeTo(::std::ostream* os) const {
+ void DescribeTo(::std::ostream* os) const override {
*os << "has a first field that ";
first_matcher_.DescribeTo(os);
*os << ", and has a second field that ";
@@ -3021,32 +2608,32 @@ class PairMatcherImpl : public MatcherInterface<PairType> {
}
// Describes what the negation of this matcher does.
- virtual void DescribeNegationTo(::std::ostream* os) const {
+ void DescribeNegationTo(::std::ostream* os) const override {
*os << "has a first field that ";
first_matcher_.DescribeNegationTo(os);
*os << ", or has a second field that ";
second_matcher_.DescribeNegationTo(os);
}
- // Returns true iff 'a_pair.first' matches first_matcher and 'a_pair.second'
- // matches second_matcher.
- virtual bool MatchAndExplain(PairType a_pair,
- MatchResultListener* listener) const {
+ // Returns true if and only if 'a_pair.first' matches first_matcher and
+ // 'a_pair.second' matches second_matcher.
+ bool MatchAndExplain(PairType a_pair,
+ MatchResultListener* listener) const override {
if (!listener->IsInterested()) {
// If the listener is not interested, we don't need to construct the
// explanation.
- return first_matcher_.Matches(a_pair.first) &&
- second_matcher_.Matches(a_pair.second);
+ return first_matcher_.Matches(pair_getters::First(a_pair, Rank0())) &&
+ second_matcher_.Matches(pair_getters::Second(a_pair, Rank0()));
}
StringMatchResultListener first_inner_listener;
- if (!first_matcher_.MatchAndExplain(a_pair.first,
+ if (!first_matcher_.MatchAndExplain(pair_getters::First(a_pair, Rank0()),
&first_inner_listener)) {
*listener << "whose first field does not match";
PrintIfNotEmpty(first_inner_listener.str(), listener->stream());
return false;
}
StringMatchResultListener second_inner_listener;
- if (!second_matcher_.MatchAndExplain(a_pair.second,
+ if (!second_matcher_.MatchAndExplain(pair_getters::Second(a_pair, Rank0()),
&second_inner_listener)) {
*listener << "whose second field does not match";
PrintIfNotEmpty(second_inner_listener.str(), listener->stream());
@@ -3058,8 +2645,8 @@ class PairMatcherImpl : public MatcherInterface<PairType> {
}
private:
- void ExplainSuccess(const internal::string& first_explanation,
- const internal::string& second_explanation,
+ void ExplainSuccess(const std::string& first_explanation,
+ const std::string& second_explanation,
MatchResultListener* listener) const {
*listener << "whose both fields match";
if (first_explanation != "") {
@@ -3091,9 +2678,8 @@ class PairMatcher {
template <typename PairType>
operator Matcher<PairType> () const {
- return MakeMatcher(
- new PairMatcherImpl<PairType>(
- first_matcher_, second_matcher_));
+ return Matcher<PairType>(
+ new PairMatcherImpl<const PairType&>(first_matcher_, second_matcher_));
}
private:
@@ -3123,7 +2709,7 @@ class ElementsAreMatcherImpl : public MatcherInterface<Container> {
}
// Describes what this matcher does.
- virtual void DescribeTo(::std::ostream* os) const {
+ void DescribeTo(::std::ostream* os) const override {
if (count() == 0) {
*os << "is empty";
} else if (count() == 1) {
@@ -3142,7 +2728,7 @@ class ElementsAreMatcherImpl : public MatcherInterface<Container> {
}
// Describes what the negation of this matcher does.
- virtual void DescribeNegationTo(::std::ostream* os) const {
+ void DescribeNegationTo(::std::ostream* os) const override {
if (count() == 0) {
*os << "isn't empty";
return;
@@ -3158,15 +2744,15 @@ class ElementsAreMatcherImpl : public MatcherInterface<Container> {
}
}
- virtual bool MatchAndExplain(Container container,
- MatchResultListener* listener) const {
+ bool MatchAndExplain(Container container,
+ MatchResultListener* listener) const override {
// To work with stream-like "containers", we must only walk
// through the elements in one pass.
const bool listener_interested = listener->IsInterested();
// explanations[i] is the explanation of the element at index i.
- ::std::vector<internal::string> explanations(count());
+ ::std::vector<std::string> explanations(count());
StlContainerReference stl_container = View::ConstReference(container);
typename StlContainer::const_iterator it = stl_container.begin();
size_t exam_pos = 0;
@@ -3225,7 +2811,7 @@ class ElementsAreMatcherImpl : public MatcherInterface<Container> {
if (listener_interested) {
bool reason_printed = false;
for (size_t i = 0; i != count(); ++i) {
- const internal::string& s = explanations[i];
+ const std::string& s = explanations[i];
if (!s.empty()) {
if (reason_printed) {
*listener << ",\nand ";
@@ -3278,7 +2864,7 @@ class GTEST_API_ MatchMatrix {
void Randomize();
- string DebugString() const;
+ std::string DebugString() const;
private:
size_t SpaceIndex(size_t ilhs, size_t irhs) const {
@@ -3302,14 +2888,23 @@ typedef ::std::vector<ElementMatcherPair> ElementMatcherPairs;
GTEST_API_ ElementMatcherPairs
FindMaxBipartiteMatching(const MatchMatrix& g);
-GTEST_API_ bool FindPairing(const MatchMatrix& matrix,
- MatchResultListener* listener);
+struct UnorderedMatcherRequire {
+ enum Flags {
+ Superset = 1 << 0,
+ Subset = 1 << 1,
+ ExactMatch = Superset | Subset,
+ };
+};
// Untyped base class for implementing UnorderedElementsAre. By
// putting logic that's not specific to the element type here, we
// reduce binary bloat and increase compilation speed.
class GTEST_API_ UnorderedElementsAreMatcherImplBase {
protected:
+ explicit UnorderedElementsAreMatcherImplBase(
+ UnorderedMatcherRequire::Flags matcher_flags)
+ : match_flags_(matcher_flags) {}
+
// A vector of matcher describers, one for each element matcher.
// Does not own the describers (and thus can be used only when the
// element matchers are alive).
@@ -3321,10 +2916,12 @@ class GTEST_API_ UnorderedElementsAreMatcherImplBase {
// Describes the negation of this UnorderedElementsAre matcher.
void DescribeNegationToImpl(::std::ostream* os) const;
- bool VerifyAllElementsAndMatchersAreMatched(
- const ::std::vector<string>& element_printouts,
- const MatchMatrix& matrix,
- MatchResultListener* listener) const;
+ bool VerifyMatchMatrix(const ::std::vector<std::string>& element_printouts,
+ const MatchMatrix& matrix,
+ MatchResultListener* listener) const;
+
+ bool FindPairing(const MatchMatrix& matrix,
+ MatchResultListener* listener) const;
MatcherDescriberVec& matcher_describers() {
return matcher_describers_;
@@ -3334,13 +2931,17 @@ class GTEST_API_ UnorderedElementsAreMatcherImplBase {
return Message() << n << " element" << (n == 1 ? "" : "s");
}
+ UnorderedMatcherRequire::Flags match_flags() const { return match_flags_; }
+
private:
+ UnorderedMatcherRequire::Flags match_flags_;
MatcherDescriberVec matcher_describers_;
GTEST_DISALLOW_ASSIGN_(UnorderedElementsAreMatcherImplBase);
};
-// Implements unordered ElementsAre and unordered ElementsAreArray.
+// Implements UnorderedElementsAre, UnorderedElementsAreArray, IsSubsetOf, and
+// IsSupersetOf.
template <typename Container>
class UnorderedElementsAreMatcherImpl
: public MatcherInterface<Container>,
@@ -3353,10 +2954,10 @@ class UnorderedElementsAreMatcherImpl
typedef typename StlContainer::const_iterator StlContainerConstIterator;
typedef typename StlContainer::value_type Element;
- // Constructs the matcher from a sequence of element values or
- // element matchers.
template <typename InputIter>
- UnorderedElementsAreMatcherImpl(InputIter first, InputIter last) {
+ UnorderedElementsAreMatcherImpl(UnorderedMatcherRequire::Flags matcher_flags,
+ InputIter first, InputIter last)
+ : UnorderedElementsAreMatcherImplBase(matcher_flags) {
for (; first != last; ++first) {
matchers_.push_back(MatcherCast<const Element&>(*first));
matcher_describers().push_back(matchers_.back().GetDescriber());
@@ -3364,50 +2965,48 @@ class UnorderedElementsAreMatcherImpl
}
// Describes what this matcher does.
- virtual void DescribeTo(::std::ostream* os) const {
+ void DescribeTo(::std::ostream* os) const override {
return UnorderedElementsAreMatcherImplBase::DescribeToImpl(os);
}
// Describes what the negation of this matcher does.
- virtual void DescribeNegationTo(::std::ostream* os) const {
+ void DescribeNegationTo(::std::ostream* os) const override {
return UnorderedElementsAreMatcherImplBase::DescribeNegationToImpl(os);
}
- virtual bool MatchAndExplain(Container container,
- MatchResultListener* listener) const {
+ bool MatchAndExplain(Container container,
+ MatchResultListener* listener) const override {
StlContainerReference stl_container = View::ConstReference(container);
- ::std::vector<string> element_printouts;
- MatchMatrix matrix = AnalyzeElements(stl_container.begin(),
- stl_container.end(),
- &element_printouts,
- listener);
-
- const size_t actual_count = matrix.LhsSize();
- if (actual_count == 0 && matchers_.empty()) {
+ ::std::vector<std::string> element_printouts;
+ MatchMatrix matrix =
+ AnalyzeElements(stl_container.begin(), stl_container.end(),
+ &element_printouts, listener);
+
+ if (matrix.LhsSize() == 0 && matrix.RhsSize() == 0) {
return true;
}
- if (actual_count != matchers_.size()) {
- // The element count doesn't match. If the container is empty,
- // there's no need to explain anything as Google Mock already
- // prints the empty container. Otherwise we just need to show
- // how many elements there actually are.
- if (actual_count != 0 && listener->IsInterested()) {
- *listener << "which has " << Elements(actual_count);
+
+ if (match_flags() == UnorderedMatcherRequire::ExactMatch) {
+ if (matrix.LhsSize() != matrix.RhsSize()) {
+ // The element count doesn't match. If the container is empty,
+ // there's no need to explain anything as Google Mock already
+ // prints the empty container. Otherwise we just need to show
+ // how many elements there actually are.
+ if (matrix.LhsSize() != 0 && listener->IsInterested()) {
+ *listener << "which has " << Elements(matrix.LhsSize());
+ }
+ return false;
}
- return false;
}
- return VerifyAllElementsAndMatchersAreMatched(element_printouts,
- matrix, listener) &&
+ return VerifyMatchMatrix(element_printouts, matrix, listener) &&
FindPairing(matrix, listener);
}
private:
- typedef ::std::vector<Matcher<const Element&> > MatcherVec;
-
template <typename ElementIter>
MatchMatrix AnalyzeElements(ElementIter elem_first, ElementIter elem_last,
- ::std::vector<string>* element_printouts,
+ ::std::vector<std::string>* element_printouts,
MatchResultListener* listener) const {
element_printouts->clear();
::std::vector<char> did_match;
@@ -3431,7 +3030,7 @@ class UnorderedElementsAreMatcherImpl
return matrix;
}
- MatcherVec matchers_;
+ ::std::vector<Matcher<const Element&> > matchers_;
GTEST_DISALLOW_ASSIGN_(UnorderedElementsAreMatcherImpl);
};
@@ -3460,11 +3059,13 @@ class UnorderedElementsAreMatcher {
typedef typename View::value_type Element;
typedef ::std::vector<Matcher<const Element&> > MatcherVec;
MatcherVec matchers;
- matchers.reserve(::testing::tuple_size<MatcherTuple>::value);
+ matchers.reserve(::std::tuple_size<MatcherTuple>::value);
TransformTupleValues(CastAndAppendTransform<const Element&>(), matchers_,
::std::back_inserter(matchers));
- return MakeMatcher(new UnorderedElementsAreMatcherImpl<Container>(
- matchers.begin(), matchers.end()));
+ return Matcher<Container>(
+ new UnorderedElementsAreMatcherImpl<const Container&>(
+ UnorderedMatcherRequire::ExactMatch, matchers.begin(),
+ matchers.end()));
}
private:
@@ -3480,16 +3081,21 @@ class ElementsAreMatcher {
template <typename Container>
operator Matcher<Container>() const {
+ GTEST_COMPILE_ASSERT_(
+ !IsHashTable<GTEST_REMOVE_REFERENCE_AND_CONST_(Container)>::value ||
+ ::std::tuple_size<MatcherTuple>::value < 2,
+ use_UnorderedElementsAre_with_hash_tables);
+
typedef GTEST_REMOVE_REFERENCE_AND_CONST_(Container) RawContainer;
typedef typename internal::StlContainerView<RawContainer>::type View;
typedef typename View::value_type Element;
typedef ::std::vector<Matcher<const Element&> > MatcherVec;
MatcherVec matchers;
- matchers.reserve(::testing::tuple_size<MatcherTuple>::value);
+ matchers.reserve(::std::tuple_size<MatcherTuple>::value);
TransformTupleValues(CastAndAppendTransform<const Element&>(), matchers_,
::std::back_inserter(matchers));
- return MakeMatcher(new ElementsAreMatcherImpl<Container>(
- matchers.begin(), matchers.end()));
+ return Matcher<Container>(new ElementsAreMatcherImpl<const Container&>(
+ matchers.begin(), matchers.end()));
}
private:
@@ -3497,24 +3103,24 @@ class ElementsAreMatcher {
GTEST_DISALLOW_ASSIGN_(ElementsAreMatcher);
};
-// Implements UnorderedElementsAreArray().
+// Implements UnorderedElementsAreArray(), IsSubsetOf(), and IsSupersetOf().
template <typename T>
class UnorderedElementsAreArrayMatcher {
public:
- UnorderedElementsAreArrayMatcher() {}
-
template <typename Iter>
- UnorderedElementsAreArrayMatcher(Iter first, Iter last)
- : matchers_(first, last) {}
+ UnorderedElementsAreArrayMatcher(UnorderedMatcherRequire::Flags match_flags,
+ Iter first, Iter last)
+ : match_flags_(match_flags), matchers_(first, last) {}
template <typename Container>
operator Matcher<Container>() const {
- return MakeMatcher(
- new UnorderedElementsAreMatcherImpl<Container>(matchers_.begin(),
- matchers_.end()));
+ return Matcher<Container>(
+ new UnorderedElementsAreMatcherImpl<const Container&>(
+ match_flags_, matchers_.begin(), matchers_.end()));
}
private:
+ UnorderedMatcherRequire::Flags match_flags_;
::std::vector<T> matchers_;
GTEST_DISALLOW_ASSIGN_(UnorderedElementsAreArrayMatcher);
@@ -3529,7 +3135,11 @@ class ElementsAreArrayMatcher {
template <typename Container>
operator Matcher<Container>() const {
- return MakeMatcher(new ElementsAreMatcherImpl<Container>(
+ GTEST_COMPILE_ASSERT_(
+ !IsHashTable<GTEST_REMOVE_REFERENCE_AND_CONST_(Container)>::value,
+ use_UnorderedElementsAreArray_with_hash_tables);
+
+ return Matcher<Container>(new ElementsAreMatcherImpl<const Container&>(
matchers_.begin(), matchers_.end()));
}
@@ -3541,8 +3151,8 @@ class ElementsAreArrayMatcher {
// Given a 2-tuple matcher tm of type Tuple2Matcher and a value second
// of type Second, BoundSecondMatcher<Tuple2Matcher, Second>(tm,
-// second) is a polymorphic matcher that matches a value x iff tm
-// matches tuple (x, second). Useful for implementing
+// second) is a polymorphic matcher that matches a value x if and only if
+// tm matches tuple (x, second). Useful for implementing
// UnorderedPointwise() in terms of UnorderedElementsAreArray().
//
// BoundSecondMatcher is copyable and assignable, as we need to put
@@ -3575,20 +3185,20 @@ class BoundSecondMatcher {
template <typename T>
class Impl : public MatcherInterface<T> {
public:
- typedef ::testing::tuple<T, Second> ArgTuple;
+ typedef ::std::tuple<T, Second> ArgTuple;
Impl(const Tuple2Matcher& tm, const Second& second)
: mono_tuple2_matcher_(SafeMatcherCast<const ArgTuple&>(tm)),
second_value_(second) {}
- virtual void DescribeTo(::std::ostream* os) const {
+ void DescribeTo(::std::ostream* os) const override {
*os << "and ";
UniversalPrint(second_value_, os);
*os << " ";
mono_tuple2_matcher_.DescribeTo(os);
}
- virtual bool MatchAndExplain(T x, MatchResultListener* listener) const {
+ bool MatchAndExplain(T x, MatchResultListener* listener) const override {
return mono_tuple2_matcher_.MatchAndExplain(ArgTuple(x, second_value_),
listener);
}
@@ -3606,8 +3216,8 @@ class BoundSecondMatcher {
// Given a 2-tuple matcher tm and a value second,
// MatcherBindSecond(tm, second) returns a matcher that matches a
-// value x iff tm matches tuple (x, second). Useful for implementing
-// UnorderedPointwise() in terms of UnorderedElementsAreArray().
+// value x if and only if tm matches tuple (x, second). Useful for
+// implementing UnorderedPointwise() in terms of UnorderedElementsAreArray().
template <typename Tuple2Matcher, typename Second>
BoundSecondMatcher<Tuple2Matcher, Second> MatcherBindSecond(
const Tuple2Matcher& tm, const Second& second) {
@@ -3619,13 +3229,264 @@ BoundSecondMatcher<Tuple2Matcher, Second> MatcherBindSecond(
// 'negation' is false; otherwise returns the description of the
// negation of the matcher. 'param_values' contains a list of strings
// that are the print-out of the matcher's parameters.
-GTEST_API_ string FormatMatcherDescription(bool negation,
- const char* matcher_name,
- const Strings& param_values);
+GTEST_API_ std::string FormatMatcherDescription(bool negation,
+ const char* matcher_name,
+ const Strings& param_values);
+
+// Implements a matcher that checks the value of a optional<> type variable.
+template <typename ValueMatcher>
+class OptionalMatcher {
+ public:
+ explicit OptionalMatcher(const ValueMatcher& value_matcher)
+ : value_matcher_(value_matcher) {}
+
+ template <typename Optional>
+ operator Matcher<Optional>() const {
+ return Matcher<Optional>(new Impl<const Optional&>(value_matcher_));
+ }
+
+ template <typename Optional>
+ class Impl : public MatcherInterface<Optional> {
+ public:
+ typedef GTEST_REMOVE_REFERENCE_AND_CONST_(Optional) OptionalView;
+ typedef typename OptionalView::value_type ValueType;
+ explicit Impl(const ValueMatcher& value_matcher)
+ : value_matcher_(MatcherCast<ValueType>(value_matcher)) {}
+
+ void DescribeTo(::std::ostream* os) const override {
+ *os << "value ";
+ value_matcher_.DescribeTo(os);
+ }
+
+ void DescribeNegationTo(::std::ostream* os) const override {
+ *os << "value ";
+ value_matcher_.DescribeNegationTo(os);
+ }
+
+ bool MatchAndExplain(Optional optional,
+ MatchResultListener* listener) const override {
+ if (!optional) {
+ *listener << "which is not engaged";
+ return false;
+ }
+ const ValueType& value = *optional;
+ StringMatchResultListener value_listener;
+ const bool match = value_matcher_.MatchAndExplain(value, &value_listener);
+ *listener << "whose value " << PrintToString(value)
+ << (match ? " matches" : " doesn't match");
+ PrintIfNotEmpty(value_listener.str(), listener->stream());
+ return match;
+ }
+
+ private:
+ const Matcher<ValueType> value_matcher_;
+ GTEST_DISALLOW_ASSIGN_(Impl);
+ };
+
+ private:
+ const ValueMatcher value_matcher_;
+ GTEST_DISALLOW_ASSIGN_(OptionalMatcher);
+};
+
+namespace variant_matcher {
+// Overloads to allow VariantMatcher to do proper ADL lookup.
+template <typename T>
+void holds_alternative() {}
+template <typename T>
+void get() {}
+
+// Implements a matcher that checks the value of a variant<> type variable.
+template <typename T>
+class VariantMatcher {
+ public:
+ explicit VariantMatcher(::testing::Matcher<const T&> matcher)
+ : matcher_(std::move(matcher)) {}
+
+ template <typename Variant>
+ bool MatchAndExplain(const Variant& value,
+ ::testing::MatchResultListener* listener) const {
+ using std::get;
+ if (!listener->IsInterested()) {
+ return holds_alternative<T>(value) && matcher_.Matches(get<T>(value));
+ }
+
+ if (!holds_alternative<T>(value)) {
+ *listener << "whose value is not of type '" << GetTypeName() << "'";
+ return false;
+ }
+
+ const T& elem = get<T>(value);
+ StringMatchResultListener elem_listener;
+ const bool match = matcher_.MatchAndExplain(elem, &elem_listener);
+ *listener << "whose value " << PrintToString(elem)
+ << (match ? " matches" : " doesn't match");
+ PrintIfNotEmpty(elem_listener.str(), listener->stream());
+ return match;
+ }
+
+ void DescribeTo(std::ostream* os) const {
+ *os << "is a variant<> with value of type '" << GetTypeName()
+ << "' and the value ";
+ matcher_.DescribeTo(os);
+ }
+
+ void DescribeNegationTo(std::ostream* os) const {
+ *os << "is a variant<> with value of type other than '" << GetTypeName()
+ << "' or the value ";
+ matcher_.DescribeNegationTo(os);
+ }
+
+ private:
+ static std::string GetTypeName() {
+#if GTEST_HAS_RTTI
+ GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(
+ return internal::GetTypeName<T>());
+#endif
+ return "the element type";
+ }
+
+ const ::testing::Matcher<const T&> matcher_;
+};
+
+} // namespace variant_matcher
+
+namespace any_cast_matcher {
+
+// Overloads to allow AnyCastMatcher to do proper ADL lookup.
+template <typename T>
+void any_cast() {}
+
+// Implements a matcher that any_casts the value.
+template <typename T>
+class AnyCastMatcher {
+ public:
+ explicit AnyCastMatcher(const ::testing::Matcher<const T&>& matcher)
+ : matcher_(matcher) {}
+
+ template <typename AnyType>
+ bool MatchAndExplain(const AnyType& value,
+ ::testing::MatchResultListener* listener) const {
+ if (!listener->IsInterested()) {
+ const T* ptr = any_cast<T>(&value);
+ return ptr != nullptr && matcher_.Matches(*ptr);
+ }
+
+ const T* elem = any_cast<T>(&value);
+ if (elem == nullptr) {
+ *listener << "whose value is not of type '" << GetTypeName() << "'";
+ return false;
+ }
+
+ StringMatchResultListener elem_listener;
+ const bool match = matcher_.MatchAndExplain(*elem, &elem_listener);
+ *listener << "whose value " << PrintToString(*elem)
+ << (match ? " matches" : " doesn't match");
+ PrintIfNotEmpty(elem_listener.str(), listener->stream());
+ return match;
+ }
+
+ void DescribeTo(std::ostream* os) const {
+ *os << "is an 'any' type with value of type '" << GetTypeName()
+ << "' and the value ";
+ matcher_.DescribeTo(os);
+ }
+
+ void DescribeNegationTo(std::ostream* os) const {
+ *os << "is an 'any' type with value of type other than '" << GetTypeName()
+ << "' or the value ";
+ matcher_.DescribeNegationTo(os);
+ }
+
+ private:
+ static std::string GetTypeName() {
+#if GTEST_HAS_RTTI
+ GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(
+ return internal::GetTypeName<T>());
+#endif
+ return "the element type";
+ }
+
+ const ::testing::Matcher<const T&> matcher_;
+};
+
+} // namespace any_cast_matcher
+
+// Implements the Args() matcher.
+template <class ArgsTuple, size_t... k>
+class ArgsMatcherImpl : public MatcherInterface<ArgsTuple> {
+ public:
+ using RawArgsTuple = typename std::decay<ArgsTuple>::type;
+ using SelectedArgs =
+ std::tuple<typename std::tuple_element<k, RawArgsTuple>::type...>;
+ using MonomorphicInnerMatcher = Matcher<const SelectedArgs&>;
+
+ template <typename InnerMatcher>
+ explicit ArgsMatcherImpl(const InnerMatcher& inner_matcher)
+ : inner_matcher_(SafeMatcherCast<const SelectedArgs&>(inner_matcher)) {}
+
+ bool MatchAndExplain(ArgsTuple args,
+ MatchResultListener* listener) const override {
+ // Workaround spurious C4100 on MSVC<=15.7 when k is empty.
+ (void)args;
+ const SelectedArgs& selected_args =
+ std::forward_as_tuple(std::get<k>(args)...);
+ if (!listener->IsInterested()) return inner_matcher_.Matches(selected_args);
+
+ PrintIndices(listener->stream());
+ *listener << "are " << PrintToString(selected_args);
+
+ StringMatchResultListener inner_listener;
+ const bool match =
+ inner_matcher_.MatchAndExplain(selected_args, &inner_listener);
+ PrintIfNotEmpty(inner_listener.str(), listener->stream());
+ return match;
+ }
+
+ void DescribeTo(::std::ostream* os) const override {
+ *os << "are a tuple ";
+ PrintIndices(os);
+ inner_matcher_.DescribeTo(os);
+ }
+
+ void DescribeNegationTo(::std::ostream* os) const override {
+ *os << "are a tuple ";
+ PrintIndices(os);
+ inner_matcher_.DescribeNegationTo(os);
+ }
+
+ private:
+ // Prints the indices of the selected fields.
+ static void PrintIndices(::std::ostream* os) {
+ *os << "whose fields (";
+ const char* sep = "";
+ // Workaround spurious C4189 on MSVC<=15.7 when k is empty.
+ (void)sep;
+ const char* dummy[] = {"", (*os << sep << "#" << k, sep = ", ")...};
+ (void)dummy;
+ *os << ") ";
+ }
+
+ MonomorphicInnerMatcher inner_matcher_;
+};
+
+template <class InnerMatcher, size_t... k>
+class ArgsMatcher {
+ public:
+ explicit ArgsMatcher(InnerMatcher inner_matcher)
+ : inner_matcher_(std::move(inner_matcher)) {}
+
+ template <typename ArgsTuple>
+ operator Matcher<ArgsTuple>() const { // NOLINT
+ return MakeMatcher(new ArgsMatcherImpl<ArgsTuple, k...>(inner_matcher_));
+ }
+
+ private:
+ InnerMatcher inner_matcher_;
+};
} // namespace internal
-// ElementsAreArray(first, last)
+// ElementsAreArray(iterator_first, iterator_last)
// ElementsAreArray(pointer, count)
// ElementsAreArray(array)
// ElementsAreArray(container)
@@ -3666,28 +3527,32 @@ ElementsAreArray(const Container& container) {
return ElementsAreArray(container.begin(), container.end());
}
-#if GTEST_HAS_STD_INITIALIZER_LIST_
template <typename T>
inline internal::ElementsAreArrayMatcher<T>
ElementsAreArray(::std::initializer_list<T> xs) {
return ElementsAreArray(xs.begin(), xs.end());
}
-#endif
-// UnorderedElementsAreArray(first, last)
+// UnorderedElementsAreArray(iterator_first, iterator_last)
// UnorderedElementsAreArray(pointer, count)
// UnorderedElementsAreArray(array)
// UnorderedElementsAreArray(container)
// UnorderedElementsAreArray({ e1, e2, ..., en })
//
-// The UnorderedElementsAreArray() functions are like
-// ElementsAreArray(...), but allow matching the elements in any order.
+// UnorderedElementsAreArray() verifies that a bijective mapping onto a
+// collection of matchers exists.
+//
+// The matchers can be specified as an array, a pointer and count, a container,
+// an initializer list, or an STL iterator range. In each of these cases, the
+// underlying matchers can be either values or matchers.
+
template <typename Iter>
inline internal::UnorderedElementsAreArrayMatcher<
typename ::std::iterator_traits<Iter>::value_type>
UnorderedElementsAreArray(Iter first, Iter last) {
typedef typename ::std::iterator_traits<Iter>::value_type T;
- return internal::UnorderedElementsAreArrayMatcher<T>(first, last);
+ return internal::UnorderedElementsAreArrayMatcher<T>(
+ internal::UnorderedMatcherRequire::ExactMatch, first, last);
}
template <typename T>
@@ -3709,13 +3574,11 @@ UnorderedElementsAreArray(const Container& container) {
return UnorderedElementsAreArray(container.begin(), container.end());
}
-#if GTEST_HAS_STD_INITIALIZER_LIST_
template <typename T>
inline internal::UnorderedElementsAreArrayMatcher<T>
UnorderedElementsAreArray(::std::initializer_list<T> xs) {
return UnorderedElementsAreArray(xs.begin(), xs.end());
}
-#endif
// _ is a matcher that matches anything of any type.
//
@@ -3729,66 +3592,19 @@ UnorderedElementsAreArray(::std::initializer_list<T> xs) {
const internal::AnythingMatcher _ = {};
// Creates a matcher that matches any value of the given type T.
template <typename T>
-inline Matcher<T> A() { return MakeMatcher(new internal::AnyMatcherImpl<T>()); }
+inline Matcher<T> A() {
+ return Matcher<T>(new internal::AnyMatcherImpl<T>());
+}
// Creates a matcher that matches any value of the given type T.
template <typename T>
inline Matcher<T> An() { return A<T>(); }
-// Creates a polymorphic matcher that matches anything equal to x.
-// Note: if the parameter of Eq() were declared as const T&, Eq("foo")
-// wouldn't compile.
-template <typename T>
-inline internal::EqMatcher<T> Eq(T x) { return internal::EqMatcher<T>(x); }
-
-// Constructs a Matcher<T> from a 'value' of type T. The constructed
-// matcher matches any value that's equal to 'value'.
-template <typename T>
-Matcher<T>::Matcher(T value) { *this = Eq(value); }
-
-// Creates a monomorphic matcher that matches anything with type Lhs
-// and equal to rhs. A user may need to use this instead of Eq(...)
-// in order to resolve an overloading ambiguity.
-//
-// TypedEq<T>(x) is just a convenient short-hand for Matcher<T>(Eq(x))
-// or Matcher<T>(x), but more readable than the latter.
-//
-// We could define similar monomorphic matchers for other comparison
-// operations (e.g. TypedLt, TypedGe, and etc), but decided not to do
-// it yet as those are used much less than Eq() in practice. A user
-// can always write Matcher<T>(Lt(5)) to be explicit about the type,
-// for example.
-template <typename Lhs, typename Rhs>
-inline Matcher<Lhs> TypedEq(const Rhs& rhs) { return Eq(rhs); }
-
-// Creates a polymorphic matcher that matches anything >= x.
-template <typename Rhs>
-inline internal::GeMatcher<Rhs> Ge(Rhs x) {
- return internal::GeMatcher<Rhs>(x);
-}
-
-// Creates a polymorphic matcher that matches anything > x.
-template <typename Rhs>
-inline internal::GtMatcher<Rhs> Gt(Rhs x) {
- return internal::GtMatcher<Rhs>(x);
-}
-
-// Creates a polymorphic matcher that matches anything <= x.
-template <typename Rhs>
-inline internal::LeMatcher<Rhs> Le(Rhs x) {
- return internal::LeMatcher<Rhs>(x);
-}
-
-// Creates a polymorphic matcher that matches anything < x.
-template <typename Rhs>
-inline internal::LtMatcher<Rhs> Lt(Rhs x) {
- return internal::LtMatcher<Rhs>(x);
-}
-
-// Creates a polymorphic matcher that matches anything != x.
-template <typename Rhs>
-inline internal::NeMatcher<Rhs> Ne(Rhs x) {
- return internal::NeMatcher<Rhs>(x);
+template <typename T, typename M>
+Matcher<T> internal::MatcherCastImpl<T, M>::CastImpl(
+ const M& value, std::false_type /* convertible_to_matcher */,
+ std::false_type /* convertible_to_T */) {
+ return Eq(value);
}
// Creates a polymorphic matcher that matches any NULL pointer.
@@ -3874,6 +3690,7 @@ inline internal::PointeeMatcher<InnerMatcher> Pointee(
return internal::PointeeMatcher<InnerMatcher>(inner_matcher);
}
+#if GTEST_HAS_RTTI
// Creates a matcher that matches a pointer or reference that matches
// inner_matcher when dynamic_cast<To> is applied.
// The result of dynamic_cast<To> is forwarded to the inner matcher.
@@ -3886,11 +3703,12 @@ WhenDynamicCastTo(const Matcher<To>& inner_matcher) {
return MakePolymorphicMatcher(
internal::WhenDynamicCastToMatcher<To>(inner_matcher));
}
+#endif // GTEST_HAS_RTTI
// Creates a matcher that matches an object whose given field matches
// 'matcher'. For example,
// Field(&Foo::number, Ge(5))
-// matches a Foo object x iff x.number >= 5.
+// matches a Foo object x if and only if x.number >= 5.
template <typename Class, typename FieldType, typename FieldMatcher>
inline PolymorphicMatcher<
internal::FieldMatcher<Class, FieldType> > Field(
@@ -3904,178 +3722,194 @@ inline PolymorphicMatcher<
// to compile where bar is an int32 and m is a matcher for int64.
}
+// Same as Field() but also takes the name of the field to provide better error
+// messages.
+template <typename Class, typename FieldType, typename FieldMatcher>
+inline PolymorphicMatcher<internal::FieldMatcher<Class, FieldType> > Field(
+ const std::string& field_name, FieldType Class::*field,
+ const FieldMatcher& matcher) {
+ return MakePolymorphicMatcher(internal::FieldMatcher<Class, FieldType>(
+ field_name, field, MatcherCast<const FieldType&>(matcher)));
+}
+
// Creates a matcher that matches an object whose given property
// matches 'matcher'. For example,
// Property(&Foo::str, StartsWith("hi"))
-// matches a Foo object x iff x.str() starts with "hi".
+// matches a Foo object x if and only if x.str() starts with "hi".
template <typename Class, typename PropertyType, typename PropertyMatcher>
-inline PolymorphicMatcher<
- internal::PropertyMatcher<Class, PropertyType> > Property(
- PropertyType (Class::*property)() const, const PropertyMatcher& matcher) {
+inline PolymorphicMatcher<internal::PropertyMatcher<
+ Class, PropertyType, PropertyType (Class::*)() const> >
+Property(PropertyType (Class::*property)() const,
+ const PropertyMatcher& matcher) {
return MakePolymorphicMatcher(
- internal::PropertyMatcher<Class, PropertyType>(
- property,
- MatcherCast<GTEST_REFERENCE_TO_CONST_(PropertyType)>(matcher)));
+ internal::PropertyMatcher<Class, PropertyType,
+ PropertyType (Class::*)() const>(
+ property, MatcherCast<const PropertyType&>(matcher)));
// The call to MatcherCast() is required for supporting inner
// matchers of compatible types. For example, it allows
// Property(&Foo::bar, m)
// to compile where bar() returns an int32 and m is a matcher for int64.
}
-// Creates a matcher that matches an object iff the result of applying
-// a callable to x matches 'matcher'.
-// For example,
+// Same as Property() above, but also takes the name of the property to provide
+// better error messages.
+template <typename Class, typename PropertyType, typename PropertyMatcher>
+inline PolymorphicMatcher<internal::PropertyMatcher<
+ Class, PropertyType, PropertyType (Class::*)() const> >
+Property(const std::string& property_name,
+ PropertyType (Class::*property)() const,
+ const PropertyMatcher& matcher) {
+ return MakePolymorphicMatcher(
+ internal::PropertyMatcher<Class, PropertyType,
+ PropertyType (Class::*)() const>(
+ property_name, property, MatcherCast<const PropertyType&>(matcher)));
+}
+
+// The same as above but for reference-qualified member functions.
+template <typename Class, typename PropertyType, typename PropertyMatcher>
+inline PolymorphicMatcher<internal::PropertyMatcher<
+ Class, PropertyType, PropertyType (Class::*)() const &> >
+Property(PropertyType (Class::*property)() const &,
+ const PropertyMatcher& matcher) {
+ return MakePolymorphicMatcher(
+ internal::PropertyMatcher<Class, PropertyType,
+ PropertyType (Class::*)() const&>(
+ property, MatcherCast<const PropertyType&>(matcher)));
+}
+
+// Three-argument form for reference-qualified member functions.
+template <typename Class, typename PropertyType, typename PropertyMatcher>
+inline PolymorphicMatcher<internal::PropertyMatcher<
+ Class, PropertyType, PropertyType (Class::*)() const &> >
+Property(const std::string& property_name,
+ PropertyType (Class::*property)() const &,
+ const PropertyMatcher& matcher) {
+ return MakePolymorphicMatcher(
+ internal::PropertyMatcher<Class, PropertyType,
+ PropertyType (Class::*)() const&>(
+ property_name, property, MatcherCast<const PropertyType&>(matcher)));
+}
+
+// Creates a matcher that matches an object if and only if the result of
+// applying a callable to x matches 'matcher'. For example,
// ResultOf(f, StartsWith("hi"))
-// matches a Foo object x iff f(x) starts with "hi".
-// callable parameter can be a function, function pointer, or a functor.
-// Callable has to satisfy the following conditions:
-// * It is required to keep no state affecting the results of
-// the calls on it and make no assumptions about how many calls
-// will be made. Any state it keeps must be protected from the
-// concurrent access.
-// * If it is a function object, it has to define type result_type.
-// We recommend deriving your functor classes from std::unary_function.
-template <typename Callable, typename ResultOfMatcher>
-internal::ResultOfMatcher<Callable> ResultOf(
- Callable callable, const ResultOfMatcher& matcher) {
- return internal::ResultOfMatcher<Callable>(
- callable,
- MatcherCast<typename internal::CallableTraits<Callable>::ResultType>(
- matcher));
- // The call to MatcherCast() is required for supporting inner
- // matchers of compatible types. For example, it allows
- // ResultOf(Function, m)
- // to compile where Function() returns an int32 and m is a matcher for int64.
+// matches a Foo object x if and only if f(x) starts with "hi".
+// `callable` parameter can be a function, function pointer, or a functor. It is
+// required to keep no state affecting the results of the calls on it and make
+// no assumptions about how many calls will be made. Any state it keeps must be
+// protected from the concurrent access.
+template <typename Callable, typename InnerMatcher>
+internal::ResultOfMatcher<Callable, InnerMatcher> ResultOf(
+ Callable callable, InnerMatcher matcher) {
+ return internal::ResultOfMatcher<Callable, InnerMatcher>(
+ std::move(callable), std::move(matcher));
}
// String matchers.
// Matches a string equal to str.
-inline PolymorphicMatcher<internal::StrEqualityMatcher<internal::string> >
- StrEq(const internal::string& str) {
- return MakePolymorphicMatcher(internal::StrEqualityMatcher<internal::string>(
- str, true, true));
+inline PolymorphicMatcher<internal::StrEqualityMatcher<std::string> > StrEq(
+ const std::string& str) {
+ return MakePolymorphicMatcher(
+ internal::StrEqualityMatcher<std::string>(str, true, true));
}
// Matches a string not equal to str.
-inline PolymorphicMatcher<internal::StrEqualityMatcher<internal::string> >
- StrNe(const internal::string& str) {
- return MakePolymorphicMatcher(internal::StrEqualityMatcher<internal::string>(
- str, false, true));
+inline PolymorphicMatcher<internal::StrEqualityMatcher<std::string> > StrNe(
+ const std::string& str) {
+ return MakePolymorphicMatcher(
+ internal::StrEqualityMatcher<std::string>(str, false, true));
}
// Matches a string equal to str, ignoring case.
-inline PolymorphicMatcher<internal::StrEqualityMatcher<internal::string> >
- StrCaseEq(const internal::string& str) {
- return MakePolymorphicMatcher(internal::StrEqualityMatcher<internal::string>(
- str, true, false));
+inline PolymorphicMatcher<internal::StrEqualityMatcher<std::string> > StrCaseEq(
+ const std::string& str) {
+ return MakePolymorphicMatcher(
+ internal::StrEqualityMatcher<std::string>(str, true, false));
}
// Matches a string not equal to str, ignoring case.
-inline PolymorphicMatcher<internal::StrEqualityMatcher<internal::string> >
- StrCaseNe(const internal::string& str) {
- return MakePolymorphicMatcher(internal::StrEqualityMatcher<internal::string>(
- str, false, false));
+inline PolymorphicMatcher<internal::StrEqualityMatcher<std::string> > StrCaseNe(
+ const std::string& str) {
+ return MakePolymorphicMatcher(
+ internal::StrEqualityMatcher<std::string>(str, false, false));
}
// Creates a matcher that matches any string, std::string, or C string
// that contains the given substring.
-inline PolymorphicMatcher<internal::HasSubstrMatcher<internal::string> >
- HasSubstr(const internal::string& substring) {
- return MakePolymorphicMatcher(internal::HasSubstrMatcher<internal::string>(
- substring));
+inline PolymorphicMatcher<internal::HasSubstrMatcher<std::string> > HasSubstr(
+ const std::string& substring) {
+ return MakePolymorphicMatcher(
+ internal::HasSubstrMatcher<std::string>(substring));
}
// Matches a string that starts with 'prefix' (case-sensitive).
-inline PolymorphicMatcher<internal::StartsWithMatcher<internal::string> >
- StartsWith(const internal::string& prefix) {
- return MakePolymorphicMatcher(internal::StartsWithMatcher<internal::string>(
- prefix));
+inline PolymorphicMatcher<internal::StartsWithMatcher<std::string> > StartsWith(
+ const std::string& prefix) {
+ return MakePolymorphicMatcher(
+ internal::StartsWithMatcher<std::string>(prefix));
}
// Matches a string that ends with 'suffix' (case-sensitive).
-inline PolymorphicMatcher<internal::EndsWithMatcher<internal::string> >
- EndsWith(const internal::string& suffix) {
- return MakePolymorphicMatcher(internal::EndsWithMatcher<internal::string>(
- suffix));
-}
-
-// Matches a string that fully matches regular expression 'regex'.
-// The matcher takes ownership of 'regex'.
-inline PolymorphicMatcher<internal::MatchesRegexMatcher> MatchesRegex(
- const internal::RE* regex) {
- return MakePolymorphicMatcher(internal::MatchesRegexMatcher(regex, true));
-}
-inline PolymorphicMatcher<internal::MatchesRegexMatcher> MatchesRegex(
- const internal::string& regex) {
- return MatchesRegex(new internal::RE(regex));
+inline PolymorphicMatcher<internal::EndsWithMatcher<std::string> > EndsWith(
+ const std::string& suffix) {
+ return MakePolymorphicMatcher(internal::EndsWithMatcher<std::string>(suffix));
}
-// Matches a string that contains regular expression 'regex'.
-// The matcher takes ownership of 'regex'.
-inline PolymorphicMatcher<internal::MatchesRegexMatcher> ContainsRegex(
- const internal::RE* regex) {
- return MakePolymorphicMatcher(internal::MatchesRegexMatcher(regex, false));
-}
-inline PolymorphicMatcher<internal::MatchesRegexMatcher> ContainsRegex(
- const internal::string& regex) {
- return ContainsRegex(new internal::RE(regex));
-}
-
-#if GTEST_HAS_GLOBAL_WSTRING || GTEST_HAS_STD_WSTRING
+#if GTEST_HAS_STD_WSTRING
// Wide string matchers.
// Matches a string equal to str.
-inline PolymorphicMatcher<internal::StrEqualityMatcher<internal::wstring> >
- StrEq(const internal::wstring& str) {
- return MakePolymorphicMatcher(internal::StrEqualityMatcher<internal::wstring>(
- str, true, true));
+inline PolymorphicMatcher<internal::StrEqualityMatcher<std::wstring> > StrEq(
+ const std::wstring& str) {
+ return MakePolymorphicMatcher(
+ internal::StrEqualityMatcher<std::wstring>(str, true, true));
}
// Matches a string not equal to str.
-inline PolymorphicMatcher<internal::StrEqualityMatcher<internal::wstring> >
- StrNe(const internal::wstring& str) {
- return MakePolymorphicMatcher(internal::StrEqualityMatcher<internal::wstring>(
- str, false, true));
+inline PolymorphicMatcher<internal::StrEqualityMatcher<std::wstring> > StrNe(
+ const std::wstring& str) {
+ return MakePolymorphicMatcher(
+ internal::StrEqualityMatcher<std::wstring>(str, false, true));
}
// Matches a string equal to str, ignoring case.
-inline PolymorphicMatcher<internal::StrEqualityMatcher<internal::wstring> >
- StrCaseEq(const internal::wstring& str) {
- return MakePolymorphicMatcher(internal::StrEqualityMatcher<internal::wstring>(
- str, true, false));
+inline PolymorphicMatcher<internal::StrEqualityMatcher<std::wstring> >
+StrCaseEq(const std::wstring& str) {
+ return MakePolymorphicMatcher(
+ internal::StrEqualityMatcher<std::wstring>(str, true, false));
}
// Matches a string not equal to str, ignoring case.
-inline PolymorphicMatcher<internal::StrEqualityMatcher<internal::wstring> >
- StrCaseNe(const internal::wstring& str) {
- return MakePolymorphicMatcher(internal::StrEqualityMatcher<internal::wstring>(
- str, false, false));
+inline PolymorphicMatcher<internal::StrEqualityMatcher<std::wstring> >
+StrCaseNe(const std::wstring& str) {
+ return MakePolymorphicMatcher(
+ internal::StrEqualityMatcher<std::wstring>(str, false, false));
}
-// Creates a matcher that matches any wstring, std::wstring, or C wide string
+// Creates a matcher that matches any ::wstring, std::wstring, or C wide string
// that contains the given substring.
-inline PolymorphicMatcher<internal::HasSubstrMatcher<internal::wstring> >
- HasSubstr(const internal::wstring& substring) {
- return MakePolymorphicMatcher(internal::HasSubstrMatcher<internal::wstring>(
- substring));
+inline PolymorphicMatcher<internal::HasSubstrMatcher<std::wstring> > HasSubstr(
+ const std::wstring& substring) {
+ return MakePolymorphicMatcher(
+ internal::HasSubstrMatcher<std::wstring>(substring));
}
// Matches a string that starts with 'prefix' (case-sensitive).
-inline PolymorphicMatcher<internal::StartsWithMatcher<internal::wstring> >
- StartsWith(const internal::wstring& prefix) {
- return MakePolymorphicMatcher(internal::StartsWithMatcher<internal::wstring>(
- prefix));
+inline PolymorphicMatcher<internal::StartsWithMatcher<std::wstring> >
+StartsWith(const std::wstring& prefix) {
+ return MakePolymorphicMatcher(
+ internal::StartsWithMatcher<std::wstring>(prefix));
}
// Matches a string that ends with 'suffix' (case-sensitive).
-inline PolymorphicMatcher<internal::EndsWithMatcher<internal::wstring> >
- EndsWith(const internal::wstring& suffix) {
- return MakePolymorphicMatcher(internal::EndsWithMatcher<internal::wstring>(
- suffix));
+inline PolymorphicMatcher<internal::EndsWithMatcher<std::wstring> > EndsWith(
+ const std::wstring& suffix) {
+ return MakePolymorphicMatcher(
+ internal::EndsWithMatcher<std::wstring>(suffix));
}
-#endif // GTEST_HAS_GLOBAL_WSTRING || GTEST_HAS_STD_WSTRING
+#endif // GTEST_HAS_STD_WSTRING
// Creates a polymorphic matcher that matches a 2-tuple where the
// first field == the second field.
@@ -4101,6 +3935,58 @@ inline internal::Lt2Matcher Lt() { return internal::Lt2Matcher(); }
// first field != the second field.
inline internal::Ne2Matcher Ne() { return internal::Ne2Matcher(); }
+// Creates a polymorphic matcher that matches a 2-tuple where
+// FloatEq(first field) matches the second field.
+inline internal::FloatingEq2Matcher<float> FloatEq() {
+ return internal::FloatingEq2Matcher<float>();
+}
+
+// Creates a polymorphic matcher that matches a 2-tuple where
+// DoubleEq(first field) matches the second field.
+inline internal::FloatingEq2Matcher<double> DoubleEq() {
+ return internal::FloatingEq2Matcher<double>();
+}
+
+// Creates a polymorphic matcher that matches a 2-tuple where
+// FloatEq(first field) matches the second field with NaN equality.
+inline internal::FloatingEq2Matcher<float> NanSensitiveFloatEq() {
+ return internal::FloatingEq2Matcher<float>(true);
+}
+
+// Creates a polymorphic matcher that matches a 2-tuple where
+// DoubleEq(first field) matches the second field with NaN equality.
+inline internal::FloatingEq2Matcher<double> NanSensitiveDoubleEq() {
+ return internal::FloatingEq2Matcher<double>(true);
+}
+
+// Creates a polymorphic matcher that matches a 2-tuple where
+// FloatNear(first field, max_abs_error) matches the second field.
+inline internal::FloatingEq2Matcher<float> FloatNear(float max_abs_error) {
+ return internal::FloatingEq2Matcher<float>(max_abs_error);
+}
+
+// Creates a polymorphic matcher that matches a 2-tuple where
+// DoubleNear(first field, max_abs_error) matches the second field.
+inline internal::FloatingEq2Matcher<double> DoubleNear(double max_abs_error) {
+ return internal::FloatingEq2Matcher<double>(max_abs_error);
+}
+
+// Creates a polymorphic matcher that matches a 2-tuple where
+// FloatNear(first field, max_abs_error) matches the second field with NaN
+// equality.
+inline internal::FloatingEq2Matcher<float> NanSensitiveFloatNear(
+ float max_abs_error) {
+ return internal::FloatingEq2Matcher<float>(max_abs_error, true);
+}
+
+// Creates a polymorphic matcher that matches a 2-tuple where
+// DoubleNear(first field, max_abs_error) matches the second field with NaN
+// equality.
+inline internal::FloatingEq2Matcher<double> NanSensitiveDoubleNear(
+ double max_abs_error) {
+ return internal::FloatingEq2Matcher<double>(max_abs_error, true);
+}
+
// Creates a matcher that matches any value of type T that m doesn't
// match.
template <typename InnerMatcher>
@@ -4145,12 +4031,12 @@ BeginEndDistanceIs(const DistanceMatcher& distance_matcher) {
// values that are included in one container but not the other. (Duplicate
// values and order differences are not explained.)
template <typename Container>
-inline PolymorphicMatcher<internal::ContainerEqMatcher< // NOLINT
- GTEST_REMOVE_CONST_(Container)> >
- ContainerEq(const Container& rhs) {
+inline PolymorphicMatcher<internal::ContainerEqMatcher<
+ typename std::remove_const<Container>::type>>
+ContainerEq(const Container& rhs) {
// This following line is for working around a bug in MSVC 8.0,
// which causes Container to be a const type sometimes.
- typedef GTEST_REMOVE_CONST_(Container) RawContainer;
+ typedef typename std::remove_const<Container>::type RawContainer;
return MakePolymorphicMatcher(
internal::ContainerEqMatcher<RawContainer>(rhs));
}
@@ -4178,22 +4064,21 @@ WhenSorted(const ContainerMatcher& container_matcher) {
// Matches an STL-style container or a native array that contains the
// same number of elements as in rhs, where its i-th element and rhs's
// i-th element (as a pair) satisfy the given pair matcher, for all i.
-// TupleMatcher must be able to be safely cast to Matcher<tuple<const
+// TupleMatcher must be able to be safely cast to Matcher<std::tuple<const
// T1&, const T2&> >, where T1 and T2 are the types of elements in the
// LHS container and the RHS container respectively.
template <typename TupleMatcher, typename Container>
inline internal::PointwiseMatcher<TupleMatcher,
- GTEST_REMOVE_CONST_(Container)>
+ typename std::remove_const<Container>::type>
Pointwise(const TupleMatcher& tuple_matcher, const Container& rhs) {
// This following line is for working around a bug in MSVC 8.0,
// which causes Container to be a const type sometimes (e.g. when
// rhs is a const int[])..
- typedef GTEST_REMOVE_CONST_(Container) RawContainer;
+ typedef typename std::remove_const<Container>::type RawContainer;
return internal::PointwiseMatcher<TupleMatcher, RawContainer>(
tuple_matcher, rhs);
}
-#if GTEST_HAS_STD_INITIALIZER_LIST_
// Supports the Pointwise(m, {a, b, c}) syntax.
template <typename TupleMatcher, typename T>
@@ -4202,14 +4087,13 @@ inline internal::PointwiseMatcher<TupleMatcher, std::vector<T> > Pointwise(
return Pointwise(tuple_matcher, std::vector<T>(rhs));
}
-#endif // GTEST_HAS_STD_INITIALIZER_LIST_
// UnorderedPointwise(pair_matcher, rhs) matches an STL-style
// container or a native array that contains the same number of
// elements as in rhs, where in some permutation of the container, its
// i-th element and rhs's i-th element (as a pair) satisfy the given
// pair matcher, for all i. Tuple2Matcher must be able to be safely
-// cast to Matcher<tuple<const T1&, const T2&> >, where T1 and T2 are
+// cast to Matcher<std::tuple<const T1&, const T2&> >, where T1 and T2 are
// the types of elements in the LHS container and the RHS container
// respectively.
//
@@ -4218,14 +4102,15 @@ inline internal::PointwiseMatcher<TupleMatcher, std::vector<T> > Pointwise(
template <typename Tuple2Matcher, typename RhsContainer>
inline internal::UnorderedElementsAreArrayMatcher<
typename internal::BoundSecondMatcher<
- Tuple2Matcher, typename internal::StlContainerView<GTEST_REMOVE_CONST_(
- RhsContainer)>::type::value_type> >
+ Tuple2Matcher,
+ typename internal::StlContainerView<
+ typename std::remove_const<RhsContainer>::type>::type::value_type>>
UnorderedPointwise(const Tuple2Matcher& tuple2_matcher,
const RhsContainer& rhs_container) {
// This following line is for working around a bug in MSVC 8.0,
// which causes RhsContainer to be a const type sometimes (e.g. when
// rhs_container is a const int[]).
- typedef GTEST_REMOVE_CONST_(RhsContainer) RawRhsContainer;
+ typedef typename std::remove_const<RhsContainer>::type RawRhsContainer;
// RhsView allows the same code to handle RhsContainer being a
// STL-style container and it being a native C-style array.
@@ -4247,7 +4132,6 @@ UnorderedPointwise(const Tuple2Matcher& tuple2_matcher,
return UnorderedElementsAreArray(matchers);
}
-#if GTEST_HAS_STD_INITIALIZER_LIST_
// Supports the UnorderedPointwise(m, {a, b, c}) syntax.
template <typename Tuple2Matcher, typename T>
@@ -4258,7 +4142,6 @@ UnorderedPointwise(const Tuple2Matcher& tuple2_matcher,
return UnorderedPointwise(tuple2_matcher, std::vector<T>(rhs));
}
-#endif // GTEST_HAS_STD_INITIALIZER_LIST_
// Matches an STL-style container or a native array that contains at
// least one element matching the given value or matcher.
@@ -4283,6 +4166,124 @@ inline internal::ContainsMatcher<M> Contains(M matcher) {
return internal::ContainsMatcher<M>(matcher);
}
+// IsSupersetOf(iterator_first, iterator_last)
+// IsSupersetOf(pointer, count)
+// IsSupersetOf(array)
+// IsSupersetOf(container)
+// IsSupersetOf({e1, e2, ..., en})
+//
+// IsSupersetOf() verifies that a surjective partial mapping onto a collection
+// of matchers exists. In other words, a container matches
+// IsSupersetOf({e1, ..., en}) if and only if there is a permutation
+// {y1, ..., yn} of some of the container's elements where y1 matches e1,
+// ..., and yn matches en. Obviously, the size of the container must be >= n
+// in order to have a match. Examples:
+//
+// - {1, 2, 3} matches IsSupersetOf({Ge(3), Ne(0)}), as 3 matches Ge(3) and
+// 1 matches Ne(0).
+// - {1, 2} doesn't match IsSupersetOf({Eq(1), Lt(2)}), even though 1 matches
+// both Eq(1) and Lt(2). The reason is that different matchers must be used
+// for elements in different slots of the container.
+// - {1, 1, 2} matches IsSupersetOf({Eq(1), Lt(2)}), as (the first) 1 matches
+// Eq(1) and (the second) 1 matches Lt(2).
+// - {1, 2, 3} matches IsSupersetOf(Gt(1), Gt(1)), as 2 matches (the first)
+// Gt(1) and 3 matches (the second) Gt(1).
+//
+// The matchers can be specified as an array, a pointer and count, a container,
+// an initializer list, or an STL iterator range. In each of these cases, the
+// underlying matchers can be either values or matchers.
+
+template <typename Iter>
+inline internal::UnorderedElementsAreArrayMatcher<
+ typename ::std::iterator_traits<Iter>::value_type>
+IsSupersetOf(Iter first, Iter last) {
+ typedef typename ::std::iterator_traits<Iter>::value_type T;
+ return internal::UnorderedElementsAreArrayMatcher<T>(
+ internal::UnorderedMatcherRequire::Superset, first, last);
+}
+
+template <typename T>
+inline internal::UnorderedElementsAreArrayMatcher<T> IsSupersetOf(
+ const T* pointer, size_t count) {
+ return IsSupersetOf(pointer, pointer + count);
+}
+
+template <typename T, size_t N>
+inline internal::UnorderedElementsAreArrayMatcher<T> IsSupersetOf(
+ const T (&array)[N]) {
+ return IsSupersetOf(array, N);
+}
+
+template <typename Container>
+inline internal::UnorderedElementsAreArrayMatcher<
+ typename Container::value_type>
+IsSupersetOf(const Container& container) {
+ return IsSupersetOf(container.begin(), container.end());
+}
+
+template <typename T>
+inline internal::UnorderedElementsAreArrayMatcher<T> IsSupersetOf(
+ ::std::initializer_list<T> xs) {
+ return IsSupersetOf(xs.begin(), xs.end());
+}
+
+// IsSubsetOf(iterator_first, iterator_last)
+// IsSubsetOf(pointer, count)
+// IsSubsetOf(array)
+// IsSubsetOf(container)
+// IsSubsetOf({e1, e2, ..., en})
+//
+// IsSubsetOf() verifies that an injective mapping onto a collection of matchers
+// exists. In other words, a container matches IsSubsetOf({e1, ..., en}) if and
+// only if there is a subset of matchers {m1, ..., mk} which would match the
+// container using UnorderedElementsAre. Obviously, the size of the container
+// must be <= n in order to have a match. Examples:
+//
+// - {1} matches IsSubsetOf({Gt(0), Lt(0)}), as 1 matches Gt(0).
+// - {1, -1} matches IsSubsetOf({Lt(0), Gt(0)}), as 1 matches Gt(0) and -1
+// matches Lt(0).
+// - {1, 2} doesn't matches IsSubsetOf({Gt(0), Lt(0)}), even though 1 and 2 both
+// match Gt(0). The reason is that different matchers must be used for
+// elements in different slots of the container.
+//
+// The matchers can be specified as an array, a pointer and count, a container,
+// an initializer list, or an STL iterator range. In each of these cases, the
+// underlying matchers can be either values or matchers.
+
+template <typename Iter>
+inline internal::UnorderedElementsAreArrayMatcher<
+ typename ::std::iterator_traits<Iter>::value_type>
+IsSubsetOf(Iter first, Iter last) {
+ typedef typename ::std::iterator_traits<Iter>::value_type T;
+ return internal::UnorderedElementsAreArrayMatcher<T>(
+ internal::UnorderedMatcherRequire::Subset, first, last);
+}
+
+template <typename T>
+inline internal::UnorderedElementsAreArrayMatcher<T> IsSubsetOf(
+ const T* pointer, size_t count) {
+ return IsSubsetOf(pointer, pointer + count);
+}
+
+template <typename T, size_t N>
+inline internal::UnorderedElementsAreArrayMatcher<T> IsSubsetOf(
+ const T (&array)[N]) {
+ return IsSubsetOf(array, N);
+}
+
+template <typename Container>
+inline internal::UnorderedElementsAreArrayMatcher<
+ typename Container::value_type>
+IsSubsetOf(const Container& container) {
+ return IsSubsetOf(container.begin(), container.end());
+}
+
+template <typename T>
+inline internal::UnorderedElementsAreArrayMatcher<T> IsSubsetOf(
+ ::std::initializer_list<T> xs) {
+ return IsSubsetOf(xs.begin(), xs.end());
+}
+
// Matches an STL-style container or a native array that contains only
// elements matching the given value or matcher.
//
@@ -4342,7 +4343,7 @@ inline internal::MatcherAsPredicate<M> Matches(M matcher) {
return internal::MatcherAsPredicate<M>(matcher);
}
-// Returns true iff the value matches the matcher.
+// Returns true if and only if the value matches the matcher.
template <typename T, typename M>
inline bool Value(const T& value, M matcher) {
return testing::Matches(matcher)(value);
@@ -4356,20 +4357,152 @@ inline bool ExplainMatchResult(
return SafeMatcherCast<const T&>(matcher).MatchAndExplain(value, listener);
}
-#if GTEST_LANG_CXX11
-// Define variadic matcher versions. They are overloaded in
-// gmock-generated-matchers.h for the cases supported by pre C++11 compilers.
+// Returns a string representation of the given matcher. Useful for description
+// strings of matchers defined using MATCHER_P* macros that accept matchers as
+// their arguments. For example:
+//
+// MATCHER_P(XAndYThat, matcher,
+// "X that " + DescribeMatcher<int>(matcher, negation) +
+// " and Y that " + DescribeMatcher<double>(matcher, negation)) {
+// return ExplainMatchResult(matcher, arg.x(), result_listener) &&
+// ExplainMatchResult(matcher, arg.y(), result_listener);
+// }
+template <typename T, typename M>
+std::string DescribeMatcher(const M& matcher, bool negation = false) {
+ ::std::stringstream ss;
+ Matcher<T> monomorphic_matcher = SafeMatcherCast<T>(matcher);
+ if (negation) {
+ monomorphic_matcher.DescribeNegationTo(&ss);
+ } else {
+ monomorphic_matcher.DescribeTo(&ss);
+ }
+ return ss.str();
+}
+
+template <typename... Args>
+internal::ElementsAreMatcher<
+ std::tuple<typename std::decay<const Args&>::type...>>
+ElementsAre(const Args&... matchers) {
+ return internal::ElementsAreMatcher<
+ std::tuple<typename std::decay<const Args&>::type...>>(
+ std::make_tuple(matchers...));
+}
+
template <typename... Args>
-inline internal::AllOfMatcher<Args...> AllOf(const Args&... matchers) {
- return internal::AllOfMatcher<Args...>(matchers...);
+internal::UnorderedElementsAreMatcher<
+ std::tuple<typename std::decay<const Args&>::type...>>
+UnorderedElementsAre(const Args&... matchers) {
+ return internal::UnorderedElementsAreMatcher<
+ std::tuple<typename std::decay<const Args&>::type...>>(
+ std::make_tuple(matchers...));
}
+// Define variadic matcher versions.
template <typename... Args>
-inline internal::AnyOfMatcher<Args...> AnyOf(const Args&... matchers) {
- return internal::AnyOfMatcher<Args...>(matchers...);
+internal::AllOfMatcher<typename std::decay<const Args&>::type...> AllOf(
+ const Args&... matchers) {
+ return internal::AllOfMatcher<typename std::decay<const Args&>::type...>(
+ matchers...);
}
-#endif // GTEST_LANG_CXX11
+template <typename... Args>
+internal::AnyOfMatcher<typename std::decay<const Args&>::type...> AnyOf(
+ const Args&... matchers) {
+ return internal::AnyOfMatcher<typename std::decay<const Args&>::type...>(
+ matchers...);
+}
+
+// AnyOfArray(array)
+// AnyOfArray(pointer, count)
+// AnyOfArray(container)
+// AnyOfArray({ e1, e2, ..., en })
+// AnyOfArray(iterator_first, iterator_last)
+//
+// AnyOfArray() verifies whether a given value matches any member of a
+// collection of matchers.
+//
+// AllOfArray(array)
+// AllOfArray(pointer, count)
+// AllOfArray(container)
+// AllOfArray({ e1, e2, ..., en })
+// AllOfArray(iterator_first, iterator_last)
+//
+// AllOfArray() verifies whether a given value matches all members of a
+// collection of matchers.
+//
+// The matchers can be specified as an array, a pointer and count, a container,
+// an initializer list, or an STL iterator range. In each of these cases, the
+// underlying matchers can be either values or matchers.
+
+template <typename Iter>
+inline internal::AnyOfArrayMatcher<
+ typename ::std::iterator_traits<Iter>::value_type>
+AnyOfArray(Iter first, Iter last) {
+ return internal::AnyOfArrayMatcher<
+ typename ::std::iterator_traits<Iter>::value_type>(first, last);
+}
+
+template <typename Iter>
+inline internal::AllOfArrayMatcher<
+ typename ::std::iterator_traits<Iter>::value_type>
+AllOfArray(Iter first, Iter last) {
+ return internal::AllOfArrayMatcher<
+ typename ::std::iterator_traits<Iter>::value_type>(first, last);
+}
+
+template <typename T>
+inline internal::AnyOfArrayMatcher<T> AnyOfArray(const T* ptr, size_t count) {
+ return AnyOfArray(ptr, ptr + count);
+}
+
+template <typename T>
+inline internal::AllOfArrayMatcher<T> AllOfArray(const T* ptr, size_t count) {
+ return AllOfArray(ptr, ptr + count);
+}
+
+template <typename T, size_t N>
+inline internal::AnyOfArrayMatcher<T> AnyOfArray(const T (&array)[N]) {
+ return AnyOfArray(array, N);
+}
+
+template <typename T, size_t N>
+inline internal::AllOfArrayMatcher<T> AllOfArray(const T (&array)[N]) {
+ return AllOfArray(array, N);
+}
+
+template <typename Container>
+inline internal::AnyOfArrayMatcher<typename Container::value_type> AnyOfArray(
+ const Container& container) {
+ return AnyOfArray(container.begin(), container.end());
+}
+
+template <typename Container>
+inline internal::AllOfArrayMatcher<typename Container::value_type> AllOfArray(
+ const Container& container) {
+ return AllOfArray(container.begin(), container.end());
+}
+
+template <typename T>
+inline internal::AnyOfArrayMatcher<T> AnyOfArray(
+ ::std::initializer_list<T> xs) {
+ return AnyOfArray(xs.begin(), xs.end());
+}
+
+template <typename T>
+inline internal::AllOfArrayMatcher<T> AllOfArray(
+ ::std::initializer_list<T> xs) {
+ return AllOfArray(xs.begin(), xs.end());
+}
+
+// Args<N1, N2, ..., Nk>(a_matcher) matches a tuple if the selected
+// fields of it matches a_matcher. C++ doesn't support default
+// arguments for function templates, so we have to overload it.
+template <size_t... k, typename InnerMatcher>
+internal::ArgsMatcher<typename std::decay<InnerMatcher>::type, k...> Args(
+ InnerMatcher&& matcher) {
+ return internal::ArgsMatcher<typename std::decay<InnerMatcher>::type, k...>(
+ std::forward<InnerMatcher>(matcher));
+}
// AllArgs(m) is a synonym of m. This is useful in
//
@@ -4381,10 +4514,43 @@ inline internal::AnyOfMatcher<Args...> AnyOf(const Args&... matchers) {
template <typename InnerMatcher>
inline InnerMatcher AllArgs(const InnerMatcher& matcher) { return matcher; }
+// Returns a matcher that matches the value of an optional<> type variable.
+// The matcher implementation only uses '!arg' and requires that the optional<>
+// type has a 'value_type' member type and that '*arg' is of type 'value_type'
+// and is printable using 'PrintToString'. It is compatible with
+// std::optional/std::experimental::optional.
+// Note that to compare an optional type variable against nullopt you should
+// use Eq(nullopt) and not Optional(Eq(nullopt)). The latter implies that the
+// optional value contains an optional itself.
+template <typename ValueMatcher>
+inline internal::OptionalMatcher<ValueMatcher> Optional(
+ const ValueMatcher& value_matcher) {
+ return internal::OptionalMatcher<ValueMatcher>(value_matcher);
+}
+
+// Returns a matcher that matches the value of a absl::any type variable.
+template <typename T>
+PolymorphicMatcher<internal::any_cast_matcher::AnyCastMatcher<T> > AnyWith(
+ const Matcher<const T&>& matcher) {
+ return MakePolymorphicMatcher(
+ internal::any_cast_matcher::AnyCastMatcher<T>(matcher));
+}
+
+// Returns a matcher that matches the value of a variant<> type variable.
+// The matcher implementation uses ADL to find the holds_alternative and get
+// functions.
+// It is compatible with std::variant.
+template <typename T>
+PolymorphicMatcher<internal::variant_matcher::VariantMatcher<T> > VariantWith(
+ const Matcher<const T&>& matcher) {
+ return MakePolymorphicMatcher(
+ internal::variant_matcher::VariantMatcher<T>(matcher));
+}
+
// These macros allow using matchers to check values in Google Test
// tests. ASSERT_THAT(value, matcher) and EXPECT_THAT(value, matcher)
-// succeed iff the value matches the matcher. If the assertion fails,
-// the value and the description of the matcher will be printed.
+// succeed if and only if the value matches the matcher. If the assertion
+// fails, the value and the description of the matcher will be printed.
#define ASSERT_THAT(value, matcher) ASSERT_PRED_FORMAT1(\
::testing::internal::MakePredicateFormatterFromMatcher(matcher), value)
#define EXPECT_THAT(value, matcher) EXPECT_PRED_FORMAT1(\
@@ -4392,8 +4558,11 @@ inline InnerMatcher AllArgs(const InnerMatcher& matcher) { return matcher; }
} // namespace testing
+GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 5046
+
// Include any custom callback matchers added by the local installation.
// We must include this header at the end to make sure it can use the
// declarations from this file.
#include "gmock/internal/custom/gmock-matchers.h"
+
#endif // GMOCK_INCLUDE_GMOCK_GMOCK_MATCHERS_H_
diff --git a/extern/gmock/include/gmock/gmock-more-actions.h b/extern/gmock/include/gmock/gmock-more-actions.h
index 3d387b6b7d7..d42484aef2b 100644
--- a/extern/gmock/include/gmock/gmock-more-actions.h
+++ b/extern/gmock/include/gmock/gmock-more-actions.h
@@ -26,70 +26,25 @@
// 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: wan@google.com (Zhanyong Wan)
+
// Google Mock - a framework for writing C++ mock classes.
//
// This file implements some actions that depend on gmock-generated-actions.h.
+// GOOGLETEST_CM0002 DO NOT DELETE
+
#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_MORE_ACTIONS_H_
#define GMOCK_INCLUDE_GMOCK_GMOCK_MORE_ACTIONS_H_
#include <algorithm>
+#include <type_traits>
#include "gmock/gmock-generated-actions.h"
namespace testing {
namespace internal {
-// Implements the Invoke(f) action. The template argument
-// FunctionImpl is the implementation type of f, which can be either a
-// function pointer or a functor. Invoke(f) can be used as an
-// Action<F> as long as f's type is compatible with F (i.e. f can be
-// assigned to a tr1::function<F>).
-template <typename FunctionImpl>
-class InvokeAction {
- public:
- // The c'tor makes a copy of function_impl (either a function
- // pointer or a functor).
- explicit InvokeAction(FunctionImpl function_impl)
- : function_impl_(function_impl) {}
-
- template <typename Result, typename ArgumentTuple>
- Result Perform(const ArgumentTuple& args) {
- return InvokeHelper<Result, ArgumentTuple>::Invoke(function_impl_, args);
- }
-
- private:
- FunctionImpl function_impl_;
-
- GTEST_DISALLOW_ASSIGN_(InvokeAction);
-};
-
-// Implements the Invoke(object_ptr, &Class::Method) action.
-template <class Class, typename MethodPtr>
-class InvokeMethodAction {
- public:
- InvokeMethodAction(Class* obj_ptr, MethodPtr method_ptr)
- : method_ptr_(method_ptr), obj_ptr_(obj_ptr) {}
-
- template <typename Result, typename ArgumentTuple>
- Result Perform(const ArgumentTuple& args) const {
- return InvokeHelper<Result, ArgumentTuple>::InvokeMethod(
- obj_ptr_, method_ptr_, args);
- }
-
- private:
- // The order of these members matters. Reversing the order can trigger
- // warning C4121 in MSVC (see
- // http://computer-programming-forum.com/7-vc.net/6fbc30265f860ad1.htm ).
- const MethodPtr method_ptr_;
- Class* const obj_ptr_;
-
- GTEST_DISALLOW_ASSIGN_(InvokeMethodAction);
-};
-
// An internal replacement for std::copy which mimics its behavior. This is
// necessary because Visual Studio deprecates ::std::copy, issuing warning 4996.
// However Visual Studio 2010 and later do not honor #pragmas which disable that
@@ -108,45 +63,6 @@ inline OutputIterator CopyElements(InputIterator first,
// Various overloads for Invoke().
-// Creates an action that invokes 'function_impl' with the mock
-// function's arguments.
-template <typename FunctionImpl>
-PolymorphicAction<internal::InvokeAction<FunctionImpl> > Invoke(
- FunctionImpl function_impl) {
- return MakePolymorphicAction(
- internal::InvokeAction<FunctionImpl>(function_impl));
-}
-
-// Creates an action that invokes the given method on the given object
-// with the mock function's arguments.
-template <class Class, typename MethodPtr>
-PolymorphicAction<internal::InvokeMethodAction<Class, MethodPtr> > Invoke(
- Class* obj_ptr, MethodPtr method_ptr) {
- return MakePolymorphicAction(
- internal::InvokeMethodAction<Class, MethodPtr>(obj_ptr, method_ptr));
-}
-
-// WithoutArgs(inner_action) can be used in a mock function with a
-// non-empty argument list to perform inner_action, which takes no
-// argument. In other words, it adapts an action accepting no
-// argument to one that accepts (and ignores) arguments.
-template <typename InnerAction>
-inline internal::WithArgsAction<InnerAction>
-WithoutArgs(const InnerAction& action) {
- return internal::WithArgsAction<InnerAction>(action);
-}
-
-// WithArg<k>(an_action) creates an action that passes the k-th
-// (0-based) argument of the mock function to an_action and performs
-// it. It adapts an action accepting one argument to one that accepts
-// multiple arguments. For convenience, we also provide
-// WithArgs<k>(an_action) (defined below) as a synonym.
-template <int k, typename InnerAction>
-inline internal::WithArgsAction<InnerAction, k>
-WithArg(const InnerAction& action) {
- return internal::WithArgsAction<InnerAction, k>(action);
-}
-
// The ACTION*() macros trigger warning C4100 (unreferenced formal
// parameter) in MSVC with -W4. Unfortunately they cannot be fixed in
// the macro definition, as the warnings are generated when the macro
@@ -161,7 +77,7 @@ WithArg(const InnerAction& action) {
ACTION_TEMPLATE(ReturnArg,
HAS_1_TEMPLATE_PARAMS(int, k),
AND_0_VALUE_PARAMS()) {
- return ::testing::get<k>(args);
+ return ::std::get<k>(args);
}
// Action SaveArg<k>(pointer) saves the k-th (0-based) argument of the
@@ -169,7 +85,7 @@ ACTION_TEMPLATE(ReturnArg,
ACTION_TEMPLATE(SaveArg,
HAS_1_TEMPLATE_PARAMS(int, k),
AND_1_VALUE_PARAMS(pointer)) {
- *pointer = ::testing::get<k>(args);
+ *pointer = ::std::get<k>(args);
}
// Action SaveArgPointee<k>(pointer) saves the value pointed to
@@ -177,7 +93,7 @@ ACTION_TEMPLATE(SaveArg,
ACTION_TEMPLATE(SaveArgPointee,
HAS_1_TEMPLATE_PARAMS(int, k),
AND_1_VALUE_PARAMS(pointer)) {
- *pointer = *::testing::get<k>(args);
+ *pointer = *::std::get<k>(args);
}
// Action SetArgReferee<k>(value) assigns 'value' to the variable
@@ -185,13 +101,13 @@ ACTION_TEMPLATE(SaveArgPointee,
ACTION_TEMPLATE(SetArgReferee,
HAS_1_TEMPLATE_PARAMS(int, k),
AND_1_VALUE_PARAMS(value)) {
- typedef typename ::testing::tuple_element<k, args_type>::type argk_type;
+ typedef typename ::std::tuple_element<k, args_type>::type argk_type;
// Ensures that argument #k is a reference. If you get a compiler
// error on the next line, you are using SetArgReferee<k>(value) in
// a mock function whose k-th (0-based) argument is not a reference.
- GTEST_COMPILE_ASSERT_(internal::is_reference<argk_type>::value,
+ GTEST_COMPILE_ASSERT_(std::is_reference<argk_type>::value,
SetArgReferee_must_be_used_with_a_reference_argument);
- ::testing::get<k>(args) = value;
+ ::std::get<k>(args) = value;
}
// Action SetArrayArgument<k>(first, last) copies the elements in
@@ -204,9 +120,9 @@ ACTION_TEMPLATE(SetArrayArgument,
AND_2_VALUE_PARAMS(first, last)) {
// Visual Studio deprecates ::std::copy, so we use our own copy in that case.
#ifdef _MSC_VER
- internal::CopyElements(first, last, ::testing::get<k>(args));
+ internal::CopyElements(first, last, ::std::get<k>(args));
#else
- ::std::copy(first, last, ::testing::get<k>(args));
+ ::std::copy(first, last, ::std::get<k>(args));
#endif
}
@@ -215,7 +131,7 @@ ACTION_TEMPLATE(SetArrayArgument,
ACTION_TEMPLATE(DeleteArg,
HAS_1_TEMPLATE_PARAMS(int, k),
AND_0_VALUE_PARAMS()) {
- delete ::testing::get<k>(args);
+ delete ::std::get<k>(args);
}
// This action returns the value pointed to by 'pointer'.
diff --git a/extern/gmock/include/gmock/gmock-more-matchers.h b/extern/gmock/include/gmock/gmock-more-matchers.h
index 3db899f4297..1c9a399a341 100644
--- a/extern/gmock/include/gmock/gmock-more-matchers.h
+++ b/extern/gmock/include/gmock/gmock-more-matchers.h
@@ -26,8 +26,7 @@
// 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: marcus.boerger@google.com (Marcus Boerger)
+
// Google Mock - a framework for writing C++ mock classes.
//
@@ -36,13 +35,27 @@
// Note that tests are implemented in gmock-matchers_test.cc rather than
// gmock-more-matchers-test.cc.
-#ifndef GMOCK_GMOCK_MORE_MATCHERS_H_
-#define GMOCK_GMOCK_MORE_MATCHERS_H_
+// GOOGLETEST_CM0002 DO NOT DELETE
+
+#ifndef GMOCK_INCLUDE_GMOCK_MORE_MATCHERS_H_
+#define GMOCK_INCLUDE_GMOCK_MORE_MATCHERS_H_
#include "gmock/gmock-generated-matchers.h"
namespace testing {
+// Silence C4100 (unreferenced formal
+// parameter) for MSVC
+#ifdef _MSC_VER
+# pragma warning(push)
+# pragma warning(disable:4100)
+#if (_MSC_VER == 1900)
+// and silence C4800 (C4800: 'int *const ': forcing value
+// to bool 'true' or 'false') for MSVC 14
+# pragma warning(disable:4800)
+ #endif
+#endif
+
// Defines a matcher that matches an empty container. The container must
// support both size() and empty(), which all STL-like containers provide.
MATCHER(IsEmpty, negation ? "isn't empty" : "is empty") {
@@ -53,6 +66,27 @@ MATCHER(IsEmpty, negation ? "isn't empty" : "is empty") {
return false;
}
+// Define a matcher that matches a value that evaluates in boolean
+// context to true. Useful for types that define "explicit operator
+// bool" operators and so can't be compared for equality with true
+// and false.
+MATCHER(IsTrue, negation ? "is false" : "is true") {
+ return static_cast<bool>(arg);
+}
+
+// Define a matcher that matches a value that evaluates in boolean
+// context to false. Useful for types that define "explicit operator
+// bool" operators and so can't be compared for equality with true
+// and false.
+MATCHER(IsFalse, negation ? "is true" : "is false") {
+ return !static_cast<bool>(arg);
+}
+
+#ifdef _MSC_VER
+# pragma warning(pop)
+#endif
+
+
} // namespace testing
-#endif // GMOCK_GMOCK_MORE_MATCHERS_H_
+#endif // GMOCK_INCLUDE_GMOCK_MORE_MATCHERS_H_
diff --git a/extern/gmock/include/gmock/gmock-nice-strict.h b/extern/gmock/include/gmock/gmock-nice-strict.h
new file mode 100644
index 00000000000..5495a9805b5
--- /dev/null
+++ b/extern/gmock/include/gmock/gmock-nice-strict.h
@@ -0,0 +1,215 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// 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.
+
+
+// Implements class templates NiceMock, NaggyMock, and StrictMock.
+//
+// Given a mock class MockFoo that is created using Google Mock,
+// NiceMock<MockFoo> is a subclass of MockFoo that allows
+// uninteresting calls (i.e. calls to mock methods that have no
+// EXPECT_CALL specs), NaggyMock<MockFoo> is a subclass of MockFoo
+// that prints a warning when an uninteresting call occurs, and
+// StrictMock<MockFoo> is a subclass of MockFoo that treats all
+// uninteresting calls as errors.
+//
+// Currently a mock is naggy by default, so MockFoo and
+// NaggyMock<MockFoo> behave like the same. However, we will soon
+// switch the default behavior of mocks to be nice, as that in general
+// leads to more maintainable tests. When that happens, MockFoo will
+// stop behaving like NaggyMock<MockFoo> and start behaving like
+// NiceMock<MockFoo>.
+//
+// NiceMock, NaggyMock, and StrictMock "inherit" the constructors of
+// their respective base class. Therefore you can write
+// NiceMock<MockFoo>(5, "a") to construct a nice mock where MockFoo
+// has a constructor that accepts (int, const char*), for example.
+//
+// A known limitation is that NiceMock<MockFoo>, NaggyMock<MockFoo>,
+// and StrictMock<MockFoo> only works for mock methods defined using
+// the MOCK_METHOD* family of macros DIRECTLY in the MockFoo class.
+// If a mock method is defined in a base class of MockFoo, the "nice"
+// or "strict" modifier may not affect it, depending on the compiler.
+// In particular, nesting NiceMock, NaggyMock, and StrictMock is NOT
+// supported.
+
+// GOOGLETEST_CM0002 DO NOT DELETE
+
+#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_NICE_STRICT_H_
+#define GMOCK_INCLUDE_GMOCK_GMOCK_NICE_STRICT_H_
+
+#include "gmock/gmock-spec-builders.h"
+#include "gmock/internal/gmock-port.h"
+
+namespace testing {
+
+template <class MockClass>
+class NiceMock : public MockClass {
+ public:
+ NiceMock() : MockClass() {
+ ::testing::Mock::AllowUninterestingCalls(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+
+ // Ideally, we would inherit base class's constructors through a using
+ // declaration, which would preserve their visibility. However, many existing
+ // tests rely on the fact that current implementation reexports protected
+ // constructors as public. These tests would need to be cleaned up first.
+
+ // Single argument constructor is special-cased so that it can be
+ // made explicit.
+ template <typename A>
+ explicit NiceMock(A&& arg) : MockClass(std::forward<A>(arg)) {
+ ::testing::Mock::AllowUninterestingCalls(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+
+ template <typename A1, typename A2, typename... An>
+ NiceMock(A1&& arg1, A2&& arg2, An&&... args)
+ : MockClass(std::forward<A1>(arg1), std::forward<A2>(arg2),
+ std::forward<An>(args)...) {
+ ::testing::Mock::AllowUninterestingCalls(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+
+ ~NiceMock() { // NOLINT
+ ::testing::Mock::UnregisterCallReaction(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+
+ private:
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(NiceMock);
+};
+
+template <class MockClass>
+class NaggyMock : public MockClass {
+ public:
+ NaggyMock() : MockClass() {
+ ::testing::Mock::WarnUninterestingCalls(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+
+ // Ideally, we would inherit base class's constructors through a using
+ // declaration, which would preserve their visibility. However, many existing
+ // tests rely on the fact that current implementation reexports protected
+ // constructors as public. These tests would need to be cleaned up first.
+
+ // Single argument constructor is special-cased so that it can be
+ // made explicit.
+ template <typename A>
+ explicit NaggyMock(A&& arg) : MockClass(std::forward<A>(arg)) {
+ ::testing::Mock::WarnUninterestingCalls(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+
+ template <typename A1, typename A2, typename... An>
+ NaggyMock(A1&& arg1, A2&& arg2, An&&... args)
+ : MockClass(std::forward<A1>(arg1), std::forward<A2>(arg2),
+ std::forward<An>(args)...) {
+ ::testing::Mock::WarnUninterestingCalls(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+
+ ~NaggyMock() { // NOLINT
+ ::testing::Mock::UnregisterCallReaction(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+
+ private:
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(NaggyMock);
+};
+
+template <class MockClass>
+class StrictMock : public MockClass {
+ public:
+ StrictMock() : MockClass() {
+ ::testing::Mock::FailUninterestingCalls(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+
+ // Ideally, we would inherit base class's constructors through a using
+ // declaration, which would preserve their visibility. However, many existing
+ // tests rely on the fact that current implementation reexports protected
+ // constructors as public. These tests would need to be cleaned up first.
+
+ // Single argument constructor is special-cased so that it can be
+ // made explicit.
+ template <typename A>
+ explicit StrictMock(A&& arg) : MockClass(std::forward<A>(arg)) {
+ ::testing::Mock::FailUninterestingCalls(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+
+ template <typename A1, typename A2, typename... An>
+ StrictMock(A1&& arg1, A2&& arg2, An&&... args)
+ : MockClass(std::forward<A1>(arg1), std::forward<A2>(arg2),
+ std::forward<An>(args)...) {
+ ::testing::Mock::FailUninterestingCalls(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+
+ ~StrictMock() { // NOLINT
+ ::testing::Mock::UnregisterCallReaction(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+
+ private:
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(StrictMock);
+};
+
+// The following specializations catch some (relatively more common)
+// user errors of nesting nice and strict mocks. They do NOT catch
+// all possible errors.
+
+// These specializations are declared but not defined, as NiceMock,
+// NaggyMock, and StrictMock cannot be nested.
+
+template <typename MockClass>
+class NiceMock<NiceMock<MockClass> >;
+template <typename MockClass>
+class NiceMock<NaggyMock<MockClass> >;
+template <typename MockClass>
+class NiceMock<StrictMock<MockClass> >;
+
+template <typename MockClass>
+class NaggyMock<NiceMock<MockClass> >;
+template <typename MockClass>
+class NaggyMock<NaggyMock<MockClass> >;
+template <typename MockClass>
+class NaggyMock<StrictMock<MockClass> >;
+
+template <typename MockClass>
+class StrictMock<NiceMock<MockClass> >;
+template <typename MockClass>
+class StrictMock<NaggyMock<MockClass> >;
+template <typename MockClass>
+class StrictMock<StrictMock<MockClass> >;
+
+} // namespace testing
+
+#endif // GMOCK_INCLUDE_GMOCK_GMOCK_NICE_STRICT_H_
diff --git a/extern/gmock/include/gmock/gmock-spec-builders.h b/extern/gmock/include/gmock/gmock-spec-builders.h
index fed7de66bc4..80c13b55492 100644
--- a/extern/gmock/include/gmock/gmock-spec-builders.h
+++ b/extern/gmock/include/gmock/gmock-spec-builders.h
@@ -26,8 +26,7 @@
// 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: wan@google.com (Zhanyong Wan)
+
// Google Mock - a framework for writing C++ mock classes.
//
@@ -57,19 +56,20 @@
// where all clauses are optional, and .InSequence()/.After()/
// .WillOnce() can appear any number of times.
+// GOOGLETEST_CM0002 DO NOT DELETE
+
#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_SPEC_BUILDERS_H_
#define GMOCK_INCLUDE_GMOCK_GMOCK_SPEC_BUILDERS_H_
+#include <functional>
#include <map>
+#include <memory>
#include <set>
#include <sstream>
#include <string>
+#include <type_traits>
+#include <utility>
#include <vector>
-
-#if GTEST_HAS_EXCEPTIONS
-# include <stdexcept> // NOLINT
-#endif
-
#include "gmock/gmock-actions.h"
#include "gmock/gmock-cardinalities.h"
#include "gmock/gmock-matchers.h"
@@ -77,6 +77,13 @@
#include "gmock/internal/gmock-port.h"
#include "gtest/gtest.h"
+#if GTEST_HAS_EXCEPTIONS
+# include <stdexcept> // NOLINT
+#endif
+
+GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
+/* class A needs to have dll-interface to be used by clients of class B */)
+
namespace testing {
// An abstract handle of an expectation.
@@ -101,9 +108,6 @@ template <typename F> class TypedExpectation;
// Helper class for testing the Expectation class template.
class ExpectationTester;
-// Base class for function mockers.
-template <typename F> class FunctionMockerBase;
-
// Protects the mock object registry (in class Mock), all function
// mockers, and all expectations.
//
@@ -120,9 +124,9 @@ GTEST_API_ GTEST_DECLARE_STATIC_MUTEX_(g_gmock_mutex);
// Untyped base class for ActionResultHolder<R>.
class UntypedActionResultHolderBase;
-// Abstract base class of FunctionMockerBase. This is the
+// Abstract base class of FunctionMocker. This is the
// type-agnostic part of the function mocker interface. Its pure
-// virtual methods are implemented by FunctionMockerBase.
+// virtual methods are implemented by FunctionMocker.
class GTEST_API_ UntypedFunctionMockerBase {
public:
UntypedFunctionMockerBase();
@@ -148,15 +152,13 @@ class GTEST_API_ UntypedFunctionMockerBase {
// action fails.
// L = *
virtual UntypedActionResultHolderBase* UntypedPerformDefaultAction(
- const void* untyped_args,
- const string& call_description) const = 0;
+ void* untyped_args, const std::string& call_description) const = 0;
// Performs the given action with the given arguments and returns
// the action's result.
// L = *
virtual UntypedActionResultHolderBase* UntypedPerformAction(
- const void* untyped_action,
- const void* untyped_args) const = 0;
+ const void* untyped_action, void* untyped_args) const = 0;
// Writes a message that the call is uninteresting (i.e. neither
// explicitly expected nor explicitly unexpected) to the given
@@ -186,7 +188,6 @@ class GTEST_API_ UntypedFunctionMockerBase {
// this information in the global mock registry. Will be called
// whenever an EXPECT_CALL() or ON_CALL() is executed on this mock
// method.
- // TODO(wan@google.com): rename to SetAndRegisterOwner().
void RegisterOwner(const void* mock_obj)
GTEST_LOCK_EXCLUDED_(g_gmock_mutex);
@@ -211,15 +212,13 @@ class GTEST_API_ UntypedFunctionMockerBase {
// arguments. This function can be safely called from multiple
// threads concurrently. The caller is responsible for deleting the
// result.
- UntypedActionResultHolderBase* UntypedInvokeWith(
- const void* untyped_args)
- GTEST_LOCK_EXCLUDED_(g_gmock_mutex);
+ UntypedActionResultHolderBase* UntypedInvokeWith(void* untyped_args)
+ GTEST_LOCK_EXCLUDED_(g_gmock_mutex);
protected:
typedef std::vector<const void*> UntypedOnCallSpecs;
- typedef std::vector<internal::linked_ptr<ExpectationBase> >
- UntypedExpectations;
+ using UntypedExpectations = std::vector<std::shared_ptr<ExpectationBase>>;
// Returns an Expectation object that references and co-owns exp,
// which must be an expectation on this mock function.
@@ -238,6 +237,14 @@ class GTEST_API_ UntypedFunctionMockerBase {
UntypedOnCallSpecs untyped_on_call_specs_;
// All expectations for this function mocker.
+ //
+ // It's undefined behavior to interleave expectations (EXPECT_CALLs
+ // or ON_CALLs) and mock function calls. Also, the order of
+ // expectations is important. Therefore it's a logic race condition
+ // to read/write untyped_expectations_ concurrently. In order for
+ // tools like tsan to catch concurrent read/write accesses to
+ // untyped_expectations, we deliberately leave accesses to it
+ // unprotected.
UntypedExpectations untyped_expectations_;
}; // class UntypedFunctionMockerBase
@@ -263,12 +270,14 @@ class UntypedOnCallSpecBase {
};
// Asserts that the ON_CALL() statement has a certain property.
- void AssertSpecProperty(bool property, const string& failure_message) const {
+ void AssertSpecProperty(bool property,
+ const std::string& failure_message) const {
Assert(property, file_, line_, failure_message);
}
// Expects that the ON_CALL() statement has a certain property.
- void ExpectSpecProperty(bool property, const string& failure_message) const {
+ void ExpectSpecProperty(bool property,
+ const std::string& failure_message) const {
Expect(property, file_, line_, failure_message);
}
@@ -294,11 +303,9 @@ class OnCallSpec : public UntypedOnCallSpecBase {
: UntypedOnCallSpecBase(a_file, a_line),
matchers_(matchers),
// By default, extra_matcher_ should match anything. However,
- // we cannot initialize it with _ as that triggers a compiler
- // bug in Symbian's C++ compiler (cannot decide between two
- // overloaded constructors of Matcher<const ArgumentTuple&>).
- extra_matcher_(A<const ArgumentTuple&>()) {
- }
+ // we cannot initialize it with _ as that causes ambiguity between
+ // Matcher's copy and move constructor for some argument types.
+ extra_matcher_(A<const ArgumentTuple&>()) {}
// Implements the .With() clause.
OnCallSpec& With(const Matcher<const ArgumentTuple&>& m) {
@@ -325,7 +332,7 @@ class OnCallSpec : public UntypedOnCallSpecBase {
return *this;
}
- // Returns true iff the given arguments match the matchers.
+ // Returns true if and only if the given arguments match the matchers.
bool Matches(const ArgumentTuple& args) const {
return TupleMatches(matchers_, args) && extra_matcher_.Matches(args);
}
@@ -362,7 +369,6 @@ enum CallReaction {
kAllow,
kWarn,
kFail,
- kDefault = kWarn // By default, warn about uninteresting calls.
};
} // namespace internal
@@ -384,18 +390,28 @@ class GTEST_API_ Mock {
GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex);
// Verifies all expectations on the given mock object and clears its
- // default actions and expectations. Returns true iff the
+ // default actions and expectations. Returns true if and only if the
// verification was successful.
static bool VerifyAndClear(void* mock_obj)
GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex);
+ // Returns whether the mock was created as a naggy mock (default)
+ static bool IsNaggy(void* mock_obj)
+ GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex);
+ // Returns whether the mock was created as a nice mock
+ static bool IsNice(void* mock_obj)
+ GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex);
+ // Returns whether the mock was created as a strict mock
+ static bool IsStrict(void* mock_obj)
+ GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex);
+
private:
friend class internal::UntypedFunctionMockerBase;
// Needed for a function mocker to register itself (so that we know
// how to clear a mock object).
template <typename F>
- friend class internal::FunctionMockerBase;
+ friend class internal::FunctionMocker;
template <typename M>
friend class NiceMock;
@@ -458,7 +474,7 @@ class GTEST_API_ Mock {
// Unregisters a mock method; removes the owning mock object from
// the registry when the last mock method associated with it has
// been unregistered. This is called only in the destructor of
- // FunctionMockerBase.
+ // FunctionMocker.
static void UnregisterLocked(internal::UntypedFunctionMockerBase* mocker)
GTEST_EXCLUSIVE_LOCK_REQUIRED_(internal::g_gmock_mutex);
}; // class Mock
@@ -478,12 +494,7 @@ class GTEST_API_ Mock {
// - Constness is shallow: a const Expectation object itself cannot
// be modified, but the mutable methods of the ExpectationBase
// object it references can be called via expectation_base().
-// - The constructors and destructor are defined out-of-line because
-// the Symbian WINSCW compiler wants to otherwise instantiate them
-// when it sees this class definition, at which point it doesn't have
-// ExpectationBase available yet, leading to incorrect destruction
-// in the linked_ptr (or compilation errors if using a checking
-// linked_ptr).
+
class GTEST_API_ Expectation {
public:
// Constructs a null object that doesn't reference any expectation.
@@ -505,7 +516,8 @@ class GTEST_API_ Expectation {
// The compiler-generated copy ctor and operator= work exactly as
// intended, so we don't need to define our own.
- // Returns true iff rhs references the same expectation as this object does.
+ // Returns true if and only if rhs references the same expectation as this
+ // object does.
bool operator==(const Expectation& rhs) const {
return expectation_base_ == rhs.expectation_base_;
}
@@ -519,7 +531,7 @@ class GTEST_API_ Expectation {
friend class ::testing::internal::UntypedFunctionMockerBase;
template <typename F>
- friend class ::testing::internal::FunctionMockerBase;
+ friend class ::testing::internal::FunctionMocker;
template <typename F>
friend class ::testing::internal::TypedExpectation;
@@ -535,16 +547,15 @@ class GTEST_API_ Expectation {
typedef ::std::set<Expectation, Less> Set;
Expectation(
- const internal::linked_ptr<internal::ExpectationBase>& expectation_base);
+ const std::shared_ptr<internal::ExpectationBase>& expectation_base);
// Returns the expectation this object references.
- const internal::linked_ptr<internal::ExpectationBase>&
- expectation_base() const {
+ const std::shared_ptr<internal::ExpectationBase>& expectation_base() const {
return expectation_base_;
}
- // A linked_ptr that co-owns the expectation this handle references.
- internal::linked_ptr<internal::ExpectationBase> expectation_base_;
+ // A shared_ptr that co-owns the expectation this handle references.
+ std::shared_ptr<internal::ExpectationBase> expectation_base_;
};
// A set of expectation handles. Useful in the .After() clause of
@@ -588,8 +599,8 @@ class ExpectationSet {
// The compiler-generator ctor and operator= works exactly as
// intended, so we don't need to define our own.
- // Returns true iff rhs contains the same set of Expectation objects
- // as this does.
+ // Returns true if and only if rhs contains the same set of Expectation
+ // objects as this does.
bool operator==(const ExpectationSet& rhs) const {
return expectations_ == rhs.expectations_;
}
@@ -626,11 +637,8 @@ class GTEST_API_ Sequence {
void AddExpectation(const Expectation& expectation) const;
private:
- // The last expectation in this sequence. We use a linked_ptr here
- // because Sequence objects are copyable and we want the copies to
- // be aliases. The linked_ptr allows the copies to co-own and share
- // the same Expectation object.
- internal::linked_ptr<Expectation> last_expectation_;
+ // The last expectation in this sequence.
+ std::shared_ptr<Expectation> last_expectation_;
}; // class Sequence
// An object of this type causes all EXPECT_CALL() statements
@@ -690,7 +698,7 @@ GTEST_API_ extern ThreadLocal<Sequence*> g_gmock_implicit_sequence;
class GTEST_API_ ExpectationBase {
public:
// source_text is the EXPECT_CALL(...) source that created this Expectation.
- ExpectationBase(const char* file, int line, const string& source_text);
+ ExpectationBase(const char* file, int line, const std::string& source_text);
virtual ~ExpectationBase();
@@ -738,12 +746,14 @@ class GTEST_API_ ExpectationBase {
virtual Expectation GetHandle() = 0;
// Asserts that the EXPECT_CALL() statement has the given property.
- void AssertSpecProperty(bool property, const string& failure_message) const {
+ void AssertSpecProperty(bool property,
+ const std::string& failure_message) const {
Assert(property, file_, line_, failure_message);
}
// Expects that the EXPECT_CALL() statement has the given property.
- void ExpectSpecProperty(bool property, const string& failure_message) const {
+ void ExpectSpecProperty(bool property,
+ const std::string& failure_message) const {
Expect(property, file_, line_, failure_message);
}
@@ -751,8 +761,8 @@ class GTEST_API_ ExpectationBase {
// by the subclasses to implement the .Times() clause.
void SpecifyCardinality(const Cardinality& cardinality);
- // Returns true iff the user specified the cardinality explicitly
- // using a .Times().
+ // Returns true if and only if the user specified the cardinality
+ // explicitly using a .Times().
bool cardinality_specified() const { return cardinality_specified_; }
// Sets the cardinality of this expectation spec.
@@ -768,7 +778,7 @@ class GTEST_API_ ExpectationBase {
void RetireAllPreRequisites()
GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex);
- // Returns true iff this expectation is retired.
+ // Returns true if and only if this expectation is retired.
bool is_retired() const
GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
g_gmock_mutex.AssertHeld();
@@ -782,28 +792,29 @@ class GTEST_API_ ExpectationBase {
retired_ = true;
}
- // Returns true iff this expectation is satisfied.
+ // Returns true if and only if this expectation is satisfied.
bool IsSatisfied() const
GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
g_gmock_mutex.AssertHeld();
return cardinality().IsSatisfiedByCallCount(call_count_);
}
- // Returns true iff this expectation is saturated.
+ // Returns true if and only if this expectation is saturated.
bool IsSaturated() const
GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
g_gmock_mutex.AssertHeld();
return cardinality().IsSaturatedByCallCount(call_count_);
}
- // Returns true iff this expectation is over-saturated.
+ // Returns true if and only if this expectation is over-saturated.
bool IsOverSaturated() const
GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
g_gmock_mutex.AssertHeld();
return cardinality().IsOverSaturatedByCallCount(call_count_);
}
- // Returns true iff all pre-requisites of this expectation are satisfied.
+ // Returns true if and only if all pre-requisites of this expectation are
+ // satisfied.
bool AllPrerequisitesAreSatisfied() const
GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex);
@@ -845,13 +856,13 @@ class GTEST_API_ ExpectationBase {
// an EXPECT_CALL() statement finishes.
const char* file_; // The file that contains the expectation.
int line_; // The line number of the expectation.
- const string source_text_; // The EXPECT_CALL(...) source text.
- // True iff the cardinality is specified explicitly.
+ const std::string source_text_; // The EXPECT_CALL(...) source text.
+ // True if and only if the cardinality is specified explicitly.
bool cardinality_specified_;
Cardinality cardinality_; // The cardinality of the expectation.
// The immediate pre-requisites (i.e. expectations that must be
// satisfied before this expectation can be matched) of this
- // expectation. We use linked_ptr in the set because we want an
+ // expectation. We use std::shared_ptr in the set because we want an
// Expectation object to be co-owned by its FunctionMocker and its
// successors. This allows multiple mock objects to be deleted at
// different times.
@@ -860,7 +871,7 @@ class GTEST_API_ ExpectationBase {
// This group of fields are the current state of the expectation,
// and can change as the mock function is called.
int call_count_; // How many times this expectation has been invoked.
- bool retired_; // True iff this expectation has retired.
+ bool retired_; // True if and only if this expectation has retired.
UntypedActions untyped_actions_;
bool extra_matcher_specified_;
bool repeated_action_specified_; // True if a WillRepeatedly() was specified.
@@ -880,20 +891,19 @@ class TypedExpectation : public ExpectationBase {
typedef typename Function<F>::ArgumentMatcherTuple ArgumentMatcherTuple;
typedef typename Function<F>::Result Result;
- TypedExpectation(FunctionMockerBase<F>* owner,
- const char* a_file, int a_line, const string& a_source_text,
+ TypedExpectation(FunctionMocker<F>* owner, const char* a_file, int a_line,
+ const std::string& a_source_text,
const ArgumentMatcherTuple& m)
: ExpectationBase(a_file, a_line, a_source_text),
owner_(owner),
matchers_(m),
// By default, extra_matcher_ should match anything. However,
- // we cannot initialize it with _ as that triggers a compiler
- // bug in Symbian's C++ compiler (cannot decide between two
- // overloaded constructors of Matcher<const ArgumentTuple&>).
+ // we cannot initialize it with _ as that causes ambiguity between
+ // Matcher's copy and move constructor for some argument types.
extra_matcher_(A<const ArgumentTuple&>()),
repeated_action_(DoDefault()) {}
- virtual ~TypedExpectation() {
+ ~TypedExpectation() override {
// Check the validity of the action count if it hasn't been done
// yet (for example, if the expectation was never used).
CheckActionCountIfNotDone();
@@ -1059,7 +1069,7 @@ class TypedExpectation : public ExpectationBase {
// If this mock method has an extra matcher (i.e. .With(matcher)),
// describes it to the ostream.
- virtual void MaybeDescribeExtraMatcherTo(::std::ostream* os) {
+ void MaybeDescribeExtraMatcherTo(::std::ostream* os) override {
if (extra_matcher_specified_) {
*os << " Expected args: ";
extra_matcher_.DescribeTo(os);
@@ -1069,26 +1079,25 @@ class TypedExpectation : public ExpectationBase {
private:
template <typename Function>
- friend class FunctionMockerBase;
+ friend class FunctionMocker;
// Returns an Expectation object that references and co-owns this
// expectation.
- virtual Expectation GetHandle() {
- return owner_->GetHandleOf(this);
- }
+ Expectation GetHandle() override { return owner_->GetHandleOf(this); }
// The following methods will be called only after the EXPECT_CALL()
// statement finishes and when the current thread holds
// g_gmock_mutex.
- // Returns true iff this expectation matches the given arguments.
+ // Returns true if and only if this expectation matches the given arguments.
bool Matches(const ArgumentTuple& args) const
GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
g_gmock_mutex.AssertHeld();
return TupleMatches(matchers_, args) && extra_matcher_.Matches(args);
}
- // Returns true iff this expectation should handle the given arguments.
+ // Returns true if and only if this expectation should handle the given
+ // arguments.
bool ShouldHandleArguments(const ArgumentTuple& args) const
GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
g_gmock_mutex.AssertHeld();
@@ -1148,10 +1157,9 @@ class TypedExpectation : public ExpectationBase {
}
// Returns the action that should be taken for the current invocation.
- const Action<F>& GetCurrentAction(
- const FunctionMockerBase<F>* mocker,
- const ArgumentTuple& args) const
- GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
+ const Action<F>& GetCurrentAction(const FunctionMocker<F>* mocker,
+ const ArgumentTuple& args) const
+ GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
g_gmock_mutex.AssertHeld();
const int count = call_count();
Assert(count >= 1, __FILE__, __LINE__,
@@ -1173,9 +1181,10 @@ class TypedExpectation : public ExpectationBase {
Log(kWarning, ss.str(), 1);
}
- return count <= action_count ?
- *static_cast<const Action<F>*>(untyped_actions_[count - 1]) :
- repeated_action();
+ return count <= action_count
+ ? *static_cast<const Action<F>*>(
+ untyped_actions_[static_cast<size_t>(count - 1)])
+ : repeated_action();
}
// Given the arguments of a mock function call, if the call will
@@ -1185,12 +1194,11 @@ class TypedExpectation : public ExpectationBase {
// Mock does it to 'why'. This method is not const as it calls
// IncrementCallCount(). A return value of NULL means the default
// action.
- const Action<F>* GetActionForArguments(
- const FunctionMockerBase<F>* mocker,
- const ArgumentTuple& args,
- ::std::ostream* what,
- ::std::ostream* why)
- GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
+ const Action<F>* GetActionForArguments(const FunctionMocker<F>* mocker,
+ const ArgumentTuple& args,
+ ::std::ostream* what,
+ ::std::ostream* why)
+ GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
g_gmock_mutex.AssertHeld();
if (IsSaturated()) {
// We have an excessive call.
@@ -1199,10 +1207,7 @@ class TypedExpectation : public ExpectationBase {
mocker->DescribeDefaultActionTo(args, what);
DescribeCallCountTo(why);
- // TODO(wan@google.com): allow the user to control whether
- // unexpected calls should fail immediately or continue using a
- // flag --gmock_unexpected_calls_are_fatal.
- return NULL;
+ return nullptr;
}
IncrementCallCount();
@@ -1219,7 +1224,7 @@ class TypedExpectation : public ExpectationBase {
// All the fields below won't change once the EXPECT_CALL()
// statement finishes.
- FunctionMockerBase<F>* const owner_;
+ FunctionMocker<F>* const owner_;
ArgumentMatcherTuple matchers_;
Matcher<const ArgumentTuple&> extra_matcher_;
Action<F> repeated_action_;
@@ -1240,7 +1245,7 @@ class TypedExpectation : public ExpectationBase {
// Logs a message including file and line number information.
GTEST_API_ void LogWithLocation(testing::internal::LogSeverity severity,
const char* file, int line,
- const string& message);
+ const std::string& message);
template <typename F>
class MockSpec {
@@ -1251,15 +1256,16 @@ class MockSpec {
// Constructs a MockSpec object, given the function mocker object
// that the spec is associated with.
- explicit MockSpec(internal::FunctionMockerBase<F>* function_mocker)
- : function_mocker_(function_mocker) {}
+ MockSpec(internal::FunctionMocker<F>* function_mocker,
+ const ArgumentMatcherTuple& matchers)
+ : function_mocker_(function_mocker), matchers_(matchers) {}
// Adds a new default action spec to the function mocker and returns
// the newly created spec.
internal::OnCallSpec<F>& InternalDefaultActionSetAt(
const char* file, int line, const char* obj, const char* call) {
LogWithLocation(internal::kInfo, file, line,
- string("ON_CALL(") + obj + ", " + call + ") invoked");
+ std::string("ON_CALL(") + obj + ", " + call + ") invoked");
return function_mocker_->AddNewOnCallSpec(file, line, matchers_);
}
@@ -1267,22 +1273,26 @@ class MockSpec {
// the newly created spec.
internal::TypedExpectation<F>& InternalExpectedAt(
const char* file, int line, const char* obj, const char* call) {
- const string source_text(string("EXPECT_CALL(") + obj + ", " + call + ")");
+ const std::string source_text(std::string("EXPECT_CALL(") + obj + ", " +
+ call + ")");
LogWithLocation(internal::kInfo, file, line, source_text + " invoked");
return function_mocker_->AddNewExpectation(
file, line, source_text, matchers_);
}
+ // This operator overload is used to swallow the superfluous parameter list
+ // introduced by the ON/EXPECT_CALL macros. See the macro comments for more
+ // explanation.
+ MockSpec<F>& operator()(const internal::WithoutMatchers&, void* const) {
+ return *this;
+ }
+
private:
template <typename Function>
friend class internal::FunctionMocker;
- void SetMatchers(const ArgumentMatcherTuple& matchers) {
- matchers_ = matchers;
- }
-
// The function mocker that owns this spec.
- internal::FunctionMockerBase<F>* const function_mocker_;
+ internal::FunctionMocker<F>* const function_mocker_;
// The argument matchers specified in the spec.
ArgumentMatcherTuple matchers_;
@@ -1303,18 +1313,18 @@ class ReferenceOrValueWrapper {
public:
// Constructs a wrapper from the given value/reference.
explicit ReferenceOrValueWrapper(T value)
- : value_(::testing::internal::move(value)) {
+ : value_(std::move(value)) {
}
// Unwraps and returns the underlying value/reference, exactly as
// originally passed. The behavior of calling this more than once on
// the same object is unspecified.
- T Unwrap() { return ::testing::internal::move(value_); }
+ T Unwrap() { return std::move(value_); }
// Provides nondestructive access to the underlying value/reference.
// Always returns a const reference (more precisely,
- // const RemoveReference<T>&). The behavior of calling this after
- // calling Unwrap on the same object is unspecified.
+ // const std::add_lvalue_reference<T>::type). The behavior of calling this
+ // after calling Unwrap on the same object is unspecified.
const T& Peek() const {
return value_;
}
@@ -1344,11 +1354,7 @@ class ReferenceOrValueWrapper<T&> {
// we need to temporarily disable the warning. We have to do it for
// the entire class to suppress the warning, even though it's about
// the constructor only.
-
-#ifdef _MSC_VER
-# pragma warning(push) // Saves the current warning state.
-# pragma warning(disable:4355) // Temporarily disables warning 4355.
-#endif // _MSV_VER
+GTEST_DISABLE_MSC_WARNINGS_PUSH_(4355)
// C++ treats the void type specially. For example, you cannot define
// a void-typed variable or pass a void value to a function.
@@ -1377,7 +1383,7 @@ class ActionResultHolder : public UntypedActionResultHolderBase {
}
// Prints the held value as an action's result to os.
- virtual void PrintAsActionResult(::std::ostream* os) const {
+ void PrintAsActionResult(::std::ostream* os) const override {
*os << "\n Returns: ";
// T may be a reference type, so we don't use UniversalPrint().
UniversalPrinter<T>::Print(result_.Peek(), os);
@@ -1387,27 +1393,27 @@ class ActionResultHolder : public UntypedActionResultHolderBase {
// result in a new-ed ActionResultHolder.
template <typename F>
static ActionResultHolder* PerformDefaultAction(
- const FunctionMockerBase<F>* func_mocker,
- const typename Function<F>::ArgumentTuple& args,
- const string& call_description) {
- return new ActionResultHolder(Wrapper(
- func_mocker->PerformDefaultAction(args, call_description)));
+ const FunctionMocker<F>* func_mocker,
+ typename Function<F>::ArgumentTuple&& args,
+ const std::string& call_description) {
+ return new ActionResultHolder(Wrapper(func_mocker->PerformDefaultAction(
+ std::move(args), call_description)));
}
// Performs the given action and returns the result in a new-ed
// ActionResultHolder.
template <typename F>
- static ActionResultHolder*
- PerformAction(const Action<F>& action,
- const typename Function<F>::ArgumentTuple& args) {
- return new ActionResultHolder(Wrapper(action.Perform(args)));
+ static ActionResultHolder* PerformAction(
+ const Action<F>& action, typename Function<F>::ArgumentTuple&& args) {
+ return new ActionResultHolder(
+ Wrapper(action.Perform(std::move(args))));
}
private:
typedef ReferenceOrValueWrapper<T> Wrapper;
explicit ActionResultHolder(Wrapper result)
- : result_(::testing::internal::move(result)) {
+ : result_(std::move(result)) {
}
Wrapper result_;
@@ -1421,16 +1427,16 @@ class ActionResultHolder<void> : public UntypedActionResultHolderBase {
public:
void Unwrap() { }
- virtual void PrintAsActionResult(::std::ostream* /* os */) const {}
+ void PrintAsActionResult(::std::ostream* /* os */) const override {}
// Performs the given mock function's default action and returns ownership
// of an empty ActionResultHolder*.
template <typename F>
static ActionResultHolder* PerformDefaultAction(
- const FunctionMockerBase<F>* func_mocker,
- const typename Function<F>::ArgumentTuple& args,
- const string& call_description) {
- func_mocker->PerformDefaultAction(args, call_description);
+ const FunctionMocker<F>* func_mocker,
+ typename Function<F>::ArgumentTuple&& args,
+ const std::string& call_description) {
+ func_mocker->PerformDefaultAction(std::move(args), call_description);
return new ActionResultHolder;
}
@@ -1438,9 +1444,8 @@ class ActionResultHolder<void> : public UntypedActionResultHolderBase {
// ActionResultHolder*.
template <typename F>
static ActionResultHolder* PerformAction(
- const Action<F>& action,
- const typename Function<F>::ArgumentTuple& args) {
- action.Perform(args);
+ const Action<F>& action, typename Function<F>::ArgumentTuple&& args) {
+ action.Perform(std::move(args));
return new ActionResultHolder;
}
@@ -1449,23 +1454,39 @@ class ActionResultHolder<void> : public UntypedActionResultHolderBase {
GTEST_DISALLOW_COPY_AND_ASSIGN_(ActionResultHolder);
};
-// The base of the function mocker class for the given function type.
-// We put the methods in this class instead of its child to avoid code
-// bloat.
template <typename F>
-class FunctionMockerBase : public UntypedFunctionMockerBase {
+class FunctionMocker;
+
+template <typename R, typename... Args>
+class FunctionMocker<R(Args...)> final : public UntypedFunctionMockerBase {
+ using F = R(Args...);
+
public:
- typedef typename Function<F>::Result Result;
- typedef typename Function<F>::ArgumentTuple ArgumentTuple;
- typedef typename Function<F>::ArgumentMatcherTuple ArgumentMatcherTuple;
+ using Result = R;
+ using ArgumentTuple = std::tuple<Args...>;
+ using ArgumentMatcherTuple = std::tuple<Matcher<Args>...>;
+
+ FunctionMocker() {}
- FunctionMockerBase() : current_spec_(this) {}
+ // There is no generally useful and implementable semantics of
+ // copying a mock object, so copying a mock is usually a user error.
+ // Thus we disallow copying function mockers. If the user really
+ // wants to copy a mock object, they should implement their own copy
+ // operation, for example:
+ //
+ // class MockFoo : public Foo {
+ // public:
+ // // Defines a copy constructor explicitly.
+ // MockFoo(const MockFoo& src) {}
+ // ...
+ // };
+ FunctionMocker(const FunctionMocker&) = delete;
+ FunctionMocker& operator=(const FunctionMocker&) = delete;
// The destructor verifies that all expectations on this mock
// function have been satisfied. If not, it will report Google Test
// non-fatal failures for the violations.
- virtual ~FunctionMockerBase()
- GTEST_LOCK_EXCLUDED_(g_gmock_mutex) {
+ ~FunctionMocker() override GTEST_LOCK_EXCLUDED_(g_gmock_mutex) {
MutexLock l(&g_gmock_mutex);
VerifyAndClearExpectationsLocked();
Mock::UnregisterLocked(this);
@@ -1485,7 +1506,7 @@ class FunctionMockerBase : public UntypedFunctionMockerBase {
return spec;
}
- return NULL;
+ return nullptr;
}
// Performs the default action of this mock function on the given
@@ -1495,14 +1516,15 @@ class FunctionMockerBase : public UntypedFunctionMockerBase {
// mutable state of this object, and thus can be called concurrently
// without locking.
// L = *
- Result PerformDefaultAction(const ArgumentTuple& args,
- const string& call_description) const {
+ Result PerformDefaultAction(ArgumentTuple&& args,
+ const std::string& call_description) const {
const OnCallSpec<F>* const spec =
this->FindOnCallSpec(args);
- if (spec != NULL) {
- return spec->GetAction().Perform(args);
+ if (spec != nullptr) {
+ return spec->GetAction().Perform(std::move(args));
}
- const string message = call_description +
+ const std::string message =
+ call_description +
"\n The mock function has no default action "
"set, and its return type has no default value set.";
#if GTEST_HAS_EXCEPTIONS
@@ -1520,31 +1542,30 @@ class FunctionMockerBase : public UntypedFunctionMockerBase {
// the error message to describe the call in the case the default
// action fails. The caller is responsible for deleting the result.
// L = *
- virtual UntypedActionResultHolderBase* UntypedPerformDefaultAction(
- const void* untyped_args, // must point to an ArgumentTuple
- const string& call_description) const {
- const ArgumentTuple& args =
- *static_cast<const ArgumentTuple*>(untyped_args);
- return ResultHolder::PerformDefaultAction(this, args, call_description);
+ UntypedActionResultHolderBase* UntypedPerformDefaultAction(
+ void* untyped_args, // must point to an ArgumentTuple
+ const std::string& call_description) const override {
+ ArgumentTuple* args = static_cast<ArgumentTuple*>(untyped_args);
+ return ResultHolder::PerformDefaultAction(this, std::move(*args),
+ call_description);
}
// Performs the given action with the given arguments and returns
// the action's result. The caller is responsible for deleting the
// result.
// L = *
- virtual UntypedActionResultHolderBase* UntypedPerformAction(
- const void* untyped_action, const void* untyped_args) const {
+ UntypedActionResultHolderBase* UntypedPerformAction(
+ const void* untyped_action, void* untyped_args) const override {
// Make a copy of the action before performing it, in case the
// action deletes the mock object (and thus deletes itself).
const Action<F> action = *static_cast<const Action<F>*>(untyped_action);
- const ArgumentTuple& args =
- *static_cast<const ArgumentTuple*>(untyped_args);
- return ResultHolder::PerformAction(action, args);
+ ArgumentTuple* args = static_cast<ArgumentTuple*>(untyped_args);
+ return ResultHolder::PerformAction(action, std::move(*args));
}
// Implements UntypedFunctionMockerBase::ClearDefaultActionsLocked():
// clears the ON_CALL()s set on this mock function.
- virtual void ClearDefaultActionsLocked()
+ void ClearDefaultActionsLocked() override
GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
g_gmock_mutex.AssertHeld();
@@ -1570,22 +1591,26 @@ class FunctionMockerBase : public UntypedFunctionMockerBase {
g_gmock_mutex.Lock();
}
- protected:
- template <typename Function>
- friend class MockSpec;
-
- typedef ActionResultHolder<Result> ResultHolder;
-
// Returns the result of invoking this mock function with the given
// arguments. This function can be safely called from multiple
// threads concurrently.
- Result InvokeWith(const ArgumentTuple& args)
- GTEST_LOCK_EXCLUDED_(g_gmock_mutex) {
- scoped_ptr<ResultHolder> holder(
- DownCast_<ResultHolder*>(this->UntypedInvokeWith(&args)));
+ Result Invoke(Args... args) GTEST_LOCK_EXCLUDED_(g_gmock_mutex) {
+ ArgumentTuple tuple(std::forward<Args>(args)...);
+ std::unique_ptr<ResultHolder> holder(DownCast_<ResultHolder*>(
+ this->UntypedInvokeWith(static_cast<void*>(&tuple))));
return holder->Unwrap();
}
+ MockSpec<F> With(Matcher<Args>... m) {
+ return MockSpec<F>(this, ::std::make_tuple(std::move(m)...));
+ }
+
+ protected:
+ template <typename Function>
+ friend class MockSpec;
+
+ typedef ActionResultHolder<Result> ResultHolder;
+
// Adds and returns a default action spec for this mock function.
OnCallSpec<F>& AddNewOnCallSpec(
const char* file, int line,
@@ -1598,31 +1623,27 @@ class FunctionMockerBase : public UntypedFunctionMockerBase {
}
// Adds and returns an expectation spec for this mock function.
- TypedExpectation<F>& AddNewExpectation(
- const char* file,
- int line,
- const string& source_text,
- const ArgumentMatcherTuple& m)
- GTEST_LOCK_EXCLUDED_(g_gmock_mutex) {
+ TypedExpectation<F>& AddNewExpectation(const char* file, int line,
+ const std::string& source_text,
+ const ArgumentMatcherTuple& m)
+ GTEST_LOCK_EXCLUDED_(g_gmock_mutex) {
Mock::RegisterUseByOnCallOrExpectCall(MockObject(), file, line);
TypedExpectation<F>* const expectation =
new TypedExpectation<F>(this, file, line, source_text, m);
- const linked_ptr<ExpectationBase> untyped_expectation(expectation);
+ const std::shared_ptr<ExpectationBase> untyped_expectation(expectation);
+ // See the definition of untyped_expectations_ for why access to
+ // it is unprotected here.
untyped_expectations_.push_back(untyped_expectation);
// Adds this expectation into the implicit sequence if there is one.
Sequence* const implicit_sequence = g_gmock_implicit_sequence.get();
- if (implicit_sequence != NULL) {
+ if (implicit_sequence != nullptr) {
implicit_sequence->AddExpectation(Expectation(untyped_expectation));
}
return *expectation;
}
- // The current spec (either default action spec or expectation spec)
- // being described on this function mocker.
- MockSpec<F>& current_spec() { return current_spec_; }
-
private:
template <typename Func> friend class TypedExpectation;
@@ -1635,10 +1656,9 @@ class FunctionMockerBase : public UntypedFunctionMockerBase {
::std::ostream* os) const {
const OnCallSpec<F>* const spec = FindOnCallSpec(args);
- if (spec == NULL) {
- *os << (internal::type_equals<Result, void>::value ?
- "returning directly.\n" :
- "returning default value.\n");
+ if (spec == nullptr) {
+ *os << (std::is_void<Result>::value ? "returning directly.\n"
+ : "returning default value.\n");
} else {
*os << "taking default action specified at:\n"
<< FormatFileLocation(spec->file(), spec->line()) << "\n";
@@ -1648,10 +1668,9 @@ class FunctionMockerBase : public UntypedFunctionMockerBase {
// Writes a message that the call is uninteresting (i.e. neither
// explicitly expected nor explicitly unexpected) to the given
// ostream.
- virtual void UntypedDescribeUninterestingCall(
- const void* untyped_args,
- ::std::ostream* os) const
- GTEST_LOCK_EXCLUDED_(g_gmock_mutex) {
+ void UntypedDescribeUninterestingCall(const void* untyped_args,
+ ::std::ostream* os) const override
+ GTEST_LOCK_EXCLUDED_(g_gmock_mutex) {
const ArgumentTuple& args =
*static_cast<const ArgumentTuple*>(untyped_args);
*os << "Uninteresting mock function call - ";
@@ -1676,18 +1695,17 @@ class FunctionMockerBase : public UntypedFunctionMockerBase {
// section. The reason is that we have no control on what the
// action does (it can invoke an arbitrary user function or even a
// mock function) and excessive locking could cause a dead lock.
- virtual const ExpectationBase* UntypedFindMatchingExpectation(
- const void* untyped_args,
- const void** untyped_action, bool* is_excessive,
- ::std::ostream* what, ::std::ostream* why)
- GTEST_LOCK_EXCLUDED_(g_gmock_mutex) {
+ const ExpectationBase* UntypedFindMatchingExpectation(
+ const void* untyped_args, const void** untyped_action, bool* is_excessive,
+ ::std::ostream* what, ::std::ostream* why) override
+ GTEST_LOCK_EXCLUDED_(g_gmock_mutex) {
const ArgumentTuple& args =
*static_cast<const ArgumentTuple*>(untyped_args);
MutexLock l(&g_gmock_mutex);
TypedExpectation<F>* exp = this->FindMatchingExpectationLocked(args);
- if (exp == NULL) { // A match wasn't found.
+ if (exp == nullptr) { // A match wasn't found.
this->FormatUnexpectedCallMessageLocked(args, what, why);
- return NULL;
+ return nullptr;
}
// This line must be done before calling GetActionForArguments(),
@@ -1695,15 +1713,15 @@ class FunctionMockerBase : public UntypedFunctionMockerBase {
// its saturation status.
*is_excessive = exp->IsSaturated();
const Action<F>* action = exp->GetActionForArguments(this, args, what, why);
- if (action != NULL && action->IsDoDefault())
- action = NULL; // Normalize "do default" to NULL.
+ if (action != nullptr && action->IsDoDefault())
+ action = nullptr; // Normalize "do default" to NULL.
*untyped_action = action;
return exp;
}
// Prints the given function arguments to the ostream.
- virtual void UntypedPrintArgs(const void* untyped_args,
- ::std::ostream* os) const {
+ void UntypedPrintArgs(const void* untyped_args,
+ ::std::ostream* os) const override {
const ArgumentTuple& args =
*static_cast<const ArgumentTuple*>(untyped_args);
UniversalPrint(args, os);
@@ -1715,6 +1733,8 @@ class FunctionMockerBase : public UntypedFunctionMockerBase {
const ArgumentTuple& args) const
GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
g_gmock_mutex.AssertHeld();
+ // See the definition of untyped_expectations_ for why access to
+ // it is unprotected here.
for (typename UntypedExpectations::const_reverse_iterator it =
untyped_expectations_.rbegin();
it != untyped_expectations_.rend(); ++it) {
@@ -1724,7 +1744,7 @@ class FunctionMockerBase : public UntypedFunctionMockerBase {
return exp;
}
}
- return NULL;
+ return nullptr;
}
// Returns a message that the arguments don't match any expectation.
@@ -1746,12 +1766,12 @@ class FunctionMockerBase : public UntypedFunctionMockerBase {
::std::ostream* why) const
GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
g_gmock_mutex.AssertHeld();
- const int count = static_cast<int>(untyped_expectations_.size());
+ const size_t count = untyped_expectations_.size();
*why << "Google Mock tried the following " << count << " "
<< (count == 1 ? "expectation, but it didn't match" :
"expectations, but none matched")
<< ":\n";
- for (int i = 0; i < count; i++) {
+ for (size_t i = 0; i < count; i++) {
TypedExpectation<F>* const expectation =
static_cast<TypedExpectation<F>*>(untyped_expectations_[i].get());
*why << "\n";
@@ -1764,41 +1784,97 @@ class FunctionMockerBase : public UntypedFunctionMockerBase {
expectation->DescribeCallCountTo(why);
}
}
+}; // class FunctionMocker
- // The current spec (either default action spec or expectation spec)
- // being described on this function mocker.
- MockSpec<F> current_spec_;
+GTEST_DISABLE_MSC_WARNINGS_POP_() // 4355
- // There is no generally useful and implementable semantics of
- // copying a mock object, so copying a mock is usually a user error.
- // Thus we disallow copying function mockers. If the user really
- // wants to copy a mock object, he should implement his own copy
- // operation, for example:
- //
- // class MockFoo : public Foo {
- // public:
- // // Defines a copy constructor explicitly.
- // MockFoo(const MockFoo& src) {}
- // ...
- // };
- GTEST_DISALLOW_COPY_AND_ASSIGN_(FunctionMockerBase);
-}; // class FunctionMockerBase
+// Reports an uninteresting call (whose description is in msg) in the
+// manner specified by 'reaction'.
+void ReportUninterestingCall(CallReaction reaction, const std::string& msg);
-#ifdef _MSC_VER
-# pragma warning(pop) // Restores the warning state.
-#endif // _MSV_VER
+} // namespace internal
-// Implements methods of FunctionMockerBase.
+// A MockFunction<F> class has one mock method whose type is F. It is
+// useful when you just want your test code to emit some messages and
+// have Google Mock verify the right messages are sent (and perhaps at
+// the right times). For example, if you are exercising code:
+//
+// Foo(1);
+// Foo(2);
+// Foo(3);
+//
+// and want to verify that Foo(1) and Foo(3) both invoke
+// mock.Bar("a"), but Foo(2) doesn't invoke anything, you can write:
+//
+// TEST(FooTest, InvokesBarCorrectly) {
+// MyMock mock;
+// MockFunction<void(string check_point_name)> check;
+// {
+// InSequence s;
+//
+// EXPECT_CALL(mock, Bar("a"));
+// EXPECT_CALL(check, Call("1"));
+// EXPECT_CALL(check, Call("2"));
+// EXPECT_CALL(mock, Bar("a"));
+// }
+// Foo(1);
+// check.Call("1");
+// Foo(2);
+// check.Call("2");
+// Foo(3);
+// }
+//
+// The expectation spec says that the first Bar("a") must happen
+// before check point "1", the second Bar("a") must happen after check
+// point "2", and nothing should happen between the two check
+// points. The explicit check points make it easy to tell which
+// Bar("a") is called by which call to Foo().
+//
+// MockFunction<F> can also be used to exercise code that accepts
+// std::function<F> callbacks. To do so, use AsStdFunction() method
+// to create std::function proxy forwarding to original object's Call.
+// Example:
+//
+// TEST(FooTest, RunsCallbackWithBarArgument) {
+// MockFunction<int(string)> callback;
+// EXPECT_CALL(callback, Call("bar")).WillOnce(Return(1));
+// Foo(callback.AsStdFunction());
+// }
+template <typename F>
+class MockFunction;
-// Verifies that all expectations on this mock function have been
-// satisfied. Reports one or more Google Test non-fatal failures and
-// returns false if not.
+template <typename R, typename... Args>
+class MockFunction<R(Args...)> {
+ public:
+ MockFunction() {}
+ MockFunction(const MockFunction&) = delete;
+ MockFunction& operator=(const MockFunction&) = delete;
-// Reports an uninteresting call (whose description is in msg) in the
-// manner specified by 'reaction'.
-void ReportUninterestingCall(CallReaction reaction, const string& msg);
+ std::function<R(Args...)> AsStdFunction() {
+ return [this](Args... args) -> R {
+ return this->Call(std::forward<Args>(args)...);
+ };
+ }
-} // namespace internal
+ // Implementation detail: the expansion of the MOCK_METHOD macro.
+ R Call(Args... args) {
+ mock_.SetOwnerAndName(this, "Call");
+ return mock_.Invoke(std::forward<Args>(args)...);
+ }
+
+ internal::MockSpec<R(Args...)> gmock_Call(Matcher<Args>... m) {
+ mock_.RegisterOwner(this);
+ return mock_.With(std::move(m)...);
+ }
+
+ internal::MockSpec<R(Args...)> gmock_Call(const internal::WithoutMatchers&,
+ R (*)(Args...)) {
+ return this->gmock_Call(::testing::A<Args>()...);
+ }
+
+ private:
+ internal::FunctionMocker<R(Args...)> mock_;
+};
// The style guide prohibits "using" statements in a namespace scope
// inside a header file. However, the MockSpec class template is
@@ -1831,17 +1907,79 @@ inline Expectation::Expectation(internal::ExpectationBase& exp) // NOLINT
} // namespace testing
-// A separate macro is required to avoid compile errors when the name
-// of the method used in call is a result of macro expansion.
-// See CompilesWithMethodNameExpandedFromMacro tests in
-// internal/gmock-spec-builders_test.cc for more details.
-#define GMOCK_ON_CALL_IMPL_(obj, call) \
- ((obj).gmock_##call).InternalDefaultActionSetAt(__FILE__, __LINE__, \
- #obj, #call)
-#define ON_CALL(obj, call) GMOCK_ON_CALL_IMPL_(obj, call)
-
-#define GMOCK_EXPECT_CALL_IMPL_(obj, call) \
- ((obj).gmock_##call).InternalExpectedAt(__FILE__, __LINE__, #obj, #call)
-#define EXPECT_CALL(obj, call) GMOCK_EXPECT_CALL_IMPL_(obj, call)
+GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251
+
+// Implementation for ON_CALL and EXPECT_CALL macros. A separate macro is
+// required to avoid compile errors when the name of the method used in call is
+// a result of macro expansion. See CompilesWithMethodNameExpandedFromMacro
+// tests in internal/gmock-spec-builders_test.cc for more details.
+//
+// This macro supports statements both with and without parameter matchers. If
+// the parameter list is omitted, gMock will accept any parameters, which allows
+// tests to be written that don't need to encode the number of method
+// parameter. This technique may only be used for non-overloaded methods.
+//
+// // These are the same:
+// ON_CALL(mock, NoArgsMethod()).WillByDefault(...);
+// ON_CALL(mock, NoArgsMethod).WillByDefault(...);
+//
+// // As are these:
+// ON_CALL(mock, TwoArgsMethod(_, _)).WillByDefault(...);
+// ON_CALL(mock, TwoArgsMethod).WillByDefault(...);
+//
+// // Can also specify args if you want, of course:
+// ON_CALL(mock, TwoArgsMethod(_, 45)).WillByDefault(...);
+//
+// // Overloads work as long as you specify parameters:
+// ON_CALL(mock, OverloadedMethod(_)).WillByDefault(...);
+// ON_CALL(mock, OverloadedMethod(_, _)).WillByDefault(...);
+//
+// // Oops! Which overload did you want?
+// ON_CALL(mock, OverloadedMethod).WillByDefault(...);
+// => ERROR: call to member function 'gmock_OverloadedMethod' is ambiguous
+//
+// How this works: The mock class uses two overloads of the gmock_Method
+// expectation setter method plus an operator() overload on the MockSpec object.
+// In the matcher list form, the macro expands to:
+//
+// // This statement:
+// ON_CALL(mock, TwoArgsMethod(_, 45))...
+//
+// // ...expands to:
+// mock.gmock_TwoArgsMethod(_, 45)(WithoutMatchers(), nullptr)...
+// |-------------v---------------||------------v-------------|
+// invokes first overload swallowed by operator()
+//
+// // ...which is essentially:
+// mock.gmock_TwoArgsMethod(_, 45)...
+//
+// Whereas the form without a matcher list:
+//
+// // This statement:
+// ON_CALL(mock, TwoArgsMethod)...
+//
+// // ...expands to:
+// mock.gmock_TwoArgsMethod(WithoutMatchers(), nullptr)...
+// |-----------------------v--------------------------|
+// invokes second overload
+//
+// // ...which is essentially:
+// mock.gmock_TwoArgsMethod(_, _)...
+//
+// The WithoutMatchers() argument is used to disambiguate overloads and to
+// block the caller from accidentally invoking the second overload directly. The
+// second argument is an internal type derived from the method signature. The
+// failure to disambiguate two overloads of this method in the ON_CALL statement
+// is how we block callers from setting expectations on overloaded methods.
+#define GMOCK_ON_CALL_IMPL_(mock_expr, Setter, call) \
+ ((mock_expr).gmock_##call)(::testing::internal::GetWithoutMatchers(), \
+ nullptr) \
+ .Setter(__FILE__, __LINE__, #mock_expr, #call)
+
+#define ON_CALL(obj, call) \
+ GMOCK_ON_CALL_IMPL_(obj, InternalDefaultActionSetAt, call)
+
+#define EXPECT_CALL(obj, call) \
+ GMOCK_ON_CALL_IMPL_(obj, InternalExpectedAt, call)
#endif // GMOCK_INCLUDE_GMOCK_GMOCK_SPEC_BUILDERS_H_
diff --git a/extern/gmock/include/gmock/gmock.h b/extern/gmock/include/gmock/gmock.h
index 6735c71bf8a..99c3d7871d0 100644
--- a/extern/gmock/include/gmock/gmock.h
+++ b/extern/gmock/include/gmock/gmock.h
@@ -26,26 +26,27 @@
// 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: wan@google.com (Zhanyong Wan)
+
// Google Mock - a framework for writing C++ mock classes.
//
// This is the main header file a user should include.
+// GOOGLETEST_CM0002 DO NOT DELETE
+
#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_H_
#define GMOCK_INCLUDE_GMOCK_GMOCK_H_
// This file implements the following syntax:
//
-// ON_CALL(mock_object.Method(...))
+// ON_CALL(mock_object, Method(...))
// .With(...) ?
// .WillByDefault(...);
//
// where With() is optional and WillByDefault() must appear exactly
// once.
//
-// EXPECT_CALL(mock_object.Method(...))
+// EXPECT_CALL(mock_object, Method(...))
// .With(...) ?
// .Times(...) ?
// .InSequence(...) *
@@ -57,13 +58,14 @@
#include "gmock/gmock-actions.h"
#include "gmock/gmock-cardinalities.h"
+#include "gmock/gmock-function-mocker.h"
#include "gmock/gmock-generated-actions.h"
#include "gmock/gmock-generated-function-mockers.h"
-#include "gmock/gmock-generated-nice-strict.h"
#include "gmock/gmock-generated-matchers.h"
#include "gmock/gmock-matchers.h"
#include "gmock/gmock-more-actions.h"
#include "gmock/gmock-more-matchers.h"
+#include "gmock/gmock-nice-strict.h"
#include "gmock/internal/gmock-internal-utils.h"
namespace testing {
@@ -71,6 +73,7 @@ namespace testing {
// Declares Google Mock flags that we want a user to use programmatically.
GMOCK_DECLARE_bool_(catch_leaked_mocks);
GMOCK_DECLARE_string_(verbose);
+GMOCK_DECLARE_int32_(default_mock_behavior);
// Initializes Google Mock. This must be called before running the
// tests. In particular, it parses the command line for the flags
@@ -89,6 +92,10 @@ GTEST_API_ void InitGoogleMock(int* argc, char** argv);
// UNICODE mode.
GTEST_API_ void InitGoogleMock(int* argc, wchar_t** argv);
+// This overloaded version can be used on Arduino/embedded platforms where
+// there is no argc/argv.
+GTEST_API_ void InitGoogleMock();
+
} // namespace testing
#endif // GMOCK_INCLUDE_GMOCK_GMOCK_H_
diff --git a/extern/gmock/include/gmock/internal/custom/README.md b/extern/gmock/include/gmock/internal/custom/README.md
new file mode 100644
index 00000000000..f6c93f616d6
--- /dev/null
+++ b/extern/gmock/include/gmock/internal/custom/README.md
@@ -0,0 +1,16 @@
+# Customization Points
+
+The custom directory is an injection point for custom user configurations.
+
+## Header `gmock-port.h`
+
+The following macros can be defined:
+
+### Flag related macros:
+
+* `GMOCK_DECLARE_bool_(name)`
+* `GMOCK_DECLARE_int32_(name)`
+* `GMOCK_DECLARE_string_(name)`
+* `GMOCK_DEFINE_bool_(name, default_val, doc)`
+* `GMOCK_DEFINE_int32_(name, default_val, doc)`
+* `GMOCK_DEFINE_string_(name, default_val, doc)`
diff --git a/extern/gmock/include/gmock/internal/custom/gmock-generated-actions.h b/extern/gmock/include/gmock/internal/custom/gmock-generated-actions.h
index 7dc3b1ad541..92d910cf06d 100644
--- a/extern/gmock/include/gmock/internal/custom/gmock-generated-actions.h
+++ b/extern/gmock/include/gmock/internal/custom/gmock-generated-actions.h
@@ -2,6 +2,8 @@
// pump.py gmock-generated-actions.h.pump
// DO NOT EDIT BY HAND!!!
+// GOOGLETEST_CM0002 DO NOT DELETE
+
#ifndef GMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_GENERATED_ACTIONS_H_
#define GMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_GENERATED_ACTIONS_H_
diff --git a/extern/gmock/include/gmock/internal/custom/gmock-matchers.h b/extern/gmock/include/gmock/internal/custom/gmock-matchers.h
index f2efef91dbe..14aafaabe6b 100644
--- a/extern/gmock/include/gmock/internal/custom/gmock-matchers.h
+++ b/extern/gmock/include/gmock/internal/custom/gmock-matchers.h
@@ -27,13 +27,10 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
-// ============================================================
-// An installation-specific extension point for gmock-matchers.h.
-// ============================================================
+// Injection point for custom user configurations. See README for details
//
-// Adds google3 callback support to CallableTraits.
-//
-#ifndef GMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_CALLBACK_MATCHERS_H_
-#define GMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_CALLBACK_MATCHERS_H_
+// GOOGLETEST_CM0002 DO NOT DELETE
-#endif // GMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_CALLBACK_MATCHERS_H_
+#ifndef GMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_MATCHERS_H_
+#define GMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_MATCHERS_H_
+#endif // GMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_MATCHERS_H_
diff --git a/extern/gmock/include/gmock/internal/custom/gmock-port.h b/extern/gmock/include/gmock/internal/custom/gmock-port.h
index 9ce8bfe06bf..0030fe91118 100644
--- a/extern/gmock/include/gmock/internal/custom/gmock-port.h
+++ b/extern/gmock/include/gmock/internal/custom/gmock-port.h
@@ -27,19 +27,12 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
-// Injection point for custom user configurations.
-// The following macros can be defined:
-//
-// Flag related macros:
-// GMOCK_DECLARE_bool_(name)
-// GMOCK_DECLARE_int32_(name)
-// GMOCK_DECLARE_string_(name)
-// GMOCK_DEFINE_bool_(name, default_val, doc)
-// GMOCK_DEFINE_int32_(name, default_val, doc)
-// GMOCK_DEFINE_string_(name, default_val, doc)
+// Injection point for custom user configurations. See README for details
//
// ** Custom implementation starts here **
+// GOOGLETEST_CM0002 DO NOT DELETE
+
#ifndef GMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_PORT_H_
#define GMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_PORT_H_
diff --git a/extern/gmock/include/gmock/internal/gmock-generated-internal-utils.h b/extern/gmock/include/gmock/internal/gmock-generated-internal-utils.h
deleted file mode 100644
index 7811e43f87c..00000000000
--- a/extern/gmock/include/gmock/internal/gmock-generated-internal-utils.h
+++ /dev/null
@@ -1,279 +0,0 @@
-// This file was GENERATED by command:
-// pump.py gmock-generated-internal-utils.h.pump
-// DO NOT EDIT BY HAND!!!
-
-// Copyright 2007, Google Inc.
-// All rights reserved.
-//
-// 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: wan@google.com (Zhanyong Wan)
-
-// Google Mock - a framework for writing C++ mock classes.
-//
-// This file contains template meta-programming utility classes needed
-// for implementing Google Mock.
-
-#ifndef GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_GENERATED_INTERNAL_UTILS_H_
-#define GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_GENERATED_INTERNAL_UTILS_H_
-
-#include "gmock/internal/gmock-port.h"
-
-namespace testing {
-
-template <typename T>
-class Matcher;
-
-namespace internal {
-
-// An IgnoredValue object can be implicitly constructed from ANY value.
-// This is used in implementing the IgnoreResult(a) action.
-class IgnoredValue {
- public:
- // This constructor template allows any value to be implicitly
- // converted to IgnoredValue. The object has no data member and
- // doesn't try to remember anything about the argument. We
- // deliberately omit the 'explicit' keyword in order to allow the
- // conversion to be implicit.
- template <typename T>
- IgnoredValue(const T& /* ignored */) {} // NOLINT(runtime/explicit)
-};
-
-// MatcherTuple<T>::type is a tuple type where each field is a Matcher
-// for the corresponding field in tuple type T.
-template <typename Tuple>
-struct MatcherTuple;
-
-template <>
-struct MatcherTuple< ::testing::tuple<> > {
- typedef ::testing::tuple< > type;
-};
-
-template <typename A1>
-struct MatcherTuple< ::testing::tuple<A1> > {
- typedef ::testing::tuple<Matcher<A1> > type;
-};
-
-template <typename A1, typename A2>
-struct MatcherTuple< ::testing::tuple<A1, A2> > {
- typedef ::testing::tuple<Matcher<A1>, Matcher<A2> > type;
-};
-
-template <typename A1, typename A2, typename A3>
-struct MatcherTuple< ::testing::tuple<A1, A2, A3> > {
- typedef ::testing::tuple<Matcher<A1>, Matcher<A2>, Matcher<A3> > type;
-};
-
-template <typename A1, typename A2, typename A3, typename A4>
-struct MatcherTuple< ::testing::tuple<A1, A2, A3, A4> > {
- typedef ::testing::tuple<Matcher<A1>, Matcher<A2>, Matcher<A3>,
- Matcher<A4> > type;
-};
-
-template <typename A1, typename A2, typename A3, typename A4, typename A5>
-struct MatcherTuple< ::testing::tuple<A1, A2, A3, A4, A5> > {
- typedef ::testing::tuple<Matcher<A1>, Matcher<A2>, Matcher<A3>, Matcher<A4>,
- Matcher<A5> > type;
-};
-
-template <typename A1, typename A2, typename A3, typename A4, typename A5,
- typename A6>
-struct MatcherTuple< ::testing::tuple<A1, A2, A3, A4, A5, A6> > {
- typedef ::testing::tuple<Matcher<A1>, Matcher<A2>, Matcher<A3>, Matcher<A4>,
- Matcher<A5>, Matcher<A6> > type;
-};
-
-template <typename A1, typename A2, typename A3, typename A4, typename A5,
- typename A6, typename A7>
-struct MatcherTuple< ::testing::tuple<A1, A2, A3, A4, A5, A6, A7> > {
- typedef ::testing::tuple<Matcher<A1>, Matcher<A2>, Matcher<A3>, Matcher<A4>,
- Matcher<A5>, Matcher<A6>, Matcher<A7> > type;
-};
-
-template <typename A1, typename A2, typename A3, typename A4, typename A5,
- typename A6, typename A7, typename A8>
-struct MatcherTuple< ::testing::tuple<A1, A2, A3, A4, A5, A6, A7, A8> > {
- typedef ::testing::tuple<Matcher<A1>, Matcher<A2>, Matcher<A3>, Matcher<A4>,
- Matcher<A5>, Matcher<A6>, Matcher<A7>, Matcher<A8> > type;
-};
-
-template <typename A1, typename A2, typename A3, typename A4, typename A5,
- typename A6, typename A7, typename A8, typename A9>
-struct MatcherTuple< ::testing::tuple<A1, A2, A3, A4, A5, A6, A7, A8, A9> > {
- typedef ::testing::tuple<Matcher<A1>, Matcher<A2>, Matcher<A3>, Matcher<A4>,
- Matcher<A5>, Matcher<A6>, Matcher<A7>, Matcher<A8>, Matcher<A9> > type;
-};
-
-template <typename A1, typename A2, typename A3, typename A4, typename A5,
- typename A6, typename A7, typename A8, typename A9, typename A10>
-struct MatcherTuple< ::testing::tuple<A1, A2, A3, A4, A5, A6, A7, A8, A9,
- A10> > {
- typedef ::testing::tuple<Matcher<A1>, Matcher<A2>, Matcher<A3>, Matcher<A4>,
- Matcher<A5>, Matcher<A6>, Matcher<A7>, Matcher<A8>, Matcher<A9>,
- Matcher<A10> > type;
-};
-
-// Template struct Function<F>, where F must be a function type, contains
-// the following typedefs:
-//
-// Result: the function's return type.
-// ArgumentN: the type of the N-th argument, where N starts with 1.
-// ArgumentTuple: the tuple type consisting of all parameters of F.
-// ArgumentMatcherTuple: the tuple type consisting of Matchers for all
-// parameters of F.
-// MakeResultVoid: the function type obtained by substituting void
-// for the return type of F.
-// MakeResultIgnoredValue:
-// the function type obtained by substituting Something
-// for the return type of F.
-template <typename F>
-struct Function;
-
-template <typename R>
-struct Function<R()> {
- typedef R Result;
- typedef ::testing::tuple<> ArgumentTuple;
- typedef typename MatcherTuple<ArgumentTuple>::type ArgumentMatcherTuple;
- typedef void MakeResultVoid();
- typedef IgnoredValue MakeResultIgnoredValue();
-};
-
-template <typename R, typename A1>
-struct Function<R(A1)>
- : Function<R()> {
- typedef A1 Argument1;
- typedef ::testing::tuple<A1> ArgumentTuple;
- typedef typename MatcherTuple<ArgumentTuple>::type ArgumentMatcherTuple;
- typedef void MakeResultVoid(A1);
- typedef IgnoredValue MakeResultIgnoredValue(A1);
-};
-
-template <typename R, typename A1, typename A2>
-struct Function<R(A1, A2)>
- : Function<R(A1)> {
- typedef A2 Argument2;
- typedef ::testing::tuple<A1, A2> ArgumentTuple;
- typedef typename MatcherTuple<ArgumentTuple>::type ArgumentMatcherTuple;
- typedef void MakeResultVoid(A1, A2);
- typedef IgnoredValue MakeResultIgnoredValue(A1, A2);
-};
-
-template <typename R, typename A1, typename A2, typename A3>
-struct Function<R(A1, A2, A3)>
- : Function<R(A1, A2)> {
- typedef A3 Argument3;
- typedef ::testing::tuple<A1, A2, A3> ArgumentTuple;
- typedef typename MatcherTuple<ArgumentTuple>::type ArgumentMatcherTuple;
- typedef void MakeResultVoid(A1, A2, A3);
- typedef IgnoredValue MakeResultIgnoredValue(A1, A2, A3);
-};
-
-template <typename R, typename A1, typename A2, typename A3, typename A4>
-struct Function<R(A1, A2, A3, A4)>
- : Function<R(A1, A2, A3)> {
- typedef A4 Argument4;
- typedef ::testing::tuple<A1, A2, A3, A4> ArgumentTuple;
- typedef typename MatcherTuple<ArgumentTuple>::type ArgumentMatcherTuple;
- typedef void MakeResultVoid(A1, A2, A3, A4);
- typedef IgnoredValue MakeResultIgnoredValue(A1, A2, A3, A4);
-};
-
-template <typename R, typename A1, typename A2, typename A3, typename A4,
- typename A5>
-struct Function<R(A1, A2, A3, A4, A5)>
- : Function<R(A1, A2, A3, A4)> {
- typedef A5 Argument5;
- typedef ::testing::tuple<A1, A2, A3, A4, A5> ArgumentTuple;
- typedef typename MatcherTuple<ArgumentTuple>::type ArgumentMatcherTuple;
- typedef void MakeResultVoid(A1, A2, A3, A4, A5);
- typedef IgnoredValue MakeResultIgnoredValue(A1, A2, A3, A4, A5);
-};
-
-template <typename R, typename A1, typename A2, typename A3, typename A4,
- typename A5, typename A6>
-struct Function<R(A1, A2, A3, A4, A5, A6)>
- : Function<R(A1, A2, A3, A4, A5)> {
- typedef A6 Argument6;
- typedef ::testing::tuple<A1, A2, A3, A4, A5, A6> ArgumentTuple;
- typedef typename MatcherTuple<ArgumentTuple>::type ArgumentMatcherTuple;
- typedef void MakeResultVoid(A1, A2, A3, A4, A5, A6);
- typedef IgnoredValue MakeResultIgnoredValue(A1, A2, A3, A4, A5, A6);
-};
-
-template <typename R, typename A1, typename A2, typename A3, typename A4,
- typename A5, typename A6, typename A7>
-struct Function<R(A1, A2, A3, A4, A5, A6, A7)>
- : Function<R(A1, A2, A3, A4, A5, A6)> {
- typedef A7 Argument7;
- typedef ::testing::tuple<A1, A2, A3, A4, A5, A6, A7> ArgumentTuple;
- typedef typename MatcherTuple<ArgumentTuple>::type ArgumentMatcherTuple;
- typedef void MakeResultVoid(A1, A2, A3, A4, A5, A6, A7);
- typedef IgnoredValue MakeResultIgnoredValue(A1, A2, A3, A4, A5, A6, A7);
-};
-
-template <typename R, typename A1, typename A2, typename A3, typename A4,
- typename A5, typename A6, typename A7, typename A8>
-struct Function<R(A1, A2, A3, A4, A5, A6, A7, A8)>
- : Function<R(A1, A2, A3, A4, A5, A6, A7)> {
- typedef A8 Argument8;
- typedef ::testing::tuple<A1, A2, A3, A4, A5, A6, A7, A8> ArgumentTuple;
- typedef typename MatcherTuple<ArgumentTuple>::type ArgumentMatcherTuple;
- typedef void MakeResultVoid(A1, A2, A3, A4, A5, A6, A7, A8);
- typedef IgnoredValue MakeResultIgnoredValue(A1, A2, A3, A4, A5, A6, A7, A8);
-};
-
-template <typename R, typename A1, typename A2, typename A3, typename A4,
- typename A5, typename A6, typename A7, typename A8, typename A9>
-struct Function<R(A1, A2, A3, A4, A5, A6, A7, A8, A9)>
- : Function<R(A1, A2, A3, A4, A5, A6, A7, A8)> {
- typedef A9 Argument9;
- typedef ::testing::tuple<A1, A2, A3, A4, A5, A6, A7, A8, A9> ArgumentTuple;
- typedef typename MatcherTuple<ArgumentTuple>::type ArgumentMatcherTuple;
- typedef void MakeResultVoid(A1, A2, A3, A4, A5, A6, A7, A8, A9);
- typedef IgnoredValue MakeResultIgnoredValue(A1, A2, A3, A4, A5, A6, A7, A8,
- A9);
-};
-
-template <typename R, typename A1, typename A2, typename A3, typename A4,
- typename A5, typename A6, typename A7, typename A8, typename A9,
- typename A10>
-struct Function<R(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10)>
- : Function<R(A1, A2, A3, A4, A5, A6, A7, A8, A9)> {
- typedef A10 Argument10;
- typedef ::testing::tuple<A1, A2, A3, A4, A5, A6, A7, A8, A9,
- A10> ArgumentTuple;
- typedef typename MatcherTuple<ArgumentTuple>::type ArgumentMatcherTuple;
- typedef void MakeResultVoid(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10);
- typedef IgnoredValue MakeResultIgnoredValue(A1, A2, A3, A4, A5, A6, A7, A8,
- A9, A10);
-};
-
-} // namespace internal
-
-} // namespace testing
-
-#endif // GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_GENERATED_INTERNAL_UTILS_H_
diff --git a/extern/gmock/include/gmock/internal/gmock-internal-utils.h b/extern/gmock/include/gmock/internal/gmock-internal-utils.h
index e2ddb05c91d..fdc049c589c 100644
--- a/extern/gmock/include/gmock/internal/gmock-internal-utils.h
+++ b/extern/gmock/include/gmock/internal/gmock-internal-utils.h
@@ -26,8 +26,7 @@
// 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: wan@google.com (Zhanyong Wan)
+
// Google Mock - a framework for writing C++ mock classes.
//
@@ -35,25 +34,42 @@
// Mock. They are subject to change without notice, so please DO NOT
// USE THEM IN USER CODE.
+// GOOGLETEST_CM0002 DO NOT DELETE
+
#ifndef GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_INTERNAL_UTILS_H_
#define GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_INTERNAL_UTILS_H_
#include <stdio.h>
#include <ostream> // NOLINT
#include <string>
-
-#include "gmock/internal/gmock-generated-internal-utils.h"
+#include <type_traits>
#include "gmock/internal/gmock-port.h"
#include "gtest/gtest.h"
namespace testing {
+
+template <typename>
+class Matcher;
+
namespace internal {
+// Silence MSVC C4100 (unreferenced formal parameter) and
+// C4805('==': unsafe mix of type 'const int' and type 'const bool')
+#ifdef _MSC_VER
+# pragma warning(push)
+# pragma warning(disable:4100)
+# pragma warning(disable:4805)
+#endif
+
+// Joins a vector of strings as if they are fields of a tuple; returns
+// the joined string.
+GTEST_API_ std::string JoinAsTuple(const Strings& fields);
+
// Converts an identifier name to a space-separated list of lower-case
// words. Each maximum substring of the form [A-Za-z][a-z]*|\d+ is
// treated as one word. For example, both "FooBar123" and
// "foo_bar_123" are converted to "foo bar 123".
-GTEST_API_ string ConvertIdentifierNameToWords(const char* id_name);
+GTEST_API_ std::string ConvertIdentifierNameToWords(const char* id_name);
// PointeeOf<Pointer>::type is the type of a value pointed to by a
// Pointer, which can be either a smart pointer or a raw pointer. The
@@ -80,44 +96,16 @@ inline const typename Pointer::element_type* GetRawPointer(const Pointer& p) {
template <typename Element>
inline Element* GetRawPointer(Element* p) { return p; }
-// This comparator allows linked_ptr to be stored in sets.
-template <typename T>
-struct LinkedPtrLessThan {
- bool operator()(const ::testing::internal::linked_ptr<T>& lhs,
- const ::testing::internal::linked_ptr<T>& rhs) const {
- return lhs.get() < rhs.get();
- }
-};
-
-// Symbian compilation can be done with wchar_t being either a native
-// type or a typedef. Using Google Mock with OpenC without wchar_t
-// should require the definition of _STLP_NO_WCHAR_T.
-//
// MSVC treats wchar_t as a native type usually, but treats it as the
// same as unsigned short when the compiler option /Zc:wchar_t- is
// specified. It defines _NATIVE_WCHAR_T_DEFINED symbol when wchar_t
// is a native type.
-#if (GTEST_OS_SYMBIAN && defined(_STLP_NO_WCHAR_T)) || \
- (defined(_MSC_VER) && !defined(_NATIVE_WCHAR_T_DEFINED))
+#if defined(_MSC_VER) && !defined(_NATIVE_WCHAR_T_DEFINED)
// wchar_t is a typedef.
#else
# define GMOCK_WCHAR_T_IS_NATIVE_ 1
#endif
-// signed wchar_t and unsigned wchar_t are NOT in the C++ standard.
-// Using them is a bad practice and not portable. So DON'T use them.
-//
-// Still, Google Mock is designed to work even if the user uses signed
-// wchar_t or unsigned wchar_t (obviously, assuming the compiler
-// supports them).
-//
-// To gcc,
-// wchar_t == signed wchar_t != unsigned wchar_t == unsigned int
-#ifdef __GNUC__
-// signed/unsigned wchar_t are valid types.
-# define GMOCK_HAS_SIGNED_WCHAR_T_ 1
-#endif
-
// In what follows, we use the term "kind" to indicate whether a type
// is bool, an integer type (excluding bool), a floating-point type,
// or none of them. This categorization is useful for determining
@@ -169,11 +157,11 @@ GMOCK_DECLARE_KIND_(long double, kFloatingPoint);
static_cast< ::testing::internal::TypeKind>( \
::testing::internal::KindOf<type>::value)
-// Evaluates to true iff integer type T is signed.
+// Evaluates to true if and only if integer type T is signed.
#define GMOCK_IS_SIGNED_(T) (static_cast<T>(-1) < 0)
// LosslessArithmeticConvertibleImpl<kFromKind, From, kToKind, To>::value
-// is true iff arithmetic type From can be losslessly converted to
+// is true if and only if arithmetic type From can be losslessly converted to
// arithmetic type To.
//
// It's the user's responsibility to ensure that both From and To are
@@ -182,30 +170,30 @@ GMOCK_DECLARE_KIND_(long double, kFloatingPoint);
// From, and kToKind is the kind of To; the value is
// implementation-defined when the above pre-condition is violated.
template <TypeKind kFromKind, typename From, TypeKind kToKind, typename To>
-struct LosslessArithmeticConvertibleImpl : public false_type {};
+struct LosslessArithmeticConvertibleImpl : public std::false_type {};
// Converting bool to bool is lossless.
template <>
struct LosslessArithmeticConvertibleImpl<kBool, bool, kBool, bool>
- : public true_type {}; // NOLINT
+ : public std::true_type {};
// Converting bool to any integer type is lossless.
template <typename To>
struct LosslessArithmeticConvertibleImpl<kBool, bool, kInteger, To>
- : public true_type {}; // NOLINT
+ : public std::true_type {};
// Converting bool to any floating-point type is lossless.
template <typename To>
struct LosslessArithmeticConvertibleImpl<kBool, bool, kFloatingPoint, To>
- : public true_type {}; // NOLINT
+ : public std::true_type {};
// Converting an integer to bool is lossy.
template <typename From>
struct LosslessArithmeticConvertibleImpl<kInteger, From, kBool, bool>
- : public false_type {}; // NOLINT
+ : public std::false_type {};
-// Converting an integer to another non-bool integer is lossless iff
-// the target type's range encloses the source type's range.
+// Converting an integer to another non-bool integer is lossless
+// if and only if the target type's range encloses the source type's range.
template <typename From, typename To>
struct LosslessArithmeticConvertibleImpl<kInteger, From, kInteger, To>
: public bool_constant<
@@ -223,27 +211,27 @@ struct LosslessArithmeticConvertibleImpl<kInteger, From, kInteger, To>
// the format of a floating-point number is implementation-defined.
template <typename From, typename To>
struct LosslessArithmeticConvertibleImpl<kInteger, From, kFloatingPoint, To>
- : public false_type {}; // NOLINT
+ : public std::false_type {};
// Converting a floating-point to bool is lossy.
template <typename From>
struct LosslessArithmeticConvertibleImpl<kFloatingPoint, From, kBool, bool>
- : public false_type {}; // NOLINT
+ : public std::false_type {};
// Converting a floating-point to an integer is lossy.
template <typename From, typename To>
struct LosslessArithmeticConvertibleImpl<kFloatingPoint, From, kInteger, To>
- : public false_type {}; // NOLINT
+ : public std::false_type {};
// Converting a floating-point to another floating-point is lossless
-// iff the target type is at least as big as the source type.
+// if and only if the target type is at least as big as the source type.
template <typename From, typename To>
struct LosslessArithmeticConvertibleImpl<
kFloatingPoint, From, kFloatingPoint, To>
: public bool_constant<sizeof(From) <= sizeof(To)> {}; // NOLINT
-// LosslessArithmeticConvertible<From, To>::value is true iff arithmetic
-// type From can be losslessly converted to arithmetic type To.
+// LosslessArithmeticConvertible<From, To>::value is true if and only if
+// arithmetic type From can be losslessly converted to arithmetic type To.
//
// It's the user's responsibility to ensure that both From and To are
// raw (i.e. has no CV modifier, is not a pointer, and is not a
@@ -267,7 +255,7 @@ class FailureReporterInterface {
// Reports a failure that occurred at the given source file location.
virtual void ReportFailure(FailureType type, const char* file, int line,
- const string& message) = 0;
+ const std::string& message) = 0;
};
// Returns the failure reporter used by Google Mock.
@@ -279,7 +267,7 @@ GTEST_API_ FailureReporterInterface* GetFailureReporter();
// inline this function to prevent it from showing up in the stack
// trace.
inline void Assert(bool condition, const char* file, int line,
- const string& msg) {
+ const std::string& msg) {
if (!condition) {
GetFailureReporter()->ReportFailure(FailureReporterInterface::kFatal,
file, line, msg);
@@ -292,7 +280,7 @@ inline void Assert(bool condition, const char* file, int line) {
// Verifies that condition is true; generates a non-fatal failure if
// condition is false.
inline void Expect(bool condition, const char* file, int line,
- const string& msg) {
+ const std::string& msg) {
if (!condition) {
GetFailureReporter()->ReportFailure(FailureReporterInterface::kNonfatal,
file, line, msg);
@@ -317,49 +305,36 @@ const char kWarningVerbosity[] = "warning";
// No logs are printed.
const char kErrorVerbosity[] = "error";
-// Returns true iff a log with the given severity is visible according
-// to the --gmock_verbose flag.
+// Returns true if and only if a log with the given severity is visible
+// according to the --gmock_verbose flag.
GTEST_API_ bool LogIsVisible(LogSeverity severity);
-// Prints the given message to stdout iff 'severity' >= the level
+// Prints the given message to stdout if and only if 'severity' >= the level
// specified by the --gmock_verbose flag. If stack_frames_to_skip >=
// 0, also prints the stack trace excluding the top
// stack_frames_to_skip frames. In opt mode, any positive
// stack_frames_to_skip is treated as 0, since we don't know which
// function calls will be inlined by the compiler and need to be
// conservative.
-GTEST_API_ void Log(LogSeverity severity,
- const string& message,
+GTEST_API_ void Log(LogSeverity severity, const std::string& message,
int stack_frames_to_skip);
-// TODO(wan@google.com): group all type utilities together.
-
-// Type traits.
-
-// is_reference<T>::value is non-zero iff T is a reference type.
-template <typename T> struct is_reference : public false_type {};
-template <typename T> struct is_reference<T&> : public true_type {};
-
-// type_equals<T1, T2>::value is non-zero iff T1 and T2 are the same type.
-template <typename T1, typename T2> struct type_equals : public false_type {};
-template <typename T> struct type_equals<T, T> : public true_type {};
+// A marker class that is used to resolve parameterless expectations to the
+// correct overload. This must not be instantiable, to prevent client code from
+// accidentally resolving to the overload; for example:
+//
+// ON_CALL(mock, Method({}, nullptr))...
+//
+class WithoutMatchers {
+ private:
+ WithoutMatchers() {}
+ friend GTEST_API_ WithoutMatchers GetWithoutMatchers();
+};
-// remove_reference<T>::type removes the reference from type T, if any.
-template <typename T> struct remove_reference { typedef T type; }; // NOLINT
-template <typename T> struct remove_reference<T&> { typedef T type; }; // NOLINT
+// Internal use only: access the singleton instance of WithoutMatchers.
+GTEST_API_ WithoutMatchers GetWithoutMatchers();
-// DecayArray<T>::type turns an array type U[N] to const U* and preserves
-// other types. Useful for saving a copy of a function argument.
-template <typename T> struct DecayArray { typedef T type; }; // NOLINT
-template <typename T, size_t N> struct DecayArray<T[N]> {
- typedef const T* type;
-};
-// Sometimes people use arrays whose size is not available at the use site
-// (e.g. extern const char kNamePrefix[]). This specialization covers that
-// case.
-template <typename T> struct DecayArray<T[]> {
- typedef const T* type;
-};
+// Type traits.
// Disable MSVC warnings for infinite recursion, since in this case the
// the recursion is unreachable.
@@ -409,9 +384,8 @@ class StlContainerView {
typedef const type& const_reference;
static const_reference ConstReference(const RawContainer& container) {
- // Ensures that RawContainer is not a const type.
- testing::StaticAssertTypeEq<RawContainer,
- GTEST_REMOVE_CONST_(RawContainer)>();
+ static_assert(!std::is_const<RawContainer>::value,
+ "RawContainer type must not be const");
return container;
}
static type Copy(const RawContainer& container) { return container; }
@@ -421,7 +395,7 @@ class StlContainerView {
template <typename Element, size_t N>
class StlContainerView<Element[N]> {
public:
- typedef GTEST_REMOVE_CONST_(Element) RawElement;
+ typedef typename std::remove_const<Element>::type RawElement;
typedef internal::NativeArray<RawElement> type;
// NativeArray<T> can represent a native array either by value or by
// reference (selected by a constructor argument), so 'const type'
@@ -431,53 +405,32 @@ class StlContainerView<Element[N]> {
typedef const type const_reference;
static const_reference ConstReference(const Element (&array)[N]) {
- // Ensures that Element is not a const type.
- testing::StaticAssertTypeEq<Element, RawElement>();
-#if GTEST_OS_SYMBIAN
- // The Nokia Symbian compiler confuses itself in template instantiation
- // for this call without the cast to Element*:
- // function call '[testing::internal::NativeArray<char *>].NativeArray(
- // {lval} const char *[4], long, testing::internal::RelationToSource)'
- // does not match
- // 'testing::internal::NativeArray<char *>::NativeArray(
- // char *const *, unsigned int, testing::internal::RelationToSource)'
- // (instantiating: 'testing::internal::ContainsMatcherImpl
- // <const char * (&)[4]>::Matches(const char * (&)[4]) const')
- // (instantiating: 'testing::internal::StlContainerView<char *[4]>::
- // ConstReference(const char * (&)[4])')
- // (and though the N parameter type is mismatched in the above explicit
- // conversion of it doesn't help - only the conversion of the array).
- return type(const_cast<Element*>(&array[0]), N,
- RelationToSourceReference());
-#else
+ static_assert(std::is_same<Element, RawElement>::value,
+ "Element type must not be const");
return type(array, N, RelationToSourceReference());
-#endif // GTEST_OS_SYMBIAN
}
static type Copy(const Element (&array)[N]) {
-#if GTEST_OS_SYMBIAN
- return type(const_cast<Element*>(&array[0]), N, RelationToSourceCopy());
-#else
return type(array, N, RelationToSourceCopy());
-#endif // GTEST_OS_SYMBIAN
}
};
// This specialization is used when RawContainer is a native array
// represented as a (pointer, size) tuple.
template <typename ElementPointer, typename Size>
-class StlContainerView< ::testing::tuple<ElementPointer, Size> > {
+class StlContainerView< ::std::tuple<ElementPointer, Size> > {
public:
- typedef GTEST_REMOVE_CONST_(
- typename internal::PointeeOf<ElementPointer>::type) RawElement;
+ typedef typename std::remove_const<
+ typename internal::PointeeOf<ElementPointer>::type>::type RawElement;
typedef internal::NativeArray<RawElement> type;
typedef const type const_reference;
static const_reference ConstReference(
- const ::testing::tuple<ElementPointer, Size>& array) {
- return type(get<0>(array), get<1>(array), RelationToSourceReference());
+ const ::std::tuple<ElementPointer, Size>& array) {
+ return type(std::get<0>(array), std::get<1>(array),
+ RelationToSourceReference());
}
- static type Copy(const ::testing::tuple<ElementPointer, Size>& array) {
- return type(get<0>(array), get<1>(array), RelationToSourceCopy());
+ static type Copy(const ::std::tuple<ElementPointer, Size>& array) {
+ return type(std::get<0>(array), std::get<1>(array), RelationToSourceCopy());
}
};
@@ -499,13 +452,62 @@ struct RemoveConstFromKey<std::pair<const K, V> > {
typedef std::pair<K, V> type;
};
-// Mapping from booleans to types. Similar to boost::bool_<kValue> and
-// std::integral_constant<bool, kValue>.
-template <bool kValue>
-struct BooleanConstant {};
+// Emit an assertion failure due to incorrect DoDefault() usage. Out-of-lined to
+// reduce code size.
+GTEST_API_ void IllegalDoDefault(const char* file, int line);
+
+template <typename F, typename Tuple, size_t... Idx>
+auto ApplyImpl(F&& f, Tuple&& args, IndexSequence<Idx...>) -> decltype(
+ std::forward<F>(f)(std::get<Idx>(std::forward<Tuple>(args))...)) {
+ return std::forward<F>(f)(std::get<Idx>(std::forward<Tuple>(args))...);
+}
+
+// Apply the function to a tuple of arguments.
+template <typename F, typename Tuple>
+auto Apply(F&& f, Tuple&& args)
+ -> decltype(ApplyImpl(std::forward<F>(f), std::forward<Tuple>(args),
+ MakeIndexSequence<std::tuple_size<Tuple>::value>())) {
+ return ApplyImpl(std::forward<F>(f), std::forward<Tuple>(args),
+ MakeIndexSequence<std::tuple_size<Tuple>::value>());
+}
+
+// Template struct Function<F>, where F must be a function type, contains
+// the following typedefs:
+//
+// Result: the function's return type.
+// Arg<N>: the type of the N-th argument, where N starts with 0.
+// ArgumentTuple: the tuple type consisting of all parameters of F.
+// ArgumentMatcherTuple: the tuple type consisting of Matchers for all
+// parameters of F.
+// MakeResultVoid: the function type obtained by substituting void
+// for the return type of F.
+// MakeResultIgnoredValue:
+// the function type obtained by substituting Something
+// for the return type of F.
+template <typename T>
+struct Function;
+
+template <typename R, typename... Args>
+struct Function<R(Args...)> {
+ using Result = R;
+ static constexpr size_t ArgumentCount = sizeof...(Args);
+ template <size_t I>
+ using Arg = ElemFromList<I, typename MakeIndexSequence<sizeof...(Args)>::type,
+ Args...>;
+ using ArgumentTuple = std::tuple<Args...>;
+ using ArgumentMatcherTuple = std::tuple<Matcher<Args>...>;
+ using MakeResultVoid = void(Args...);
+ using MakeResultIgnoredValue = IgnoredValue(Args...);
+};
+
+template <typename R, typename... Args>
+constexpr size_t Function<R(Args...)>::ArgumentCount;
+
+#ifdef _MSC_VER
+# pragma warning(pop)
+#endif
} // namespace internal
} // namespace testing
#endif // GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_INTERNAL_UTILS_H_
-
diff --git a/extern/gmock/include/gmock/internal/gmock-port.h b/extern/gmock/include/gmock/internal/gmock-port.h
index 63f4a6802e8..063e2929acd 100644
--- a/extern/gmock/include/gmock/internal/gmock-port.h
+++ b/extern/gmock/include/gmock/internal/gmock-port.h
@@ -26,8 +26,7 @@
// 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: vadimb@google.com (Vadim Berman)
+
//
// Low-level types and utilities for porting Google Mock to various
// platforms. All macros ending with _ and symbols defined in an
@@ -36,6 +35,8 @@
// end with _ are part of Google Mock's public API and can be used by
// code outside Google Mock.
+// GOOGLETEST_CM0002 DO NOT DELETE
+
#ifndef GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_PORT_H_
#define GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_PORT_H_
@@ -50,19 +51,14 @@
// portability utilities to Google Test's gtest-port.h instead of
// here, as Google Mock depends on Google Test. Only add a utility
// here if it's truly specific to Google Mock.
-#include "gtest/internal/gtest-linked_ptr.h"
+
#include "gtest/internal/gtest-port.h"
#include "gmock/internal/custom/gmock-port.h"
-// To avoid conditional compilation everywhere, we make it
-// gmock-port.h's responsibility to #include the header implementing
-// tr1/tuple. gmock-port.h does this via gtest-port.h, which is
-// guaranteed to pull in the tuple header.
-
-// For MS Visual C++, check the compiler version. At least VS 2003 is
+// For MS Visual C++, check the compiler version. At least VS 2015 is
// required to compile Google Mock.
-#if defined(_MSC_VER) && _MSC_VER < 1310
-# error "At least Visual C++ 2003 (7.1) is required to compile Google Mock."
+#if defined(_MSC_VER) && _MSC_VER < 1900
+# error "At least Visual C++ 2015 (14.0) is required to compile Google Mock."
#endif
// Macro for referencing flags. This is public as we want the user to
@@ -72,18 +68,18 @@
#if !defined(GMOCK_DECLARE_bool_)
// Macros for declaring flags.
-#define GMOCK_DECLARE_bool_(name) extern GTEST_API_ bool GMOCK_FLAG(name)
-#define GMOCK_DECLARE_int32_(name) \
+# define GMOCK_DECLARE_bool_(name) extern GTEST_API_ bool GMOCK_FLAG(name)
+# define GMOCK_DECLARE_int32_(name) \
extern GTEST_API_ ::testing::internal::Int32 GMOCK_FLAG(name)
-#define GMOCK_DECLARE_string_(name) \
+# define GMOCK_DECLARE_string_(name) \
extern GTEST_API_ ::std::string GMOCK_FLAG(name)
// Macros for defining flags.
-#define GMOCK_DEFINE_bool_(name, default_val, doc) \
+# define GMOCK_DEFINE_bool_(name, default_val, doc) \
GTEST_API_ bool GMOCK_FLAG(name) = (default_val)
-#define GMOCK_DEFINE_int32_(name, default_val, doc) \
+# define GMOCK_DEFINE_int32_(name, default_val, doc) \
GTEST_API_ ::testing::internal::Int32 GMOCK_FLAG(name) = (default_val)
-#define GMOCK_DEFINE_string_(name, default_val, doc) \
+# define GMOCK_DEFINE_string_(name, default_val, doc) \
GTEST_API_ ::std::string GMOCK_FLAG(name) = (default_val)
#endif // !defined(GMOCK_DECLARE_bool_)
diff --git a/extern/gmock/include/gmock/internal/gmock-pp.h b/extern/gmock/include/gmock/internal/gmock-pp.h
new file mode 100644
index 00000000000..1ab80e1cdbe
--- /dev/null
+++ b/extern/gmock/include/gmock/internal/gmock-pp.h
@@ -0,0 +1,317 @@
+#ifndef THIRD_PARTY_GOOGLETEST_GOOGLEMOCK_INCLUDE_GMOCK_PP_H_
+#define THIRD_PARTY_GOOGLETEST_GOOGLEMOCK_INCLUDE_GMOCK_PP_H_
+
+#undef GMOCK_PP_INTERNAL_USE_MSVC
+#if defined(__clang__)
+#define GMOCK_PP_INTERNAL_USE_MSVC 0
+#elif defined(_MSC_VER)
+// TODO(iserna): Also verify tradional versus comformant preprocessor.
+static_assert(
+ _MSC_VER >= 1900,
+ "MSVC version not supported. There is support for MSVC 14.0 and above.");
+#define GMOCK_PP_INTERNAL_USE_MSVC 1
+#else
+#define GMOCK_PP_INTERNAL_USE_MSVC 0
+#endif
+
+// Expands and concatenates the arguments. Constructed macros reevaluate.
+#define GMOCK_PP_CAT(_1, _2) GMOCK_PP_INTERNAL_CAT(_1, _2)
+
+// Expands and stringifies the only argument.
+#define GMOCK_PP_STRINGIZE(...) GMOCK_PP_INTERNAL_STRINGIZE(__VA_ARGS__)
+
+// Returns empty. Given a variadic number of arguments.
+#define GMOCK_PP_EMPTY(...)
+
+// Returns a comma. Given a variadic number of arguments.
+#define GMOCK_PP_COMMA(...) ,
+
+// Returns the only argument.
+#define GMOCK_PP_IDENTITY(_1) _1
+
+// MSVC preprocessor collapses __VA_ARGS__ in a single argument, we use a
+// CAT-like directive to force correct evaluation. Each macro has its own.
+#if GMOCK_PP_INTERNAL_USE_MSVC
+
+// Evaluates to the number of arguments after expansion.
+//
+// #define PAIR x, y
+//
+// GMOCK_PP_NARG() => 1
+// GMOCK_PP_NARG(x) => 1
+// GMOCK_PP_NARG(x, y) => 2
+// GMOCK_PP_NARG(PAIR) => 2
+//
+// Requires: the number of arguments after expansion is at most 15.
+#define GMOCK_PP_NARG(...) \
+ GMOCK_PP_INTERNAL_NARG_CAT( \
+ GMOCK_PP_INTERNAL_INTERNAL_16TH(__VA_ARGS__, 15, 14, 13, 12, 11, 10, 9, \
+ 8, 7, 6, 5, 4, 3, 2, 1), )
+
+// Returns 1 if the expansion of arguments has an unprotected comma. Otherwise
+// returns 0. Requires no more than 15 unprotected commas.
+#define GMOCK_PP_HAS_COMMA(...) \
+ GMOCK_PP_INTERNAL_HAS_COMMA_CAT( \
+ GMOCK_PP_INTERNAL_INTERNAL_16TH(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
+ 1, 1, 1, 1, 1, 0), )
+// Returns the first argument.
+#define GMOCK_PP_HEAD(...) \
+ GMOCK_PP_INTERNAL_HEAD_CAT(GMOCK_PP_INTERNAL_HEAD(__VA_ARGS__), )
+
+// Returns the tail. A variadic list of all arguments minus the first. Requires
+// at least one argument.
+#define GMOCK_PP_TAIL(...) \
+ GMOCK_PP_INTERNAL_TAIL_CAT(GMOCK_PP_INTERNAL_TAIL(__VA_ARGS__), )
+
+// Calls CAT(_Macro, NARG(__VA_ARGS__))(__VA_ARGS__)
+#define GMOCK_PP_VARIADIC_CALL(_Macro, ...) \
+ GMOCK_PP_INTERNAL_VARIADIC_CALL_CAT( \
+ GMOCK_PP_CAT(_Macro, GMOCK_PP_NARG(__VA_ARGS__))(__VA_ARGS__), )
+
+#else // GMOCK_PP_INTERNAL_USE_MSVC
+
+#define GMOCK_PP_NARG(...) \
+ GMOCK_PP_INTERNAL_INTERNAL_16TH(__VA_ARGS__, 15, 14, 13, 12, 11, 10, 9, 8, \
+ 7, 6, 5, 4, 3, 2, 1)
+#define GMOCK_PP_HAS_COMMA(...) \
+ GMOCK_PP_INTERNAL_INTERNAL_16TH(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
+ 1, 1, 1, 1, 0)
+#define GMOCK_PP_HEAD(...) GMOCK_PP_INTERNAL_HEAD(__VA_ARGS__)
+#define GMOCK_PP_TAIL(...) GMOCK_PP_INTERNAL_TAIL(__VA_ARGS__)
+#define GMOCK_PP_VARIADIC_CALL(_Macro, ...) \
+ GMOCK_PP_CAT(_Macro, GMOCK_PP_NARG(__VA_ARGS__))(__VA_ARGS__)
+
+#endif // GMOCK_PP_INTERNAL_USE_MSVC
+
+// If the arguments after expansion have no tokens, evaluates to `1`. Otherwise
+// evaluates to `0`.
+//
+// Requires: * the number of arguments after expansion is at most 15.
+// * If the argument is a macro, it must be able to be called with one
+// argument.
+//
+// Implementation details:
+//
+// There is one case when it generates a compile error: if the argument is macro
+// that cannot be called with one argument.
+//
+// #define M(a, b) // it doesn't matter what it expands to
+//
+// // Expected: expands to `0`.
+// // Actual: compile error.
+// GMOCK_PP_IS_EMPTY(M)
+//
+// There are 4 cases tested:
+//
+// * __VA_ARGS__ possible expansion has no unparen'd commas. Expected 0.
+// * __VA_ARGS__ possible expansion is not enclosed in parenthesis. Expected 0.
+// * __VA_ARGS__ possible expansion is not a macro that ()-evaluates to a comma.
+// Expected 0
+// * __VA_ARGS__ is empty, or has unparen'd commas, or is enclosed in
+// parenthesis, or is a macro that ()-evaluates to comma. Expected 1.
+//
+// We trigger detection on '0001', i.e. on empty.
+#define GMOCK_PP_IS_EMPTY(...) \
+ GMOCK_PP_INTERNAL_IS_EMPTY(GMOCK_PP_HAS_COMMA(__VA_ARGS__), \
+ GMOCK_PP_HAS_COMMA(GMOCK_PP_COMMA __VA_ARGS__), \
+ GMOCK_PP_HAS_COMMA(__VA_ARGS__()), \
+ GMOCK_PP_HAS_COMMA(GMOCK_PP_COMMA __VA_ARGS__()))
+
+// Evaluates to _Then if _Cond is 1 and _Else if _Cond is 0.
+#define GMOCK_PP_IF(_Cond, _Then, _Else) \
+ GMOCK_PP_CAT(GMOCK_PP_INTERNAL_IF_, _Cond)(_Then, _Else)
+
+// Evaluates to the number of arguments after expansion. Identifies 'empty' as
+// 0.
+//
+// #define PAIR x, y
+//
+// GMOCK_PP_NARG0() => 0
+// GMOCK_PP_NARG0(x) => 1
+// GMOCK_PP_NARG0(x, y) => 2
+// GMOCK_PP_NARG0(PAIR) => 2
+//
+// Requires: * the number of arguments after expansion is at most 15.
+// * If the argument is a macro, it must be able to be called with one
+// argument.
+#define GMOCK_PP_NARG0(...) \
+ GMOCK_PP_IF(GMOCK_PP_IS_EMPTY(__VA_ARGS__), 0, GMOCK_PP_NARG(__VA_ARGS__))
+
+// Expands to 1 if the first argument starts with something in parentheses,
+// otherwise to 0.
+#define GMOCK_PP_IS_BEGIN_PARENS(...) \
+ GMOCK_PP_INTERNAL_ALTERNATE_HEAD( \
+ GMOCK_PP_CAT(GMOCK_PP_INTERNAL_IBP_IS_VARIADIC_R_, \
+ GMOCK_PP_INTERNAL_IBP_IS_VARIADIC_C __VA_ARGS__))
+
+// Expands to 1 is there is only one argument and it is enclosed in parentheses.
+#define GMOCK_PP_IS_ENCLOSED_PARENS(...) \
+ GMOCK_PP_IF(GMOCK_PP_IS_BEGIN_PARENS(__VA_ARGS__), \
+ GMOCK_PP_IS_EMPTY(GMOCK_PP_EMPTY __VA_ARGS__), 0)
+
+// Remove the parens, requires GMOCK_PP_IS_ENCLOSED_PARENS(args) => 1.
+#define GMOCK_PP_REMOVE_PARENS(...) GMOCK_PP_INTERNAL_REMOVE_PARENS __VA_ARGS__
+
+// Expands to _Macro(0, _Data, e1) _Macro(1, _Data, e2) ... _Macro(K -1, _Data,
+// eK) as many of GMOCK_INTERNAL_NARG0 _Tuple.
+// Requires: * |_Macro| can be called with 3 arguments.
+// * |_Tuple| expansion has no more than 15 elements.
+#define GMOCK_PP_FOR_EACH(_Macro, _Data, _Tuple) \
+ GMOCK_PP_CAT(GMOCK_PP_INTERNAL_FOR_EACH_IMPL_, GMOCK_PP_NARG0 _Tuple) \
+ (0, _Macro, _Data, _Tuple)
+
+// Expands to _Macro(0, _Data, ) _Macro(1, _Data, ) ... _Macro(K - 1, _Data, )
+// Empty if _K = 0.
+// Requires: * |_Macro| can be called with 3 arguments.
+// * |_K| literal between 0 and 15
+#define GMOCK_PP_REPEAT(_Macro, _Data, _N) \
+ GMOCK_PP_CAT(GMOCK_PP_INTERNAL_FOR_EACH_IMPL_, _N) \
+ (0, _Macro, _Data, GMOCK_PP_INTENRAL_EMPTY_TUPLE)
+
+// Increments the argument, requires the argument to be between 0 and 15.
+#define GMOCK_PP_INC(_i) GMOCK_PP_CAT(GMOCK_PP_INTERNAL_INC_, _i)
+
+// Returns comma if _i != 0. Requires _i to be between 0 and 15.
+#define GMOCK_PP_COMMA_IF(_i) GMOCK_PP_CAT(GMOCK_PP_INTERNAL_COMMA_IF_, _i)
+
+// Internal details follow. Do not use any of these symbols outside of this
+// file or we will break your code.
+#define GMOCK_PP_INTENRAL_EMPTY_TUPLE (, , , , , , , , , , , , , , , )
+#define GMOCK_PP_INTERNAL_CAT(_1, _2) _1##_2
+#define GMOCK_PP_INTERNAL_STRINGIZE(...) #__VA_ARGS__
+#define GMOCK_PP_INTERNAL_INTERNAL_16TH(_1, _2, _3, _4, _5, _6, _7, _8, _9, \
+ _10, _11, _12, _13, _14, _15, _16, \
+ ...) \
+ _16
+#define GMOCK_PP_INTERNAL_CAT_5(_1, _2, _3, _4, _5) _1##_2##_3##_4##_5
+#define GMOCK_PP_INTERNAL_IS_EMPTY(_1, _2, _3, _4) \
+ GMOCK_PP_HAS_COMMA(GMOCK_PP_INTERNAL_CAT_5(GMOCK_PP_INTERNAL_IS_EMPTY_CASE_, \
+ _1, _2, _3, _4))
+#define GMOCK_PP_INTERNAL_IS_EMPTY_CASE_0001 ,
+#define GMOCK_PP_INTERNAL_IF_1(_Then, _Else) _Then
+#define GMOCK_PP_INTERNAL_IF_0(_Then, _Else) _Else
+#define GMOCK_PP_INTERNAL_HEAD(_1, ...) _1
+#define GMOCK_PP_INTERNAL_TAIL(_1, ...) __VA_ARGS__
+
+#if GMOCK_PP_INTERNAL_USE_MSVC
+#define GMOCK_PP_INTERNAL_NARG_CAT(_1, _2) GMOCK_PP_INTERNAL_NARG_CAT_I(_1, _2)
+#define GMOCK_PP_INTERNAL_HEAD_CAT(_1, _2) GMOCK_PP_INTERNAL_HEAD_CAT_I(_1, _2)
+#define GMOCK_PP_INTERNAL_HAS_COMMA_CAT(_1, _2) \
+ GMOCK_PP_INTERNAL_HAS_COMMA_CAT_I(_1, _2)
+#define GMOCK_PP_INTERNAL_TAIL_CAT(_1, _2) GMOCK_PP_INTERNAL_TAIL_CAT_I(_1, _2)
+#define GMOCK_PP_INTERNAL_VARIADIC_CALL_CAT(_1, _2) \
+ GMOCK_PP_INTERNAL_VARIADIC_CALL_CAT_I(_1, _2)
+#define GMOCK_PP_INTERNAL_NARG_CAT_I(_1, _2) _1##_2
+#define GMOCK_PP_INTERNAL_HEAD_CAT_I(_1, _2) _1##_2
+#define GMOCK_PP_INTERNAL_HAS_COMMA_CAT_I(_1, _2) _1##_2
+#define GMOCK_PP_INTERNAL_TAIL_CAT_I(_1, _2) _1##_2
+#define GMOCK_PP_INTERNAL_VARIADIC_CALL_CAT_I(_1, _2) _1##_2
+#define GMOCK_PP_INTERNAL_ALTERNATE_HEAD(...) \
+ GMOCK_PP_INTERNAL_ALTERNATE_HEAD_CAT(GMOCK_PP_HEAD(__VA_ARGS__), )
+#define GMOCK_PP_INTERNAL_ALTERNATE_HEAD_CAT(_1, _2) \
+ GMOCK_PP_INTERNAL_ALTERNATE_HEAD_CAT_I(_1, _2)
+#define GMOCK_PP_INTERNAL_ALTERNATE_HEAD_CAT_I(_1, _2) _1##_2
+#else // GMOCK_PP_INTERNAL_USE_MSVC
+#define GMOCK_PP_INTERNAL_ALTERNATE_HEAD(...) GMOCK_PP_HEAD(__VA_ARGS__)
+#endif // GMOCK_PP_INTERNAL_USE_MSVC
+
+#define GMOCK_PP_INTERNAL_IBP_IS_VARIADIC_C(...) 1 _
+#define GMOCK_PP_INTERNAL_IBP_IS_VARIADIC_R_1 1,
+#define GMOCK_PP_INTERNAL_IBP_IS_VARIADIC_R_GMOCK_PP_INTERNAL_IBP_IS_VARIADIC_C \
+ 0,
+#define GMOCK_PP_INTERNAL_REMOVE_PARENS(...) __VA_ARGS__
+#define GMOCK_PP_INTERNAL_INC_0 1
+#define GMOCK_PP_INTERNAL_INC_1 2
+#define GMOCK_PP_INTERNAL_INC_2 3
+#define GMOCK_PP_INTERNAL_INC_3 4
+#define GMOCK_PP_INTERNAL_INC_4 5
+#define GMOCK_PP_INTERNAL_INC_5 6
+#define GMOCK_PP_INTERNAL_INC_6 7
+#define GMOCK_PP_INTERNAL_INC_7 8
+#define GMOCK_PP_INTERNAL_INC_8 9
+#define GMOCK_PP_INTERNAL_INC_9 10
+#define GMOCK_PP_INTERNAL_INC_10 11
+#define GMOCK_PP_INTERNAL_INC_11 12
+#define GMOCK_PP_INTERNAL_INC_12 13
+#define GMOCK_PP_INTERNAL_INC_13 14
+#define GMOCK_PP_INTERNAL_INC_14 15
+#define GMOCK_PP_INTERNAL_INC_15 16
+#define GMOCK_PP_INTERNAL_COMMA_IF_0
+#define GMOCK_PP_INTERNAL_COMMA_IF_1 ,
+#define GMOCK_PP_INTERNAL_COMMA_IF_2 ,
+#define GMOCK_PP_INTERNAL_COMMA_IF_3 ,
+#define GMOCK_PP_INTERNAL_COMMA_IF_4 ,
+#define GMOCK_PP_INTERNAL_COMMA_IF_5 ,
+#define GMOCK_PP_INTERNAL_COMMA_IF_6 ,
+#define GMOCK_PP_INTERNAL_COMMA_IF_7 ,
+#define GMOCK_PP_INTERNAL_COMMA_IF_8 ,
+#define GMOCK_PP_INTERNAL_COMMA_IF_9 ,
+#define GMOCK_PP_INTERNAL_COMMA_IF_10 ,
+#define GMOCK_PP_INTERNAL_COMMA_IF_11 ,
+#define GMOCK_PP_INTERNAL_COMMA_IF_12 ,
+#define GMOCK_PP_INTERNAL_COMMA_IF_13 ,
+#define GMOCK_PP_INTERNAL_COMMA_IF_14 ,
+#define GMOCK_PP_INTERNAL_COMMA_IF_15 ,
+#define GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, _element) \
+ _Macro(_i, _Data, _element)
+#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_0(_i, _Macro, _Data, _Tuple)
+#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_1(_i, _Macro, _Data, _Tuple) \
+ GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, GMOCK_PP_HEAD _Tuple)
+#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_2(_i, _Macro, _Data, _Tuple) \
+ GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, GMOCK_PP_HEAD _Tuple) \
+ GMOCK_PP_INTERNAL_FOR_EACH_IMPL_1(GMOCK_PP_INC(_i), _Macro, _Data, \
+ (GMOCK_PP_TAIL _Tuple))
+#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_3(_i, _Macro, _Data, _Tuple) \
+ GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, GMOCK_PP_HEAD _Tuple) \
+ GMOCK_PP_INTERNAL_FOR_EACH_IMPL_2(GMOCK_PP_INC(_i), _Macro, _Data, \
+ (GMOCK_PP_TAIL _Tuple))
+#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_4(_i, _Macro, _Data, _Tuple) \
+ GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, GMOCK_PP_HEAD _Tuple) \
+ GMOCK_PP_INTERNAL_FOR_EACH_IMPL_3(GMOCK_PP_INC(_i), _Macro, _Data, \
+ (GMOCK_PP_TAIL _Tuple))
+#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_5(_i, _Macro, _Data, _Tuple) \
+ GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, GMOCK_PP_HEAD _Tuple) \
+ GMOCK_PP_INTERNAL_FOR_EACH_IMPL_4(GMOCK_PP_INC(_i), _Macro, _Data, \
+ (GMOCK_PP_TAIL _Tuple))
+#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_6(_i, _Macro, _Data, _Tuple) \
+ GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, GMOCK_PP_HEAD _Tuple) \
+ GMOCK_PP_INTERNAL_FOR_EACH_IMPL_5(GMOCK_PP_INC(_i), _Macro, _Data, \
+ (GMOCK_PP_TAIL _Tuple))
+#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_7(_i, _Macro, _Data, _Tuple) \
+ GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, GMOCK_PP_HEAD _Tuple) \
+ GMOCK_PP_INTERNAL_FOR_EACH_IMPL_6(GMOCK_PP_INC(_i), _Macro, _Data, \
+ (GMOCK_PP_TAIL _Tuple))
+#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_8(_i, _Macro, _Data, _Tuple) \
+ GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, GMOCK_PP_HEAD _Tuple) \
+ GMOCK_PP_INTERNAL_FOR_EACH_IMPL_7(GMOCK_PP_INC(_i), _Macro, _Data, \
+ (GMOCK_PP_TAIL _Tuple))
+#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_9(_i, _Macro, _Data, _Tuple) \
+ GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, GMOCK_PP_HEAD _Tuple) \
+ GMOCK_PP_INTERNAL_FOR_EACH_IMPL_8(GMOCK_PP_INC(_i), _Macro, _Data, \
+ (GMOCK_PP_TAIL _Tuple))
+#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_10(_i, _Macro, _Data, _Tuple) \
+ GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, GMOCK_PP_HEAD _Tuple) \
+ GMOCK_PP_INTERNAL_FOR_EACH_IMPL_9(GMOCK_PP_INC(_i), _Macro, _Data, \
+ (GMOCK_PP_TAIL _Tuple))
+#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_11(_i, _Macro, _Data, _Tuple) \
+ GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, GMOCK_PP_HEAD _Tuple) \
+ GMOCK_PP_INTERNAL_FOR_EACH_IMPL_10(GMOCK_PP_INC(_i), _Macro, _Data, \
+ (GMOCK_PP_TAIL _Tuple))
+#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_12(_i, _Macro, _Data, _Tuple) \
+ GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, GMOCK_PP_HEAD _Tuple) \
+ GMOCK_PP_INTERNAL_FOR_EACH_IMPL_11(GMOCK_PP_INC(_i), _Macro, _Data, \
+ (GMOCK_PP_TAIL _Tuple))
+#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_13(_i, _Macro, _Data, _Tuple) \
+ GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, GMOCK_PP_HEAD _Tuple) \
+ GMOCK_PP_INTERNAL_FOR_EACH_IMPL_12(GMOCK_PP_INC(_i), _Macro, _Data, \
+ (GMOCK_PP_TAIL _Tuple))
+#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_14(_i, _Macro, _Data, _Tuple) \
+ GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, GMOCK_PP_HEAD _Tuple) \
+ GMOCK_PP_INTERNAL_FOR_EACH_IMPL_13(GMOCK_PP_INC(_i), _Macro, _Data, \
+ (GMOCK_PP_TAIL _Tuple))
+#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_15(_i, _Macro, _Data, _Tuple) \
+ GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, GMOCK_PP_HEAD _Tuple) \
+ GMOCK_PP_INTERNAL_FOR_EACH_IMPL_14(GMOCK_PP_INC(_i), _Macro, _Data, \
+ (GMOCK_PP_TAIL _Tuple))
+
+#endif // THIRD_PARTY_GOOGLETEST_GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_PP_H_
diff --git a/extern/gmock/src/gmock-all.cc b/extern/gmock/src/gmock-all.cc
index 7aebce7afef..e43c9b7b4c1 100644
--- a/extern/gmock/src/gmock-all.cc
+++ b/extern/gmock/src/gmock-all.cc
@@ -26,8 +26,7 @@
// 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: wan@google.com (Zhanyong Wan)
+
//
// Google C++ Mocking Framework (Google Mock)
//
diff --git a/extern/gmock/src/gmock-cardinalities.cc b/extern/gmock/src/gmock-cardinalities.cc
index 50ec7286eeb..7463f438323 100644
--- a/extern/gmock/src/gmock-cardinalities.cc
+++ b/extern/gmock/src/gmock-cardinalities.cc
@@ -26,8 +26,7 @@
// 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: wan@google.com (Zhanyong Wan)
+
// Google Mock - a framework for writing C++ mock classes.
//
@@ -71,18 +70,18 @@ class BetweenCardinalityImpl : public CardinalityInterface {
// Conservative estimate on the lower/upper bound of the number of
// calls allowed.
- virtual int ConservativeLowerBound() const { return min_; }
- virtual int ConservativeUpperBound() const { return max_; }
+ int ConservativeLowerBound() const override { return min_; }
+ int ConservativeUpperBound() const override { return max_; }
- virtual bool IsSatisfiedByCallCount(int call_count) const {
+ bool IsSatisfiedByCallCount(int call_count) const override {
return min_ <= call_count && call_count <= max_;
}
- virtual bool IsSaturatedByCallCount(int call_count) const {
+ bool IsSaturatedByCallCount(int call_count) const override {
return call_count >= max_;
}
- virtual void DescribeTo(::std::ostream* os) const;
+ void DescribeTo(::std::ostream* os) const override;
private:
const int min_;
@@ -92,7 +91,7 @@ class BetweenCardinalityImpl : public CardinalityInterface {
};
// Formats "n times" in a human-friendly way.
-inline internal::string FormatTimes(int n) {
+inline std::string FormatTimes(int n) {
if (n == 1) {
return "once";
} else if (n == 2) {
diff --git a/extern/gmock/src/gmock-internal-utils.cc b/extern/gmock/src/gmock-internal-utils.cc
index fb5308018a7..e5b547981d1 100644
--- a/extern/gmock/src/gmock-internal-utils.cc
+++ b/extern/gmock/src/gmock-internal-utils.cc
@@ -26,8 +26,7 @@
// 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: wan@google.com (Zhanyong Wan)
+
// Google Mock - a framework for writing C++ mock classes.
//
@@ -47,12 +46,31 @@
namespace testing {
namespace internal {
+// Joins a vector of strings as if they are fields of a tuple; returns
+// the joined string.
+GTEST_API_ std::string JoinAsTuple(const Strings& fields) {
+ switch (fields.size()) {
+ case 0:
+ return "";
+ case 1:
+ return fields[0];
+ default:
+ std::string result = "(" + fields[0];
+ for (size_t i = 1; i < fields.size(); i++) {
+ result += ", ";
+ result += fields[i];
+ }
+ result += ")";
+ return result;
+ }
+}
+
// Converts an identifier name to a space-separated list of lower-case
// words. Each maximum substring of the form [A-Za-z][a-z]*|\d+ is
// treated as one word. For example, both "FooBar123" and
// "foo_bar_123" are converted to "foo bar 123".
-GTEST_API_ string ConvertIdentifierNameToWords(const char* id_name) {
- string result;
+GTEST_API_ std::string ConvertIdentifierNameToWords(const char* id_name) {
+ std::string result;
char prev_char = '\0';
for (const char* p = id_name; *p != '\0'; prev_char = *(p++)) {
// We don't care about the current locale as the input is
@@ -71,12 +89,12 @@ GTEST_API_ string ConvertIdentifierNameToWords(const char* id_name) {
}
// This class reports Google Mock failures as Google Test failures. A
-// user can define another class in a similar fashion if he intends to
+// user can define another class in a similar fashion if they intend to
// use Google Mock with a testing framework other than Google Test.
class GoogleTestFailureReporter : public FailureReporterInterface {
public:
- virtual void ReportFailure(FailureType type, const char* file, int line,
- const string& message) {
+ void ReportFailure(FailureType type, const char* file, int line,
+ const std::string& message) override {
AssertHelper(type == kFatal ?
TestPartResult::kFatalFailure :
TestPartResult::kNonFatalFailure,
@@ -105,8 +123,8 @@ GTEST_API_ FailureReporterInterface* GetFailureReporter() {
// Protects global resources (stdout in particular) used by Log().
static GTEST_DEFINE_STATIC_MUTEX_(g_log_mutex);
-// Returns true iff a log with the given severity is visible according
-// to the --gmock_verbose flag.
+// Returns true if and only if a log with the given severity is visible
+// according to the --gmock_verbose flag.
GTEST_API_ bool LogIsVisible(LogSeverity severity) {
if (GMOCK_FLAG(verbose) == kInfoVerbosity) {
// Always show the log if --gmock_verbose=info.
@@ -121,15 +139,14 @@ GTEST_API_ bool LogIsVisible(LogSeverity severity) {
}
}
-// Prints the given message to stdout iff 'severity' >= the level
+// Prints the given message to stdout if and only if 'severity' >= the level
// specified by the --gmock_verbose flag. If stack_frames_to_skip >=
// 0, also prints the stack trace excluding the top
// stack_frames_to_skip frames. In opt mode, any positive
// stack_frames_to_skip is treated as 0, since we don't know which
// function calls will be inlined by the compiler and need to be
// conservative.
-GTEST_API_ void Log(LogSeverity severity,
- const string& message,
+GTEST_API_ void Log(LogSeverity severity, const std::string& message,
int stack_frames_to_skip) {
if (!LogIsVisible(severity))
return;
@@ -137,9 +154,6 @@ GTEST_API_ void Log(LogSeverity severity,
// Ensures that logs from different threads don't interleave.
MutexLock l(&g_log_mutex);
- // "using ::std::cout;" doesn't work with Symbian's STLport, where cout is a
- // macro.
-
if (severity == kWarning) {
// Prints a GMOCK WARNING marker to make the warnings easily searchable.
std::cout << "\nGMOCK WARNING:";
@@ -170,5 +184,17 @@ GTEST_API_ void Log(LogSeverity severity,
std::cout << ::std::flush;
}
+GTEST_API_ WithoutMatchers GetWithoutMatchers() { return WithoutMatchers(); }
+
+GTEST_API_ void IllegalDoDefault(const char* file, int line) {
+ internal::Assert(
+ false, file, line,
+ "You are using DoDefault() inside a composite action like "
+ "DoAll() or WithArgs(). This is not supported for technical "
+ "reasons. Please instead spell out the default action, or "
+ "assign the default action to an Action variable and use "
+ "the variable in various places.");
+}
+
} // namespace internal
} // namespace testing
diff --git a/extern/gmock/src/gmock-matchers.cc b/extern/gmock/src/gmock-matchers.cc
index e7424510fca..4a3f7af2351 100644
--- a/extern/gmock/src/gmock-matchers.cc
+++ b/extern/gmock/src/gmock-matchers.cc
@@ -26,8 +26,7 @@
// 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: wan@google.com (Zhanyong Wan)
+
// Google Mock - a framework for writing C++ mock classes.
//
@@ -38,98 +37,23 @@
#include "gmock/gmock-generated-matchers.h"
#include <string.h>
+#include <iostream>
#include <sstream>
#include <string>
namespace testing {
-
-// Constructs a matcher that matches a const string& whose value is
-// equal to s.
-Matcher<const internal::string&>::Matcher(const internal::string& s) {
- *this = Eq(s);
-}
-
-// Constructs a matcher that matches a const string& whose value is
-// equal to s.
-Matcher<const internal::string&>::Matcher(const char* s) {
- *this = Eq(internal::string(s));
-}
-
-// Constructs a matcher that matches a string whose value is equal to s.
-Matcher<internal::string>::Matcher(const internal::string& s) { *this = Eq(s); }
-
-// Constructs a matcher that matches a string whose value is equal to s.
-Matcher<internal::string>::Matcher(const char* s) {
- *this = Eq(internal::string(s));
-}
-
-#if GTEST_HAS_STRING_PIECE_
-// Constructs a matcher that matches a const StringPiece& whose value is
-// equal to s.
-Matcher<const StringPiece&>::Matcher(const internal::string& s) {
- *this = Eq(s);
-}
-
-// Constructs a matcher that matches a const StringPiece& whose value is
-// equal to s.
-Matcher<const StringPiece&>::Matcher(const char* s) {
- *this = Eq(internal::string(s));
-}
-
-// Constructs a matcher that matches a const StringPiece& whose value is
-// equal to s.
-Matcher<const StringPiece&>::Matcher(StringPiece s) {
- *this = Eq(s.ToString());
-}
-
-// Constructs a matcher that matches a StringPiece whose value is equal to s.
-Matcher<StringPiece>::Matcher(const internal::string& s) {
- *this = Eq(s);
-}
-
-// Constructs a matcher that matches a StringPiece whose value is equal to s.
-Matcher<StringPiece>::Matcher(const char* s) {
- *this = Eq(internal::string(s));
-}
-
-// Constructs a matcher that matches a StringPiece whose value is equal to s.
-Matcher<StringPiece>::Matcher(StringPiece s) {
- *this = Eq(s.ToString());
-}
-#endif // GTEST_HAS_STRING_PIECE_
-
namespace internal {
-// Joins a vector of strings as if they are fields of a tuple; returns
-// the joined string.
-GTEST_API_ string JoinAsTuple(const Strings& fields) {
- switch (fields.size()) {
- case 0:
- return "";
- case 1:
- return fields[0];
- default:
- string result = "(" + fields[0];
- for (size_t i = 1; i < fields.size(); i++) {
- result += ", ";
- result += fields[i];
- }
- result += ")";
- return result;
- }
-}
-
// Returns the description for a matcher defined using the MATCHER*()
// macro where the user-supplied description string is "", if
// 'negation' is false; otherwise returns the description of the
// negation of the matcher. 'param_values' contains a list of strings
// that are the print-out of the matcher's parameters.
-GTEST_API_ string FormatMatcherDescription(bool negation,
- const char* matcher_name,
- const Strings& param_values) {
- string result = ConvertIdentifierNameToWords(matcher_name);
- if (param_values.size() >= 1)
- result += " " + JoinAsTuple(param_values);
+GTEST_API_ std::string FormatMatcherDescription(bool negation,
+ const char* matcher_name,
+ const Strings& param_values) {
+ std::string result = ConvertIdentifierNameToWords(matcher_name);
+ if (param_values.size() >= 1) result += " " + JoinAsTuple(param_values);
return negation ? "not (" + result + ")" : result;
}
@@ -200,8 +124,7 @@ class MaxBipartiteMatchState {
explicit MaxBipartiteMatchState(const MatchMatrix& graph)
: graph_(&graph),
left_(graph_->LhsSize(), kUnused),
- right_(graph_->RhsSize(), kUnused) {
- }
+ right_(graph_->RhsSize(), kUnused) {}
// Returns the edges of a maximal match, each in the form {left, right}.
ElementMatcherPairs Compute() {
@@ -258,10 +181,8 @@ class MaxBipartiteMatchState {
//
bool TryAugment(size_t ilhs, ::std::vector<char>* seen) {
for (size_t irhs = 0; irhs < graph_->RhsSize(); ++irhs) {
- if ((*seen)[irhs])
- continue;
- if (!graph_->HasEdge(ilhs, irhs))
- continue;
+ if ((*seen)[irhs]) continue;
+ if (!graph_->HasEdge(ilhs, irhs)) continue;
// There's an available edge from ilhs to irhs.
(*seen)[irhs] = 1;
// Next a search is performed to determine whether
@@ -288,7 +209,7 @@ class MaxBipartiteMatchState {
// Each element of the left_ vector represents a left hand side node
// (i.e. an element) and each element of right_ is a right hand side
// node (i.e. a matcher). The values in the left_ vector indicate
- // outflow from that node to a node on the the right_ side. The values
+ // outflow from that node to a node on the right_ side. The values
// in the right_ indicate inflow, and specify which left_ node is
// feeding that right_ node, if any. For example, left_[3] == 1 means
// there's a flow from element #3 to matcher #1. Such a flow would also
@@ -304,8 +225,7 @@ class MaxBipartiteMatchState {
const size_t MaxBipartiteMatchState::kUnused;
-GTEST_API_ ElementMatcherPairs
-FindMaxBipartiteMatching(const MatchMatrix& g) {
+GTEST_API_ ElementMatcherPairs FindMaxBipartiteMatching(const MatchMatrix& g) {
return MaxBipartiteMatchState(g).Compute();
}
@@ -314,7 +234,7 @@ static void LogElementMatcherPairVec(const ElementMatcherPairs& pairs,
typedef ElementMatcherPairs::const_iterator Iter;
::std::ostream& os = *stream;
os << "{";
- const char *sep = "";
+ const char* sep = "";
for (Iter it = pairs.begin(); it != pairs.end(); ++it) {
os << sep << "\n ("
<< "element #" << it->first << ", "
@@ -324,38 +244,6 @@ static void LogElementMatcherPairVec(const ElementMatcherPairs& pairs,
os << "\n}";
}
-// Tries to find a pairing, and explains the result.
-GTEST_API_ bool FindPairing(const MatchMatrix& matrix,
- MatchResultListener* listener) {
- ElementMatcherPairs matches = FindMaxBipartiteMatching(matrix);
-
- size_t max_flow = matches.size();
- bool result = (max_flow == matrix.RhsSize());
-
- if (!result) {
- if (listener->IsInterested()) {
- *listener << "where no permutation of the elements can "
- "satisfy all matchers, and the closest match is "
- << max_flow << " of " << matrix.RhsSize()
- << " matchers with the pairings:\n";
- LogElementMatcherPairVec(matches, listener->stream());
- }
- return false;
- }
-
- if (matches.size() > 1) {
- if (listener->IsInterested()) {
- const char *sep = "where:\n";
- for (size_t mi = 0; mi < matches.size(); ++mi) {
- *listener << sep << " - element #" << matches[mi].first
- << " is matched by matcher #" << matches[mi].second;
- sep = ",\n";
- }
- }
- }
- return true;
-}
-
bool MatchMatrix::NextGraph() {
for (size_t ilhs = 0; ilhs < LhsSize(); ++ilhs) {
for (size_t irhs = 0; irhs < RhsSize(); ++irhs) {
@@ -379,9 +267,9 @@ void MatchMatrix::Randomize() {
}
}
-string MatchMatrix::DebugString() const {
+std::string MatchMatrix::DebugString() const {
::std::stringstream ss;
- const char *sep = "";
+ const char* sep = "";
for (size_t i = 0; i < LhsSize(); ++i) {
ss << sep;
for (size_t j = 0; j < RhsSize(); ++j) {
@@ -394,44 +282,83 @@ string MatchMatrix::DebugString() const {
void UnorderedElementsAreMatcherImplBase::DescribeToImpl(
::std::ostream* os) const {
- if (matcher_describers_.empty()) {
- *os << "is empty";
- return;
- }
- if (matcher_describers_.size() == 1) {
- *os << "has " << Elements(1) << " and that element ";
- matcher_describers_[0]->DescribeTo(os);
- return;
+ switch (match_flags()) {
+ case UnorderedMatcherRequire::ExactMatch:
+ if (matcher_describers_.empty()) {
+ *os << "is empty";
+ return;
+ }
+ if (matcher_describers_.size() == 1) {
+ *os << "has " << Elements(1) << " and that element ";
+ matcher_describers_[0]->DescribeTo(os);
+ return;
+ }
+ *os << "has " << Elements(matcher_describers_.size())
+ << " and there exists some permutation of elements such that:\n";
+ break;
+ case UnorderedMatcherRequire::Superset:
+ *os << "a surjection from elements to requirements exists such that:\n";
+ break;
+ case UnorderedMatcherRequire::Subset:
+ *os << "an injection from elements to requirements exists such that:\n";
+ break;
}
- *os << "has " << Elements(matcher_describers_.size())
- << " and there exists some permutation of elements such that:\n";
+
const char* sep = "";
for (size_t i = 0; i != matcher_describers_.size(); ++i) {
- *os << sep << " - element #" << i << " ";
+ *os << sep;
+ if (match_flags() == UnorderedMatcherRequire::ExactMatch) {
+ *os << " - element #" << i << " ";
+ } else {
+ *os << " - an element ";
+ }
matcher_describers_[i]->DescribeTo(os);
- sep = ", and\n";
+ if (match_flags() == UnorderedMatcherRequire::ExactMatch) {
+ sep = ", and\n";
+ } else {
+ sep = "\n";
+ }
}
}
void UnorderedElementsAreMatcherImplBase::DescribeNegationToImpl(
::std::ostream* os) const {
- if (matcher_describers_.empty()) {
- *os << "isn't empty";
- return;
- }
- if (matcher_describers_.size() == 1) {
- *os << "doesn't have " << Elements(1)
- << ", or has " << Elements(1) << " that ";
- matcher_describers_[0]->DescribeNegationTo(os);
- return;
+ switch (match_flags()) {
+ case UnorderedMatcherRequire::ExactMatch:
+ if (matcher_describers_.empty()) {
+ *os << "isn't empty";
+ return;
+ }
+ if (matcher_describers_.size() == 1) {
+ *os << "doesn't have " << Elements(1) << ", or has " << Elements(1)
+ << " that ";
+ matcher_describers_[0]->DescribeNegationTo(os);
+ return;
+ }
+ *os << "doesn't have " << Elements(matcher_describers_.size())
+ << ", or there exists no permutation of elements such that:\n";
+ break;
+ case UnorderedMatcherRequire::Superset:
+ *os << "no surjection from elements to requirements exists such that:\n";
+ break;
+ case UnorderedMatcherRequire::Subset:
+ *os << "no injection from elements to requirements exists such that:\n";
+ break;
}
- *os << "doesn't have " << Elements(matcher_describers_.size())
- << ", or there exists no permutation of elements such that:\n";
const char* sep = "";
for (size_t i = 0; i != matcher_describers_.size(); ++i) {
- *os << sep << " - element #" << i << " ";
+ *os << sep;
+ if (match_flags() == UnorderedMatcherRequire::ExactMatch) {
+ *os << " - element #" << i << " ";
+ } else {
+ *os << " - an element ";
+ }
matcher_describers_[i]->DescribeTo(os);
- sep = ", and\n";
+ if (match_flags() == UnorderedMatcherRequire::ExactMatch) {
+ sep = ", and\n";
+ } else {
+ sep = "\n";
+ }
}
}
@@ -440,11 +367,9 @@ void UnorderedElementsAreMatcherImplBase::DescribeNegationToImpl(
// and better error reporting.
// Returns false, writing an explanation to 'listener', if and only
// if the success criteria are not met.
-bool UnorderedElementsAreMatcherImplBase::
-VerifyAllElementsAndMatchersAreMatched(
- const ::std::vector<string>& element_printouts,
- const MatchMatrix& matrix,
- MatchResultListener* listener) const {
+bool UnorderedElementsAreMatcherImplBase::VerifyMatchMatrix(
+ const ::std::vector<std::string>& element_printouts,
+ const MatchMatrix& matrix, MatchResultListener* listener) const {
bool result = true;
::std::vector<char> element_matched(matrix.LhsSize(), 0);
::std::vector<char> matcher_matched(matrix.RhsSize(), 0);
@@ -457,12 +382,11 @@ VerifyAllElementsAndMatchersAreMatched(
}
}
- {
+ if (match_flags() & UnorderedMatcherRequire::Superset) {
const char* sep =
"where the following matchers don't match any elements:\n";
for (size_t mi = 0; mi < matcher_matched.size(); ++mi) {
- if (matcher_matched[mi])
- continue;
+ if (matcher_matched[mi]) continue;
result = false;
if (listener->IsInterested()) {
*listener << sep << "matcher #" << mi << ": ";
@@ -472,7 +396,7 @@ VerifyAllElementsAndMatchersAreMatched(
}
}
- {
+ if (match_flags() & UnorderedMatcherRequire::Subset) {
const char* sep =
"where the following elements don't match any matchers:\n";
const char* outer_sep = "";
@@ -480,8 +404,7 @@ VerifyAllElementsAndMatchersAreMatched(
outer_sep = "\nand ";
}
for (size_t ei = 0; ei < element_matched.size(); ++ei) {
- if (element_matched[ei])
- continue;
+ if (element_matched[ei]) continue;
result = false;
if (listener->IsInterested()) {
*listener << outer_sep << sep << "element #" << ei << ": "
@@ -494,5 +417,46 @@ VerifyAllElementsAndMatchersAreMatched(
return result;
}
+bool UnorderedElementsAreMatcherImplBase::FindPairing(
+ const MatchMatrix& matrix, MatchResultListener* listener) const {
+ ElementMatcherPairs matches = FindMaxBipartiteMatching(matrix);
+
+ size_t max_flow = matches.size();
+ if ((match_flags() & UnorderedMatcherRequire::Superset) &&
+ max_flow < matrix.RhsSize()) {
+ if (listener->IsInterested()) {
+ *listener << "where no permutation of the elements can satisfy all "
+ "matchers, and the closest match is "
+ << max_flow << " of " << matrix.RhsSize()
+ << " matchers with the pairings:\n";
+ LogElementMatcherPairVec(matches, listener->stream());
+ }
+ return false;
+ }
+ if ((match_flags() & UnorderedMatcherRequire::Subset) &&
+ max_flow < matrix.LhsSize()) {
+ if (listener->IsInterested()) {
+ *listener
+ << "where not all elements can be matched, and the closest match is "
+ << max_flow << " of " << matrix.RhsSize()
+ << " matchers with the pairings:\n";
+ LogElementMatcherPairVec(matches, listener->stream());
+ }
+ return false;
+ }
+
+ if (matches.size() > 1) {
+ if (listener->IsInterested()) {
+ const char* sep = "where:\n";
+ for (size_t mi = 0; mi < matches.size(); ++mi) {
+ *listener << sep << " - element #" << matches[mi].first
+ << " is matched by matcher #" << matches[mi].second;
+ sep = ",\n";
+ }
+ }
+ }
+ return true;
+}
+
} // namespace internal
} // namespace testing
diff --git a/extern/gmock/src/gmock-spec-builders.cc b/extern/gmock/src/gmock-spec-builders.cc
index 95513420707..f9d3434560b 100644
--- a/extern/gmock/src/gmock-spec-builders.cc
+++ b/extern/gmock/src/gmock-spec-builders.cc
@@ -26,8 +26,7 @@
// 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: wan@google.com (Zhanyong Wan)
+
// Google Mock - a framework for writing C++ mock classes.
//
@@ -39,8 +38,10 @@
#include <stdlib.h>
#include <iostream> // NOLINT
#include <map>
+#include <memory>
#include <set>
#include <string>
+#include <vector>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
@@ -48,6 +49,15 @@
# include <unistd.h> // NOLINT
#endif
+// Silence C4800 (C4800: 'int *const ': forcing value
+// to bool 'true' or 'false') for MSVC 15
+#ifdef _MSC_VER
+#if _MSC_VER == 1900
+# pragma warning(push)
+# pragma warning(disable:4800)
+#endif
+#endif
+
namespace testing {
namespace internal {
@@ -58,16 +68,15 @@ GTEST_API_ GTEST_DEFINE_STATIC_MUTEX_(g_gmock_mutex);
// Logs a message including file and line number information.
GTEST_API_ void LogWithLocation(testing::internal::LogSeverity severity,
const char* file, int line,
- const string& message) {
+ const std::string& message) {
::std::ostringstream s;
s << file << ":" << line << ": " << message << ::std::endl;
Log(severity, s.str(), 0);
}
// Constructs an ExpectationBase object.
-ExpectationBase::ExpectationBase(const char* a_file,
- int a_line,
- const string& a_source_text)
+ExpectationBase::ExpectationBase(const char* a_file, int a_line,
+ const std::string& a_source_text)
: file_(a_file),
line_(a_line),
source_text_(a_source_text),
@@ -100,26 +109,40 @@ void ExpectationBase::RetireAllPreRequisites()
return;
}
- for (ExpectationSet::const_iterator it = immediate_prerequisites_.begin();
- it != immediate_prerequisites_.end(); ++it) {
- ExpectationBase* const prerequisite = it->expectation_base().get();
- if (!prerequisite->is_retired()) {
- prerequisite->RetireAllPreRequisites();
- prerequisite->Retire();
+ ::std::vector<ExpectationBase*> expectations(1, this);
+ while (!expectations.empty()) {
+ ExpectationBase* exp = expectations.back();
+ expectations.pop_back();
+
+ for (ExpectationSet::const_iterator it =
+ exp->immediate_prerequisites_.begin();
+ it != exp->immediate_prerequisites_.end(); ++it) {
+ ExpectationBase* next = it->expectation_base().get();
+ if (!next->is_retired()) {
+ next->Retire();
+ expectations.push_back(next);
+ }
}
}
}
-// Returns true iff all pre-requisites of this expectation have been
-// satisfied.
+// Returns true if and only if all pre-requisites of this expectation
+// have been satisfied.
bool ExpectationBase::AllPrerequisitesAreSatisfied() const
GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
g_gmock_mutex.AssertHeld();
- for (ExpectationSet::const_iterator it = immediate_prerequisites_.begin();
- it != immediate_prerequisites_.end(); ++it) {
- if (!(it->expectation_base()->IsSatisfied()) ||
- !(it->expectation_base()->AllPrerequisitesAreSatisfied()))
- return false;
+ ::std::vector<const ExpectationBase*> expectations(1, this);
+ while (!expectations.empty()) {
+ const ExpectationBase* exp = expectations.back();
+ expectations.pop_back();
+
+ for (ExpectationSet::const_iterator it =
+ exp->immediate_prerequisites_.begin();
+ it != exp->immediate_prerequisites_.end(); ++it) {
+ const ExpectationBase* next = it->expectation_base().get();
+ if (!next->IsSatisfied()) return false;
+ expectations.push_back(next);
+ }
}
return true;
}
@@ -128,19 +151,28 @@ bool ExpectationBase::AllPrerequisitesAreSatisfied() const
void ExpectationBase::FindUnsatisfiedPrerequisites(ExpectationSet* result) const
GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
g_gmock_mutex.AssertHeld();
- for (ExpectationSet::const_iterator it = immediate_prerequisites_.begin();
- it != immediate_prerequisites_.end(); ++it) {
- if (it->expectation_base()->IsSatisfied()) {
- // If *it is satisfied and has a call count of 0, some of its
- // pre-requisites may not be satisfied yet.
- if (it->expectation_base()->call_count_ == 0) {
- it->expectation_base()->FindUnsatisfiedPrerequisites(result);
+ ::std::vector<const ExpectationBase*> expectations(1, this);
+ while (!expectations.empty()) {
+ const ExpectationBase* exp = expectations.back();
+ expectations.pop_back();
+
+ for (ExpectationSet::const_iterator it =
+ exp->immediate_prerequisites_.begin();
+ it != exp->immediate_prerequisites_.end(); ++it) {
+ const ExpectationBase* next = it->expectation_base().get();
+
+ if (next->IsSatisfied()) {
+ // If *it is satisfied and has a call count of 0, some of its
+ // pre-requisites may not be satisfied yet.
+ if (next->call_count_ == 0) {
+ expectations.push_back(next);
+ }
+ } else {
+ // Now that we know next is unsatisfied, we are not so interested
+ // in whether its pre-requisites are satisfied. Therefore we
+ // don't iterate into it here.
+ *result += *it;
}
- } else {
- // Now that we know *it is unsatisfied, we are not so interested
- // in whether its pre-requisites are satisfied. Therefore we
- // don't recursively call FindUnsatisfiedPrerequisites() here.
- *result += *it;
}
}
}
@@ -244,7 +276,7 @@ GTEST_API_ ThreadLocal<Sequence*> g_gmock_implicit_sequence;
// Reports an uninteresting call (whose description is in msg) in the
// manner specified by 'reaction'.
-void ReportUninterestingCall(CallReaction reaction, const string& msg) {
+void ReportUninterestingCall(CallReaction reaction, const std::string& msg) {
// Include a stack trace only if --gmock_verbose=info is specified.
const int stack_frames_to_skip =
GMOCK_FLAG(verbose) == kInfoVerbosity ? 3 : -1;
@@ -255,20 +287,22 @@ void ReportUninterestingCall(CallReaction reaction, const string& msg) {
case kWarn:
Log(kWarning,
msg +
- "\nNOTE: You can safely ignore the above warning unless this "
- "call should not happen. Do not suppress it by blindly adding "
- "an EXPECT_CALL() if you don't mean to enforce the call. "
- "See https://github.com/google/googletest/blob/master/googlemock/docs/CookBook.md#"
- "knowing-when-to-expect for details.\n",
+ "\nNOTE: You can safely ignore the above warning unless this "
+ "call should not happen. Do not suppress it by blindly adding "
+ "an EXPECT_CALL() if you don't mean to enforce the call. "
+ "See "
+ "https://github.com/google/googletest/blob/master/googlemock/"
+ "docs/cook_book.md#"
+ "knowing-when-to-expect for details.\n",
stack_frames_to_skip);
break;
default: // FAIL
- Expect(false, NULL, -1, msg);
+ Expect(false, nullptr, -1, msg);
}
}
UntypedFunctionMockerBase::UntypedFunctionMockerBase()
- : mock_obj_(NULL), name_("") {}
+ : mock_obj_(nullptr), name_("") {}
UntypedFunctionMockerBase::~UntypedFunctionMockerBase() {}
@@ -307,7 +341,7 @@ const void* UntypedFunctionMockerBase::MockObject() const
// We protect mock_obj_ under g_gmock_mutex in case this mock
// function is called from two threads concurrently.
MutexLock l(&g_gmock_mutex);
- Assert(mock_obj_ != NULL, __FILE__, __LINE__,
+ Assert(mock_obj_ != nullptr, __FILE__, __LINE__,
"MockObject() must not be called before RegisterOwner() or "
"SetOwnerAndName() has been called.");
mock_obj = mock_obj_;
@@ -324,7 +358,7 @@ const char* UntypedFunctionMockerBase::Name() const
// We protect name_ under g_gmock_mutex in case this mock
// function is called from two threads concurrently.
MutexLock l(&g_gmock_mutex);
- Assert(name_ != NULL, __FILE__, __LINE__,
+ Assert(name_ != nullptr, __FILE__, __LINE__,
"Name() must not be called before SetOwnerAndName() has "
"been called.");
name = name_;
@@ -335,9 +369,10 @@ const char* UntypedFunctionMockerBase::Name() const
// Calculates the result of invoking this mock function with the given
// arguments, prints it, and returns it. The caller is responsible
// for deleting the result.
-UntypedActionResultHolderBase*
-UntypedFunctionMockerBase::UntypedInvokeWith(const void* const untyped_args)
- GTEST_LOCK_EXCLUDED_(g_gmock_mutex) {
+UntypedActionResultHolderBase* UntypedFunctionMockerBase::UntypedInvokeWith(
+ void* const untyped_args) GTEST_LOCK_EXCLUDED_(g_gmock_mutex) {
+ // See the definition of untyped_expectations_ for why access to it
+ // is unprotected here.
if (untyped_expectations_.size() == 0) {
// No expectation is set on this mock method - we have an
// uninteresting call.
@@ -349,23 +384,26 @@ UntypedFunctionMockerBase::UntypedInvokeWith(const void* const untyped_args)
const CallReaction reaction =
Mock::GetReactionOnUninterestingCalls(MockObject());
- // True iff we need to print this call's arguments and return
+ // True if and only if we need to print this call's arguments and return
// value. This definition must be kept in sync with
// the behavior of ReportUninterestingCall().
const bool need_to_report_uninteresting_call =
// If the user allows this uninteresting call, we print it
- // only when he wants informational messages.
+ // only when they want informational messages.
reaction == kAllow ? LogIsVisible(kInfo) :
- // If the user wants this to be a warning, we print it only
- // when he wants to see warnings.
- reaction == kWarn ? LogIsVisible(kWarning) :
- // Otherwise, the user wants this to be an error, and we
- // should always print detailed information in the error.
- true;
+ // If the user wants this to be a warning, we print
+ // it only when they want to see warnings.
+ reaction == kWarn
+ ? LogIsVisible(kWarning)
+ :
+ // Otherwise, the user wants this to be an error, and we
+ // should always print detailed information in the error.
+ true;
if (!need_to_report_uninteresting_call) {
// Perform the action without printing the call information.
- return this->UntypedPerformDefaultAction(untyped_args, "");
+ return this->UntypedPerformDefaultAction(
+ untyped_args, "Function call: " + std::string(Name()));
}
// Warns about the uninteresting call.
@@ -377,8 +415,7 @@ UntypedFunctionMockerBase::UntypedInvokeWith(const void* const untyped_args)
this->UntypedPerformDefaultAction(untyped_args, ss.str());
// Prints the function result.
- if (result != NULL)
- result->PrintAsActionResult(&ss);
+ if (result != nullptr) result->PrintAsActionResult(&ss);
ReportUninterestingCall(reaction, ss.str());
return result;
@@ -388,7 +425,7 @@ UntypedFunctionMockerBase::UntypedInvokeWith(const void* const untyped_args)
::std::stringstream ss;
::std::stringstream why;
::std::stringstream loc;
- const void* untyped_action = NULL;
+ const void* untyped_action = nullptr;
// The UntypedFindMatchingExpectation() function acquires and
// releases g_gmock_mutex.
@@ -396,19 +433,19 @@ UntypedFunctionMockerBase::UntypedInvokeWith(const void* const untyped_args)
this->UntypedFindMatchingExpectation(
untyped_args, &untyped_action, &is_excessive,
&ss, &why);
- const bool found = untyped_expectation != NULL;
+ const bool found = untyped_expectation != nullptr;
- // True iff we need to print the call's arguments and return value.
+ // True if and only if we need to print the call's arguments
+ // and return value.
// This definition must be kept in sync with the uses of Expect()
// and Log() in this function.
const bool need_to_report_call =
!found || is_excessive || LogIsVisible(kInfo);
if (!need_to_report_call) {
// Perform the action without printing the call information.
- return
- untyped_action == NULL ?
- this->UntypedPerformDefaultAction(untyped_args, "") :
- this->UntypedPerformAction(untyped_action, untyped_args);
+ return untyped_action == nullptr
+ ? this->UntypedPerformDefaultAction(untyped_args, "")
+ : this->UntypedPerformAction(untyped_action, untyped_args);
}
ss << " Function call: " << Name();
@@ -421,16 +458,15 @@ UntypedFunctionMockerBase::UntypedInvokeWith(const void* const untyped_args)
}
UntypedActionResultHolderBase* const result =
- untyped_action == NULL ?
- this->UntypedPerformDefaultAction(untyped_args, ss.str()) :
- this->UntypedPerformAction(untyped_action, untyped_args);
- if (result != NULL)
- result->PrintAsActionResult(&ss);
+ untyped_action == nullptr
+ ? this->UntypedPerformDefaultAction(untyped_args, ss.str())
+ : this->UntypedPerformAction(untyped_action, untyped_args);
+ if (result != nullptr) result->PrintAsActionResult(&ss);
ss << "\n" << why.str();
if (!found) {
// No expectation matches this call - reports a failure.
- Expect(false, NULL, -1, ss.str());
+ Expect(false, nullptr, -1, ss.str());
} else if (is_excessive) {
// We had an upper-bound violation and the failure message is in ss.
Expect(false, untyped_expectation->file(),
@@ -447,6 +483,8 @@ UntypedFunctionMockerBase::UntypedInvokeWith(const void* const untyped_args)
// Returns an Expectation object that references and co-owns exp,
// which must be an expectation on this mock function.
Expectation UntypedFunctionMockerBase::GetHandleOf(ExpectationBase* exp) {
+ // See the definition of untyped_expectations_ for why access to it
+ // is unprotected here.
for (UntypedExpectations::const_iterator it =
untyped_expectations_.begin();
it != untyped_expectations_.end(); ++it) {
@@ -509,6 +547,13 @@ bool UntypedFunctionMockerBase::VerifyAndClearExpectationsLocked()
return expectations_met;
}
+CallReaction intToCallReaction(int mock_behavior) {
+ if (mock_behavior >= kAllow && mock_behavior <= kFail) {
+ return static_cast<internal::CallReaction>(mock_behavior);
+ }
+ return kWarn;
+}
+
} // namespace internal
// Class Mock.
@@ -522,15 +567,15 @@ typedef std::set<internal::UntypedFunctionMockerBase*> FunctionMockers;
// expectations.
struct MockObjectState {
MockObjectState()
- : first_used_file(NULL), first_used_line(-1), leakable(false) {}
+ : first_used_file(nullptr), first_used_line(-1), leakable(false) {}
// Where in the source file an ON_CALL or EXPECT_CALL is first
// invoked on this mock object.
const char* first_used_file;
int first_used_line;
- ::std::string first_used_test_case;
+ ::std::string first_used_test_suite;
::std::string first_used_test;
- bool leakable; // true iff it's OK to leak the object.
+ bool leakable; // true if and only if it's OK to leak the object.
FunctionMockers function_mockers; // All registered methods of the object.
};
@@ -548,9 +593,6 @@ class MockObjectRegistry {
// object alive. Therefore we report any living object as test
// failure, unless the user explicitly asked us to ignore it.
~MockObjectRegistry() {
- // "using ::std::cout;" doesn't work with Symbian's STLport, where cout is
- // a macro.
-
if (!GMOCK_FLAG(catch_leaked_mocks))
return;
@@ -560,7 +602,7 @@ class MockObjectRegistry {
if (it->second.leakable) // The user said it's fine to leak this object.
continue;
- // TODO(wan@google.com): Print the type of the leaked object.
+ // FIXME: Print the type of the leaked object.
// This can help the user identify the leaked object.
std::cout << "\n";
const MockObjectState& state = it->second;
@@ -568,17 +610,23 @@ class MockObjectRegistry {
state.first_used_line);
std::cout << " ERROR: this mock object";
if (state.first_used_test != "") {
- std::cout << " (used in test " << state.first_used_test_case << "."
- << state.first_used_test << ")";
+ std::cout << " (used in test " << state.first_used_test_suite << "."
+ << state.first_used_test << ")";
}
std::cout << " should be deleted but never is. Its address is @"
<< it->first << ".";
leaked_count++;
}
if (leaked_count > 0) {
- std::cout << "\nERROR: " << leaked_count
- << " leaked mock " << (leaked_count == 1 ? "object" : "objects")
- << " found at program exit.\n";
+ std::cout << "\nERROR: " << leaked_count << " leaked mock "
+ << (leaked_count == 1 ? "object" : "objects")
+ << " found at program exit. Expectations on a mock object is "
+ "verified when the object is destructed. Leaking a mock "
+ "means that its expectations aren't verified, which is "
+ "usually a test bug. If you really intend to leak a mock, "
+ "you can suppress this error using "
+ "testing::Mock::AllowLeak(mock_object), or you may use a "
+ "fake or stub instead of a mock.\n";
std::cout.flush();
::std::cerr.flush();
// RUN_ALL_TESTS() has already returned when this destructor is
@@ -649,7 +697,8 @@ internal::CallReaction Mock::GetReactionOnUninterestingCalls(
GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) {
internal::MutexLock l(&internal::g_gmock_mutex);
return (g_uninteresting_call_reaction.count(mock_obj) == 0) ?
- internal::kDefault : g_uninteresting_call_reaction[mock_obj];
+ internal::intToCallReaction(GMOCK_FLAG(default_mock_behavior)) :
+ g_uninteresting_call_reaction[mock_obj];
}
// Tells Google Mock to ignore mock_obj when checking for leaked mock
@@ -670,7 +719,7 @@ bool Mock::VerifyAndClearExpectations(void* mock_obj)
}
// Verifies all expectations on the given mock object and clears its
-// default actions and expectations. Returns true iff the
+// default actions and expectations. Returns true if and only if the
// verification was successful.
bool Mock::VerifyAndClear(void* mock_obj)
GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) {
@@ -707,6 +756,19 @@ bool Mock::VerifyAndClearExpectationsLocked(void* mock_obj)
return expectations_met;
}
+bool Mock::IsNaggy(void* mock_obj)
+ GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) {
+ return Mock::GetReactionOnUninterestingCalls(mock_obj) == internal::kWarn;
+}
+bool Mock::IsNice(void* mock_obj)
+ GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) {
+ return Mock::GetReactionOnUninterestingCalls(mock_obj) == internal::kAllow;
+}
+bool Mock::IsStrict(void* mock_obj)
+ GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) {
+ return Mock::GetReactionOnUninterestingCalls(mock_obj) == internal::kFail;
+}
+
// Registers a mock object and a mock method it owns.
void Mock::Register(const void* mock_obj,
internal::UntypedFunctionMockerBase* mocker)
@@ -723,16 +785,13 @@ void Mock::RegisterUseByOnCallOrExpectCall(const void* mock_obj,
GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) {
internal::MutexLock l(&internal::g_gmock_mutex);
MockObjectState& state = g_mock_object_registry.states()[mock_obj];
- if (state.first_used_file == NULL) {
+ if (state.first_used_file == nullptr) {
state.first_used_file = file;
state.first_used_line = line;
const TestInfo* const test_info =
UnitTest::GetInstance()->current_test_info();
- if (test_info != NULL) {
- // TODO(wan@google.com): record the test case name when the
- // ON_CALL or EXPECT_CALL is invoked from SetUpTestCase() or
- // TearDownTestCase().
- state.first_used_test_case = test_info->test_case_name();
+ if (test_info != nullptr) {
+ state.first_used_test_suite = test_info->test_suite_name();
state.first_used_test = test_info->name();
}
}
@@ -785,7 +844,7 @@ void Mock::ClearDefaultActionsLocked(void* mock_obj)
Expectation::Expectation() {}
Expectation::Expectation(
- const internal::linked_ptr<internal::ExpectationBase>& an_expectation_base)
+ const std::shared_ptr<internal::ExpectationBase>& an_expectation_base)
: expectation_base_(an_expectation_base) {}
Expectation::~Expectation() {}
@@ -793,7 +852,7 @@ Expectation::~Expectation() {}
// Adds an expectation to a sequence.
void Sequence::AddExpectation(const Expectation& expectation) const {
if (*last_expectation_ != expectation) {
- if (last_expectation_->expectation_base() != NULL) {
+ if (last_expectation_->expectation_base() != nullptr) {
expectation.expectation_base()->immediate_prerequisites_
+= *last_expectation_;
}
@@ -803,7 +862,7 @@ void Sequence::AddExpectation(const Expectation& expectation) const {
// Creates the implicit sequence if there isn't one.
InSequence::InSequence() {
- if (internal::g_gmock_implicit_sequence.get() == NULL) {
+ if (internal::g_gmock_implicit_sequence.get() == nullptr) {
internal::g_gmock_implicit_sequence.set(new Sequence);
sequence_created_ = true;
} else {
@@ -816,8 +875,14 @@ InSequence::InSequence() {
InSequence::~InSequence() {
if (sequence_created_) {
delete internal::g_gmock_implicit_sequence.get();
- internal::g_gmock_implicit_sequence.set(NULL);
+ internal::g_gmock_implicit_sequence.set(nullptr);
}
}
} // namespace testing
+
+#ifdef _MSC_VER
+#if _MSC_VER == 1900
+# pragma warning(pop)
+#endif
+#endif
diff --git a/extern/gmock/src/gmock.cc b/extern/gmock/src/gmock.cc
index eac3d842ba0..32b2a7394fd 100644
--- a/extern/gmock/src/gmock.cc
+++ b/extern/gmock/src/gmock.cc
@@ -26,20 +26,16 @@
// 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: wan@google.com (Zhanyong Wan)
+
#include "gmock/gmock.h"
#include "gmock/internal/gmock-port.h"
namespace testing {
-// TODO(wan@google.com): support using environment variables to
-// control the flag values, like what Google Test does.
-
GMOCK_DEFINE_bool_(catch_leaked_mocks, true,
- "true iff Google Mock should report leaked mock objects "
- "as failures.");
+ "true if and only if Google Mock should report leaked "
+ "mock objects as failures.");
GMOCK_DEFINE_string_(verbose, internal::kWarningVerbosity,
"Controls how verbose Google Mock's output is."
@@ -48,6 +44,13 @@ GMOCK_DEFINE_string_(verbose, internal::kWarningVerbosity,
" warning - prints warnings and errors.\n"
" error - prints errors only.");
+GMOCK_DEFINE_int32_(default_mock_behavior, 1,
+ "Controls the default behavior of mocks."
+ " Valid values:\n"
+ " 0 - by default, mocks act as NiceMocks.\n"
+ " 1 - by default, mocks act as NaggyMocks.\n"
+ " 2 - by default, mocks act as StrictMocks.");
+
namespace internal {
// Parses a string as a command line flag. The string should have the
@@ -59,12 +62,12 @@ static const char* ParseGoogleMockFlagValue(const char* str,
const char* flag,
bool def_optional) {
// str and flag must not be NULL.
- if (str == NULL || flag == NULL) return NULL;
+ if (str == nullptr || flag == nullptr) return nullptr;
// The flag must start with "--gmock_".
const std::string flag_str = std::string("--gmock_") + flag;
const size_t flag_len = flag_str.length();
- if (strncmp(str, flag_str.c_str(), flag_len) != 0) return NULL;
+ if (strncmp(str, flag_str.c_str(), flag_len) != 0) return nullptr;
// Skips the flag name.
const char* flag_end = str + flag_len;
@@ -77,7 +80,7 @@ static const char* ParseGoogleMockFlagValue(const char* str,
// If def_optional is true and there are more characters after the
// flag name, or if def_optional is false, there must be a '=' after
// the flag name.
- if (flag_end[0] != '=') return NULL;
+ if (flag_end[0] != '=') return nullptr;
// Returns the string after "=".
return flag_end + 1;
@@ -94,7 +97,7 @@ static bool ParseGoogleMockBoolFlag(const char* str, const char* flag,
const char* const value_str = ParseGoogleMockFlagValue(str, flag, true);
// Aborts if the parsing failed.
- if (value_str == NULL) return false;
+ if (value_str == nullptr) return false;
// Converts the string value to a bool.
*value = !(*value_str == '0' || *value_str == 'f' || *value_str == 'F');
@@ -113,13 +116,26 @@ static bool ParseGoogleMockStringFlag(const char* str, const char* flag,
const char* const value_str = ParseGoogleMockFlagValue(str, flag, false);
// Aborts if the parsing failed.
- if (value_str == NULL) return false;
+ if (value_str == nullptr) return false;
// Sets *value to the value of the flag.
*value = value_str;
return true;
}
+static bool ParseGoogleMockIntFlag(const char* str, const char* flag,
+ int* value) {
+ // Gets the value of the flag as a string.
+ const char* const value_str = ParseGoogleMockFlagValue(str, flag, true);
+
+ // Aborts if the parsing failed.
+ if (value_str == nullptr) return false;
+
+ // Sets *value to the value of the flag.
+ return ParseInt32(Message() << "The value of flag --" << flag,
+ value_str, value);
+}
+
// The internal implementation of InitGoogleMock().
//
// The type parameter CharType can be instantiated to either char or
@@ -138,7 +154,9 @@ void InitGoogleMockImpl(int* argc, CharType** argv) {
// Do we see a Google Mock flag?
if (ParseGoogleMockBoolFlag(arg, "catch_leaked_mocks",
&GMOCK_FLAG(catch_leaked_mocks)) ||
- ParseGoogleMockStringFlag(arg, "verbose", &GMOCK_FLAG(verbose))) {
+ ParseGoogleMockStringFlag(arg, "verbose", &GMOCK_FLAG(verbose)) ||
+ ParseGoogleMockIntFlag(arg, "default_mock_behavior",
+ &GMOCK_FLAG(default_mock_behavior))) {
// Yes. Shift the remainder of the argv list left by one. Note
// that argv has (*argc + 1) elements, the last one always being
// NULL. The following loop moves the trailing NULL element as
@@ -180,4 +198,16 @@ GTEST_API_ void InitGoogleMock(int* argc, wchar_t** argv) {
internal::InitGoogleMockImpl(argc, argv);
}
+// This overloaded version can be used on Arduino/embedded platforms where
+// there is no argc/argv.
+GTEST_API_ void InitGoogleMock() {
+ // Since Arduino doesn't have a command line, fake out the argc/argv arguments
+ int argc = 1;
+ const auto arg0 = "dummy";
+ char* argv0 = const_cast<char*>(arg0);
+ char** argv = &argv0;
+
+ internal::InitGoogleMockImpl(&argc, argv);
+}
+
} // namespace testing
diff --git a/extern/gmock/src/gmock_main.cc b/extern/gmock/src/gmock_main.cc
index bd5be03be22..98611b93535 100644
--- a/extern/gmock/src/gmock_main.cc
+++ b/extern/gmock/src/gmock_main.cc
@@ -26,18 +26,28 @@
// 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: wan@google.com (Zhanyong Wan)
+
#include <iostream>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
+#ifdef ARDUINO
+void setup() {
+ // Since Google Mock depends on Google Test, InitGoogleMock() is
+ // also responsible for initializing Google Test. Therefore there's
+ // no need for calling testing::InitGoogleTest() separately.
+ testing::InitGoogleMock();
+}
+void loop() { RUN_ALL_TESTS(); }
+#else
+
// MS C++ compiler/linker has a bug on Windows (not on Windows CE), which
// causes a link error when _tmain is defined in a static library and UNICODE
// is enabled. For this reason instead of _tmain, main function is used on
// Windows. See the following link to track the current status of this bug:
-// http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=394464 // NOLINT
+// https://web.archive.org/web/20170912203238/connect.microsoft.com/VisualStudio/feedback/details/394464/wmain-link-error-in-the-static-library
+// // NOLINT
#if GTEST_OS_WINDOWS_MOBILE
# include <tchar.h> // NOLINT
@@ -52,3 +62,4 @@ GTEST_API_ int main(int argc, char** argv) {
testing::InitGoogleMock(&argc, argv);
return RUN_ALL_TESTS();
}
+#endif
diff --git a/extern/gtest/CHANGES b/extern/gtest/CHANGES
new file mode 100644
index 00000000000..05521324212
--- /dev/null
+++ b/extern/gtest/CHANGES
@@ -0,0 +1,157 @@
+Changes for 1.7.0:
+
+* New feature: death tests are supported on OpenBSD and in iOS
+ simulator now.
+* New feature: Google Test now implements a protocol to allow
+ a test runner to detect that a test program has exited
+ prematurely and report it as a failure (before it would be
+ falsely reported as a success if the exit code is 0).
+* New feature: Test::RecordProperty() can now be used outside of the
+ lifespan of a test method, in which case it will be attributed to
+ the current test case or the test program in the XML report.
+* New feature (potentially breaking): --gtest_list_tests now prints
+ the type parameters and value parameters for each test.
+* Improvement: char pointers and char arrays are now escaped properly
+ in failure messages.
+* Improvement: failure summary in XML reports now includes file and
+ line information.
+* Improvement: the <testsuites> XML element now has a timestamp attribute.
+* Improvement: When --gtest_filter is specified, XML report now doesn't
+ contain information about tests that are filtered out.
+* Fixed the bug where long --gtest_filter flag values are truncated in
+ death tests.
+* Potentially breaking change: RUN_ALL_TESTS() is now implemented as a
+ function instead of a macro in order to work better with Clang.
+* Compatibility fixes with C++ 11 and various platforms.
+* Bug/warning fixes.
+
+Changes for 1.6.0:
+
+* New feature: ADD_FAILURE_AT() for reporting a test failure at the
+ given source location -- useful for writing testing utilities.
+* New feature: the universal value printer is moved from Google Mock
+ to Google Test.
+* New feature: type parameters and value parameters are reported in
+ the XML report now.
+* A gtest_disable_pthreads CMake option.
+* Colored output works in GNU Screen sessions now.
+* Parameters of value-parameterized tests are now printed in the
+ textual output.
+* Failures from ad hoc test assertions run before RUN_ALL_TESTS() are
+ now correctly reported.
+* Arguments of ASSERT_XY and EXPECT_XY no longer need to support << to
+ ostream.
+* More complete handling of exceptions.
+* GTEST_ASSERT_XY can be used instead of ASSERT_XY in case the latter
+ name is already used by another library.
+* --gtest_catch_exceptions is now true by default, allowing a test
+ program to continue after an exception is thrown.
+* Value-parameterized test fixtures can now derive from Test and
+ WithParamInterface<T> separately, easing conversion of legacy tests.
+* Death test messages are clearly marked to make them more
+ distinguishable from other messages.
+* Compatibility fixes for Android, Google Native Client, MinGW, HP UX,
+ PowerPC, Lucid autotools, libCStd, Sun C++, Borland C++ Builder (Code Gear),
+ IBM XL C++ (Visual Age C++), and C++0x.
+* Bug fixes and implementation clean-ups.
+* Potentially incompatible changes: disables the harmful 'make install'
+ command in autotools.
+
+Changes for 1.5.0:
+
+ * New feature: assertions can be safely called in multiple threads
+ where the pthreads library is available.
+ * New feature: predicates used inside EXPECT_TRUE() and friends
+ can now generate custom failure messages.
+ * New feature: Google Test can now be compiled as a DLL.
+ * New feature: fused source files are included.
+ * New feature: prints help when encountering unrecognized Google Test flags.
+ * Experimental feature: CMake build script (requires CMake 2.6.4+).
+ * Experimental feature: the Pump script for meta programming.
+ * double values streamed to an assertion are printed with enough precision
+ to differentiate any two different values.
+ * Google Test now works on Solaris and AIX.
+ * Build and test script improvements.
+ * Bug fixes and implementation clean-ups.
+
+ Potentially breaking changes:
+
+ * Stopped supporting VC++ 7.1 with exceptions disabled.
+ * Dropped support for 'make install'.
+
+Changes for 1.4.0:
+
+ * New feature: the event listener API
+ * New feature: test shuffling
+ * New feature: the XML report format is closer to junitreport and can
+ be parsed by Hudson now.
+ * New feature: when a test runs under Visual Studio, its failures are
+ integrated in the IDE.
+ * New feature: /MD(d) versions of VC++ projects.
+ * New feature: elapsed time for the tests is printed by default.
+ * New feature: comes with a TR1 tuple implementation such that Boost
+ is no longer needed for Combine().
+ * New feature: EXPECT_DEATH_IF_SUPPORTED macro and friends.
+ * New feature: the Xcode project can now produce static gtest
+ libraries in addition to a framework.
+ * Compatibility fixes for Solaris, Cygwin, minGW, Windows Mobile,
+ Symbian, gcc, and C++Builder.
+ * Bug fixes and implementation clean-ups.
+
+Changes for 1.3.0:
+
+ * New feature: death tests on Windows, Cygwin, and Mac.
+ * New feature: ability to use Google Test assertions in other testing
+ frameworks.
+ * New feature: ability to run disabled test via
+ --gtest_also_run_disabled_tests.
+ * New feature: the --help flag for printing the usage.
+ * New feature: access to Google Test flag values in user code.
+ * New feature: a script that packs Google Test into one .h and one
+ .cc file for easy deployment.
+ * New feature: support for distributing test functions to multiple
+ machines (requires support from the test runner).
+ * Bug fixes and implementation clean-ups.
+
+Changes for 1.2.1:
+
+ * Compatibility fixes for Linux IA-64 and IBM z/OS.
+ * Added support for using Boost and other TR1 implementations.
+ * Changes to the build scripts to support upcoming release of Google C++
+ Mocking Framework.
+ * Added Makefile to the distribution package.
+ * Improved build instructions in README.
+
+Changes for 1.2.0:
+
+ * New feature: value-parameterized tests.
+ * New feature: the ASSERT/EXPECT_(NON)FATAL_FAILURE(_ON_ALL_THREADS)
+ macros.
+ * Changed the XML report format to match JUnit/Ant's.
+ * Added tests to the Xcode project.
+ * Added scons/SConscript for building with SCons.
+ * Added src/gtest-all.cc for building Google Test from a single file.
+ * Fixed compatibility with Solaris and z/OS.
+ * Enabled running Python tests on systems with python 2.3 installed,
+ e.g. Mac OS X 10.4.
+ * Bug fixes.
+
+Changes for 1.1.0:
+
+ * New feature: type-parameterized tests.
+ * New feature: exception assertions.
+ * New feature: printing elapsed time of tests.
+ * Improved the robustness of death tests.
+ * Added an Xcode project and samples.
+ * Adjusted the output format on Windows to be understandable by Visual Studio.
+ * Minor bug fixes.
+
+Changes for 1.0.1:
+
+ * Added project files for Visual Studio 7.1.
+ * Fixed issues with compiling on Mac OS X.
+ * Fixed issues with compiling on Cygwin.
+
+Changes for 1.0.0:
+
+ * Initial Open Source release of Google Test
diff --git a/extern/gtest/CMakeLists.txt b/extern/gtest/CMakeLists.txt
index 056b32cc99e..42a1cd6fbc7 100644
--- a/extern/gtest/CMakeLists.txt
+++ b/extern/gtest/CMakeLists.txt
@@ -35,34 +35,41 @@ set(INC_SYS
)
set(SRC
+ # src/gtest-all.cc
+ # src/gtest_main.cc
+
src/gtest.cc
src/gtest-death-test.cc
src/gtest-filepath.cc
+ src/gtest-matchers.cc
src/gtest-port.cc
src/gtest-printers.cc
src/gtest-test-part.cc
src/gtest-typed-test.cc
src/gtest-internal-inl.h
- include/gtest/gtest-death-test.h
+
include/gtest/gtest.h
+ include/gtest/gtest_pred_impl.h
+ include/gtest/gtest_prod.h
+ include/gtest/gtest-death-test.h
+ include/gtest/gtest-matchers.h
include/gtest/gtest-message.h
include/gtest/gtest-param-test.h
- include/gtest/gtest_pred_impl.h
include/gtest/gtest-printers.h
- include/gtest/gtest_prod.h
include/gtest/gtest-spi.h
include/gtest/gtest-test-part.h
include/gtest/gtest-typed-test.h
+ include/gtest/internal/custom/gtest.h
+ include/gtest/internal/custom/gtest-port.h
+ include/gtest/internal/custom/gtest-printers.h
include/gtest/internal/gtest-death-test-internal.h
include/gtest/internal/gtest-filepath.h
include/gtest/internal/gtest-internal.h
- include/gtest/internal/gtest-linked_ptr.h
- include/gtest/internal/gtest-param-util-generated.h
include/gtest/internal/gtest-param-util.h
include/gtest/internal/gtest-port.h
+ include/gtest/internal/gtest-port-arch.h
include/gtest/internal/gtest-string.h
- include/gtest/internal/gtest-tuple.h
include/gtest/internal/gtest-type-util.h
)
diff --git a/extern/gtest/CONTRIBUTORS b/extern/gtest/CONTRIBUTORS
new file mode 100644
index 00000000000..feae2fc0441
--- /dev/null
+++ b/extern/gtest/CONTRIBUTORS
@@ -0,0 +1,37 @@
+# This file contains a list of people who've made non-trivial
+# contribution to the Google C++ Testing Framework project. People
+# who commit code to the project are encouraged to add their names
+# here. Please keep the list sorted by first names.
+
+Ajay Joshi <jaj@google.com>
+Balázs Dán <balazs.dan@gmail.com>
+Bharat Mediratta <bharat@menalto.com>
+Chandler Carruth <chandlerc@google.com>
+Chris Prince <cprince@google.com>
+Chris Taylor <taylorc@google.com>
+Dan Egnor <egnor@google.com>
+Eric Roman <eroman@chromium.org>
+Hady Zalek <hady.zalek@gmail.com>
+Jeffrey Yasskin <jyasskin@google.com>
+Jói Sigurðsson <joi@google.com>
+Keir Mierle <mierle@gmail.com>
+Keith Ray <keith.ray@gmail.com>
+Kenton Varda <kenton@google.com>
+Manuel Klimek <klimek@google.com>
+Markus Heule <markus.heule@gmail.com>
+Mika Raento <mikie@iki.fi>
+Miklós Fazekas <mfazekas@szemafor.com>
+Pasi Valminen <pasi.valminen@gmail.com>
+Patrick Hanna <phanna@google.com>
+Patrick Riley <pfr@google.com>
+Peter Kaminski <piotrk@google.com>
+Preston Jackson <preston.a.jackson@gmail.com>
+Rainer Klaffenboeck <rainer.klaffenboeck@dynatrace.com>
+Russ Cox <rsc@google.com>
+Russ Rufer <russ@pentad.com>
+Sean Mcafee <eefacm@gmail.com>
+Sigurður Ásgeirsson <siggi@google.com>
+Tracy Bialik <tracy@pentad.com>
+Vadim Berman <vadimb@google.com>
+Vlad Losev <vladl@google.com>
+Zhanyong Wan <wan@google.com>
diff --git a/extern/gtest/README.blender b/extern/gtest/README.blender
index 6165bd6f717..3f9d80362d8 100644
--- a/extern/gtest/README.blender
+++ b/extern/gtest/README.blender
@@ -1,7 +1,5 @@
Project: Google C++ Testing Framework
URL: https://github.com/google/googletest
License: New BSD
-Upstream version: 1.8.0 (ec44c6c1675)
-Local modifications:
-
-None.
+Upstream version: 1.10.0 (703bd9caab5)
+Local modifications: None
diff --git a/extern/gtest/README.md b/extern/gtest/README.md
index 22df99bd4b0..fe569414864 100644
--- a/extern/gtest/README.md
+++ b/extern/gtest/README.md
@@ -2,6 +2,13 @@
# Google Test #
[![Build Status](https://travis-ci.org/google/googletest.svg?branch=master)](https://travis-ci.org/google/googletest)
+[![Build status](https://ci.appveyor.com/api/projects/status/4o38plt0xbo1ubc8/branch/master?svg=true)](https://ci.appveyor.com/project/GoogleTestAppVeyor/googletest/branch/master)
+
+**Future Plans**:
+* 1.8.x Release - the 1.8.x will be the last release that works with pre-C++11 compilers. The 1.8.x will not accept any requests for any new features and any bugfix requests will only be accepted if proven "critical"
+* Post 1.8.x - work to improve/cleanup/pay technical debt. When this work is completed there will be a 1.9.x tagged release
+* Post 1.9.x googletest will follow [Abseil Live at Head philosophy](https://abseil.io/about/philosophy)
+
Welcome to **Google Test**, Google's C++ test framework!
@@ -11,9 +18,12 @@ maintain and release them together.
Please see the project page above for more information as well as the
mailing list for questions, discussions, and development. There is
-also an IRC channel on OFTC (irc.oftc.net) #gtest available. Please
+also an IRC channel on [OFTC](https://webchat.oftc.net/) (irc.oftc.net) #gtest available. Please
join us!
+Getting started information for **Google Test** is available in the
+[Google Test Primer](googletest/docs/primer.md) documentation.
+
**Google Mock** is an extension to Google Test for writing and using C++ mock
classes. See the separate [Google Mock documentation](googlemock/README.md).
@@ -22,7 +32,7 @@ in its interior [googletest/README.md](googletest/README.md) file.
## Features ##
- * An [XUnit](https://en.wikipedia.org/wiki/XUnit) test framework.
+ * An [xUnit](https://en.wikipedia.org/wiki/XUnit) test framework.
* Test discovery.
* A rich set of assertions.
* User-defined assertions.
@@ -56,9 +66,12 @@ the following notable projects:
* [Protocol Buffers](https://github.com/google/protobuf), Google's data
interchange format.
* The [OpenCV](http://opencv.org/) computer vision library.
+ * [tiny-dnn](https://github.com/tiny-dnn/tiny-dnn): header only, dependency-free deep learning framework in C++11.
## Related Open Source Projects ##
+[GTest Runner](https://github.com/nholthaus/gtest-runner) is a Qt5 based automated test-runner and Graphical User Interface with powerful features for Windows and Linux platforms.
+
[Google Test UI](https://github.com/ospector/gtest-gbar) is test runner that runs
your test binary, allows you to track its progress via a progress bar, and
displays a list of test failures. Clicking on one shows failure text. Google
@@ -69,6 +82,11 @@ listener for Google Test that implements the
[TAP protocol](https://en.wikipedia.org/wiki/Test_Anything_Protocol) for test
result output. If your test runner understands TAP, you may find it useful.
+[gtest-parallel](https://github.com/google/gtest-parallel) is a test runner that
+runs tests from your binary in parallel to provide significant speed-up.
+
+[GoogleTest Adapter](https://marketplace.visualstudio.com/items?itemName=DavidSchuldenfrei.gtest-adapter) is a VS Code extension allowing to view Google Tests in a tree view, and run/debug your tests.
+
## Requirements ##
Google Test is designed to have fairly minimal requirements to build
@@ -78,7 +96,7 @@ effort to support other platforms (e.g. Solaris, AIX, and z/OS).
However, since core members of the Google Test project have no access
to these platforms, Google Test may have outstanding issues there. If
you notice any problems on your platform, please notify
-<googletestframework@googlegroups.com>. Patches for fixing them are
+[googletestframework@googlegroups.com](https://groups.google.com/forum/#!forum/googletestframework). Patches for fixing them are
even more welcome!
### Linux Requirements ###
@@ -93,7 +111,7 @@ package (as described below):
### Windows Requirements ###
- * Microsoft Visual C++ v7.1 or newer
+ * Microsoft Visual C++ 2015 or newer
### Cygwin Requirements ###
@@ -102,37 +120,11 @@ package (as described below):
### Mac OS X Requirements ###
* Mac OS X v10.4 Tiger or newer
- * XCode Developer Tools
-
-### Requirements for Contributors ###
-
-We welcome patches. If you plan to contribute a patch, you need to
-build Google Test and its own tests from a git checkout (described
-below), which has further requirements:
-
- * [Python](https://www.python.org/) v2.3 or newer (for running some of
- the tests and re-generating certain source files from templates)
- * [CMake](https://cmake.org/) v2.6.4 or newer
-
-## Regenerating Source Files ##
-
-Some of Google Test's source files are generated from templates (not
-in the C++ sense) using a script.
-For example, the
-file include/gtest/internal/gtest-type-util.h.pump is used to generate
-gtest-type-util.h in the same directory.
-
-You don't need to worry about regenerating the source files
-unless you need to modify them. You would then modify the
-corresponding `.pump` files and run the '[pump.py](googletest/scripts/pump.py)'
-generator script. See the [Pump Manual](googletest/docs/PumpManual.md).
+ * Xcode Developer Tools
-### Contributing Code ###
+## Contributing change
-We welcome patches. Please read the
-[Developer's Guide](googletest/docs/DevGuide.md)
-for how you can contribute. In particular, make sure you have signed
-the Contributor License Agreement, or we won't be able to accept the
-patch.
+Please read the [`CONTRIBUTING.md`](CONTRIBUTING.md) for details on
+how to contribute to this project.
Happy testing!
diff --git a/extern/gtest/include/gtest/gtest-death-test.h b/extern/gtest/include/gtest/gtest-death-test.h
index 957a69c6a9e..dc878ffbb3a 100644
--- a/extern/gtest/include/gtest/gtest-death-test.h
+++ b/extern/gtest/include/gtest/gtest-death-test.h
@@ -26,14 +26,14 @@
// 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: wan@google.com (Zhanyong Wan)
-//
-// The Google C++ Testing Framework (Google Test)
+// The Google C++ Testing and Mocking Framework (Google Test)
//
// This header file defines the public API for death tests. It is
// #included by gtest.h so a user doesn't need to include this
// directly.
+// GOOGLETEST_CM0001 DO NOT DELETE
#ifndef GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_
#define GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_
@@ -99,10 +99,11 @@ GTEST_API_ bool InDeathTestChild();
//
// On the regular expressions used in death tests:
//
+// GOOGLETEST_CM0005 DO NOT DELETE
// On POSIX-compliant systems (*nix), we use the <regex.h> library,
// which uses the POSIX extended regex syntax.
//
-// On other platforms (e.g. Windows), we only support a simple regex
+// On other platforms (e.g. Windows or Mac), we only support a simple regex
// syntax implemented as part of Google Test. This limited
// implementation should be enough most of the time when writing
// death tests; though it lacks many features you can find in PCRE
@@ -160,7 +161,6 @@ GTEST_API_ bool InDeathTestChild();
// is rarely a problem as people usually don't put the test binary
// directory in PATH.
//
-// TODO(wan@google.com): make thread-safe death tests search the PATH.
// Asserts that a given statement causes the program to exit, with an
// integer exit status that satisfies predicate, and emitting error output
@@ -169,7 +169,7 @@ GTEST_API_ bool InDeathTestChild();
GTEST_DEATH_TEST_(statement, predicate, regex, GTEST_FATAL_FAILURE_)
// Like ASSERT_EXIT, but continues on to successive tests in the
-// test case, if any:
+// test suite, if any:
# define EXPECT_EXIT(statement, predicate, regex) \
GTEST_DEATH_TEST_(statement, predicate, regex, GTEST_NONFATAL_FAILURE_)
@@ -180,7 +180,7 @@ GTEST_API_ bool InDeathTestChild();
ASSERT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, regex)
// Like ASSERT_DEATH, but continues on to successive tests in the
-// test case, if any:
+// test suite, if any:
# define EXPECT_DEATH(statement, regex) \
EXPECT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, regex)
@@ -198,9 +198,10 @@ class GTEST_API_ ExitedWithCode {
const int exit_code_;
};
-# if !GTEST_OS_WINDOWS
+# if !GTEST_OS_WINDOWS && !GTEST_OS_FUCHSIA
// Tests that an exit code describes an exit due to termination by a
// given signal.
+// GOOGLETEST_CM0006 DO NOT DELETE
class GTEST_API_ KilledBySignal {
public:
explicit KilledBySignal(int signum);
@@ -226,7 +227,7 @@ class GTEST_API_ KilledBySignal {
// return 12;
// }
//
-// TEST(TestCase, TestDieOr12WorksInDgbAndOpt) {
+// TEST(TestSuite, TestDieOr12WorksInDgbAndOpt) {
// int sideeffect = 0;
// // Only asserts in dbg.
// EXPECT_DEBUG_DEATH(DieInDebugOr12(&sideeffect), "death");
@@ -272,6 +273,54 @@ class GTEST_API_ KilledBySignal {
# endif // NDEBUG for EXPECT_DEBUG_DEATH
#endif // GTEST_HAS_DEATH_TEST
+// This macro is used for implementing macros such as
+// EXPECT_DEATH_IF_SUPPORTED and ASSERT_DEATH_IF_SUPPORTED on systems where
+// death tests are not supported. Those macros must compile on such systems
+// if and only if EXPECT_DEATH and ASSERT_DEATH compile with the same parameters
+// on systems that support death tests. This allows one to write such a macro on
+// a system that does not support death tests and be sure that it will compile
+// on a death-test supporting system. It is exposed publicly so that systems
+// that have death-tests with stricter requirements than GTEST_HAS_DEATH_TEST
+// can write their own equivalent of EXPECT_DEATH_IF_SUPPORTED and
+// ASSERT_DEATH_IF_SUPPORTED.
+//
+// Parameters:
+// statement - A statement that a macro such as EXPECT_DEATH would test
+// for program termination. This macro has to make sure this
+// statement is compiled but not executed, to ensure that
+// EXPECT_DEATH_IF_SUPPORTED compiles with a certain
+// parameter if and only if EXPECT_DEATH compiles with it.
+// regex - A regex that a macro such as EXPECT_DEATH would use to test
+// the output of statement. This parameter has to be
+// compiled but not evaluated by this macro, to ensure that
+// this macro only accepts expressions that a macro such as
+// EXPECT_DEATH would accept.
+// terminator - Must be an empty statement for EXPECT_DEATH_IF_SUPPORTED
+// and a return statement for ASSERT_DEATH_IF_SUPPORTED.
+// This ensures that ASSERT_DEATH_IF_SUPPORTED will not
+// compile inside functions where ASSERT_DEATH doesn't
+// compile.
+//
+// The branch that has an always false condition is used to ensure that
+// statement and regex are compiled (and thus syntactically correct) but
+// never executed. The unreachable code macro protects the terminator
+// statement from generating an 'unreachable code' warning in case
+// statement unconditionally returns or throws. The Message constructor at
+// the end allows the syntax of streaming additional messages into the
+// macro, for compilational compatibility with EXPECT_DEATH/ASSERT_DEATH.
+# define GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, terminator) \
+ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+ if (::testing::internal::AlwaysTrue()) { \
+ GTEST_LOG_(WARNING) \
+ << "Death tests are not supported on this platform.\n" \
+ << "Statement '" #statement "' cannot be verified."; \
+ } else if (::testing::internal::AlwaysFalse()) { \
+ ::testing::internal::RE::PartialMatch(".*", (regex)); \
+ GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
+ terminator; \
+ } else \
+ ::testing::Message()
+
// EXPECT_DEATH_IF_SUPPORTED(statement, regex) and
// ASSERT_DEATH_IF_SUPPORTED(statement, regex) expand to real death tests if
// death tests are supported; otherwise they just issue a warning. This is
@@ -284,9 +333,9 @@ class GTEST_API_ KilledBySignal {
ASSERT_DEATH(statement, regex)
#else
# define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \
- GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, )
+ GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, )
# define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \
- GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, return)
+ GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, return)
#endif
} // namespace testing
diff --git a/extern/gtest/include/gtest/gtest-matchers.h b/extern/gtest/include/gtest/gtest-matchers.h
new file mode 100644
index 00000000000..9de6c2e10a2
--- /dev/null
+++ b/extern/gtest/include/gtest/gtest-matchers.h
@@ -0,0 +1,750 @@
+// Copyright 2007, Google Inc.
+// All rights reserved.
+//
+// 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.
+
+// The Google C++ Testing and Mocking Framework (Google Test)
+//
+// This file implements just enough of the matcher interface to allow
+// EXPECT_DEATH and friends to accept a matcher argument.
+
+// IWYU pragma: private, include "testing/base/public/gunit.h"
+// IWYU pragma: friend third_party/googletest/googlemock/.*
+// IWYU pragma: friend third_party/googletest/googletest/.*
+
+#ifndef GTEST_INCLUDE_GTEST_GTEST_MATCHERS_H_
+#define GTEST_INCLUDE_GTEST_GTEST_MATCHERS_H_
+
+#include <memory>
+#include <ostream>
+#include <string>
+#include <type_traits>
+
+#include "gtest/gtest-printers.h"
+#include "gtest/internal/gtest-internal.h"
+#include "gtest/internal/gtest-port.h"
+
+// MSVC warning C5046 is new as of VS2017 version 15.8.
+#if defined(_MSC_VER) && _MSC_VER >= 1915
+#define GTEST_MAYBE_5046_ 5046
+#else
+#define GTEST_MAYBE_5046_
+#endif
+
+GTEST_DISABLE_MSC_WARNINGS_PUSH_(
+ 4251 GTEST_MAYBE_5046_ /* class A needs to have dll-interface to be used by
+ clients of class B */
+ /* Symbol involving type with internal linkage not defined */)
+
+namespace testing {
+
+// To implement a matcher Foo for type T, define:
+// 1. a class FooMatcherImpl that implements the
+// MatcherInterface<T> interface, and
+// 2. a factory function that creates a Matcher<T> object from a
+// FooMatcherImpl*.
+//
+// The two-level delegation design makes it possible to allow a user
+// to write "v" instead of "Eq(v)" where a Matcher is expected, which
+// is impossible if we pass matchers by pointers. It also eases
+// ownership management as Matcher objects can now be copied like
+// plain values.
+
+// MatchResultListener is an abstract class. Its << operator can be
+// used by a matcher to explain why a value matches or doesn't match.
+//
+class MatchResultListener {
+ public:
+ // Creates a listener object with the given underlying ostream. The
+ // listener does not own the ostream, and does not dereference it
+ // in the constructor or destructor.
+ explicit MatchResultListener(::std::ostream* os) : stream_(os) {}
+ virtual ~MatchResultListener() = 0; // Makes this class abstract.
+
+ // Streams x to the underlying ostream; does nothing if the ostream
+ // is NULL.
+ template <typename T>
+ MatchResultListener& operator<<(const T& x) {
+ if (stream_ != nullptr) *stream_ << x;
+ return *this;
+ }
+
+ // Returns the underlying ostream.
+ ::std::ostream* stream() { return stream_; }
+
+ // Returns true if and only if the listener is interested in an explanation
+ // of the match result. A matcher's MatchAndExplain() method can use
+ // this information to avoid generating the explanation when no one
+ // intends to hear it.
+ bool IsInterested() const { return stream_ != nullptr; }
+
+ private:
+ ::std::ostream* const stream_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(MatchResultListener);
+};
+
+inline MatchResultListener::~MatchResultListener() {
+}
+
+// An instance of a subclass of this knows how to describe itself as a
+// matcher.
+class MatcherDescriberInterface {
+ public:
+ virtual ~MatcherDescriberInterface() {}
+
+ // Describes this matcher to an ostream. The function should print
+ // a verb phrase that describes the property a value matching this
+ // matcher should have. The subject of the verb phrase is the value
+ // being matched. For example, the DescribeTo() method of the Gt(7)
+ // matcher prints "is greater than 7".
+ virtual void DescribeTo(::std::ostream* os) const = 0;
+
+ // Describes the negation of this matcher to an ostream. For
+ // example, if the description of this matcher is "is greater than
+ // 7", the negated description could be "is not greater than 7".
+ // You are not required to override this when implementing
+ // MatcherInterface, but it is highly advised so that your matcher
+ // can produce good error messages.
+ virtual void DescribeNegationTo(::std::ostream* os) const {
+ *os << "not (";
+ DescribeTo(os);
+ *os << ")";
+ }
+};
+
+// The implementation of a matcher.
+template <typename T>
+class MatcherInterface : public MatcherDescriberInterface {
+ public:
+ // Returns true if and only if the matcher matches x; also explains the
+ // match result to 'listener' if necessary (see the next paragraph), in
+ // the form of a non-restrictive relative clause ("which ...",
+ // "whose ...", etc) that describes x. For example, the
+ // MatchAndExplain() method of the Pointee(...) matcher should
+ // generate an explanation like "which points to ...".
+ //
+ // Implementations of MatchAndExplain() should add an explanation of
+ // the match result *if and only if* they can provide additional
+ // information that's not already present (or not obvious) in the
+ // print-out of x and the matcher's description. Whether the match
+ // succeeds is not a factor in deciding whether an explanation is
+ // needed, as sometimes the caller needs to print a failure message
+ // when the match succeeds (e.g. when the matcher is used inside
+ // Not()).
+ //
+ // For example, a "has at least 10 elements" matcher should explain
+ // what the actual element count is, regardless of the match result,
+ // as it is useful information to the reader; on the other hand, an
+ // "is empty" matcher probably only needs to explain what the actual
+ // size is when the match fails, as it's redundant to say that the
+ // size is 0 when the value is already known to be empty.
+ //
+ // You should override this method when defining a new matcher.
+ //
+ // It's the responsibility of the caller (Google Test) to guarantee
+ // that 'listener' is not NULL. This helps to simplify a matcher's
+ // implementation when it doesn't care about the performance, as it
+ // can talk to 'listener' without checking its validity first.
+ // However, in order to implement dummy listeners efficiently,
+ // listener->stream() may be NULL.
+ virtual bool MatchAndExplain(T x, MatchResultListener* listener) const = 0;
+
+ // Inherits these methods from MatcherDescriberInterface:
+ // virtual void DescribeTo(::std::ostream* os) const = 0;
+ // virtual void DescribeNegationTo(::std::ostream* os) const;
+};
+
+namespace internal {
+
+// Converts a MatcherInterface<T> to a MatcherInterface<const T&>.
+template <typename T>
+class MatcherInterfaceAdapter : public MatcherInterface<const T&> {
+ public:
+ explicit MatcherInterfaceAdapter(const MatcherInterface<T>* impl)
+ : impl_(impl) {}
+ ~MatcherInterfaceAdapter() override { delete impl_; }
+
+ void DescribeTo(::std::ostream* os) const override { impl_->DescribeTo(os); }
+
+ void DescribeNegationTo(::std::ostream* os) const override {
+ impl_->DescribeNegationTo(os);
+ }
+
+ bool MatchAndExplain(const T& x,
+ MatchResultListener* listener) const override {
+ return impl_->MatchAndExplain(x, listener);
+ }
+
+ private:
+ const MatcherInterface<T>* const impl_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(MatcherInterfaceAdapter);
+};
+
+struct AnyEq {
+ template <typename A, typename B>
+ bool operator()(const A& a, const B& b) const { return a == b; }
+};
+struct AnyNe {
+ template <typename A, typename B>
+ bool operator()(const A& a, const B& b) const { return a != b; }
+};
+struct AnyLt {
+ template <typename A, typename B>
+ bool operator()(const A& a, const B& b) const { return a < b; }
+};
+struct AnyGt {
+ template <typename A, typename B>
+ bool operator()(const A& a, const B& b) const { return a > b; }
+};
+struct AnyLe {
+ template <typename A, typename B>
+ bool operator()(const A& a, const B& b) const { return a <= b; }
+};
+struct AnyGe {
+ template <typename A, typename B>
+ bool operator()(const A& a, const B& b) const { return a >= b; }
+};
+
+// A match result listener that ignores the explanation.
+class DummyMatchResultListener : public MatchResultListener {
+ public:
+ DummyMatchResultListener() : MatchResultListener(nullptr) {}
+
+ private:
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(DummyMatchResultListener);
+};
+
+// A match result listener that forwards the explanation to a given
+// ostream. The difference between this and MatchResultListener is
+// that the former is concrete.
+class StreamMatchResultListener : public MatchResultListener {
+ public:
+ explicit StreamMatchResultListener(::std::ostream* os)
+ : MatchResultListener(os) {}
+
+ private:
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(StreamMatchResultListener);
+};
+
+// An internal class for implementing Matcher<T>, which will derive
+// from it. We put functionalities common to all Matcher<T>
+// specializations here to avoid code duplication.
+template <typename T>
+class MatcherBase {
+ public:
+ // Returns true if and only if the matcher matches x; also explains the
+ // match result to 'listener'.
+ bool MatchAndExplain(const T& x, MatchResultListener* listener) const {
+ return impl_->MatchAndExplain(x, listener);
+ }
+
+ // Returns true if and only if this matcher matches x.
+ bool Matches(const T& x) const {
+ DummyMatchResultListener dummy;
+ return MatchAndExplain(x, &dummy);
+ }
+
+ // Describes this matcher to an ostream.
+ void DescribeTo(::std::ostream* os) const { impl_->DescribeTo(os); }
+
+ // Describes the negation of this matcher to an ostream.
+ void DescribeNegationTo(::std::ostream* os) const {
+ impl_->DescribeNegationTo(os);
+ }
+
+ // Explains why x matches, or doesn't match, the matcher.
+ void ExplainMatchResultTo(const T& x, ::std::ostream* os) const {
+ StreamMatchResultListener listener(os);
+ MatchAndExplain(x, &listener);
+ }
+
+ // Returns the describer for this matcher object; retains ownership
+ // of the describer, which is only guaranteed to be alive when
+ // this matcher object is alive.
+ const MatcherDescriberInterface* GetDescriber() const {
+ return impl_.get();
+ }
+
+ protected:
+ MatcherBase() {}
+
+ // Constructs a matcher from its implementation.
+ explicit MatcherBase(const MatcherInterface<const T&>* impl) : impl_(impl) {}
+
+ template <typename U>
+ explicit MatcherBase(
+ const MatcherInterface<U>* impl,
+ typename std::enable_if<!std::is_same<U, const U&>::value>::type* =
+ nullptr)
+ : impl_(new internal::MatcherInterfaceAdapter<U>(impl)) {}
+
+ MatcherBase(const MatcherBase&) = default;
+ MatcherBase& operator=(const MatcherBase&) = default;
+ MatcherBase(MatcherBase&&) = default;
+ MatcherBase& operator=(MatcherBase&&) = default;
+
+ virtual ~MatcherBase() {}
+
+ private:
+ std::shared_ptr<const MatcherInterface<const T&>> impl_;
+};
+
+} // namespace internal
+
+// A Matcher<T> is a copyable and IMMUTABLE (except by assignment)
+// object that can check whether a value of type T matches. The
+// implementation of Matcher<T> is just a std::shared_ptr to const
+// MatcherInterface<T>. Don't inherit from Matcher!
+template <typename T>
+class Matcher : public internal::MatcherBase<T> {
+ public:
+ // Constructs a null matcher. Needed for storing Matcher objects in STL
+ // containers. A default-constructed matcher is not yet initialized. You
+ // cannot use it until a valid value has been assigned to it.
+ explicit Matcher() {} // NOLINT
+
+ // Constructs a matcher from its implementation.
+ explicit Matcher(const MatcherInterface<const T&>* impl)
+ : internal::MatcherBase<T>(impl) {}
+
+ template <typename U>
+ explicit Matcher(
+ const MatcherInterface<U>* impl,
+ typename std::enable_if<!std::is_same<U, const U&>::value>::type* =
+ nullptr)
+ : internal::MatcherBase<T>(impl) {}
+
+ // Implicit constructor here allows people to write
+ // EXPECT_CALL(foo, Bar(5)) instead of EXPECT_CALL(foo, Bar(Eq(5))) sometimes
+ Matcher(T value); // NOLINT
+};
+
+// The following two specializations allow the user to write str
+// instead of Eq(str) and "foo" instead of Eq("foo") when a std::string
+// matcher is expected.
+template <>
+class GTEST_API_ Matcher<const std::string&>
+ : public internal::MatcherBase<const std::string&> {
+ public:
+ Matcher() {}
+
+ explicit Matcher(const MatcherInterface<const std::string&>* impl)
+ : internal::MatcherBase<const std::string&>(impl) {}
+
+ // Allows the user to write str instead of Eq(str) sometimes, where
+ // str is a std::string object.
+ Matcher(const std::string& s); // NOLINT
+
+ // Allows the user to write "foo" instead of Eq("foo") sometimes.
+ Matcher(const char* s); // NOLINT
+};
+
+template <>
+class GTEST_API_ Matcher<std::string>
+ : public internal::MatcherBase<std::string> {
+ public:
+ Matcher() {}
+
+ explicit Matcher(const MatcherInterface<const std::string&>* impl)
+ : internal::MatcherBase<std::string>(impl) {}
+ explicit Matcher(const MatcherInterface<std::string>* impl)
+ : internal::MatcherBase<std::string>(impl) {}
+
+ // Allows the user to write str instead of Eq(str) sometimes, where
+ // str is a string object.
+ Matcher(const std::string& s); // NOLINT
+
+ // Allows the user to write "foo" instead of Eq("foo") sometimes.
+ Matcher(const char* s); // NOLINT
+};
+
+#if GTEST_HAS_ABSL
+// The following two specializations allow the user to write str
+// instead of Eq(str) and "foo" instead of Eq("foo") when a absl::string_view
+// matcher is expected.
+template <>
+class GTEST_API_ Matcher<const absl::string_view&>
+ : public internal::MatcherBase<const absl::string_view&> {
+ public:
+ Matcher() {}
+
+ explicit Matcher(const MatcherInterface<const absl::string_view&>* impl)
+ : internal::MatcherBase<const absl::string_view&>(impl) {}
+
+ // Allows the user to write str instead of Eq(str) sometimes, where
+ // str is a std::string object.
+ Matcher(const std::string& s); // NOLINT
+
+ // Allows the user to write "foo" instead of Eq("foo") sometimes.
+ Matcher(const char* s); // NOLINT
+
+ // Allows the user to pass absl::string_views directly.
+ Matcher(absl::string_view s); // NOLINT
+};
+
+template <>
+class GTEST_API_ Matcher<absl::string_view>
+ : public internal::MatcherBase<absl::string_view> {
+ public:
+ Matcher() {}
+
+ explicit Matcher(const MatcherInterface<const absl::string_view&>* impl)
+ : internal::MatcherBase<absl::string_view>(impl) {}
+ explicit Matcher(const MatcherInterface<absl::string_view>* impl)
+ : internal::MatcherBase<absl::string_view>(impl) {}
+
+ // Allows the user to write str instead of Eq(str) sometimes, where
+ // str is a std::string object.
+ Matcher(const std::string& s); // NOLINT
+
+ // Allows the user to write "foo" instead of Eq("foo") sometimes.
+ Matcher(const char* s); // NOLINT
+
+ // Allows the user to pass absl::string_views directly.
+ Matcher(absl::string_view s); // NOLINT
+};
+#endif // GTEST_HAS_ABSL
+
+// Prints a matcher in a human-readable format.
+template <typename T>
+std::ostream& operator<<(std::ostream& os, const Matcher<T>& matcher) {
+ matcher.DescribeTo(&os);
+ return os;
+}
+
+// The PolymorphicMatcher class template makes it easy to implement a
+// polymorphic matcher (i.e. a matcher that can match values of more
+// than one type, e.g. Eq(n) and NotNull()).
+//
+// To define a polymorphic matcher, a user should provide an Impl
+// class that has a DescribeTo() method and a DescribeNegationTo()
+// method, and define a member function (or member function template)
+//
+// bool MatchAndExplain(const Value& value,
+// MatchResultListener* listener) const;
+//
+// See the definition of NotNull() for a complete example.
+template <class Impl>
+class PolymorphicMatcher {
+ public:
+ explicit PolymorphicMatcher(const Impl& an_impl) : impl_(an_impl) {}
+
+ // Returns a mutable reference to the underlying matcher
+ // implementation object.
+ Impl& mutable_impl() { return impl_; }
+
+ // Returns an immutable reference to the underlying matcher
+ // implementation object.
+ const Impl& impl() const { return impl_; }
+
+ template <typename T>
+ operator Matcher<T>() const {
+ return Matcher<T>(new MonomorphicImpl<const T&>(impl_));
+ }
+
+ private:
+ template <typename T>
+ class MonomorphicImpl : public MatcherInterface<T> {
+ public:
+ explicit MonomorphicImpl(const Impl& impl) : impl_(impl) {}
+
+ virtual void DescribeTo(::std::ostream* os) const { impl_.DescribeTo(os); }
+
+ virtual void DescribeNegationTo(::std::ostream* os) const {
+ impl_.DescribeNegationTo(os);
+ }
+
+ virtual bool MatchAndExplain(T x, MatchResultListener* listener) const {
+ return impl_.MatchAndExplain(x, listener);
+ }
+
+ private:
+ const Impl impl_;
+ };
+
+ Impl impl_;
+};
+
+// Creates a matcher from its implementation.
+// DEPRECATED: Especially in the generic code, prefer:
+// Matcher<T>(new MyMatcherImpl<const T&>(...));
+//
+// MakeMatcher may create a Matcher that accepts its argument by value, which
+// leads to unnecessary copies & lack of support for non-copyable types.
+template <typename T>
+inline Matcher<T> MakeMatcher(const MatcherInterface<T>* impl) {
+ return Matcher<T>(impl);
+}
+
+// Creates a polymorphic matcher from its implementation. This is
+// easier to use than the PolymorphicMatcher<Impl> constructor as it
+// doesn't require you to explicitly write the template argument, e.g.
+//
+// MakePolymorphicMatcher(foo);
+// vs
+// PolymorphicMatcher<TypeOfFoo>(foo);
+template <class Impl>
+inline PolymorphicMatcher<Impl> MakePolymorphicMatcher(const Impl& impl) {
+ return PolymorphicMatcher<Impl>(impl);
+}
+
+namespace internal {
+// Implements a matcher that compares a given value with a
+// pre-supplied value using one of the ==, <=, <, etc, operators. The
+// two values being compared don't have to have the same type.
+//
+// The matcher defined here is polymorphic (for example, Eq(5) can be
+// used to match an int, a short, a double, etc). Therefore we use
+// a template type conversion operator in the implementation.
+//
+// The following template definition assumes that the Rhs parameter is
+// a "bare" type (i.e. neither 'const T' nor 'T&').
+template <typename D, typename Rhs, typename Op>
+class ComparisonBase {
+ public:
+ explicit ComparisonBase(const Rhs& rhs) : rhs_(rhs) {}
+ template <typename Lhs>
+ operator Matcher<Lhs>() const {
+ return Matcher<Lhs>(new Impl<const Lhs&>(rhs_));
+ }
+
+ private:
+ template <typename T>
+ static const T& Unwrap(const T& v) { return v; }
+ template <typename T>
+ static const T& Unwrap(std::reference_wrapper<T> v) { return v; }
+
+ template <typename Lhs, typename = Rhs>
+ class Impl : public MatcherInterface<Lhs> {
+ public:
+ explicit Impl(const Rhs& rhs) : rhs_(rhs) {}
+ bool MatchAndExplain(Lhs lhs,
+ MatchResultListener* /* listener */) const override {
+ return Op()(lhs, Unwrap(rhs_));
+ }
+ void DescribeTo(::std::ostream* os) const override {
+ *os << D::Desc() << " ";
+ UniversalPrint(Unwrap(rhs_), os);
+ }
+ void DescribeNegationTo(::std::ostream* os) const override {
+ *os << D::NegatedDesc() << " ";
+ UniversalPrint(Unwrap(rhs_), os);
+ }
+
+ private:
+ Rhs rhs_;
+ };
+ Rhs rhs_;
+};
+
+template <typename Rhs>
+class EqMatcher : public ComparisonBase<EqMatcher<Rhs>, Rhs, AnyEq> {
+ public:
+ explicit EqMatcher(const Rhs& rhs)
+ : ComparisonBase<EqMatcher<Rhs>, Rhs, AnyEq>(rhs) { }
+ static const char* Desc() { return "is equal to"; }
+ static const char* NegatedDesc() { return "isn't equal to"; }
+};
+template <typename Rhs>
+class NeMatcher : public ComparisonBase<NeMatcher<Rhs>, Rhs, AnyNe> {
+ public:
+ explicit NeMatcher(const Rhs& rhs)
+ : ComparisonBase<NeMatcher<Rhs>, Rhs, AnyNe>(rhs) { }
+ static const char* Desc() { return "isn't equal to"; }
+ static const char* NegatedDesc() { return "is equal to"; }
+};
+template <typename Rhs>
+class LtMatcher : public ComparisonBase<LtMatcher<Rhs>, Rhs, AnyLt> {
+ public:
+ explicit LtMatcher(const Rhs& rhs)
+ : ComparisonBase<LtMatcher<Rhs>, Rhs, AnyLt>(rhs) { }
+ static const char* Desc() { return "is <"; }
+ static const char* NegatedDesc() { return "isn't <"; }
+};
+template <typename Rhs>
+class GtMatcher : public ComparisonBase<GtMatcher<Rhs>, Rhs, AnyGt> {
+ public:
+ explicit GtMatcher(const Rhs& rhs)
+ : ComparisonBase<GtMatcher<Rhs>, Rhs, AnyGt>(rhs) { }
+ static const char* Desc() { return "is >"; }
+ static const char* NegatedDesc() { return "isn't >"; }
+};
+template <typename Rhs>
+class LeMatcher : public ComparisonBase<LeMatcher<Rhs>, Rhs, AnyLe> {
+ public:
+ explicit LeMatcher(const Rhs& rhs)
+ : ComparisonBase<LeMatcher<Rhs>, Rhs, AnyLe>(rhs) { }
+ static const char* Desc() { return "is <="; }
+ static const char* NegatedDesc() { return "isn't <="; }
+};
+template <typename Rhs>
+class GeMatcher : public ComparisonBase<GeMatcher<Rhs>, Rhs, AnyGe> {
+ public:
+ explicit GeMatcher(const Rhs& rhs)
+ : ComparisonBase<GeMatcher<Rhs>, Rhs, AnyGe>(rhs) { }
+ static const char* Desc() { return "is >="; }
+ static const char* NegatedDesc() { return "isn't >="; }
+};
+
+// Implements polymorphic matchers MatchesRegex(regex) and
+// ContainsRegex(regex), which can be used as a Matcher<T> as long as
+// T can be converted to a string.
+class MatchesRegexMatcher {
+ public:
+ MatchesRegexMatcher(const RE* regex, bool full_match)
+ : regex_(regex), full_match_(full_match) {}
+
+#if GTEST_HAS_ABSL
+ bool MatchAndExplain(const absl::string_view& s,
+ MatchResultListener* listener) const {
+ return MatchAndExplain(std::string(s), listener);
+ }
+#endif // GTEST_HAS_ABSL
+
+ // Accepts pointer types, particularly:
+ // const char*
+ // char*
+ // const wchar_t*
+ // wchar_t*
+ template <typename CharType>
+ bool MatchAndExplain(CharType* s, MatchResultListener* listener) const {
+ return s != nullptr && MatchAndExplain(std::string(s), listener);
+ }
+
+ // Matches anything that can convert to std::string.
+ //
+ // This is a template, not just a plain function with const std::string&,
+ // because absl::string_view has some interfering non-explicit constructors.
+ template <class MatcheeStringType>
+ bool MatchAndExplain(const MatcheeStringType& s,
+ MatchResultListener* /* listener */) const {
+ const std::string& s2(s);
+ return full_match_ ? RE::FullMatch(s2, *regex_)
+ : RE::PartialMatch(s2, *regex_);
+ }
+
+ void DescribeTo(::std::ostream* os) const {
+ *os << (full_match_ ? "matches" : "contains") << " regular expression ";
+ UniversalPrinter<std::string>::Print(regex_->pattern(), os);
+ }
+
+ void DescribeNegationTo(::std::ostream* os) const {
+ *os << "doesn't " << (full_match_ ? "match" : "contain")
+ << " regular expression ";
+ UniversalPrinter<std::string>::Print(regex_->pattern(), os);
+ }
+
+ private:
+ const std::shared_ptr<const RE> regex_;
+ const bool full_match_;
+};
+} // namespace internal
+
+// Matches a string that fully matches regular expression 'regex'.
+// The matcher takes ownership of 'regex'.
+inline PolymorphicMatcher<internal::MatchesRegexMatcher> MatchesRegex(
+ const internal::RE* regex) {
+ return MakePolymorphicMatcher(internal::MatchesRegexMatcher(regex, true));
+}
+inline PolymorphicMatcher<internal::MatchesRegexMatcher> MatchesRegex(
+ const std::string& regex) {
+ return MatchesRegex(new internal::RE(regex));
+}
+
+// Matches a string that contains regular expression 'regex'.
+// The matcher takes ownership of 'regex'.
+inline PolymorphicMatcher<internal::MatchesRegexMatcher> ContainsRegex(
+ const internal::RE* regex) {
+ return MakePolymorphicMatcher(internal::MatchesRegexMatcher(regex, false));
+}
+inline PolymorphicMatcher<internal::MatchesRegexMatcher> ContainsRegex(
+ const std::string& regex) {
+ return ContainsRegex(new internal::RE(regex));
+}
+
+// Creates a polymorphic matcher that matches anything equal to x.
+// Note: if the parameter of Eq() were declared as const T&, Eq("foo")
+// wouldn't compile.
+template <typename T>
+inline internal::EqMatcher<T> Eq(T x) { return internal::EqMatcher<T>(x); }
+
+// Constructs a Matcher<T> from a 'value' of type T. The constructed
+// matcher matches any value that's equal to 'value'.
+template <typename T>
+Matcher<T>::Matcher(T value) { *this = Eq(value); }
+
+// Creates a monomorphic matcher that matches anything with type Lhs
+// and equal to rhs. A user may need to use this instead of Eq(...)
+// in order to resolve an overloading ambiguity.
+//
+// TypedEq<T>(x) is just a convenient short-hand for Matcher<T>(Eq(x))
+// or Matcher<T>(x), but more readable than the latter.
+//
+// We could define similar monomorphic matchers for other comparison
+// operations (e.g. TypedLt, TypedGe, and etc), but decided not to do
+// it yet as those are used much less than Eq() in practice. A user
+// can always write Matcher<T>(Lt(5)) to be explicit about the type,
+// for example.
+template <typename Lhs, typename Rhs>
+inline Matcher<Lhs> TypedEq(const Rhs& rhs) { return Eq(rhs); }
+
+// Creates a polymorphic matcher that matches anything >= x.
+template <typename Rhs>
+inline internal::GeMatcher<Rhs> Ge(Rhs x) {
+ return internal::GeMatcher<Rhs>(x);
+}
+
+// Creates a polymorphic matcher that matches anything > x.
+template <typename Rhs>
+inline internal::GtMatcher<Rhs> Gt(Rhs x) {
+ return internal::GtMatcher<Rhs>(x);
+}
+
+// Creates a polymorphic matcher that matches anything <= x.
+template <typename Rhs>
+inline internal::LeMatcher<Rhs> Le(Rhs x) {
+ return internal::LeMatcher<Rhs>(x);
+}
+
+// Creates a polymorphic matcher that matches anything < x.
+template <typename Rhs>
+inline internal::LtMatcher<Rhs> Lt(Rhs x) {
+ return internal::LtMatcher<Rhs>(x);
+}
+
+// Creates a polymorphic matcher that matches anything != x.
+template <typename Rhs>
+inline internal::NeMatcher<Rhs> Ne(Rhs x) {
+ return internal::NeMatcher<Rhs>(x);
+}
+} // namespace testing
+
+GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 5046
+
+#endif // GTEST_INCLUDE_GTEST_GTEST_MATCHERS_H_
diff --git a/extern/gtest/include/gtest/gtest-message.h b/extern/gtest/include/gtest/gtest-message.h
index fe879bca792..4a80e11e6b2 100644
--- a/extern/gtest/include/gtest/gtest-message.h
+++ b/extern/gtest/include/gtest/gtest-message.h
@@ -26,10 +26,9 @@
// 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: wan@google.com (Zhanyong Wan)
-//
-// The Google C++ Testing Framework (Google Test)
+// The Google C++ Testing and Mocking Framework (Google Test)
//
// This header file defines the Message class.
//
@@ -43,13 +42,19 @@
// to CHANGE WITHOUT NOTICE. Therefore DO NOT DEPEND ON IT in a user
// program!
+// GOOGLETEST_CM0001 DO NOT DELETE
+
#ifndef GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_
#define GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_
#include <limits>
+#include <memory>
#include "gtest/internal/gtest-port.h"
+GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
+/* class A needs to have dll-interface to be used by clients of class B */)
+
// Ensures that there is at least one operator<< in the global namespace.
// See Message& operator<<(...) below for why.
void operator<<(const testing::internal::Secret&, int);
@@ -102,14 +107,6 @@ class GTEST_API_ Message {
*ss_ << str;
}
-#if GTEST_OS_SYMBIAN
- // Streams a value (either a pointer or not) to this object.
- template <typename T>
- inline Message& operator <<(const T& value) {
- StreamHelper(typename internal::is_pointer<T>::type(), value);
- return *this;
- }
-#else
// Streams a non-pointer value to this object.
template <typename T>
inline Message& operator <<(const T& val) {
@@ -147,14 +144,13 @@ class GTEST_API_ Message {
// as "(null)".
template <typename T>
inline Message& operator <<(T* const& pointer) { // NOLINT
- if (pointer == NULL) {
+ if (pointer == nullptr) {
*ss_ << "(null)";
} else {
*ss_ << pointer;
}
return *this;
}
-#endif // GTEST_OS_SYMBIAN
// Since the basic IO manipulators are overloaded for both narrow
// and wide streams, we have to provide this specialized definition
@@ -183,12 +179,6 @@ class GTEST_API_ Message {
Message& operator <<(const ::std::wstring& wstr);
#endif // GTEST_HAS_STD_WSTRING
-#if GTEST_HAS_GLOBAL_WSTRING
- // Converts the given wide string to a narrow string using the UTF-8
- // encoding, and streams the result to this Message object.
- Message& operator <<(const ::wstring& wstr);
-#endif // GTEST_HAS_GLOBAL_WSTRING
-
// Gets the text streamed to this object so far as an std::string.
// Each '\0' character in the buffer is replaced with "\\0".
//
@@ -196,32 +186,8 @@ class GTEST_API_ Message {
std::string GetString() const;
private:
-
-#if GTEST_OS_SYMBIAN
- // These are needed as the Nokia Symbian Compiler cannot decide between
- // const T& and const T* in a function template. The Nokia compiler _can_
- // decide between class template specializations for T and T*, so a
- // tr1::type_traits-like is_pointer works, and we can overload on that.
- template <typename T>
- inline void StreamHelper(internal::true_type /*is_pointer*/, T* pointer) {
- if (pointer == NULL) {
- *ss_ << "(null)";
- } else {
- *ss_ << pointer;
- }
- }
- template <typename T>
- inline void StreamHelper(internal::false_type /*is_pointer*/,
- const T& value) {
- // See the comments in Message& operator <<(const T&) above for why
- // we need this using statement.
- using ::operator <<;
- *ss_ << value;
- }
-#endif // GTEST_OS_SYMBIAN
-
// We'll hold the text streamed to this object here.
- const internal::scoped_ptr< ::std::stringstream> ss_;
+ const std::unique_ptr< ::std::stringstream> ss_;
// We declare (but don't implement) this to prevent the compiler
// from implementing the assignment operator.
@@ -247,4 +213,6 @@ std::string StreamableToString(const T& streamable) {
} // namespace internal
} // namespace testing
+GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251
+
#endif // GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_
diff --git a/extern/gtest/include/gtest/gtest-param-test.h b/extern/gtest/include/gtest/gtest-param-test.h
index 038f9ba79eb..c2e6eae3d83 100644
--- a/extern/gtest/include/gtest/gtest-param-test.h
+++ b/extern/gtest/include/gtest/gtest-param-test.h
@@ -1,7 +1,3 @@
-// This file was GENERATED by command:
-// pump.py gtest-param-test.h.pump
-// DO NOT EDIT BY HAND!!!
-
// Copyright 2008, Google Inc.
// All rights reserved.
//
@@ -31,13 +27,12 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
-// Authors: vladl@google.com (Vlad Losev)
-//
// Macros and functions for implementing parameterized tests
-// in Google C++ Testing Framework (Google Test)
+// in Google C++ Testing and Mocking Framework (Google Test)
//
// This file is generated by a SCRIPT. DO NOT EDIT BY HAND!
//
+// GOOGLETEST_CM0001 DO NOT DELETE
#ifndef GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_
#define GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_
@@ -76,10 +71,10 @@ TEST_P(FooTest, HasBlahBlah) {
...
}
-// Finally, you can use INSTANTIATE_TEST_CASE_P to instantiate the test
+// Finally, you can use INSTANTIATE_TEST_SUITE_P to instantiate the test
// case with any set of parameters you want. Google Test defines a number
// of functions for generating test parameters. They return what we call
-// (surprise!) parameter generators. Here is a summary of them, which
+// (surprise!) parameter generators. Here is a summary of them, which
// are all in the testing namespace:
//
//
@@ -97,17 +92,17 @@ TEST_P(FooTest, HasBlahBlah) {
// For more details, see comments at the definitions of these functions below
// in this file.
//
-// The following statement will instantiate tests from the FooTest test case
+// The following statement will instantiate tests from the FooTest test suite
// each with parameter values "meeny", "miny", and "moe".
-INSTANTIATE_TEST_CASE_P(InstantiationName,
- FooTest,
- Values("meeny", "miny", "moe"));
+INSTANTIATE_TEST_SUITE_P(InstantiationName,
+ FooTest,
+ Values("meeny", "miny", "moe"));
// To distinguish different instances of the pattern, (yes, you
-// can instantiate it more then once) the first argument to the
-// INSTANTIATE_TEST_CASE_P macro is a prefix that will be added to the
-// actual test case name. Remember to pick unique prefixes for different
+// can instantiate it more than once) the first argument to the
+// INSTANTIATE_TEST_SUITE_P macro is a prefix that will be added to the
+// actual test suite name. Remember to pick unique prefixes for different
// instantiations. The tests from the instantiation above will have
// these names:
//
@@ -124,7 +119,7 @@ INSTANTIATE_TEST_CASE_P(InstantiationName,
// with parameter values "cat" and "dog":
const char* pets[] = {"cat", "dog"};
-INSTANTIATE_TEST_CASE_P(AnotherInstantiationName, FooTest, ValuesIn(pets));
+INSTANTIATE_TEST_SUITE_P(AnotherInstantiationName, FooTest, ValuesIn(pets));
// The tests from the instantiation above will have these names:
//
@@ -133,9 +128,9 @@ INSTANTIATE_TEST_CASE_P(AnotherInstantiationName, FooTest, ValuesIn(pets));
// * AnotherInstantiationName/FooTest.HasBlahBlah/0 for "cat"
// * AnotherInstantiationName/FooTest.HasBlahBlah/1 for "dog"
//
-// Please note that INSTANTIATE_TEST_CASE_P will instantiate all tests
-// in the given test case, whether their definitions come before or
-// AFTER the INSTANTIATE_TEST_CASE_P statement.
+// Please note that INSTANTIATE_TEST_SUITE_P will instantiate all tests
+// in the given test suite, whether their definitions come before or
+// AFTER the INSTANTIATE_TEST_SUITE_P statement.
//
// Please also note that generator expressions (including parameters to the
// generators) are evaluated in InitGoogleTest(), after main() has started.
@@ -179,31 +174,23 @@ TEST_P(DerivedTest, DoesBlah) {
#endif // 0
-#include "gtest/internal/gtest-port.h"
-
-#if !GTEST_OS_SYMBIAN
-# include <utility>
-#endif
+#include <iterator>
+#include <utility>
-// scripts/fuse_gtest.py depends on gtest's own header being #included
-// *unconditionally*. Therefore these #includes cannot be moved
-// inside #if GTEST_HAS_PARAM_TEST.
#include "gtest/internal/gtest-internal.h"
#include "gtest/internal/gtest-param-util.h"
-#include "gtest/internal/gtest-param-util-generated.h"
-
-#if GTEST_HAS_PARAM_TEST
+#include "gtest/internal/gtest-port.h"
namespace testing {
// Functions producing parameter generators.
//
// Google Test uses these generators to produce parameters for value-
-// parameterized tests. When a parameterized test case is instantiated
+// parameterized tests. When a parameterized test suite is instantiated
// with a particular generator, Google Test creates and runs tests
// for each element in the sequence produced by the generator.
//
-// In the following sample, tests from test case FooTest are instantiated
+// In the following sample, tests from test suite FooTest are instantiated
// each three times with parameter values 3, 5, and 8:
//
// class FooTest : public TestWithParam<int> { ... };
@@ -212,7 +199,7 @@ namespace testing {
// }
// TEST_P(FooTest, TestThat) {
// }
-// INSTANTIATE_TEST_CASE_P(TestSequence, FooTest, Values(3, 5, 8));
+// INSTANTIATE_TEST_SUITE_P(TestSequence, FooTest, Values(3, 5, 8));
//
// Range() returns generators providing sequences of values in a range.
@@ -269,13 +256,13 @@ internal::ParamGenerator<T> Range(T start, T end) {
//
// Examples:
//
-// This instantiates tests from test case StringTest
+// This instantiates tests from test suite StringTest
// each with C-string values of "foo", "bar", and "baz":
//
// const char* strings[] = {"foo", "bar", "baz"};
-// INSTANTIATE_TEST_CASE_P(StringSequence, SrtingTest, ValuesIn(strings));
+// INSTANTIATE_TEST_SUITE_P(StringSequence, StringTest, ValuesIn(strings));
//
-// This instantiates tests from test case StlStringTest
+// This instantiates tests from test suite StlStringTest
// each with STL strings with values "a" and "b":
//
// ::std::vector< ::std::string> GetParameterStrings() {
@@ -285,9 +272,9 @@ internal::ParamGenerator<T> Range(T start, T end) {
// return v;
// }
//
-// INSTANTIATE_TEST_CASE_P(CharSequence,
-// StlStringTest,
-// ValuesIn(GetParameterStrings()));
+// INSTANTIATE_TEST_SUITE_P(CharSequence,
+// StlStringTest,
+// ValuesIn(GetParameterStrings()));
//
//
// This will also instantiate tests from CharTest
@@ -300,16 +287,15 @@ internal::ParamGenerator<T> Range(T start, T end) {
// return list;
// }
// ::std::list<char> l = GetParameterChars();
-// INSTANTIATE_TEST_CASE_P(CharSequence2,
-// CharTest,
-// ValuesIn(l.begin(), l.end()));
+// INSTANTIATE_TEST_SUITE_P(CharSequence2,
+// CharTest,
+// ValuesIn(l.begin(), l.end()));
//
template <typename ForwardIterator>
internal::ParamGenerator<
- typename ::testing::internal::IteratorTraits<ForwardIterator>::value_type>
+ typename std::iterator_traits<ForwardIterator>::value_type>
ValuesIn(ForwardIterator begin, ForwardIterator end) {
- typedef typename ::testing::internal::IteratorTraits<ForwardIterator>
- ::value_type ParamType;
+ typedef typename std::iterator_traits<ForwardIterator>::value_type ParamType;
return internal::ParamGenerator<ParamType>(
new internal::ValuesInIteratorRangeGenerator<ParamType>(begin, end));
}
@@ -332,869 +318,22 @@ internal::ParamGenerator<typename Container::value_type> ValuesIn(
// Values(T v1, T v2, ..., T vN)
// - returns a generator producing sequences with elements v1, v2, ..., vN.
//
-// For example, this instantiates tests from test case BarTest each
+// For example, this instantiates tests from test suite BarTest each
// with values "one", "two", and "three":
//
-// INSTANTIATE_TEST_CASE_P(NumSequence, BarTest, Values("one", "two", "three"));
+// INSTANTIATE_TEST_SUITE_P(NumSequence,
+// BarTest,
+// Values("one", "two", "three"));
//
-// This instantiates tests from test case BazTest each with values 1, 2, 3.5.
+// This instantiates tests from test suite BazTest each with values 1, 2, 3.5.
// The exact type of values will depend on the type of parameter in BazTest.
//
-// INSTANTIATE_TEST_CASE_P(FloatingNumbers, BazTest, Values(1, 2, 3.5));
+// INSTANTIATE_TEST_SUITE_P(FloatingNumbers, BazTest, Values(1, 2, 3.5));
//
-// Currently, Values() supports from 1 to 50 parameters.
//
-template <typename T1>
-internal::ValueArray1<T1> Values(T1 v1) {
- return internal::ValueArray1<T1>(v1);
-}
-
-template <typename T1, typename T2>
-internal::ValueArray2<T1, T2> Values(T1 v1, T2 v2) {
- return internal::ValueArray2<T1, T2>(v1, v2);
-}
-
-template <typename T1, typename T2, typename T3>
-internal::ValueArray3<T1, T2, T3> Values(T1 v1, T2 v2, T3 v3) {
- return internal::ValueArray3<T1, T2, T3>(v1, v2, v3);
-}
-
-template <typename T1, typename T2, typename T3, typename T4>
-internal::ValueArray4<T1, T2, T3, T4> Values(T1 v1, T2 v2, T3 v3, T4 v4) {
- return internal::ValueArray4<T1, T2, T3, T4>(v1, v2, v3, v4);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5>
-internal::ValueArray5<T1, T2, T3, T4, T5> Values(T1 v1, T2 v2, T3 v3, T4 v4,
- T5 v5) {
- return internal::ValueArray5<T1, T2, T3, T4, T5>(v1, v2, v3, v4, v5);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6>
-internal::ValueArray6<T1, T2, T3, T4, T5, T6> Values(T1 v1, T2 v2, T3 v3,
- T4 v4, T5 v5, T6 v6) {
- return internal::ValueArray6<T1, T2, T3, T4, T5, T6>(v1, v2, v3, v4, v5, v6);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7>
-internal::ValueArray7<T1, T2, T3, T4, T5, T6, T7> Values(T1 v1, T2 v2, T3 v3,
- T4 v4, T5 v5, T6 v6, T7 v7) {
- return internal::ValueArray7<T1, T2, T3, T4, T5, T6, T7>(v1, v2, v3, v4, v5,
- v6, v7);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8>
-internal::ValueArray8<T1, T2, T3, T4, T5, T6, T7, T8> Values(T1 v1, T2 v2,
- T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8) {
- return internal::ValueArray8<T1, T2, T3, T4, T5, T6, T7, T8>(v1, v2, v3, v4,
- v5, v6, v7, v8);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9>
-internal::ValueArray9<T1, T2, T3, T4, T5, T6, T7, T8, T9> Values(T1 v1, T2 v2,
- T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9) {
- return internal::ValueArray9<T1, T2, T3, T4, T5, T6, T7, T8, T9>(v1, v2, v3,
- v4, v5, v6, v7, v8, v9);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10>
-internal::ValueArray10<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> Values(T1 v1,
- T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10) {
- return internal::ValueArray10<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(v1,
- v2, v3, v4, v5, v6, v7, v8, v9, v10);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11>
-internal::ValueArray11<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10,
- T11> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11) {
- return internal::ValueArray11<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10,
- T11>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12>
-internal::ValueArray12<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
- T12> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12) {
- return internal::ValueArray12<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
- T12>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13>
-internal::ValueArray13<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
- T13> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13) {
- return internal::ValueArray13<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
- T12, T13>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14>
-internal::ValueArray14<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
- T14> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13, T14 v14) {
- return internal::ValueArray14<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
- T12, T13, T14>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13,
- v14);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15>
-internal::ValueArray15<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
- T14, T15> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8,
- T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15) {
- return internal::ValueArray15<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
- T12, T13, T14, T15>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12,
- v13, v14, v15);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16>
-internal::ValueArray16<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
- T14, T15, T16> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7,
- T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15,
- T16 v16) {
- return internal::ValueArray16<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
- T12, T13, T14, T15, T16>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11,
- v12, v13, v14, v15, v16);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17>
-internal::ValueArray17<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
- T14, T15, T16, T17> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7,
- T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15,
- T16 v16, T17 v17) {
- return internal::ValueArray17<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
- T12, T13, T14, T15, T16, T17>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10,
- v11, v12, v13, v14, v15, v16, v17);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18>
-internal::ValueArray18<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
- T14, T15, T16, T17, T18> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6,
- T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15,
- T16 v16, T17 v17, T18 v18) {
- return internal::ValueArray18<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
- T12, T13, T14, T15, T16, T17, T18>(v1, v2, v3, v4, v5, v6, v7, v8, v9,
- v10, v11, v12, v13, v14, v15, v16, v17, v18);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19>
-internal::ValueArray19<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
- T14, T15, T16, T17, T18, T19> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5,
- T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14,
- T15 v15, T16 v16, T17 v17, T18 v18, T19 v19) {
- return internal::ValueArray19<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
- T12, T13, T14, T15, T16, T17, T18, T19>(v1, v2, v3, v4, v5, v6, v7, v8,
- v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20>
-internal::ValueArray20<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
- T14, T15, T16, T17, T18, T19, T20> Values(T1 v1, T2 v2, T3 v3, T4 v4,
- T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13,
- T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20) {
- return internal::ValueArray20<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
- T12, T13, T14, T15, T16, T17, T18, T19, T20>(v1, v2, v3, v4, v5, v6, v7,
- v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21>
-internal::ValueArray21<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
- T14, T15, T16, T17, T18, T19, T20, T21> Values(T1 v1, T2 v2, T3 v3, T4 v4,
- T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13,
- T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21) {
- return internal::ValueArray21<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
- T12, T13, T14, T15, T16, T17, T18, T19, T20, T21>(v1, v2, v3, v4, v5, v6,
- v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22>
-internal::ValueArray22<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
- T14, T15, T16, T17, T18, T19, T20, T21, T22> Values(T1 v1, T2 v2, T3 v3,
- T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12,
- T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20,
- T21 v21, T22 v22) {
- return internal::ValueArray22<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
- T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22>(v1, v2, v3, v4,
- v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19,
- v20, v21, v22);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23>
-internal::ValueArray23<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
- T14, T15, T16, T17, T18, T19, T20, T21, T22, T23> Values(T1 v1, T2 v2,
- T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12,
- T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20,
- T21 v21, T22 v22, T23 v23) {
- return internal::ValueArray23<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
- T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23>(v1, v2, v3,
- v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19,
- v20, v21, v22, v23);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24>
-internal::ValueArray24<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
- T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24> Values(T1 v1, T2 v2,
- T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12,
- T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20,
- T21 v21, T22 v22, T23 v23, T24 v24) {
- return internal::ValueArray24<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
- T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24>(v1, v2,
- v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18,
- v19, v20, v21, v22, v23, v24);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25>
-internal::ValueArray25<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
- T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25> Values(T1 v1,
- T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11,
- T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19,
- T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25) {
- return internal::ValueArray25<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
- T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25>(v1,
- v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17,
- v18, v19, v20, v21, v22, v23, v24, v25);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26>
-internal::ValueArray26<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
- T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
- T26> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
- T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
- T26 v26) {
- return internal::ValueArray26<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
- T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
- T26>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15,
- v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27>
-internal::ValueArray27<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
- T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
- T27> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
- T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
- T26 v26, T27 v27) {
- return internal::ValueArray27<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
- T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
- T26, T27>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14,
- v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27, typename T28>
-internal::ValueArray28<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
- T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
- T28> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
- T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
- T26 v26, T27 v27, T28 v28) {
- return internal::ValueArray28<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
- T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
- T26, T27, T28>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13,
- v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27,
- v28);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27, typename T28, typename T29>
-internal::ValueArray29<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
- T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
- T29> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
- T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
- T26 v26, T27 v27, T28 v28, T29 v29) {
- return internal::ValueArray29<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
- T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
- T26, T27, T28, T29>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12,
- v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26,
- v27, v28, v29);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27, typename T28, typename T29, typename T30>
-internal::ValueArray30<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
- T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
- T29, T30> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8,
- T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16,
- T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24,
- T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30) {
- return internal::ValueArray30<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
- T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
- T26, T27, T28, T29, T30>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11,
- v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25,
- v26, v27, v28, v29, v30);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27, typename T28, typename T29, typename T30,
- typename T31>
-internal::ValueArray31<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
- T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
- T29, T30, T31> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7,
- T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15,
- T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23,
- T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31) {
- return internal::ValueArray31<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
- T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
- T26, T27, T28, T29, T30, T31>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10,
- v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24,
- v25, v26, v27, v28, v29, v30, v31);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27, typename T28, typename T29, typename T30,
- typename T31, typename T32>
-internal::ValueArray32<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
- T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
- T29, T30, T31, T32> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7,
- T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15,
- T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23,
- T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31,
- T32 v32) {
- return internal::ValueArray32<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
- T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
- T26, T27, T28, T29, T30, T31, T32>(v1, v2, v3, v4, v5, v6, v7, v8, v9,
- v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23,
- v24, v25, v26, v27, v28, v29, v30, v31, v32);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27, typename T28, typename T29, typename T30,
- typename T31, typename T32, typename T33>
-internal::ValueArray33<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
- T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
- T29, T30, T31, T32, T33> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6,
- T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15,
- T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23,
- T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31,
- T32 v32, T33 v33) {
- return internal::ValueArray33<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
- T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
- T26, T27, T28, T29, T30, T31, T32, T33>(v1, v2, v3, v4, v5, v6, v7, v8,
- v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23,
- v24, v25, v26, v27, v28, v29, v30, v31, v32, v33);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27, typename T28, typename T29, typename T30,
- typename T31, typename T32, typename T33, typename T34>
-internal::ValueArray34<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
- T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
- T29, T30, T31, T32, T33, T34> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5,
- T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14,
- T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22,
- T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30,
- T31 v31, T32 v32, T33 v33, T34 v34) {
- return internal::ValueArray34<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
- T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
- T26, T27, T28, T29, T30, T31, T32, T33, T34>(v1, v2, v3, v4, v5, v6, v7,
- v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22,
- v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27, typename T28, typename T29, typename T30,
- typename T31, typename T32, typename T33, typename T34, typename T35>
-internal::ValueArray35<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
- T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
- T29, T30, T31, T32, T33, T34, T35> Values(T1 v1, T2 v2, T3 v3, T4 v4,
- T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13,
- T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21,
- T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29,
- T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35) {
- return internal::ValueArray35<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
- T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
- T26, T27, T28, T29, T30, T31, T32, T33, T34, T35>(v1, v2, v3, v4, v5, v6,
- v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21,
- v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27, typename T28, typename T29, typename T30,
- typename T31, typename T32, typename T33, typename T34, typename T35,
- typename T36>
-internal::ValueArray36<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
- T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
- T29, T30, T31, T32, T33, T34, T35, T36> Values(T1 v1, T2 v2, T3 v3, T4 v4,
- T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13,
- T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21,
- T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29,
- T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36) {
- return internal::ValueArray36<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
- T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
- T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36>(v1, v2, v3, v4,
- v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19,
- v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33,
- v34, v35, v36);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27, typename T28, typename T29, typename T30,
- typename T31, typename T32, typename T33, typename T34, typename T35,
- typename T36, typename T37>
-internal::ValueArray37<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
- T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
- T29, T30, T31, T32, T33, T34, T35, T36, T37> Values(T1 v1, T2 v2, T3 v3,
- T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12,
- T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20,
- T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28,
- T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36,
- T37 v37) {
- return internal::ValueArray37<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
- T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
- T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37>(v1, v2, v3,
- v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19,
- v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33,
- v34, v35, v36, v37);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27, typename T28, typename T29, typename T30,
- typename T31, typename T32, typename T33, typename T34, typename T35,
- typename T36, typename T37, typename T38>
-internal::ValueArray38<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
- T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
- T29, T30, T31, T32, T33, T34, T35, T36, T37, T38> Values(T1 v1, T2 v2,
- T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12,
- T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20,
- T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28,
- T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36,
- T37 v37, T38 v38) {
- return internal::ValueArray38<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
- T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
- T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38>(v1, v2,
- v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18,
- v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32,
- v33, v34, v35, v36, v37, v38);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27, typename T28, typename T29, typename T30,
- typename T31, typename T32, typename T33, typename T34, typename T35,
- typename T36, typename T37, typename T38, typename T39>
-internal::ValueArray39<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
- T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
- T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39> Values(T1 v1, T2 v2,
- T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12,
- T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20,
- T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28,
- T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36,
- T37 v37, T38 v38, T39 v39) {
- return internal::ValueArray39<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
- T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
- T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39>(v1,
- v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17,
- v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31,
- v32, v33, v34, v35, v36, v37, v38, v39);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27, typename T28, typename T29, typename T30,
- typename T31, typename T32, typename T33, typename T34, typename T35,
- typename T36, typename T37, typename T38, typename T39, typename T40>
-internal::ValueArray40<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
- T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
- T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40> Values(T1 v1,
- T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11,
- T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19,
- T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27,
- T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35,
- T36 v36, T37 v37, T38 v38, T39 v39, T40 v40) {
- return internal::ValueArray40<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
- T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
- T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
- T40>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15,
- v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29,
- v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27, typename T28, typename T29, typename T30,
- typename T31, typename T32, typename T33, typename T34, typename T35,
- typename T36, typename T37, typename T38, typename T39, typename T40,
- typename T41>
-internal::ValueArray41<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
- T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
- T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
- T41> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
- T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
- T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
- T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41) {
- return internal::ValueArray41<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
- T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
- T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
- T40, T41>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14,
- v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28,
- v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27, typename T28, typename T29, typename T30,
- typename T31, typename T32, typename T33, typename T34, typename T35,
- typename T36, typename T37, typename T38, typename T39, typename T40,
- typename T41, typename T42>
-internal::ValueArray42<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
- T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
- T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41,
- T42> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
- T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
- T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
- T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
- T42 v42) {
- return internal::ValueArray42<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
- T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
- T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
- T40, T41, T42>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13,
- v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27,
- v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41,
- v42);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27, typename T28, typename T29, typename T30,
- typename T31, typename T32, typename T33, typename T34, typename T35,
- typename T36, typename T37, typename T38, typename T39, typename T40,
- typename T41, typename T42, typename T43>
-internal::ValueArray43<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
- T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
- T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42,
- T43> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
- T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
- T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
- T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
- T42 v42, T43 v43) {
- return internal::ValueArray43<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
- T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
- T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
- T40, T41, T42, T43>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12,
- v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26,
- v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40,
- v41, v42, v43);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27, typename T28, typename T29, typename T30,
- typename T31, typename T32, typename T33, typename T34, typename T35,
- typename T36, typename T37, typename T38, typename T39, typename T40,
- typename T41, typename T42, typename T43, typename T44>
-internal::ValueArray44<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
- T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
- T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
- T44> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
- T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
- T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
- T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
- T42 v42, T43 v43, T44 v44) {
- return internal::ValueArray44<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
- T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
- T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
- T40, T41, T42, T43, T44>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11,
- v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25,
- v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39,
- v40, v41, v42, v43, v44);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27, typename T28, typename T29, typename T30,
- typename T31, typename T32, typename T33, typename T34, typename T35,
- typename T36, typename T37, typename T38, typename T39, typename T40,
- typename T41, typename T42, typename T43, typename T44, typename T45>
-internal::ValueArray45<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
- T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
- T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
- T44, T45> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8,
- T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16,
- T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24,
- T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32,
- T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40,
- T41 v41, T42 v42, T43 v43, T44 v44, T45 v45) {
- return internal::ValueArray45<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
- T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
- T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
- T40, T41, T42, T43, T44, T45>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10,
- v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24,
- v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38,
- v39, v40, v41, v42, v43, v44, v45);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27, typename T28, typename T29, typename T30,
- typename T31, typename T32, typename T33, typename T34, typename T35,
- typename T36, typename T37, typename T38, typename T39, typename T40,
- typename T41, typename T42, typename T43, typename T44, typename T45,
- typename T46>
-internal::ValueArray46<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
- T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
- T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
- T44, T45, T46> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7,
- T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15,
- T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23,
- T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31,
- T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39,
- T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46) {
- return internal::ValueArray46<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
- T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
- T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
- T40, T41, T42, T43, T44, T45, T46>(v1, v2, v3, v4, v5, v6, v7, v8, v9,
- v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23,
- v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37,
- v38, v39, v40, v41, v42, v43, v44, v45, v46);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27, typename T28, typename T29, typename T30,
- typename T31, typename T32, typename T33, typename T34, typename T35,
- typename T36, typename T37, typename T38, typename T39, typename T40,
- typename T41, typename T42, typename T43, typename T44, typename T45,
- typename T46, typename T47>
-internal::ValueArray47<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
- T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
- T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
- T44, T45, T46, T47> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7,
- T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15,
- T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23,
- T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31,
- T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39,
- T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47) {
- return internal::ValueArray47<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
- T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
- T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
- T40, T41, T42, T43, T44, T45, T46, T47>(v1, v2, v3, v4, v5, v6, v7, v8,
- v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23,
- v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37,
- v38, v39, v40, v41, v42, v43, v44, v45, v46, v47);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27, typename T28, typename T29, typename T30,
- typename T31, typename T32, typename T33, typename T34, typename T35,
- typename T36, typename T37, typename T38, typename T39, typename T40,
- typename T41, typename T42, typename T43, typename T44, typename T45,
- typename T46, typename T47, typename T48>
-internal::ValueArray48<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
- T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
- T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
- T44, T45, T46, T47, T48> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6,
- T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15,
- T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23,
- T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31,
- T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39,
- T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47,
- T48 v48) {
- return internal::ValueArray48<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
- T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
- T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
- T40, T41, T42, T43, T44, T45, T46, T47, T48>(v1, v2, v3, v4, v5, v6, v7,
- v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22,
- v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36,
- v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27, typename T28, typename T29, typename T30,
- typename T31, typename T32, typename T33, typename T34, typename T35,
- typename T36, typename T37, typename T38, typename T39, typename T40,
- typename T41, typename T42, typename T43, typename T44, typename T45,
- typename T46, typename T47, typename T48, typename T49>
-internal::ValueArray49<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
- T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
- T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
- T44, T45, T46, T47, T48, T49> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5,
- T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14,
- T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22,
- T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30,
- T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38,
- T39 v39, T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46,
- T47 v47, T48 v48, T49 v49) {
- return internal::ValueArray49<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
- T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
- T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
- T40, T41, T42, T43, T44, T45, T46, T47, T48, T49>(v1, v2, v3, v4, v5, v6,
- v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21,
- v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35,
- v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27, typename T28, typename T29, typename T30,
- typename T31, typename T32, typename T33, typename T34, typename T35,
- typename T36, typename T37, typename T38, typename T39, typename T40,
- typename T41, typename T42, typename T43, typename T44, typename T45,
- typename T46, typename T47, typename T48, typename T49, typename T50>
-internal::ValueArray50<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
- T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
- T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
- T44, T45, T46, T47, T48, T49, T50> Values(T1 v1, T2 v2, T3 v3, T4 v4,
- T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13,
- T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21,
- T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29,
- T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37,
- T38 v38, T39 v39, T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45,
- T46 v46, T47 v47, T48 v48, T49 v49, T50 v50) {
- return internal::ValueArray50<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
- T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
- T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
- T40, T41, T42, T43, T44, T45, T46, T47, T48, T49, T50>(v1, v2, v3, v4,
- v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19,
- v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33,
- v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47,
- v48, v49, v50);
+template <typename... T>
+internal::ValueArray<T...> Values(T... v) {
+ return internal::ValueArray<T...>(std::move(v)...);
}
// Bool() allows generating tests with parameters in a set of (false, true).
@@ -1207,7 +346,7 @@ internal::ValueArray50<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
// of multiple flags can be tested when several Bool()'s are combined using
// Combine() function.
//
-// In the following example all tests in the test case FlagDependentTest
+// In the following example all tests in the test suite FlagDependentTest
// will be instantiated twice with parameters false and true.
//
// class FlagDependentTest : public testing::TestWithParam<bool> {
@@ -1215,13 +354,12 @@ internal::ValueArray50<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
// external_flag = GetParam();
// }
// }
-// INSTANTIATE_TEST_CASE_P(BoolSequence, FlagDependentTest, Bool());
+// INSTANTIATE_TEST_SUITE_P(BoolSequence, FlagDependentTest, Bool());
//
inline internal::ParamGenerator<bool> Bool() {
return Values(false, true);
}
-# if GTEST_HAS_COMBINE
// Combine() allows the user to combine two or more sequences to produce
// values of a Cartesian product of those sequences' elements.
//
@@ -1230,215 +368,136 @@ inline internal::ParamGenerator<bool> Bool() {
// - returns a generator producing sequences with elements coming from
// the Cartesian product of elements from the sequences generated by
// gen1, gen2, ..., genN. The sequence elements will have a type of
-// tuple<T1, T2, ..., TN> where T1, T2, ..., TN are the types
+// std::tuple<T1, T2, ..., TN> where T1, T2, ..., TN are the types
// of elements from sequences produces by gen1, gen2, ..., genN.
//
-// Combine can have up to 10 arguments. This number is currently limited
-// by the maximum number of elements in the tuple implementation used by Google
-// Test.
+// Combine can have up to 10 arguments.
//
// Example:
//
-// This will instantiate tests in test case AnimalTest each one with
+// This will instantiate tests in test suite AnimalTest each one with
// the parameter values tuple("cat", BLACK), tuple("cat", WHITE),
// tuple("dog", BLACK), and tuple("dog", WHITE):
//
// enum Color { BLACK, GRAY, WHITE };
// class AnimalTest
-// : public testing::TestWithParam<tuple<const char*, Color> > {...};
+// : public testing::TestWithParam<std::tuple<const char*, Color> > {...};
//
// TEST_P(AnimalTest, AnimalLooksNice) {...}
//
-// INSTANTIATE_TEST_CASE_P(AnimalVariations, AnimalTest,
-// Combine(Values("cat", "dog"),
-// Values(BLACK, WHITE)));
+// INSTANTIATE_TEST_SUITE_P(AnimalVariations, AnimalTest,
+// Combine(Values("cat", "dog"),
+// Values(BLACK, WHITE)));
//
// This will instantiate tests in FlagDependentTest with all variations of two
// Boolean flags:
//
// class FlagDependentTest
-// : public testing::TestWithParam<tuple<bool, bool> > {
+// : public testing::TestWithParam<std::tuple<bool, bool> > {
// virtual void SetUp() {
// // Assigns external_flag_1 and external_flag_2 values from the tuple.
-// tie(external_flag_1, external_flag_2) = GetParam();
+// std::tie(external_flag_1, external_flag_2) = GetParam();
// }
// };
//
// TEST_P(FlagDependentTest, TestFeature1) {
// // Test your code using external_flag_1 and external_flag_2 here.
// }
-// INSTANTIATE_TEST_CASE_P(TwoBoolSequence, FlagDependentTest,
-// Combine(Bool(), Bool()));
-//
-template <typename Generator1, typename Generator2>
-internal::CartesianProductHolder2<Generator1, Generator2> Combine(
- const Generator1& g1, const Generator2& g2) {
- return internal::CartesianProductHolder2<Generator1, Generator2>(
- g1, g2);
-}
-
-template <typename Generator1, typename Generator2, typename Generator3>
-internal::CartesianProductHolder3<Generator1, Generator2, Generator3> Combine(
- const Generator1& g1, const Generator2& g2, const Generator3& g3) {
- return internal::CartesianProductHolder3<Generator1, Generator2, Generator3>(
- g1, g2, g3);
-}
-
-template <typename Generator1, typename Generator2, typename Generator3,
- typename Generator4>
-internal::CartesianProductHolder4<Generator1, Generator2, Generator3,
- Generator4> Combine(
- const Generator1& g1, const Generator2& g2, const Generator3& g3,
- const Generator4& g4) {
- return internal::CartesianProductHolder4<Generator1, Generator2, Generator3,
- Generator4>(
- g1, g2, g3, g4);
-}
-
-template <typename Generator1, typename Generator2, typename Generator3,
- typename Generator4, typename Generator5>
-internal::CartesianProductHolder5<Generator1, Generator2, Generator3,
- Generator4, Generator5> Combine(
- const Generator1& g1, const Generator2& g2, const Generator3& g3,
- const Generator4& g4, const Generator5& g5) {
- return internal::CartesianProductHolder5<Generator1, Generator2, Generator3,
- Generator4, Generator5>(
- g1, g2, g3, g4, g5);
-}
-
-template <typename Generator1, typename Generator2, typename Generator3,
- typename Generator4, typename Generator5, typename Generator6>
-internal::CartesianProductHolder6<Generator1, Generator2, Generator3,
- Generator4, Generator5, Generator6> Combine(
- const Generator1& g1, const Generator2& g2, const Generator3& g3,
- const Generator4& g4, const Generator5& g5, const Generator6& g6) {
- return internal::CartesianProductHolder6<Generator1, Generator2, Generator3,
- Generator4, Generator5, Generator6>(
- g1, g2, g3, g4, g5, g6);
-}
-
-template <typename Generator1, typename Generator2, typename Generator3,
- typename Generator4, typename Generator5, typename Generator6,
- typename Generator7>
-internal::CartesianProductHolder7<Generator1, Generator2, Generator3,
- Generator4, Generator5, Generator6, Generator7> Combine(
- const Generator1& g1, const Generator2& g2, const Generator3& g3,
- const Generator4& g4, const Generator5& g5, const Generator6& g6,
- const Generator7& g7) {
- return internal::CartesianProductHolder7<Generator1, Generator2, Generator3,
- Generator4, Generator5, Generator6, Generator7>(
- g1, g2, g3, g4, g5, g6, g7);
-}
-
-template <typename Generator1, typename Generator2, typename Generator3,
- typename Generator4, typename Generator5, typename Generator6,
- typename Generator7, typename Generator8>
-internal::CartesianProductHolder8<Generator1, Generator2, Generator3,
- Generator4, Generator5, Generator6, Generator7, Generator8> Combine(
- const Generator1& g1, const Generator2& g2, const Generator3& g3,
- const Generator4& g4, const Generator5& g5, const Generator6& g6,
- const Generator7& g7, const Generator8& g8) {
- return internal::CartesianProductHolder8<Generator1, Generator2, Generator3,
- Generator4, Generator5, Generator6, Generator7, Generator8>(
- g1, g2, g3, g4, g5, g6, g7, g8);
-}
-
-template <typename Generator1, typename Generator2, typename Generator3,
- typename Generator4, typename Generator5, typename Generator6,
- typename Generator7, typename Generator8, typename Generator9>
-internal::CartesianProductHolder9<Generator1, Generator2, Generator3,
- Generator4, Generator5, Generator6, Generator7, Generator8,
- Generator9> Combine(
- const Generator1& g1, const Generator2& g2, const Generator3& g3,
- const Generator4& g4, const Generator5& g5, const Generator6& g6,
- const Generator7& g7, const Generator8& g8, const Generator9& g9) {
- return internal::CartesianProductHolder9<Generator1, Generator2, Generator3,
- Generator4, Generator5, Generator6, Generator7, Generator8, Generator9>(
- g1, g2, g3, g4, g5, g6, g7, g8, g9);
-}
-
-template <typename Generator1, typename Generator2, typename Generator3,
- typename Generator4, typename Generator5, typename Generator6,
- typename Generator7, typename Generator8, typename Generator9,
- typename Generator10>
-internal::CartesianProductHolder10<Generator1, Generator2, Generator3,
- Generator4, Generator5, Generator6, Generator7, Generator8, Generator9,
- Generator10> Combine(
- const Generator1& g1, const Generator2& g2, const Generator3& g3,
- const Generator4& g4, const Generator5& g5, const Generator6& g6,
- const Generator7& g7, const Generator8& g8, const Generator9& g9,
- const Generator10& g10) {
- return internal::CartesianProductHolder10<Generator1, Generator2, Generator3,
- Generator4, Generator5, Generator6, Generator7, Generator8, Generator9,
- Generator10>(
- g1, g2, g3, g4, g5, g6, g7, g8, g9, g10);
-}
-# endif // GTEST_HAS_COMBINE
-
-
-
-# define TEST_P(test_case_name, test_name) \
- class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) \
- : public test_case_name { \
- public: \
- GTEST_TEST_CLASS_NAME_(test_case_name, test_name)() {} \
- virtual void TestBody(); \
- private: \
- static int AddToRegistry() { \
- ::testing::UnitTest::GetInstance()->parameterized_test_registry(). \
- GetTestCasePatternHolder<test_case_name>(\
- #test_case_name, \
- ::testing::internal::CodeLocation(\
- __FILE__, __LINE__))->AddTestPattern(\
- #test_case_name, \
- #test_name, \
- new ::testing::internal::TestMetaFactory< \
- GTEST_TEST_CLASS_NAME_(\
- test_case_name, test_name)>()); \
- return 0; \
- } \
- static int gtest_registering_dummy_ GTEST_ATTRIBUTE_UNUSED_; \
- GTEST_DISALLOW_COPY_AND_ASSIGN_(\
- GTEST_TEST_CLASS_NAME_(test_case_name, test_name)); \
- }; \
- int GTEST_TEST_CLASS_NAME_(test_case_name, \
- test_name)::gtest_registering_dummy_ = \
- GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::AddToRegistry(); \
- void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody()
-
-// The optional last argument to INSTANTIATE_TEST_CASE_P allows the user
-// to specify a function or functor that generates custom test name suffixes
-// based on the test parameters. The function should accept one argument of
-// type testing::TestParamInfo<class ParamType>, and return std::string.
+// INSTANTIATE_TEST_SUITE_P(TwoBoolSequence, FlagDependentTest,
+// Combine(Bool(), Bool()));
+//
+template <typename... Generator>
+internal::CartesianProductHolder<Generator...> Combine(const Generator&... g) {
+ return internal::CartesianProductHolder<Generator...>(g...);
+}
+
+#define TEST_P(test_suite_name, test_name) \
+ class GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \
+ : public test_suite_name { \
+ public: \
+ GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() {} \
+ virtual void TestBody(); \
+ \
+ private: \
+ static int AddToRegistry() { \
+ ::testing::UnitTest::GetInstance() \
+ ->parameterized_test_registry() \
+ .GetTestSuitePatternHolder<test_suite_name>( \
+ #test_suite_name, \
+ ::testing::internal::CodeLocation(__FILE__, __LINE__)) \
+ ->AddTestPattern( \
+ GTEST_STRINGIFY_(test_suite_name), GTEST_STRINGIFY_(test_name), \
+ new ::testing::internal::TestMetaFactory<GTEST_TEST_CLASS_NAME_( \
+ test_suite_name, test_name)>()); \
+ return 0; \
+ } \
+ static int gtest_registering_dummy_ GTEST_ATTRIBUTE_UNUSED_; \
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(GTEST_TEST_CLASS_NAME_(test_suite_name, \
+ test_name)); \
+ }; \
+ int GTEST_TEST_CLASS_NAME_(test_suite_name, \
+ test_name)::gtest_registering_dummy_ = \
+ GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::AddToRegistry(); \
+ void GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::TestBody()
+
+// The last argument to INSTANTIATE_TEST_SUITE_P allows the user to specify
+// generator and an optional function or functor that generates custom test name
+// suffixes based on the test parameters. Such a function or functor should
+// accept one argument of type testing::TestParamInfo<class ParamType>, and
+// return std::string.
//
// testing::PrintToStringParamName is a builtin test suffix generator that
-// returns the value of testing::PrintToString(GetParam()). It does not work
-// for std::string or C strings.
+// returns the value of testing::PrintToString(GetParam()).
//
// Note: test names must be non-empty, unique, and may only contain ASCII
-// alphanumeric characters or underscore.
-
-# define INSTANTIATE_TEST_CASE_P(prefix, test_case_name, generator, ...) \
- ::testing::internal::ParamGenerator<test_case_name::ParamType> \
- gtest_##prefix##test_case_name##_EvalGenerator_() { return generator; } \
- ::std::string gtest_##prefix##test_case_name##_EvalGenerateName_( \
- const ::testing::TestParamInfo<test_case_name::ParamType>& info) { \
- return ::testing::internal::GetParamNameGen<test_case_name::ParamType> \
- (__VA_ARGS__)(info); \
- } \
- int gtest_##prefix##test_case_name##_dummy_ GTEST_ATTRIBUTE_UNUSED_ = \
- ::testing::UnitTest::GetInstance()->parameterized_test_registry(). \
- GetTestCasePatternHolder<test_case_name>(\
- #test_case_name, \
- ::testing::internal::CodeLocation(\
- __FILE__, __LINE__))->AddTestCaseInstantiation(\
- #prefix, \
- &gtest_##prefix##test_case_name##_EvalGenerator_, \
- &gtest_##prefix##test_case_name##_EvalGenerateName_, \
- __FILE__, __LINE__)
+// alphanumeric characters or underscore. Because PrintToString adds quotes
+// to std::string and C strings, it won't work for these types.
+
+#define GTEST_EXPAND_(arg) arg
+#define GTEST_GET_FIRST_(first, ...) first
+#define GTEST_GET_SECOND_(first, second, ...) second
+
+#define INSTANTIATE_TEST_SUITE_P(prefix, test_suite_name, ...) \
+ static ::testing::internal::ParamGenerator<test_suite_name::ParamType> \
+ gtest_##prefix##test_suite_name##_EvalGenerator_() { \
+ return GTEST_EXPAND_(GTEST_GET_FIRST_(__VA_ARGS__, DUMMY_PARAM_)); \
+ } \
+ static ::std::string gtest_##prefix##test_suite_name##_EvalGenerateName_( \
+ const ::testing::TestParamInfo<test_suite_name::ParamType>& info) { \
+ if (::testing::internal::AlwaysFalse()) { \
+ ::testing::internal::TestNotEmpty(GTEST_EXPAND_(GTEST_GET_SECOND_( \
+ __VA_ARGS__, \
+ ::testing::internal::DefaultParamName<test_suite_name::ParamType>, \
+ DUMMY_PARAM_))); \
+ auto t = std::make_tuple(__VA_ARGS__); \
+ static_assert(std::tuple_size<decltype(t)>::value <= 2, \
+ "Too Many Args!"); \
+ } \
+ return ((GTEST_EXPAND_(GTEST_GET_SECOND_( \
+ __VA_ARGS__, \
+ ::testing::internal::DefaultParamName<test_suite_name::ParamType>, \
+ DUMMY_PARAM_))))(info); \
+ } \
+ static int gtest_##prefix##test_suite_name##_dummy_ \
+ GTEST_ATTRIBUTE_UNUSED_ = \
+ ::testing::UnitTest::GetInstance() \
+ ->parameterized_test_registry() \
+ .GetTestSuitePatternHolder<test_suite_name>( \
+ #test_suite_name, \
+ ::testing::internal::CodeLocation(__FILE__, __LINE__)) \
+ ->AddTestSuiteInstantiation( \
+ #prefix, &gtest_##prefix##test_suite_name##_EvalGenerator_, \
+ &gtest_##prefix##test_suite_name##_EvalGenerateName_, \
+ __FILE__, __LINE__)
+
+// Legacy API is deprecated but still available
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+#define INSTANTIATE_TEST_CASE_P \
+ static_assert(::testing::internal::InstantiateTestCase_P_IsDeprecated(), \
+ ""); \
+ INSTANTIATE_TEST_SUITE_P
+#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
} // namespace testing
-#endif // GTEST_HAS_PARAM_TEST
-
#endif // GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_
diff --git a/extern/gtest/include/gtest/gtest-printers.h b/extern/gtest/include/gtest/gtest-printers.h
index 8a33164cb38..56a05450ef5 100644
--- a/extern/gtest/include/gtest/gtest-printers.h
+++ b/extern/gtest/include/gtest/gtest-printers.h
@@ -26,10 +26,9 @@
// 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: wan@google.com (Zhanyong Wan)
-// Google Test - The Google C++ Testing Framework
+
+// Google Test - The Google C++ Testing and Mocking Framework
//
// This file implements a universal value printer that can print a
// value of any type T:
@@ -46,6 +45,10 @@
// 2. operator<<(ostream&, const T&) defined in either foo or the
// global namespace.
//
+// However if T is an STL-style container then it is printed element-wise
+// unless foo::PrintTo(const T&, ostream*) is defined. Note that
+// operator<<() is ignored for container types.
+//
// If none of the above is defined, it will print the debug string of
// the value if it is a protocol buffer, or print the raw bytes in the
// value otherwise.
@@ -92,20 +95,27 @@
// being defined as many user-defined container types don't have
// value_type.
+// GOOGLETEST_CM0001 DO NOT DELETE
+
#ifndef GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_
#define GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_
+#include <functional>
#include <ostream> // NOLINT
#include <sstream>
#include <string>
+#include <tuple>
+#include <type_traits>
#include <utility>
#include <vector>
-#include "gtest/internal/gtest-port.h"
#include "gtest/internal/gtest-internal.h"
+#include "gtest/internal/gtest-port.h"
-#if GTEST_HAS_STD_TUPLE_
-# include <tuple>
-#endif
+#if GTEST_HAS_ABSL
+#include "absl/strings/string_view.h"
+#include "absl/types/optional.h"
+#include "absl/types/variant.h"
+#endif // GTEST_HAS_ABSL
namespace testing {
@@ -125,7 +135,11 @@ enum TypeKind {
kProtobuf, // a protobuf type
kConvertibleToInteger, // a type implicitly convertible to BiggestInt
// (e.g. a named or unnamed enum type)
- kOtherType // anything else
+#if GTEST_HAS_ABSL
+ kConvertibleToStringView, // a type implicitly convertible to
+ // absl::string_view
+#endif
+ kOtherType // anything else
};
// TypeWithoutFormatter<T, kTypeKind>::PrintValue(value, os) is called
@@ -137,8 +151,10 @@ class TypeWithoutFormatter {
public:
// This default version is called when kTypeKind is kOtherType.
static void PrintValue(const T& value, ::std::ostream* os) {
- PrintBytesInObjectTo(reinterpret_cast<const unsigned char*>(&value),
- sizeof(value), os);
+ PrintBytesInObjectTo(
+ static_cast<const unsigned char*>(
+ reinterpret_cast<const void*>(std::addressof(value))),
+ sizeof(value), os);
}
};
@@ -151,10 +167,10 @@ template <typename T>
class TypeWithoutFormatter<T, kProtobuf> {
public:
static void PrintValue(const T& value, ::std::ostream* os) {
- const ::testing::internal::string short_str = value.ShortDebugString();
- const ::testing::internal::string pretty_str =
- short_str.length() <= kProtobufOneLinerMaxLength ?
- short_str : ("\n" + value.DebugString());
+ std::string pretty_str = value.ShortDebugString();
+ if (pretty_str.length() > kProtobufOneLinerMaxLength) {
+ pretty_str = "\n" + value.DebugString();
+ }
*os << ("<" + pretty_str + ">");
}
};
@@ -175,6 +191,19 @@ class TypeWithoutFormatter<T, kConvertibleToInteger> {
}
};
+#if GTEST_HAS_ABSL
+template <typename T>
+class TypeWithoutFormatter<T, kConvertibleToStringView> {
+ public:
+ // Since T has neither operator<< nor PrintTo() but can be implicitly
+ // converted to absl::string_view, we print it as a absl::string_view.
+ //
+ // Note: the implementation is further below, as it depends on
+ // internal::PrintTo symbol which is defined later in the file.
+ static void PrintValue(const T& value, ::std::ostream* os);
+};
+#endif
+
// Prints the given value to the given ostream. If the value is a
// protocol message, its debug string is printed; if it's an enum or
// of a type implicitly convertible to BiggestInt, it's printed as an
@@ -202,10 +231,19 @@ class TypeWithoutFormatter<T, kConvertibleToInteger> {
template <typename Char, typename CharTraits, typename T>
::std::basic_ostream<Char, CharTraits>& operator<<(
::std::basic_ostream<Char, CharTraits>& os, const T& x) {
- TypeWithoutFormatter<T,
- (internal::IsAProtocolMessage<T>::value ? kProtobuf :
- internal::ImplicitlyConvertible<const T&, internal::BiggestInt>::value ?
- kConvertibleToInteger : kOtherType)>::PrintValue(x, &os);
+ TypeWithoutFormatter<T, (internal::IsAProtocolMessage<T>::value
+ ? kProtobuf
+ : std::is_convertible<
+ const T&, internal::BiggestInt>::value
+ ? kConvertibleToInteger
+ :
+#if GTEST_HAS_ABSL
+ std::is_convertible<
+ const T&, absl::string_view>::value
+ ? kConvertibleToStringView
+ :
+#endif
+ kOtherType)>::PrintValue(x, &os);
return os;
}
@@ -320,16 +358,6 @@ GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const wchar_t);
GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char, ::std::string);
GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char, ::std::string);
-#if GTEST_HAS_GLOBAL_STRING
-GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char, ::string);
-GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char, ::string);
-#endif
-
-#if GTEST_HAS_GLOBAL_WSTRING
-GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(wchar_t, ::wstring);
-GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const wchar_t, ::wstring);
-#endif
-
#if GTEST_HAS_STD_WSTRING
GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(wchar_t, ::std::wstring);
GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const wchar_t, ::std::wstring);
@@ -364,11 +392,18 @@ class UniversalPrinter;
template <typename T>
void UniversalPrint(const T& value, ::std::ostream* os);
+enum DefaultPrinterType {
+ kPrintContainer,
+ kPrintPointer,
+ kPrintFunctionPointer,
+ kPrintOther,
+};
+template <DefaultPrinterType type> struct WrapPrinterType {};
+
// Used to print an STL-style container when the user doesn't define
// a PrintTo() for it.
template <typename C>
-void DefaultPrintTo(IsContainer /* dummy */,
- false_type /* is not a pointer */,
+void DefaultPrintTo(WrapPrinterType<kPrintContainer> /* dummy */,
const C& container, ::std::ostream* os) {
const size_t kMaxCount = 32; // The maximum number of elements to print.
*os << '{';
@@ -401,40 +436,34 @@ void DefaultPrintTo(IsContainer /* dummy */,
// implementation-defined. Therefore they will be printed as raw
// bytes.)
template <typename T>
-void DefaultPrintTo(IsNotContainer /* dummy */,
- true_type /* is a pointer */,
+void DefaultPrintTo(WrapPrinterType<kPrintPointer> /* dummy */,
T* p, ::std::ostream* os) {
- if (p == NULL) {
+ if (p == nullptr) {
*os << "NULL";
} else {
- // C++ doesn't allow casting from a function pointer to any object
- // pointer.
- //
- // IsTrue() silences warnings: "Condition is always true",
- // "unreachable code".
- if (IsTrue(ImplicitlyConvertible<T*, const void*>::value)) {
- // T is not a function type. We just call << to print p,
- // relying on ADL to pick up user-defined << for their pointer
- // types, if any.
- *os << p;
- } else {
- // T is a function type, so '*os << p' doesn't do what we want
- // (it just prints p as bool). We want to print p as a const
- // void*. However, we cannot cast it to const void* directly,
- // even using reinterpret_cast, as earlier versions of gcc
- // (e.g. 3.4.5) cannot compile the cast when p is a function
- // pointer. Casting to UInt64 first solves the problem.
- *os << reinterpret_cast<const void*>(
- reinterpret_cast<internal::UInt64>(p));
- }
+ // T is not a function type. We just call << to print p,
+ // relying on ADL to pick up user-defined << for their pointer
+ // types, if any.
+ *os << p;
+ }
+}
+template <typename T>
+void DefaultPrintTo(WrapPrinterType<kPrintFunctionPointer> /* dummy */,
+ T* p, ::std::ostream* os) {
+ if (p == nullptr) {
+ *os << "NULL";
+ } else {
+ // T is a function type, so '*os << p' doesn't do what we want
+ // (it just prints p as bool). We want to print p as a const
+ // void*.
+ *os << reinterpret_cast<const void*>(p);
}
}
// Used to print a non-container, non-pointer value when the user
// doesn't define PrintTo() for it.
template <typename T>
-void DefaultPrintTo(IsNotContainer /* dummy */,
- false_type /* is not a pointer */,
+void DefaultPrintTo(WrapPrinterType<kPrintOther> /* dummy */,
const T& value, ::std::ostream* os) {
::testing_internal::DefaultPrintNonContainerTo(value, os);
}
@@ -452,11 +481,8 @@ void DefaultPrintTo(IsNotContainer /* dummy */,
// wants).
template <typename T>
void PrintTo(const T& value, ::std::ostream* os) {
- // DefaultPrintTo() is overloaded. The type of its first two
- // arguments determine which version will be picked. If T is an
- // STL-style container, the version for container will be called; if
- // T is a pointer, the pointer version will be called; otherwise the
- // generic version will be called.
+ // DefaultPrintTo() is overloaded. The type of its first argument
+ // determines which version will be picked.
//
// Note that we check for container types here, prior to we check
// for protocol message types in our operator<<. The rationale is:
@@ -468,13 +494,23 @@ void PrintTo(const T& value, ::std::ostream* os) {
// elements; therefore we check for container types here to ensure
// that our format is used.
//
- // The second argument of DefaultPrintTo() is needed to bypass a bug
- // in Symbian's C++ compiler that prevents it from picking the right
- // overload between:
- //
- // PrintTo(const T& x, ...);
- // PrintTo(T* x, ...);
- DefaultPrintTo(IsContainerTest<T>(0), is_pointer<T>(), value, os);
+ // Note that MSVC and clang-cl do allow an implicit conversion from
+ // pointer-to-function to pointer-to-object, but clang-cl warns on it.
+ // So don't use ImplicitlyConvertible if it can be helped since it will
+ // cause this warning, and use a separate overload of DefaultPrintTo for
+ // function pointers so that the `*os << p` in the object pointer overload
+ // doesn't cause that warning either.
+ DefaultPrintTo(
+ WrapPrinterType <
+ (sizeof(IsContainerTest<T>(0)) == sizeof(IsContainer)) &&
+ !IsRecursiveContainer<T>::value
+ ? kPrintContainer
+ : !std::is_pointer<T>::value
+ ? kPrintOther
+ : std::is_function<typename std::remove_pointer<T>::type>::value
+ ? kPrintFunctionPointer
+ : kPrintPointer > (),
+ value, os);
}
// The following list of PrintTo() overloads tells
@@ -553,27 +589,13 @@ void PrintRawArrayTo(const T a[], size_t count, ::std::ostream* os) {
}
}
-// Overloads for ::string and ::std::string.
-#if GTEST_HAS_GLOBAL_STRING
-GTEST_API_ void PrintStringTo(const ::string&s, ::std::ostream* os);
-inline void PrintTo(const ::string& s, ::std::ostream* os) {
- PrintStringTo(s, os);
-}
-#endif // GTEST_HAS_GLOBAL_STRING
-
+// Overloads for ::std::string.
GTEST_API_ void PrintStringTo(const ::std::string&s, ::std::ostream* os);
inline void PrintTo(const ::std::string& s, ::std::ostream* os) {
PrintStringTo(s, os);
}
-// Overloads for ::wstring and ::std::wstring.
-#if GTEST_HAS_GLOBAL_WSTRING
-GTEST_API_ void PrintWideStringTo(const ::wstring&s, ::std::ostream* os);
-inline void PrintTo(const ::wstring& s, ::std::ostream* os) {
- PrintWideStringTo(s, os);
-}
-#endif // GTEST_HAS_GLOBAL_WSTRING
-
+// Overloads for ::std::wstring.
#if GTEST_HAS_STD_WSTRING
GTEST_API_ void PrintWideStringTo(const ::std::wstring&s, ::std::ostream* os);
inline void PrintTo(const ::std::wstring& s, ::std::ostream* os) {
@@ -581,95 +603,45 @@ inline void PrintTo(const ::std::wstring& s, ::std::ostream* os) {
}
#endif // GTEST_HAS_STD_WSTRING
-#if GTEST_HAS_TR1_TUPLE || GTEST_HAS_STD_TUPLE_
-// Helper function for printing a tuple. T must be instantiated with
-// a tuple type.
-template <typename T>
-void PrintTupleTo(const T& t, ::std::ostream* os);
-#endif // GTEST_HAS_TR1_TUPLE || GTEST_HAS_STD_TUPLE_
-
-#if GTEST_HAS_TR1_TUPLE
-// Overload for ::std::tr1::tuple. Needed for printing function arguments,
-// which are packed as tuples.
-
-// Overloaded PrintTo() for tuples of various arities. We support
-// tuples of up-to 10 fields. The following implementation works
-// regardless of whether tr1::tuple is implemented using the
-// non-standard variadic template feature or not.
-
-inline void PrintTo(const ::std::tr1::tuple<>& t, ::std::ostream* os) {
- PrintTupleTo(t, os);
+#if GTEST_HAS_ABSL
+// Overload for absl::string_view.
+inline void PrintTo(absl::string_view sp, ::std::ostream* os) {
+ PrintTo(::std::string(sp), os);
}
+#endif // GTEST_HAS_ABSL
-template <typename T1>
-void PrintTo(const ::std::tr1::tuple<T1>& t, ::std::ostream* os) {
- PrintTupleTo(t, os);
-}
-
-template <typename T1, typename T2>
-void PrintTo(const ::std::tr1::tuple<T1, T2>& t, ::std::ostream* os) {
- PrintTupleTo(t, os);
-}
-
-template <typename T1, typename T2, typename T3>
-void PrintTo(const ::std::tr1::tuple<T1, T2, T3>& t, ::std::ostream* os) {
- PrintTupleTo(t, os);
-}
-
-template <typename T1, typename T2, typename T3, typename T4>
-void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4>& t, ::std::ostream* os) {
- PrintTupleTo(t, os);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5>
-void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4, T5>& t,
- ::std::ostream* os) {
- PrintTupleTo(t, os);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6>
-void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4, T5, T6>& t,
- ::std::ostream* os) {
- PrintTupleTo(t, os);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7>
-void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7>& t,
- ::std::ostream* os) {
- PrintTupleTo(t, os);
-}
+inline void PrintTo(std::nullptr_t, ::std::ostream* os) { *os << "(nullptr)"; }
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8>
-void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8>& t,
- ::std::ostream* os) {
- PrintTupleTo(t, os);
-}
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9>
-void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9>& t,
- ::std::ostream* os) {
- PrintTupleTo(t, os);
+template <typename T>
+void PrintTo(std::reference_wrapper<T> ref, ::std::ostream* os) {
+ UniversalPrinter<T&>::Print(ref.get(), os);
}
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10>
-void PrintTo(
- const ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>& t,
- ::std::ostream* os) {
- PrintTupleTo(t, os);
+// Helper function for printing a tuple. T must be instantiated with
+// a tuple type.
+template <typename T>
+void PrintTupleTo(const T&, std::integral_constant<size_t, 0>,
+ ::std::ostream*) {}
+
+template <typename T, size_t I>
+void PrintTupleTo(const T& t, std::integral_constant<size_t, I>,
+ ::std::ostream* os) {
+ PrintTupleTo(t, std::integral_constant<size_t, I - 1>(), os);
+ GTEST_INTENTIONAL_CONST_COND_PUSH_()
+ if (I > 1) {
+ GTEST_INTENTIONAL_CONST_COND_POP_()
+ *os << ", ";
+ }
+ UniversalPrinter<typename std::tuple_element<I - 1, T>::type>::Print(
+ std::get<I - 1>(t), os);
}
-#endif // GTEST_HAS_TR1_TUPLE
-#if GTEST_HAS_STD_TUPLE_
template <typename... Types>
void PrintTo(const ::std::tuple<Types...>& t, ::std::ostream* os) {
- PrintTupleTo(t, os);
+ *os << "(";
+ PrintTupleTo(t, std::integral_constant<size_t, sizeof...(Types)>(), os);
+ *os << ")";
}
-#endif // GTEST_HAS_STD_TUPLE_
// Overload for std::pair.
template <typename T1, typename T2>
@@ -710,6 +682,48 @@ class UniversalPrinter {
GTEST_DISABLE_MSC_WARNINGS_POP_()
};
+#if GTEST_HAS_ABSL
+
+// Printer for absl::optional
+
+template <typename T>
+class UniversalPrinter<::absl::optional<T>> {
+ public:
+ static void Print(const ::absl::optional<T>& value, ::std::ostream* os) {
+ *os << '(';
+ if (!value) {
+ *os << "nullopt";
+ } else {
+ UniversalPrint(*value, os);
+ }
+ *os << ')';
+ }
+};
+
+// Printer for absl::variant
+
+template <typename... T>
+class UniversalPrinter<::absl::variant<T...>> {
+ public:
+ static void Print(const ::absl::variant<T...>& value, ::std::ostream* os) {
+ *os << '(';
+ absl::visit(Visitor{os}, value);
+ *os << ')';
+ }
+
+ private:
+ struct Visitor {
+ template <typename U>
+ void operator()(const U& u) const {
+ *os << "'" << GetTypeName<U>() << "' with value ";
+ UniversalPrint(u, os);
+ }
+ ::std::ostream* os;
+ };
+};
+
+#endif // GTEST_HAS_ABSL
+
// UniversalPrintArray(begin, len, os) prints an array of 'len'
// elements, starting at address 'begin'.
template <typename T>
@@ -723,7 +737,6 @@ void UniversalPrintArray(const T* begin, size_t len, ::std::ostream* os) {
// If the array has more than kThreshold elements, we'll have to
// omit some details by printing only the first and the last
// kChunkSize elements.
- // TODO(wan@google.com): let the user control the threshold using a flag.
if (len <= kThreshold) {
PrintRawArrayTo(begin, len, os);
} else {
@@ -802,10 +815,10 @@ template <>
class UniversalTersePrinter<const char*> {
public:
static void Print(const char* str, ::std::ostream* os) {
- if (str == NULL) {
+ if (str == nullptr) {
*os << "NULL";
} else {
- UniversalPrint(string(str), os);
+ UniversalPrint(std::string(str), os);
}
}
};
@@ -822,7 +835,7 @@ template <>
class UniversalTersePrinter<const wchar_t*> {
public:
static void Print(const wchar_t* str, ::std::ostream* os) {
- if (str == NULL) {
+ if (str == nullptr) {
*os << "NULL";
} else {
UniversalPrint(::std::wstring(str), os);
@@ -856,110 +869,22 @@ void UniversalPrint(const T& value, ::std::ostream* os) {
UniversalPrinter<T1>::Print(value, os);
}
-typedef ::std::vector<string> Strings;
-
-// TuplePolicy<TupleT> must provide:
-// - tuple_size
-// size of tuple TupleT.
-// - get<size_t I>(const TupleT& t)
-// static function extracting element I of tuple TupleT.
-// - tuple_element<size_t I>::type
-// type of element I of tuple TupleT.
-template <typename TupleT>
-struct TuplePolicy;
-
-#if GTEST_HAS_TR1_TUPLE
-template <typename TupleT>
-struct TuplePolicy {
- typedef TupleT Tuple;
- static const size_t tuple_size = ::std::tr1::tuple_size<Tuple>::value;
-
- template <size_t I>
- struct tuple_element : ::std::tr1::tuple_element<I, Tuple> {};
-
- template <size_t I>
- static typename AddReference<
- const typename ::std::tr1::tuple_element<I, Tuple>::type>::type get(
- const Tuple& tuple) {
- return ::std::tr1::get<I>(tuple);
- }
-};
-template <typename TupleT>
-const size_t TuplePolicy<TupleT>::tuple_size;
-#endif // GTEST_HAS_TR1_TUPLE
-
-#if GTEST_HAS_STD_TUPLE_
-template <typename... Types>
-struct TuplePolicy< ::std::tuple<Types...> > {
- typedef ::std::tuple<Types...> Tuple;
- static const size_t tuple_size = ::std::tuple_size<Tuple>::value;
-
- template <size_t I>
- struct tuple_element : ::std::tuple_element<I, Tuple> {};
-
- template <size_t I>
- static const typename ::std::tuple_element<I, Tuple>::type& get(
- const Tuple& tuple) {
- return ::std::get<I>(tuple);
- }
-};
-template <typename... Types>
-const size_t TuplePolicy< ::std::tuple<Types...> >::tuple_size;
-#endif // GTEST_HAS_STD_TUPLE_
-
-#if GTEST_HAS_TR1_TUPLE || GTEST_HAS_STD_TUPLE_
-// This helper template allows PrintTo() for tuples and
-// UniversalTersePrintTupleFieldsToStrings() to be defined by
-// induction on the number of tuple fields. The idea is that
-// TuplePrefixPrinter<N>::PrintPrefixTo(t, os) prints the first N
-// fields in tuple t, and can be defined in terms of
-// TuplePrefixPrinter<N - 1>.
-//
-// The inductive case.
-template <size_t N>
-struct TuplePrefixPrinter {
- // Prints the first N fields of a tuple.
- template <typename Tuple>
- static void PrintPrefixTo(const Tuple& t, ::std::ostream* os) {
- TuplePrefixPrinter<N - 1>::PrintPrefixTo(t, os);
- GTEST_INTENTIONAL_CONST_COND_PUSH_()
- if (N > 1) {
- GTEST_INTENTIONAL_CONST_COND_POP_()
- *os << ", ";
- }
- UniversalPrinter<
- typename TuplePolicy<Tuple>::template tuple_element<N - 1>::type>
- ::Print(TuplePolicy<Tuple>::template get<N - 1>(t), os);
- }
+typedef ::std::vector< ::std::string> Strings;
// Tersely prints the first N fields of a tuple to a string vector,
// one element for each field.
- template <typename Tuple>
- static void TersePrintPrefixToStrings(const Tuple& t, Strings* strings) {
- TuplePrefixPrinter<N - 1>::TersePrintPrefixToStrings(t, strings);
- ::std::stringstream ss;
- UniversalTersePrint(TuplePolicy<Tuple>::template get<N - 1>(t), &ss);
- strings->push_back(ss.str());
- }
-};
-
-// Base case.
-template <>
-struct TuplePrefixPrinter<0> {
- template <typename Tuple>
- static void PrintPrefixTo(const Tuple&, ::std::ostream*) {}
-
- template <typename Tuple>
- static void TersePrintPrefixToStrings(const Tuple&, Strings*) {}
-};
-
-// Helper function for printing a tuple.
-// Tuple must be either std::tr1::tuple or std::tuple type.
template <typename Tuple>
-void PrintTupleTo(const Tuple& t, ::std::ostream* os) {
- *os << "(";
- TuplePrefixPrinter<TuplePolicy<Tuple>::tuple_size>::PrintPrefixTo(t, os);
- *os << ")";
+void TersePrintPrefixToStrings(const Tuple&, std::integral_constant<size_t, 0>,
+ Strings*) {}
+template <typename Tuple, size_t I>
+void TersePrintPrefixToStrings(const Tuple& t,
+ std::integral_constant<size_t, I>,
+ Strings* strings) {
+ TersePrintPrefixToStrings(t, std::integral_constant<size_t, I - 1>(),
+ strings);
+ ::std::stringstream ss;
+ UniversalTersePrint(std::get<I - 1>(t), &ss);
+ strings->push_back(ss.str());
}
// Prints the fields of a tuple tersely to a string vector, one
@@ -968,14 +893,24 @@ void PrintTupleTo(const Tuple& t, ::std::ostream* os) {
template <typename Tuple>
Strings UniversalTersePrintTupleFieldsToStrings(const Tuple& value) {
Strings result;
- TuplePrefixPrinter<TuplePolicy<Tuple>::tuple_size>::
- TersePrintPrefixToStrings(value, &result);
+ TersePrintPrefixToStrings(
+ value, std::integral_constant<size_t, std::tuple_size<Tuple>::value>(),
+ &result);
return result;
}
-#endif // GTEST_HAS_TR1_TUPLE || GTEST_HAS_STD_TUPLE_
} // namespace internal
+#if GTEST_HAS_ABSL
+namespace internal2 {
+template <typename T>
+void TypeWithoutFormatter<T, kConvertibleToStringView>::PrintValue(
+ const T& value, ::std::ostream* os) {
+ internal::PrintTo(absl::string_view(value), os);
+}
+} // namespace internal2
+#endif
+
template <typename T>
::std::string PrintToString(const T& value) {
::std::stringstream ss;
diff --git a/extern/gtest/include/gtest/gtest-spi.h b/extern/gtest/include/gtest/gtest-spi.h
index f63fa9a1b2a..aa38870e8e1 100644
--- a/extern/gtest/include/gtest/gtest-spi.h
+++ b/extern/gtest/include/gtest/gtest-spi.h
@@ -26,17 +26,21 @@
// 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: wan@google.com (Zhanyong Wan)
+
//
// Utilities for testing Google Test itself and code that uses Google Test
// (e.g. frameworks built on top of Google Test).
+// GOOGLETEST_CM0004 DO NOT DELETE
+
#ifndef GTEST_INCLUDE_GTEST_GTEST_SPI_H_
#define GTEST_INCLUDE_GTEST_GTEST_SPI_H_
#include "gtest/gtest.h"
+GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
+/* class A needs to have dll-interface to be used by clients of class B */)
+
namespace testing {
// This helper class can be used to mock out Google Test failure reporting
@@ -68,14 +72,15 @@ class GTEST_API_ ScopedFakeTestPartResultReporter
TestPartResultArray* result);
// The d'tor restores the previous test part result reporter.
- virtual ~ScopedFakeTestPartResultReporter();
+ ~ScopedFakeTestPartResultReporter() override;
// Appends the TestPartResult object to the TestPartResultArray
// received in the constructor.
//
// This method is from the TestPartResultReporterInterface
// interface.
- virtual void ReportTestPartResult(const TestPartResult& result);
+ void ReportTestPartResult(const TestPartResult& result) override;
+
private:
void Init();
@@ -97,13 +102,12 @@ class GTEST_API_ SingleFailureChecker {
public:
// The constructor remembers the arguments.
SingleFailureChecker(const TestPartResultArray* results,
- TestPartResult::Type type,
- const string& substr);
+ TestPartResult::Type type, const std::string& substr);
~SingleFailureChecker();
private:
const TestPartResultArray* const results_;
const TestPartResult::Type type_;
- const string substr_;
+ const std::string substr_;
GTEST_DISALLOW_COPY_AND_ASSIGN_(SingleFailureChecker);
};
@@ -112,6 +116,8 @@ class GTEST_API_ SingleFailureChecker {
} // namespace testing
+GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251
+
// A set of macros for testing Google Test assertions or code that's expected
// to generate Google Test fatal failures. It verifies that the given
// statement will cause exactly one fatal Google Test failure with 'substr'
diff --git a/extern/gtest/include/gtest/gtest-test-part.h b/extern/gtest/include/gtest/gtest-test-part.h
index 77eb844839d..05a79853586 100644
--- a/extern/gtest/include/gtest/gtest-test-part.h
+++ b/extern/gtest/include/gtest/gtest-test-part.h
@@ -27,8 +27,7 @@
// (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: mheule@google.com (Markus Heule)
-//
+// GOOGLETEST_CM0001 DO NOT DELETE
#ifndef GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_
#define GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_
@@ -38,6 +37,9 @@
#include "gtest/internal/gtest-internal.h"
#include "gtest/internal/gtest-string.h"
+GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
+/* class A needs to have dll-interface to be used by clients of class B */)
+
namespace testing {
// A copyable object representing the result of a test part (i.e. an
@@ -51,22 +53,20 @@ class GTEST_API_ TestPartResult {
enum Type {
kSuccess, // Succeeded.
kNonFatalFailure, // Failed but the test can continue.
- kFatalFailure // Failed and the test should be terminated.
+ kFatalFailure, // Failed and the test should be terminated.
+ kSkip // Skipped.
};
// C'tor. TestPartResult does NOT have a default constructor.
// Always use this constructor (with parameters) to create a
// TestPartResult object.
- TestPartResult(Type a_type,
- const char* a_file_name,
- int a_line_number,
+ TestPartResult(Type a_type, const char* a_file_name, int a_line_number,
const char* a_message)
: type_(a_type),
- file_name_(a_file_name == NULL ? "" : a_file_name),
+ file_name_(a_file_name == nullptr ? "" : a_file_name),
line_number_(a_line_number),
summary_(ExtractSummary(a_message)),
- message_(a_message) {
- }
+ message_(a_message) {}
// Gets the outcome of the test part.
Type type() const { return type_; }
@@ -74,7 +74,7 @@ class GTEST_API_ TestPartResult {
// Gets the name of the source file where the test part took place, or
// NULL if it's unknown.
const char* file_name() const {
- return file_name_.empty() ? NULL : file_name_.c_str();
+ return file_name_.empty() ? nullptr : file_name_.c_str();
}
// Gets the line in the source file where the test part took place,
@@ -87,18 +87,21 @@ class GTEST_API_ TestPartResult {
// Gets the message associated with the test part.
const char* message() const { return message_.c_str(); }
- // Returns true iff the test part passed.
- bool passed() const { return type_ == kSuccess; }
+ // Returns true if and only if the test part was skipped.
+ bool skipped() const { return type_ == kSkip; }
- // Returns true iff the test part failed.
- bool failed() const { return type_ != kSuccess; }
+ // Returns true if and only if the test part passed.
+ bool passed() const { return type_ == kSuccess; }
- // Returns true iff the test part non-fatally failed.
+ // Returns true if and only if the test part non-fatally failed.
bool nonfatally_failed() const { return type_ == kNonFatalFailure; }
- // Returns true iff the test part fatally failed.
+ // Returns true if and only if the test part fatally failed.
bool fatally_failed() const { return type_ == kFatalFailure; }
+ // Returns true if and only if the test part failed.
+ bool failed() const { return fatally_failed() || nonfatally_failed(); }
+
private:
Type type_;
@@ -143,7 +146,7 @@ class GTEST_API_ TestPartResultArray {
};
// This interface knows how to report a test part result.
-class TestPartResultReporterInterface {
+class GTEST_API_ TestPartResultReporterInterface {
public:
virtual ~TestPartResultReporterInterface() {}
@@ -162,8 +165,8 @@ class GTEST_API_ HasNewFatalFailureHelper
: public TestPartResultReporterInterface {
public:
HasNewFatalFailureHelper();
- virtual ~HasNewFatalFailureHelper();
- virtual void ReportTestPartResult(const TestPartResult& result);
+ ~HasNewFatalFailureHelper() override;
+ void ReportTestPartResult(const TestPartResult& result) override;
bool has_new_fatal_failure() const { return has_new_fatal_failure_; }
private:
bool has_new_fatal_failure_;
@@ -176,4 +179,6 @@ class GTEST_API_ HasNewFatalFailureHelper
} // namespace testing
+GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251
+
#endif // GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_
diff --git a/extern/gtest/include/gtest/gtest-typed-test.h b/extern/gtest/include/gtest/gtest-typed-test.h
index 5f69d5678ea..095ce058022 100644
--- a/extern/gtest/include/gtest/gtest-typed-test.h
+++ b/extern/gtest/include/gtest/gtest-typed-test.h
@@ -26,8 +26,9 @@
// 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: wan@google.com (Zhanyong Wan)
+
+
+// GOOGLETEST_CM0001 DO NOT DELETE
#ifndef GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_
#define GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_
@@ -51,22 +52,22 @@ class FooTest : public testing::Test {
T value_;
};
-// Next, associate a list of types with the test case, which will be
+// Next, associate a list of types with the test suite, which will be
// repeated for each type in the list. The typedef is necessary for
// the macro to parse correctly.
typedef testing::Types<char, int, unsigned int> MyTypes;
-TYPED_TEST_CASE(FooTest, MyTypes);
+TYPED_TEST_SUITE(FooTest, MyTypes);
// If the type list contains only one type, you can write that type
// directly without Types<...>:
-// TYPED_TEST_CASE(FooTest, int);
+// TYPED_TEST_SUITE(FooTest, int);
// Then, use TYPED_TEST() instead of TEST_F() to define as many typed
-// tests for this test case as you want.
+// tests for this test suite as you want.
TYPED_TEST(FooTest, DoesBlah) {
- // Inside a test, refer to TypeParam to get the type parameter.
- // Since we are inside a derived class template, C++ requires use to
- // visit the members of FooTest via 'this'.
+ // Inside a test, refer to the special name TypeParam to get the type
+ // parameter. Since we are inside a derived class template, C++ requires
+ // us to visit the members of FooTest via 'this'.
TypeParam n = this->value_;
// To visit static members of the fixture, add the TestFixture::
@@ -82,6 +83,24 @@ TYPED_TEST(FooTest, DoesBlah) {
TYPED_TEST(FooTest, HasPropertyA) { ... }
+// TYPED_TEST_SUITE takes an optional third argument which allows to specify a
+// class that generates custom test name suffixes based on the type. This should
+// be a class which has a static template function GetName(int index) returning
+// a string for each type. The provided integer index equals the index of the
+// type in the provided type list. In many cases the index can be ignored.
+//
+// For example:
+// class MyTypeNames {
+// public:
+// template <typename T>
+// static std::string GetName(int) {
+// if (std::is_same<T, char>()) return "char";
+// if (std::is_same<T, int>()) return "int";
+// if (std::is_same<T, unsigned int>()) return "unsignedInt";
+// }
+// };
+// TYPED_TEST_SUITE(FooTest, MyTypes, MyTypeNames);
+
#endif // 0
// Type-parameterized tests are abstract test patterns parameterized
@@ -107,13 +126,13 @@ class FooTest : public testing::Test {
...
};
-// Next, declare that you will define a type-parameterized test case
+// Next, declare that you will define a type-parameterized test suite
// (the _P suffix is for "parameterized" or "pattern", whichever you
// prefer):
-TYPED_TEST_CASE_P(FooTest);
+TYPED_TEST_SUITE_P(FooTest);
// Then, use TYPED_TEST_P() to define as many type-parameterized tests
-// for this type-parameterized test case as you want.
+// for this type-parameterized test suite as you want.
TYPED_TEST_P(FooTest, DoesBlah) {
// Inside a test, refer to TypeParam to get the type parameter.
TypeParam n = 0;
@@ -124,10 +143,10 @@ TYPED_TEST_P(FooTest, HasPropertyA) { ... }
// Now the tricky part: you need to register all test patterns before
// you can instantiate them. The first argument of the macro is the
-// test case name; the rest are the names of the tests in this test
+// test suite name; the rest are the names of the tests in this test
// case.
-REGISTER_TYPED_TEST_CASE_P(FooTest,
- DoesBlah, HasPropertyA);
+REGISTER_TYPED_TEST_SUITE_P(FooTest,
+ DoesBlah, HasPropertyA);
// Finally, you are free to instantiate the pattern with the types you
// want. If you put the above code in a header file, you can #include
@@ -135,14 +154,19 @@ REGISTER_TYPED_TEST_CASE_P(FooTest,
//
// To distinguish different instances of the pattern, the first
// argument to the INSTANTIATE_* macro is a prefix that will be added
-// to the actual test case name. Remember to pick unique prefixes for
+// to the actual test suite name. Remember to pick unique prefixes for
// different instances.
typedef testing::Types<char, int, unsigned int> MyTypes;
-INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(My, FooTest, MyTypes);
// If the type list contains only one type, you can write that type
// directly without Types<...>:
-// INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, int);
+// INSTANTIATE_TYPED_TEST_SUITE_P(My, FooTest, int);
+//
+// Similar to the optional argument of TYPED_TEST_SUITE above,
+// INSTANTIATE_TEST_SUITE_P takes an optional fourth argument which allows to
+// generate custom names.
+// INSTANTIATE_TYPED_TEST_SUITE_P(My, FooTest, MyTypes, MyTypeNames);
#endif // 0
@@ -156,35 +180,53 @@ INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes);
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
//
// Expands to the name of the typedef for the type parameters of the
-// given test case.
-# define GTEST_TYPE_PARAMS_(TestCaseName) gtest_type_params_##TestCaseName##_
-
-// The 'Types' template argument below must have spaces around it
-// since some compilers may choke on '>>' when passing a template
-// instance (e.g. Types<int>)
-# define TYPED_TEST_CASE(CaseName, Types) \
- typedef ::testing::internal::TypeList< Types >::type \
- GTEST_TYPE_PARAMS_(CaseName)
-
-# define TYPED_TEST(CaseName, TestName) \
- template <typename gtest_TypeParam_> \
- class GTEST_TEST_CLASS_NAME_(CaseName, TestName) \
- : public CaseName<gtest_TypeParam_> { \
- private: \
- typedef CaseName<gtest_TypeParam_> TestFixture; \
- typedef gtest_TypeParam_ TypeParam; \
- virtual void TestBody(); \
- }; \
- bool gtest_##CaseName##_##TestName##_registered_ GTEST_ATTRIBUTE_UNUSED_ = \
- ::testing::internal::TypeParameterizedTest< \
- CaseName, \
- ::testing::internal::TemplateSel< \
- GTEST_TEST_CLASS_NAME_(CaseName, TestName)>, \
- GTEST_TYPE_PARAMS_(CaseName)>::Register(\
- "", ::testing::internal::CodeLocation(__FILE__, __LINE__), \
- #CaseName, #TestName, 0); \
- template <typename gtest_TypeParam_> \
- void GTEST_TEST_CLASS_NAME_(CaseName, TestName)<gtest_TypeParam_>::TestBody()
+// given test suite.
+#define GTEST_TYPE_PARAMS_(TestSuiteName) gtest_type_params_##TestSuiteName##_
+
+// Expands to the name of the typedef for the NameGenerator, responsible for
+// creating the suffixes of the name.
+#define GTEST_NAME_GENERATOR_(TestSuiteName) \
+ gtest_type_params_##TestSuiteName##_NameGenerator
+
+#define TYPED_TEST_SUITE(CaseName, Types, ...) \
+ typedef ::testing::internal::TypeList<Types>::type GTEST_TYPE_PARAMS_( \
+ CaseName); \
+ typedef ::testing::internal::NameGeneratorSelector<__VA_ARGS__>::type \
+ GTEST_NAME_GENERATOR_(CaseName)
+
+# define TYPED_TEST(CaseName, TestName) \
+ template <typename gtest_TypeParam_> \
+ class GTEST_TEST_CLASS_NAME_(CaseName, TestName) \
+ : public CaseName<gtest_TypeParam_> { \
+ private: \
+ typedef CaseName<gtest_TypeParam_> TestFixture; \
+ typedef gtest_TypeParam_ TypeParam; \
+ virtual void TestBody(); \
+ }; \
+ static bool gtest_##CaseName##_##TestName##_registered_ \
+ GTEST_ATTRIBUTE_UNUSED_ = \
+ ::testing::internal::TypeParameterizedTest< \
+ CaseName, \
+ ::testing::internal::TemplateSel<GTEST_TEST_CLASS_NAME_(CaseName, \
+ TestName)>, \
+ GTEST_TYPE_PARAMS_( \
+ CaseName)>::Register("", \
+ ::testing::internal::CodeLocation( \
+ __FILE__, __LINE__), \
+ #CaseName, #TestName, 0, \
+ ::testing::internal::GenerateNames< \
+ GTEST_NAME_GENERATOR_(CaseName), \
+ GTEST_TYPE_PARAMS_(CaseName)>()); \
+ template <typename gtest_TypeParam_> \
+ void GTEST_TEST_CLASS_NAME_(CaseName, \
+ TestName)<gtest_TypeParam_>::TestBody()
+
+// Legacy API is deprecated but still available
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+#define TYPED_TEST_CASE \
+ static_assert(::testing::internal::TypedTestCaseIsDeprecated(), ""); \
+ TYPED_TEST_SUITE
+#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
#endif // GTEST_HAS_TYPED_TEST
@@ -195,68 +237,93 @@ INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes);
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
//
// Expands to the namespace name that the type-parameterized tests for
-// the given type-parameterized test case are defined in. The exact
+// the given type-parameterized test suite are defined in. The exact
// name of the namespace is subject to change without notice.
-# define GTEST_CASE_NAMESPACE_(TestCaseName) \
- gtest_case_##TestCaseName##_
+#define GTEST_SUITE_NAMESPACE_(TestSuiteName) gtest_suite_##TestSuiteName##_
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
//
// Expands to the name of the variable used to remember the names of
-// the defined tests in the given test case.
-# define GTEST_TYPED_TEST_CASE_P_STATE_(TestCaseName) \
- gtest_typed_test_case_p_state_##TestCaseName##_
+// the defined tests in the given test suite.
+#define GTEST_TYPED_TEST_SUITE_P_STATE_(TestSuiteName) \
+ gtest_typed_test_suite_p_state_##TestSuiteName##_
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE DIRECTLY.
//
// Expands to the name of the variable used to remember the names of
-// the registered tests in the given test case.
-# define GTEST_REGISTERED_TEST_NAMES_(TestCaseName) \
- gtest_registered_test_names_##TestCaseName##_
+// the registered tests in the given test suite.
+#define GTEST_REGISTERED_TEST_NAMES_(TestSuiteName) \
+ gtest_registered_test_names_##TestSuiteName##_
// The variables defined in the type-parameterized test macros are
// static as typically these macros are used in a .h file that can be
// #included in multiple translation units linked together.
-# define TYPED_TEST_CASE_P(CaseName) \
- static ::testing::internal::TypedTestCasePState \
- GTEST_TYPED_TEST_CASE_P_STATE_(CaseName)
-
-# define TYPED_TEST_P(CaseName, TestName) \
- namespace GTEST_CASE_NAMESPACE_(CaseName) { \
- template <typename gtest_TypeParam_> \
- class TestName : public CaseName<gtest_TypeParam_> { \
- private: \
- typedef CaseName<gtest_TypeParam_> TestFixture; \
- typedef gtest_TypeParam_ TypeParam; \
- virtual void TestBody(); \
- }; \
- static bool gtest_##TestName##_defined_ GTEST_ATTRIBUTE_UNUSED_ = \
- GTEST_TYPED_TEST_CASE_P_STATE_(CaseName).AddTestName(\
- __FILE__, __LINE__, #CaseName, #TestName); \
- } \
- template <typename gtest_TypeParam_> \
- void GTEST_CASE_NAMESPACE_(CaseName)::TestName<gtest_TypeParam_>::TestBody()
-
-# define REGISTER_TYPED_TEST_CASE_P(CaseName, ...) \
- namespace GTEST_CASE_NAMESPACE_(CaseName) { \
- typedef ::testing::internal::Templates<__VA_ARGS__>::type gtest_AllTests_; \
- } \
- static const char* const GTEST_REGISTERED_TEST_NAMES_(CaseName) = \
- GTEST_TYPED_TEST_CASE_P_STATE_(CaseName).VerifyRegisteredTestNames(\
+#define TYPED_TEST_SUITE_P(SuiteName) \
+ static ::testing::internal::TypedTestSuitePState \
+ GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName)
+
+// Legacy API is deprecated but still available
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+#define TYPED_TEST_CASE_P \
+ static_assert(::testing::internal::TypedTestCase_P_IsDeprecated(), ""); \
+ TYPED_TEST_SUITE_P
+#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+
+#define TYPED_TEST_P(SuiteName, TestName) \
+ namespace GTEST_SUITE_NAMESPACE_(SuiteName) { \
+ template <typename gtest_TypeParam_> \
+ class TestName : public SuiteName<gtest_TypeParam_> { \
+ private: \
+ typedef SuiteName<gtest_TypeParam_> TestFixture; \
+ typedef gtest_TypeParam_ TypeParam; \
+ virtual void TestBody(); \
+ }; \
+ static bool gtest_##TestName##_defined_ GTEST_ATTRIBUTE_UNUSED_ = \
+ GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName).AddTestName( \
+ __FILE__, __LINE__, #SuiteName, #TestName); \
+ } \
+ template <typename gtest_TypeParam_> \
+ void GTEST_SUITE_NAMESPACE_( \
+ SuiteName)::TestName<gtest_TypeParam_>::TestBody()
+
+#define REGISTER_TYPED_TEST_SUITE_P(SuiteName, ...) \
+ namespace GTEST_SUITE_NAMESPACE_(SuiteName) { \
+ typedef ::testing::internal::Templates<__VA_ARGS__>::type gtest_AllTests_; \
+ } \
+ static const char* const GTEST_REGISTERED_TEST_NAMES_( \
+ SuiteName) GTEST_ATTRIBUTE_UNUSED_ = \
+ GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName).VerifyRegisteredTestNames( \
__FILE__, __LINE__, #__VA_ARGS__)
-// The 'Types' template argument below must have spaces around it
-// since some compilers may choke on '>>' when passing a template
-// instance (e.g. Types<int>)
-# define INSTANTIATE_TYPED_TEST_CASE_P(Prefix, CaseName, Types) \
- bool gtest_##Prefix##_##CaseName GTEST_ATTRIBUTE_UNUSED_ = \
- ::testing::internal::TypeParameterizedTestCase<CaseName, \
- GTEST_CASE_NAMESPACE_(CaseName)::gtest_AllTests_, \
- ::testing::internal::TypeList< Types >::type>::Register(\
- #Prefix, \
- ::testing::internal::CodeLocation(__FILE__, __LINE__), \
- &GTEST_TYPED_TEST_CASE_P_STATE_(CaseName), \
- #CaseName, GTEST_REGISTERED_TEST_NAMES_(CaseName))
+// Legacy API is deprecated but still available
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+#define REGISTER_TYPED_TEST_CASE_P \
+ static_assert(::testing::internal::RegisterTypedTestCase_P_IsDeprecated(), \
+ ""); \
+ REGISTER_TYPED_TEST_SUITE_P
+#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+
+#define INSTANTIATE_TYPED_TEST_SUITE_P(Prefix, SuiteName, Types, ...) \
+ static bool gtest_##Prefix##_##SuiteName GTEST_ATTRIBUTE_UNUSED_ = \
+ ::testing::internal::TypeParameterizedTestSuite< \
+ SuiteName, GTEST_SUITE_NAMESPACE_(SuiteName)::gtest_AllTests_, \
+ ::testing::internal::TypeList<Types>::type>:: \
+ Register(#Prefix, \
+ ::testing::internal::CodeLocation(__FILE__, __LINE__), \
+ &GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName), #SuiteName, \
+ GTEST_REGISTERED_TEST_NAMES_(SuiteName), \
+ ::testing::internal::GenerateNames< \
+ ::testing::internal::NameGeneratorSelector< \
+ __VA_ARGS__>::type, \
+ ::testing::internal::TypeList<Types>::type>())
+
+// Legacy API is deprecated but still available
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+#define INSTANTIATE_TYPED_TEST_CASE_P \
+ static_assert( \
+ ::testing::internal::InstantiateTypedTestCase_P_IsDeprecated(), ""); \
+ INSTANTIATE_TYPED_TEST_SUITE_P
+#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
#endif // GTEST_HAS_TYPED_TEST_P
diff --git a/extern/gtest/include/gtest/gtest.h b/extern/gtest/include/gtest/gtest.h
index f846c5bd669..dbe5b1c2c3f 100644
--- a/extern/gtest/include/gtest/gtest.h
+++ b/extern/gtest/include/gtest/gtest.h
@@ -26,10 +26,9 @@
// 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: wan@google.com (Zhanyong Wan)
-//
-// The Google C++ Testing Framework (Google Test)
+// The Google C++ Testing and Mocking Framework (Google Test)
//
// This header file defines the public API for Google Test. It should be
// included by any test program that uses Google Test.
@@ -48,16 +47,22 @@
// registration from Barthelemy Dagenais' (barthelemy@prologique.com)
// easyUnit framework.
+// GOOGLETEST_CM0001 DO NOT DELETE
+
#ifndef GTEST_INCLUDE_GTEST_GTEST_H_
#define GTEST_INCLUDE_GTEST_GTEST_H_
+#include <cstddef>
#include <limits>
+#include <memory>
#include <ostream>
+#include <type_traits>
#include <vector>
#include "gtest/internal/gtest-internal.h"
#include "gtest/internal/gtest-string.h"
#include "gtest/gtest-death-test.h"
+#include "gtest/gtest-matchers.h"
#include "gtest/gtest-message.h"
#include "gtest/gtest-param-test.h"
#include "gtest/gtest-printers.h"
@@ -65,23 +70,20 @@
#include "gtest/gtest-test-part.h"
#include "gtest/gtest-typed-test.h"
-// Depending on the platform, different string classes are available.
-// On Linux, in addition to ::std::string, Google also makes use of
-// class ::string, which has the same interface as ::std::string, but
-// has a different implementation.
-//
-// You can define GTEST_HAS_GLOBAL_STRING to 1 to indicate that
-// ::string is available AND is a distinct type to ::std::string, or
-// define it to 0 to indicate otherwise.
-//
-// If ::std::string and ::string are the same class on your platform
-// due to aliasing, you should define GTEST_HAS_GLOBAL_STRING to 0.
-//
-// If you do not define GTEST_HAS_GLOBAL_STRING, it is defined
-// heuristically.
+GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
+/* class A needs to have dll-interface to be used by clients of class B */)
namespace testing {
+// Silence C4100 (unreferenced formal parameter) and 4805
+// unsafe mix of type 'const int' and type 'const bool'
+#ifdef _MSC_VER
+# pragma warning(push)
+# pragma warning(disable:4805)
+# pragma warning(disable:4100)
+#endif
+
+
// Declares the flags.
// This flag temporary enables the disabled tests.
@@ -103,6 +105,10 @@ GTEST_DECLARE_string_(color);
// the tests to run. If the filter is not given all tests are executed.
GTEST_DECLARE_string_(filter);
+// This flag controls whether Google Test installs a signal handler that dumps
+// debugging information when fatal signals are raised.
+GTEST_DECLARE_bool_(install_failure_signal_handler);
+
// This flag causes the Google Test to list tests. None of the tests listed
// are actually run if the flag is provided.
GTEST_DECLARE_bool_(list_tests);
@@ -115,6 +121,9 @@ GTEST_DECLARE_string_(output);
// test.
GTEST_DECLARE_bool_(print_time);
+// This flags control whether Google Test prints UTF8 characters as text.
+GTEST_DECLARE_bool_(print_utf8);
+
// This flag specifies the random number seed.
GTEST_DECLARE_int32_(random_seed);
@@ -135,7 +144,7 @@ GTEST_DECLARE_int32_(stack_trace_depth);
// When this flag is specified, a failed assertion will throw an
// exception if exceptions are enabled, or exit the program with a
-// non-zero code otherwise.
+// non-zero code otherwise. For use with an external test framework.
GTEST_DECLARE_bool_(throw_on_failure);
// When this flag is set with a "host:port" string, on supported
@@ -143,6 +152,10 @@ GTEST_DECLARE_bool_(throw_on_failure);
// the specified host machine.
GTEST_DECLARE_string_(stream_result_to);
+#if GTEST_USE_OWN_FLAGFILE_FLAG_
+GTEST_DECLARE_string_(flagfile);
+#endif // GTEST_USE_OWN_FLAGFILE_FLAG_
+
// The upper limit for valid stack trace depths.
const int kMaxStackTraceDepth = 100;
@@ -160,6 +173,7 @@ class TestEventListenersAccessor;
class TestEventRepeater;
class UnitTestRecordPropertyTestHelper;
class WindowsDeathTest;
+class FuchsiaDeathTest;
class UnitTestImpl* GetUnitTestImpl();
void ReportFailureInUnknownLocation(TestPartResult::Type result_type,
const std::string& message);
@@ -170,7 +184,12 @@ void ReportFailureInUnknownLocation(TestPartResult::Type result_type,
// If we don't forward declare them the compiler might confuse the classes
// in friendship clauses with same named classes on the scope.
class Test;
-class TestCase;
+class TestSuite;
+
+// Old API is still available but deprecated
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+using TestCase = TestSuite;
+#endif
class TestInfo;
class UnitTest;
@@ -259,7 +278,9 @@ class GTEST_API_ AssertionResult {
// Used in EXPECT_TRUE/FALSE(assertion_result).
AssertionResult(const AssertionResult& other);
+#if defined(_MSC_VER) && _MSC_VER < 1910
GTEST_DISABLE_MSC_WARNINGS_PUSH_(4800 /* forcing value to bool */)
+#endif
// Used in the EXPECT_TRUE/FALSE(bool_expression).
//
@@ -271,12 +292,15 @@ class GTEST_API_ AssertionResult {
template <typename T>
explicit AssertionResult(
const T& success,
- typename internal::EnableIf<
- !internal::ImplicitlyConvertible<T, AssertionResult>::value>::type*
- /*enabler*/ = NULL)
+ typename std::enable_if<
+ !std::is_convertible<T, AssertionResult>::value>::type*
+ /*enabler*/
+ = nullptr)
: success_(success) {}
+#if defined(_MSC_VER) && _MSC_VER < 1910
GTEST_DISABLE_MSC_WARNINGS_POP_()
+#endif
// Assignment operator.
AssertionResult& operator=(AssertionResult other) {
@@ -284,7 +308,7 @@ class GTEST_API_ AssertionResult {
return *this;
}
- // Returns true iff the assertion succeeded.
+ // Returns true if and only if the assertion succeeded.
operator bool() const { return success_; } // NOLINT
// Returns the assertion's negation. Used with EXPECT/ASSERT_FALSE.
@@ -295,9 +319,8 @@ class GTEST_API_ AssertionResult {
// assertion's expectation). When nothing has been streamed into the
// object, returns an empty string.
const char* message() const {
- return message_.get() != NULL ? message_->c_str() : "";
+ return message_.get() != nullptr ? message_->c_str() : "";
}
- // TODO(vladl@google.com): Remove this after making sure no clients use it.
// Deprecated; please use message() instead.
const char* failure_message() const { return message(); }
@@ -318,8 +341,7 @@ class GTEST_API_ AssertionResult {
private:
// Appends the contents of message to message_.
void AppendMessage(const Message& a_message) {
- if (message_.get() == NULL)
- message_.reset(new ::std::string);
+ if (message_.get() == nullptr) message_.reset(new ::std::string);
message_->append(a_message.GetString().c_str());
}
@@ -332,7 +354,7 @@ class GTEST_API_ AssertionResult {
// construct is not satisfied with the predicate's outcome.
// Referenced via a pointer to avoid taking too much stack frame space
// with test assertions.
- internal::scoped_ptr< ::std::string> message_;
+ std::unique_ptr< ::std::string> message_;
};
// Makes a successful assertion result.
@@ -345,17 +367,26 @@ GTEST_API_ AssertionResult AssertionFailure();
// Deprecated; use AssertionFailure() << msg.
GTEST_API_ AssertionResult AssertionFailure(const Message& msg);
+} // namespace testing
+
+// Includes the auto-generated header that implements a family of generic
+// predicate assertion macros. This include comes late because it relies on
+// APIs declared above.
+#include "gtest/gtest_pred_impl.h"
+
+namespace testing {
+
// The abstract class that all tests inherit from.
//
-// In Google Test, a unit test program contains one or many TestCases, and
-// each TestCase contains one or many Tests.
+// In Google Test, a unit test program contains one or many TestSuites, and
+// each TestSuite contains one or many Tests.
//
// When you define a test using the TEST macro, you don't need to
// explicitly derive from Test - the TEST macro automatically does
// this for you.
//
// The only time you derive from Test is when defining a test fixture
-// to be used a TEST_F. For example:
+// to be used in a TEST_F. For example:
//
// class FooTest : public testing::Test {
// protected:
@@ -372,49 +403,57 @@ class GTEST_API_ Test {
public:
friend class TestInfo;
- // Defines types for pointers to functions that set up and tear down
- // a test case.
- typedef internal::SetUpTestCaseFunc SetUpTestCaseFunc;
- typedef internal::TearDownTestCaseFunc TearDownTestCaseFunc;
-
// The d'tor is virtual as we intend to inherit from Test.
virtual ~Test();
// Sets up the stuff shared by all tests in this test case.
//
- // Google Test will call Foo::SetUpTestCase() before running the first
+ // Google Test will call Foo::SetUpTestSuite() before running the first
// test in test case Foo. Hence a sub-class can define its own
- // SetUpTestCase() method to shadow the one defined in the super
+ // SetUpTestSuite() method to shadow the one defined in the super
// class.
- static void SetUpTestCase() {}
+ // Failures that happen during SetUpTestSuite are logged but otherwise
+ // ignored.
+ static void SetUpTestSuite() {}
- // Tears down the stuff shared by all tests in this test case.
+ // Tears down the stuff shared by all tests in this test suite.
//
- // Google Test will call Foo::TearDownTestCase() after running the last
+ // Google Test will call Foo::TearDownTestSuite() after running the last
// test in test case Foo. Hence a sub-class can define its own
- // TearDownTestCase() method to shadow the one defined in the super
+ // TearDownTestSuite() method to shadow the one defined in the super
// class.
+ // Failures that happen during TearDownTestSuite are logged but otherwise
+ // ignored.
+ static void TearDownTestSuite() {}
+
+ // Legacy API is deprecated but still available
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
static void TearDownTestCase() {}
+ static void SetUpTestCase() {}
+#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
- // Returns true iff the current test has a fatal failure.
+ // Returns true if and only if the current test has a fatal failure.
static bool HasFatalFailure();
- // Returns true iff the current test has a non-fatal failure.
+ // Returns true if and only if the current test has a non-fatal failure.
static bool HasNonfatalFailure();
- // Returns true iff the current test has a (either fatal or
+ // Returns true if and only if the current test was skipped.
+ static bool IsSkipped();
+
+ // Returns true if and only if the current test has a (either fatal or
// non-fatal) failure.
static bool HasFailure() { return HasFatalFailure() || HasNonfatalFailure(); }
- // Logs a property for the current test, test case, or for the entire
+ // Logs a property for the current test, test suite, or for the entire
// invocation of the test program when used outside of the context of a
- // test case. Only the last value for a given key is remembered. These
+ // test suite. Only the last value for a given key is remembered. These
// are public static so they can be called from utility functions that are
// not members of the test fixture. Calls to RecordProperty made during
// lifespan of the test (from the moment its constructor starts to the
// moment its destructor finishes) will be output in XML as attributes of
// the <testcase> element. Properties recorded from fixture's
- // SetUpTestCase or TearDownTestCase are logged as attributes of the
+ // SetUpTestSuite or TearDownTestSuite are logged as attributes of the
// corresponding <testsuite> element. Calls to RecordProperty made in the
// global context (before or after invocation of RUN_ALL_TESTS and from
// SetUp/TearDown method of Environment objects registered with Google
@@ -433,8 +472,8 @@ class GTEST_API_ Test {
virtual void TearDown();
private:
- // Returns true iff the current test has the same fixture class as
- // the first test in the current test case.
+ // Returns true if and only if the current test has the same fixture class
+ // as the first test in the current test suite.
static bool HasSameFixtureClass();
// Runs the test after the test fixture has been set up.
@@ -452,7 +491,7 @@ class GTEST_API_ Test {
// internal method to avoid clashing with names used in user TESTs.
void DeleteSelf_() { delete this; }
- const internal::scoped_ptr< GTEST_FLAG_SAVER_ > gtest_flag_saver_;
+ const std::unique_ptr<GTEST_FLAG_SAVER_> gtest_flag_saver_;
// Often a user misspells SetUp() as Setup() and spends a long time
// wondering why it is never called by Google Test. The declaration of
@@ -471,7 +510,7 @@ class GTEST_API_ Test {
// If you see an error about overriding the following function or
// about it being private, you have mis-spelled SetUp() as Setup().
struct Setup_should_be_spelled_SetUp {};
- virtual Setup_should_be_spelled_SetUp* Setup() { return NULL; }
+ virtual Setup_should_be_spelled_SetUp* Setup() { return nullptr; }
// We disallow copying Tests.
GTEST_DISALLOW_COPY_AND_ASSIGN_(Test);
@@ -535,24 +574,30 @@ class GTEST_API_ TestResult {
// Returns the number of the test properties.
int test_property_count() const;
- // Returns true iff the test passed (i.e. no test part failed).
- bool Passed() const { return !Failed(); }
+ // Returns true if and only if the test passed (i.e. no test part failed).
+ bool Passed() const { return !Skipped() && !Failed(); }
+
+ // Returns true if and only if the test was skipped.
+ bool Skipped() const;
- // Returns true iff the test failed.
+ // Returns true if and only if the test failed.
bool Failed() const;
- // Returns true iff the test fatally failed.
+ // Returns true if and only if the test fatally failed.
bool HasFatalFailure() const;
- // Returns true iff the test has a non-fatal failure.
+ // Returns true if and only if the test has a non-fatal failure.
bool HasNonfatalFailure() const;
// Returns the elapsed time, in milliseconds.
TimeInMillis elapsed_time() const { return elapsed_time_; }
- // Returns the i-th test part result among all the results. i can range
- // from 0 to test_property_count() - 1. If i is not in that range, aborts
- // the program.
+ // Gets the time of the test case start, in ms from the start of the
+ // UNIX epoch.
+ TimeInMillis start_timestamp() const { return start_timestamp_; }
+
+ // Returns the i-th test part result among all the results. i can range from 0
+ // to total_part_count() - 1. If i is not in that range, aborts the program.
const TestPartResult& GetTestPartResult(int i) const;
// Returns the i-th test property. i can range from 0 to
@@ -562,13 +607,14 @@ class GTEST_API_ TestResult {
private:
friend class TestInfo;
- friend class TestCase;
+ friend class TestSuite;
friend class UnitTest;
friend class internal::DefaultGlobalTestPartResultReporter;
friend class internal::ExecDeathTest;
friend class internal::TestResultAccessor;
friend class internal::UnitTestImpl;
friend class internal::WindowsDeathTest;
+ friend class internal::FuchsiaDeathTest;
// Gets the vector of TestPartResults.
const std::vector<TestPartResult>& test_part_results() const {
@@ -580,6 +626,9 @@ class GTEST_API_ TestResult {
return test_properties_;
}
+ // Sets the start time.
+ void set_start_timestamp(TimeInMillis start) { start_timestamp_ = start; }
+
// Sets the elapsed time.
void set_elapsed_time(TimeInMillis elapsed) { elapsed_time_ = elapsed; }
@@ -593,8 +642,8 @@ class GTEST_API_ TestResult {
const TestProperty& test_property);
// Adds a failure if the key is a reserved attribute of Google Test
- // testcase tags. Returns true if the property is valid.
- // TODO(russr): Validate attribute names are legal and human readable.
+ // testsuite tags. Returns true if the property is valid.
+ // FIXME: Validate attribute names are legal and human readable.
static bool ValidateTestProperty(const std::string& xml_element,
const TestProperty& test_property);
@@ -623,6 +672,8 @@ class GTEST_API_ TestResult {
std::vector<TestProperty> test_properties_;
// Running count of death tests.
int death_test_count_;
+ // The start time, in milliseconds since UNIX Epoch.
+ TimeInMillis start_timestamp_;
// The elapsed time, in milliseconds.
TimeInMillis elapsed_time_;
@@ -632,7 +683,7 @@ class GTEST_API_ TestResult {
// A TestInfo object stores the following information about a test:
//
-// Test case name
+// Test suite name
// Test name
// Whether the test should be run
// A function pointer that creates the test object when invoked
@@ -647,8 +698,13 @@ class GTEST_API_ TestInfo {
// don't inherit from TestInfo.
~TestInfo();
- // Returns the test case name.
- const char* test_case_name() const { return test_case_name_.c_str(); }
+ // Returns the test suite name.
+ const char* test_suite_name() const { return test_suite_name_.c_str(); }
+
+// Legacy API is deprecated but still available
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+ const char* test_case_name() const { return test_suite_name(); }
+#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
// Returns the test name.
const char* name() const { return name_.c_str(); }
@@ -656,17 +712,15 @@ class GTEST_API_ TestInfo {
// Returns the name of the parameter type, or NULL if this is not a typed
// or a type-parameterized test.
const char* type_param() const {
- if (type_param_.get() != NULL)
- return type_param_->c_str();
- return NULL;
+ if (type_param_.get() != nullptr) return type_param_->c_str();
+ return nullptr;
}
// Returns the text representation of the value parameter, or NULL if this
// is not a value-parameterized test.
const char* value_param() const {
- if (value_param_.get() != NULL)
- return value_param_->c_str();
- return NULL;
+ if (value_param_.get() != nullptr) return value_param_->c_str();
+ return nullptr;
}
// Returns the file name where this test is defined.
@@ -675,12 +729,15 @@ class GTEST_API_ TestInfo {
// Returns the line where this test is defined.
int line() const { return location_.line; }
+ // Return true if this test should not be run because it's in another shard.
+ bool is_in_another_shard() const { return is_in_another_shard_; }
+
// Returns true if this test should run, that is if the test is not
// disabled (or it is disabled but the also_run_disabled_tests flag has
// been specified) and its full name matches the user-specified filter.
//
// Google Test allows the user to filter the tests by their full names.
- // The full name of a test Bar in test case Foo is defined as
+ // The full name of a test Bar in test suite Foo is defined as
// "Foo.Bar". Only the tests that match the filter will run.
//
// A filter is a colon-separated list of glob (not regex) patterns,
@@ -693,12 +750,11 @@ class GTEST_API_ TestInfo {
// contains the character 'A' or starts with "Foo.".
bool should_run() const { return should_run_; }
- // Returns true iff this test will appear in the XML report.
+ // Returns true if and only if this test will appear in the XML report.
bool is_reportable() const {
- // For now, the XML report includes all tests matching the filter.
- // In the future, we may trim tests that are excluded because of
- // sharding.
- return matches_filter_;
+ // The XML report includes tests matching the filter, excluding those
+ // run in other shards.
+ return matches_filter_ && !is_in_another_shard_;
}
// Returns the result of the test.
@@ -709,24 +765,19 @@ class GTEST_API_ TestInfo {
friend class internal::DefaultDeathTestFactory;
#endif // GTEST_HAS_DEATH_TEST
friend class Test;
- friend class TestCase;
+ friend class TestSuite;
friend class internal::UnitTestImpl;
friend class internal::StreamingListenerTest;
friend TestInfo* internal::MakeAndRegisterTestInfo(
- const char* test_case_name,
- const char* name,
- const char* type_param,
- const char* value_param,
- internal::CodeLocation code_location,
- internal::TypeId fixture_class_id,
- Test::SetUpTestCaseFunc set_up_tc,
- Test::TearDownTestCaseFunc tear_down_tc,
+ const char* test_suite_name, const char* name, const char* type_param,
+ const char* value_param, internal::CodeLocation code_location,
+ internal::TypeId fixture_class_id, internal::SetUpTestSuiteFunc set_up_tc,
+ internal::TearDownTestSuiteFunc tear_down_tc,
internal::TestFactoryBase* factory);
// Constructs a TestInfo object. The newly constructed instance assumes
// ownership of the factory object.
- TestInfo(const std::string& test_case_name,
- const std::string& name,
+ TestInfo(const std::string& test_suite_name, const std::string& name,
const char* a_type_param, // NULL if not a type-parameterized test
const char* a_value_param, // NULL if not a value-parameterized test
internal::CodeLocation a_code_location,
@@ -748,20 +799,21 @@ class GTEST_API_ TestInfo {
}
// These fields are immutable properties of the test.
- const std::string test_case_name_; // Test case name
+ const std::string test_suite_name_; // test suite name
const std::string name_; // Test name
// Name of the parameter type, or NULL if this is not a typed or a
// type-parameterized test.
- const internal::scoped_ptr<const ::std::string> type_param_;
+ const std::unique_ptr<const ::std::string> type_param_;
// Text representation of the value parameter, or NULL if this is not a
// value-parameterized test.
- const internal::scoped_ptr<const ::std::string> value_param_;
+ const std::unique_ptr<const ::std::string> value_param_;
internal::CodeLocation location_;
- const internal::TypeId fixture_class_id_; // ID of the test fixture class
- bool should_run_; // True iff this test should run
- bool is_disabled_; // True iff this test is disabled
- bool matches_filter_; // True if this test matches the
- // user-specified filter.
+ const internal::TypeId fixture_class_id_; // ID of the test fixture class
+ bool should_run_; // True if and only if this test should run
+ bool is_disabled_; // True if and only if this test is disabled
+ bool matches_filter_; // True if this test matches the
+ // user-specified filter.
+ bool is_in_another_shard_; // Will be run in another shard.
internal::TestFactoryBase* const factory_; // The factory that creates
// the test object
@@ -772,90 +824,96 @@ class GTEST_API_ TestInfo {
GTEST_DISALLOW_COPY_AND_ASSIGN_(TestInfo);
};
-// A test case, which consists of a vector of TestInfos.
+// A test suite, which consists of a vector of TestInfos.
//
-// TestCase is not copyable.
-class GTEST_API_ TestCase {
+// TestSuite is not copyable.
+class GTEST_API_ TestSuite {
public:
- // Creates a TestCase with the given name.
+ // Creates a TestSuite with the given name.
//
- // TestCase does NOT have a default constructor. Always use this
- // constructor to create a TestCase object.
+ // TestSuite does NOT have a default constructor. Always use this
+ // constructor to create a TestSuite object.
//
// Arguments:
//
- // name: name of the test case
+ // name: name of the test suite
// a_type_param: the name of the test's type parameter, or NULL if
// this is not a type-parameterized test.
- // set_up_tc: pointer to the function that sets up the test case
- // tear_down_tc: pointer to the function that tears down the test case
- TestCase(const char* name, const char* a_type_param,
- Test::SetUpTestCaseFunc set_up_tc,
- Test::TearDownTestCaseFunc tear_down_tc);
+ // set_up_tc: pointer to the function that sets up the test suite
+ // tear_down_tc: pointer to the function that tears down the test suite
+ TestSuite(const char* name, const char* a_type_param,
+ internal::SetUpTestSuiteFunc set_up_tc,
+ internal::TearDownTestSuiteFunc tear_down_tc);
- // Destructor of TestCase.
- virtual ~TestCase();
+ // Destructor of TestSuite.
+ virtual ~TestSuite();
- // Gets the name of the TestCase.
+ // Gets the name of the TestSuite.
const char* name() const { return name_.c_str(); }
// Returns the name of the parameter type, or NULL if this is not a
- // type-parameterized test case.
+ // type-parameterized test suite.
const char* type_param() const {
- if (type_param_.get() != NULL)
- return type_param_->c_str();
- return NULL;
+ if (type_param_.get() != nullptr) return type_param_->c_str();
+ return nullptr;
}
- // Returns true if any test in this test case should run.
+ // Returns true if any test in this test suite should run.
bool should_run() const { return should_run_; }
- // Gets the number of successful tests in this test case.
+ // Gets the number of successful tests in this test suite.
int successful_test_count() const;
- // Gets the number of failed tests in this test case.
+ // Gets the number of skipped tests in this test suite.
+ int skipped_test_count() const;
+
+ // Gets the number of failed tests in this test suite.
int failed_test_count() const;
// Gets the number of disabled tests that will be reported in the XML report.
int reportable_disabled_test_count() const;
- // Gets the number of disabled tests in this test case.
+ // Gets the number of disabled tests in this test suite.
int disabled_test_count() const;
// Gets the number of tests to be printed in the XML report.
int reportable_test_count() const;
- // Get the number of tests in this test case that should run.
+ // Get the number of tests in this test suite that should run.
int test_to_run_count() const;
- // Gets the number of all tests in this test case.
+ // Gets the number of all tests in this test suite.
int total_test_count() const;
- // Returns true iff the test case passed.
+ // Returns true if and only if the test suite passed.
bool Passed() const { return !Failed(); }
- // Returns true iff the test case failed.
+ // Returns true if and only if the test suite failed.
bool Failed() const { return failed_test_count() > 0; }
// Returns the elapsed time, in milliseconds.
TimeInMillis elapsed_time() const { return elapsed_time_; }
+ // Gets the time of the test suite start, in ms from the start of the
+ // UNIX epoch.
+ TimeInMillis start_timestamp() const { return start_timestamp_; }
+
// Returns the i-th test among all the tests. i can range from 0 to
// total_test_count() - 1. If i is not in that range, returns NULL.
const TestInfo* GetTestInfo(int i) const;
// Returns the TestResult that holds test properties recorded during
- // execution of SetUpTestCase and TearDownTestCase.
+ // execution of SetUpTestSuite and TearDownTestSuite.
const TestResult& ad_hoc_test_result() const { return ad_hoc_test_result_; }
private:
friend class Test;
friend class internal::UnitTestImpl;
- // Gets the (mutable) vector of TestInfos in this TestCase.
+ // Gets the (mutable) vector of TestInfos in this TestSuite.
std::vector<TestInfo*>& test_info_list() { return test_info_list_; }
- // Gets the (immutable) vector of TestInfos in this TestCase.
+ // Gets the (immutable) vector of TestInfos in this TestSuite.
const std::vector<TestInfo*>& test_info_list() const {
return test_info_list_;
}
@@ -867,51 +925,64 @@ class GTEST_API_ TestCase {
// Sets the should_run member.
void set_should_run(bool should) { should_run_ = should; }
- // Adds a TestInfo to this test case. Will delete the TestInfo upon
- // destruction of the TestCase object.
+ // Adds a TestInfo to this test suite. Will delete the TestInfo upon
+ // destruction of the TestSuite object.
void AddTestInfo(TestInfo * test_info);
- // Clears the results of all tests in this test case.
+ // Clears the results of all tests in this test suite.
void ClearResult();
- // Clears the results of all tests in the given test case.
- static void ClearTestCaseResult(TestCase* test_case) {
- test_case->ClearResult();
+ // Clears the results of all tests in the given test suite.
+ static void ClearTestSuiteResult(TestSuite* test_suite) {
+ test_suite->ClearResult();
}
- // Runs every test in this TestCase.
+ // Runs every test in this TestSuite.
void Run();
- // Runs SetUpTestCase() for this TestCase. This wrapper is needed
- // for catching exceptions thrown from SetUpTestCase().
- void RunSetUpTestCase() { (*set_up_tc_)(); }
+ // Runs SetUpTestSuite() for this TestSuite. This wrapper is needed
+ // for catching exceptions thrown from SetUpTestSuite().
+ void RunSetUpTestSuite() {
+ if (set_up_tc_ != nullptr) {
+ (*set_up_tc_)();
+ }
+ }
- // Runs TearDownTestCase() for this TestCase. This wrapper is
- // needed for catching exceptions thrown from TearDownTestCase().
- void RunTearDownTestCase() { (*tear_down_tc_)(); }
+ // Runs TearDownTestSuite() for this TestSuite. This wrapper is
+ // needed for catching exceptions thrown from TearDownTestSuite().
+ void RunTearDownTestSuite() {
+ if (tear_down_tc_ != nullptr) {
+ (*tear_down_tc_)();
+ }
+ }
- // Returns true iff test passed.
+ // Returns true if and only if test passed.
static bool TestPassed(const TestInfo* test_info) {
return test_info->should_run() && test_info->result()->Passed();
}
- // Returns true iff test failed.
+ // Returns true if and only if test skipped.
+ static bool TestSkipped(const TestInfo* test_info) {
+ return test_info->should_run() && test_info->result()->Skipped();
+ }
+
+ // Returns true if and only if test failed.
static bool TestFailed(const TestInfo* test_info) {
return test_info->should_run() && test_info->result()->Failed();
}
- // Returns true iff the test is disabled and will be reported in the XML
- // report.
+ // Returns true if and only if the test is disabled and will be reported in
+ // the XML report.
static bool TestReportableDisabled(const TestInfo* test_info) {
return test_info->is_reportable() && test_info->is_disabled_;
}
- // Returns true iff test is disabled.
+ // Returns true if and only if test is disabled.
static bool TestDisabled(const TestInfo* test_info) {
return test_info->is_disabled_;
}
- // Returns true iff this test will appear in the XML report.
+ // Returns true if and only if this test will appear in the XML report.
static bool TestReportable(const TestInfo* test_info) {
return test_info->is_reportable();
}
@@ -921,17 +992,17 @@ class GTEST_API_ TestCase {
return test_info->should_run();
}
- // Shuffles the tests in this test case.
+ // Shuffles the tests in this test suite.
void ShuffleTests(internal::Random* random);
// Restores the test order to before the first shuffle.
void UnshuffleTests();
- // Name of the test case.
+ // Name of the test suite.
std::string name_;
// Name of the parameter type, or NULL if this is not a typed or a
// type-parameterized test.
- const internal::scoped_ptr<const ::std::string> type_param_;
+ const std::unique_ptr<const ::std::string> type_param_;
// The vector of TestInfos in their original order. It owns the
// elements in the vector.
std::vector<TestInfo*> test_info_list_;
@@ -939,20 +1010,22 @@ class GTEST_API_ TestCase {
// shuffling and restoring the test order. The i-th element in this
// vector is the index of the i-th test in the shuffled test list.
std::vector<int> test_indices_;
- // Pointer to the function that sets up the test case.
- Test::SetUpTestCaseFunc set_up_tc_;
- // Pointer to the function that tears down the test case.
- Test::TearDownTestCaseFunc tear_down_tc_;
- // True iff any test in this test case should run.
+ // Pointer to the function that sets up the test suite.
+ internal::SetUpTestSuiteFunc set_up_tc_;
+ // Pointer to the function that tears down the test suite.
+ internal::TearDownTestSuiteFunc tear_down_tc_;
+ // True if and only if any test in this test suite should run.
bool should_run_;
+ // The start time, in milliseconds since UNIX Epoch.
+ TimeInMillis start_timestamp_;
// Elapsed time, in milliseconds.
TimeInMillis elapsed_time_;
- // Holds test properties recorded during execution of SetUpTestCase and
- // TearDownTestCase.
+ // Holds test properties recorded during execution of SetUpTestSuite and
+ // TearDownTestSuite.
TestResult ad_hoc_test_result_;
- // We disallow copying TestCases.
- GTEST_DISALLOW_COPY_AND_ASSIGN_(TestCase);
+ // We disallow copying TestSuites.
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(TestSuite);
};
// An Environment object is capable of setting up and tearing down an
@@ -983,9 +1056,21 @@ class Environment {
// If you see an error about overriding the following function or
// about it being private, you have mis-spelled SetUp() as Setup().
struct Setup_should_be_spelled_SetUp {};
- virtual Setup_should_be_spelled_SetUp* Setup() { return NULL; }
+ virtual Setup_should_be_spelled_SetUp* Setup() { return nullptr; }
};
+#if GTEST_HAS_EXCEPTIONS
+
+// Exception which can be thrown from TestEventListener::OnTestPartResult.
+class GTEST_API_ AssertionException
+ : public internal::GoogleTestFailureException {
+ public:
+ explicit AssertionException(const TestPartResult& result)
+ : GoogleTestFailureException(result) {}
+};
+
+#endif // GTEST_HAS_EXCEPTIONS
+
// The interface for tracing execution of tests. The methods are organized in
// the order the corresponding events are fired.
class TestEventListener {
@@ -1007,20 +1092,32 @@ class TestEventListener {
// Fired after environment set-up for each iteration of tests ends.
virtual void OnEnvironmentsSetUpEnd(const UnitTest& unit_test) = 0;
- // Fired before the test case starts.
- virtual void OnTestCaseStart(const TestCase& test_case) = 0;
+ // Fired before the test suite starts.
+ virtual void OnTestSuiteStart(const TestSuite& /*test_suite*/) {}
+
+ // Legacy API is deprecated but still available
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+ virtual void OnTestCaseStart(const TestCase& /*test_case*/) {}
+#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
// Fired before the test starts.
virtual void OnTestStart(const TestInfo& test_info) = 0;
// Fired after a failed assertion or a SUCCEED() invocation.
+ // If you want to throw an exception from this function to skip to the next
+ // TEST, it must be AssertionException defined above, or inherited from it.
virtual void OnTestPartResult(const TestPartResult& test_part_result) = 0;
// Fired after the test ends.
virtual void OnTestEnd(const TestInfo& test_info) = 0;
- // Fired after the test case ends.
- virtual void OnTestCaseEnd(const TestCase& test_case) = 0;
+ // Fired after the test suite ends.
+ virtual void OnTestSuiteEnd(const TestSuite& /*test_suite*/) {}
+
+// Legacy API is deprecated but still available
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+ virtual void OnTestCaseEnd(const TestCase& /*test_case*/) {}
+#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
// Fired before environment tear-down for each iteration of tests starts.
virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test) = 0;
@@ -1043,21 +1140,30 @@ class TestEventListener {
// above.
class EmptyTestEventListener : public TestEventListener {
public:
- virtual void OnTestProgramStart(const UnitTest& /*unit_test*/) {}
- virtual void OnTestIterationStart(const UnitTest& /*unit_test*/,
- int /*iteration*/) {}
- virtual void OnEnvironmentsSetUpStart(const UnitTest& /*unit_test*/) {}
- virtual void OnEnvironmentsSetUpEnd(const UnitTest& /*unit_test*/) {}
- virtual void OnTestCaseStart(const TestCase& /*test_case*/) {}
- virtual void OnTestStart(const TestInfo& /*test_info*/) {}
- virtual void OnTestPartResult(const TestPartResult& /*test_part_result*/) {}
- virtual void OnTestEnd(const TestInfo& /*test_info*/) {}
- virtual void OnTestCaseEnd(const TestCase& /*test_case*/) {}
- virtual void OnEnvironmentsTearDownStart(const UnitTest& /*unit_test*/) {}
- virtual void OnEnvironmentsTearDownEnd(const UnitTest& /*unit_test*/) {}
- virtual void OnTestIterationEnd(const UnitTest& /*unit_test*/,
- int /*iteration*/) {}
- virtual void OnTestProgramEnd(const UnitTest& /*unit_test*/) {}
+ void OnTestProgramStart(const UnitTest& /*unit_test*/) override {}
+ void OnTestIterationStart(const UnitTest& /*unit_test*/,
+ int /*iteration*/) override {}
+ void OnEnvironmentsSetUpStart(const UnitTest& /*unit_test*/) override {}
+ void OnEnvironmentsSetUpEnd(const UnitTest& /*unit_test*/) override {}
+ void OnTestSuiteStart(const TestSuite& /*test_suite*/) override {}
+// Legacy API is deprecated but still available
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+ void OnTestCaseStart(const TestCase& /*test_case*/) override {}
+#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+
+ void OnTestStart(const TestInfo& /*test_info*/) override {}
+ void OnTestPartResult(const TestPartResult& /*test_part_result*/) override {}
+ void OnTestEnd(const TestInfo& /*test_info*/) override {}
+ void OnTestSuiteEnd(const TestSuite& /*test_suite*/) override {}
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+ void OnTestCaseEnd(const TestCase& /*test_case*/) override {}
+#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+
+ void OnEnvironmentsTearDownStart(const UnitTest& /*unit_test*/) override {}
+ void OnEnvironmentsTearDownEnd(const UnitTest& /*unit_test*/) override {}
+ void OnTestIterationEnd(const UnitTest& /*unit_test*/,
+ int /*iteration*/) override {}
+ void OnTestProgramEnd(const UnitTest& /*unit_test*/) override {}
};
// TestEventListeners lets users add listeners to track events in Google Test.
@@ -1097,7 +1203,7 @@ class GTEST_API_ TestEventListeners {
}
private:
- friend class TestCase;
+ friend class TestSuite;
friend class TestInfo;
friend class internal::DefaultGlobalTestPartResultReporter;
friend class internal::NoExecDeathTest;
@@ -1138,7 +1244,7 @@ class GTEST_API_ TestEventListeners {
GTEST_DISALLOW_COPY_AND_ASSIGN_(TestEventListeners);
};
-// A UnitTest consists of a vector of TestCases.
+// A UnitTest consists of a vector of TestSuites.
//
// This is a singleton class. The only instance of UnitTest is
// created when UnitTest::GetInstance() is first called. This
@@ -1167,10 +1273,14 @@ class GTEST_API_ UnitTest {
// was executed. The UnitTest object owns the string.
const char* original_working_dir() const;
- // Returns the TestCase object for the test that's currently running,
+ // Returns the TestSuite object for the test that's currently running,
// or NULL if no test is running.
- const TestCase* current_test_case() const
- GTEST_LOCK_EXCLUDED_(mutex_);
+ const TestSuite* current_test_suite() const GTEST_LOCK_EXCLUDED_(mutex_);
+
+// Legacy API is still available but deprecated
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+ const TestCase* current_test_case() const GTEST_LOCK_EXCLUDED_(mutex_);
+#endif
// Returns the TestInfo object for the test that's currently running,
// or NULL if no test is running.
@@ -1180,31 +1290,40 @@ class GTEST_API_ UnitTest {
// Returns the random seed used at the start of the current test run.
int random_seed() const;
-#if GTEST_HAS_PARAM_TEST
- // Returns the ParameterizedTestCaseRegistry object used to keep track of
+ // Returns the ParameterizedTestSuiteRegistry object used to keep track of
// value-parameterized tests and instantiate and register them.
//
// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
- internal::ParameterizedTestCaseRegistry& parameterized_test_registry()
+ internal::ParameterizedTestSuiteRegistry& parameterized_test_registry()
GTEST_LOCK_EXCLUDED_(mutex_);
-#endif // GTEST_HAS_PARAM_TEST
- // Gets the number of successful test cases.
- int successful_test_case_count() const;
+ // Gets the number of successful test suites.
+ int successful_test_suite_count() const;
- // Gets the number of failed test cases.
- int failed_test_case_count() const;
+ // Gets the number of failed test suites.
+ int failed_test_suite_count() const;
- // Gets the number of all test cases.
- int total_test_case_count() const;
+ // Gets the number of all test suites.
+ int total_test_suite_count() const;
- // Gets the number of all test cases that contain at least one test
+ // Gets the number of all test suites that contain at least one test
// that should run.
+ int test_suite_to_run_count() const;
+
+ // Legacy API is deprecated but still available
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+ int successful_test_case_count() const;
+ int failed_test_case_count() const;
+ int total_test_case_count() const;
int test_case_to_run_count() const;
+#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
// Gets the number of successful tests.
int successful_test_count() const;
+ // Gets the number of skipped tests.
+ int skipped_test_count() const;
+
// Gets the number of failed tests.
int failed_test_count() const;
@@ -1230,19 +1349,25 @@ class GTEST_API_ UnitTest {
// Gets the elapsed time, in milliseconds.
TimeInMillis elapsed_time() const;
- // Returns true iff the unit test passed (i.e. all test cases passed).
+ // Returns true if and only if the unit test passed (i.e. all test suites
+ // passed).
bool Passed() const;
- // Returns true iff the unit test failed (i.e. some test case failed
- // or something outside of all tests failed).
+ // Returns true if and only if the unit test failed (i.e. some test suite
+ // failed or something outside of all tests failed).
bool Failed() const;
- // Gets the i-th test case among all the test cases. i can range from 0 to
- // total_test_case_count() - 1. If i is not in that range, returns NULL.
+ // Gets the i-th test suite among all the test suites. i can range from 0 to
+ // total_test_suite_count() - 1. If i is not in that range, returns NULL.
+ const TestSuite* GetTestSuite(int i) const;
+
+// Legacy API is deprecated but still available
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
const TestCase* GetTestCase(int i) const;
+#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
// Returns the TestResult containing information on test failures and
- // properties logged outside of individual test cases.
+ // properties logged outside of individual test suites.
const TestResult& ad_hoc_test_result() const;
// Returns the list of event listeners that can be used to track events
@@ -1273,25 +1398,25 @@ class GTEST_API_ UnitTest {
GTEST_LOCK_EXCLUDED_(mutex_);
// Adds a TestProperty to the current TestResult object when invoked from
- // inside a test, to current TestCase's ad_hoc_test_result_ when invoked
- // from SetUpTestCase or TearDownTestCase, or to the global property set
+ // inside a test, to current TestSuite's ad_hoc_test_result_ when invoked
+ // from SetUpTestSuite or TearDownTestSuite, or to the global property set
// when invoked elsewhere. If the result already contains a property with
// the same key, the value will be updated.
void RecordProperty(const std::string& key, const std::string& value);
- // Gets the i-th test case among all the test cases. i can range from 0 to
- // total_test_case_count() - 1. If i is not in that range, returns NULL.
- TestCase* GetMutableTestCase(int i);
+ // Gets the i-th test suite among all the test suites. i can range from 0 to
+ // total_test_suite_count() - 1. If i is not in that range, returns NULL.
+ TestSuite* GetMutableTestSuite(int i);
// Accessors for the implementation object.
internal::UnitTestImpl* impl() { return impl_; }
const internal::UnitTestImpl* impl() const { return impl_; }
- // These classes and funcions are friends as they need to access private
+ // These classes and functions are friends as they need to access private
// members of UnitTest.
+ friend class ScopedTrace;
friend class Test;
friend class internal::AssertHelper;
- friend class internal::ScopedTrace;
friend class internal::StreamingListenerTest;
friend class internal::UnitTestRecordPropertyTestHelper;
friend Environment* AddGlobalTestEnvironment(Environment* env);
@@ -1366,6 +1491,10 @@ GTEST_API_ void InitGoogleTest(int* argc, char** argv);
// UNICODE mode.
GTEST_API_ void InitGoogleTest(int* argc, wchar_t** argv);
+// This overloaded version can be used on Arduino/embedded platforms where
+// there is no argc/argv.
+GTEST_API_ void InitGoogleTest();
+
namespace internal {
// Separate the error generating code from the code path to reduce the stack
@@ -1382,17 +1511,22 @@ AssertionResult CmpHelperEQFailure(const char* lhs_expression,
false);
}
+// This block of code defines operator==/!=
+// to block lexical scope lookup.
+// It prevents using invalid operator==/!= defined at namespace scope.
+struct faketype {};
+inline bool operator==(faketype, faketype) { return true; }
+inline bool operator!=(faketype, faketype) { return false; }
+
// The helper function for {ASSERT|EXPECT}_EQ.
template <typename T1, typename T2>
AssertionResult CmpHelperEQ(const char* lhs_expression,
const char* rhs_expression,
const T1& lhs,
const T2& rhs) {
-GTEST_DISABLE_MSC_WARNINGS_PUSH_(4389 /* signed/unsigned mismatch */)
if (lhs == rhs) {
return AssertionSuccess();
}
-GTEST_DISABLE_MSC_WARNINGS_POP_()
return CmpHelperEQFailure(lhs_expression, rhs_expression, lhs, rhs);
}
@@ -1405,18 +1539,17 @@ GTEST_API_ AssertionResult CmpHelperEQ(const char* lhs_expression,
BiggestInt lhs,
BiggestInt rhs);
-// The helper class for {ASSERT|EXPECT}_EQ. The template argument
-// lhs_is_null_literal is true iff the first argument to ASSERT_EQ()
-// is a null pointer literal. The following default implementation is
-// for lhs_is_null_literal being false.
-template <bool lhs_is_null_literal>
class EqHelper {
public:
// This templatized version is for the general case.
- template <typename T1, typename T2>
+ template <
+ typename T1, typename T2,
+ // Disable this overload for cases where one argument is a pointer
+ // and the other is the null pointer constant.
+ typename std::enable_if<!std::is_integral<T1>::value ||
+ !std::is_pointer<T2>::value>::type* = nullptr>
static AssertionResult Compare(const char* lhs_expression,
- const char* rhs_expression,
- const T1& lhs,
+ const char* rhs_expression, const T1& lhs,
const T2& rhs) {
return CmpHelperEQ(lhs_expression, rhs_expression, lhs, rhs);
}
@@ -1433,49 +1566,15 @@ class EqHelper {
BiggestInt rhs) {
return CmpHelperEQ(lhs_expression, rhs_expression, lhs, rhs);
}
-};
-// This specialization is used when the first argument to ASSERT_EQ()
-// is a null pointer literal, like NULL, false, or 0.
-template <>
-class EqHelper<true> {
- public:
- // We define two overloaded versions of Compare(). The first
- // version will be picked when the second argument to ASSERT_EQ() is
- // NOT a pointer, e.g. ASSERT_EQ(0, AnIntFunction()) or
- // EXPECT_EQ(false, a_bool).
- template <typename T1, typename T2>
- static AssertionResult Compare(
- const char* lhs_expression,
- const char* rhs_expression,
- const T1& lhs,
- const T2& rhs,
- // The following line prevents this overload from being considered if T2
- // is not a pointer type. We need this because ASSERT_EQ(NULL, my_ptr)
- // expands to Compare("", "", NULL, my_ptr), which requires a conversion
- // to match the Secret* in the other overload, which would otherwise make
- // this template match better.
- typename EnableIf<!is_pointer<T2>::value>::type* = 0) {
- return CmpHelperEQ(lhs_expression, rhs_expression, lhs, rhs);
- }
-
- // This version will be picked when the second argument to ASSERT_EQ() is a
- // pointer, e.g. ASSERT_EQ(NULL, a_pointer).
template <typename T>
static AssertionResult Compare(
- const char* lhs_expression,
- const char* rhs_expression,
- // We used to have a second template parameter instead of Secret*. That
- // template parameter would deduce to 'long', making this a better match
- // than the first overload even without the first overload's EnableIf.
- // Unfortunately, gcc with -Wconversion-null warns when "passing NULL to
- // non-pointer argument" (even a deduced integral argument), so the old
- // implementation caused warnings in user code.
- Secret* /* lhs (NULL) */,
- T* rhs) {
+ const char* lhs_expression, const char* rhs_expression,
+ // Handle cases where '0' is used as a null pointer literal.
+ std::nullptr_t /* lhs */, T* rhs) {
// We already know that 'lhs' is a null pointer.
- return CmpHelperEQ(lhs_expression, rhs_expression,
- static_cast<T*>(NULL), rhs);
+ return CmpHelperEQ(lhs_expression, rhs_expression, static_cast<T*>(nullptr),
+ rhs);
}
};
@@ -1704,9 +1803,14 @@ class GTEST_API_ AssertHelper {
GTEST_DISALLOW_COPY_AND_ASSIGN_(AssertHelper);
};
+enum GTestColor { COLOR_DEFAULT, COLOR_RED, COLOR_GREEN, COLOR_YELLOW };
+
+GTEST_API_ GTEST_ATTRIBUTE_PRINTF_(2, 3) void ColoredPrintf(GTestColor color,
+ const char* fmt,
+ ...);
+
} // namespace internal
-#if GTEST_HAS_PARAM_TEST
// The pure interface class that all value-parameterized tests inherit from.
// A value-parameterized class must inherit from both ::testing::Test and
// ::testing::WithParamInterface. In most cases that just means inheriting
@@ -1724,13 +1828,13 @@ class GTEST_API_ AssertHelper {
// FooTest() {
// // Can use GetParam() here.
// }
-// virtual ~FooTest() {
+// ~FooTest() override {
// // Can use GetParam() here.
// }
-// virtual void SetUp() {
+// void SetUp() override {
// // Can use GetParam() here.
// }
-// virtual void TearDown {
+// void TearDown override {
// // Can use GetParam() here.
// }
// };
@@ -1739,7 +1843,7 @@ class GTEST_API_ AssertHelper {
// Foo foo;
// ASSERT_TRUE(foo.DoesBar(GetParam()));
// }
-// INSTANTIATE_TEST_CASE_P(OneToTenRange, FooTest, ::testing::Range(1, 10));
+// INSTANTIATE_TEST_SUITE_P(OneToTenRange, FooTest, ::testing::Range(1, 10));
template <typename T>
class WithParamInterface {
@@ -1748,12 +1852,9 @@ class WithParamInterface {
virtual ~WithParamInterface() {}
// The current parameter value. Is also available in the test fixture's
- // constructor. This member function is non-static, even though it only
- // references static data, to reduce the opportunity for incorrect uses
- // like writing 'WithParamInterface<bool>::GetParam()' for a test that
- // uses a fixture whose parameter type is int.
- const ParamType& GetParam() const {
- GTEST_CHECK_(parameter_ != NULL)
+ // constructor.
+ static const ParamType& GetParam() {
+ GTEST_CHECK_(parameter_ != nullptr)
<< "GetParam() can only be called inside a value-parameterized test "
<< "-- did you intend to write TEST_P instead of TEST_F?";
return *parameter_;
@@ -1774,7 +1875,7 @@ class WithParamInterface {
};
template <typename T>
-const T* WithParamInterface<T>::parameter_ = NULL;
+const T* WithParamInterface<T>::parameter_ = nullptr;
// Most value-parameterized classes can ignore the existence of
// WithParamInterface, and can just inherit from ::testing::TestWithParam.
@@ -1783,10 +1884,13 @@ template <typename T>
class TestWithParam : public Test, public WithParamInterface<T> {
};
-#endif // GTEST_HAS_PARAM_TEST
-
// Macros for indicating success/failure in test code.
+// Skips test in runtime.
+// Skipping test aborts current function.
+// Skipped tests are neither successful nor failed.
+#define GTEST_SKIP() GTEST_SKIP_("Skipped")
+
// ADD_FAILURE unconditionally adds a failure to the current test.
// SUCCEED generates a success - it doesn't automatically make the
// current test successful, as a test is only successful when it has
@@ -1816,6 +1920,11 @@ class TestWithParam : public Test, public WithParamInterface<T> {
// Generates a fatal failure with a generic message.
#define GTEST_FAIL() GTEST_FATAL_FAILURE_("Failed")
+// Like GTEST_FAIL(), but at the given source file location.
+#define GTEST_FAIL_AT(file, line) \
+ GTEST_MESSAGE_AT_(file, line, "Failed", \
+ ::testing::TestPartResult::kFatalFailure)
+
// Define this macro to 1 to omit the definition of FAIL(), which is a
// generic name and clashes with some other libraries.
#if !GTEST_DONT_DEFINE_FAIL
@@ -1857,22 +1966,18 @@ class TestWithParam : public Test, public WithParamInterface<T> {
// AssertionResult. For more information on how to use AssertionResult with
// these macros see comments on that class.
#define EXPECT_TRUE(condition) \
- GTEST_TEST_BOOLEAN_((condition), #condition, false, true, \
+ GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \
GTEST_NONFATAL_FAILURE_)
#define EXPECT_FALSE(condition) \
GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \
GTEST_NONFATAL_FAILURE_)
#define ASSERT_TRUE(condition) \
- GTEST_TEST_BOOLEAN_((condition), #condition, false, true, \
+ GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \
GTEST_FATAL_FAILURE_)
#define ASSERT_FALSE(condition) \
GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \
GTEST_FATAL_FAILURE_)
-// Includes the auto-generated header that implements a family of
-// generic predicate assertion macros.
-#include "gtest/gtest_pred_impl.h"
-
// Macros for testing equalities and inequalities.
//
// * {ASSERT|EXPECT}_EQ(v1, v2): Tests that v1 == v2
@@ -1914,15 +2019,13 @@ class TestWithParam : public Test, public WithParamInterface<T> {
//
// Examples:
//
-// EXPECT_NE(5, Foo());
-// EXPECT_EQ(NULL, a_pointer);
+// EXPECT_NE(Foo(), 5);
+// EXPECT_EQ(a_pointer, NULL);
// ASSERT_LT(i, array_size);
// ASSERT_GT(records.size(), 0) << "There is no record left.";
#define EXPECT_EQ(val1, val2) \
- EXPECT_PRED_FORMAT2(::testing::internal:: \
- EqHelper<GTEST_IS_NULL_LITERAL_(val1)>::Compare, \
- val1, val2)
+ EXPECT_PRED_FORMAT2(::testing::internal::EqHelper::Compare, val1, val2)
#define EXPECT_NE(val1, val2) \
EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperNE, val1, val2)
#define EXPECT_LE(val1, val2) \
@@ -1935,9 +2038,7 @@ class TestWithParam : public Test, public WithParamInterface<T> {
EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGT, val1, val2)
#define GTEST_ASSERT_EQ(val1, val2) \
- ASSERT_PRED_FORMAT2(::testing::internal:: \
- EqHelper<GTEST_IS_NULL_LITERAL_(val1)>::Compare, \
- val1, val2)
+ ASSERT_PRED_FORMAT2(::testing::internal::EqHelper::Compare, val1, val2)
#define GTEST_ASSERT_NE(val1, val2) \
ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperNE, val1, val2)
#define GTEST_ASSERT_LE(val1, val2) \
@@ -2101,6 +2202,51 @@ GTEST_API_ AssertionResult DoubleLE(const char* expr1, const char* expr2,
#define EXPECT_NO_FATAL_FAILURE(statement) \
GTEST_TEST_NO_FATAL_FAILURE_(statement, GTEST_NONFATAL_FAILURE_)
+// Causes a trace (including the given source file path and line number,
+// and the given message) to be included in every test failure message generated
+// by code in the scope of the lifetime of an instance of this class. The effect
+// is undone with the destruction of the instance.
+//
+// The message argument can be anything streamable to std::ostream.
+//
+// Example:
+// testing::ScopedTrace trace("file.cc", 123, "message");
+//
+class GTEST_API_ ScopedTrace {
+ public:
+ // The c'tor pushes the given source file location and message onto
+ // a trace stack maintained by Google Test.
+
+ // Template version. Uses Message() to convert the values into strings.
+ // Slow, but flexible.
+ template <typename T>
+ ScopedTrace(const char* file, int line, const T& message) {
+ PushTrace(file, line, (Message() << message).GetString());
+ }
+
+ // Optimize for some known types.
+ ScopedTrace(const char* file, int line, const char* message) {
+ PushTrace(file, line, message ? message : "(null)");
+ }
+
+ ScopedTrace(const char* file, int line, const std::string& message) {
+ PushTrace(file, line, message);
+ }
+
+ // The d'tor pops the info pushed by the c'tor.
+ //
+ // Note that the d'tor is not virtual in order to be efficient.
+ // Don't inherit from ScopedTrace!
+ ~ScopedTrace();
+
+ private:
+ void PushTrace(const char* file, int line, std::string message);
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedTrace);
+} GTEST_ATTRIBUTE_UNUSED_; // A ScopedTrace object does its job in its
+ // c'tor and d'tor. Therefore it doesn't
+ // need to be used otherwise.
+
// Causes a trace (including the source file path, the current line
// number, and the given message) to be included in every test failure
// message generated by code in the current scope. The effect is
@@ -2112,13 +2258,17 @@ GTEST_API_ AssertionResult DoubleLE(const char* expr1, const char* expr2,
// of the dummy variable name, thus allowing multiple SCOPED_TRACE()s
// to appear in the same block - as long as they are on different
// lines.
+//
+// Assuming that each thread maintains its own stack of traces.
+// Therefore, a SCOPED_TRACE() would (correctly) only affect the
+// assertions in its own thread.
#define SCOPED_TRACE(message) \
- ::testing::internal::ScopedTrace GTEST_CONCAT_TOKEN_(gtest_trace_, __LINE__)(\
- __FILE__, __LINE__, ::testing::Message() << (message))
+ ::testing::ScopedTrace GTEST_CONCAT_TOKEN_(gtest_trace_, __LINE__)(\
+ __FILE__, __LINE__, (message))
// Compile-time assertion for type equality.
-// StaticAssertTypeEq<type1, type2>() compiles iff type1 and type2 are
-// the same type. The value it returns is not interesting.
+// StaticAssertTypeEq<type1, type2>() compiles if and only if type1 and type2
+// are the same type. The value it returns is not interesting.
//
// Instead of making StaticAssertTypeEq a class template, we make it a
// function template that invokes a helper class template. This
@@ -2147,18 +2297,19 @@ GTEST_API_ AssertionResult DoubleLE(const char* expr1, const char* expr2,
//
// to cause a compiler error.
template <typename T1, typename T2>
-bool StaticAssertTypeEq() {
- (void)internal::StaticAssertTypeEqHelper<T1, T2>();
+constexpr bool StaticAssertTypeEq() noexcept {
+ static_assert(std::is_same<T1, T2>::value,
+ "type1 and type2 are not the same type");
return true;
}
// Defines a test.
//
-// The first parameter is the name of the test case, and the second
-// parameter is the name of the test within the test case.
+// The first parameter is the name of the test suite, and the second
+// parameter is the name of the test within the test suite.
//
-// The convention is to end the test case name with "Test". For
-// example, a test case for the Foo class can be named FooTest.
+// The convention is to end the test suite name with "Test". For
+// example, a test suite for the Foo class can be named FooTest.
//
// Test code should appear between braces after an invocation of
// this macro. Example:
@@ -2177,28 +2328,28 @@ bool StaticAssertTypeEq() {
// code. GetTestTypeId() is guaranteed to always return the same
// value, as it always calls GetTypeId<>() from the Google Test
// framework.
-#define GTEST_TEST(test_case_name, test_name)\
- GTEST_TEST_(test_case_name, test_name, \
- ::testing::Test, ::testing::internal::GetTestTypeId())
+#define GTEST_TEST(test_suite_name, test_name) \
+ GTEST_TEST_(test_suite_name, test_name, ::testing::Test, \
+ ::testing::internal::GetTestTypeId())
// Define this macro to 1 to omit the definition of TEST(), which
// is a generic name and clashes with some other libraries.
#if !GTEST_DONT_DEFINE_TEST
-# define TEST(test_case_name, test_name) GTEST_TEST(test_case_name, test_name)
+#define TEST(test_suite_name, test_name) GTEST_TEST(test_suite_name, test_name)
#endif
// Defines a test that uses a test fixture.
//
// The first parameter is the name of the test fixture class, which
-// also doubles as the test case name. The second parameter is the
-// name of the test within the test case.
+// also doubles as the test suite name. The second parameter is the
+// name of the test within the test suite.
//
// A test fixture class must be declared earlier. The user should put
-// his test code between braces after using this macro. Example:
+// the test code between braces after using this macro. Example:
//
// class FooTest : public testing::Test {
// protected:
-// virtual void SetUp() { b_.AddElement(3); }
+// void SetUp() override { b_.AddElement(3); }
//
// Foo a_;
// Foo b_;
@@ -2209,14 +2360,103 @@ bool StaticAssertTypeEq() {
// }
//
// TEST_F(FooTest, ReturnsElementCountCorrectly) {
-// EXPECT_EQ(0, a_.size());
-// EXPECT_EQ(1, b_.size());
+// EXPECT_EQ(a_.size(), 0);
+// EXPECT_EQ(b_.size(), 1);
// }
-
+//
+// GOOGLETEST_CM0011 DO NOT DELETE
#define TEST_F(test_fixture, test_name)\
GTEST_TEST_(test_fixture, test_name, test_fixture, \
::testing::internal::GetTypeId<test_fixture>())
+// Returns a path to temporary directory.
+// Tries to determine an appropriate directory for the platform.
+GTEST_API_ std::string TempDir();
+
+#ifdef _MSC_VER
+# pragma warning(pop)
+#endif
+
+// Dynamically registers a test with the framework.
+//
+// This is an advanced API only to be used when the `TEST` macros are
+// insufficient. The macros should be preferred when possible, as they avoid
+// most of the complexity of calling this function.
+//
+// The `factory` argument is a factory callable (move-constructible) object or
+// function pointer that creates a new instance of the Test object. It
+// handles ownership to the caller. The signature of the callable is
+// `Fixture*()`, where `Fixture` is the test fixture class for the test. All
+// tests registered with the same `test_suite_name` must return the same
+// fixture type. This is checked at runtime.
+//
+// The framework will infer the fixture class from the factory and will call
+// the `SetUpTestSuite` and `TearDownTestSuite` for it.
+//
+// Must be called before `RUN_ALL_TESTS()` is invoked, otherwise behavior is
+// undefined.
+//
+// Use case example:
+//
+// class MyFixture : public ::testing::Test {
+// public:
+// // All of these optional, just like in regular macro usage.
+// static void SetUpTestSuite() { ... }
+// static void TearDownTestSuite() { ... }
+// void SetUp() override { ... }
+// void TearDown() override { ... }
+// };
+//
+// class MyTest : public MyFixture {
+// public:
+// explicit MyTest(int data) : data_(data) {}
+// void TestBody() override { ... }
+//
+// private:
+// int data_;
+// };
+//
+// void RegisterMyTests(const std::vector<int>& values) {
+// for (int v : values) {
+// ::testing::RegisterTest(
+// "MyFixture", ("Test" + std::to_string(v)).c_str(), nullptr,
+// std::to_string(v).c_str(),
+// __FILE__, __LINE__,
+// // Important to use the fixture type as the return type here.
+// [=]() -> MyFixture* { return new MyTest(v); });
+// }
+// }
+// ...
+// int main(int argc, char** argv) {
+// std::vector<int> values_to_test = LoadValuesFromConfig();
+// RegisterMyTests(values_to_test);
+// ...
+// return RUN_ALL_TESTS();
+// }
+//
+template <int&... ExplicitParameterBarrier, typename Factory>
+TestInfo* RegisterTest(const char* test_suite_name, const char* test_name,
+ const char* type_param, const char* value_param,
+ const char* file, int line, Factory factory) {
+ using TestT = typename std::remove_pointer<decltype(factory())>::type;
+
+ class FactoryImpl : public internal::TestFactoryBase {
+ public:
+ explicit FactoryImpl(Factory f) : factory_(std::move(f)) {}
+ Test* CreateTest() override { return factory_(); }
+
+ private:
+ Factory factory_;
+ };
+
+ return internal::MakeAndRegisterTestInfo(
+ test_suite_name, test_name, type_param, value_param,
+ internal::CodeLocation(file, line), internal::GetTypeId<TestT>(),
+ internal::SuiteApiResolver<TestT>::GetSetUpCaseOrSuite(file, line),
+ internal::SuiteApiResolver<TestT>::GetTearDownCaseOrSuite(file, line),
+ new FactoryImpl{std::move(factory)});
+}
+
} // namespace testing
// Use this function in main() to run all tests. It returns 0 if all
@@ -2233,4 +2473,6 @@ inline int RUN_ALL_TESTS() {
return ::testing::UnitTest::GetInstance()->Run();
}
+GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251
+
#endif // GTEST_INCLUDE_GTEST_GTEST_H_
diff --git a/extern/gtest/include/gtest/gtest_pred_impl.h b/extern/gtest/include/gtest/gtest_pred_impl.h
index 30ae712f50e..d514255c733 100644
--- a/extern/gtest/include/gtest/gtest_pred_impl.h
+++ b/extern/gtest/include/gtest/gtest_pred_impl.h
@@ -27,18 +27,18 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-// This file is AUTOMATICALLY GENERATED on 10/31/2011 by command
+// This file is AUTOMATICALLY GENERATED on 01/02/2019 by command
// 'gen_gtest_pred_impl.py 5'. DO NOT EDIT BY HAND!
//
// Implements a family of generic predicate assertion macros.
+// GOOGLETEST_CM0001 DO NOT DELETE
#ifndef GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_
#define GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_
-// Makes sure this header is not included before gtest.h.
-#ifndef GTEST_INCLUDE_GTEST_GTEST_H_
-# error Do not include gtest_pred_impl.h directly. Include gtest.h instead.
-#endif // GTEST_INCLUDE_GTEST_GTEST_H_
+#include "gtest/gtest.h"
+
+namespace testing {
// This header implements a family of generic predicate assertion
// macros:
@@ -90,9 +90,10 @@ AssertionResult AssertPred1Helper(const char* pred_text,
const T1& v1) {
if (pred(v1)) return AssertionSuccess();
- return AssertionFailure() << pred_text << "("
- << e1 << ") evaluates to false, where"
- << "\n" << e1 << " evaluates to " << v1;
+ return AssertionFailure()
+ << pred_text << "(" << e1 << ") evaluates to false, where"
+ << "\n"
+ << e1 << " evaluates to " << ::testing::PrintToString(v1);
}
// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT1.
@@ -134,11 +135,12 @@ AssertionResult AssertPred2Helper(const char* pred_text,
const T2& v2) {
if (pred(v1, v2)) return AssertionSuccess();
- return AssertionFailure() << pred_text << "("
- << e1 << ", "
- << e2 << ") evaluates to false, where"
- << "\n" << e1 << " evaluates to " << v1
- << "\n" << e2 << " evaluates to " << v2;
+ return AssertionFailure()
+ << pred_text << "(" << e1 << ", " << e2
+ << ") evaluates to false, where"
+ << "\n"
+ << e1 << " evaluates to " << ::testing::PrintToString(v1) << "\n"
+ << e2 << " evaluates to " << ::testing::PrintToString(v2);
}
// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT2.
@@ -185,13 +187,13 @@ AssertionResult AssertPred3Helper(const char* pred_text,
const T3& v3) {
if (pred(v1, v2, v3)) return AssertionSuccess();
- return AssertionFailure() << pred_text << "("
- << e1 << ", "
- << e2 << ", "
- << e3 << ") evaluates to false, where"
- << "\n" << e1 << " evaluates to " << v1
- << "\n" << e2 << " evaluates to " << v2
- << "\n" << e3 << " evaluates to " << v3;
+ return AssertionFailure()
+ << pred_text << "(" << e1 << ", " << e2 << ", " << e3
+ << ") evaluates to false, where"
+ << "\n"
+ << e1 << " evaluates to " << ::testing::PrintToString(v1) << "\n"
+ << e2 << " evaluates to " << ::testing::PrintToString(v2) << "\n"
+ << e3 << " evaluates to " << ::testing::PrintToString(v3);
}
// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT3.
@@ -243,15 +245,14 @@ AssertionResult AssertPred4Helper(const char* pred_text,
const T4& v4) {
if (pred(v1, v2, v3, v4)) return AssertionSuccess();
- return AssertionFailure() << pred_text << "("
- << e1 << ", "
- << e2 << ", "
- << e3 << ", "
- << e4 << ") evaluates to false, where"
- << "\n" << e1 << " evaluates to " << v1
- << "\n" << e2 << " evaluates to " << v2
- << "\n" << e3 << " evaluates to " << v3
- << "\n" << e4 << " evaluates to " << v4;
+ return AssertionFailure()
+ << pred_text << "(" << e1 << ", " << e2 << ", " << e3 << ", " << e4
+ << ") evaluates to false, where"
+ << "\n"
+ << e1 << " evaluates to " << ::testing::PrintToString(v1) << "\n"
+ << e2 << " evaluates to " << ::testing::PrintToString(v2) << "\n"
+ << e3 << " evaluates to " << ::testing::PrintToString(v3) << "\n"
+ << e4 << " evaluates to " << ::testing::PrintToString(v4);
}
// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT4.
@@ -308,17 +309,15 @@ AssertionResult AssertPred5Helper(const char* pred_text,
const T5& v5) {
if (pred(v1, v2, v3, v4, v5)) return AssertionSuccess();
- return AssertionFailure() << pred_text << "("
- << e1 << ", "
- << e2 << ", "
- << e3 << ", "
- << e4 << ", "
- << e5 << ") evaluates to false, where"
- << "\n" << e1 << " evaluates to " << v1
- << "\n" << e2 << " evaluates to " << v2
- << "\n" << e3 << " evaluates to " << v3
- << "\n" << e4 << " evaluates to " << v4
- << "\n" << e5 << " evaluates to " << v5;
+ return AssertionFailure()
+ << pred_text << "(" << e1 << ", " << e2 << ", " << e3 << ", " << e4
+ << ", " << e5 << ") evaluates to false, where"
+ << "\n"
+ << e1 << " evaluates to " << ::testing::PrintToString(v1) << "\n"
+ << e2 << " evaluates to " << ::testing::PrintToString(v2) << "\n"
+ << e3 << " evaluates to " << ::testing::PrintToString(v3) << "\n"
+ << e4 << " evaluates to " << ::testing::PrintToString(v4) << "\n"
+ << e5 << " evaluates to " << ::testing::PrintToString(v5);
}
// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT5.
@@ -355,4 +354,6 @@ AssertionResult AssertPred5Helper(const char* pred_text,
+} // namespace testing
+
#endif // GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_
diff --git a/extern/gtest/include/gtest/gtest_prod.h b/extern/gtest/include/gtest/gtest_prod.h
index da80ddc6c70..e651671ebde 100644
--- a/extern/gtest/include/gtest/gtest_prod.h
+++ b/extern/gtest/include/gtest/gtest_prod.h
@@ -26,10 +26,10 @@
// 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: wan@google.com (Zhanyong Wan)
-//
-// Google C++ Testing Framework definitions useful in production code.
+// Google C++ Testing and Mocking Framework definitions useful in production code.
+// GOOGLETEST_CM0003 DO NOT DELETE
#ifndef GTEST_INCLUDE_GTEST_GTEST_PROD_H_
#define GTEST_INCLUDE_GTEST_GTEST_PROD_H_
@@ -40,17 +40,20 @@
//
// class MyClass {
// private:
-// void MyMethod();
-// FRIEND_TEST(MyClassTest, MyMethod);
+// void PrivateMethod();
+// FRIEND_TEST(MyClassTest, PrivateMethodWorks);
// };
//
// class MyClassTest : public testing::Test {
// // ...
// };
//
-// TEST_F(MyClassTest, MyMethod) {
-// // Can call MyClass::MyMethod() here.
+// TEST_F(MyClassTest, PrivateMethodWorks) {
+// // Can call MyClass::PrivateMethod() here.
// }
+//
+// Note: The test class must be in the same namespace as the class being tested.
+// For example, putting MyClassTest in an anonymous namespace will not work.
#define FRIEND_TEST(test_case_name, test_name)\
friend class test_case_name##_##test_name##_Test
diff --git a/extern/gtest/include/gtest/internal/custom/README.md b/extern/gtest/include/gtest/internal/custom/README.md
new file mode 100644
index 00000000000..ff391fb4e2b
--- /dev/null
+++ b/extern/gtest/include/gtest/internal/custom/README.md
@@ -0,0 +1,56 @@
+# Customization Points
+
+The custom directory is an injection point for custom user configurations.
+
+## Header `gtest.h`
+
+### The following macros can be defined:
+
+* `GTEST_OS_STACK_TRACE_GETTER_` - The name of an implementation of
+ `OsStackTraceGetterInterface`.
+* `GTEST_CUSTOM_TEMPDIR_FUNCTION_` - An override for `testing::TempDir()`. See
+ `testing::TempDir` for semantics and signature.
+
+## Header `gtest-port.h`
+
+The following macros can be defined:
+
+### Flag related macros:
+
+* `GTEST_FLAG(flag_name)`
+* `GTEST_USE_OWN_FLAGFILE_FLAG_` - Define to 0 when the system provides its
+ own flagfile flag parsing.
+* `GTEST_DECLARE_bool_(name)`
+* `GTEST_DECLARE_int32_(name)`
+* `GTEST_DECLARE_string_(name)`
+* `GTEST_DEFINE_bool_(name, default_val, doc)`
+* `GTEST_DEFINE_int32_(name, default_val, doc)`
+* `GTEST_DEFINE_string_(name, default_val, doc)`
+
+### Logging:
+
+* `GTEST_LOG_(severity)`
+* `GTEST_CHECK_(condition)`
+* Functions `LogToStderr()` and `FlushInfoLog()` have to be provided too.
+
+### Threading:
+
+* `GTEST_HAS_NOTIFICATION_` - Enabled if Notification is already provided.
+* `GTEST_HAS_MUTEX_AND_THREAD_LOCAL_` - Enabled if `Mutex` and `ThreadLocal`
+ are already provided. Must also provide `GTEST_DECLARE_STATIC_MUTEX_(mutex)`
+ and `GTEST_DEFINE_STATIC_MUTEX_(mutex)`
+* `GTEST_EXCLUSIVE_LOCK_REQUIRED_(locks)`
+* `GTEST_LOCK_EXCLUDED_(locks)`
+
+### Underlying library support features
+
+* `GTEST_HAS_CXXABI_H_`
+
+### Exporting API symbols:
+
+* `GTEST_API_` - Specifier for exported symbols.
+
+## Header `gtest-printers.h`
+
+* See documentation at `gtest/gtest-printers.h` for details on how to define a
+ custom printer.
diff --git a/extern/gtest/include/gtest/internal/custom/gtest-port.h b/extern/gtest/include/gtest/internal/custom/gtest-port.h
index 7e744bd3bb3..cd85d956d2d 100644
--- a/extern/gtest/include/gtest/internal/custom/gtest-port.h
+++ b/extern/gtest/include/gtest/internal/custom/gtest-port.h
@@ -27,39 +27,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
-// Injection point for custom user configurations.
-// The following macros can be defined:
-//
-// Flag related macros:
-// GTEST_FLAG(flag_name)
-// GTEST_USE_OWN_FLAGFILE_FLAG_ - Define to 0 when the system provides its
-// own flagfile flag parsing.
-// GTEST_DECLARE_bool_(name)
-// GTEST_DECLARE_int32_(name)
-// GTEST_DECLARE_string_(name)
-// GTEST_DEFINE_bool_(name, default_val, doc)
-// GTEST_DEFINE_int32_(name, default_val, doc)
-// GTEST_DEFINE_string_(name, default_val, doc)
-//
-// Test filtering:
-// GTEST_TEST_FILTER_ENV_VAR_ - The name of an environment variable that
-// will be used if --GTEST_FLAG(test_filter)
-// is not provided.
-//
-// Logging:
-// GTEST_LOG_(severity)
-// GTEST_CHECK_(condition)
-// Functions LogToStderr() and FlushInfoLog() have to be provided too.
-//
-// Threading:
-// GTEST_HAS_NOTIFICATION_ - Enabled if Notification is already provided.
-// GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ - Enabled if Mutex and ThreadLocal are
-// already provided.
-// Must also provide GTEST_DECLARE_STATIC_MUTEX_(mutex) and
-// GTEST_DEFINE_STATIC_MUTEX_(mutex)
-//
-// GTEST_EXCLUSIVE_LOCK_REQUIRED_(locks)
-// GTEST_LOCK_EXCLUDED_(locks)
+// Injection point for custom user configurations. See README for details
//
// ** Custom implementation starts here **
diff --git a/extern/gtest/include/gtest/internal/custom/gtest-printers.h b/extern/gtest/include/gtest/internal/custom/gtest-printers.h
index 60c1ea050b6..eb4467abcab 100644
--- a/extern/gtest/include/gtest/internal/custom/gtest-printers.h
+++ b/extern/gtest/include/gtest/internal/custom/gtest-printers.h
@@ -31,8 +31,8 @@
// installation of gTest.
// It will be included from gtest-printers.h and the overrides in this file
// will be visible to everyone.
-// See documentation at gtest/gtest-printers.h for details on how to define a
-// custom printer.
+//
+// Injection point for custom user configurations. See README for details
//
// ** Custom implementation starts here **
diff --git a/extern/gtest/include/gtest/internal/custom/gtest.h b/extern/gtest/include/gtest/internal/custom/gtest.h
index c27412a8981..4c8e07be23f 100644
--- a/extern/gtest/include/gtest/internal/custom/gtest.h
+++ b/extern/gtest/include/gtest/internal/custom/gtest.h
@@ -27,11 +27,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
-// Injection point for custom user configurations.
-// The following macros can be defined:
-//
-// GTEST_OS_STACK_TRACE_GETTER_ - The name of an implementation of
-// OsStackTraceGetterInterface.
+// Injection point for custom user configurations. See README for details
//
// ** Custom implementation starts here **
diff --git a/extern/gtest/include/gtest/internal/gtest-death-test-internal.h b/extern/gtest/include/gtest/internal/gtest-death-test-internal.h
index 2b3a78f5bf8..68bd3530618 100644
--- a/extern/gtest/include/gtest/internal/gtest-death-test-internal.h
+++ b/extern/gtest/include/gtest/internal/gtest-death-test-internal.h
@@ -27,19 +27,20 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
-// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee)
-//
-// The Google C++ Testing Framework (Google Test)
+// The Google C++ Testing and Mocking Framework (Google Test)
//
// This header file defines internal utilities needed for implementing
// death tests. They are subject to change without notice.
+// GOOGLETEST_CM0001 DO NOT DELETE
#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_
#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_
+#include "gtest/gtest-matchers.h"
#include "gtest/internal/gtest-internal.h"
#include <stdio.h>
+#include <memory>
namespace testing {
namespace internal {
@@ -53,6 +54,9 @@ const char kInternalRunDeathTestFlag[] = "internal_run_death_test";
#if GTEST_HAS_DEATH_TEST
+GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
+/* class A needs to have dll-interface to be used by clients of class B */)
+
// DeathTest is a class that hides much of the complexity of the
// GTEST_DEATH_TEST_ macro. It is abstract; its static Create method
// returns a concrete class that depends on the prevailing death test
@@ -76,7 +80,7 @@ class GTEST_API_ DeathTest {
// argument is set. If the death test should be skipped, the pointer
// is set to NULL; otherwise, it is set to the address of a new concrete
// DeathTest object that controls the execution of the current test.
- static bool Create(const char* statement, const RE* regex,
+ static bool Create(const char* statement, Matcher<const std::string&> matcher,
const char* file, int line, DeathTest** test);
DeathTest();
virtual ~DeathTest() { }
@@ -136,25 +140,50 @@ class GTEST_API_ DeathTest {
GTEST_DISALLOW_COPY_AND_ASSIGN_(DeathTest);
};
+GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251
+
// Factory interface for death tests. May be mocked out for testing.
class DeathTestFactory {
public:
virtual ~DeathTestFactory() { }
- virtual bool Create(const char* statement, const RE* regex,
- const char* file, int line, DeathTest** test) = 0;
+ virtual bool Create(const char* statement,
+ Matcher<const std::string&> matcher, const char* file,
+ int line, DeathTest** test) = 0;
};
// A concrete DeathTestFactory implementation for normal use.
class DefaultDeathTestFactory : public DeathTestFactory {
public:
- virtual bool Create(const char* statement, const RE* regex,
- const char* file, int line, DeathTest** test);
+ bool Create(const char* statement, Matcher<const std::string&> matcher,
+ const char* file, int line, DeathTest** test) override;
};
// Returns true if exit_status describes a process that was terminated
// by a signal, or exited normally with a nonzero exit code.
GTEST_API_ bool ExitedUnsuccessfully(int exit_status);
+// A string passed to EXPECT_DEATH (etc.) is caught by one of these overloads
+// and interpreted as a regex (rather than an Eq matcher) for legacy
+// compatibility.
+inline Matcher<const ::std::string&> MakeDeathTestMatcher(
+ ::testing::internal::RE regex) {
+ return ContainsRegex(regex.pattern());
+}
+inline Matcher<const ::std::string&> MakeDeathTestMatcher(const char* regex) {
+ return ContainsRegex(regex);
+}
+inline Matcher<const ::std::string&> MakeDeathTestMatcher(
+ const ::std::string& regex) {
+ return ContainsRegex(regex);
+}
+
+// If a Matcher<const ::std::string&> is passed to EXPECT_DEATH (etc.), it's
+// used directly.
+inline Matcher<const ::std::string&> MakeDeathTestMatcher(
+ Matcher<const ::std::string&> matcher) {
+ return matcher;
+}
+
// Traps C++ exceptions escaping statement and reports them as test
// failures. Note that trapping SEH exceptions is not implemented here.
# if GTEST_HAS_EXCEPTIONS
@@ -182,50 +211,53 @@ GTEST_API_ bool ExitedUnsuccessfully(int exit_status);
// This macro is for implementing ASSERT_DEATH*, EXPECT_DEATH*,
// ASSERT_EXIT*, and EXPECT_EXIT*.
-# define GTEST_DEATH_TEST_(statement, predicate, regex, fail) \
- GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
- if (::testing::internal::AlwaysTrue()) { \
- const ::testing::internal::RE& gtest_regex = (regex); \
- ::testing::internal::DeathTest* gtest_dt; \
- if (!::testing::internal::DeathTest::Create(#statement, &gtest_regex, \
- __FILE__, __LINE__, &gtest_dt)) { \
- goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \
- } \
- if (gtest_dt != NULL) { \
- ::testing::internal::scoped_ptr< ::testing::internal::DeathTest> \
- gtest_dt_ptr(gtest_dt); \
- switch (gtest_dt->AssumeRole()) { \
- case ::testing::internal::DeathTest::OVERSEE_TEST: \
- if (!gtest_dt->Passed(predicate(gtest_dt->Wait()))) { \
- goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \
- } \
- break; \
- case ::testing::internal::DeathTest::EXECUTE_TEST: { \
- ::testing::internal::DeathTest::ReturnSentinel \
- gtest_sentinel(gtest_dt); \
- GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, gtest_dt); \
- gtest_dt->Abort(::testing::internal::DeathTest::TEST_DID_NOT_DIE); \
- break; \
- } \
- default: \
- break; \
- } \
- } \
- } else \
- GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__): \
- fail(::testing::internal::DeathTest::LastMessage())
+#define GTEST_DEATH_TEST_(statement, predicate, regex_or_matcher, fail) \
+ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+ if (::testing::internal::AlwaysTrue()) { \
+ ::testing::internal::DeathTest* gtest_dt; \
+ if (!::testing::internal::DeathTest::Create( \
+ #statement, \
+ ::testing::internal::MakeDeathTestMatcher(regex_or_matcher), \
+ __FILE__, __LINE__, &gtest_dt)) { \
+ goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \
+ } \
+ if (gtest_dt != nullptr) { \
+ std::unique_ptr< ::testing::internal::DeathTest> gtest_dt_ptr(gtest_dt); \
+ switch (gtest_dt->AssumeRole()) { \
+ case ::testing::internal::DeathTest::OVERSEE_TEST: \
+ if (!gtest_dt->Passed(predicate(gtest_dt->Wait()))) { \
+ goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \
+ } \
+ break; \
+ case ::testing::internal::DeathTest::EXECUTE_TEST: { \
+ ::testing::internal::DeathTest::ReturnSentinel gtest_sentinel( \
+ gtest_dt); \
+ GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, gtest_dt); \
+ gtest_dt->Abort(::testing::internal::DeathTest::TEST_DID_NOT_DIE); \
+ break; \
+ } \
+ default: \
+ break; \
+ } \
+ } \
+ } else \
+ GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__) \
+ : fail(::testing::internal::DeathTest::LastMessage())
// The symbol "fail" here expands to something into which a message
// can be streamed.
// This macro is for implementing ASSERT/EXPECT_DEBUG_DEATH when compiled in
-// NDEBUG mode. In this case we need the statements to be executed, the regex is
-// ignored, and the macro must accept a streamed message even though the message
-// is never printed.
-# define GTEST_EXECUTE_STATEMENT_(statement, regex) \
- GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
- if (::testing::internal::AlwaysTrue()) { \
- GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
- } else \
+// NDEBUG mode. In this case we need the statements to be executed and the macro
+// must accept a streamed message even though the message is never printed.
+// The regex object is not evaluated, but it is used to prevent "unused"
+// warnings and to avoid an expression that doesn't compile in debug mode.
+#define GTEST_EXECUTE_STATEMENT_(statement, regex_or_matcher) \
+ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+ if (::testing::internal::AlwaysTrue()) { \
+ GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
+ } else if (!::testing::internal::AlwaysTrue()) { \
+ ::testing::internal::MakeDeathTestMatcher(regex_or_matcher); \
+ } else \
::testing::Message()
// A class representing the parsed contents of the
@@ -264,53 +296,6 @@ class InternalRunDeathTestFlag {
// the flag is specified; otherwise returns NULL.
InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag();
-#else // GTEST_HAS_DEATH_TEST
-
-// This macro is used for implementing macros such as
-// EXPECT_DEATH_IF_SUPPORTED and ASSERT_DEATH_IF_SUPPORTED on systems where
-// death tests are not supported. Those macros must compile on such systems
-// iff EXPECT_DEATH and ASSERT_DEATH compile with the same parameters on
-// systems that support death tests. This allows one to write such a macro
-// on a system that does not support death tests and be sure that it will
-// compile on a death-test supporting system.
-//
-// Parameters:
-// statement - A statement that a macro such as EXPECT_DEATH would test
-// for program termination. This macro has to make sure this
-// statement is compiled but not executed, to ensure that
-// EXPECT_DEATH_IF_SUPPORTED compiles with a certain
-// parameter iff EXPECT_DEATH compiles with it.
-// regex - A regex that a macro such as EXPECT_DEATH would use to test
-// the output of statement. This parameter has to be
-// compiled but not evaluated by this macro, to ensure that
-// this macro only accepts expressions that a macro such as
-// EXPECT_DEATH would accept.
-// terminator - Must be an empty statement for EXPECT_DEATH_IF_SUPPORTED
-// and a return statement for ASSERT_DEATH_IF_SUPPORTED.
-// This ensures that ASSERT_DEATH_IF_SUPPORTED will not
-// compile inside functions where ASSERT_DEATH doesn't
-// compile.
-//
-// The branch that has an always false condition is used to ensure that
-// statement and regex are compiled (and thus syntactically correct) but
-// never executed. The unreachable code macro protects the terminator
-// statement from generating an 'unreachable code' warning in case
-// statement unconditionally returns or throws. The Message constructor at
-// the end allows the syntax of streaming additional messages into the
-// macro, for compilational compatibility with EXPECT_DEATH/ASSERT_DEATH.
-# define GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, terminator) \
- GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
- if (::testing::internal::AlwaysTrue()) { \
- GTEST_LOG_(WARNING) \
- << "Death tests are not supported on this platform.\n" \
- << "Statement '" #statement "' cannot be verified."; \
- } else if (::testing::internal::AlwaysFalse()) { \
- ::testing::internal::RE::PartialMatch(".*", (regex)); \
- GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
- terminator; \
- } else \
- ::testing::Message()
-
#endif // GTEST_HAS_DEATH_TEST
} // namespace internal
diff --git a/extern/gtest/include/gtest/internal/gtest-filepath.h b/extern/gtest/include/gtest/internal/gtest-filepath.h
index 7a13b4b0de6..c11b101516e 100644
--- a/extern/gtest/include/gtest/internal/gtest-filepath.h
+++ b/extern/gtest/include/gtest/internal/gtest-filepath.h
@@ -27,21 +27,24 @@
// (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: keith.ray@gmail.com (Keith Ray)
-//
// Google Test filepath utilities
//
// This header file declares classes and functions used internally by
// Google Test. They are subject to change without notice.
//
-// This file is #included in <gtest/internal/gtest-internal.h>.
+// This file is #included in gtest/internal/gtest-internal.h.
// Do not include this header file separately!
+// GOOGLETEST_CM0001 DO NOT DELETE
+
#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_
#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_
#include "gtest/internal/gtest-string.h"
+GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
+/* class A needs to have dll-interface to be used by clients of class B */)
+
namespace testing {
namespace internal {
@@ -107,7 +110,7 @@ class GTEST_API_ FilePath {
const FilePath& base_name,
const char* extension);
- // Returns true iff the path is "".
+ // Returns true if and only if the path is "".
bool IsEmpty() const { return pathname_.empty(); }
// If input name has a trailing separator character, removes it and returns
@@ -203,4 +206,6 @@ class GTEST_API_ FilePath {
} // namespace internal
} // namespace testing
+GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251
+
#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_
diff --git a/extern/gtest/include/gtest/internal/gtest-internal.h b/extern/gtest/include/gtest/internal/gtest-internal.h
index ebd1cf615de..94c816a28bd 100644
--- a/extern/gtest/include/gtest/internal/gtest-internal.h
+++ b/extern/gtest/include/gtest/internal/gtest-internal.h
@@ -27,13 +27,13 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
-// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee)
-//
-// The Google C++ Testing Framework (Google Test)
+// The Google C++ Testing and Mocking Framework (Google Test)
//
// This header file declares functions and macros used internally by
// Google Test. They are subject to change without notice.
+// GOOGLETEST_CM0001 DO NOT DELETE
+
#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_
#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_
@@ -58,11 +58,12 @@
#include <map>
#include <set>
#include <string>
+#include <type_traits>
#include <vector>
#include "gtest/gtest-message.h"
-#include "gtest/internal/gtest-string.h"
#include "gtest/internal/gtest-filepath.h"
+#include "gtest/internal/gtest-string.h"
#include "gtest/internal/gtest-type-util.h"
// Due to C++ preprocessor weirdness, we need double indirection to
@@ -76,7 +77,9 @@
#define GTEST_CONCAT_TOKEN_(foo, bar) GTEST_CONCAT_TOKEN_IMPL_(foo, bar)
#define GTEST_CONCAT_TOKEN_IMPL_(foo, bar) foo ## bar
-class ProtocolMessage;
+// Stringifies its argument.
+#define GTEST_STRINGIFY_(name) #name
+
namespace proto2 { class Message; }
namespace testing {
@@ -88,7 +91,7 @@ class Message; // Represents a failure message.
class Test; // Represents a test.
class TestInfo; // Information about a test.
class TestPartResult; // Result of a test part.
-class UnitTest; // A collection of test cases.
+class UnitTest; // A collection of test suites.
template <typename T>
::std::string PrintToString(const T& value);
@@ -96,7 +99,6 @@ template <typename T>
namespace internal {
struct TraceInfo; // Information about a trace point.
-class ScopedTrace; // Implements scoped trace.
class TestInfoImpl; // Opaque implementation of TestInfo
class UnitTestImpl; // Opaque implementation of UnitTest
@@ -104,34 +106,22 @@ class UnitTestImpl; // Opaque implementation of UnitTest
// stack trace.
GTEST_API_ extern const char kStackTraceMarker[];
-// Two overloaded helpers for checking at compile time whether an
-// expression is a null pointer literal (i.e. NULL or any 0-valued
-// compile-time integral constant). Their return values have
-// different sizes, so we can use sizeof() to test which version is
-// picked by the compiler. These helpers have no implementations, as
-// we only need their signatures.
-//
-// Given IsNullLiteralHelper(x), the compiler will pick the first
-// version if x can be implicitly converted to Secret*, and pick the
-// second version otherwise. Since Secret is a secret and incomplete
-// type, the only expression a user can write that has type Secret* is
-// a null pointer literal. Therefore, we know that x is a null
-// pointer literal if and only if the first version is picked by the
-// compiler.
-char IsNullLiteralHelper(Secret* p);
-char (&IsNullLiteralHelper(...))[2]; // NOLINT
-
-// A compile-time bool constant that is true if and only if x is a
-// null pointer literal (i.e. NULL or any 0-valued compile-time
-// integral constant).
-#ifdef GTEST_ELLIPSIS_NEEDS_POD_
-// We lose support for NULL detection where the compiler doesn't like
-// passing non-POD classes through ellipsis (...).
-# define GTEST_IS_NULL_LITERAL_(x) false
-#else
-# define GTEST_IS_NULL_LITERAL_(x) \
- (sizeof(::testing::internal::IsNullLiteralHelper(x)) == 1)
-#endif // GTEST_ELLIPSIS_NEEDS_POD_
+// An IgnoredValue object can be implicitly constructed from ANY value.
+class IgnoredValue {
+ struct Sink {};
+ public:
+ // This constructor template allows any value to be implicitly
+ // converted to IgnoredValue. The object has no data member and
+ // doesn't try to remember anything about the argument. We
+ // deliberately omit the 'explicit' keyword in order to allow the
+ // conversion to be implicit.
+ // Disable the conversion if T already has a magical conversion operator.
+ // Otherwise we get ambiguity.
+ template <typename T,
+ typename std::enable_if<!std::is_convertible<T, Sink>::value,
+ int>::type = 0>
+ IgnoredValue(const T& /* ignored */) {} // NOLINT(runtime/explicit)
+};
// Appends the user-supplied message to the Google-Test-generated message.
GTEST_API_ std::string AppendUserMessage(
@@ -139,6 +129,9 @@ GTEST_API_ std::string AppendUserMessage(
#if GTEST_HAS_EXCEPTIONS
+GTEST_DISABLE_MSC_WARNINGS_PUSH_(4275 \
+/* an exported class was derived from a class that was not exported */)
+
// This exception is thrown by (and only by) a failed Google Test
// assertion when GTEST_FLAG(throw_on_failure) is true (if exceptions
// are enabled). We derive it from std::runtime_error, which is for
@@ -150,32 +143,15 @@ class GTEST_API_ GoogleTestFailureException : public ::std::runtime_error {
explicit GoogleTestFailureException(const TestPartResult& failure);
};
-#endif // GTEST_HAS_EXCEPTIONS
-
-// A helper class for creating scoped traces in user programs.
-class GTEST_API_ ScopedTrace {
- public:
- // The c'tor pushes the given source file location and message onto
- // a trace stack maintained by Google Test.
- ScopedTrace(const char* file, int line, const Message& message);
-
- // The d'tor pops the info pushed by the c'tor.
- //
- // Note that the d'tor is not virtual in order to be efficient.
- // Don't inherit from ScopedTrace!
- ~ScopedTrace();
+GTEST_DISABLE_MSC_WARNINGS_POP_() // 4275
- private:
- GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedTrace);
-} GTEST_ATTRIBUTE_UNUSED_; // A ScopedTrace object does its job in its
- // c'tor and d'tor. Therefore it doesn't
- // need to be used otherwise.
+#endif // GTEST_HAS_EXCEPTIONS
namespace edit_distance {
// Returns the optimal edits to go from 'left' to 'right'.
// All edits cost the same, with replace having lower priority than
// add/remove.
-// Simple implementation of the Wagner–Fischer algorithm.
+// Simple implementation of the Wagner-Fischer algorithm.
// See http://en.wikipedia.org/wiki/Wagner-Fischer_algorithm
enum EditType { kMatch, kAdd, kRemove, kReplace };
GTEST_API_ std::vector<EditType> CalculateOptimalEdits(
@@ -213,7 +189,7 @@ GTEST_API_ std::string DiffStrings(const std::string& left,
// expected_value: "5"
// actual_value: "6"
//
-// The ignoring_case parameter is true iff the assertion is a
+// The ignoring_case parameter is true if and only if the assertion is a
// *_STRCASEEQ*. When it's true, the string " (ignoring case)" will
// be inserted into the message.
GTEST_API_ AssertionResult EqFailure(const char* expected_expression,
@@ -342,15 +318,15 @@ class FloatingPoint {
// Returns the sign bit of this number.
Bits sign_bit() const { return kSignBitMask & u_.bits_; }
- // Returns true iff this is NAN (not a number).
+ // Returns true if and only if this is NAN (not a number).
bool is_nan() const {
// It's a NAN if the exponent bits are all ones and the fraction
// bits are not entirely zeros.
return (exponent_bits() == kExponentBitMask) && (fraction_bits() != 0);
}
- // Returns true iff this number is at most kMaxUlps ULP's away from
- // rhs. In particular, this function:
+ // Returns true if and only if this number is at most kMaxUlps ULP's away
+ // from rhs. In particular, this function:
//
// - returns false if either number is (or both are) NAN.
// - treats really large numbers as almost equal to infinity.
@@ -421,7 +397,7 @@ typedef FloatingPoint<float> Float;
typedef FloatingPoint<double> Double;
// In order to catch the mistake of putting tests that use different
-// test fixture classes in the same test case, we need to assign
+// test fixture classes in the same test suite, we need to assign
// unique IDs to fixture classes and compare them. The TypeId type is
// used to hold such IDs. The user should treat TypeId as an opaque
// type: the only operation allowed on TypeId values is to compare
@@ -481,7 +457,7 @@ class TestFactoryBase {
template <class TestClass>
class TestFactoryImpl : public TestFactoryBase {
public:
- virtual Test* CreateTest() { return new TestClass; }
+ Test* CreateTest() override { return new TestClass; }
};
#if GTEST_OS_WINDOWS
@@ -497,23 +473,76 @@ GTEST_API_ AssertionResult IsHRESULTFailure(const char* expr,
#endif // GTEST_OS_WINDOWS
-// Types of SetUpTestCase() and TearDownTestCase() functions.
-typedef void (*SetUpTestCaseFunc)();
-typedef void (*TearDownTestCaseFunc)();
+// Types of SetUpTestSuite() and TearDownTestSuite() functions.
+using SetUpTestSuiteFunc = void (*)();
+using TearDownTestSuiteFunc = void (*)();
struct CodeLocation {
- CodeLocation(const string& a_file, int a_line) : file(a_file), line(a_line) {}
+ CodeLocation(const std::string& a_file, int a_line)
+ : file(a_file), line(a_line) {}
- string file;
+ std::string file;
int line;
};
+// Helper to identify which setup function for TestCase / TestSuite to call.
+// Only one function is allowed, either TestCase or TestSute but not both.
+
+// Utility functions to help SuiteApiResolver
+using SetUpTearDownSuiteFuncType = void (*)();
+
+inline SetUpTearDownSuiteFuncType GetNotDefaultOrNull(
+ SetUpTearDownSuiteFuncType a, SetUpTearDownSuiteFuncType def) {
+ return a == def ? nullptr : a;
+}
+
+template <typename T>
+// Note that SuiteApiResolver inherits from T because
+// SetUpTestSuite()/TearDownTestSuite() could be protected. Ths way
+// SuiteApiResolver can access them.
+struct SuiteApiResolver : T {
+ // testing::Test is only forward declared at this point. So we make it a
+ // dependend class for the compiler to be OK with it.
+ using Test =
+ typename std::conditional<sizeof(T) != 0, ::testing::Test, void>::type;
+
+ static SetUpTearDownSuiteFuncType GetSetUpCaseOrSuite(const char* filename,
+ int line_num) {
+ SetUpTearDownSuiteFuncType test_case_fp =
+ GetNotDefaultOrNull(&T::SetUpTestCase, &Test::SetUpTestCase);
+ SetUpTearDownSuiteFuncType test_suite_fp =
+ GetNotDefaultOrNull(&T::SetUpTestSuite, &Test::SetUpTestSuite);
+
+ GTEST_CHECK_(!test_case_fp || !test_suite_fp)
+ << "Test can not provide both SetUpTestSuite and SetUpTestCase, please "
+ "make sure there is only one present at "
+ << filename << ":" << line_num;
+
+ return test_case_fp != nullptr ? test_case_fp : test_suite_fp;
+ }
+
+ static SetUpTearDownSuiteFuncType GetTearDownCaseOrSuite(const char* filename,
+ int line_num) {
+ SetUpTearDownSuiteFuncType test_case_fp =
+ GetNotDefaultOrNull(&T::TearDownTestCase, &Test::TearDownTestCase);
+ SetUpTearDownSuiteFuncType test_suite_fp =
+ GetNotDefaultOrNull(&T::TearDownTestSuite, &Test::TearDownTestSuite);
+
+ GTEST_CHECK_(!test_case_fp || !test_suite_fp)
+ << "Test can not provide both TearDownTestSuite and TearDownTestCase,"
+ " please make sure there is only one present at"
+ << filename << ":" << line_num;
+
+ return test_case_fp != nullptr ? test_case_fp : test_suite_fp;
+ }
+};
+
// Creates a new TestInfo object and registers it with Google Test;
// returns the created object.
//
// Arguments:
//
-// test_case_name: name of the test case
+// test_suite_name: name of the test suite
// name: name of the test
// type_param the name of the test's type parameter, or NULL if
// this is not a typed or a type-parameterized test.
@@ -521,21 +550,16 @@ struct CodeLocation {
// or NULL if this is not a type-parameterized test.
// code_location: code location where the test is defined
// fixture_class_id: ID of the test fixture class
-// set_up_tc: pointer to the function that sets up the test case
-// tear_down_tc: pointer to the function that tears down the test case
+// set_up_tc: pointer to the function that sets up the test suite
+// tear_down_tc: pointer to the function that tears down the test suite
// factory: pointer to the factory that creates a test object.
// The newly created TestInfo instance will assume
// ownership of the factory object.
GTEST_API_ TestInfo* MakeAndRegisterTestInfo(
- const char* test_case_name,
- const char* name,
- const char* type_param,
- const char* value_param,
- CodeLocation code_location,
- TypeId fixture_class_id,
- SetUpTestCaseFunc set_up_tc,
- TearDownTestCaseFunc tear_down_tc,
- TestFactoryBase* factory);
+ const char* test_suite_name, const char* name, const char* type_param,
+ const char* value_param, CodeLocation code_location,
+ TypeId fixture_class_id, SetUpTestSuiteFunc set_up_tc,
+ TearDownTestSuiteFunc tear_down_tc, TestFactoryBase* factory);
// If *pstr starts with the given prefix, modifies *pstr to be right
// past the prefix and returns true; otherwise leaves *pstr unchanged
@@ -544,19 +568,23 @@ GTEST_API_ bool SkipPrefix(const char* prefix, const char** pstr);
#if GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P
-// State of the definition of a type-parameterized test case.
-class GTEST_API_ TypedTestCasePState {
+GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
+/* class A needs to have dll-interface to be used by clients of class B */)
+
+// State of the definition of a type-parameterized test suite.
+class GTEST_API_ TypedTestSuitePState {
public:
- TypedTestCasePState() : registered_(false) {}
+ TypedTestSuitePState() : registered_(false) {}
// Adds the given test name to defined_test_names_ and return true
- // if the test case hasn't been registered; otherwise aborts the
+ // if the test suite hasn't been registered; otherwise aborts the
// program.
bool AddTestName(const char* file, int line, const char* case_name,
const char* test_name) {
if (registered_) {
- fprintf(stderr, "%s Test %s must be defined before "
- "REGISTER_TYPED_TEST_CASE_P(%s, ...).\n",
+ fprintf(stderr,
+ "%s Test %s must be defined before "
+ "REGISTER_TYPED_TEST_SUITE_P(%s, ...).\n",
FormatFileLocation(file, line).c_str(), test_name, case_name);
fflush(stderr);
posix::Abort();
@@ -589,12 +617,19 @@ class GTEST_API_ TypedTestCasePState {
RegisteredTestsMap registered_tests_;
};
+// Legacy API is deprecated but still available
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+using TypedTestCasePState = TypedTestSuitePState;
+#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+
+GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251
+
// Skips to the first non-space char after the first comma in 'str';
// returns NULL if no comma is found in 'str'.
inline const char* SkipComma(const char* str) {
const char* comma = strchr(str, ',');
- if (comma == NULL) {
- return NULL;
+ if (comma == nullptr) {
+ return nullptr;
}
while (IsSpace(*(++comma))) {}
return comma;
@@ -604,7 +639,7 @@ inline const char* SkipComma(const char* str) {
// the entire string if it contains no comma.
inline std::string GetPrefixUntilComma(const char* str) {
const char* comma = strchr(str, ',');
- return comma == NULL ? str : std::string(str, comma);
+ return comma == nullptr ? str : std::string(str, comma);
}
// Splits a given string on a given delimiter, populating a given
@@ -612,6 +647,37 @@ inline std::string GetPrefixUntilComma(const char* str) {
void SplitString(const ::std::string& str, char delimiter,
::std::vector< ::std::string>* dest);
+// The default argument to the template below for the case when the user does
+// not provide a name generator.
+struct DefaultNameGenerator {
+ template <typename T>
+ static std::string GetName(int i) {
+ return StreamableToString(i);
+ }
+};
+
+template <typename Provided = DefaultNameGenerator>
+struct NameGeneratorSelector {
+ typedef Provided type;
+};
+
+template <typename NameGenerator>
+void GenerateNamesRecursively(Types0, std::vector<std::string>*, int) {}
+
+template <typename NameGenerator, typename Types>
+void GenerateNamesRecursively(Types, std::vector<std::string>* result, int i) {
+ result->push_back(NameGenerator::template GetName<typename Types::Head>(i));
+ GenerateNamesRecursively<NameGenerator>(typename Types::Tail(), result,
+ i + 1);
+}
+
+template <typename NameGenerator, typename Types>
+std::vector<std::string> GenerateNames() {
+ std::vector<std::string> result;
+ GenerateNamesRecursively<NameGenerator>(Types(), &result, 0);
+ return result;
+}
+
// TypeParameterizedTest<Fixture, TestSel, Types>::Register()
// registers a list of type-parameterized tests with Google Test. The
// return value is insignificant - we just need to return something
@@ -623,13 +689,13 @@ template <GTEST_TEMPLATE_ Fixture, class TestSel, typename Types>
class TypeParameterizedTest {
public:
// 'index' is the index of the test in the type list 'Types'
- // specified in INSTANTIATE_TYPED_TEST_CASE_P(Prefix, TestCase,
+ // specified in INSTANTIATE_TYPED_TEST_SUITE_P(Prefix, TestSuite,
// Types). Valid values for 'index' are [0, N - 1] where N is the
// length of Types.
- static bool Register(const char* prefix,
- CodeLocation code_location,
- const char* case_name, const char* test_names,
- int index) {
+ static bool Register(const char* prefix, const CodeLocation& code_location,
+ const char* case_name, const char* test_names, int index,
+ const std::vector<std::string>& type_names =
+ GenerateNames<DefaultNameGenerator, Types>()) {
typedef typename Types::Head Type;
typedef Fixture<Type> FixtureClass;
typedef typename GTEST_BIND_(TestSel, Type) TestClass;
@@ -637,20 +703,27 @@ class TypeParameterizedTest {
// First, registers the first type-parameterized test in the type
// list.
MakeAndRegisterTestInfo(
- (std::string(prefix) + (prefix[0] == '\0' ? "" : "/") + case_name + "/"
- + StreamableToString(index)).c_str(),
+ (std::string(prefix) + (prefix[0] == '\0' ? "" : "/") + case_name +
+ "/" + type_names[static_cast<size_t>(index)])
+ .c_str(),
StripTrailingSpaces(GetPrefixUntilComma(test_names)).c_str(),
GetTypeName<Type>().c_str(),
- NULL, // No value parameter.
- code_location,
- GetTypeId<FixtureClass>(),
- TestClass::SetUpTestCase,
- TestClass::TearDownTestCase,
+ nullptr, // No value parameter.
+ code_location, GetTypeId<FixtureClass>(),
+ SuiteApiResolver<TestClass>::GetSetUpCaseOrSuite(
+ code_location.file.c_str(), code_location.line),
+ SuiteApiResolver<TestClass>::GetTearDownCaseOrSuite(
+ code_location.file.c_str(), code_location.line),
new TestFactoryImpl<TestClass>);
// Next, recurses (at compile time) with the tail of the type list.
- return TypeParameterizedTest<Fixture, TestSel, typename Types::Tail>
- ::Register(prefix, code_location, case_name, test_names, index + 1);
+ return TypeParameterizedTest<Fixture, TestSel,
+ typename Types::Tail>::Register(prefix,
+ code_location,
+ case_name,
+ test_names,
+ index + 1,
+ type_names);
}
};
@@ -658,23 +731,27 @@ class TypeParameterizedTest {
template <GTEST_TEMPLATE_ Fixture, class TestSel>
class TypeParameterizedTest<Fixture, TestSel, Types0> {
public:
- static bool Register(const char* /*prefix*/, CodeLocation,
+ static bool Register(const char* /*prefix*/, const CodeLocation&,
const char* /*case_name*/, const char* /*test_names*/,
- int /*index*/) {
+ int /*index*/,
+ const std::vector<std::string>& =
+ std::vector<std::string>() /*type_names*/) {
return true;
}
};
-// TypeParameterizedTestCase<Fixture, Tests, Types>::Register()
+// TypeParameterizedTestSuite<Fixture, Tests, Types>::Register()
// registers *all combinations* of 'Tests' and 'Types' with Google
// Test. The return value is insignificant - we just need to return
// something such that we can call this function in a namespace scope.
template <GTEST_TEMPLATE_ Fixture, typename Tests, typename Types>
-class TypeParameterizedTestCase {
+class TypeParameterizedTestSuite {
public:
static bool Register(const char* prefix, CodeLocation code_location,
- const TypedTestCasePState* state,
- const char* case_name, const char* test_names) {
+ const TypedTestSuitePState* state, const char* case_name,
+ const char* test_names,
+ const std::vector<std::string>& type_names =
+ GenerateNames<DefaultNameGenerator, Types>()) {
std::string test_name = StripTrailingSpaces(
GetPrefixUntilComma(test_names));
if (!state->TestExists(test_name)) {
@@ -691,22 +768,26 @@ class TypeParameterizedTestCase {
// First, register the first test in 'Test' for each type in 'Types'.
TypeParameterizedTest<Fixture, Head, Types>::Register(
- prefix, test_location, case_name, test_names, 0);
+ prefix, test_location, case_name, test_names, 0, type_names);
// Next, recurses (at compile time) with the tail of the test list.
- return TypeParameterizedTestCase<Fixture, typename Tests::Tail, Types>
- ::Register(prefix, code_location, state,
- case_name, SkipComma(test_names));
+ return TypeParameterizedTestSuite<Fixture, typename Tests::Tail,
+ Types>::Register(prefix, code_location,
+ state, case_name,
+ SkipComma(test_names),
+ type_names);
}
};
// The base case for the compile time recursion.
template <GTEST_TEMPLATE_ Fixture, typename Types>
-class TypeParameterizedTestCase<Fixture, Templates0, Types> {
+class TypeParameterizedTestSuite<Fixture, Templates0, Types> {
public:
- static bool Register(const char* /*prefix*/, CodeLocation,
- const TypedTestCasePState* /*state*/,
- const char* /*case_name*/, const char* /*test_names*/) {
+ static bool Register(const char* /*prefix*/, const CodeLocation&,
+ const TypedTestSuitePState* /*state*/,
+ const char* /*case_name*/, const char* /*test_names*/,
+ const std::vector<std::string>& =
+ std::vector<std::string>() /*type_names*/) {
return true;
}
};
@@ -766,145 +847,16 @@ class GTEST_API_ Random {
GTEST_DISALLOW_COPY_AND_ASSIGN_(Random);
};
-// Defining a variable of type CompileAssertTypesEqual<T1, T2> will cause a
-// compiler error iff T1 and T2 are different types.
-template <typename T1, typename T2>
-struct CompileAssertTypesEqual;
-
-template <typename T>
-struct CompileAssertTypesEqual<T, T> {
-};
-
-// Removes the reference from a type if it is a reference type,
-// otherwise leaves it unchanged. This is the same as
-// tr1::remove_reference, which is not widely available yet.
-template <typename T>
-struct RemoveReference { typedef T type; }; // NOLINT
-template <typename T>
-struct RemoveReference<T&> { typedef T type; }; // NOLINT
-
-// A handy wrapper around RemoveReference that works when the argument
-// T depends on template parameters.
-#define GTEST_REMOVE_REFERENCE_(T) \
- typename ::testing::internal::RemoveReference<T>::type
-
-// Removes const from a type if it is a const type, otherwise leaves
-// it unchanged. This is the same as tr1::remove_const, which is not
-// widely available yet.
-template <typename T>
-struct RemoveConst { typedef T type; }; // NOLINT
-template <typename T>
-struct RemoveConst<const T> { typedef T type; }; // NOLINT
-
-// MSVC 8.0, Sun C++, and IBM XL C++ have a bug which causes the above
-// definition to fail to remove the const in 'const int[3]' and 'const
-// char[3][4]'. The following specialization works around the bug.
-template <typename T, size_t N>
-struct RemoveConst<const T[N]> {
- typedef typename RemoveConst<T>::type type[N];
-};
-
-#if defined(_MSC_VER) && _MSC_VER < 1400
-// This is the only specialization that allows VC++ 7.1 to remove const in
-// 'const int[3] and 'const int[3][4]'. However, it causes trouble with GCC
-// and thus needs to be conditionally compiled.
-template <typename T, size_t N>
-struct RemoveConst<T[N]> {
- typedef typename RemoveConst<T>::type type[N];
-};
-#endif
-
-// A handy wrapper around RemoveConst that works when the argument
-// T depends on template parameters.
-#define GTEST_REMOVE_CONST_(T) \
- typename ::testing::internal::RemoveConst<T>::type
-
// Turns const U&, U&, const U, and U all into U.
#define GTEST_REMOVE_REFERENCE_AND_CONST_(T) \
- GTEST_REMOVE_CONST_(GTEST_REMOVE_REFERENCE_(T))
-
-// Adds reference to a type if it is not a reference type,
-// otherwise leaves it unchanged. This is the same as
-// tr1::add_reference, which is not widely available yet.
-template <typename T>
-struct AddReference { typedef T& type; }; // NOLINT
-template <typename T>
-struct AddReference<T&> { typedef T& type; }; // NOLINT
-
-// A handy wrapper around AddReference that works when the argument T
-// depends on template parameters.
-#define GTEST_ADD_REFERENCE_(T) \
- typename ::testing::internal::AddReference<T>::type
-
-// Adds a reference to const on top of T as necessary. For example,
-// it transforms
-//
-// char ==> const char&
-// const char ==> const char&
-// char& ==> const char&
-// const char& ==> const char&
-//
-// The argument T must depend on some template parameters.
-#define GTEST_REFERENCE_TO_CONST_(T) \
- GTEST_ADD_REFERENCE_(const GTEST_REMOVE_REFERENCE_(T))
-
-// ImplicitlyConvertible<From, To>::value is a compile-time bool
-// constant that's true iff type From can be implicitly converted to
-// type To.
-template <typename From, typename To>
-class ImplicitlyConvertible {
- private:
- // We need the following helper functions only for their types.
- // They have no implementations.
-
- // MakeFrom() is an expression whose type is From. We cannot simply
- // use From(), as the type From may not have a public default
- // constructor.
- static typename AddReference<From>::type MakeFrom();
-
- // These two functions are overloaded. Given an expression
- // Helper(x), the compiler will pick the first version if x can be
- // implicitly converted to type To; otherwise it will pick the
- // second version.
- //
- // The first version returns a value of size 1, and the second
- // version returns a value of size 2. Therefore, by checking the
- // size of Helper(x), which can be done at compile time, we can tell
- // which version of Helper() is used, and hence whether x can be
- // implicitly converted to type To.
- static char Helper(To);
- static char (&Helper(...))[2]; // NOLINT
-
- // We have to put the 'public' section after the 'private' section,
- // or MSVC refuses to compile the code.
- public:
-#if defined(__BORLANDC__)
- // C++Builder cannot use member overload resolution during template
- // instantiation. The simplest workaround is to use its C++0x type traits
- // functions (C++Builder 2009 and above only).
- static const bool value = __is_convertible(From, To);
-#else
- // MSVC warns about implicitly converting from double to int for
- // possible loss of data, so we need to temporarily disable the
- // warning.
- GTEST_DISABLE_MSC_WARNINGS_PUSH_(4244)
- static const bool value =
- sizeof(Helper(ImplicitlyConvertible::MakeFrom())) == 1;
- GTEST_DISABLE_MSC_WARNINGS_POP_()
-#endif // __BORLANDC__
-};
-template <typename From, typename To>
-const bool ImplicitlyConvertible<From, To>::value;
+ typename std::remove_const<typename std::remove_reference<T>::type>::type
// IsAProtocolMessage<T>::value is a compile-time bool constant that's
-// true iff T is type ProtocolMessage, proto2::Message, or a subclass
-// of those.
+// true if and only if T is type proto2::Message or a subclass of it.
template <typename T>
struct IsAProtocolMessage
: public bool_constant<
- ImplicitlyConvertible<const T*, const ::ProtocolMessage*>::value ||
- ImplicitlyConvertible<const T*, const ::proto2::Message*>::value> {
-};
+ std::is_convertible<const T*, const ::proto2::Message*>::value> {};
// When the compiler sees expression IsContainerTest<C>(0), if C is an
// STL-style container class, the first overload of IsContainerTest
@@ -917,8 +869,11 @@ struct IsAProtocolMessage
// a container class by checking the type of IsContainerTest<C>(0).
// The value of the expression is insignificant.
//
-// Note that we look for both C::iterator and C::const_iterator. The
-// reason is that C++ injects the name of a class as a member of the
+// In C++11 mode we check the existence of a const_iterator and that an
+// iterator is properly implemented for the container.
+//
+// For pre-C++11 that we look for both C::iterator and C::const_iterator.
+// The reason is that C++ injects the name of a class as a member of the
// class itself (e.g. you can refer to class iterator as either
// 'iterator' or 'iterator::iterator'). If we look for C::iterator
// only, for example, we would mistakenly think that a class named
@@ -928,10 +883,13 @@ struct IsAProtocolMessage
// IsContainerTest(typename C::const_iterator*) and
// IsContainerTest(...) doesn't work with Visual Age C++ and Sun C++.
typedef int IsContainer;
-template <class C>
-IsContainer IsContainerTest(int /* dummy */,
- typename C::iterator* /* it */ = NULL,
- typename C::const_iterator* /* const_it */ = NULL) {
+template <class C,
+ class Iterator = decltype(::std::declval<const C&>().begin()),
+ class = decltype(::std::declval<const C&>().end()),
+ class = decltype(++::std::declval<Iterator&>()),
+ class = decltype(*::std::declval<Iterator>()),
+ class = typename C::const_iterator>
+IsContainer IsContainerTest(int /* dummy */) {
return 0;
}
@@ -939,12 +897,55 @@ typedef char IsNotContainer;
template <class C>
IsNotContainer IsContainerTest(long /* dummy */) { return '\0'; }
-// EnableIf<condition>::type is void when 'Cond' is true, and
-// undefined when 'Cond' is false. To use SFINAE to make a function
-// overload only apply when a particular expression is true, add
-// "typename EnableIf<expression>::type* = 0" as the last parameter.
-template<bool> struct EnableIf;
-template<> struct EnableIf<true> { typedef void type; }; // NOLINT
+// Trait to detect whether a type T is a hash table.
+// The heuristic used is that the type contains an inner type `hasher` and does
+// not contain an inner type `reverse_iterator`.
+// If the container is iterable in reverse, then order might actually matter.
+template <typename T>
+struct IsHashTable {
+ private:
+ template <typename U>
+ static char test(typename U::hasher*, typename U::reverse_iterator*);
+ template <typename U>
+ static int test(typename U::hasher*, ...);
+ template <typename U>
+ static char test(...);
+
+ public:
+ static const bool value = sizeof(test<T>(nullptr, nullptr)) == sizeof(int);
+};
+
+template <typename T>
+const bool IsHashTable<T>::value;
+
+template <typename C,
+ bool = sizeof(IsContainerTest<C>(0)) == sizeof(IsContainer)>
+struct IsRecursiveContainerImpl;
+
+template <typename C>
+struct IsRecursiveContainerImpl<C, false> : public std::false_type {};
+
+// Since the IsRecursiveContainerImpl depends on the IsContainerTest we need to
+// obey the same inconsistencies as the IsContainerTest, namely check if
+// something is a container is relying on only const_iterator in C++11 and
+// is relying on both const_iterator and iterator otherwise
+template <typename C>
+struct IsRecursiveContainerImpl<C, true> {
+ using value_type = decltype(*std::declval<typename C::const_iterator>());
+ using type =
+ std::is_same<typename std::remove_const<
+ typename std::remove_reference<value_type>::type>::type,
+ C>;
+};
+
+// IsRecursiveContainer<Type> is a unary compile-time predicate that
+// evaluates whether C is a recursive container type. A recursive container
+// type is a container type whose value_type is equal to the container type
+// itself. An example for a recursive container type is
+// boost::filesystem::path, whose iterator has a value_type that is equal to
+// boost::filesystem::path.
+template <typename C>
+struct IsRecursiveContainer : public IsRecursiveContainerImpl<C>::type {};
// Utilities for native arrays.
@@ -1068,10 +1069,9 @@ class NativeArray {
}
private:
- enum {
- kCheckTypeIsNotConstOrAReference = StaticAssertTypeEqHelper<
- Element, GTEST_REMOVE_REFERENCE_AND_CONST_(Element)>::value,
- };
+ static_assert(!std::is_const<Element>::value, "Type must not be const");
+ static_assert(!std::is_reference<Element>::value,
+ "Type must not be a reference");
// Initializes this object with a copy of the input.
void InitCopy(const Element* array, size_t a_size) {
@@ -1096,6 +1096,139 @@ class NativeArray {
GTEST_DISALLOW_ASSIGN_(NativeArray);
};
+// Backport of std::index_sequence.
+template <size_t... Is>
+struct IndexSequence {
+ using type = IndexSequence;
+};
+
+// Double the IndexSequence, and one if plus_one is true.
+template <bool plus_one, typename T, size_t sizeofT>
+struct DoubleSequence;
+template <size_t... I, size_t sizeofT>
+struct DoubleSequence<true, IndexSequence<I...>, sizeofT> {
+ using type = IndexSequence<I..., (sizeofT + I)..., 2 * sizeofT>;
+};
+template <size_t... I, size_t sizeofT>
+struct DoubleSequence<false, IndexSequence<I...>, sizeofT> {
+ using type = IndexSequence<I..., (sizeofT + I)...>;
+};
+
+// Backport of std::make_index_sequence.
+// It uses O(ln(N)) instantiation depth.
+template <size_t N>
+struct MakeIndexSequence
+ : DoubleSequence<N % 2 == 1, typename MakeIndexSequence<N / 2>::type,
+ N / 2>::type {};
+
+template <>
+struct MakeIndexSequence<0> : IndexSequence<> {};
+
+// FIXME: This implementation of ElemFromList is O(1) in instantiation depth,
+// but it is O(N^2) in total instantiations. Not sure if this is the best
+// tradeoff, as it will make it somewhat slow to compile.
+template <typename T, size_t, size_t>
+struct ElemFromListImpl {};
+
+template <typename T, size_t I>
+struct ElemFromListImpl<T, I, I> {
+ using type = T;
+};
+
+// Get the Nth element from T...
+// It uses O(1) instantiation depth.
+template <size_t N, typename I, typename... T>
+struct ElemFromList;
+
+template <size_t N, size_t... I, typename... T>
+struct ElemFromList<N, IndexSequence<I...>, T...>
+ : ElemFromListImpl<T, N, I>... {};
+
+template <typename... T>
+class FlatTuple;
+
+template <typename Derived, size_t I>
+struct FlatTupleElemBase;
+
+template <typename... T, size_t I>
+struct FlatTupleElemBase<FlatTuple<T...>, I> {
+ using value_type =
+ typename ElemFromList<I, typename MakeIndexSequence<sizeof...(T)>::type,
+ T...>::type;
+ FlatTupleElemBase() = default;
+ explicit FlatTupleElemBase(value_type t) : value(std::move(t)) {}
+ value_type value;
+};
+
+template <typename Derived, typename Idx>
+struct FlatTupleBase;
+
+template <size_t... Idx, typename... T>
+struct FlatTupleBase<FlatTuple<T...>, IndexSequence<Idx...>>
+ : FlatTupleElemBase<FlatTuple<T...>, Idx>... {
+ using Indices = IndexSequence<Idx...>;
+ FlatTupleBase() = default;
+ explicit FlatTupleBase(T... t)
+ : FlatTupleElemBase<FlatTuple<T...>, Idx>(std::move(t))... {}
+};
+
+// Analog to std::tuple but with different tradeoffs.
+// This class minimizes the template instantiation depth, thus allowing more
+// elements that std::tuple would. std::tuple has been seen to require an
+// instantiation depth of more than 10x the number of elements in some
+// implementations.
+// FlatTuple and ElemFromList are not recursive and have a fixed depth
+// regardless of T...
+// MakeIndexSequence, on the other hand, it is recursive but with an
+// instantiation depth of O(ln(N)).
+template <typename... T>
+class FlatTuple
+ : private FlatTupleBase<FlatTuple<T...>,
+ typename MakeIndexSequence<sizeof...(T)>::type> {
+ using Indices = typename FlatTuple::FlatTupleBase::Indices;
+
+ public:
+ FlatTuple() = default;
+ explicit FlatTuple(T... t) : FlatTuple::FlatTupleBase(std::move(t)...) {}
+
+ template <size_t I>
+ const typename ElemFromList<I, Indices, T...>::type& Get() const {
+ return static_cast<const FlatTupleElemBase<FlatTuple, I>*>(this)->value;
+ }
+
+ template <size_t I>
+ typename ElemFromList<I, Indices, T...>::type& Get() {
+ return static_cast<FlatTupleElemBase<FlatTuple, I>*>(this)->value;
+ }
+};
+
+// Utility functions to be called with static_assert to induce deprecation
+// warnings.
+GTEST_INTERNAL_DEPRECATED(
+ "INSTANTIATE_TEST_CASE_P is deprecated, please use "
+ "INSTANTIATE_TEST_SUITE_P")
+constexpr bool InstantiateTestCase_P_IsDeprecated() { return true; }
+
+GTEST_INTERNAL_DEPRECATED(
+ "TYPED_TEST_CASE_P is deprecated, please use "
+ "TYPED_TEST_SUITE_P")
+constexpr bool TypedTestCase_P_IsDeprecated() { return true; }
+
+GTEST_INTERNAL_DEPRECATED(
+ "TYPED_TEST_CASE is deprecated, please use "
+ "TYPED_TEST_SUITE")
+constexpr bool TypedTestCaseIsDeprecated() { return true; }
+
+GTEST_INTERNAL_DEPRECATED(
+ "REGISTER_TYPED_TEST_CASE_P is deprecated, please use "
+ "REGISTER_TYPED_TEST_SUITE_P")
+constexpr bool RegisterTypedTestCase_P_IsDeprecated() { return true; }
+
+GTEST_INTERNAL_DEPRECATED(
+ "INSTANTIATE_TYPED_TEST_CASE_P is deprecated, please use "
+ "INSTANTIATE_TYPED_TEST_SUITE_P")
+constexpr bool InstantiateTypedTestCase_P_IsDeprecated() { return true; }
+
} // namespace internal
} // namespace testing
@@ -1115,7 +1248,10 @@ class NativeArray {
#define GTEST_SUCCESS_(message) \
GTEST_MESSAGE_(message, ::testing::TestPartResult::kSuccess)
-// Suppresses MSVC warnings 4072 (unreachable code) for the code following
+#define GTEST_SKIP_(message) \
+ return GTEST_MESSAGE_(message, ::testing::TestPartResult::kSkip)
+
+// Suppress MSVC warning 4072 (unreachable code) for the code following
// statement if it returns or throws (or doesn't return or throw in some
// situations).
#define GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) \
@@ -1207,32 +1343,38 @@ class NativeArray {
" Actual: it does.")
// Expands to the name of the class that implements the given test.
-#define GTEST_TEST_CLASS_NAME_(test_case_name, test_name) \
- test_case_name##_##test_name##_Test
+#define GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \
+ test_suite_name##_##test_name##_Test
// Helper macro for defining tests.
-#define GTEST_TEST_(test_case_name, test_name, parent_class, parent_id)\
-class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) : public parent_class {\
- public:\
- GTEST_TEST_CLASS_NAME_(test_case_name, test_name)() {}\
- private:\
- virtual void TestBody();\
- static ::testing::TestInfo* const test_info_ GTEST_ATTRIBUTE_UNUSED_;\
- GTEST_DISALLOW_COPY_AND_ASSIGN_(\
- GTEST_TEST_CLASS_NAME_(test_case_name, test_name));\
-};\
-\
-::testing::TestInfo* const GTEST_TEST_CLASS_NAME_(test_case_name, test_name)\
- ::test_info_ =\
- ::testing::internal::MakeAndRegisterTestInfo(\
- #test_case_name, #test_name, NULL, NULL, \
- ::testing::internal::CodeLocation(__FILE__, __LINE__), \
- (parent_id), \
- parent_class::SetUpTestCase, \
- parent_class::TearDownTestCase, \
- new ::testing::internal::TestFactoryImpl<\
- GTEST_TEST_CLASS_NAME_(test_case_name, test_name)>);\
-void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody()
+#define GTEST_TEST_(test_suite_name, test_name, parent_class, parent_id) \
+ static_assert(sizeof(GTEST_STRINGIFY_(test_suite_name)) > 1, \
+ "test_suite_name must not be empty"); \
+ static_assert(sizeof(GTEST_STRINGIFY_(test_name)) > 1, \
+ "test_name must not be empty"); \
+ class GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \
+ : public parent_class { \
+ public: \
+ GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() {} \
+ \
+ private: \
+ virtual void TestBody(); \
+ static ::testing::TestInfo* const test_info_ GTEST_ATTRIBUTE_UNUSED_; \
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(GTEST_TEST_CLASS_NAME_(test_suite_name, \
+ test_name)); \
+ }; \
+ \
+ ::testing::TestInfo* const GTEST_TEST_CLASS_NAME_(test_suite_name, \
+ test_name)::test_info_ = \
+ ::testing::internal::MakeAndRegisterTestInfo( \
+ #test_suite_name, #test_name, nullptr, nullptr, \
+ ::testing::internal::CodeLocation(__FILE__, __LINE__), (parent_id), \
+ ::testing::internal::SuiteApiResolver< \
+ parent_class>::GetSetUpCaseOrSuite(__FILE__, __LINE__), \
+ ::testing::internal::SuiteApiResolver< \
+ parent_class>::GetTearDownCaseOrSuite(__FILE__, __LINE__), \
+ new ::testing::internal::TestFactoryImpl<GTEST_TEST_CLASS_NAME_( \
+ test_suite_name, test_name)>); \
+ void GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::TestBody()
#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_
-
diff --git a/extern/gtest/include/gtest/internal/gtest-linked_ptr.h b/extern/gtest/include/gtest/internal/gtest-linked_ptr.h
deleted file mode 100644
index 36029422174..00000000000
--- a/extern/gtest/include/gtest/internal/gtest-linked_ptr.h
+++ /dev/null
@@ -1,243 +0,0 @@
-// Copyright 2003 Google Inc.
-// All rights reserved.
-//
-// 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.
-//
-// Authors: Dan Egnor (egnor@google.com)
-//
-// A "smart" pointer type with reference tracking. Every pointer to a
-// particular object is kept on a circular linked list. When the last pointer
-// to an object is destroyed or reassigned, the object is deleted.
-//
-// Used properly, this deletes the object when the last reference goes away.
-// There are several caveats:
-// - Like all reference counting schemes, cycles lead to leaks.
-// - Each smart pointer is actually two pointers (8 bytes instead of 4).
-// - Every time a pointer is assigned, the entire list of pointers to that
-// object is traversed. This class is therefore NOT SUITABLE when there
-// will often be more than two or three pointers to a particular object.
-// - References are only tracked as long as linked_ptr<> objects are copied.
-// If a linked_ptr<> is converted to a raw pointer and back, BAD THINGS
-// will happen (double deletion).
-//
-// A good use of this class is storing object references in STL containers.
-// You can safely put linked_ptr<> in a vector<>.
-// Other uses may not be as good.
-//
-// Note: If you use an incomplete type with linked_ptr<>, the class
-// *containing* linked_ptr<> must have a constructor and destructor (even
-// if they do nothing!).
-//
-// Bill Gibbons suggested we use something like this.
-//
-// Thread Safety:
-// Unlike other linked_ptr implementations, in this implementation
-// a linked_ptr object is thread-safe in the sense that:
-// - it's safe to copy linked_ptr objects concurrently,
-// - it's safe to copy *from* a linked_ptr and read its underlying
-// raw pointer (e.g. via get()) concurrently, and
-// - it's safe to write to two linked_ptrs that point to the same
-// shared object concurrently.
-// TODO(wan@google.com): rename this to safe_linked_ptr to avoid
-// confusion with normal linked_ptr.
-
-#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_
-#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_
-
-#include <stdlib.h>
-#include <assert.h>
-
-#include "gtest/internal/gtest-port.h"
-
-namespace testing {
-namespace internal {
-
-// Protects copying of all linked_ptr objects.
-GTEST_API_ GTEST_DECLARE_STATIC_MUTEX_(g_linked_ptr_mutex);
-
-// This is used internally by all instances of linked_ptr<>. It needs to be
-// a non-template class because different types of linked_ptr<> can refer to
-// the same object (linked_ptr<Superclass>(obj) vs linked_ptr<Subclass>(obj)).
-// So, it needs to be possible for different types of linked_ptr to participate
-// in the same circular linked list, so we need a single class type here.
-//
-// DO NOT USE THIS CLASS DIRECTLY YOURSELF. Use linked_ptr<T>.
-class linked_ptr_internal {
- public:
- // Create a new circle that includes only this instance.
- void join_new() {
- next_ = this;
- }
-
- // Many linked_ptr operations may change p.link_ for some linked_ptr
- // variable p in the same circle as this object. Therefore we need
- // to prevent two such operations from occurring concurrently.
- //
- // Note that different types of linked_ptr objects can coexist in a
- // circle (e.g. linked_ptr<Base>, linked_ptr<Derived1>, and
- // linked_ptr<Derived2>). Therefore we must use a single mutex to
- // protect all linked_ptr objects. This can create serious
- // contention in production code, but is acceptable in a testing
- // framework.
-
- // Join an existing circle.
- void join(linked_ptr_internal const* ptr)
- GTEST_LOCK_EXCLUDED_(g_linked_ptr_mutex) {
- MutexLock lock(&g_linked_ptr_mutex);
-
- linked_ptr_internal const* p = ptr;
- while (p->next_ != ptr) {
- assert(p->next_ != this &&
- "Trying to join() a linked ring we are already in. "
- "Is GMock thread safety enabled?");
- p = p->next_;
- }
- p->next_ = this;
- next_ = ptr;
- }
-
- // Leave whatever circle we're part of. Returns true if we were the
- // last member of the circle. Once this is done, you can join() another.
- bool depart()
- GTEST_LOCK_EXCLUDED_(g_linked_ptr_mutex) {
- MutexLock lock(&g_linked_ptr_mutex);
-
- if (next_ == this) return true;
- linked_ptr_internal const* p = next_;
- while (p->next_ != this) {
- assert(p->next_ != next_ &&
- "Trying to depart() a linked ring we are not in. "
- "Is GMock thread safety enabled?");
- p = p->next_;
- }
- p->next_ = next_;
- return false;
- }
-
- private:
- mutable linked_ptr_internal const* next_;
-};
-
-template <typename T>
-class linked_ptr {
- public:
- typedef T element_type;
-
- // Take over ownership of a raw pointer. This should happen as soon as
- // possible after the object is created.
- explicit linked_ptr(T* ptr = NULL) { capture(ptr); }
- ~linked_ptr() { depart(); }
-
- // Copy an existing linked_ptr<>, adding ourselves to the list of references.
- template <typename U> linked_ptr(linked_ptr<U> const& ptr) { copy(&ptr); }
- linked_ptr(linked_ptr const& ptr) { // NOLINT
- assert(&ptr != this);
- copy(&ptr);
- }
-
- // Assignment releases the old value and acquires the new.
- template <typename U> linked_ptr& operator=(linked_ptr<U> const& ptr) {
- depart();
- copy(&ptr);
- return *this;
- }
-
- linked_ptr& operator=(linked_ptr const& ptr) {
- if (&ptr != this) {
- depart();
- copy(&ptr);
- }
- return *this;
- }
-
- // Smart pointer members.
- void reset(T* ptr = NULL) {
- depart();
- capture(ptr);
- }
- T* get() const { return value_; }
- T* operator->() const { return value_; }
- T& operator*() const { return *value_; }
-
- bool operator==(T* p) const { return value_ == p; }
- bool operator!=(T* p) const { return value_ != p; }
- template <typename U>
- bool operator==(linked_ptr<U> const& ptr) const {
- return value_ == ptr.get();
- }
- template <typename U>
- bool operator!=(linked_ptr<U> const& ptr) const {
- return value_ != ptr.get();
- }
-
- private:
- template <typename U>
- friend class linked_ptr;
-
- T* value_;
- linked_ptr_internal link_;
-
- void depart() {
- if (link_.depart()) delete value_;
- }
-
- void capture(T* ptr) {
- value_ = ptr;
- link_.join_new();
- }
-
- template <typename U> void copy(linked_ptr<U> const* ptr) {
- value_ = ptr->get();
- if (value_)
- link_.join(&ptr->link_);
- else
- link_.join_new();
- }
-};
-
-template<typename T> inline
-bool operator==(T* ptr, const linked_ptr<T>& x) {
- return ptr == x.get();
-}
-
-template<typename T> inline
-bool operator!=(T* ptr, const linked_ptr<T>& x) {
- return ptr != x.get();
-}
-
-// A function to convert T* into linked_ptr<T>
-// Doing e.g. make_linked_ptr(new FooBarBaz<type>(arg)) is a shorter notation
-// for linked_ptr<FooBarBaz<type> >(new FooBarBaz<type>(arg))
-template <typename T>
-linked_ptr<T> make_linked_ptr(T* ptr) {
- return linked_ptr<T>(ptr);
-}
-
-} // namespace internal
-} // namespace testing
-
-#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_
diff --git a/extern/gtest/include/gtest/internal/gtest-param-util-generated.h b/extern/gtest/include/gtest/internal/gtest-param-util-generated.h
deleted file mode 100644
index 4d1d81d20ff..00000000000
--- a/extern/gtest/include/gtest/internal/gtest-param-util-generated.h
+++ /dev/null
@@ -1,5146 +0,0 @@
-// This file was GENERATED by command:
-// pump.py gtest-param-util-generated.h.pump
-// DO NOT EDIT BY HAND!!!
-
-// Copyright 2008 Google Inc.
-// All Rights Reserved.
-//
-// 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: vladl@google.com (Vlad Losev)
-
-// Type and function utilities for implementing parameterized tests.
-// This file is generated by a SCRIPT. DO NOT EDIT BY HAND!
-//
-// Currently Google Test supports at most 50 arguments in Values,
-// and at most 10 arguments in Combine. Please contact
-// googletestframework@googlegroups.com if you need more.
-// Please note that the number of arguments to Combine is limited
-// by the maximum arity of the implementation of tuple which is
-// currently set at 10.
-
-#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_
-#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_
-
-// scripts/fuse_gtest.py depends on gtest's own header being #included
-// *unconditionally*. Therefore these #includes cannot be moved
-// inside #if GTEST_HAS_PARAM_TEST.
-#include "gtest/internal/gtest-param-util.h"
-#include "gtest/internal/gtest-port.h"
-
-#if GTEST_HAS_PARAM_TEST
-
-namespace testing {
-
-// Forward declarations of ValuesIn(), which is implemented in
-// include/gtest/gtest-param-test.h.
-template <typename ForwardIterator>
-internal::ParamGenerator<
- typename ::testing::internal::IteratorTraits<ForwardIterator>::value_type>
-ValuesIn(ForwardIterator begin, ForwardIterator end);
-
-template <typename T, size_t N>
-internal::ParamGenerator<T> ValuesIn(const T (&array)[N]);
-
-template <class Container>
-internal::ParamGenerator<typename Container::value_type> ValuesIn(
- const Container& container);
-
-namespace internal {
-
-// Used in the Values() function to provide polymorphic capabilities.
-template <typename T1>
-class ValueArray1 {
- public:
- explicit ValueArray1(T1 v1) : v1_(v1) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray1& other);
-
- const T1 v1_;
-};
-
-template <typename T1, typename T2>
-class ValueArray2 {
- public:
- ValueArray2(T1 v1, T2 v2) : v1_(v1), v2_(v2) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray2& other);
-
- const T1 v1_;
- const T2 v2_;
-};
-
-template <typename T1, typename T2, typename T3>
-class ValueArray3 {
- public:
- ValueArray3(T1 v1, T2 v2, T3 v3) : v1_(v1), v2_(v2), v3_(v3) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray3& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
-};
-
-template <typename T1, typename T2, typename T3, typename T4>
-class ValueArray4 {
- public:
- ValueArray4(T1 v1, T2 v2, T3 v3, T4 v4) : v1_(v1), v2_(v2), v3_(v3),
- v4_(v4) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_), static_cast<T>(v4_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray4& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
- const T4 v4_;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5>
-class ValueArray5 {
- public:
- ValueArray5(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5) : v1_(v1), v2_(v2), v3_(v3),
- v4_(v4), v5_(v5) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray5& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
- const T4 v4_;
- const T5 v5_;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6>
-class ValueArray6 {
- public:
- ValueArray6(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6) : v1_(v1), v2_(v2),
- v3_(v3), v4_(v4), v5_(v5), v6_(v6) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
- static_cast<T>(v6_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray6& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
- const T4 v4_;
- const T5 v5_;
- const T6 v6_;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7>
-class ValueArray7 {
- public:
- ValueArray7(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7) : v1_(v1),
- v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
- static_cast<T>(v6_), static_cast<T>(v7_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray7& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
- const T4 v4_;
- const T5 v5_;
- const T6 v6_;
- const T7 v7_;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8>
-class ValueArray8 {
- public:
- ValueArray8(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7,
- T8 v8) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
- v8_(v8) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
- static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray8& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
- const T4 v4_;
- const T5 v5_;
- const T6 v6_;
- const T7 v7_;
- const T8 v8_;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9>
-class ValueArray9 {
- public:
- ValueArray9(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8,
- T9 v9) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
- v8_(v8), v9_(v9) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
- static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
- static_cast<T>(v9_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray9& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
- const T4 v4_;
- const T5 v5_;
- const T6 v6_;
- const T7 v7_;
- const T8 v8_;
- const T9 v9_;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10>
-class ValueArray10 {
- public:
- ValueArray10(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
- v8_(v8), v9_(v9), v10_(v10) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
- static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
- static_cast<T>(v9_), static_cast<T>(v10_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray10& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
- const T4 v4_;
- const T5 v5_;
- const T6 v6_;
- const T7 v7_;
- const T8 v8_;
- const T9 v9_;
- const T10 v10_;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11>
-class ValueArray11 {
- public:
- ValueArray11(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6),
- v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
- static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
- static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray11& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
- const T4 v4_;
- const T5 v5_;
- const T6 v6_;
- const T7 v7_;
- const T8 v8_;
- const T9 v9_;
- const T10 v10_;
- const T11 v11_;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12>
-class ValueArray12 {
- public:
- ValueArray12(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5),
- v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
- static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
- static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
- static_cast<T>(v12_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray12& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
- const T4 v4_;
- const T5 v5_;
- const T6 v6_;
- const T7 v7_;
- const T8 v8_;
- const T9 v9_;
- const T10 v10_;
- const T11 v11_;
- const T12 v12_;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13>
-class ValueArray13 {
- public:
- ValueArray13(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13) : v1_(v1), v2_(v2), v3_(v3), v4_(v4),
- v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11),
- v12_(v12), v13_(v13) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
- static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
- static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
- static_cast<T>(v12_), static_cast<T>(v13_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray13& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
- const T4 v4_;
- const T5 v5_;
- const T6 v6_;
- const T7 v7_;
- const T8 v8_;
- const T9 v9_;
- const T10 v10_;
- const T11 v11_;
- const T12 v12_;
- const T13 v13_;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14>
-class ValueArray14 {
- public:
- ValueArray14(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13, T14 v14) : v1_(v1), v2_(v2), v3_(v3),
- v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10),
- v11_(v11), v12_(v12), v13_(v13), v14_(v14) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
- static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
- static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
- static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray14& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
- const T4 v4_;
- const T5 v5_;
- const T6 v6_;
- const T7 v7_;
- const T8 v8_;
- const T9 v9_;
- const T10 v10_;
- const T11 v11_;
- const T12 v12_;
- const T13 v13_;
- const T14 v14_;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15>
-class ValueArray15 {
- public:
- ValueArray15(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15) : v1_(v1), v2_(v2),
- v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10),
- v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
- static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
- static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
- static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
- static_cast<T>(v15_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray15& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
- const T4 v4_;
- const T5 v5_;
- const T6 v6_;
- const T7 v7_;
- const T8 v8_;
- const T9 v9_;
- const T10 v10_;
- const T11 v11_;
- const T12 v12_;
- const T13 v13_;
- const T14 v14_;
- const T15 v15_;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16>
-class ValueArray16 {
- public:
- ValueArray16(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16) : v1_(v1),
- v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9),
- v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15),
- v16_(v16) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
- static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
- static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
- static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
- static_cast<T>(v15_), static_cast<T>(v16_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray16& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
- const T4 v4_;
- const T5 v5_;
- const T6 v6_;
- const T7 v7_;
- const T8 v8_;
- const T9 v9_;
- const T10 v10_;
- const T11 v11_;
- const T12 v12_;
- const T13 v13_;
- const T14 v14_;
- const T15 v15_;
- const T16 v16_;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17>
-class ValueArray17 {
- public:
- ValueArray17(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16,
- T17 v17) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
- v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14),
- v15_(v15), v16_(v16), v17_(v17) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
- static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
- static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
- static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
- static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray17& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
- const T4 v4_;
- const T5 v5_;
- const T6 v6_;
- const T7 v7_;
- const T8 v8_;
- const T9 v9_;
- const T10 v10_;
- const T11 v11_;
- const T12 v12_;
- const T13 v13_;
- const T14 v14_;
- const T15 v15_;
- const T16 v16_;
- const T17 v17_;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18>
-class ValueArray18 {
- public:
- ValueArray18(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
- T18 v18) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
- v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14),
- v15_(v15), v16_(v16), v17_(v17), v18_(v18) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
- static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
- static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
- static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
- static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
- static_cast<T>(v18_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray18& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
- const T4 v4_;
- const T5 v5_;
- const T6 v6_;
- const T7 v7_;
- const T8 v8_;
- const T9 v9_;
- const T10 v10_;
- const T11 v11_;
- const T12 v12_;
- const T13 v13_;
- const T14 v14_;
- const T15 v15_;
- const T16 v16_;
- const T17 v17_;
- const T18 v18_;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19>
-class ValueArray19 {
- public:
- ValueArray19(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
- T18 v18, T19 v19) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6),
- v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13),
- v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
- static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
- static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
- static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
- static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
- static_cast<T>(v18_), static_cast<T>(v19_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray19& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
- const T4 v4_;
- const T5 v5_;
- const T6 v6_;
- const T7 v7_;
- const T8 v8_;
- const T9 v9_;
- const T10 v10_;
- const T11 v11_;
- const T12 v12_;
- const T13 v13_;
- const T14 v14_;
- const T15 v15_;
- const T16 v16_;
- const T17 v17_;
- const T18 v18_;
- const T19 v19_;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20>
-class ValueArray20 {
- public:
- ValueArray20(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
- T18 v18, T19 v19, T20 v20) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5),
- v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12),
- v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18),
- v19_(v19), v20_(v20) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
- static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
- static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
- static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
- static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
- static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray20& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
- const T4 v4_;
- const T5 v5_;
- const T6 v6_;
- const T7 v7_;
- const T8 v8_;
- const T9 v9_;
- const T10 v10_;
- const T11 v11_;
- const T12 v12_;
- const T13 v13_;
- const T14 v14_;
- const T15 v15_;
- const T16 v16_;
- const T17 v17_;
- const T18 v18_;
- const T19 v19_;
- const T20 v20_;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21>
-class ValueArray21 {
- public:
- ValueArray21(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
- T18 v18, T19 v19, T20 v20, T21 v21) : v1_(v1), v2_(v2), v3_(v3), v4_(v4),
- v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11),
- v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17),
- v18_(v18), v19_(v19), v20_(v20), v21_(v21) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
- static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
- static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
- static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
- static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
- static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
- static_cast<T>(v21_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray21& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
- const T4 v4_;
- const T5 v5_;
- const T6 v6_;
- const T7 v7_;
- const T8 v8_;
- const T9 v9_;
- const T10 v10_;
- const T11 v11_;
- const T12 v12_;
- const T13 v13_;
- const T14 v14_;
- const T15 v15_;
- const T16 v16_;
- const T17 v17_;
- const T18 v18_;
- const T19 v19_;
- const T20 v20_;
- const T21 v21_;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22>
-class ValueArray22 {
- public:
- ValueArray22(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
- T18 v18, T19 v19, T20 v20, T21 v21, T22 v22) : v1_(v1), v2_(v2), v3_(v3),
- v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10),
- v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16),
- v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
- static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
- static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
- static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
- static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
- static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
- static_cast<T>(v21_), static_cast<T>(v22_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray22& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
- const T4 v4_;
- const T5 v5_;
- const T6 v6_;
- const T7 v7_;
- const T8 v8_;
- const T9 v9_;
- const T10 v10_;
- const T11 v11_;
- const T12 v12_;
- const T13 v13_;
- const T14 v14_;
- const T15 v15_;
- const T16 v16_;
- const T17 v17_;
- const T18 v18_;
- const T19 v19_;
- const T20 v20_;
- const T21 v21_;
- const T22 v22_;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23>
-class ValueArray23 {
- public:
- ValueArray23(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
- T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23) : v1_(v1), v2_(v2),
- v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10),
- v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16),
- v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22),
- v23_(v23) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
- static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
- static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
- static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
- static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
- static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
- static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray23& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
- const T4 v4_;
- const T5 v5_;
- const T6 v6_;
- const T7 v7_;
- const T8 v8_;
- const T9 v9_;
- const T10 v10_;
- const T11 v11_;
- const T12 v12_;
- const T13 v13_;
- const T14 v14_;
- const T15 v15_;
- const T16 v16_;
- const T17 v17_;
- const T18 v18_;
- const T19 v19_;
- const T20 v20_;
- const T21 v21_;
- const T22 v22_;
- const T23 v23_;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24>
-class ValueArray24 {
- public:
- ValueArray24(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
- T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24) : v1_(v1),
- v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9),
- v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15),
- v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21),
- v22_(v22), v23_(v23), v24_(v24) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
- static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
- static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
- static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
- static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
- static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
- static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
- static_cast<T>(v24_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray24& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
- const T4 v4_;
- const T5 v5_;
- const T6 v6_;
- const T7 v7_;
- const T8 v8_;
- const T9 v9_;
- const T10 v10_;
- const T11 v11_;
- const T12 v12_;
- const T13 v13_;
- const T14 v14_;
- const T15 v15_;
- const T16 v16_;
- const T17 v17_;
- const T18 v18_;
- const T19 v19_;
- const T20 v20_;
- const T21 v21_;
- const T22 v22_;
- const T23 v23_;
- const T24 v24_;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25>
-class ValueArray25 {
- public:
- ValueArray25(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
- T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24,
- T25 v25) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
- v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14),
- v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20),
- v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
- static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
- static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
- static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
- static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
- static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
- static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
- static_cast<T>(v24_), static_cast<T>(v25_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray25& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
- const T4 v4_;
- const T5 v5_;
- const T6 v6_;
- const T7 v7_;
- const T8 v8_;
- const T9 v9_;
- const T10 v10_;
- const T11 v11_;
- const T12 v12_;
- const T13 v13_;
- const T14 v14_;
- const T15 v15_;
- const T16 v16_;
- const T17 v17_;
- const T18 v18_;
- const T19 v19_;
- const T20 v20_;
- const T21 v21_;
- const T22 v22_;
- const T23 v23_;
- const T24 v24_;
- const T25 v25_;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26>
-class ValueArray26 {
- public:
- ValueArray26(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
- T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
- T26 v26) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
- v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14),
- v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20),
- v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
- static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
- static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
- static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
- static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
- static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
- static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
- static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray26& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
- const T4 v4_;
- const T5 v5_;
- const T6 v6_;
- const T7 v7_;
- const T8 v8_;
- const T9 v9_;
- const T10 v10_;
- const T11 v11_;
- const T12 v12_;
- const T13 v13_;
- const T14 v14_;
- const T15 v15_;
- const T16 v16_;
- const T17 v17_;
- const T18 v18_;
- const T19 v19_;
- const T20 v20_;
- const T21 v21_;
- const T22 v22_;
- const T23 v23_;
- const T24 v24_;
- const T25 v25_;
- const T26 v26_;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27>
-class ValueArray27 {
- public:
- ValueArray27(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
- T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
- T26 v26, T27 v27) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6),
- v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13),
- v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19),
- v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25),
- v26_(v26), v27_(v27) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
- static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
- static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
- static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
- static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
- static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
- static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
- static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
- static_cast<T>(v27_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray27& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
- const T4 v4_;
- const T5 v5_;
- const T6 v6_;
- const T7 v7_;
- const T8 v8_;
- const T9 v9_;
- const T10 v10_;
- const T11 v11_;
- const T12 v12_;
- const T13 v13_;
- const T14 v14_;
- const T15 v15_;
- const T16 v16_;
- const T17 v17_;
- const T18 v18_;
- const T19 v19_;
- const T20 v20_;
- const T21 v21_;
- const T22 v22_;
- const T23 v23_;
- const T24 v24_;
- const T25 v25_;
- const T26 v26_;
- const T27 v27_;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27, typename T28>
-class ValueArray28 {
- public:
- ValueArray28(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
- T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
- T26 v26, T27 v27, T28 v28) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5),
- v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12),
- v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18),
- v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24),
- v25_(v25), v26_(v26), v27_(v27), v28_(v28) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
- static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
- static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
- static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
- static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
- static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
- static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
- static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
- static_cast<T>(v27_), static_cast<T>(v28_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray28& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
- const T4 v4_;
- const T5 v5_;
- const T6 v6_;
- const T7 v7_;
- const T8 v8_;
- const T9 v9_;
- const T10 v10_;
- const T11 v11_;
- const T12 v12_;
- const T13 v13_;
- const T14 v14_;
- const T15 v15_;
- const T16 v16_;
- const T17 v17_;
- const T18 v18_;
- const T19 v19_;
- const T20 v20_;
- const T21 v21_;
- const T22 v22_;
- const T23 v23_;
- const T24 v24_;
- const T25 v25_;
- const T26 v26_;
- const T27 v27_;
- const T28 v28_;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27, typename T28, typename T29>
-class ValueArray29 {
- public:
- ValueArray29(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
- T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
- T26 v26, T27 v27, T28 v28, T29 v29) : v1_(v1), v2_(v2), v3_(v3), v4_(v4),
- v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11),
- v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17),
- v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23),
- v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
- static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
- static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
- static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
- static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
- static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
- static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
- static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
- static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray29& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
- const T4 v4_;
- const T5 v5_;
- const T6 v6_;
- const T7 v7_;
- const T8 v8_;
- const T9 v9_;
- const T10 v10_;
- const T11 v11_;
- const T12 v12_;
- const T13 v13_;
- const T14 v14_;
- const T15 v15_;
- const T16 v16_;
- const T17 v17_;
- const T18 v18_;
- const T19 v19_;
- const T20 v20_;
- const T21 v21_;
- const T22 v22_;
- const T23 v23_;
- const T24 v24_;
- const T25 v25_;
- const T26 v26_;
- const T27 v27_;
- const T28 v28_;
- const T29 v29_;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27, typename T28, typename T29, typename T30>
-class ValueArray30 {
- public:
- ValueArray30(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
- T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
- T26 v26, T27 v27, T28 v28, T29 v29, T30 v30) : v1_(v1), v2_(v2), v3_(v3),
- v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10),
- v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16),
- v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22),
- v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28),
- v29_(v29), v30_(v30) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
- static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
- static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
- static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
- static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
- static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
- static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
- static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
- static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
- static_cast<T>(v30_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray30& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
- const T4 v4_;
- const T5 v5_;
- const T6 v6_;
- const T7 v7_;
- const T8 v8_;
- const T9 v9_;
- const T10 v10_;
- const T11 v11_;
- const T12 v12_;
- const T13 v13_;
- const T14 v14_;
- const T15 v15_;
- const T16 v16_;
- const T17 v17_;
- const T18 v18_;
- const T19 v19_;
- const T20 v20_;
- const T21 v21_;
- const T22 v22_;
- const T23 v23_;
- const T24 v24_;
- const T25 v25_;
- const T26 v26_;
- const T27 v27_;
- const T28 v28_;
- const T29 v29_;
- const T30 v30_;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27, typename T28, typename T29, typename T30,
- typename T31>
-class ValueArray31 {
- public:
- ValueArray31(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
- T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
- T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31) : v1_(v1), v2_(v2),
- v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10),
- v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16),
- v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22),
- v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28),
- v29_(v29), v30_(v30), v31_(v31) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
- static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
- static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
- static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
- static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
- static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
- static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
- static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
- static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
- static_cast<T>(v30_), static_cast<T>(v31_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray31& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
- const T4 v4_;
- const T5 v5_;
- const T6 v6_;
- const T7 v7_;
- const T8 v8_;
- const T9 v9_;
- const T10 v10_;
- const T11 v11_;
- const T12 v12_;
- const T13 v13_;
- const T14 v14_;
- const T15 v15_;
- const T16 v16_;
- const T17 v17_;
- const T18 v18_;
- const T19 v19_;
- const T20 v20_;
- const T21 v21_;
- const T22 v22_;
- const T23 v23_;
- const T24 v24_;
- const T25 v25_;
- const T26 v26_;
- const T27 v27_;
- const T28 v28_;
- const T29 v29_;
- const T30 v30_;
- const T31 v31_;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27, typename T28, typename T29, typename T30,
- typename T31, typename T32>
-class ValueArray32 {
- public:
- ValueArray32(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
- T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
- T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32) : v1_(v1),
- v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9),
- v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15),
- v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21),
- v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27),
- v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
- static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
- static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
- static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
- static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
- static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
- static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
- static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
- static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
- static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray32& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
- const T4 v4_;
- const T5 v5_;
- const T6 v6_;
- const T7 v7_;
- const T8 v8_;
- const T9 v9_;
- const T10 v10_;
- const T11 v11_;
- const T12 v12_;
- const T13 v13_;
- const T14 v14_;
- const T15 v15_;
- const T16 v16_;
- const T17 v17_;
- const T18 v18_;
- const T19 v19_;
- const T20 v20_;
- const T21 v21_;
- const T22 v22_;
- const T23 v23_;
- const T24 v24_;
- const T25 v25_;
- const T26 v26_;
- const T27 v27_;
- const T28 v28_;
- const T29 v29_;
- const T30 v30_;
- const T31 v31_;
- const T32 v32_;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27, typename T28, typename T29, typename T30,
- typename T31, typename T32, typename T33>
-class ValueArray33 {
- public:
- ValueArray33(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
- T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
- T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32,
- T33 v33) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
- v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14),
- v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20),
- v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26),
- v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32),
- v33_(v33) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
- static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
- static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
- static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
- static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
- static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
- static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
- static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
- static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
- static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
- static_cast<T>(v33_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray33& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
- const T4 v4_;
- const T5 v5_;
- const T6 v6_;
- const T7 v7_;
- const T8 v8_;
- const T9 v9_;
- const T10 v10_;
- const T11 v11_;
- const T12 v12_;
- const T13 v13_;
- const T14 v14_;
- const T15 v15_;
- const T16 v16_;
- const T17 v17_;
- const T18 v18_;
- const T19 v19_;
- const T20 v20_;
- const T21 v21_;
- const T22 v22_;
- const T23 v23_;
- const T24 v24_;
- const T25 v25_;
- const T26 v26_;
- const T27 v27_;
- const T28 v28_;
- const T29 v29_;
- const T30 v30_;
- const T31 v31_;
- const T32 v32_;
- const T33 v33_;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27, typename T28, typename T29, typename T30,
- typename T31, typename T32, typename T33, typename T34>
-class ValueArray34 {
- public:
- ValueArray34(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
- T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
- T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
- T34 v34) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
- v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14),
- v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20),
- v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26),
- v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32),
- v33_(v33), v34_(v34) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
- static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
- static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
- static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
- static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
- static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
- static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
- static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
- static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
- static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
- static_cast<T>(v33_), static_cast<T>(v34_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray34& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
- const T4 v4_;
- const T5 v5_;
- const T6 v6_;
- const T7 v7_;
- const T8 v8_;
- const T9 v9_;
- const T10 v10_;
- const T11 v11_;
- const T12 v12_;
- const T13 v13_;
- const T14 v14_;
- const T15 v15_;
- const T16 v16_;
- const T17 v17_;
- const T18 v18_;
- const T19 v19_;
- const T20 v20_;
- const T21 v21_;
- const T22 v22_;
- const T23 v23_;
- const T24 v24_;
- const T25 v25_;
- const T26 v26_;
- const T27 v27_;
- const T28 v28_;
- const T29 v29_;
- const T30 v30_;
- const T31 v31_;
- const T32 v32_;
- const T33 v33_;
- const T34 v34_;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27, typename T28, typename T29, typename T30,
- typename T31, typename T32, typename T33, typename T34, typename T35>
-class ValueArray35 {
- public:
- ValueArray35(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
- T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
- T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
- T34 v34, T35 v35) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6),
- v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13),
- v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19),
- v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25),
- v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31),
- v32_(v32), v33_(v33), v34_(v34), v35_(v35) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
- static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
- static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
- static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
- static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
- static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
- static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
- static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
- static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
- static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
- static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray35& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
- const T4 v4_;
- const T5 v5_;
- const T6 v6_;
- const T7 v7_;
- const T8 v8_;
- const T9 v9_;
- const T10 v10_;
- const T11 v11_;
- const T12 v12_;
- const T13 v13_;
- const T14 v14_;
- const T15 v15_;
- const T16 v16_;
- const T17 v17_;
- const T18 v18_;
- const T19 v19_;
- const T20 v20_;
- const T21 v21_;
- const T22 v22_;
- const T23 v23_;
- const T24 v24_;
- const T25 v25_;
- const T26 v26_;
- const T27 v27_;
- const T28 v28_;
- const T29 v29_;
- const T30 v30_;
- const T31 v31_;
- const T32 v32_;
- const T33 v33_;
- const T34 v34_;
- const T35 v35_;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27, typename T28, typename T29, typename T30,
- typename T31, typename T32, typename T33, typename T34, typename T35,
- typename T36>
-class ValueArray36 {
- public:
- ValueArray36(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
- T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
- T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
- T34 v34, T35 v35, T36 v36) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5),
- v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12),
- v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18),
- v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24),
- v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30),
- v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35), v36_(v36) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
- static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
- static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
- static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
- static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
- static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
- static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
- static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
- static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
- static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
- static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
- static_cast<T>(v36_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray36& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
- const T4 v4_;
- const T5 v5_;
- const T6 v6_;
- const T7 v7_;
- const T8 v8_;
- const T9 v9_;
- const T10 v10_;
- const T11 v11_;
- const T12 v12_;
- const T13 v13_;
- const T14 v14_;
- const T15 v15_;
- const T16 v16_;
- const T17 v17_;
- const T18 v18_;
- const T19 v19_;
- const T20 v20_;
- const T21 v21_;
- const T22 v22_;
- const T23 v23_;
- const T24 v24_;
- const T25 v25_;
- const T26 v26_;
- const T27 v27_;
- const T28 v28_;
- const T29 v29_;
- const T30 v30_;
- const T31 v31_;
- const T32 v32_;
- const T33 v33_;
- const T34 v34_;
- const T35 v35_;
- const T36 v36_;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27, typename T28, typename T29, typename T30,
- typename T31, typename T32, typename T33, typename T34, typename T35,
- typename T36, typename T37>
-class ValueArray37 {
- public:
- ValueArray37(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
- T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
- T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
- T34 v34, T35 v35, T36 v36, T37 v37) : v1_(v1), v2_(v2), v3_(v3), v4_(v4),
- v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11),
- v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17),
- v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23),
- v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29),
- v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35),
- v36_(v36), v37_(v37) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
- static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
- static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
- static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
- static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
- static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
- static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
- static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
- static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
- static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
- static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
- static_cast<T>(v36_), static_cast<T>(v37_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray37& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
- const T4 v4_;
- const T5 v5_;
- const T6 v6_;
- const T7 v7_;
- const T8 v8_;
- const T9 v9_;
- const T10 v10_;
- const T11 v11_;
- const T12 v12_;
- const T13 v13_;
- const T14 v14_;
- const T15 v15_;
- const T16 v16_;
- const T17 v17_;
- const T18 v18_;
- const T19 v19_;
- const T20 v20_;
- const T21 v21_;
- const T22 v22_;
- const T23 v23_;
- const T24 v24_;
- const T25 v25_;
- const T26 v26_;
- const T27 v27_;
- const T28 v28_;
- const T29 v29_;
- const T30 v30_;
- const T31 v31_;
- const T32 v32_;
- const T33 v33_;
- const T34 v34_;
- const T35 v35_;
- const T36 v36_;
- const T37 v37_;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27, typename T28, typename T29, typename T30,
- typename T31, typename T32, typename T33, typename T34, typename T35,
- typename T36, typename T37, typename T38>
-class ValueArray38 {
- public:
- ValueArray38(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
- T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
- T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
- T34 v34, T35 v35, T36 v36, T37 v37, T38 v38) : v1_(v1), v2_(v2), v3_(v3),
- v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10),
- v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16),
- v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22),
- v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28),
- v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34),
- v35_(v35), v36_(v36), v37_(v37), v38_(v38) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
- static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
- static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
- static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
- static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
- static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
- static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
- static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
- static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
- static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
- static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
- static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray38& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
- const T4 v4_;
- const T5 v5_;
- const T6 v6_;
- const T7 v7_;
- const T8 v8_;
- const T9 v9_;
- const T10 v10_;
- const T11 v11_;
- const T12 v12_;
- const T13 v13_;
- const T14 v14_;
- const T15 v15_;
- const T16 v16_;
- const T17 v17_;
- const T18 v18_;
- const T19 v19_;
- const T20 v20_;
- const T21 v21_;
- const T22 v22_;
- const T23 v23_;
- const T24 v24_;
- const T25 v25_;
- const T26 v26_;
- const T27 v27_;
- const T28 v28_;
- const T29 v29_;
- const T30 v30_;
- const T31 v31_;
- const T32 v32_;
- const T33 v33_;
- const T34 v34_;
- const T35 v35_;
- const T36 v36_;
- const T37 v37_;
- const T38 v38_;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27, typename T28, typename T29, typename T30,
- typename T31, typename T32, typename T33, typename T34, typename T35,
- typename T36, typename T37, typename T38, typename T39>
-class ValueArray39 {
- public:
- ValueArray39(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
- T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
- T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
- T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39) : v1_(v1), v2_(v2),
- v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10),
- v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16),
- v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22),
- v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28),
- v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34),
- v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
- static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
- static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
- static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
- static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
- static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
- static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
- static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
- static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
- static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
- static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
- static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
- static_cast<T>(v39_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray39& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
- const T4 v4_;
- const T5 v5_;
- const T6 v6_;
- const T7 v7_;
- const T8 v8_;
- const T9 v9_;
- const T10 v10_;
- const T11 v11_;
- const T12 v12_;
- const T13 v13_;
- const T14 v14_;
- const T15 v15_;
- const T16 v16_;
- const T17 v17_;
- const T18 v18_;
- const T19 v19_;
- const T20 v20_;
- const T21 v21_;
- const T22 v22_;
- const T23 v23_;
- const T24 v24_;
- const T25 v25_;
- const T26 v26_;
- const T27 v27_;
- const T28 v28_;
- const T29 v29_;
- const T30 v30_;
- const T31 v31_;
- const T32 v32_;
- const T33 v33_;
- const T34 v34_;
- const T35 v35_;
- const T36 v36_;
- const T37 v37_;
- const T38 v38_;
- const T39 v39_;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27, typename T28, typename T29, typename T30,
- typename T31, typename T32, typename T33, typename T34, typename T35,
- typename T36, typename T37, typename T38, typename T39, typename T40>
-class ValueArray40 {
- public:
- ValueArray40(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
- T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
- T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
- T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40) : v1_(v1),
- v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9),
- v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15),
- v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21),
- v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27),
- v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33),
- v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39),
- v40_(v40) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
- static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
- static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
- static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
- static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
- static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
- static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
- static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
- static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
- static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
- static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
- static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
- static_cast<T>(v39_), static_cast<T>(v40_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray40& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
- const T4 v4_;
- const T5 v5_;
- const T6 v6_;
- const T7 v7_;
- const T8 v8_;
- const T9 v9_;
- const T10 v10_;
- const T11 v11_;
- const T12 v12_;
- const T13 v13_;
- const T14 v14_;
- const T15 v15_;
- const T16 v16_;
- const T17 v17_;
- const T18 v18_;
- const T19 v19_;
- const T20 v20_;
- const T21 v21_;
- const T22 v22_;
- const T23 v23_;
- const T24 v24_;
- const T25 v25_;
- const T26 v26_;
- const T27 v27_;
- const T28 v28_;
- const T29 v29_;
- const T30 v30_;
- const T31 v31_;
- const T32 v32_;
- const T33 v33_;
- const T34 v34_;
- const T35 v35_;
- const T36 v36_;
- const T37 v37_;
- const T38 v38_;
- const T39 v39_;
- const T40 v40_;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27, typename T28, typename T29, typename T30,
- typename T31, typename T32, typename T33, typename T34, typename T35,
- typename T36, typename T37, typename T38, typename T39, typename T40,
- typename T41>
-class ValueArray41 {
- public:
- ValueArray41(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
- T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
- T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
- T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40,
- T41 v41) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
- v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14),
- v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20),
- v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26),
- v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32),
- v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38),
- v39_(v39), v40_(v40), v41_(v41) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
- static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
- static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
- static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
- static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
- static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
- static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
- static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
- static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
- static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
- static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
- static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
- static_cast<T>(v39_), static_cast<T>(v40_), static_cast<T>(v41_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray41& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
- const T4 v4_;
- const T5 v5_;
- const T6 v6_;
- const T7 v7_;
- const T8 v8_;
- const T9 v9_;
- const T10 v10_;
- const T11 v11_;
- const T12 v12_;
- const T13 v13_;
- const T14 v14_;
- const T15 v15_;
- const T16 v16_;
- const T17 v17_;
- const T18 v18_;
- const T19 v19_;
- const T20 v20_;
- const T21 v21_;
- const T22 v22_;
- const T23 v23_;
- const T24 v24_;
- const T25 v25_;
- const T26 v26_;
- const T27 v27_;
- const T28 v28_;
- const T29 v29_;
- const T30 v30_;
- const T31 v31_;
- const T32 v32_;
- const T33 v33_;
- const T34 v34_;
- const T35 v35_;
- const T36 v36_;
- const T37 v37_;
- const T38 v38_;
- const T39 v39_;
- const T40 v40_;
- const T41 v41_;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27, typename T28, typename T29, typename T30,
- typename T31, typename T32, typename T33, typename T34, typename T35,
- typename T36, typename T37, typename T38, typename T39, typename T40,
- typename T41, typename T42>
-class ValueArray42 {
- public:
- ValueArray42(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
- T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
- T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
- T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
- T42 v42) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
- v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14),
- v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20),
- v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26),
- v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32),
- v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38),
- v39_(v39), v40_(v40), v41_(v41), v42_(v42) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
- static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
- static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
- static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
- static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
- static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
- static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
- static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
- static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
- static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
- static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
- static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
- static_cast<T>(v39_), static_cast<T>(v40_), static_cast<T>(v41_),
- static_cast<T>(v42_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray42& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
- const T4 v4_;
- const T5 v5_;
- const T6 v6_;
- const T7 v7_;
- const T8 v8_;
- const T9 v9_;
- const T10 v10_;
- const T11 v11_;
- const T12 v12_;
- const T13 v13_;
- const T14 v14_;
- const T15 v15_;
- const T16 v16_;
- const T17 v17_;
- const T18 v18_;
- const T19 v19_;
- const T20 v20_;
- const T21 v21_;
- const T22 v22_;
- const T23 v23_;
- const T24 v24_;
- const T25 v25_;
- const T26 v26_;
- const T27 v27_;
- const T28 v28_;
- const T29 v29_;
- const T30 v30_;
- const T31 v31_;
- const T32 v32_;
- const T33 v33_;
- const T34 v34_;
- const T35 v35_;
- const T36 v36_;
- const T37 v37_;
- const T38 v38_;
- const T39 v39_;
- const T40 v40_;
- const T41 v41_;
- const T42 v42_;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27, typename T28, typename T29, typename T30,
- typename T31, typename T32, typename T33, typename T34, typename T35,
- typename T36, typename T37, typename T38, typename T39, typename T40,
- typename T41, typename T42, typename T43>
-class ValueArray43 {
- public:
- ValueArray43(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
- T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
- T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
- T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
- T42 v42, T43 v43) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6),
- v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13),
- v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19),
- v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25),
- v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31),
- v32_(v32), v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37),
- v38_(v38), v39_(v39), v40_(v40), v41_(v41), v42_(v42), v43_(v43) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
- static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
- static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
- static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
- static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
- static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
- static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
- static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
- static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
- static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
- static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
- static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
- static_cast<T>(v39_), static_cast<T>(v40_), static_cast<T>(v41_),
- static_cast<T>(v42_), static_cast<T>(v43_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray43& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
- const T4 v4_;
- const T5 v5_;
- const T6 v6_;
- const T7 v7_;
- const T8 v8_;
- const T9 v9_;
- const T10 v10_;
- const T11 v11_;
- const T12 v12_;
- const T13 v13_;
- const T14 v14_;
- const T15 v15_;
- const T16 v16_;
- const T17 v17_;
- const T18 v18_;
- const T19 v19_;
- const T20 v20_;
- const T21 v21_;
- const T22 v22_;
- const T23 v23_;
- const T24 v24_;
- const T25 v25_;
- const T26 v26_;
- const T27 v27_;
- const T28 v28_;
- const T29 v29_;
- const T30 v30_;
- const T31 v31_;
- const T32 v32_;
- const T33 v33_;
- const T34 v34_;
- const T35 v35_;
- const T36 v36_;
- const T37 v37_;
- const T38 v38_;
- const T39 v39_;
- const T40 v40_;
- const T41 v41_;
- const T42 v42_;
- const T43 v43_;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27, typename T28, typename T29, typename T30,
- typename T31, typename T32, typename T33, typename T34, typename T35,
- typename T36, typename T37, typename T38, typename T39, typename T40,
- typename T41, typename T42, typename T43, typename T44>
-class ValueArray44 {
- public:
- ValueArray44(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
- T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
- T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
- T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
- T42 v42, T43 v43, T44 v44) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5),
- v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12),
- v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18),
- v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24),
- v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30),
- v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35), v36_(v36),
- v37_(v37), v38_(v38), v39_(v39), v40_(v40), v41_(v41), v42_(v42),
- v43_(v43), v44_(v44) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
- static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
- static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
- static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
- static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
- static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
- static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
- static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
- static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
- static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
- static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
- static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
- static_cast<T>(v39_), static_cast<T>(v40_), static_cast<T>(v41_),
- static_cast<T>(v42_), static_cast<T>(v43_), static_cast<T>(v44_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray44& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
- const T4 v4_;
- const T5 v5_;
- const T6 v6_;
- const T7 v7_;
- const T8 v8_;
- const T9 v9_;
- const T10 v10_;
- const T11 v11_;
- const T12 v12_;
- const T13 v13_;
- const T14 v14_;
- const T15 v15_;
- const T16 v16_;
- const T17 v17_;
- const T18 v18_;
- const T19 v19_;
- const T20 v20_;
- const T21 v21_;
- const T22 v22_;
- const T23 v23_;
- const T24 v24_;
- const T25 v25_;
- const T26 v26_;
- const T27 v27_;
- const T28 v28_;
- const T29 v29_;
- const T30 v30_;
- const T31 v31_;
- const T32 v32_;
- const T33 v33_;
- const T34 v34_;
- const T35 v35_;
- const T36 v36_;
- const T37 v37_;
- const T38 v38_;
- const T39 v39_;
- const T40 v40_;
- const T41 v41_;
- const T42 v42_;
- const T43 v43_;
- const T44 v44_;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27, typename T28, typename T29, typename T30,
- typename T31, typename T32, typename T33, typename T34, typename T35,
- typename T36, typename T37, typename T38, typename T39, typename T40,
- typename T41, typename T42, typename T43, typename T44, typename T45>
-class ValueArray45 {
- public:
- ValueArray45(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
- T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
- T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
- T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
- T42 v42, T43 v43, T44 v44, T45 v45) : v1_(v1), v2_(v2), v3_(v3), v4_(v4),
- v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11),
- v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17),
- v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23),
- v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29),
- v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35),
- v36_(v36), v37_(v37), v38_(v38), v39_(v39), v40_(v40), v41_(v41),
- v42_(v42), v43_(v43), v44_(v44), v45_(v45) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
- static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
- static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
- static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
- static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
- static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
- static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
- static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
- static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
- static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
- static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
- static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
- static_cast<T>(v39_), static_cast<T>(v40_), static_cast<T>(v41_),
- static_cast<T>(v42_), static_cast<T>(v43_), static_cast<T>(v44_),
- static_cast<T>(v45_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray45& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
- const T4 v4_;
- const T5 v5_;
- const T6 v6_;
- const T7 v7_;
- const T8 v8_;
- const T9 v9_;
- const T10 v10_;
- const T11 v11_;
- const T12 v12_;
- const T13 v13_;
- const T14 v14_;
- const T15 v15_;
- const T16 v16_;
- const T17 v17_;
- const T18 v18_;
- const T19 v19_;
- const T20 v20_;
- const T21 v21_;
- const T22 v22_;
- const T23 v23_;
- const T24 v24_;
- const T25 v25_;
- const T26 v26_;
- const T27 v27_;
- const T28 v28_;
- const T29 v29_;
- const T30 v30_;
- const T31 v31_;
- const T32 v32_;
- const T33 v33_;
- const T34 v34_;
- const T35 v35_;
- const T36 v36_;
- const T37 v37_;
- const T38 v38_;
- const T39 v39_;
- const T40 v40_;
- const T41 v41_;
- const T42 v42_;
- const T43 v43_;
- const T44 v44_;
- const T45 v45_;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27, typename T28, typename T29, typename T30,
- typename T31, typename T32, typename T33, typename T34, typename T35,
- typename T36, typename T37, typename T38, typename T39, typename T40,
- typename T41, typename T42, typename T43, typename T44, typename T45,
- typename T46>
-class ValueArray46 {
- public:
- ValueArray46(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
- T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
- T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
- T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
- T42 v42, T43 v43, T44 v44, T45 v45, T46 v46) : v1_(v1), v2_(v2), v3_(v3),
- v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10),
- v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16),
- v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22),
- v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28),
- v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34),
- v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39), v40_(v40),
- v41_(v41), v42_(v42), v43_(v43), v44_(v44), v45_(v45), v46_(v46) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
- static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
- static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
- static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
- static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
- static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
- static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
- static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
- static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
- static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
- static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
- static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
- static_cast<T>(v39_), static_cast<T>(v40_), static_cast<T>(v41_),
- static_cast<T>(v42_), static_cast<T>(v43_), static_cast<T>(v44_),
- static_cast<T>(v45_), static_cast<T>(v46_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray46& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
- const T4 v4_;
- const T5 v5_;
- const T6 v6_;
- const T7 v7_;
- const T8 v8_;
- const T9 v9_;
- const T10 v10_;
- const T11 v11_;
- const T12 v12_;
- const T13 v13_;
- const T14 v14_;
- const T15 v15_;
- const T16 v16_;
- const T17 v17_;
- const T18 v18_;
- const T19 v19_;
- const T20 v20_;
- const T21 v21_;
- const T22 v22_;
- const T23 v23_;
- const T24 v24_;
- const T25 v25_;
- const T26 v26_;
- const T27 v27_;
- const T28 v28_;
- const T29 v29_;
- const T30 v30_;
- const T31 v31_;
- const T32 v32_;
- const T33 v33_;
- const T34 v34_;
- const T35 v35_;
- const T36 v36_;
- const T37 v37_;
- const T38 v38_;
- const T39 v39_;
- const T40 v40_;
- const T41 v41_;
- const T42 v42_;
- const T43 v43_;
- const T44 v44_;
- const T45 v45_;
- const T46 v46_;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27, typename T28, typename T29, typename T30,
- typename T31, typename T32, typename T33, typename T34, typename T35,
- typename T36, typename T37, typename T38, typename T39, typename T40,
- typename T41, typename T42, typename T43, typename T44, typename T45,
- typename T46, typename T47>
-class ValueArray47 {
- public:
- ValueArray47(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
- T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
- T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
- T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
- T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47) : v1_(v1), v2_(v2),
- v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10),
- v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16),
- v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22),
- v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28),
- v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34),
- v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39), v40_(v40),
- v41_(v41), v42_(v42), v43_(v43), v44_(v44), v45_(v45), v46_(v46),
- v47_(v47) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
- static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
- static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
- static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
- static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
- static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
- static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
- static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
- static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
- static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
- static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
- static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
- static_cast<T>(v39_), static_cast<T>(v40_), static_cast<T>(v41_),
- static_cast<T>(v42_), static_cast<T>(v43_), static_cast<T>(v44_),
- static_cast<T>(v45_), static_cast<T>(v46_), static_cast<T>(v47_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray47& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
- const T4 v4_;
- const T5 v5_;
- const T6 v6_;
- const T7 v7_;
- const T8 v8_;
- const T9 v9_;
- const T10 v10_;
- const T11 v11_;
- const T12 v12_;
- const T13 v13_;
- const T14 v14_;
- const T15 v15_;
- const T16 v16_;
- const T17 v17_;
- const T18 v18_;
- const T19 v19_;
- const T20 v20_;
- const T21 v21_;
- const T22 v22_;
- const T23 v23_;
- const T24 v24_;
- const T25 v25_;
- const T26 v26_;
- const T27 v27_;
- const T28 v28_;
- const T29 v29_;
- const T30 v30_;
- const T31 v31_;
- const T32 v32_;
- const T33 v33_;
- const T34 v34_;
- const T35 v35_;
- const T36 v36_;
- const T37 v37_;
- const T38 v38_;
- const T39 v39_;
- const T40 v40_;
- const T41 v41_;
- const T42 v42_;
- const T43 v43_;
- const T44 v44_;
- const T45 v45_;
- const T46 v46_;
- const T47 v47_;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27, typename T28, typename T29, typename T30,
- typename T31, typename T32, typename T33, typename T34, typename T35,
- typename T36, typename T37, typename T38, typename T39, typename T40,
- typename T41, typename T42, typename T43, typename T44, typename T45,
- typename T46, typename T47, typename T48>
-class ValueArray48 {
- public:
- ValueArray48(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
- T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
- T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
- T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
- T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47, T48 v48) : v1_(v1),
- v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9),
- v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15),
- v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21),
- v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27),
- v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33),
- v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39),
- v40_(v40), v41_(v41), v42_(v42), v43_(v43), v44_(v44), v45_(v45),
- v46_(v46), v47_(v47), v48_(v48) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
- static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
- static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
- static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
- static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
- static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
- static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
- static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
- static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
- static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
- static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
- static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
- static_cast<T>(v39_), static_cast<T>(v40_), static_cast<T>(v41_),
- static_cast<T>(v42_), static_cast<T>(v43_), static_cast<T>(v44_),
- static_cast<T>(v45_), static_cast<T>(v46_), static_cast<T>(v47_),
- static_cast<T>(v48_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray48& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
- const T4 v4_;
- const T5 v5_;
- const T6 v6_;
- const T7 v7_;
- const T8 v8_;
- const T9 v9_;
- const T10 v10_;
- const T11 v11_;
- const T12 v12_;
- const T13 v13_;
- const T14 v14_;
- const T15 v15_;
- const T16 v16_;
- const T17 v17_;
- const T18 v18_;
- const T19 v19_;
- const T20 v20_;
- const T21 v21_;
- const T22 v22_;
- const T23 v23_;
- const T24 v24_;
- const T25 v25_;
- const T26 v26_;
- const T27 v27_;
- const T28 v28_;
- const T29 v29_;
- const T30 v30_;
- const T31 v31_;
- const T32 v32_;
- const T33 v33_;
- const T34 v34_;
- const T35 v35_;
- const T36 v36_;
- const T37 v37_;
- const T38 v38_;
- const T39 v39_;
- const T40 v40_;
- const T41 v41_;
- const T42 v42_;
- const T43 v43_;
- const T44 v44_;
- const T45 v45_;
- const T46 v46_;
- const T47 v47_;
- const T48 v48_;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27, typename T28, typename T29, typename T30,
- typename T31, typename T32, typename T33, typename T34, typename T35,
- typename T36, typename T37, typename T38, typename T39, typename T40,
- typename T41, typename T42, typename T43, typename T44, typename T45,
- typename T46, typename T47, typename T48, typename T49>
-class ValueArray49 {
- public:
- ValueArray49(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
- T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
- T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
- T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
- T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47, T48 v48,
- T49 v49) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
- v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14),
- v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20),
- v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26),
- v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32),
- v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38),
- v39_(v39), v40_(v40), v41_(v41), v42_(v42), v43_(v43), v44_(v44),
- v45_(v45), v46_(v46), v47_(v47), v48_(v48), v49_(v49) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
- static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
- static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
- static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
- static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
- static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
- static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
- static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
- static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
- static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
- static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
- static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
- static_cast<T>(v39_), static_cast<T>(v40_), static_cast<T>(v41_),
- static_cast<T>(v42_), static_cast<T>(v43_), static_cast<T>(v44_),
- static_cast<T>(v45_), static_cast<T>(v46_), static_cast<T>(v47_),
- static_cast<T>(v48_), static_cast<T>(v49_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray49& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
- const T4 v4_;
- const T5 v5_;
- const T6 v6_;
- const T7 v7_;
- const T8 v8_;
- const T9 v9_;
- const T10 v10_;
- const T11 v11_;
- const T12 v12_;
- const T13 v13_;
- const T14 v14_;
- const T15 v15_;
- const T16 v16_;
- const T17 v17_;
- const T18 v18_;
- const T19 v19_;
- const T20 v20_;
- const T21 v21_;
- const T22 v22_;
- const T23 v23_;
- const T24 v24_;
- const T25 v25_;
- const T26 v26_;
- const T27 v27_;
- const T28 v28_;
- const T29 v29_;
- const T30 v30_;
- const T31 v31_;
- const T32 v32_;
- const T33 v33_;
- const T34 v34_;
- const T35 v35_;
- const T36 v36_;
- const T37 v37_;
- const T38 v38_;
- const T39 v39_;
- const T40 v40_;
- const T41 v41_;
- const T42 v42_;
- const T43 v43_;
- const T44 v44_;
- const T45 v45_;
- const T46 v46_;
- const T47 v47_;
- const T48 v48_;
- const T49 v49_;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10,
- typename T11, typename T12, typename T13, typename T14, typename T15,
- typename T16, typename T17, typename T18, typename T19, typename T20,
- typename T21, typename T22, typename T23, typename T24, typename T25,
- typename T26, typename T27, typename T28, typename T29, typename T30,
- typename T31, typename T32, typename T33, typename T34, typename T35,
- typename T36, typename T37, typename T38, typename T39, typename T40,
- typename T41, typename T42, typename T43, typename T44, typename T45,
- typename T46, typename T47, typename T48, typename T49, typename T50>
-class ValueArray50 {
- public:
- ValueArray50(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
- T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
- T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
- T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
- T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
- T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47, T48 v48, T49 v49,
- T50 v50) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
- v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14),
- v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20),
- v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26),
- v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32),
- v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38),
- v39_(v39), v40_(v40), v41_(v41), v42_(v42), v43_(v43), v44_(v44),
- v45_(v45), v46_(v46), v47_(v47), v48_(v48), v49_(v49), v50_(v50) {}
-
- template <typename T>
- operator ParamGenerator<T>() const {
- const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
- static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
- static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
- static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
- static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
- static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
- static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
- static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
- static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
- static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
- static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
- static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
- static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
- static_cast<T>(v39_), static_cast<T>(v40_), static_cast<T>(v41_),
- static_cast<T>(v42_), static_cast<T>(v43_), static_cast<T>(v44_),
- static_cast<T>(v45_), static_cast<T>(v46_), static_cast<T>(v47_),
- static_cast<T>(v48_), static_cast<T>(v49_), static_cast<T>(v50_)};
- return ValuesIn(array);
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const ValueArray50& other);
-
- const T1 v1_;
- const T2 v2_;
- const T3 v3_;
- const T4 v4_;
- const T5 v5_;
- const T6 v6_;
- const T7 v7_;
- const T8 v8_;
- const T9 v9_;
- const T10 v10_;
- const T11 v11_;
- const T12 v12_;
- const T13 v13_;
- const T14 v14_;
- const T15 v15_;
- const T16 v16_;
- const T17 v17_;
- const T18 v18_;
- const T19 v19_;
- const T20 v20_;
- const T21 v21_;
- const T22 v22_;
- const T23 v23_;
- const T24 v24_;
- const T25 v25_;
- const T26 v26_;
- const T27 v27_;
- const T28 v28_;
- const T29 v29_;
- const T30 v30_;
- const T31 v31_;
- const T32 v32_;
- const T33 v33_;
- const T34 v34_;
- const T35 v35_;
- const T36 v36_;
- const T37 v37_;
- const T38 v38_;
- const T39 v39_;
- const T40 v40_;
- const T41 v41_;
- const T42 v42_;
- const T43 v43_;
- const T44 v44_;
- const T45 v45_;
- const T46 v46_;
- const T47 v47_;
- const T48 v48_;
- const T49 v49_;
- const T50 v50_;
-};
-
-# if GTEST_HAS_COMBINE
-// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
-//
-// Generates values from the Cartesian product of values produced
-// by the argument generators.
-//
-template <typename T1, typename T2>
-class CartesianProductGenerator2
- : public ParamGeneratorInterface< ::testing::tuple<T1, T2> > {
- public:
- typedef ::testing::tuple<T1, T2> ParamType;
-
- CartesianProductGenerator2(const ParamGenerator<T1>& g1,
- const ParamGenerator<T2>& g2)
- : g1_(g1), g2_(g2) {}
- virtual ~CartesianProductGenerator2() {}
-
- virtual ParamIteratorInterface<ParamType>* Begin() const {
- return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin());
- }
- virtual ParamIteratorInterface<ParamType>* End() const {
- return new Iterator(this, g1_, g1_.end(), g2_, g2_.end());
- }
-
- private:
- class Iterator : public ParamIteratorInterface<ParamType> {
- public:
- Iterator(const ParamGeneratorInterface<ParamType>* base,
- const ParamGenerator<T1>& g1,
- const typename ParamGenerator<T1>::iterator& current1,
- const ParamGenerator<T2>& g2,
- const typename ParamGenerator<T2>::iterator& current2)
- : base_(base),
- begin1_(g1.begin()), end1_(g1.end()), current1_(current1),
- begin2_(g2.begin()), end2_(g2.end()), current2_(current2) {
- ComputeCurrentValue();
- }
- virtual ~Iterator() {}
-
- virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const {
- return base_;
- }
- // Advance should not be called on beyond-of-range iterators
- // so no component iterators must be beyond end of range, either.
- virtual void Advance() {
- assert(!AtEnd());
- ++current2_;
- if (current2_ == end2_) {
- current2_ = begin2_;
- ++current1_;
- }
- ComputeCurrentValue();
- }
- virtual ParamIteratorInterface<ParamType>* Clone() const {
- return new Iterator(*this);
- }
- virtual const ParamType* Current() const { return &current_value_; }
- virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
- // Having the same base generator guarantees that the other
- // iterator is of the same type and we can downcast.
- GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
- << "The program attempted to compare iterators "
- << "from different generators." << std::endl;
- const Iterator* typed_other =
- CheckedDowncastToActualType<const Iterator>(&other);
- // We must report iterators equal if they both point beyond their
- // respective ranges. That can happen in a variety of fashions,
- // so we have to consult AtEnd().
- return (AtEnd() && typed_other->AtEnd()) ||
- (
- current1_ == typed_other->current1_ &&
- current2_ == typed_other->current2_);
- }
-
- private:
- Iterator(const Iterator& other)
- : base_(other.base_),
- begin1_(other.begin1_),
- end1_(other.end1_),
- current1_(other.current1_),
- begin2_(other.begin2_),
- end2_(other.end2_),
- current2_(other.current2_) {
- ComputeCurrentValue();
- }
-
- void ComputeCurrentValue() {
- if (!AtEnd())
- current_value_ = ParamType(*current1_, *current2_);
- }
- bool AtEnd() const {
- // We must report iterator past the end of the range when either of the
- // component iterators has reached the end of its range.
- return
- current1_ == end1_ ||
- current2_ == end2_;
- }
-
- // No implementation - assignment is unsupported.
- void operator=(const Iterator& other);
-
- const ParamGeneratorInterface<ParamType>* const base_;
- // begin[i]_ and end[i]_ define the i-th range that Iterator traverses.
- // current[i]_ is the actual traversing iterator.
- const typename ParamGenerator<T1>::iterator begin1_;
- const typename ParamGenerator<T1>::iterator end1_;
- typename ParamGenerator<T1>::iterator current1_;
- const typename ParamGenerator<T2>::iterator begin2_;
- const typename ParamGenerator<T2>::iterator end2_;
- typename ParamGenerator<T2>::iterator current2_;
- ParamType current_value_;
- }; // class CartesianProductGenerator2::Iterator
-
- // No implementation - assignment is unsupported.
- void operator=(const CartesianProductGenerator2& other);
-
- const ParamGenerator<T1> g1_;
- const ParamGenerator<T2> g2_;
-}; // class CartesianProductGenerator2
-
-
-template <typename T1, typename T2, typename T3>
-class CartesianProductGenerator3
- : public ParamGeneratorInterface< ::testing::tuple<T1, T2, T3> > {
- public:
- typedef ::testing::tuple<T1, T2, T3> ParamType;
-
- CartesianProductGenerator3(const ParamGenerator<T1>& g1,
- const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3)
- : g1_(g1), g2_(g2), g3_(g3) {}
- virtual ~CartesianProductGenerator3() {}
-
- virtual ParamIteratorInterface<ParamType>* Begin() const {
- return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_,
- g3_.begin());
- }
- virtual ParamIteratorInterface<ParamType>* End() const {
- return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end());
- }
-
- private:
- class Iterator : public ParamIteratorInterface<ParamType> {
- public:
- Iterator(const ParamGeneratorInterface<ParamType>* base,
- const ParamGenerator<T1>& g1,
- const typename ParamGenerator<T1>::iterator& current1,
- const ParamGenerator<T2>& g2,
- const typename ParamGenerator<T2>::iterator& current2,
- const ParamGenerator<T3>& g3,
- const typename ParamGenerator<T3>::iterator& current3)
- : base_(base),
- begin1_(g1.begin()), end1_(g1.end()), current1_(current1),
- begin2_(g2.begin()), end2_(g2.end()), current2_(current2),
- begin3_(g3.begin()), end3_(g3.end()), current3_(current3) {
- ComputeCurrentValue();
- }
- virtual ~Iterator() {}
-
- virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const {
- return base_;
- }
- // Advance should not be called on beyond-of-range iterators
- // so no component iterators must be beyond end of range, either.
- virtual void Advance() {
- assert(!AtEnd());
- ++current3_;
- if (current3_ == end3_) {
- current3_ = begin3_;
- ++current2_;
- }
- if (current2_ == end2_) {
- current2_ = begin2_;
- ++current1_;
- }
- ComputeCurrentValue();
- }
- virtual ParamIteratorInterface<ParamType>* Clone() const {
- return new Iterator(*this);
- }
- virtual const ParamType* Current() const { return &current_value_; }
- virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
- // Having the same base generator guarantees that the other
- // iterator is of the same type and we can downcast.
- GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
- << "The program attempted to compare iterators "
- << "from different generators." << std::endl;
- const Iterator* typed_other =
- CheckedDowncastToActualType<const Iterator>(&other);
- // We must report iterators equal if they both point beyond their
- // respective ranges. That can happen in a variety of fashions,
- // so we have to consult AtEnd().
- return (AtEnd() && typed_other->AtEnd()) ||
- (
- current1_ == typed_other->current1_ &&
- current2_ == typed_other->current2_ &&
- current3_ == typed_other->current3_);
- }
-
- private:
- Iterator(const Iterator& other)
- : base_(other.base_),
- begin1_(other.begin1_),
- end1_(other.end1_),
- current1_(other.current1_),
- begin2_(other.begin2_),
- end2_(other.end2_),
- current2_(other.current2_),
- begin3_(other.begin3_),
- end3_(other.end3_),
- current3_(other.current3_) {
- ComputeCurrentValue();
- }
-
- void ComputeCurrentValue() {
- if (!AtEnd())
- current_value_ = ParamType(*current1_, *current2_, *current3_);
- }
- bool AtEnd() const {
- // We must report iterator past the end of the range when either of the
- // component iterators has reached the end of its range.
- return
- current1_ == end1_ ||
- current2_ == end2_ ||
- current3_ == end3_;
- }
-
- // No implementation - assignment is unsupported.
- void operator=(const Iterator& other);
-
- const ParamGeneratorInterface<ParamType>* const base_;
- // begin[i]_ and end[i]_ define the i-th range that Iterator traverses.
- // current[i]_ is the actual traversing iterator.
- const typename ParamGenerator<T1>::iterator begin1_;
- const typename ParamGenerator<T1>::iterator end1_;
- typename ParamGenerator<T1>::iterator current1_;
- const typename ParamGenerator<T2>::iterator begin2_;
- const typename ParamGenerator<T2>::iterator end2_;
- typename ParamGenerator<T2>::iterator current2_;
- const typename ParamGenerator<T3>::iterator begin3_;
- const typename ParamGenerator<T3>::iterator end3_;
- typename ParamGenerator<T3>::iterator current3_;
- ParamType current_value_;
- }; // class CartesianProductGenerator3::Iterator
-
- // No implementation - assignment is unsupported.
- void operator=(const CartesianProductGenerator3& other);
-
- const ParamGenerator<T1> g1_;
- const ParamGenerator<T2> g2_;
- const ParamGenerator<T3> g3_;
-}; // class CartesianProductGenerator3
-
-
-template <typename T1, typename T2, typename T3, typename T4>
-class CartesianProductGenerator4
- : public ParamGeneratorInterface< ::testing::tuple<T1, T2, T3, T4> > {
- public:
- typedef ::testing::tuple<T1, T2, T3, T4> ParamType;
-
- CartesianProductGenerator4(const ParamGenerator<T1>& g1,
- const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3,
- const ParamGenerator<T4>& g4)
- : g1_(g1), g2_(g2), g3_(g3), g4_(g4) {}
- virtual ~CartesianProductGenerator4() {}
-
- virtual ParamIteratorInterface<ParamType>* Begin() const {
- return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_,
- g3_.begin(), g4_, g4_.begin());
- }
- virtual ParamIteratorInterface<ParamType>* End() const {
- return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(),
- g4_, g4_.end());
- }
-
- private:
- class Iterator : public ParamIteratorInterface<ParamType> {
- public:
- Iterator(const ParamGeneratorInterface<ParamType>* base,
- const ParamGenerator<T1>& g1,
- const typename ParamGenerator<T1>::iterator& current1,
- const ParamGenerator<T2>& g2,
- const typename ParamGenerator<T2>::iterator& current2,
- const ParamGenerator<T3>& g3,
- const typename ParamGenerator<T3>::iterator& current3,
- const ParamGenerator<T4>& g4,
- const typename ParamGenerator<T4>::iterator& current4)
- : base_(base),
- begin1_(g1.begin()), end1_(g1.end()), current1_(current1),
- begin2_(g2.begin()), end2_(g2.end()), current2_(current2),
- begin3_(g3.begin()), end3_(g3.end()), current3_(current3),
- begin4_(g4.begin()), end4_(g4.end()), current4_(current4) {
- ComputeCurrentValue();
- }
- virtual ~Iterator() {}
-
- virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const {
- return base_;
- }
- // Advance should not be called on beyond-of-range iterators
- // so no component iterators must be beyond end of range, either.
- virtual void Advance() {
- assert(!AtEnd());
- ++current4_;
- if (current4_ == end4_) {
- current4_ = begin4_;
- ++current3_;
- }
- if (current3_ == end3_) {
- current3_ = begin3_;
- ++current2_;
- }
- if (current2_ == end2_) {
- current2_ = begin2_;
- ++current1_;
- }
- ComputeCurrentValue();
- }
- virtual ParamIteratorInterface<ParamType>* Clone() const {
- return new Iterator(*this);
- }
- virtual const ParamType* Current() const { return &current_value_; }
- virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
- // Having the same base generator guarantees that the other
- // iterator is of the same type and we can downcast.
- GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
- << "The program attempted to compare iterators "
- << "from different generators." << std::endl;
- const Iterator* typed_other =
- CheckedDowncastToActualType<const Iterator>(&other);
- // We must report iterators equal if they both point beyond their
- // respective ranges. That can happen in a variety of fashions,
- // so we have to consult AtEnd().
- return (AtEnd() && typed_other->AtEnd()) ||
- (
- current1_ == typed_other->current1_ &&
- current2_ == typed_other->current2_ &&
- current3_ == typed_other->current3_ &&
- current4_ == typed_other->current4_);
- }
-
- private:
- Iterator(const Iterator& other)
- : base_(other.base_),
- begin1_(other.begin1_),
- end1_(other.end1_),
- current1_(other.current1_),
- begin2_(other.begin2_),
- end2_(other.end2_),
- current2_(other.current2_),
- begin3_(other.begin3_),
- end3_(other.end3_),
- current3_(other.current3_),
- begin4_(other.begin4_),
- end4_(other.end4_),
- current4_(other.current4_) {
- ComputeCurrentValue();
- }
-
- void ComputeCurrentValue() {
- if (!AtEnd())
- current_value_ = ParamType(*current1_, *current2_, *current3_,
- *current4_);
- }
- bool AtEnd() const {
- // We must report iterator past the end of the range when either of the
- // component iterators has reached the end of its range.
- return
- current1_ == end1_ ||
- current2_ == end2_ ||
- current3_ == end3_ ||
- current4_ == end4_;
- }
-
- // No implementation - assignment is unsupported.
- void operator=(const Iterator& other);
-
- const ParamGeneratorInterface<ParamType>* const base_;
- // begin[i]_ and end[i]_ define the i-th range that Iterator traverses.
- // current[i]_ is the actual traversing iterator.
- const typename ParamGenerator<T1>::iterator begin1_;
- const typename ParamGenerator<T1>::iterator end1_;
- typename ParamGenerator<T1>::iterator current1_;
- const typename ParamGenerator<T2>::iterator begin2_;
- const typename ParamGenerator<T2>::iterator end2_;
- typename ParamGenerator<T2>::iterator current2_;
- const typename ParamGenerator<T3>::iterator begin3_;
- const typename ParamGenerator<T3>::iterator end3_;
- typename ParamGenerator<T3>::iterator current3_;
- const typename ParamGenerator<T4>::iterator begin4_;
- const typename ParamGenerator<T4>::iterator end4_;
- typename ParamGenerator<T4>::iterator current4_;
- ParamType current_value_;
- }; // class CartesianProductGenerator4::Iterator
-
- // No implementation - assignment is unsupported.
- void operator=(const CartesianProductGenerator4& other);
-
- const ParamGenerator<T1> g1_;
- const ParamGenerator<T2> g2_;
- const ParamGenerator<T3> g3_;
- const ParamGenerator<T4> g4_;
-}; // class CartesianProductGenerator4
-
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5>
-class CartesianProductGenerator5
- : public ParamGeneratorInterface< ::testing::tuple<T1, T2, T3, T4, T5> > {
- public:
- typedef ::testing::tuple<T1, T2, T3, T4, T5> ParamType;
-
- CartesianProductGenerator5(const ParamGenerator<T1>& g1,
- const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3,
- const ParamGenerator<T4>& g4, const ParamGenerator<T5>& g5)
- : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5) {}
- virtual ~CartesianProductGenerator5() {}
-
- virtual ParamIteratorInterface<ParamType>* Begin() const {
- return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_,
- g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin());
- }
- virtual ParamIteratorInterface<ParamType>* End() const {
- return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(),
- g4_, g4_.end(), g5_, g5_.end());
- }
-
- private:
- class Iterator : public ParamIteratorInterface<ParamType> {
- public:
- Iterator(const ParamGeneratorInterface<ParamType>* base,
- const ParamGenerator<T1>& g1,
- const typename ParamGenerator<T1>::iterator& current1,
- const ParamGenerator<T2>& g2,
- const typename ParamGenerator<T2>::iterator& current2,
- const ParamGenerator<T3>& g3,
- const typename ParamGenerator<T3>::iterator& current3,
- const ParamGenerator<T4>& g4,
- const typename ParamGenerator<T4>::iterator& current4,
- const ParamGenerator<T5>& g5,
- const typename ParamGenerator<T5>::iterator& current5)
- : base_(base),
- begin1_(g1.begin()), end1_(g1.end()), current1_(current1),
- begin2_(g2.begin()), end2_(g2.end()), current2_(current2),
- begin3_(g3.begin()), end3_(g3.end()), current3_(current3),
- begin4_(g4.begin()), end4_(g4.end()), current4_(current4),
- begin5_(g5.begin()), end5_(g5.end()), current5_(current5) {
- ComputeCurrentValue();
- }
- virtual ~Iterator() {}
-
- virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const {
- return base_;
- }
- // Advance should not be called on beyond-of-range iterators
- // so no component iterators must be beyond end of range, either.
- virtual void Advance() {
- assert(!AtEnd());
- ++current5_;
- if (current5_ == end5_) {
- current5_ = begin5_;
- ++current4_;
- }
- if (current4_ == end4_) {
- current4_ = begin4_;
- ++current3_;
- }
- if (current3_ == end3_) {
- current3_ = begin3_;
- ++current2_;
- }
- if (current2_ == end2_) {
- current2_ = begin2_;
- ++current1_;
- }
- ComputeCurrentValue();
- }
- virtual ParamIteratorInterface<ParamType>* Clone() const {
- return new Iterator(*this);
- }
- virtual const ParamType* Current() const { return &current_value_; }
- virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
- // Having the same base generator guarantees that the other
- // iterator is of the same type and we can downcast.
- GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
- << "The program attempted to compare iterators "
- << "from different generators." << std::endl;
- const Iterator* typed_other =
- CheckedDowncastToActualType<const Iterator>(&other);
- // We must report iterators equal if they both point beyond their
- // respective ranges. That can happen in a variety of fashions,
- // so we have to consult AtEnd().
- return (AtEnd() && typed_other->AtEnd()) ||
- (
- current1_ == typed_other->current1_ &&
- current2_ == typed_other->current2_ &&
- current3_ == typed_other->current3_ &&
- current4_ == typed_other->current4_ &&
- current5_ == typed_other->current5_);
- }
-
- private:
- Iterator(const Iterator& other)
- : base_(other.base_),
- begin1_(other.begin1_),
- end1_(other.end1_),
- current1_(other.current1_),
- begin2_(other.begin2_),
- end2_(other.end2_),
- current2_(other.current2_),
- begin3_(other.begin3_),
- end3_(other.end3_),
- current3_(other.current3_),
- begin4_(other.begin4_),
- end4_(other.end4_),
- current4_(other.current4_),
- begin5_(other.begin5_),
- end5_(other.end5_),
- current5_(other.current5_) {
- ComputeCurrentValue();
- }
-
- void ComputeCurrentValue() {
- if (!AtEnd())
- current_value_ = ParamType(*current1_, *current2_, *current3_,
- *current4_, *current5_);
- }
- bool AtEnd() const {
- // We must report iterator past the end of the range when either of the
- // component iterators has reached the end of its range.
- return
- current1_ == end1_ ||
- current2_ == end2_ ||
- current3_ == end3_ ||
- current4_ == end4_ ||
- current5_ == end5_;
- }
-
- // No implementation - assignment is unsupported.
- void operator=(const Iterator& other);
-
- const ParamGeneratorInterface<ParamType>* const base_;
- // begin[i]_ and end[i]_ define the i-th range that Iterator traverses.
- // current[i]_ is the actual traversing iterator.
- const typename ParamGenerator<T1>::iterator begin1_;
- const typename ParamGenerator<T1>::iterator end1_;
- typename ParamGenerator<T1>::iterator current1_;
- const typename ParamGenerator<T2>::iterator begin2_;
- const typename ParamGenerator<T2>::iterator end2_;
- typename ParamGenerator<T2>::iterator current2_;
- const typename ParamGenerator<T3>::iterator begin3_;
- const typename ParamGenerator<T3>::iterator end3_;
- typename ParamGenerator<T3>::iterator current3_;
- const typename ParamGenerator<T4>::iterator begin4_;
- const typename ParamGenerator<T4>::iterator end4_;
- typename ParamGenerator<T4>::iterator current4_;
- const typename ParamGenerator<T5>::iterator begin5_;
- const typename ParamGenerator<T5>::iterator end5_;
- typename ParamGenerator<T5>::iterator current5_;
- ParamType current_value_;
- }; // class CartesianProductGenerator5::Iterator
-
- // No implementation - assignment is unsupported.
- void operator=(const CartesianProductGenerator5& other);
-
- const ParamGenerator<T1> g1_;
- const ParamGenerator<T2> g2_;
- const ParamGenerator<T3> g3_;
- const ParamGenerator<T4> g4_;
- const ParamGenerator<T5> g5_;
-}; // class CartesianProductGenerator5
-
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6>
-class CartesianProductGenerator6
- : public ParamGeneratorInterface< ::testing::tuple<T1, T2, T3, T4, T5,
- T6> > {
- public:
- typedef ::testing::tuple<T1, T2, T3, T4, T5, T6> ParamType;
-
- CartesianProductGenerator6(const ParamGenerator<T1>& g1,
- const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3,
- const ParamGenerator<T4>& g4, const ParamGenerator<T5>& g5,
- const ParamGenerator<T6>& g6)
- : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6) {}
- virtual ~CartesianProductGenerator6() {}
-
- virtual ParamIteratorInterface<ParamType>* Begin() const {
- return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_,
- g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin());
- }
- virtual ParamIteratorInterface<ParamType>* End() const {
- return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(),
- g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end());
- }
-
- private:
- class Iterator : public ParamIteratorInterface<ParamType> {
- public:
- Iterator(const ParamGeneratorInterface<ParamType>* base,
- const ParamGenerator<T1>& g1,
- const typename ParamGenerator<T1>::iterator& current1,
- const ParamGenerator<T2>& g2,
- const typename ParamGenerator<T2>::iterator& current2,
- const ParamGenerator<T3>& g3,
- const typename ParamGenerator<T3>::iterator& current3,
- const ParamGenerator<T4>& g4,
- const typename ParamGenerator<T4>::iterator& current4,
- const ParamGenerator<T5>& g5,
- const typename ParamGenerator<T5>::iterator& current5,
- const ParamGenerator<T6>& g6,
- const typename ParamGenerator<T6>::iterator& current6)
- : base_(base),
- begin1_(g1.begin()), end1_(g1.end()), current1_(current1),
- begin2_(g2.begin()), end2_(g2.end()), current2_(current2),
- begin3_(g3.begin()), end3_(g3.end()), current3_(current3),
- begin4_(g4.begin()), end4_(g4.end()), current4_(current4),
- begin5_(g5.begin()), end5_(g5.end()), current5_(current5),
- begin6_(g6.begin()), end6_(g6.end()), current6_(current6) {
- ComputeCurrentValue();
- }
- virtual ~Iterator() {}
-
- virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const {
- return base_;
- }
- // Advance should not be called on beyond-of-range iterators
- // so no component iterators must be beyond end of range, either.
- virtual void Advance() {
- assert(!AtEnd());
- ++current6_;
- if (current6_ == end6_) {
- current6_ = begin6_;
- ++current5_;
- }
- if (current5_ == end5_) {
- current5_ = begin5_;
- ++current4_;
- }
- if (current4_ == end4_) {
- current4_ = begin4_;
- ++current3_;
- }
- if (current3_ == end3_) {
- current3_ = begin3_;
- ++current2_;
- }
- if (current2_ == end2_) {
- current2_ = begin2_;
- ++current1_;
- }
- ComputeCurrentValue();
- }
- virtual ParamIteratorInterface<ParamType>* Clone() const {
- return new Iterator(*this);
- }
- virtual const ParamType* Current() const { return &current_value_; }
- virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
- // Having the same base generator guarantees that the other
- // iterator is of the same type and we can downcast.
- GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
- << "The program attempted to compare iterators "
- << "from different generators." << std::endl;
- const Iterator* typed_other =
- CheckedDowncastToActualType<const Iterator>(&other);
- // We must report iterators equal if they both point beyond their
- // respective ranges. That can happen in a variety of fashions,
- // so we have to consult AtEnd().
- return (AtEnd() && typed_other->AtEnd()) ||
- (
- current1_ == typed_other->current1_ &&
- current2_ == typed_other->current2_ &&
- current3_ == typed_other->current3_ &&
- current4_ == typed_other->current4_ &&
- current5_ == typed_other->current5_ &&
- current6_ == typed_other->current6_);
- }
-
- private:
- Iterator(const Iterator& other)
- : base_(other.base_),
- begin1_(other.begin1_),
- end1_(other.end1_),
- current1_(other.current1_),
- begin2_(other.begin2_),
- end2_(other.end2_),
- current2_(other.current2_),
- begin3_(other.begin3_),
- end3_(other.end3_),
- current3_(other.current3_),
- begin4_(other.begin4_),
- end4_(other.end4_),
- current4_(other.current4_),
- begin5_(other.begin5_),
- end5_(other.end5_),
- current5_(other.current5_),
- begin6_(other.begin6_),
- end6_(other.end6_),
- current6_(other.current6_) {
- ComputeCurrentValue();
- }
-
- void ComputeCurrentValue() {
- if (!AtEnd())
- current_value_ = ParamType(*current1_, *current2_, *current3_,
- *current4_, *current5_, *current6_);
- }
- bool AtEnd() const {
- // We must report iterator past the end of the range when either of the
- // component iterators has reached the end of its range.
- return
- current1_ == end1_ ||
- current2_ == end2_ ||
- current3_ == end3_ ||
- current4_ == end4_ ||
- current5_ == end5_ ||
- current6_ == end6_;
- }
-
- // No implementation - assignment is unsupported.
- void operator=(const Iterator& other);
-
- const ParamGeneratorInterface<ParamType>* const base_;
- // begin[i]_ and end[i]_ define the i-th range that Iterator traverses.
- // current[i]_ is the actual traversing iterator.
- const typename ParamGenerator<T1>::iterator begin1_;
- const typename ParamGenerator<T1>::iterator end1_;
- typename ParamGenerator<T1>::iterator current1_;
- const typename ParamGenerator<T2>::iterator begin2_;
- const typename ParamGenerator<T2>::iterator end2_;
- typename ParamGenerator<T2>::iterator current2_;
- const typename ParamGenerator<T3>::iterator begin3_;
- const typename ParamGenerator<T3>::iterator end3_;
- typename ParamGenerator<T3>::iterator current3_;
- const typename ParamGenerator<T4>::iterator begin4_;
- const typename ParamGenerator<T4>::iterator end4_;
- typename ParamGenerator<T4>::iterator current4_;
- const typename ParamGenerator<T5>::iterator begin5_;
- const typename ParamGenerator<T5>::iterator end5_;
- typename ParamGenerator<T5>::iterator current5_;
- const typename ParamGenerator<T6>::iterator begin6_;
- const typename ParamGenerator<T6>::iterator end6_;
- typename ParamGenerator<T6>::iterator current6_;
- ParamType current_value_;
- }; // class CartesianProductGenerator6::Iterator
-
- // No implementation - assignment is unsupported.
- void operator=(const CartesianProductGenerator6& other);
-
- const ParamGenerator<T1> g1_;
- const ParamGenerator<T2> g2_;
- const ParamGenerator<T3> g3_;
- const ParamGenerator<T4> g4_;
- const ParamGenerator<T5> g5_;
- const ParamGenerator<T6> g6_;
-}; // class CartesianProductGenerator6
-
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7>
-class CartesianProductGenerator7
- : public ParamGeneratorInterface< ::testing::tuple<T1, T2, T3, T4, T5, T6,
- T7> > {
- public:
- typedef ::testing::tuple<T1, T2, T3, T4, T5, T6, T7> ParamType;
-
- CartesianProductGenerator7(const ParamGenerator<T1>& g1,
- const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3,
- const ParamGenerator<T4>& g4, const ParamGenerator<T5>& g5,
- const ParamGenerator<T6>& g6, const ParamGenerator<T7>& g7)
- : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7) {}
- virtual ~CartesianProductGenerator7() {}
-
- virtual ParamIteratorInterface<ParamType>* Begin() const {
- return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_,
- g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_,
- g7_.begin());
- }
- virtual ParamIteratorInterface<ParamType>* End() const {
- return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(),
- g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end());
- }
-
- private:
- class Iterator : public ParamIteratorInterface<ParamType> {
- public:
- Iterator(const ParamGeneratorInterface<ParamType>* base,
- const ParamGenerator<T1>& g1,
- const typename ParamGenerator<T1>::iterator& current1,
- const ParamGenerator<T2>& g2,
- const typename ParamGenerator<T2>::iterator& current2,
- const ParamGenerator<T3>& g3,
- const typename ParamGenerator<T3>::iterator& current3,
- const ParamGenerator<T4>& g4,
- const typename ParamGenerator<T4>::iterator& current4,
- const ParamGenerator<T5>& g5,
- const typename ParamGenerator<T5>::iterator& current5,
- const ParamGenerator<T6>& g6,
- const typename ParamGenerator<T6>::iterator& current6,
- const ParamGenerator<T7>& g7,
- const typename ParamGenerator<T7>::iterator& current7)
- : base_(base),
- begin1_(g1.begin()), end1_(g1.end()), current1_(current1),
- begin2_(g2.begin()), end2_(g2.end()), current2_(current2),
- begin3_(g3.begin()), end3_(g3.end()), current3_(current3),
- begin4_(g4.begin()), end4_(g4.end()), current4_(current4),
- begin5_(g5.begin()), end5_(g5.end()), current5_(current5),
- begin6_(g6.begin()), end6_(g6.end()), current6_(current6),
- begin7_(g7.begin()), end7_(g7.end()), current7_(current7) {
- ComputeCurrentValue();
- }
- virtual ~Iterator() {}
-
- virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const {
- return base_;
- }
- // Advance should not be called on beyond-of-range iterators
- // so no component iterators must be beyond end of range, either.
- virtual void Advance() {
- assert(!AtEnd());
- ++current7_;
- if (current7_ == end7_) {
- current7_ = begin7_;
- ++current6_;
- }
- if (current6_ == end6_) {
- current6_ = begin6_;
- ++current5_;
- }
- if (current5_ == end5_) {
- current5_ = begin5_;
- ++current4_;
- }
- if (current4_ == end4_) {
- current4_ = begin4_;
- ++current3_;
- }
- if (current3_ == end3_) {
- current3_ = begin3_;
- ++current2_;
- }
- if (current2_ == end2_) {
- current2_ = begin2_;
- ++current1_;
- }
- ComputeCurrentValue();
- }
- virtual ParamIteratorInterface<ParamType>* Clone() const {
- return new Iterator(*this);
- }
- virtual const ParamType* Current() const { return &current_value_; }
- virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
- // Having the same base generator guarantees that the other
- // iterator is of the same type and we can downcast.
- GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
- << "The program attempted to compare iterators "
- << "from different generators." << std::endl;
- const Iterator* typed_other =
- CheckedDowncastToActualType<const Iterator>(&other);
- // We must report iterators equal if they both point beyond their
- // respective ranges. That can happen in a variety of fashions,
- // so we have to consult AtEnd().
- return (AtEnd() && typed_other->AtEnd()) ||
- (
- current1_ == typed_other->current1_ &&
- current2_ == typed_other->current2_ &&
- current3_ == typed_other->current3_ &&
- current4_ == typed_other->current4_ &&
- current5_ == typed_other->current5_ &&
- current6_ == typed_other->current6_ &&
- current7_ == typed_other->current7_);
- }
-
- private:
- Iterator(const Iterator& other)
- : base_(other.base_),
- begin1_(other.begin1_),
- end1_(other.end1_),
- current1_(other.current1_),
- begin2_(other.begin2_),
- end2_(other.end2_),
- current2_(other.current2_),
- begin3_(other.begin3_),
- end3_(other.end3_),
- current3_(other.current3_),
- begin4_(other.begin4_),
- end4_(other.end4_),
- current4_(other.current4_),
- begin5_(other.begin5_),
- end5_(other.end5_),
- current5_(other.current5_),
- begin6_(other.begin6_),
- end6_(other.end6_),
- current6_(other.current6_),
- begin7_(other.begin7_),
- end7_(other.end7_),
- current7_(other.current7_) {
- ComputeCurrentValue();
- }
-
- void ComputeCurrentValue() {
- if (!AtEnd())
- current_value_ = ParamType(*current1_, *current2_, *current3_,
- *current4_, *current5_, *current6_, *current7_);
- }
- bool AtEnd() const {
- // We must report iterator past the end of the range when either of the
- // component iterators has reached the end of its range.
- return
- current1_ == end1_ ||
- current2_ == end2_ ||
- current3_ == end3_ ||
- current4_ == end4_ ||
- current5_ == end5_ ||
- current6_ == end6_ ||
- current7_ == end7_;
- }
-
- // No implementation - assignment is unsupported.
- void operator=(const Iterator& other);
-
- const ParamGeneratorInterface<ParamType>* const base_;
- // begin[i]_ and end[i]_ define the i-th range that Iterator traverses.
- // current[i]_ is the actual traversing iterator.
- const typename ParamGenerator<T1>::iterator begin1_;
- const typename ParamGenerator<T1>::iterator end1_;
- typename ParamGenerator<T1>::iterator current1_;
- const typename ParamGenerator<T2>::iterator begin2_;
- const typename ParamGenerator<T2>::iterator end2_;
- typename ParamGenerator<T2>::iterator current2_;
- const typename ParamGenerator<T3>::iterator begin3_;
- const typename ParamGenerator<T3>::iterator end3_;
- typename ParamGenerator<T3>::iterator current3_;
- const typename ParamGenerator<T4>::iterator begin4_;
- const typename ParamGenerator<T4>::iterator end4_;
- typename ParamGenerator<T4>::iterator current4_;
- const typename ParamGenerator<T5>::iterator begin5_;
- const typename ParamGenerator<T5>::iterator end5_;
- typename ParamGenerator<T5>::iterator current5_;
- const typename ParamGenerator<T6>::iterator begin6_;
- const typename ParamGenerator<T6>::iterator end6_;
- typename ParamGenerator<T6>::iterator current6_;
- const typename ParamGenerator<T7>::iterator begin7_;
- const typename ParamGenerator<T7>::iterator end7_;
- typename ParamGenerator<T7>::iterator current7_;
- ParamType current_value_;
- }; // class CartesianProductGenerator7::Iterator
-
- // No implementation - assignment is unsupported.
- void operator=(const CartesianProductGenerator7& other);
-
- const ParamGenerator<T1> g1_;
- const ParamGenerator<T2> g2_;
- const ParamGenerator<T3> g3_;
- const ParamGenerator<T4> g4_;
- const ParamGenerator<T5> g5_;
- const ParamGenerator<T6> g6_;
- const ParamGenerator<T7> g7_;
-}; // class CartesianProductGenerator7
-
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8>
-class CartesianProductGenerator8
- : public ParamGeneratorInterface< ::testing::tuple<T1, T2, T3, T4, T5, T6,
- T7, T8> > {
- public:
- typedef ::testing::tuple<T1, T2, T3, T4, T5, T6, T7, T8> ParamType;
-
- CartesianProductGenerator8(const ParamGenerator<T1>& g1,
- const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3,
- const ParamGenerator<T4>& g4, const ParamGenerator<T5>& g5,
- const ParamGenerator<T6>& g6, const ParamGenerator<T7>& g7,
- const ParamGenerator<T8>& g8)
- : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7),
- g8_(g8) {}
- virtual ~CartesianProductGenerator8() {}
-
- virtual ParamIteratorInterface<ParamType>* Begin() const {
- return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_,
- g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_,
- g7_.begin(), g8_, g8_.begin());
- }
- virtual ParamIteratorInterface<ParamType>* End() const {
- return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(),
- g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end(), g8_,
- g8_.end());
- }
-
- private:
- class Iterator : public ParamIteratorInterface<ParamType> {
- public:
- Iterator(const ParamGeneratorInterface<ParamType>* base,
- const ParamGenerator<T1>& g1,
- const typename ParamGenerator<T1>::iterator& current1,
- const ParamGenerator<T2>& g2,
- const typename ParamGenerator<T2>::iterator& current2,
- const ParamGenerator<T3>& g3,
- const typename ParamGenerator<T3>::iterator& current3,
- const ParamGenerator<T4>& g4,
- const typename ParamGenerator<T4>::iterator& current4,
- const ParamGenerator<T5>& g5,
- const typename ParamGenerator<T5>::iterator& current5,
- const ParamGenerator<T6>& g6,
- const typename ParamGenerator<T6>::iterator& current6,
- const ParamGenerator<T7>& g7,
- const typename ParamGenerator<T7>::iterator& current7,
- const ParamGenerator<T8>& g8,
- const typename ParamGenerator<T8>::iterator& current8)
- : base_(base),
- begin1_(g1.begin()), end1_(g1.end()), current1_(current1),
- begin2_(g2.begin()), end2_(g2.end()), current2_(current2),
- begin3_(g3.begin()), end3_(g3.end()), current3_(current3),
- begin4_(g4.begin()), end4_(g4.end()), current4_(current4),
- begin5_(g5.begin()), end5_(g5.end()), current5_(current5),
- begin6_(g6.begin()), end6_(g6.end()), current6_(current6),
- begin7_(g7.begin()), end7_(g7.end()), current7_(current7),
- begin8_(g8.begin()), end8_(g8.end()), current8_(current8) {
- ComputeCurrentValue();
- }
- virtual ~Iterator() {}
-
- virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const {
- return base_;
- }
- // Advance should not be called on beyond-of-range iterators
- // so no component iterators must be beyond end of range, either.
- virtual void Advance() {
- assert(!AtEnd());
- ++current8_;
- if (current8_ == end8_) {
- current8_ = begin8_;
- ++current7_;
- }
- if (current7_ == end7_) {
- current7_ = begin7_;
- ++current6_;
- }
- if (current6_ == end6_) {
- current6_ = begin6_;
- ++current5_;
- }
- if (current5_ == end5_) {
- current5_ = begin5_;
- ++current4_;
- }
- if (current4_ == end4_) {
- current4_ = begin4_;
- ++current3_;
- }
- if (current3_ == end3_) {
- current3_ = begin3_;
- ++current2_;
- }
- if (current2_ == end2_) {
- current2_ = begin2_;
- ++current1_;
- }
- ComputeCurrentValue();
- }
- virtual ParamIteratorInterface<ParamType>* Clone() const {
- return new Iterator(*this);
- }
- virtual const ParamType* Current() const { return &current_value_; }
- virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
- // Having the same base generator guarantees that the other
- // iterator is of the same type and we can downcast.
- GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
- << "The program attempted to compare iterators "
- << "from different generators." << std::endl;
- const Iterator* typed_other =
- CheckedDowncastToActualType<const Iterator>(&other);
- // We must report iterators equal if they both point beyond their
- // respective ranges. That can happen in a variety of fashions,
- // so we have to consult AtEnd().
- return (AtEnd() && typed_other->AtEnd()) ||
- (
- current1_ == typed_other->current1_ &&
- current2_ == typed_other->current2_ &&
- current3_ == typed_other->current3_ &&
- current4_ == typed_other->current4_ &&
- current5_ == typed_other->current5_ &&
- current6_ == typed_other->current6_ &&
- current7_ == typed_other->current7_ &&
- current8_ == typed_other->current8_);
- }
-
- private:
- Iterator(const Iterator& other)
- : base_(other.base_),
- begin1_(other.begin1_),
- end1_(other.end1_),
- current1_(other.current1_),
- begin2_(other.begin2_),
- end2_(other.end2_),
- current2_(other.current2_),
- begin3_(other.begin3_),
- end3_(other.end3_),
- current3_(other.current3_),
- begin4_(other.begin4_),
- end4_(other.end4_),
- current4_(other.current4_),
- begin5_(other.begin5_),
- end5_(other.end5_),
- current5_(other.current5_),
- begin6_(other.begin6_),
- end6_(other.end6_),
- current6_(other.current6_),
- begin7_(other.begin7_),
- end7_(other.end7_),
- current7_(other.current7_),
- begin8_(other.begin8_),
- end8_(other.end8_),
- current8_(other.current8_) {
- ComputeCurrentValue();
- }
-
- void ComputeCurrentValue() {
- if (!AtEnd())
- current_value_ = ParamType(*current1_, *current2_, *current3_,
- *current4_, *current5_, *current6_, *current7_, *current8_);
- }
- bool AtEnd() const {
- // We must report iterator past the end of the range when either of the
- // component iterators has reached the end of its range.
- return
- current1_ == end1_ ||
- current2_ == end2_ ||
- current3_ == end3_ ||
- current4_ == end4_ ||
- current5_ == end5_ ||
- current6_ == end6_ ||
- current7_ == end7_ ||
- current8_ == end8_;
- }
-
- // No implementation - assignment is unsupported.
- void operator=(const Iterator& other);
-
- const ParamGeneratorInterface<ParamType>* const base_;
- // begin[i]_ and end[i]_ define the i-th range that Iterator traverses.
- // current[i]_ is the actual traversing iterator.
- const typename ParamGenerator<T1>::iterator begin1_;
- const typename ParamGenerator<T1>::iterator end1_;
- typename ParamGenerator<T1>::iterator current1_;
- const typename ParamGenerator<T2>::iterator begin2_;
- const typename ParamGenerator<T2>::iterator end2_;
- typename ParamGenerator<T2>::iterator current2_;
- const typename ParamGenerator<T3>::iterator begin3_;
- const typename ParamGenerator<T3>::iterator end3_;
- typename ParamGenerator<T3>::iterator current3_;
- const typename ParamGenerator<T4>::iterator begin4_;
- const typename ParamGenerator<T4>::iterator end4_;
- typename ParamGenerator<T4>::iterator current4_;
- const typename ParamGenerator<T5>::iterator begin5_;
- const typename ParamGenerator<T5>::iterator end5_;
- typename ParamGenerator<T5>::iterator current5_;
- const typename ParamGenerator<T6>::iterator begin6_;
- const typename ParamGenerator<T6>::iterator end6_;
- typename ParamGenerator<T6>::iterator current6_;
- const typename ParamGenerator<T7>::iterator begin7_;
- const typename ParamGenerator<T7>::iterator end7_;
- typename ParamGenerator<T7>::iterator current7_;
- const typename ParamGenerator<T8>::iterator begin8_;
- const typename ParamGenerator<T8>::iterator end8_;
- typename ParamGenerator<T8>::iterator current8_;
- ParamType current_value_;
- }; // class CartesianProductGenerator8::Iterator
-
- // No implementation - assignment is unsupported.
- void operator=(const CartesianProductGenerator8& other);
-
- const ParamGenerator<T1> g1_;
- const ParamGenerator<T2> g2_;
- const ParamGenerator<T3> g3_;
- const ParamGenerator<T4> g4_;
- const ParamGenerator<T5> g5_;
- const ParamGenerator<T6> g6_;
- const ParamGenerator<T7> g7_;
- const ParamGenerator<T8> g8_;
-}; // class CartesianProductGenerator8
-
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9>
-class CartesianProductGenerator9
- : public ParamGeneratorInterface< ::testing::tuple<T1, T2, T3, T4, T5, T6,
- T7, T8, T9> > {
- public:
- typedef ::testing::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9> ParamType;
-
- CartesianProductGenerator9(const ParamGenerator<T1>& g1,
- const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3,
- const ParamGenerator<T4>& g4, const ParamGenerator<T5>& g5,
- const ParamGenerator<T6>& g6, const ParamGenerator<T7>& g7,
- const ParamGenerator<T8>& g8, const ParamGenerator<T9>& g9)
- : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8),
- g9_(g9) {}
- virtual ~CartesianProductGenerator9() {}
-
- virtual ParamIteratorInterface<ParamType>* Begin() const {
- return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_,
- g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_,
- g7_.begin(), g8_, g8_.begin(), g9_, g9_.begin());
- }
- virtual ParamIteratorInterface<ParamType>* End() const {
- return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(),
- g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end(), g8_,
- g8_.end(), g9_, g9_.end());
- }
-
- private:
- class Iterator : public ParamIteratorInterface<ParamType> {
- public:
- Iterator(const ParamGeneratorInterface<ParamType>* base,
- const ParamGenerator<T1>& g1,
- const typename ParamGenerator<T1>::iterator& current1,
- const ParamGenerator<T2>& g2,
- const typename ParamGenerator<T2>::iterator& current2,
- const ParamGenerator<T3>& g3,
- const typename ParamGenerator<T3>::iterator& current3,
- const ParamGenerator<T4>& g4,
- const typename ParamGenerator<T4>::iterator& current4,
- const ParamGenerator<T5>& g5,
- const typename ParamGenerator<T5>::iterator& current5,
- const ParamGenerator<T6>& g6,
- const typename ParamGenerator<T6>::iterator& current6,
- const ParamGenerator<T7>& g7,
- const typename ParamGenerator<T7>::iterator& current7,
- const ParamGenerator<T8>& g8,
- const typename ParamGenerator<T8>::iterator& current8,
- const ParamGenerator<T9>& g9,
- const typename ParamGenerator<T9>::iterator& current9)
- : base_(base),
- begin1_(g1.begin()), end1_(g1.end()), current1_(current1),
- begin2_(g2.begin()), end2_(g2.end()), current2_(current2),
- begin3_(g3.begin()), end3_(g3.end()), current3_(current3),
- begin4_(g4.begin()), end4_(g4.end()), current4_(current4),
- begin5_(g5.begin()), end5_(g5.end()), current5_(current5),
- begin6_(g6.begin()), end6_(g6.end()), current6_(current6),
- begin7_(g7.begin()), end7_(g7.end()), current7_(current7),
- begin8_(g8.begin()), end8_(g8.end()), current8_(current8),
- begin9_(g9.begin()), end9_(g9.end()), current9_(current9) {
- ComputeCurrentValue();
- }
- virtual ~Iterator() {}
-
- virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const {
- return base_;
- }
- // Advance should not be called on beyond-of-range iterators
- // so no component iterators must be beyond end of range, either.
- virtual void Advance() {
- assert(!AtEnd());
- ++current9_;
- if (current9_ == end9_) {
- current9_ = begin9_;
- ++current8_;
- }
- if (current8_ == end8_) {
- current8_ = begin8_;
- ++current7_;
- }
- if (current7_ == end7_) {
- current7_ = begin7_;
- ++current6_;
- }
- if (current6_ == end6_) {
- current6_ = begin6_;
- ++current5_;
- }
- if (current5_ == end5_) {
- current5_ = begin5_;
- ++current4_;
- }
- if (current4_ == end4_) {
- current4_ = begin4_;
- ++current3_;
- }
- if (current3_ == end3_) {
- current3_ = begin3_;
- ++current2_;
- }
- if (current2_ == end2_) {
- current2_ = begin2_;
- ++current1_;
- }
- ComputeCurrentValue();
- }
- virtual ParamIteratorInterface<ParamType>* Clone() const {
- return new Iterator(*this);
- }
- virtual const ParamType* Current() const { return &current_value_; }
- virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
- // Having the same base generator guarantees that the other
- // iterator is of the same type and we can downcast.
- GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
- << "The program attempted to compare iterators "
- << "from different generators." << std::endl;
- const Iterator* typed_other =
- CheckedDowncastToActualType<const Iterator>(&other);
- // We must report iterators equal if they both point beyond their
- // respective ranges. That can happen in a variety of fashions,
- // so we have to consult AtEnd().
- return (AtEnd() && typed_other->AtEnd()) ||
- (
- current1_ == typed_other->current1_ &&
- current2_ == typed_other->current2_ &&
- current3_ == typed_other->current3_ &&
- current4_ == typed_other->current4_ &&
- current5_ == typed_other->current5_ &&
- current6_ == typed_other->current6_ &&
- current7_ == typed_other->current7_ &&
- current8_ == typed_other->current8_ &&
- current9_ == typed_other->current9_);
- }
-
- private:
- Iterator(const Iterator& other)
- : base_(other.base_),
- begin1_(other.begin1_),
- end1_(other.end1_),
- current1_(other.current1_),
- begin2_(other.begin2_),
- end2_(other.end2_),
- current2_(other.current2_),
- begin3_(other.begin3_),
- end3_(other.end3_),
- current3_(other.current3_),
- begin4_(other.begin4_),
- end4_(other.end4_),
- current4_(other.current4_),
- begin5_(other.begin5_),
- end5_(other.end5_),
- current5_(other.current5_),
- begin6_(other.begin6_),
- end6_(other.end6_),
- current6_(other.current6_),
- begin7_(other.begin7_),
- end7_(other.end7_),
- current7_(other.current7_),
- begin8_(other.begin8_),
- end8_(other.end8_),
- current8_(other.current8_),
- begin9_(other.begin9_),
- end9_(other.end9_),
- current9_(other.current9_) {
- ComputeCurrentValue();
- }
-
- void ComputeCurrentValue() {
- if (!AtEnd())
- current_value_ = ParamType(*current1_, *current2_, *current3_,
- *current4_, *current5_, *current6_, *current7_, *current8_,
- *current9_);
- }
- bool AtEnd() const {
- // We must report iterator past the end of the range when either of the
- // component iterators has reached the end of its range.
- return
- current1_ == end1_ ||
- current2_ == end2_ ||
- current3_ == end3_ ||
- current4_ == end4_ ||
- current5_ == end5_ ||
- current6_ == end6_ ||
- current7_ == end7_ ||
- current8_ == end8_ ||
- current9_ == end9_;
- }
-
- // No implementation - assignment is unsupported.
- void operator=(const Iterator& other);
-
- const ParamGeneratorInterface<ParamType>* const base_;
- // begin[i]_ and end[i]_ define the i-th range that Iterator traverses.
- // current[i]_ is the actual traversing iterator.
- const typename ParamGenerator<T1>::iterator begin1_;
- const typename ParamGenerator<T1>::iterator end1_;
- typename ParamGenerator<T1>::iterator current1_;
- const typename ParamGenerator<T2>::iterator begin2_;
- const typename ParamGenerator<T2>::iterator end2_;
- typename ParamGenerator<T2>::iterator current2_;
- const typename ParamGenerator<T3>::iterator begin3_;
- const typename ParamGenerator<T3>::iterator end3_;
- typename ParamGenerator<T3>::iterator current3_;
- const typename ParamGenerator<T4>::iterator begin4_;
- const typename ParamGenerator<T4>::iterator end4_;
- typename ParamGenerator<T4>::iterator current4_;
- const typename ParamGenerator<T5>::iterator begin5_;
- const typename ParamGenerator<T5>::iterator end5_;
- typename ParamGenerator<T5>::iterator current5_;
- const typename ParamGenerator<T6>::iterator begin6_;
- const typename ParamGenerator<T6>::iterator end6_;
- typename ParamGenerator<T6>::iterator current6_;
- const typename ParamGenerator<T7>::iterator begin7_;
- const typename ParamGenerator<T7>::iterator end7_;
- typename ParamGenerator<T7>::iterator current7_;
- const typename ParamGenerator<T8>::iterator begin8_;
- const typename ParamGenerator<T8>::iterator end8_;
- typename ParamGenerator<T8>::iterator current8_;
- const typename ParamGenerator<T9>::iterator begin9_;
- const typename ParamGenerator<T9>::iterator end9_;
- typename ParamGenerator<T9>::iterator current9_;
- ParamType current_value_;
- }; // class CartesianProductGenerator9::Iterator
-
- // No implementation - assignment is unsupported.
- void operator=(const CartesianProductGenerator9& other);
-
- const ParamGenerator<T1> g1_;
- const ParamGenerator<T2> g2_;
- const ParamGenerator<T3> g3_;
- const ParamGenerator<T4> g4_;
- const ParamGenerator<T5> g5_;
- const ParamGenerator<T6> g6_;
- const ParamGenerator<T7> g7_;
- const ParamGenerator<T8> g8_;
- const ParamGenerator<T9> g9_;
-}; // class CartesianProductGenerator9
-
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10>
-class CartesianProductGenerator10
- : public ParamGeneratorInterface< ::testing::tuple<T1, T2, T3, T4, T5, T6,
- T7, T8, T9, T10> > {
- public:
- typedef ::testing::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> ParamType;
-
- CartesianProductGenerator10(const ParamGenerator<T1>& g1,
- const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3,
- const ParamGenerator<T4>& g4, const ParamGenerator<T5>& g5,
- const ParamGenerator<T6>& g6, const ParamGenerator<T7>& g7,
- const ParamGenerator<T8>& g8, const ParamGenerator<T9>& g9,
- const ParamGenerator<T10>& g10)
- : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8),
- g9_(g9), g10_(g10) {}
- virtual ~CartesianProductGenerator10() {}
-
- virtual ParamIteratorInterface<ParamType>* Begin() const {
- return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_,
- g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_,
- g7_.begin(), g8_, g8_.begin(), g9_, g9_.begin(), g10_, g10_.begin());
- }
- virtual ParamIteratorInterface<ParamType>* End() const {
- return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(),
- g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end(), g8_,
- g8_.end(), g9_, g9_.end(), g10_, g10_.end());
- }
-
- private:
- class Iterator : public ParamIteratorInterface<ParamType> {
- public:
- Iterator(const ParamGeneratorInterface<ParamType>* base,
- const ParamGenerator<T1>& g1,
- const typename ParamGenerator<T1>::iterator& current1,
- const ParamGenerator<T2>& g2,
- const typename ParamGenerator<T2>::iterator& current2,
- const ParamGenerator<T3>& g3,
- const typename ParamGenerator<T3>::iterator& current3,
- const ParamGenerator<T4>& g4,
- const typename ParamGenerator<T4>::iterator& current4,
- const ParamGenerator<T5>& g5,
- const typename ParamGenerator<T5>::iterator& current5,
- const ParamGenerator<T6>& g6,
- const typename ParamGenerator<T6>::iterator& current6,
- const ParamGenerator<T7>& g7,
- const typename ParamGenerator<T7>::iterator& current7,
- const ParamGenerator<T8>& g8,
- const typename ParamGenerator<T8>::iterator& current8,
- const ParamGenerator<T9>& g9,
- const typename ParamGenerator<T9>::iterator& current9,
- const ParamGenerator<T10>& g10,
- const typename ParamGenerator<T10>::iterator& current10)
- : base_(base),
- begin1_(g1.begin()), end1_(g1.end()), current1_(current1),
- begin2_(g2.begin()), end2_(g2.end()), current2_(current2),
- begin3_(g3.begin()), end3_(g3.end()), current3_(current3),
- begin4_(g4.begin()), end4_(g4.end()), current4_(current4),
- begin5_(g5.begin()), end5_(g5.end()), current5_(current5),
- begin6_(g6.begin()), end6_(g6.end()), current6_(current6),
- begin7_(g7.begin()), end7_(g7.end()), current7_(current7),
- begin8_(g8.begin()), end8_(g8.end()), current8_(current8),
- begin9_(g9.begin()), end9_(g9.end()), current9_(current9),
- begin10_(g10.begin()), end10_(g10.end()), current10_(current10) {
- ComputeCurrentValue();
- }
- virtual ~Iterator() {}
-
- virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const {
- return base_;
- }
- // Advance should not be called on beyond-of-range iterators
- // so no component iterators must be beyond end of range, either.
- virtual void Advance() {
- assert(!AtEnd());
- ++current10_;
- if (current10_ == end10_) {
- current10_ = begin10_;
- ++current9_;
- }
- if (current9_ == end9_) {
- current9_ = begin9_;
- ++current8_;
- }
- if (current8_ == end8_) {
- current8_ = begin8_;
- ++current7_;
- }
- if (current7_ == end7_) {
- current7_ = begin7_;
- ++current6_;
- }
- if (current6_ == end6_) {
- current6_ = begin6_;
- ++current5_;
- }
- if (current5_ == end5_) {
- current5_ = begin5_;
- ++current4_;
- }
- if (current4_ == end4_) {
- current4_ = begin4_;
- ++current3_;
- }
- if (current3_ == end3_) {
- current3_ = begin3_;
- ++current2_;
- }
- if (current2_ == end2_) {
- current2_ = begin2_;
- ++current1_;
- }
- ComputeCurrentValue();
- }
- virtual ParamIteratorInterface<ParamType>* Clone() const {
- return new Iterator(*this);
- }
- virtual const ParamType* Current() const { return &current_value_; }
- virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
- // Having the same base generator guarantees that the other
- // iterator is of the same type and we can downcast.
- GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
- << "The program attempted to compare iterators "
- << "from different generators." << std::endl;
- const Iterator* typed_other =
- CheckedDowncastToActualType<const Iterator>(&other);
- // We must report iterators equal if they both point beyond their
- // respective ranges. That can happen in a variety of fashions,
- // so we have to consult AtEnd().
- return (AtEnd() && typed_other->AtEnd()) ||
- (
- current1_ == typed_other->current1_ &&
- current2_ == typed_other->current2_ &&
- current3_ == typed_other->current3_ &&
- current4_ == typed_other->current4_ &&
- current5_ == typed_other->current5_ &&
- current6_ == typed_other->current6_ &&
- current7_ == typed_other->current7_ &&
- current8_ == typed_other->current8_ &&
- current9_ == typed_other->current9_ &&
- current10_ == typed_other->current10_);
- }
-
- private:
- Iterator(const Iterator& other)
- : base_(other.base_),
- begin1_(other.begin1_),
- end1_(other.end1_),
- current1_(other.current1_),
- begin2_(other.begin2_),
- end2_(other.end2_),
- current2_(other.current2_),
- begin3_(other.begin3_),
- end3_(other.end3_),
- current3_(other.current3_),
- begin4_(other.begin4_),
- end4_(other.end4_),
- current4_(other.current4_),
- begin5_(other.begin5_),
- end5_(other.end5_),
- current5_(other.current5_),
- begin6_(other.begin6_),
- end6_(other.end6_),
- current6_(other.current6_),
- begin7_(other.begin7_),
- end7_(other.end7_),
- current7_(other.current7_),
- begin8_(other.begin8_),
- end8_(other.end8_),
- current8_(other.current8_),
- begin9_(other.begin9_),
- end9_(other.end9_),
- current9_(other.current9_),
- begin10_(other.begin10_),
- end10_(other.end10_),
- current10_(other.current10_) {
- ComputeCurrentValue();
- }
-
- void ComputeCurrentValue() {
- if (!AtEnd())
- current_value_ = ParamType(*current1_, *current2_, *current3_,
- *current4_, *current5_, *current6_, *current7_, *current8_,
- *current9_, *current10_);
- }
- bool AtEnd() const {
- // We must report iterator past the end of the range when either of the
- // component iterators has reached the end of its range.
- return
- current1_ == end1_ ||
- current2_ == end2_ ||
- current3_ == end3_ ||
- current4_ == end4_ ||
- current5_ == end5_ ||
- current6_ == end6_ ||
- current7_ == end7_ ||
- current8_ == end8_ ||
- current9_ == end9_ ||
- current10_ == end10_;
- }
-
- // No implementation - assignment is unsupported.
- void operator=(const Iterator& other);
-
- const ParamGeneratorInterface<ParamType>* const base_;
- // begin[i]_ and end[i]_ define the i-th range that Iterator traverses.
- // current[i]_ is the actual traversing iterator.
- const typename ParamGenerator<T1>::iterator begin1_;
- const typename ParamGenerator<T1>::iterator end1_;
- typename ParamGenerator<T1>::iterator current1_;
- const typename ParamGenerator<T2>::iterator begin2_;
- const typename ParamGenerator<T2>::iterator end2_;
- typename ParamGenerator<T2>::iterator current2_;
- const typename ParamGenerator<T3>::iterator begin3_;
- const typename ParamGenerator<T3>::iterator end3_;
- typename ParamGenerator<T3>::iterator current3_;
- const typename ParamGenerator<T4>::iterator begin4_;
- const typename ParamGenerator<T4>::iterator end4_;
- typename ParamGenerator<T4>::iterator current4_;
- const typename ParamGenerator<T5>::iterator begin5_;
- const typename ParamGenerator<T5>::iterator end5_;
- typename ParamGenerator<T5>::iterator current5_;
- const typename ParamGenerator<T6>::iterator begin6_;
- const typename ParamGenerator<T6>::iterator end6_;
- typename ParamGenerator<T6>::iterator current6_;
- const typename ParamGenerator<T7>::iterator begin7_;
- const typename ParamGenerator<T7>::iterator end7_;
- typename ParamGenerator<T7>::iterator current7_;
- const typename ParamGenerator<T8>::iterator begin8_;
- const typename ParamGenerator<T8>::iterator end8_;
- typename ParamGenerator<T8>::iterator current8_;
- const typename ParamGenerator<T9>::iterator begin9_;
- const typename ParamGenerator<T9>::iterator end9_;
- typename ParamGenerator<T9>::iterator current9_;
- const typename ParamGenerator<T10>::iterator begin10_;
- const typename ParamGenerator<T10>::iterator end10_;
- typename ParamGenerator<T10>::iterator current10_;
- ParamType current_value_;
- }; // class CartesianProductGenerator10::Iterator
-
- // No implementation - assignment is unsupported.
- void operator=(const CartesianProductGenerator10& other);
-
- const ParamGenerator<T1> g1_;
- const ParamGenerator<T2> g2_;
- const ParamGenerator<T3> g3_;
- const ParamGenerator<T4> g4_;
- const ParamGenerator<T5> g5_;
- const ParamGenerator<T6> g6_;
- const ParamGenerator<T7> g7_;
- const ParamGenerator<T8> g8_;
- const ParamGenerator<T9> g9_;
- const ParamGenerator<T10> g10_;
-}; // class CartesianProductGenerator10
-
-
-// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
-//
-// Helper classes providing Combine() with polymorphic features. They allow
-// casting CartesianProductGeneratorN<T> to ParamGenerator<U> if T is
-// convertible to U.
-//
-template <class Generator1, class Generator2>
-class CartesianProductHolder2 {
- public:
-CartesianProductHolder2(const Generator1& g1, const Generator2& g2)
- : g1_(g1), g2_(g2) {}
- template <typename T1, typename T2>
- operator ParamGenerator< ::testing::tuple<T1, T2> >() const {
- return ParamGenerator< ::testing::tuple<T1, T2> >(
- new CartesianProductGenerator2<T1, T2>(
- static_cast<ParamGenerator<T1> >(g1_),
- static_cast<ParamGenerator<T2> >(g2_)));
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const CartesianProductHolder2& other);
-
- const Generator1 g1_;
- const Generator2 g2_;
-}; // class CartesianProductHolder2
-
-template <class Generator1, class Generator2, class Generator3>
-class CartesianProductHolder3 {
- public:
-CartesianProductHolder3(const Generator1& g1, const Generator2& g2,
- const Generator3& g3)
- : g1_(g1), g2_(g2), g3_(g3) {}
- template <typename T1, typename T2, typename T3>
- operator ParamGenerator< ::testing::tuple<T1, T2, T3> >() const {
- return ParamGenerator< ::testing::tuple<T1, T2, T3> >(
- new CartesianProductGenerator3<T1, T2, T3>(
- static_cast<ParamGenerator<T1> >(g1_),
- static_cast<ParamGenerator<T2> >(g2_),
- static_cast<ParamGenerator<T3> >(g3_)));
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const CartesianProductHolder3& other);
-
- const Generator1 g1_;
- const Generator2 g2_;
- const Generator3 g3_;
-}; // class CartesianProductHolder3
-
-template <class Generator1, class Generator2, class Generator3,
- class Generator4>
-class CartesianProductHolder4 {
- public:
-CartesianProductHolder4(const Generator1& g1, const Generator2& g2,
- const Generator3& g3, const Generator4& g4)
- : g1_(g1), g2_(g2), g3_(g3), g4_(g4) {}
- template <typename T1, typename T2, typename T3, typename T4>
- operator ParamGenerator< ::testing::tuple<T1, T2, T3, T4> >() const {
- return ParamGenerator< ::testing::tuple<T1, T2, T3, T4> >(
- new CartesianProductGenerator4<T1, T2, T3, T4>(
- static_cast<ParamGenerator<T1> >(g1_),
- static_cast<ParamGenerator<T2> >(g2_),
- static_cast<ParamGenerator<T3> >(g3_),
- static_cast<ParamGenerator<T4> >(g4_)));
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const CartesianProductHolder4& other);
-
- const Generator1 g1_;
- const Generator2 g2_;
- const Generator3 g3_;
- const Generator4 g4_;
-}; // class CartesianProductHolder4
-
-template <class Generator1, class Generator2, class Generator3,
- class Generator4, class Generator5>
-class CartesianProductHolder5 {
- public:
-CartesianProductHolder5(const Generator1& g1, const Generator2& g2,
- const Generator3& g3, const Generator4& g4, const Generator5& g5)
- : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5) {}
- template <typename T1, typename T2, typename T3, typename T4, typename T5>
- operator ParamGenerator< ::testing::tuple<T1, T2, T3, T4, T5> >() const {
- return ParamGenerator< ::testing::tuple<T1, T2, T3, T4, T5> >(
- new CartesianProductGenerator5<T1, T2, T3, T4, T5>(
- static_cast<ParamGenerator<T1> >(g1_),
- static_cast<ParamGenerator<T2> >(g2_),
- static_cast<ParamGenerator<T3> >(g3_),
- static_cast<ParamGenerator<T4> >(g4_),
- static_cast<ParamGenerator<T5> >(g5_)));
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const CartesianProductHolder5& other);
-
- const Generator1 g1_;
- const Generator2 g2_;
- const Generator3 g3_;
- const Generator4 g4_;
- const Generator5 g5_;
-}; // class CartesianProductHolder5
-
-template <class Generator1, class Generator2, class Generator3,
- class Generator4, class Generator5, class Generator6>
-class CartesianProductHolder6 {
- public:
-CartesianProductHolder6(const Generator1& g1, const Generator2& g2,
- const Generator3& g3, const Generator4& g4, const Generator5& g5,
- const Generator6& g6)
- : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6) {}
- template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6>
- operator ParamGenerator< ::testing::tuple<T1, T2, T3, T4, T5, T6> >() const {
- return ParamGenerator< ::testing::tuple<T1, T2, T3, T4, T5, T6> >(
- new CartesianProductGenerator6<T1, T2, T3, T4, T5, T6>(
- static_cast<ParamGenerator<T1> >(g1_),
- static_cast<ParamGenerator<T2> >(g2_),
- static_cast<ParamGenerator<T3> >(g3_),
- static_cast<ParamGenerator<T4> >(g4_),
- static_cast<ParamGenerator<T5> >(g5_),
- static_cast<ParamGenerator<T6> >(g6_)));
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const CartesianProductHolder6& other);
-
- const Generator1 g1_;
- const Generator2 g2_;
- const Generator3 g3_;
- const Generator4 g4_;
- const Generator5 g5_;
- const Generator6 g6_;
-}; // class CartesianProductHolder6
-
-template <class Generator1, class Generator2, class Generator3,
- class Generator4, class Generator5, class Generator6, class Generator7>
-class CartesianProductHolder7 {
- public:
-CartesianProductHolder7(const Generator1& g1, const Generator2& g2,
- const Generator3& g3, const Generator4& g4, const Generator5& g5,
- const Generator6& g6, const Generator7& g7)
- : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7) {}
- template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7>
- operator ParamGenerator< ::testing::tuple<T1, T2, T3, T4, T5, T6,
- T7> >() const {
- return ParamGenerator< ::testing::tuple<T1, T2, T3, T4, T5, T6, T7> >(
- new CartesianProductGenerator7<T1, T2, T3, T4, T5, T6, T7>(
- static_cast<ParamGenerator<T1> >(g1_),
- static_cast<ParamGenerator<T2> >(g2_),
- static_cast<ParamGenerator<T3> >(g3_),
- static_cast<ParamGenerator<T4> >(g4_),
- static_cast<ParamGenerator<T5> >(g5_),
- static_cast<ParamGenerator<T6> >(g6_),
- static_cast<ParamGenerator<T7> >(g7_)));
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const CartesianProductHolder7& other);
-
- const Generator1 g1_;
- const Generator2 g2_;
- const Generator3 g3_;
- const Generator4 g4_;
- const Generator5 g5_;
- const Generator6 g6_;
- const Generator7 g7_;
-}; // class CartesianProductHolder7
-
-template <class Generator1, class Generator2, class Generator3,
- class Generator4, class Generator5, class Generator6, class Generator7,
- class Generator8>
-class CartesianProductHolder8 {
- public:
-CartesianProductHolder8(const Generator1& g1, const Generator2& g2,
- const Generator3& g3, const Generator4& g4, const Generator5& g5,
- const Generator6& g6, const Generator7& g7, const Generator8& g8)
- : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7),
- g8_(g8) {}
- template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8>
- operator ParamGenerator< ::testing::tuple<T1, T2, T3, T4, T5, T6, T7,
- T8> >() const {
- return ParamGenerator< ::testing::tuple<T1, T2, T3, T4, T5, T6, T7, T8> >(
- new CartesianProductGenerator8<T1, T2, T3, T4, T5, T6, T7, T8>(
- static_cast<ParamGenerator<T1> >(g1_),
- static_cast<ParamGenerator<T2> >(g2_),
- static_cast<ParamGenerator<T3> >(g3_),
- static_cast<ParamGenerator<T4> >(g4_),
- static_cast<ParamGenerator<T5> >(g5_),
- static_cast<ParamGenerator<T6> >(g6_),
- static_cast<ParamGenerator<T7> >(g7_),
- static_cast<ParamGenerator<T8> >(g8_)));
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const CartesianProductHolder8& other);
-
- const Generator1 g1_;
- const Generator2 g2_;
- const Generator3 g3_;
- const Generator4 g4_;
- const Generator5 g5_;
- const Generator6 g6_;
- const Generator7 g7_;
- const Generator8 g8_;
-}; // class CartesianProductHolder8
-
-template <class Generator1, class Generator2, class Generator3,
- class Generator4, class Generator5, class Generator6, class Generator7,
- class Generator8, class Generator9>
-class CartesianProductHolder9 {
- public:
-CartesianProductHolder9(const Generator1& g1, const Generator2& g2,
- const Generator3& g3, const Generator4& g4, const Generator5& g5,
- const Generator6& g6, const Generator7& g7, const Generator8& g8,
- const Generator9& g9)
- : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8),
- g9_(g9) {}
- template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9>
- operator ParamGenerator< ::testing::tuple<T1, T2, T3, T4, T5, T6, T7, T8,
- T9> >() const {
- return ParamGenerator< ::testing::tuple<T1, T2, T3, T4, T5, T6, T7, T8,
- T9> >(
- new CartesianProductGenerator9<T1, T2, T3, T4, T5, T6, T7, T8, T9>(
- static_cast<ParamGenerator<T1> >(g1_),
- static_cast<ParamGenerator<T2> >(g2_),
- static_cast<ParamGenerator<T3> >(g3_),
- static_cast<ParamGenerator<T4> >(g4_),
- static_cast<ParamGenerator<T5> >(g5_),
- static_cast<ParamGenerator<T6> >(g6_),
- static_cast<ParamGenerator<T7> >(g7_),
- static_cast<ParamGenerator<T8> >(g8_),
- static_cast<ParamGenerator<T9> >(g9_)));
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const CartesianProductHolder9& other);
-
- const Generator1 g1_;
- const Generator2 g2_;
- const Generator3 g3_;
- const Generator4 g4_;
- const Generator5 g5_;
- const Generator6 g6_;
- const Generator7 g7_;
- const Generator8 g8_;
- const Generator9 g9_;
-}; // class CartesianProductHolder9
-
-template <class Generator1, class Generator2, class Generator3,
- class Generator4, class Generator5, class Generator6, class Generator7,
- class Generator8, class Generator9, class Generator10>
-class CartesianProductHolder10 {
- public:
-CartesianProductHolder10(const Generator1& g1, const Generator2& g2,
- const Generator3& g3, const Generator4& g4, const Generator5& g5,
- const Generator6& g6, const Generator7& g7, const Generator8& g8,
- const Generator9& g9, const Generator10& g10)
- : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8),
- g9_(g9), g10_(g10) {}
- template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8, typename T9, typename T10>
- operator ParamGenerator< ::testing::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9,
- T10> >() const {
- return ParamGenerator< ::testing::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9,
- T10> >(
- new CartesianProductGenerator10<T1, T2, T3, T4, T5, T6, T7, T8, T9,
- T10>(
- static_cast<ParamGenerator<T1> >(g1_),
- static_cast<ParamGenerator<T2> >(g2_),
- static_cast<ParamGenerator<T3> >(g3_),
- static_cast<ParamGenerator<T4> >(g4_),
- static_cast<ParamGenerator<T5> >(g5_),
- static_cast<ParamGenerator<T6> >(g6_),
- static_cast<ParamGenerator<T7> >(g7_),
- static_cast<ParamGenerator<T8> >(g8_),
- static_cast<ParamGenerator<T9> >(g9_),
- static_cast<ParamGenerator<T10> >(g10_)));
- }
-
- private:
- // No implementation - assignment is unsupported.
- void operator=(const CartesianProductHolder10& other);
-
- const Generator1 g1_;
- const Generator2 g2_;
- const Generator3 g3_;
- const Generator4 g4_;
- const Generator5 g5_;
- const Generator6 g6_;
- const Generator7 g7_;
- const Generator8 g8_;
- const Generator9 g9_;
- const Generator10 g10_;
-}; // class CartesianProductHolder10
-
-# endif // GTEST_HAS_COMBINE
-
-} // namespace internal
-} // namespace testing
-
-#endif // GTEST_HAS_PARAM_TEST
-
-#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_
diff --git a/extern/gtest/include/gtest/internal/gtest-param-util.h b/extern/gtest/include/gtest/internal/gtest-param-util.h
index 82cab9b0201..97533993c0c 100644
--- a/extern/gtest/include/gtest/internal/gtest-param-util.h
+++ b/extern/gtest/include/gtest/internal/gtest-param-util.h
@@ -26,33 +26,30 @@
// 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: vladl@google.com (Vlad Losev)
+
// Type and function utilities for implementing parameterized tests.
+// GOOGLETEST_CM0001 DO NOT DELETE
+
#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_
#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_
#include <ctype.h>
+#include <cassert>
#include <iterator>
+#include <memory>
#include <set>
+#include <tuple>
#include <utility>
#include <vector>
-// scripts/fuse_gtest.py depends on gtest's own header being #included
-// *unconditionally*. Therefore these #includes cannot be moved
-// inside #if GTEST_HAS_PARAM_TEST.
#include "gtest/internal/gtest-internal.h"
-#include "gtest/internal/gtest-linked_ptr.h"
#include "gtest/internal/gtest-port.h"
#include "gtest/gtest-printers.h"
-#if GTEST_HAS_PARAM_TEST
-
namespace testing {
-
// Input to a parameterized test name generator, describing a test parameter.
// Consists of the parameter value and the integer parameter index.
template <class ParamType>
@@ -76,13 +73,14 @@ struct PrintToStringParamName {
namespace internal {
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
-//
+// Utility Functions
+
// Outputs a message explaining invalid registration of different
-// fixture class for the same test case. This may happen when
+// fixture class for the same test suite. This may happen when
// TEST_P macro is used to define two tests with the same name
// but in different namespaces.
-GTEST_API_ void ReportInvalidTestCaseType(const char* test_case_name,
- CodeLocation code_location);
+GTEST_API_ void ReportInvalidTestSuiteType(const char* test_suite_name,
+ CodeLocation code_location);
template <typename> class ParamGeneratorInterface;
template <typename> class ParamGenerator;
@@ -157,7 +155,7 @@ class ParamIterator {
private:
friend class ParamGenerator<T>;
explicit ParamIterator(ParamIteratorInterface<T>* impl) : impl_(impl) {}
- scoped_ptr<ParamIteratorInterface<T> > impl_;
+ std::unique_ptr<ParamIteratorInterface<T> > impl_;
};
// ParamGeneratorInterface<T> is the binary interface to access generators
@@ -196,7 +194,7 @@ class ParamGenerator {
iterator end() const { return iterator(impl_->End()); }
private:
- linked_ptr<const ParamGeneratorInterface<T> > impl_;
+ std::shared_ptr<const ParamGeneratorInterface<T> > impl_;
};
// Generates values from a range of two comparable values. Can be used to
@@ -209,12 +207,12 @@ class RangeGenerator : public ParamGeneratorInterface<T> {
RangeGenerator(T begin, T end, IncrementT step)
: begin_(begin), end_(end),
step_(step), end_index_(CalculateEndIndex(begin, end, step)) {}
- virtual ~RangeGenerator() {}
+ ~RangeGenerator() override {}
- virtual ParamIteratorInterface<T>* Begin() const {
+ ParamIteratorInterface<T>* Begin() const override {
return new Iterator(this, begin_, 0, step_);
}
- virtual ParamIteratorInterface<T>* End() const {
+ ParamIteratorInterface<T>* End() const override {
return new Iterator(this, end_, end_index_, step_);
}
@@ -224,20 +222,20 @@ class RangeGenerator : public ParamGeneratorInterface<T> {
Iterator(const ParamGeneratorInterface<T>* base, T value, int index,
IncrementT step)
: base_(base), value_(value), index_(index), step_(step) {}
- virtual ~Iterator() {}
+ ~Iterator() override {}
- virtual const ParamGeneratorInterface<T>* BaseGenerator() const {
+ const ParamGeneratorInterface<T>* BaseGenerator() const override {
return base_;
}
- virtual void Advance() {
+ void Advance() override {
value_ = static_cast<T>(value_ + step_);
index_++;
}
- virtual ParamIteratorInterface<T>* Clone() const {
+ ParamIteratorInterface<T>* Clone() const override {
return new Iterator(*this);
}
- virtual const T* Current() const { return &value_; }
- virtual bool Equals(const ParamIteratorInterface<T>& other) const {
+ const T* Current() const override { return &value_; }
+ bool Equals(const ParamIteratorInterface<T>& other) const override {
// Having the same base generator guarantees that the other
// iterator is of the same type and we can downcast.
GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
@@ -294,12 +292,12 @@ class ValuesInIteratorRangeGenerator : public ParamGeneratorInterface<T> {
template <typename ForwardIterator>
ValuesInIteratorRangeGenerator(ForwardIterator begin, ForwardIterator end)
: container_(begin, end) {}
- virtual ~ValuesInIteratorRangeGenerator() {}
+ ~ValuesInIteratorRangeGenerator() override {}
- virtual ParamIteratorInterface<T>* Begin() const {
+ ParamIteratorInterface<T>* Begin() const override {
return new Iterator(this, container_.begin());
}
- virtual ParamIteratorInterface<T>* End() const {
+ ParamIteratorInterface<T>* End() const override {
return new Iterator(this, container_.end());
}
@@ -311,16 +309,16 @@ class ValuesInIteratorRangeGenerator : public ParamGeneratorInterface<T> {
Iterator(const ParamGeneratorInterface<T>* base,
typename ContainerType::const_iterator iterator)
: base_(base), iterator_(iterator) {}
- virtual ~Iterator() {}
+ ~Iterator() override {}
- virtual const ParamGeneratorInterface<T>* BaseGenerator() const {
+ const ParamGeneratorInterface<T>* BaseGenerator() const override {
return base_;
}
- virtual void Advance() {
+ void Advance() override {
++iterator_;
value_.reset();
}
- virtual ParamIteratorInterface<T>* Clone() const {
+ ParamIteratorInterface<T>* Clone() const override {
return new Iterator(*this);
}
// We need to use cached value referenced by iterator_ because *iterator_
@@ -330,12 +328,11 @@ class ValuesInIteratorRangeGenerator : public ParamGeneratorInterface<T> {
// can advance iterator_ beyond the end of the range, and we cannot
// detect that fact. The client code, on the other hand, is
// responsible for not calling Current() on an out-of-range iterator.
- virtual const T* Current() const {
- if (value_.get() == NULL)
- value_.reset(new T(*iterator_));
+ const T* Current() const override {
+ if (value_.get() == nullptr) value_.reset(new T(*iterator_));
return value_.get();
}
- virtual bool Equals(const ParamIteratorInterface<T>& other) const {
+ bool Equals(const ParamIteratorInterface<T>& other) const override {
// Having the same base generator guarantees that the other
// iterator is of the same type and we can downcast.
GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
@@ -358,9 +355,9 @@ class ValuesInIteratorRangeGenerator : public ParamGeneratorInterface<T> {
// A cached value of *iterator_. We keep it here to allow access by
// pointer in the wrapping iterator's operator->().
// value_ needs to be mutable to be accessed in Current().
- // Use of scoped_ptr helps manage cached value's lifetime,
+ // Use of std::unique_ptr helps manage cached value's lifetime,
// which is bound by the lifespan of the iterator itself.
- mutable scoped_ptr<const T> value_;
+ mutable std::unique_ptr<const T> value_;
}; // class ValuesInIteratorRangeGenerator::Iterator
// No implementation - assignment is unsupported.
@@ -380,25 +377,12 @@ std::string DefaultParamName(const TestParamInfo<ParamType>& info) {
return name_stream.GetString();
}
-// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
-//
-// Parameterized test name overload helpers, which help the
-// INSTANTIATE_TEST_CASE_P macro choose between the default parameterized
-// test name generator and user param name generator.
-template <class ParamType, class ParamNameGenFunctor>
-ParamNameGenFunctor GetParamNameGen(ParamNameGenFunctor func) {
- return func;
-}
-
-template <class ParamType>
-struct ParamNameGenFunc {
- typedef std::string Type(const TestParamInfo<ParamType>&);
-};
-
-template <class ParamType>
-typename ParamNameGenFunc<ParamType>::Type *GetParamNameGen() {
- return DefaultParamName;
+template <typename T = int>
+void TestNotEmpty() {
+ static_assert(sizeof(T) == 0, "Empty arguments are not allowed.");
}
+template <typename T = int>
+void TestNotEmpty(const T&) {}
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
//
@@ -410,7 +394,7 @@ class ParameterizedTestFactory : public TestFactoryBase {
typedef typename TestClass::ParamType ParamType;
explicit ParameterizedTestFactory(ParamType parameter) :
parameter_(parameter) {}
- virtual Test* CreateTest() {
+ Test* CreateTest() override {
TestClass::SetParam(&parameter_);
return new TestClass();
}
@@ -438,19 +422,19 @@ class TestMetaFactoryBase {
// TestMetaFactory creates test factories for passing into
// MakeAndRegisterTestInfo function. Since MakeAndRegisterTestInfo receives
// ownership of test factory pointer, same factory object cannot be passed
-// into that method twice. But ParameterizedTestCaseInfo is going to call
+// into that method twice. But ParameterizedTestSuiteInfo is going to call
// it for each Test/Parameter value combination. Thus it needs meta factory
// creator class.
-template <class TestCase>
+template <class TestSuite>
class TestMetaFactory
- : public TestMetaFactoryBase<typename TestCase::ParamType> {
+ : public TestMetaFactoryBase<typename TestSuite::ParamType> {
public:
- typedef typename TestCase::ParamType ParamType;
+ using ParamType = typename TestSuite::ParamType;
TestMetaFactory() {}
- virtual TestFactoryBase* CreateTestFactory(ParamType parameter) {
- return new ParameterizedTestFactory<TestCase>(parameter);
+ TestFactoryBase* CreateTestFactory(ParamType parameter) override {
+ return new ParameterizedTestFactory<TestSuite>(parameter);
}
private:
@@ -459,107 +443,106 @@ class TestMetaFactory
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
//
-// ParameterizedTestCaseInfoBase is a generic interface
-// to ParameterizedTestCaseInfo classes. ParameterizedTestCaseInfoBase
+// ParameterizedTestSuiteInfoBase is a generic interface
+// to ParameterizedTestSuiteInfo classes. ParameterizedTestSuiteInfoBase
// accumulates test information provided by TEST_P macro invocations
-// and generators provided by INSTANTIATE_TEST_CASE_P macro invocations
+// and generators provided by INSTANTIATE_TEST_SUITE_P macro invocations
// and uses that information to register all resulting test instances
-// in RegisterTests method. The ParameterizeTestCaseRegistry class holds
-// a collection of pointers to the ParameterizedTestCaseInfo objects
+// in RegisterTests method. The ParameterizeTestSuiteRegistry class holds
+// a collection of pointers to the ParameterizedTestSuiteInfo objects
// and calls RegisterTests() on each of them when asked.
-class ParameterizedTestCaseInfoBase {
+class ParameterizedTestSuiteInfoBase {
public:
- virtual ~ParameterizedTestCaseInfoBase() {}
+ virtual ~ParameterizedTestSuiteInfoBase() {}
- // Base part of test case name for display purposes.
- virtual const string& GetTestCaseName() const = 0;
+ // Base part of test suite name for display purposes.
+ virtual const std::string& GetTestSuiteName() const = 0;
// Test case id to verify identity.
- virtual TypeId GetTestCaseTypeId() const = 0;
+ virtual TypeId GetTestSuiteTypeId() const = 0;
// UnitTest class invokes this method to register tests in this
- // test case right before running them in RUN_ALL_TESTS macro.
- // This method should not be called more then once on any single
- // instance of a ParameterizedTestCaseInfoBase derived class.
+ // test suite right before running them in RUN_ALL_TESTS macro.
+ // This method should not be called more than once on any single
+ // instance of a ParameterizedTestSuiteInfoBase derived class.
virtual void RegisterTests() = 0;
protected:
- ParameterizedTestCaseInfoBase() {}
+ ParameterizedTestSuiteInfoBase() {}
private:
- GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseInfoBase);
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestSuiteInfoBase);
};
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
//
-// ParameterizedTestCaseInfo accumulates tests obtained from TEST_P
-// macro invocations for a particular test case and generators
-// obtained from INSTANTIATE_TEST_CASE_P macro invocations for that
-// test case. It registers tests with all values generated by all
+// ParameterizedTestSuiteInfo accumulates tests obtained from TEST_P
+// macro invocations for a particular test suite and generators
+// obtained from INSTANTIATE_TEST_SUITE_P macro invocations for that
+// test suite. It registers tests with all values generated by all
// generators when asked.
-template <class TestCase>
-class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase {
+template <class TestSuite>
+class ParameterizedTestSuiteInfo : public ParameterizedTestSuiteInfoBase {
public:
// ParamType and GeneratorCreationFunc are private types but are required
// for declarations of public methods AddTestPattern() and
- // AddTestCaseInstantiation().
- typedef typename TestCase::ParamType ParamType;
+ // AddTestSuiteInstantiation().
+ using ParamType = typename TestSuite::ParamType;
// A function that returns an instance of appropriate generator type.
typedef ParamGenerator<ParamType>(GeneratorCreationFunc)();
- typedef typename ParamNameGenFunc<ParamType>::Type ParamNameGeneratorFunc;
+ using ParamNameGeneratorFunc = std::string(const TestParamInfo<ParamType>&);
- explicit ParameterizedTestCaseInfo(
- const char* name, CodeLocation code_location)
- : test_case_name_(name), code_location_(code_location) {}
+ explicit ParameterizedTestSuiteInfo(const char* name,
+ CodeLocation code_location)
+ : test_suite_name_(name), code_location_(code_location) {}
// Test case base name for display purposes.
- virtual const string& GetTestCaseName() const { return test_case_name_; }
+ const std::string& GetTestSuiteName() const override {
+ return test_suite_name_;
+ }
// Test case id to verify identity.
- virtual TypeId GetTestCaseTypeId() const { return GetTypeId<TestCase>(); }
+ TypeId GetTestSuiteTypeId() const override { return GetTypeId<TestSuite>(); }
// TEST_P macro uses AddTestPattern() to record information
// about a single test in a LocalTestInfo structure.
- // test_case_name is the base name of the test case (without invocation
+ // test_suite_name is the base name of the test suite (without invocation
// prefix). test_base_name is the name of an individual test without
// parameter index. For the test SequenceA/FooTest.DoBar/1 FooTest is
- // test case base name and DoBar is test base name.
- void AddTestPattern(const char* test_case_name,
- const char* test_base_name,
+ // test suite base name and DoBar is test base name.
+ void AddTestPattern(const char* test_suite_name, const char* test_base_name,
TestMetaFactoryBase<ParamType>* meta_factory) {
- tests_.push_back(linked_ptr<TestInfo>(new TestInfo(test_case_name,
- test_base_name,
- meta_factory)));
+ tests_.push_back(std::shared_ptr<TestInfo>(
+ new TestInfo(test_suite_name, test_base_name, meta_factory)));
}
- // INSTANTIATE_TEST_CASE_P macro uses AddGenerator() to record information
+ // INSTANTIATE_TEST_SUITE_P macro uses AddGenerator() to record information
// about a generator.
- int AddTestCaseInstantiation(const string& instantiation_name,
- GeneratorCreationFunc* func,
- ParamNameGeneratorFunc* name_func,
- const char* file,
- int line) {
+ int AddTestSuiteInstantiation(const std::string& instantiation_name,
+ GeneratorCreationFunc* func,
+ ParamNameGeneratorFunc* name_func,
+ const char* file, int line) {
instantiations_.push_back(
InstantiationInfo(instantiation_name, func, name_func, file, line));
return 0; // Return value used only to run this method in namespace scope.
}
- // UnitTest class invokes this method to register tests in this test case
- // test cases right before running tests in RUN_ALL_TESTS macro.
- // This method should not be called more then once on any single
- // instance of a ParameterizedTestCaseInfoBase derived class.
- // UnitTest has a guard to prevent from calling this method more then once.
- virtual void RegisterTests() {
+ // UnitTest class invokes this method to register tests in this test suite
+ // test suites right before running tests in RUN_ALL_TESTS macro.
+ // This method should not be called more than once on any single
+ // instance of a ParameterizedTestSuiteInfoBase derived class.
+ // UnitTest has a guard to prevent from calling this method more than once.
+ void RegisterTests() override {
for (typename TestInfoContainer::iterator test_it = tests_.begin();
test_it != tests_.end(); ++test_it) {
- linked_ptr<TestInfo> test_info = *test_it;
+ std::shared_ptr<TestInfo> test_info = *test_it;
for (typename InstantiationContainer::iterator gen_it =
instantiations_.begin(); gen_it != instantiations_.end();
++gen_it) {
- const string& instantiation_name = gen_it->name;
+ const std::string& instantiation_name = gen_it->name;
ParamGenerator<ParamType> generator((*gen_it->generator)());
ParamNameGeneratorFunc* name_func = gen_it->name_func;
const char* file = gen_it->file;
int line = gen_it->line;
- string test_case_name;
+ std::string test_suite_name;
if ( !instantiation_name.empty() )
- test_case_name = instantiation_name + "/";
- test_case_name += test_info->test_case_base_name;
+ test_suite_name = instantiation_name + "/";
+ test_suite_name += test_info->test_suite_base_name;
size_t i = 0;
std::set<std::string> test_param_names;
@@ -582,39 +565,39 @@ class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase {
test_param_names.insert(param_name);
- test_name_stream << test_info->test_base_name << "/" << param_name;
+ if (!test_info->test_base_name.empty()) {
+ test_name_stream << test_info->test_base_name << "/";
+ }
+ test_name_stream << param_name;
MakeAndRegisterTestInfo(
- test_case_name.c_str(),
- test_name_stream.GetString().c_str(),
- NULL, // No type parameter.
- PrintToString(*param_it).c_str(),
- code_location_,
- GetTestCaseTypeId(),
- TestCase::SetUpTestCase,
- TestCase::TearDownTestCase,
+ test_suite_name.c_str(), test_name_stream.GetString().c_str(),
+ nullptr, // No type parameter.
+ PrintToString(*param_it).c_str(), code_location_,
+ GetTestSuiteTypeId(),
+ SuiteApiResolver<TestSuite>::GetSetUpCaseOrSuite(file, line),
+ SuiteApiResolver<TestSuite>::GetTearDownCaseOrSuite(file, line),
test_info->test_meta_factory->CreateTestFactory(*param_it));
} // for param_it
} // for gen_it
} // for test_it
- } // RegisterTests
+ } // RegisterTests
private:
// LocalTestInfo structure keeps information about a single test registered
// with TEST_P macro.
struct TestInfo {
- TestInfo(const char* a_test_case_base_name,
- const char* a_test_base_name,
- TestMetaFactoryBase<ParamType>* a_test_meta_factory) :
- test_case_base_name(a_test_case_base_name),
- test_base_name(a_test_base_name),
- test_meta_factory(a_test_meta_factory) {}
-
- const string test_case_base_name;
- const string test_base_name;
- const scoped_ptr<TestMetaFactoryBase<ParamType> > test_meta_factory;
+ TestInfo(const char* a_test_suite_base_name, const char* a_test_base_name,
+ TestMetaFactoryBase<ParamType>* a_test_meta_factory)
+ : test_suite_base_name(a_test_suite_base_name),
+ test_base_name(a_test_base_name),
+ test_meta_factory(a_test_meta_factory) {}
+
+ const std::string test_suite_base_name;
+ const std::string test_base_name;
+ const std::unique_ptr<TestMetaFactoryBase<ParamType> > test_meta_factory;
};
- typedef ::std::vector<linked_ptr<TestInfo> > TestInfoContainer;
- // Records data received from INSTANTIATE_TEST_CASE_P macros:
+ using TestInfoContainer = ::std::vector<std::shared_ptr<TestInfo> >;
+ // Records data received from INSTANTIATE_TEST_SUITE_P macros:
// <Instantiation name, Sequence generator creation function,
// Name generator function, Source file, Source line>
struct InstantiationInfo {
@@ -651,81 +634,250 @@ class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase {
return true;
}
- const string test_case_name_;
+ const std::string test_suite_name_;
CodeLocation code_location_;
TestInfoContainer tests_;
InstantiationContainer instantiations_;
- GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseInfo);
-}; // class ParameterizedTestCaseInfo
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestSuiteInfo);
+}; // class ParameterizedTestSuiteInfo
+
+// Legacy API is deprecated but still available
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+template <class TestCase>
+using ParameterizedTestCaseInfo = ParameterizedTestSuiteInfo<TestCase>;
+#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
//
-// ParameterizedTestCaseRegistry contains a map of ParameterizedTestCaseInfoBase
-// classes accessed by test case names. TEST_P and INSTANTIATE_TEST_CASE_P
-// macros use it to locate their corresponding ParameterizedTestCaseInfo
-// descriptors.
-class ParameterizedTestCaseRegistry {
+// ParameterizedTestSuiteRegistry contains a map of
+// ParameterizedTestSuiteInfoBase classes accessed by test suite names. TEST_P
+// and INSTANTIATE_TEST_SUITE_P macros use it to locate their corresponding
+// ParameterizedTestSuiteInfo descriptors.
+class ParameterizedTestSuiteRegistry {
public:
- ParameterizedTestCaseRegistry() {}
- ~ParameterizedTestCaseRegistry() {
- for (TestCaseInfoContainer::iterator it = test_case_infos_.begin();
- it != test_case_infos_.end(); ++it) {
- delete *it;
+ ParameterizedTestSuiteRegistry() {}
+ ~ParameterizedTestSuiteRegistry() {
+ for (auto& test_suite_info : test_suite_infos_) {
+ delete test_suite_info;
}
}
// Looks up or creates and returns a structure containing information about
- // tests and instantiations of a particular test case.
- template <class TestCase>
- ParameterizedTestCaseInfo<TestCase>* GetTestCasePatternHolder(
- const char* test_case_name,
- CodeLocation code_location) {
- ParameterizedTestCaseInfo<TestCase>* typed_test_info = NULL;
- for (TestCaseInfoContainer::iterator it = test_case_infos_.begin();
- it != test_case_infos_.end(); ++it) {
- if ((*it)->GetTestCaseName() == test_case_name) {
- if ((*it)->GetTestCaseTypeId() != GetTypeId<TestCase>()) {
+ // tests and instantiations of a particular test suite.
+ template <class TestSuite>
+ ParameterizedTestSuiteInfo<TestSuite>* GetTestSuitePatternHolder(
+ const char* test_suite_name, CodeLocation code_location) {
+ ParameterizedTestSuiteInfo<TestSuite>* typed_test_info = nullptr;
+ for (auto& test_suite_info : test_suite_infos_) {
+ if (test_suite_info->GetTestSuiteName() == test_suite_name) {
+ if (test_suite_info->GetTestSuiteTypeId() != GetTypeId<TestSuite>()) {
// Complain about incorrect usage of Google Test facilities
// and terminate the program since we cannot guaranty correct
- // test case setup and tear-down in this case.
- ReportInvalidTestCaseType(test_case_name, code_location);
+ // test suite setup and tear-down in this case.
+ ReportInvalidTestSuiteType(test_suite_name, code_location);
posix::Abort();
} else {
// At this point we are sure that the object we found is of the same
// type we are looking for, so we downcast it to that type
// without further checks.
typed_test_info = CheckedDowncastToActualType<
- ParameterizedTestCaseInfo<TestCase> >(*it);
+ ParameterizedTestSuiteInfo<TestSuite> >(test_suite_info);
}
break;
}
}
- if (typed_test_info == NULL) {
- typed_test_info = new ParameterizedTestCaseInfo<TestCase>(
- test_case_name, code_location);
- test_case_infos_.push_back(typed_test_info);
+ if (typed_test_info == nullptr) {
+ typed_test_info = new ParameterizedTestSuiteInfo<TestSuite>(
+ test_suite_name, code_location);
+ test_suite_infos_.push_back(typed_test_info);
}
return typed_test_info;
}
void RegisterTests() {
- for (TestCaseInfoContainer::iterator it = test_case_infos_.begin();
- it != test_case_infos_.end(); ++it) {
- (*it)->RegisterTests();
+ for (auto& test_suite_info : test_suite_infos_) {
+ test_suite_info->RegisterTests();
}
}
+// Legacy API is deprecated but still available
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+ template <class TestCase>
+ ParameterizedTestCaseInfo<TestCase>* GetTestCasePatternHolder(
+ const char* test_case_name, CodeLocation code_location) {
+ return GetTestSuitePatternHolder<TestCase>(test_case_name, code_location);
+ }
+
+#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
private:
- typedef ::std::vector<ParameterizedTestCaseInfoBase*> TestCaseInfoContainer;
+ using TestSuiteInfoContainer = ::std::vector<ParameterizedTestSuiteInfoBase*>;
- TestCaseInfoContainer test_case_infos_;
+ TestSuiteInfoContainer test_suite_infos_;
- GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseRegistry);
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestSuiteRegistry);
};
} // namespace internal
-} // namespace testing
-#endif // GTEST_HAS_PARAM_TEST
+// Forward declarations of ValuesIn(), which is implemented in
+// include/gtest/gtest-param-test.h.
+template <class Container>
+internal::ParamGenerator<typename Container::value_type> ValuesIn(
+ const Container& container);
+
+namespace internal {
+// Used in the Values() function to provide polymorphic capabilities.
+
+template <typename... Ts>
+class ValueArray {
+ public:
+ ValueArray(Ts... v) : v_{std::move(v)...} {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const { // NOLINT
+ return ValuesIn(MakeVector<T>(MakeIndexSequence<sizeof...(Ts)>()));
+ }
+
+ private:
+ template <typename T, size_t... I>
+ std::vector<T> MakeVector(IndexSequence<I...>) const {
+ return std::vector<T>{static_cast<T>(v_.template Get<I>())...};
+ }
+
+ FlatTuple<Ts...> v_;
+};
+
+template <typename... T>
+class CartesianProductGenerator
+ : public ParamGeneratorInterface<::std::tuple<T...>> {
+ public:
+ typedef ::std::tuple<T...> ParamType;
+
+ CartesianProductGenerator(const std::tuple<ParamGenerator<T>...>& g)
+ : generators_(g) {}
+ ~CartesianProductGenerator() override {}
+
+ ParamIteratorInterface<ParamType>* Begin() const override {
+ return new Iterator(this, generators_, false);
+ }
+ ParamIteratorInterface<ParamType>* End() const override {
+ return new Iterator(this, generators_, true);
+ }
+
+ private:
+ template <class I>
+ class IteratorImpl;
+ template <size_t... I>
+ class IteratorImpl<IndexSequence<I...>>
+ : public ParamIteratorInterface<ParamType> {
+ public:
+ IteratorImpl(const ParamGeneratorInterface<ParamType>* base,
+ const std::tuple<ParamGenerator<T>...>& generators, bool is_end)
+ : base_(base),
+ begin_(std::get<I>(generators).begin()...),
+ end_(std::get<I>(generators).end()...),
+ current_(is_end ? end_ : begin_) {
+ ComputeCurrentValue();
+ }
+ ~IteratorImpl() override {}
+
+ const ParamGeneratorInterface<ParamType>* BaseGenerator() const override {
+ return base_;
+ }
+ // Advance should not be called on beyond-of-range iterators
+ // so no component iterators must be beyond end of range, either.
+ void Advance() override {
+ assert(!AtEnd());
+ // Advance the last iterator.
+ ++std::get<sizeof...(T) - 1>(current_);
+ // if that reaches end, propagate that up.
+ AdvanceIfEnd<sizeof...(T) - 1>();
+ ComputeCurrentValue();
+ }
+ ParamIteratorInterface<ParamType>* Clone() const override {
+ return new IteratorImpl(*this);
+ }
+
+ const ParamType* Current() const override { return current_value_.get(); }
+
+ bool Equals(const ParamIteratorInterface<ParamType>& other) const override {
+ // Having the same base generator guarantees that the other
+ // iterator is of the same type and we can downcast.
+ GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
+ << "The program attempted to compare iterators "
+ << "from different generators." << std::endl;
+ const IteratorImpl* typed_other =
+ CheckedDowncastToActualType<const IteratorImpl>(&other);
+
+ // We must report iterators equal if they both point beyond their
+ // respective ranges. That can happen in a variety of fashions,
+ // so we have to consult AtEnd().
+ if (AtEnd() && typed_other->AtEnd()) return true;
+
+ bool same = true;
+ bool dummy[] = {
+ (same = same && std::get<I>(current_) ==
+ std::get<I>(typed_other->current_))...};
+ (void)dummy;
+ return same;
+ }
+
+ private:
+ template <size_t ThisI>
+ void AdvanceIfEnd() {
+ if (std::get<ThisI>(current_) != std::get<ThisI>(end_)) return;
+
+ bool last = ThisI == 0;
+ if (last) {
+ // We are done. Nothing else to propagate.
+ return;
+ }
+
+ constexpr size_t NextI = ThisI - (ThisI != 0);
+ std::get<ThisI>(current_) = std::get<ThisI>(begin_);
+ ++std::get<NextI>(current_);
+ AdvanceIfEnd<NextI>();
+ }
+
+ void ComputeCurrentValue() {
+ if (!AtEnd())
+ current_value_ = std::make_shared<ParamType>(*std::get<I>(current_)...);
+ }
+ bool AtEnd() const {
+ bool at_end = false;
+ bool dummy[] = {
+ (at_end = at_end || std::get<I>(current_) == std::get<I>(end_))...};
+ (void)dummy;
+ return at_end;
+ }
+
+ const ParamGeneratorInterface<ParamType>* const base_;
+ std::tuple<typename ParamGenerator<T>::iterator...> begin_;
+ std::tuple<typename ParamGenerator<T>::iterator...> end_;
+ std::tuple<typename ParamGenerator<T>::iterator...> current_;
+ std::shared_ptr<ParamType> current_value_;
+ };
+
+ using Iterator = IteratorImpl<typename MakeIndexSequence<sizeof...(T)>::type>;
+
+ std::tuple<ParamGenerator<T>...> generators_;
+};
+
+template <class... Gen>
+class CartesianProductHolder {
+ public:
+ CartesianProductHolder(const Gen&... g) : generators_(g...) {}
+ template <typename... T>
+ operator ParamGenerator<::std::tuple<T...>>() const {
+ return ParamGenerator<::std::tuple<T...>>(
+ new CartesianProductGenerator<T...>(generators_));
+ }
+
+ private:
+ std::tuple<Gen...> generators_;
+};
+
+} // namespace internal
+} // namespace testing
#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_
diff --git a/extern/gtest/include/gtest/internal/gtest-port-arch.h b/extern/gtest/include/gtest/internal/gtest-port-arch.h
index 74ab949057c..cece93dba12 100644
--- a/extern/gtest/include/gtest/internal/gtest-port-arch.h
+++ b/extern/gtest/include/gtest/internal/gtest-port-arch.h
@@ -27,7 +27,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
-// The Google C++ Testing Framework (Google Test)
+// The Google C++ Testing and Mocking Framework (Google Test)
//
// This header file defines the GTEST_OS_* macro.
// It is separate from gtest-port.h so that custom/gtest-port.h can include it.
@@ -38,14 +38,13 @@
// Determines the platform on which Google Test is compiled.
#ifdef __CYGWIN__
# define GTEST_OS_CYGWIN 1
-#elif defined __SYMBIAN32__
-# define GTEST_OS_SYMBIAN 1
+# elif defined(__MINGW__) || defined(__MINGW32__) || defined(__MINGW64__)
+# define GTEST_OS_WINDOWS_MINGW 1
+# define GTEST_OS_WINDOWS 1
#elif defined _WIN32
# define GTEST_OS_WINDOWS 1
# ifdef _WIN32_WCE
# define GTEST_OS_WINDOWS_MOBILE 1
-# elif defined(__MINGW__) || defined(__MINGW32__)
-# define GTEST_OS_WINDOWS_MINGW 1
# elif defined(WINAPI_FAMILY)
# include <winapifamily.h>
# if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
@@ -54,6 +53,9 @@
# define GTEST_OS_WINDOWS_PHONE 1
# elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)
# define GTEST_OS_WINDOWS_RT 1
+# elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_TV_TITLE)
+# define GTEST_OS_WINDOWS_PHONE 1
+# define GTEST_OS_WINDOWS_TV_TITLE 1
# else
// WINAPI_FAMILY defined but no known partition matched.
// Default to desktop.
@@ -62,13 +64,21 @@
# else
# define GTEST_OS_WINDOWS_DESKTOP 1
# endif // _WIN32_WCE
+#elif defined __OS2__
+# define GTEST_OS_OS2 1
#elif defined __APPLE__
# define GTEST_OS_MAC 1
# if TARGET_OS_IPHONE
# define GTEST_OS_IOS 1
# endif
+#elif defined __DragonFly__
+# define GTEST_OS_DRAGONFLY 1
#elif defined __FreeBSD__
# define GTEST_OS_FREEBSD 1
+#elif defined __Fuchsia__
+# define GTEST_OS_FUCHSIA 1
+#elif defined(__GLIBC__) && defined(__FreeBSD_kernel__)
+# define GTEST_OS_GNU_KFREEBSD 1
#elif defined __linux__
# define GTEST_OS_LINUX 1
# if defined __ANDROID__
@@ -84,10 +94,14 @@
# define GTEST_OS_HPUX 1
#elif defined __native_client__
# define GTEST_OS_NACL 1
+#elif defined __NetBSD__
+# define GTEST_OS_NETBSD 1
#elif defined __OpenBSD__
# define GTEST_OS_OPENBSD 1
#elif defined __QNX__
# define GTEST_OS_QNX 1
+#elif defined(__HAIKU__)
+#define GTEST_OS_HAIKU 1
#endif // __CYGWIN__
#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_
diff --git a/extern/gtest/include/gtest/internal/gtest-port.h b/extern/gtest/include/gtest/internal/gtest-port.h
index 0094ed5077e..063fcb1083b 100644
--- a/extern/gtest/include/gtest/internal/gtest-port.h
+++ b/extern/gtest/include/gtest/internal/gtest-port.h
@@ -27,8 +27,6 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
-// Authors: wan@google.com (Zhanyong Wan)
-//
// Low-level types and utilities for porting Google Test to various
// platforms. All macros ending with _ and symbols defined in an
// internal namespace are subject to change without notice. Code
@@ -40,6 +38,8 @@
// files are expected to #include this. Therefore, it cannot #include
// any other Google Test header.
+// GOOGLETEST_CM0001 DO NOT DELETE
+
#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_
#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_
@@ -72,12 +72,6 @@
// is/isn't available.
// GTEST_HAS_EXCEPTIONS - Define it to 1/0 to indicate that exceptions
// are enabled.
-// GTEST_HAS_GLOBAL_STRING - Define it to 1/0 to indicate that ::string
-// is/isn't available (some systems define
-// ::string, which is different to std::string).
-// GTEST_HAS_GLOBAL_WSTRING - Define it to 1/0 to indicate that ::string
-// is/isn't available (some systems define
-// ::wstring, which is different to std::wstring).
// GTEST_HAS_POSIX_RE - Define it to 1/0 to indicate that POSIX regular
// expressions are/aren't available.
// GTEST_HAS_PTHREAD - Define it to 1/0 to indicate that <pthread.h>
@@ -87,8 +81,6 @@
// GTEST_HAS_STD_WSTRING - Define it to 1/0 to indicate that
// std::wstring does/doesn't work (Google Test can
// be used where std::wstring is unavailable).
-// GTEST_HAS_TR1_TUPLE - Define it to 1/0 to indicate tr1::tuple
-// is/isn't available.
// GTEST_HAS_SEH - Define it to 1/0 to indicate whether the
// compiler supports Microsoft's "Structured
// Exception Handling".
@@ -96,12 +88,6 @@
// - Define it to 1/0 to indicate whether the
// platform supports I/O stream redirection using
// dup() and dup2().
-// GTEST_USE_OWN_TR1_TUPLE - Define it to 1/0 to indicate whether Google
-// Test's own tr1 tuple implementation should be
-// used. Unused when the user sets
-// GTEST_HAS_TR1_TUPLE to 0.
-// GTEST_LANG_CXX11 - Define it to 1/0 to indicate that Google Test
-// is building in C++11/C++98 mode.
// GTEST_LINKED_AS_SHARED_LIBRARY
// - Define to 1 when compiling tests that use
// Google Test as a shared library (known as
@@ -109,6 +95,12 @@
// GTEST_CREATE_SHARED_LIBRARY
// - Define to 1 when compiling Google Test itself
// as a shared library.
+// GTEST_DEFAULT_DEATH_TEST_STYLE
+// - The default value of --gtest_death_test_style.
+// The legacy default has been "fast" in the open
+// source version since 2008. The recommended value
+// is "threadsafe", and can be set in
+// custom/gtest-port.h.
// Platform-indicating macros
// --------------------------
@@ -121,17 +113,22 @@
//
// GTEST_OS_AIX - IBM AIX
// GTEST_OS_CYGWIN - Cygwin
+// GTEST_OS_DRAGONFLY - DragonFlyBSD
// GTEST_OS_FREEBSD - FreeBSD
+// GTEST_OS_FUCHSIA - Fuchsia
+// GTEST_OS_GNU_KFREEBSD - GNU/kFreeBSD
+// GTEST_OS_HAIKU - Haiku
// GTEST_OS_HPUX - HP-UX
// GTEST_OS_LINUX - Linux
// GTEST_OS_LINUX_ANDROID - Google Android
// GTEST_OS_MAC - Mac OS X
// GTEST_OS_IOS - iOS
// GTEST_OS_NACL - Google Native Client (NaCl)
+// GTEST_OS_NETBSD - NetBSD
// GTEST_OS_OPENBSD - OpenBSD
+// GTEST_OS_OS2 - OS/2
// GTEST_OS_QNX - QNX
// GTEST_OS_SOLARIS - Sun Solaris
-// GTEST_OS_SYMBIAN - Symbian
// GTEST_OS_WINDOWS - Windows (Desktop, MinGW, or Mobile)
// GTEST_OS_WINDOWS_DESKTOP - Windows Desktop
// GTEST_OS_WINDOWS_MINGW - MinGW
@@ -140,7 +137,7 @@
// GTEST_OS_WINDOWS_RT - Windows Store App/WinRT
// GTEST_OS_ZOS - z/OS
//
-// Among the platforms, Cygwin, Linux, Max OS X, and Windows have the
+// Among the platforms, Cygwin, Linux, Mac OS X, and Windows have the
// most stable support. Since core members of the Google Test project
// don't have access to other platforms, support for them may be less
// stable. If you notice any problems on your platform, please notify
@@ -166,19 +163,16 @@
// EXPECT_DEATH(DoSomethingDeadly());
// #endif
//
-// GTEST_HAS_COMBINE - the Combine() function (for value-parameterized
-// tests)
// GTEST_HAS_DEATH_TEST - death tests
-// GTEST_HAS_PARAM_TEST - value-parameterized tests
// GTEST_HAS_TYPED_TEST - typed tests
// GTEST_HAS_TYPED_TEST_P - type-parameterized tests
// GTEST_IS_THREADSAFE - Google Test is thread-safe.
+// GOOGLETEST_CM0007 DO NOT DELETE
// GTEST_USES_POSIX_RE - enhanced POSIX regex is used. Do not confuse with
// GTEST_HAS_POSIX_RE (see above) which users can
// define themselves.
// GTEST_USES_SIMPLE_RE - our own simple regex is used;
-// the above two are mutually exclusive.
-// GTEST_CAN_COMPARE_NULL - accepts untyped NULL in EXPECT_EQ().
+// the above RE\b(s) are mutually exclusive.
// Misc public macros
// ------------------
@@ -204,28 +198,16 @@
// GTEST_INTENTIONAL_CONST_COND_POP_ - finish code section where MSVC C4127
// is suppressed.
//
-// C++11 feature wrappers:
-//
-// testing::internal::move - portability wrapper for std::move.
-//
// Synchronization:
// Mutex, MutexLock, ThreadLocal, GetThreadCount()
// - synchronization primitives.
//
-// Template meta programming:
-// is_pointer - as in TR1; needed on Symbian and IBM XL C/C++ only.
-// IteratorTraits - partial implementation of std::iterator_traits, which
-// is not available in libCstd when compiled with Sun C++.
-//
-// Smart pointers:
-// scoped_ptr - as in TR2.
-//
// Regular expressions:
// RE - a simple regular expression class using the POSIX
-// Extended Regular Expression syntax on UNIX-like
-// platforms, or a reduced regular exception syntax on
-// other platforms, including Windows.
-//
+// Extended Regular Expression syntax on UNIX-like platforms
+// GOOGLETEST_CM0008 DO NOT DELETE
+// or a reduced regular exception syntax on other
+// platforms, including Windows.
// Logging:
// GTEST_LOG_() - logs messages at the specified severity level.
// LogToStderr() - directs all log messages to stderr.
@@ -255,12 +237,20 @@
// BoolFromGTestEnv() - parses a bool environment variable.
// Int32FromGTestEnv() - parses an Int32 environment variable.
// StringFromGTestEnv() - parses a string environment variable.
+//
+// Deprecation warnings:
+// GTEST_INTERNAL_DEPRECATED(message) - attribute marking a function as
+// deprecated; calling a marked function
+// should generate a compiler warning
#include <ctype.h> // for isspace, etc
#include <stddef.h> // for ptrdiff_t
-#include <stdlib.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
+#include <memory>
+#include <type_traits>
+
#ifndef _WIN32_WCE
# include <sys/types.h>
# include <sys/stat.h>
@@ -272,9 +262,10 @@
#endif
#include <algorithm> // NOLINT
-#include <iostream> // NOLINT
-#include <sstream> // NOLINT
-#include <string> // NOLINT
+#include <iostream> // NOLINT
+#include <sstream> // NOLINT
+#include <string> // NOLINT
+#include <tuple>
#include <utility>
#include <vector> // NOLINT
@@ -306,85 +297,32 @@
// GTEST_DISABLE_MSC_WARNINGS_PUSH_(4800 4385)
// /* code that triggers warnings C4800 and C4385 */
// GTEST_DISABLE_MSC_WARNINGS_POP_()
-#if _MSC_VER >= 1500
+#if defined(_MSC_VER)
# define GTEST_DISABLE_MSC_WARNINGS_PUSH_(warnings) \
__pragma(warning(push)) \
__pragma(warning(disable: warnings))
# define GTEST_DISABLE_MSC_WARNINGS_POP_() \
__pragma(warning(pop))
#else
-// Older versions of MSVC don't have __pragma.
+// Not all compilers are MSVC
# define GTEST_DISABLE_MSC_WARNINGS_PUSH_(warnings)
# define GTEST_DISABLE_MSC_WARNINGS_POP_()
#endif
-#ifndef GTEST_LANG_CXX11
-// gcc and clang define __GXX_EXPERIMENTAL_CXX0X__ when
-// -std={c,gnu}++{0x,11} is passed. The C++11 standard specifies a
-// value for __cplusplus, and recent versions of clang, gcc, and
-// probably other compilers set that too in C++11 mode.
-# if __GXX_EXPERIMENTAL_CXX0X__ || __cplusplus >= 201103L
-// Compiling in at least C++11 mode.
-# define GTEST_LANG_CXX11 1
-# else
-# define GTEST_LANG_CXX11 0
-# endif
-#endif
-
-// Distinct from C++11 language support, some environments don't provide
-// proper C++11 library support. Notably, it's possible to build in
-// C++11 mode when targeting Mac OS X 10.6, which has an old libstdc++
-// with no C++11 support.
-//
-// libstdc++ has sufficient C++11 support as of GCC 4.6.0, __GLIBCXX__
-// 20110325, but maintenance releases in the 4.4 and 4.5 series followed
-// this date, so check for those versions by their date stamps.
-// https://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html#abi.versioning
-#if GTEST_LANG_CXX11 && \
- (!defined(__GLIBCXX__) || ( \
- __GLIBCXX__ >= 20110325ul && /* GCC >= 4.6.0 */ \
- /* Blacklist of patch releases of older branches: */ \
- __GLIBCXX__ != 20110416ul && /* GCC 4.4.6 */ \
- __GLIBCXX__ != 20120313ul && /* GCC 4.4.7 */ \
- __GLIBCXX__ != 20110428ul && /* GCC 4.5.3 */ \
- __GLIBCXX__ != 20120702ul)) /* GCC 4.5.4 */
-# define GTEST_STDLIB_CXX11 1
-#endif
-
-// Only use C++11 library features if the library provides them.
-#if GTEST_STDLIB_CXX11
-# define GTEST_HAS_STD_BEGIN_AND_END_ 1
-# define GTEST_HAS_STD_FORWARD_LIST_ 1
-# define GTEST_HAS_STD_FUNCTION_ 1
-# define GTEST_HAS_STD_INITIALIZER_LIST_ 1
-# define GTEST_HAS_STD_MOVE_ 1
-# define GTEST_HAS_STD_SHARED_PTR_ 1
-# define GTEST_HAS_STD_TYPE_TRAITS_ 1
-# define GTEST_HAS_STD_UNIQUE_PTR_ 1
-#endif
-
-// C++11 specifies that <tuple> provides std::tuple.
-// Some platforms still might not have it, however.
-#if GTEST_LANG_CXX11
-# define GTEST_HAS_STD_TUPLE_ 1
-# if defined(__clang__)
-// Inspired by http://clang.llvm.org/docs/LanguageExtensions.html#__has_include
-# if defined(__has_include) && !__has_include(<tuple>)
-# undef GTEST_HAS_STD_TUPLE_
-# endif
-# elif defined(_MSC_VER)
-// Inspired by boost/config/stdlib/dinkumware.hpp
-# if defined(_CPPLIB_VER) && _CPPLIB_VER < 520
-# undef GTEST_HAS_STD_TUPLE_
-# endif
-# elif defined(__GLIBCXX__)
-// Inspired by boost/config/stdlib/libstdcpp3.hpp,
-// http://gcc.gnu.org/gcc-4.2/changes.html and
-// http://gcc.gnu.org/onlinedocs/libstdc++/manual/bk01pt01ch01.html#manual.intro.status.standard.200x
-# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 2)
-# undef GTEST_HAS_STD_TUPLE_
-# endif
-# endif
+// Clang on Windows does not understand MSVC's pragma warning.
+// We need clang-specific way to disable function deprecation warning.
+#ifdef __clang__
+# define GTEST_DISABLE_MSC_DEPRECATED_PUSH_() \
+ _Pragma("clang diagnostic push") \
+ _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") \
+ _Pragma("clang diagnostic ignored \"-Wdeprecated-implementations\"")
+#define GTEST_DISABLE_MSC_DEPRECATED_POP_() \
+ _Pragma("clang diagnostic pop")
+#else
+# define GTEST_DISABLE_MSC_DEPRECATED_PUSH_() \
+ GTEST_DISABLE_MSC_WARNINGS_PUSH_(4996)
+# define GTEST_DISABLE_MSC_DEPRECATED_POP_() \
+ GTEST_DISABLE_MSC_WARNINGS_POP_()
#endif
// Brings in definitions for functions used in the testing::internal::posix
@@ -396,10 +334,16 @@
# include <io.h>
# endif
// In order to avoid having to include <windows.h>, use forward declaration
-// assuming CRITICAL_SECTION is a typedef of _RTL_CRITICAL_SECTION.
+#if GTEST_OS_WINDOWS_MINGW && !defined(__MINGW64_VERSION_MAJOR)
+// MinGW defined _CRITICAL_SECTION and _RTL_CRITICAL_SECTION as two
+// separate (equivalent) structs, instead of using typedef
+typedef struct _CRITICAL_SECTION GTEST_CRITICAL_SECTION;
+#else
+// Assume CRITICAL_SECTION is a typedef of _RTL_CRITICAL_SECTION.
// This assumption is verified by
// WindowsTypesTest.CRITICAL_SECTIONIs_RTL_CRITICAL_SECTION.
-struct _RTL_CRITICAL_SECTION;
+typedef struct _RTL_CRITICAL_SECTION GTEST_CRITICAL_SECTION;
+#endif
#else
// This assumes that non-Windows OSes provide unistd.h. For OSes where this
// is not the case, we need to include headers that provide the functions
@@ -413,7 +357,8 @@ struct _RTL_CRITICAL_SECTION;
# include <android/api-level.h> // NOLINT
#endif
-// Defines this to true iff Google Test can use POSIX regular expressions.
+// Defines this to true if and only if Google Test can use POSIX regular
+// expressions.
#ifndef GTEST_HAS_POSIX_RE
# if GTEST_OS_LINUX_ANDROID
// On Android, <regex.h> is only available starting with Gingerbread.
@@ -453,8 +398,11 @@ struct _RTL_CRITICAL_SECTION;
#ifndef GTEST_HAS_EXCEPTIONS
// The user didn't tell us whether exceptions are enabled, so we need
// to figure it out.
-# if defined(_MSC_VER) || defined(__BORLANDC__)
-// MSVC's and C++Builder's implementations of the STL use the _HAS_EXCEPTIONS
+# if defined(_MSC_VER) && defined(_CPPUNWIND)
+// MSVC defines _CPPUNWIND to 1 if and only if exceptions are enabled.
+# define GTEST_HAS_EXCEPTIONS 1
+# elif defined(__BORLANDC__)
+// C++Builder's implementation of the STL uses the _HAS_EXCEPTIONS
// macro to enable exceptions, so we'll do the same.
// Assumes that exceptions are enabled by default.
# ifndef _HAS_EXCEPTIONS
@@ -462,16 +410,17 @@ struct _RTL_CRITICAL_SECTION;
# endif // _HAS_EXCEPTIONS
# define GTEST_HAS_EXCEPTIONS _HAS_EXCEPTIONS
# elif defined(__clang__)
-// clang defines __EXCEPTIONS iff exceptions are enabled before clang 220714,
-// but iff cleanups are enabled after that. In Obj-C++ files, there can be
-// cleanups for ObjC exceptions which also need cleanups, even if C++ exceptions
-// are disabled. clang has __has_feature(cxx_exceptions) which checks for C++
-// exceptions starting at clang r206352, but which checked for cleanups prior to
-// that. To reliably check for C++ exception availability with clang, check for
+// clang defines __EXCEPTIONS if and only if exceptions are enabled before clang
+// 220714, but if and only if cleanups are enabled after that. In Obj-C++ files,
+// there can be cleanups for ObjC exceptions which also need cleanups, even if
+// C++ exceptions are disabled. clang has __has_feature(cxx_exceptions) which
+// checks for C++ exceptions starting at clang r206352, but which checked for
+// cleanups prior to that. To reliably check for C++ exception availability with
+// clang, check for
// __EXCEPTIONS && __has_feature(cxx_exceptions).
# define GTEST_HAS_EXCEPTIONS (__EXCEPTIONS && __has_feature(cxx_exceptions))
# elif defined(__GNUC__) && __EXCEPTIONS
-// gcc defines __EXCEPTIONS to 1 iff exceptions are enabled.
+// gcc defines __EXCEPTIONS to 1 if and only if exceptions are enabled.
# define GTEST_HAS_EXCEPTIONS 1
# elif defined(__SUNPRO_CC)
// Sun Pro CC supports exceptions. However, there is no compile-time way of
@@ -479,7 +428,7 @@ struct _RTL_CRITICAL_SECTION;
// they are enabled unless the user tells us otherwise.
# define GTEST_HAS_EXCEPTIONS 1
# elif defined(__IBMCPP__) && __EXCEPTIONS
-// xlC defines __EXCEPTIONS to 1 iff exceptions are enabled.
+// xlC defines __EXCEPTIONS to 1 if and only if exceptions are enabled.
# define GTEST_HAS_EXCEPTIONS 1
# elif defined(__HP_aCC)
// Exception handling is in effect by default in HP aCC compiler. It has to
@@ -498,38 +447,21 @@ struct _RTL_CRITICAL_SECTION;
# define GTEST_HAS_STD_STRING 1
#elif !GTEST_HAS_STD_STRING
// The user told us that ::std::string isn't available.
-# error "Google Test cannot be used where ::std::string isn't available."
+# error "::std::string isn't available."
#endif // !defined(GTEST_HAS_STD_STRING)
-#ifndef GTEST_HAS_GLOBAL_STRING
-// The user didn't tell us whether ::string is available, so we need
-// to figure it out.
-
-# define GTEST_HAS_GLOBAL_STRING 0
-
-#endif // GTEST_HAS_GLOBAL_STRING
-
#ifndef GTEST_HAS_STD_WSTRING
// The user didn't tell us whether ::std::wstring is available, so we need
// to figure it out.
-// TODO(wan@google.com): uses autoconf to detect whether ::std::wstring
-// is available.
-
// Cygwin 1.7 and below doesn't support ::std::wstring.
// Solaris' libc++ doesn't support it either. Android has
// no support for it at least as recent as Froyo (2.2).
-# define GTEST_HAS_STD_WSTRING \
- (!(GTEST_OS_LINUX_ANDROID || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS))
+#define GTEST_HAS_STD_WSTRING \
+ (!(GTEST_OS_LINUX_ANDROID || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS || \
+ GTEST_OS_HAIKU))
#endif // GTEST_HAS_STD_WSTRING
-#ifndef GTEST_HAS_GLOBAL_WSTRING
-// The user didn't tell us whether ::wstring is available, so we need
-// to figure it out.
-# define GTEST_HAS_GLOBAL_WSTRING \
- (GTEST_HAS_STD_WSTRING && GTEST_HAS_GLOBAL_STRING)
-#endif // GTEST_HAS_GLOBAL_WSTRING
-
// Determines whether RTTI is available.
#ifndef GTEST_HAS_RTTI
// The user didn't tell us whether RTTI is enabled, so we need to
@@ -537,14 +469,15 @@ struct _RTL_CRITICAL_SECTION;
# ifdef _MSC_VER
-# ifdef _CPPRTTI // MSVC defines this macro iff RTTI is enabled.
+#ifdef _CPPRTTI // MSVC defines this macro if and only if RTTI is enabled.
# define GTEST_HAS_RTTI 1
# else
# define GTEST_HAS_RTTI 0
# endif
-// Starting with version 4.3.2, gcc defines __GXX_RTTI iff RTTI is enabled.
-# elif defined(__GNUC__) && (GTEST_GCC_VER_ >= 40302)
+// Starting with version 4.3.2, gcc defines __GXX_RTTI if and only if RTTI is
+// enabled.
+# elif defined(__GNUC__)
# ifdef __GXX_RTTI
// When building against STLport with the Android NDK and with
@@ -600,8 +533,11 @@ struct _RTL_CRITICAL_SECTION;
//
// To disable threading support in Google Test, add -DGTEST_HAS_PTHREAD=0
// to your compiler flags.
-# define GTEST_HAS_PTHREAD (GTEST_OS_LINUX || GTEST_OS_MAC || GTEST_OS_HPUX \
- || GTEST_OS_QNX || GTEST_OS_FREEBSD || GTEST_OS_NACL)
+#define GTEST_HAS_PTHREAD \
+ (GTEST_OS_LINUX || GTEST_OS_MAC || GTEST_OS_HPUX || GTEST_OS_QNX || \
+ GTEST_OS_FREEBSD || GTEST_OS_NACL || GTEST_OS_NETBSD || GTEST_OS_FUCHSIA || \
+ GTEST_OS_DRAGONFLY || GTEST_OS_GNU_KFREEBSD || GTEST_OS_OPENBSD || \
+ GTEST_OS_HAIKU)
#endif // GTEST_HAS_PTHREAD
#if GTEST_HAS_PTHREAD
@@ -613,138 +549,6 @@ struct _RTL_CRITICAL_SECTION;
# include <time.h> // NOLINT
#endif
-// Determines if hash_map/hash_set are available.
-// Only used for testing against those containers.
-#if !defined(GTEST_HAS_HASH_MAP_)
-# if _MSC_VER
-# define GTEST_HAS_HASH_MAP_ 1 // Indicates that hash_map is available.
-# define GTEST_HAS_HASH_SET_ 1 // Indicates that hash_set is available.
-# endif // _MSC_VER
-#endif // !defined(GTEST_HAS_HASH_MAP_)
-
-// Determines whether Google Test can use tr1/tuple. You can define
-// this macro to 0 to prevent Google Test from using tuple (any
-// feature depending on tuple with be disabled in this mode).
-#ifndef GTEST_HAS_TR1_TUPLE
-# if GTEST_OS_LINUX_ANDROID && defined(_STLPORT_MAJOR)
-// STLport, provided with the Android NDK, has neither <tr1/tuple> or <tuple>.
-# define GTEST_HAS_TR1_TUPLE 0
-# else
-// The user didn't tell us not to do it, so we assume it's OK.
-# define GTEST_HAS_TR1_TUPLE 1
-# endif
-#endif // GTEST_HAS_TR1_TUPLE
-
-// Determines whether Google Test's own tr1 tuple implementation
-// should be used.
-#ifndef GTEST_USE_OWN_TR1_TUPLE
-// The user didn't tell us, so we need to figure it out.
-
-// We use our own TR1 tuple if we aren't sure the user has an
-// implementation of it already. At this time, libstdc++ 4.0.0+ and
-// MSVC 2010 are the only mainstream standard libraries that come
-// with a TR1 tuple implementation. NVIDIA's CUDA NVCC compiler
-// pretends to be GCC by defining __GNUC__ and friends, but cannot
-// compile GCC's tuple implementation. MSVC 2008 (9.0) provides TR1
-// tuple in a 323 MB Feature Pack download, which we cannot assume the
-// user has. QNX's QCC compiler is a modified GCC but it doesn't
-// support TR1 tuple. libc++ only provides std::tuple, in C++11 mode,
-// and it can be used with some compilers that define __GNUC__.
-# if (defined(__GNUC__) && !defined(__CUDACC__) && (GTEST_GCC_VER_ >= 40000) \
- && !GTEST_OS_QNX && !defined(_LIBCPP_VERSION)) || _MSC_VER >= 1600
-# define GTEST_ENV_HAS_TR1_TUPLE_ 1
-# endif
-
-// C++11 specifies that <tuple> provides std::tuple. Use that if gtest is used
-// in C++11 mode and libstdc++ isn't very old (binaries targeting OS X 10.6
-// can build with clang but need to use gcc4.2's libstdc++).
-# if GTEST_LANG_CXX11 && (!defined(__GLIBCXX__) || __GLIBCXX__ > 20110325)
-# define GTEST_ENV_HAS_STD_TUPLE_ 1
-# endif
-
-# if GTEST_ENV_HAS_TR1_TUPLE_ || GTEST_ENV_HAS_STD_TUPLE_
-# define GTEST_USE_OWN_TR1_TUPLE 0
-# else
-# define GTEST_USE_OWN_TR1_TUPLE 1
-# endif
-
-#endif // GTEST_USE_OWN_TR1_TUPLE
-
-// To avoid conditional compilation everywhere, we make it
-// gtest-port.h's responsibility to #include the header implementing
-// tuple.
-#if GTEST_HAS_STD_TUPLE_
-# include <tuple> // IWYU pragma: export
-# define GTEST_TUPLE_NAMESPACE_ ::std
-#endif // GTEST_HAS_STD_TUPLE_
-
-// We include tr1::tuple even if std::tuple is available to define printers for
-// them.
-#if GTEST_HAS_TR1_TUPLE
-# ifndef GTEST_TUPLE_NAMESPACE_
-# define GTEST_TUPLE_NAMESPACE_ ::std::tr1
-# endif // GTEST_TUPLE_NAMESPACE_
-
-# if GTEST_USE_OWN_TR1_TUPLE
-# include "gtest/internal/gtest-tuple.h" // IWYU pragma: export // NOLINT
-# elif GTEST_ENV_HAS_STD_TUPLE_
-# include <tuple>
-// C++11 puts its tuple into the ::std namespace rather than
-// ::std::tr1. gtest expects tuple to live in ::std::tr1, so put it there.
-// This causes undefined behavior, but supported compilers react in
-// the way we intend.
-namespace std {
-namespace tr1 {
-using ::std::get;
-using ::std::make_tuple;
-using ::std::tuple;
-using ::std::tuple_element;
-using ::std::tuple_size;
-}
-}
-
-# elif GTEST_OS_SYMBIAN
-
-// On Symbian, BOOST_HAS_TR1_TUPLE causes Boost's TR1 tuple library to
-// use STLport's tuple implementation, which unfortunately doesn't
-// work as the copy of STLport distributed with Symbian is incomplete.
-// By making sure BOOST_HAS_TR1_TUPLE is undefined, we force Boost to
-// use its own tuple implementation.
-# ifdef BOOST_HAS_TR1_TUPLE
-# undef BOOST_HAS_TR1_TUPLE
-# endif // BOOST_HAS_TR1_TUPLE
-
-// This prevents <boost/tr1/detail/config.hpp>, which defines
-// BOOST_HAS_TR1_TUPLE, from being #included by Boost's <tuple>.
-# define BOOST_TR1_DETAIL_CONFIG_HPP_INCLUDED
-# include <tuple> // IWYU pragma: export // NOLINT
-
-# elif defined(__GNUC__) && (GTEST_GCC_VER_ >= 40000)
-// GCC 4.0+ implements tr1/tuple in the <tr1/tuple> header. This does
-// not conform to the TR1 spec, which requires the header to be <tuple>.
-
-# if !GTEST_HAS_RTTI && GTEST_GCC_VER_ < 40302
-// Until version 4.3.2, gcc has a bug that causes <tr1/functional>,
-// which is #included by <tr1/tuple>, to not compile when RTTI is
-// disabled. _TR1_FUNCTIONAL is the header guard for
-// <tr1/functional>. Hence the following #define is a hack to prevent
-// <tr1/functional> from being included.
-# define _TR1_FUNCTIONAL 1
-# include <tr1/tuple>
-# undef _TR1_FUNCTIONAL // Allows the user to #include
- // <tr1/functional> if he chooses to.
-# else
-# include <tr1/tuple> // NOLINT
-# endif // !GTEST_HAS_RTTI && GTEST_GCC_VER_ < 40302
-
-# else
-// If the compiler is not GCC 4.0+, we assume the user is using a
-// spec-conforming TR1 implementation.
-# include <tuple> // IWYU pragma: export // NOLINT
-# endif // GTEST_USE_OWN_TR1_TUPLE
-
-#endif // GTEST_HAS_TR1_TUPLE
-
// Determines whether clone(2) is supported.
// Usually it will only be available on Linux, excluding
// Linux on the Itanium architecture.
@@ -754,8 +558,12 @@ using ::std::tuple_size;
# if GTEST_OS_LINUX && !defined(__ia64__)
# if GTEST_OS_LINUX_ANDROID
-// On Android, clone() is only available on ARM starting with Gingerbread.
-# if defined(__arm__) && __ANDROID_API__ >= 9
+// On Android, clone() became available at different API levels for each 32-bit
+// architecture.
+# if defined(__LP64__) || \
+ (defined(__arm__) && __ANDROID_API__ >= 9) || \
+ (defined(__mips__) && __ANDROID_API__ >= 12) || \
+ (defined(__i386__) && __ANDROID_API__ >= 17)
# define GTEST_HAS_CLONE 1
# else
# define GTEST_HAS_CLONE 0
@@ -774,55 +582,41 @@ using ::std::tuple_size;
#ifndef GTEST_HAS_STREAM_REDIRECTION
// By default, we assume that stream redirection is supported on all
// platforms except known mobile ones.
-# if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN || \
- GTEST_OS_WINDOWS_PHONE || GTEST_OS_WINDOWS_RT
+# if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_PHONE || GTEST_OS_WINDOWS_RT
# define GTEST_HAS_STREAM_REDIRECTION 0
# else
# define GTEST_HAS_STREAM_REDIRECTION 1
-# endif // !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_SYMBIAN
+# endif // !GTEST_OS_WINDOWS_MOBILE
#endif // GTEST_HAS_STREAM_REDIRECTION
// Determines whether to support death tests.
-// Google Test does not support death tests for VC 7.1 and earlier as
-// abort() in a VC 7.1 application compiled as GUI in debug config
// pops up a dialog window that cannot be suppressed programmatically.
-#if (GTEST_OS_LINUX || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS || \
- (GTEST_OS_MAC && !GTEST_OS_IOS) || \
- (GTEST_OS_WINDOWS_DESKTOP && _MSC_VER >= 1400) || \
- GTEST_OS_WINDOWS_MINGW || GTEST_OS_AIX || GTEST_OS_HPUX || \
- GTEST_OS_OPENBSD || GTEST_OS_QNX || GTEST_OS_FREEBSD)
+#if (GTEST_OS_LINUX || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS || \
+ (GTEST_OS_MAC && !GTEST_OS_IOS) || \
+ (GTEST_OS_WINDOWS_DESKTOP && _MSC_VER) || GTEST_OS_WINDOWS_MINGW || \
+ GTEST_OS_AIX || GTEST_OS_HPUX || GTEST_OS_OPENBSD || GTEST_OS_QNX || \
+ GTEST_OS_FREEBSD || GTEST_OS_NETBSD || GTEST_OS_FUCHSIA || \
+ GTEST_OS_DRAGONFLY || GTEST_OS_GNU_KFREEBSD || GTEST_OS_HAIKU)
# define GTEST_HAS_DEATH_TEST 1
#endif
-// We don't support MSVC 7.1 with exceptions disabled now. Therefore
-// all the compilers we care about are adequate for supporting
-// value-parameterized tests.
-#define GTEST_HAS_PARAM_TEST 1
-
// Determines whether to support type-driven tests.
// Typed tests need <typeinfo> and variadic macros, which GCC, VC++ 8.0,
// Sun Pro CC, IBM Visual Age, and HP aCC support.
-#if defined(__GNUC__) || (_MSC_VER >= 1400) || defined(__SUNPRO_CC) || \
+#if defined(__GNUC__) || defined(_MSC_VER) || defined(__SUNPRO_CC) || \
defined(__IBMCPP__) || defined(__HP_aCC)
# define GTEST_HAS_TYPED_TEST 1
# define GTEST_HAS_TYPED_TEST_P 1
#endif
-// Determines whether to support Combine(). This only makes sense when
-// value-parameterized tests are enabled. The implementation doesn't
-// work on Sun Studio since it doesn't understand templated conversion
-// operators.
-#if GTEST_HAS_PARAM_TEST && GTEST_HAS_TR1_TUPLE && !defined(__SUNPRO_CC)
-# define GTEST_HAS_COMBINE 1
-#endif
-
// Determines whether the system compiler uses UTF-16 for encoding wide strings.
#define GTEST_WIDE_STRING_USES_UTF16_ \
- (GTEST_OS_WINDOWS || GTEST_OS_CYGWIN || GTEST_OS_SYMBIAN || GTEST_OS_AIX)
+ (GTEST_OS_WINDOWS || GTEST_OS_CYGWIN || GTEST_OS_AIX || GTEST_OS_OS2)
// Determines whether test results can be streamed to a socket.
-#if GTEST_OS_LINUX
+#if GTEST_OS_LINUX || GTEST_OS_GNU_KFREEBSD || GTEST_OS_DRAGONFLY || \
+ GTEST_OS_FREEBSD || GTEST_OS_NETBSD || GTEST_OS_OPENBSD
# define GTEST_CAN_STREAM_RESULTS_ 1
#endif
@@ -864,15 +658,33 @@ using ::std::tuple_size;
# define GTEST_ATTRIBUTE_UNUSED_
#endif
+// Use this annotation before a function that takes a printf format string.
+#if (defined(__GNUC__) || defined(__clang__)) && !defined(COMPILER_ICC)
+# if defined(__MINGW_PRINTF_FORMAT)
+// MinGW has two different printf implementations. Ensure the format macro
+// matches the selected implementation. See
+// https://sourceforge.net/p/mingw-w64/wiki2/gnu%20printf/.
+# define GTEST_ATTRIBUTE_PRINTF_(string_index, first_to_check) \
+ __attribute__((__format__(__MINGW_PRINTF_FORMAT, string_index, \
+ first_to_check)))
+# else
+# define GTEST_ATTRIBUTE_PRINTF_(string_index, first_to_check) \
+ __attribute__((__format__(__printf__, string_index, first_to_check)))
+# endif
+#else
+# define GTEST_ATTRIBUTE_PRINTF_(string_index, first_to_check)
+#endif
+
+
// A macro to disallow operator=
// This should be used in the private: declarations for a class.
-#define GTEST_DISALLOW_ASSIGN_(type)\
- void operator=(type const &)
+#define GTEST_DISALLOW_ASSIGN_(type) \
+ void operator=(type const &) = delete
// A macro to disallow copy constructor and operator=
// This should be used in the private: declarations for a class.
-#define GTEST_DISALLOW_COPY_AND_ASSIGN_(type)\
- type(type const &);\
+#define GTEST_DISALLOW_COPY_AND_ASSIGN_(type) \
+ type(type const &) = delete; \
GTEST_DISALLOW_ASSIGN_(type)
// Tell the compiler to warn about unused return values for functions declared
@@ -880,11 +692,11 @@ using ::std::tuple_size;
// following the argument list:
//
// Sprocket* AllocateSprocket() GTEST_MUST_USE_RESULT_;
-#if defined(__GNUC__) && (GTEST_GCC_VER_ >= 30400) && !defined(COMPILER_ICC)
+#if defined(__GNUC__) && !defined(COMPILER_ICC)
# define GTEST_MUST_USE_RESULT_ __attribute__ ((warn_unused_result))
#else
# define GTEST_MUST_USE_RESULT_
-#endif // __GNUC__ && (GTEST_GCC_VER_ >= 30400) && !COMPILER_ICC
+#endif // __GNUC__ && !COMPILER_ICC
// MS C++ compiler emits warning when a conditional expression is compile time
// constant. In some contexts this warning is false positive and needs to be
@@ -913,13 +725,22 @@ using ::std::tuple_size;
# define GTEST_HAS_SEH 0
# endif
-#define GTEST_IS_THREADSAFE \
- (GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ \
- || (GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT) \
- || GTEST_HAS_PTHREAD)
-
#endif // GTEST_HAS_SEH
+#ifndef GTEST_IS_THREADSAFE
+
+#define GTEST_IS_THREADSAFE \
+ (GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ || \
+ (GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT) || \
+ GTEST_HAS_PTHREAD)
+
+#endif // GTEST_IS_THREADSAFE
+
+// GTEST_API_ qualifies all symbols that must be exported. The definitions below
+// are guarded by #ifndef to give embedders a chance to define GTEST_API_ in
+// gtest/internal/custom/gtest-port.h
+#ifndef GTEST_API_
+
#ifdef _MSC_VER
# if GTEST_LINKED_AS_SHARED_LIBRARY
# define GTEST_API_ __declspec(dllimport)
@@ -928,11 +749,17 @@ using ::std::tuple_size;
# endif
#elif __GNUC__ >= 4 || defined(__clang__)
# define GTEST_API_ __attribute__((visibility ("default")))
-#endif // _MSC_VER
+#endif // _MSC_VER
+
+#endif // GTEST_API_
#ifndef GTEST_API_
# define GTEST_API_
-#endif
+#endif // GTEST_API_
+
+#ifndef GTEST_DEFAULT_DEATH_TEST_STYLE
+# define GTEST_DEFAULT_DEATH_TEST_STYLE "fast"
+#endif // GTEST_DEFAULT_DEATH_TEST_STYLE
#ifdef __GNUC__
// Ask the compiler to never inline a given function.
@@ -942,10 +769,12 @@ using ::std::tuple_size;
#endif
// _LIBCPP_VERSION is defined by the libc++ library from the LLVM project.
-#if defined(__GLIBCXX__) || defined(_LIBCPP_VERSION)
-# define GTEST_HAS_CXXABI_H_ 1
-#else
-# define GTEST_HAS_CXXABI_H_ 0
+#if !defined(GTEST_HAS_CXXABI_H_)
+# if defined(__GLIBCXX__) || (defined(_LIBCPP_VERSION) && !defined(_MSC_VER))
+# define GTEST_HAS_CXXABI_H_ 1
+# else
+# define GTEST_HAS_CXXABI_H_ 0
+# endif
#endif
// A function level attribute to disable checking for use of uninitialized
@@ -973,6 +802,18 @@ using ::std::tuple_size;
# define GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_
#endif // __clang__
+// A function level attribute to disable HWAddressSanitizer instrumentation.
+#if defined(__clang__)
+# if __has_feature(hwaddress_sanitizer)
+# define GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_ \
+ __attribute__((no_sanitize("hwaddress")))
+# else
+# define GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_
+# endif // __has_feature(hwaddress_sanitizer)
+#else
+# define GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_
+#endif // __clang__
+
// A function level attribute to disable ThreadSanitizer instrumentation.
#if defined(__clang__)
# if __has_feature(thread_sanitizer)
@@ -989,16 +830,13 @@ namespace testing {
class Message;
-#if defined(GTEST_TUPLE_NAMESPACE_)
-// Import tuple and friends into the ::testing namespace.
-// It is part of our interface, having them in ::testing allows us to change
-// their types as needed.
-using GTEST_TUPLE_NAMESPACE_::get;
-using GTEST_TUPLE_NAMESPACE_::make_tuple;
-using GTEST_TUPLE_NAMESPACE_::tuple;
-using GTEST_TUPLE_NAMESPACE_::tuple_size;
-using GTEST_TUPLE_NAMESPACE_::tuple_element;
-#endif // defined(GTEST_TUPLE_NAMESPACE_)
+// Legacy imports for backwards compatibility.
+// New code should use std:: names directly.
+using std::get;
+using std::make_tuple;
+using std::tuple;
+using std::tuple_element;
+using std::tuple_size;
namespace internal {
@@ -1007,150 +845,30 @@ namespace internal {
// Secret object, which is what we want.
class Secret;
-// The GTEST_COMPILE_ASSERT_ macro can be used to verify that a compile time
-// expression is true. For example, you could use it to verify the
-// size of a static array:
+// The GTEST_COMPILE_ASSERT_ is a legacy macro used to verify that a compile
+// time expression is true (in new code, use static_assert instead). For
+// example, you could use it to verify the size of a static array:
//
// GTEST_COMPILE_ASSERT_(GTEST_ARRAY_SIZE_(names) == NUM_NAMES,
// names_incorrect_size);
//
-// or to make sure a struct is smaller than a certain size:
-//
-// GTEST_COMPILE_ASSERT_(sizeof(foo) < 128, foo_too_large);
-//
-// The second argument to the macro is the name of the variable. If
-// the expression is false, most compilers will issue a warning/error
-// containing the name of the variable.
-
-#if GTEST_LANG_CXX11
-# define GTEST_COMPILE_ASSERT_(expr, msg) static_assert(expr, #msg)
-#else // !GTEST_LANG_CXX11
-template <bool>
- struct CompileAssert {
-};
-
-# define GTEST_COMPILE_ASSERT_(expr, msg) \
- typedef ::testing::internal::CompileAssert<(static_cast<bool>(expr))> \
- msg[static_cast<bool>(expr) ? 1 : -1] GTEST_ATTRIBUTE_UNUSED_
-#endif // !GTEST_LANG_CXX11
-
-// Implementation details of GTEST_COMPILE_ASSERT_:
-//
-// (In C++11, we simply use static_assert instead of the following)
-//
-// - GTEST_COMPILE_ASSERT_ works by defining an array type that has -1
-// elements (and thus is invalid) when the expression is false.
-//
-// - The simpler definition
-//
-// #define GTEST_COMPILE_ASSERT_(expr, msg) typedef char msg[(expr) ? 1 : -1]
-//
-// does not work, as gcc supports variable-length arrays whose sizes
-// are determined at run-time (this is gcc's extension and not part
-// of the C++ standard). As a result, gcc fails to reject the
-// following code with the simple definition:
-//
-// int foo;
-// GTEST_COMPILE_ASSERT_(foo, msg); // not supposed to compile as foo is
-// // not a compile-time constant.
-//
-// - By using the type CompileAssert<(bool(expr))>, we ensures that
-// expr is a compile-time constant. (Template arguments must be
-// determined at compile-time.)
-//
-// - The outter parentheses in CompileAssert<(bool(expr))> are necessary
-// to work around a bug in gcc 3.4.4 and 4.0.1. If we had written
-//
-// CompileAssert<bool(expr)>
-//
-// instead, these compilers will refuse to compile
-//
-// GTEST_COMPILE_ASSERT_(5 > 0, some_message);
-//
-// (They seem to think the ">" in "5 > 0" marks the end of the
-// template argument list.)
-//
-// - The array size is (bool(expr) ? 1 : -1), instead of simply
-//
-// ((expr) ? 1 : -1).
-//
-// This is to avoid running into a bug in MS VC 7.1, which
-// causes ((0.0) ? 1 : -1) to incorrectly evaluate to 1.
-
-// StaticAssertTypeEqHelper is used by StaticAssertTypeEq defined in gtest.h.
-//
-// This template is declared, but intentionally undefined.
-template <typename T1, typename T2>
-struct StaticAssertTypeEqHelper;
-
-template <typename T>
-struct StaticAssertTypeEqHelper<T, T> {
- enum { value = true };
-};
+// The second argument to the macro must be a valid C++ identifier. If the
+// expression is false, compiler will issue an error containing this identifier.
+#define GTEST_COMPILE_ASSERT_(expr, msg) static_assert(expr, #msg)
// Evaluates to the number of elements in 'array'.
#define GTEST_ARRAY_SIZE_(array) (sizeof(array) / sizeof(array[0]))
-#if GTEST_HAS_GLOBAL_STRING
-typedef ::string string;
-#else
-typedef ::std::string string;
-#endif // GTEST_HAS_GLOBAL_STRING
-
-#if GTEST_HAS_GLOBAL_WSTRING
-typedef ::wstring wstring;
-#elif GTEST_HAS_STD_WSTRING
-typedef ::std::wstring wstring;
-#endif // GTEST_HAS_GLOBAL_WSTRING
-
// A helper for suppressing warnings on constant condition. It just
// returns 'condition'.
GTEST_API_ bool IsTrue(bool condition);
-// Defines scoped_ptr.
-
-// This implementation of scoped_ptr is PARTIAL - it only contains
-// enough stuff to satisfy Google Test's need.
-template <typename T>
-class scoped_ptr {
- public:
- typedef T element_type;
-
- explicit scoped_ptr(T* p = NULL) : ptr_(p) {}
- ~scoped_ptr() { reset(); }
-
- T& operator*() const { return *ptr_; }
- T* operator->() const { return ptr_; }
- T* get() const { return ptr_; }
-
- T* release() {
- T* const ptr = ptr_;
- ptr_ = NULL;
- return ptr;
- }
-
- void reset(T* p = NULL) {
- if (p != ptr_) {
- if (IsTrue(sizeof(T) > 0)) { // Makes sure T is a complete type.
- delete ptr_;
- }
- ptr_ = p;
- }
- }
-
- friend void swap(scoped_ptr& a, scoped_ptr& b) {
- using std::swap;
- swap(a.ptr_, b.ptr_);
- }
-
- private:
- T* ptr_;
-
- GTEST_DISALLOW_COPY_AND_ASSIGN_(scoped_ptr);
-};
-
// Defines RE.
+#if GTEST_USES_PCRE
+// if used, PCRE is injected by custom/gtest-port.h
+#elif GTEST_USES_POSIX_RE || GTEST_USES_SIMPLE_RE
+
// A simple C++ wrapper for <regex.h>. It uses the POSIX Extended
// Regular Expression syntax.
class GTEST_API_ RE {
@@ -1162,25 +880,16 @@ class GTEST_API_ RE {
// Constructs an RE from a string.
RE(const ::std::string& regex) { Init(regex.c_str()); } // NOLINT
-#if GTEST_HAS_GLOBAL_STRING
-
- RE(const ::string& regex) { Init(regex.c_str()); } // NOLINT
-
-#endif // GTEST_HAS_GLOBAL_STRING
-
RE(const char* regex) { Init(regex); } // NOLINT
~RE();
// Returns the string representation of the regex.
const char* pattern() const { return pattern_; }
- // FullMatch(str, re) returns true iff regular expression re matches
- // the entire str.
- // PartialMatch(str, re) returns true iff regular expression re
+ // FullMatch(str, re) returns true if and only if regular expression re
+ // matches the entire str.
+ // PartialMatch(str, re) returns true if and only if regular expression re
// matches a substring of str (including str itself).
- //
- // TODO(wan@google.com): make FullMatch() and PartialMatch() work
- // when str contains NUL characters.
static bool FullMatch(const ::std::string& str, const RE& re) {
return FullMatch(str.c_str(), re);
}
@@ -1188,43 +897,30 @@ class GTEST_API_ RE {
return PartialMatch(str.c_str(), re);
}
-#if GTEST_HAS_GLOBAL_STRING
-
- static bool FullMatch(const ::string& str, const RE& re) {
- return FullMatch(str.c_str(), re);
- }
- static bool PartialMatch(const ::string& str, const RE& re) {
- return PartialMatch(str.c_str(), re);
- }
-
-#endif // GTEST_HAS_GLOBAL_STRING
-
static bool FullMatch(const char* str, const RE& re);
static bool PartialMatch(const char* str, const RE& re);
private:
void Init(const char* regex);
-
- // We use a const char* instead of an std::string, as Google Test used to be
- // used where std::string is not available. TODO(wan@google.com): change to
- // std::string.
const char* pattern_;
bool is_valid_;
-#if GTEST_USES_POSIX_RE
+# if GTEST_USES_POSIX_RE
regex_t full_regex_; // For FullMatch().
regex_t partial_regex_; // For PartialMatch().
-#else // GTEST_USES_SIMPLE_RE
+# else // GTEST_USES_SIMPLE_RE
const char* full_pattern_; // For FullMatch();
-#endif
+# endif
GTEST_DISALLOW_ASSIGN_(RE);
};
+#endif // GTEST_USES_PCRE
+
// Formats a source file path and a line number as they would appear
// in an error message from the compiler used to compile this code.
GTEST_API_ ::std::string FormatFileLocation(const char* file, int line);
@@ -1273,7 +969,7 @@ class GTEST_API_ GTestLog {
__FILE__, __LINE__).GetStream()
inline void LogToStderr() {}
-inline void FlushInfoLog() { fflush(NULL); }
+inline void FlushInfoLog() { fflush(nullptr); }
#endif // !defined(GTEST_LOG_)
@@ -1310,14 +1006,25 @@ inline void FlushInfoLog() { fflush(NULL); }
GTEST_LOG_(FATAL) << #posix_call << "failed with error " \
<< gtest_error
-#if GTEST_HAS_STD_MOVE_
-using std::move;
-#else // GTEST_HAS_STD_MOVE_
+// Transforms "T" into "const T&" according to standard reference collapsing
+// rules (this is only needed as a backport for C++98 compilers that do not
+// support reference collapsing). Specifically, it transforms:
+//
+// char ==> const char&
+// const char ==> const char&
+// char& ==> char&
+// const char& ==> const char&
+//
+// Note that the non-const reference will not have "const" added. This is
+// standard, and necessary so that "T" can always bind to "const T&".
template <typename T>
-const T& move(const T& t) {
- return t;
-}
-#endif // GTEST_HAS_STD_MOVE_
+struct ConstRef { typedef const T& type; };
+template <typename T>
+struct ConstRef<T&> { typedef T& type; };
+
+// The argument T must depend on some template parameters.
+#define GTEST_REFERENCE_TO_CONST_(T) \
+ typename ::testing::internal::ConstRef<T>::type
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
//
@@ -1372,13 +1079,13 @@ inline To DownCast_(From* f) { // so we only accept pointers
GTEST_INTENTIONAL_CONST_COND_PUSH_()
if (false) {
GTEST_INTENTIONAL_CONST_COND_POP_()
- const To to = NULL;
- ::testing::internal::ImplicitCast_<From*>(to);
+ const To to = nullptr;
+ ::testing::internal::ImplicitCast_<From*>(to);
}
#if GTEST_HAS_RTTI
// RTTI: debug mode only!
- GTEST_CHECK_(f == NULL || dynamic_cast<To>(f) != NULL);
+ GTEST_CHECK_(f == nullptr || dynamic_cast<To>(f) != nullptr);
#endif
return static_cast<To>(f);
}
@@ -1417,10 +1124,6 @@ GTEST_API_ void CaptureStderr();
GTEST_API_ std::string GetCapturedStderr();
#endif // GTEST_HAS_STREAM_REDIRECTION
-
-// Returns a path to temporary directory.
-GTEST_API_ std::string TempDir();
-
// Returns the size (in bytes) of a file.
GTEST_API_ size_t GetFileSize(FILE* file);
@@ -1428,14 +1131,15 @@ GTEST_API_ size_t GetFileSize(FILE* file);
GTEST_API_ std::string ReadEntireFile(FILE* file);
// All command line arguments.
-GTEST_API_ const ::std::vector<testing::internal::string>& GetArgvs();
+GTEST_API_ std::vector<std::string> GetArgvs();
#if GTEST_HAS_DEATH_TEST
-const ::std::vector<testing::internal::string>& GetInjectableArgvs();
-void SetInjectableArgvs(const ::std::vector<testing::internal::string>*
- new_argvs);
-
+std::vector<std::string> GetInjectableArgvs();
+// Deprecated: pass the args vector by value instead.
+void SetInjectableArgvs(const std::vector<std::string>* new_argvs);
+void SetInjectableArgvs(const std::vector<std::string>& new_argvs);
+void ClearInjectableArgvs();
#endif // GTEST_HAS_DEATH_TEST
@@ -1450,7 +1154,7 @@ inline void SleepMilliseconds(int n) {
0, // 0 seconds.
n * 1000L * 1000L, // And n ms.
};
- nanosleep(&time, NULL);
+ nanosleep(&time, nullptr);
}
# endif // GTEST_HAS_PTHREAD
@@ -1468,7 +1172,7 @@ inline void SleepMilliseconds(int n) {
class Notification {
public:
Notification() : notified_(false) {
- GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_init(&mutex_, NULL));
+ GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_init(&mutex_, nullptr));
}
~Notification() {
pthread_mutex_destroy(&mutex_);
@@ -1526,7 +1230,8 @@ class GTEST_API_ AutoHandle {
void Reset(Handle handle);
private:
- // Returns true iff the handle is a valid handle object that can be closed.
+ // Returns true if and only if the handle is a valid handle object that can be
+ // closed.
bool IsCloseable() const;
Handle handle_;
@@ -1577,7 +1282,7 @@ class ThreadWithParamBase {
// pass into pthread_create().
extern "C" inline void* ThreadFuncWithCLinkage(void* thread) {
static_cast<ThreadWithParamBase*>(thread)->Run();
- return NULL;
+ return nullptr;
}
// Helper class for testing Google Test's multi-threading constructs.
@@ -1606,20 +1311,19 @@ class ThreadWithParam : public ThreadWithParamBase {
// The thread can be created only after all fields except thread_
// have been initialized.
GTEST_CHECK_POSIX_SUCCESS_(
- pthread_create(&thread_, 0, &ThreadFuncWithCLinkage, base));
+ pthread_create(&thread_, nullptr, &ThreadFuncWithCLinkage, base));
}
- ~ThreadWithParam() { Join(); }
+ ~ThreadWithParam() override { Join(); }
void Join() {
if (!finished_) {
- GTEST_CHECK_POSIX_SUCCESS_(pthread_join(thread_, 0));
+ GTEST_CHECK_POSIX_SUCCESS_(pthread_join(thread_, nullptr));
finished_ = true;
}
}
- virtual void Run() {
- if (thread_can_start_ != NULL)
- thread_can_start_->WaitForNotification();
+ void Run() override {
+ if (thread_can_start_ != nullptr) thread_can_start_->WaitForNotification();
func_(param_);
}
@@ -1629,7 +1333,8 @@ class ThreadWithParam : public ThreadWithParamBase {
// When non-NULL, used to block execution until the controller thread
// notifies.
Notification* const thread_can_start_;
- bool finished_; // true iff we know that the thread function has finished.
+ bool finished_; // true if and only if we know that the thread function has
+ // finished.
pthread_t thread_; // The native thread object.
GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadWithParam);
@@ -1685,7 +1390,7 @@ class GTEST_API_ Mutex {
// Initializes owner_thread_id_ and critical_section_ in static mutexes.
void ThreadSafeLazyInit();
- // Per http://blogs.msdn.com/b/oldnewthing/archive/2004/02/23/78395.aspx,
+ // Per https://blogs.msdn.microsoft.com/oldnewthing/20040223-00/?p=40503,
// we assume that 0 is an invalid value for thread IDs.
unsigned int owner_thread_id_;
@@ -1693,7 +1398,7 @@ class GTEST_API_ Mutex {
// by the linker.
MutexType type_;
long critical_section_init_phase_; // NOLINT
- _RTL_CRITICAL_SECTION* critical_section_;
+ GTEST_CRITICAL_SECTION* critical_section_;
GTEST_DISALLOW_COPY_AND_ASSIGN_(Mutex);
};
@@ -1913,7 +1618,7 @@ class ThreadLocal : public ThreadLocalBase {
GTEST_DISALLOW_COPY_AND_ASSIGN_(InstanceValueHolderFactory);
};
- scoped_ptr<ValueHolderFactory> default_factory_;
+ std::unique_ptr<ValueHolderFactory> default_factory_;
GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadLocal);
};
@@ -1969,15 +1674,20 @@ class MutexBase {
extern ::testing::internal::MutexBase mutex
// Defines and statically (i.e. at link time) initializes a static mutex.
-# define GTEST_DEFINE_STATIC_MUTEX_(mutex) \
- ::testing::internal::MutexBase mutex = { PTHREAD_MUTEX_INITIALIZER, false, pthread_t() }
+// The initialization list here does not explicitly initialize each field,
+// instead relying on default initialization for the unspecified fields. In
+// particular, the owner_ field (a pthread_t) is not explicitly initialized.
+// This allows initialization to work whether pthread_t is a scalar or struct.
+// The flag -Wmissing-field-initializers must not be specified for this to work.
+#define GTEST_DEFINE_STATIC_MUTEX_(mutex) \
+ ::testing::internal::MutexBase mutex = {PTHREAD_MUTEX_INITIALIZER, false, 0}
// The Mutex class can only be used for mutexes created at runtime. It
// shares its API with MutexBase otherwise.
class Mutex : public MutexBase {
public:
Mutex() {
- GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_init(&mutex_, NULL));
+ GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_init(&mutex_, nullptr));
has_owner_ = false;
}
~Mutex() {
@@ -2027,7 +1737,7 @@ extern "C" inline void DeleteThreadLocalValue(void* value_holder) {
// Implements thread-local storage on pthreads-based systems.
template <typename T>
-class ThreadLocal {
+class GTEST_API_ ThreadLocal {
public:
ThreadLocal()
: key_(CreateKey()), default_factory_(new DefaultValueHolderFactory()) {}
@@ -2075,7 +1785,7 @@ class ThreadLocal {
T* GetOrCreateValue() const {
ThreadLocalValueHolderBase* const holder =
static_cast<ThreadLocalValueHolderBase*>(pthread_getspecific(key_));
- if (holder != NULL) {
+ if (holder != nullptr) {
return CheckedDowncastToActualType<ValueHolder>(holder)->pointer();
}
@@ -2119,7 +1829,7 @@ class ThreadLocal {
// A key pthreads uses for looking up per-thread values.
const pthread_key_t key_;
- scoped_ptr<ValueHolderFactory> default_factory_;
+ std::unique_ptr<ValueHolderFactory> default_factory_;
GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadLocal);
};
@@ -2159,7 +1869,7 @@ class GTestMutexLock {
typedef GTestMutexLock MutexLock;
template <typename T>
-class ThreadLocal {
+class GTEST_API_ ThreadLocal {
public:
ThreadLocal() : value_() {}
explicit ThreadLocal(const T& value) : value_(value) {}
@@ -2177,58 +1887,8 @@ class ThreadLocal {
// we cannot detect it.
GTEST_API_ size_t GetThreadCount();
-// Passing non-POD classes through ellipsis (...) crashes the ARM
-// compiler and generates a warning in Sun Studio. The Nokia Symbian
-// and the IBM XL C/C++ compiler try to instantiate a copy constructor
-// for objects passed through ellipsis (...), failing for uncopyable
-// objects. We define this to ensure that only POD is passed through
-// ellipsis on these systems.
-#if defined(__SYMBIAN32__) || defined(__IBMCPP__) || defined(__SUNPRO_CC)
-// We lose support for NULL detection where the compiler doesn't like
-// passing non-POD classes through ellipsis (...).
-# define GTEST_ELLIPSIS_NEEDS_POD_ 1
-#else
-# define GTEST_CAN_COMPARE_NULL 1
-#endif
-
-// The Nokia Symbian and IBM XL C/C++ compilers cannot decide between
-// const T& and const T* in a function template. These compilers
-// _can_ decide between class template specializations for T and T*,
-// so a tr1::type_traits-like is_pointer works.
-#if defined(__SYMBIAN32__) || defined(__IBMCPP__)
-# define GTEST_NEEDS_IS_POINTER_ 1
-#endif
-
-template <bool bool_value>
-struct bool_constant {
- typedef bool_constant<bool_value> type;
- static const bool value = bool_value;
-};
-template <bool bool_value> const bool bool_constant<bool_value>::value;
-
-typedef bool_constant<false> false_type;
-typedef bool_constant<true> true_type;
-
-template <typename T>
-struct is_pointer : public false_type {};
-
-template <typename T>
-struct is_pointer<T*> : public true_type {};
-
-template <typename Iterator>
-struct IteratorTraits {
- typedef typename Iterator::value_type value_type;
-};
-
-template <typename T>
-struct IteratorTraits<T*> {
- typedef T value_type;
-};
-
-template <typename T>
-struct IteratorTraits<const T*> {
- typedef T value_type;
-};
+template <bool B>
+using bool_constant = std::integral_constant<bool, B>;
#if GTEST_OS_WINDOWS
# define GTEST_PATH_SEP_ "\\"
@@ -2351,7 +2011,7 @@ inline bool IsDir(const StatStruct& st) { return S_ISDIR(st.st_mode); }
// Functions deprecated by MSVC 8.0.
-GTEST_DISABLE_MSC_WARNINGS_PUSH_(4996 /* deprecated function */)
+GTEST_DISABLE_MSC_DEPRECATED_PUSH_()
inline const char* StrNCpy(char* dest, const char* src, size_t n) {
return strncpy(dest, src, n);
@@ -2385,29 +2045,29 @@ inline int Close(int fd) { return close(fd); }
inline const char* StrError(int errnum) { return strerror(errnum); }
#endif
inline const char* GetEnv(const char* name) {
-#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_PHONE | GTEST_OS_WINDOWS_RT
+#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_PHONE || GTEST_OS_WINDOWS_RT
// We are on Windows CE, which has no environment variables.
static_cast<void>(name); // To prevent 'unused argument' warning.
- return NULL;
+ return nullptr;
#elif defined(__BORLANDC__) || defined(__SunOS_5_8) || defined(__SunOS_5_9)
// Environment variables which we programmatically clear will be set to the
// empty string rather than unset (NULL). Handle that case.
const char* const env = getenv(name);
- return (env != NULL && env[0] != '\0') ? env : NULL;
+ return (env != nullptr && env[0] != '\0') ? env : nullptr;
#else
return getenv(name);
#endif
}
-GTEST_DISABLE_MSC_WARNINGS_POP_()
+GTEST_DISABLE_MSC_DEPRECATED_POP_()
#if GTEST_OS_WINDOWS_MOBILE
// Windows CE has no C library. The abort() function is used in
// several places in Google Test. This implementation provides a reasonable
// imitation of standard behaviour.
-void Abort();
+[[noreturn]] void Abort();
#else
-inline void Abort() { abort(); }
+[[noreturn]] inline void Abort() { abort(); }
#endif // GTEST_OS_WINDOWS_MOBILE
} // namespace posix
@@ -2417,13 +2077,12 @@ inline void Abort() { abort(); }
// MSVC-based platforms. We map the GTEST_SNPRINTF_ macro to the appropriate
// function in order to achieve that. We use macro definition here because
// snprintf is a variadic function.
-#if _MSC_VER >= 1400 && !GTEST_OS_WINDOWS_MOBILE
+#if _MSC_VER && !GTEST_OS_WINDOWS_MOBILE
// MSVC 2005 and above support variadic macros.
# define GTEST_SNPRINTF_(buffer, size, format, ...) \
_snprintf_s(buffer, size, size, format, __VA_ARGS__)
#elif defined(_MSC_VER)
-// Windows CE does not define _snprintf_s and MSVC prior to 2005 doesn't
-// complain about _snprintf.
+// Windows CE does not define _snprintf_s
# define GTEST_SNPRINTF_ _snprintf
#else
# define GTEST_SNPRINTF_ snprintf
@@ -2515,15 +2174,15 @@ typedef TypeWithSize<8>::Int TimeInMillis; // Represents time in milliseconds.
# define GTEST_DECLARE_bool_(name) GTEST_API_ extern bool GTEST_FLAG(name)
# define GTEST_DECLARE_int32_(name) \
GTEST_API_ extern ::testing::internal::Int32 GTEST_FLAG(name)
-#define GTEST_DECLARE_string_(name) \
+# define GTEST_DECLARE_string_(name) \
GTEST_API_ extern ::std::string GTEST_FLAG(name)
// Macros for defining flags.
-#define GTEST_DEFINE_bool_(name, default_val, doc) \
+# define GTEST_DEFINE_bool_(name, default_val, doc) \
GTEST_API_ bool GTEST_FLAG(name) = (default_val)
-#define GTEST_DEFINE_int32_(name, default_val, doc) \
+# define GTEST_DEFINE_int32_(name, default_val, doc) \
GTEST_API_ ::testing::internal::Int32 GTEST_FLAG(name) = (default_val)
-#define GTEST_DEFINE_string_(name, default_val, doc) \
+# define GTEST_DEFINE_string_(name, default_val, doc) \
GTEST_API_ ::std::string GTEST_FLAG(name) = (default_val)
#endif // !defined(GTEST_DECLARE_bool_)
@@ -2537,18 +2196,36 @@ typedef TypeWithSize<8>::Int TimeInMillis; // Represents time in milliseconds.
// Parses 'str' for a 32-bit signed integer. If successful, writes the result
// to *value and returns true; otherwise leaves *value unchanged and returns
// false.
-// TODO(chandlerc): Find a better way to refactor flag and environment parsing
-// out of both gtest-port.cc and gtest.cc to avoid exporting this utility
-// function.
bool ParseInt32(const Message& src_text, const char* str, Int32* value);
// Parses a bool/Int32/string from the environment variable
// corresponding to the given Google Test flag.
bool BoolFromGTestEnv(const char* flag, bool default_val);
GTEST_API_ Int32 Int32FromGTestEnv(const char* flag, Int32 default_val);
-std::string StringFromGTestEnv(const char* flag, const char* default_val);
+std::string OutputFlagAlsoCheckEnvVar();
+const char* StringFromGTestEnv(const char* flag, const char* default_val);
} // namespace internal
} // namespace testing
+#if !defined(GTEST_INTERNAL_DEPRECATED)
+
+// Internal Macro to mark an API deprecated, for googletest usage only
+// Usage: class GTEST_INTERNAL_DEPRECATED(message) MyClass or
+// GTEST_INTERNAL_DEPRECATED(message) <return_type> myFunction(); Every usage of
+// a deprecated entity will trigger a warning when compiled with
+// `-Wdeprecated-declarations` option (clang, gcc, any __GNUC__ compiler).
+// For msvc /W3 option will need to be used
+// Note that for 'other' compilers this macro evaluates to nothing to prevent
+// compilations errors.
+#if defined(_MSC_VER)
+#define GTEST_INTERNAL_DEPRECATED(message) __declspec(deprecated(message))
+#elif defined(__GNUC__)
+#define GTEST_INTERNAL_DEPRECATED(message) __attribute__((deprecated(message)))
+#else
+#define GTEST_INTERNAL_DEPRECATED(message)
+#endif
+
+#endif // !defined(GTEST_INTERNAL_DEPRECATED)
+
#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_
diff --git a/extern/gtest/include/gtest/internal/gtest-string.h b/extern/gtest/include/gtest/internal/gtest-string.h
index 97f1a7fdd2c..82aaa63bf46 100644
--- a/extern/gtest/include/gtest/internal/gtest-string.h
+++ b/extern/gtest/include/gtest/internal/gtest-string.h
@@ -27,17 +27,17 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
-// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee)
-//
-// The Google C++ Testing Framework (Google Test)
+// The Google C++ Testing and Mocking Framework (Google Test)
//
// This header file declares the String class and functions used internally by
// Google Test. They are subject to change without notice. They should not used
// by code external to Google Test.
//
-// This header file is #included by <gtest/internal/gtest-internal.h>.
+// This header file is #included by gtest-internal.h.
// It should not be #included by other files.
+// GOOGLETEST_CM0001 DO NOT DELETE
+
#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_
#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_
@@ -94,7 +94,8 @@ class GTEST_API_ String {
static const char* Utf16ToAnsi(LPCWSTR utf16_str);
#endif
- // Compares two C strings. Returns true iff they have the same content.
+ // Compares two C strings. Returns true if and only if they have the same
+ // content.
//
// Unlike strcmp(), this function can handle NULL argument(s). A
// NULL C string is considered different to any non-NULL C string,
@@ -107,16 +108,16 @@ class GTEST_API_ String {
// returned.
static std::string ShowWideCString(const wchar_t* wide_c_str);
- // Compares two wide C strings. Returns true iff they have the same
- // content.
+ // Compares two wide C strings. Returns true if and only if they have the
+ // same content.
//
// Unlike wcscmp(), this function can handle NULL argument(s). A
// NULL C string is considered different to any non-NULL C string,
// including the empty string.
static bool WideCStringEquals(const wchar_t* lhs, const wchar_t* rhs);
- // Compares two C strings, ignoring case. Returns true iff they
- // have the same content.
+ // Compares two C strings, ignoring case. Returns true if and only if
+ // they have the same content.
//
// Unlike strcasecmp(), this function can handle NULL argument(s).
// A NULL C string is considered different to any non-NULL C string,
@@ -124,8 +125,8 @@ class GTEST_API_ String {
static bool CaseInsensitiveCStringEquals(const char* lhs,
const char* rhs);
- // Compares two wide C strings, ignoring case. Returns true iff they
- // have the same content.
+ // Compares two wide C strings, ignoring case. Returns true if and only if
+ // they have the same content.
//
// Unlike wcscasecmp(), this function can handle NULL argument(s).
// A NULL C string is considered different to any non-NULL wide C string,
@@ -139,8 +140,8 @@ class GTEST_API_ String {
static bool CaseInsensitiveWideCStringEquals(const wchar_t* lhs,
const wchar_t* rhs);
- // Returns true iff the given string ends with the given suffix, ignoring
- // case. Any string is considered to end with an empty suffix.
+ // Returns true if and only if the given string ends with the given suffix,
+ // ignoring case. Any string is considered to end with an empty suffix.
static bool EndsWithCaseInsensitive(
const std::string& str, const std::string& suffix);
@@ -150,6 +151,9 @@ class GTEST_API_ String {
// Formats an int value as "%X".
static std::string FormatHexInt(int value);
+ // Formats an int value as "%X".
+ static std::string FormatHexUInt32(UInt32 value);
+
// Formats a byte as "%02X".
static std::string FormatByte(unsigned char value);
diff --git a/extern/gtest/include/gtest/internal/gtest-tuple.h b/extern/gtest/include/gtest/internal/gtest-tuple.h
deleted file mode 100644
index e9b405340a8..00000000000
--- a/extern/gtest/include/gtest/internal/gtest-tuple.h
+++ /dev/null
@@ -1,1020 +0,0 @@
-// This file was GENERATED by command:
-// pump.py gtest-tuple.h.pump
-// DO NOT EDIT BY HAND!!!
-
-// Copyright 2009 Google Inc.
-// All Rights Reserved.
-//
-// 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: wan@google.com (Zhanyong Wan)
-
-// Implements a subset of TR1 tuple needed by Google Test and Google Mock.
-
-#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_
-#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_
-
-#include <utility> // For ::std::pair.
-
-// The compiler used in Symbian has a bug that prevents us from declaring the
-// tuple template as a friend (it complains that tuple is redefined). This
-// hack bypasses the bug by declaring the members that should otherwise be
-// private as public.
-// Sun Studio versions < 12 also have the above bug.
-#if defined(__SYMBIAN32__) || (defined(__SUNPRO_CC) && __SUNPRO_CC < 0x590)
-# define GTEST_DECLARE_TUPLE_AS_FRIEND_ public:
-#else
-# define GTEST_DECLARE_TUPLE_AS_FRIEND_ \
- template <GTEST_10_TYPENAMES_(U)> friend class tuple; \
- private:
-#endif
-
-// Visual Studio 2010, 2012, and 2013 define symbols in std::tr1 that conflict
-// with our own definitions. Therefore using our own tuple does not work on
-// those compilers.
-#if defined(_MSC_VER) && _MSC_VER >= 1600 /* 1600 is Visual Studio 2010 */
-# error "gtest's tuple doesn't compile on Visual Studio 2010 or later. \
-GTEST_USE_OWN_TR1_TUPLE must be set to 0 on those compilers."
-#endif
-
-// GTEST_n_TUPLE_(T) is the type of an n-tuple.
-#define GTEST_0_TUPLE_(T) tuple<>
-#define GTEST_1_TUPLE_(T) tuple<T##0, void, void, void, void, void, void, \
- void, void, void>
-#define GTEST_2_TUPLE_(T) tuple<T##0, T##1, void, void, void, void, void, \
- void, void, void>
-#define GTEST_3_TUPLE_(T) tuple<T##0, T##1, T##2, void, void, void, void, \
- void, void, void>
-#define GTEST_4_TUPLE_(T) tuple<T##0, T##1, T##2, T##3, void, void, void, \
- void, void, void>
-#define GTEST_5_TUPLE_(T) tuple<T##0, T##1, T##2, T##3, T##4, void, void, \
- void, void, void>
-#define GTEST_6_TUPLE_(T) tuple<T##0, T##1, T##2, T##3, T##4, T##5, void, \
- void, void, void>
-#define GTEST_7_TUPLE_(T) tuple<T##0, T##1, T##2, T##3, T##4, T##5, T##6, \
- void, void, void>
-#define GTEST_8_TUPLE_(T) tuple<T##0, T##1, T##2, T##3, T##4, T##5, T##6, \
- T##7, void, void>
-#define GTEST_9_TUPLE_(T) tuple<T##0, T##1, T##2, T##3, T##4, T##5, T##6, \
- T##7, T##8, void>
-#define GTEST_10_TUPLE_(T) tuple<T##0, T##1, T##2, T##3, T##4, T##5, T##6, \
- T##7, T##8, T##9>
-
-// GTEST_n_TYPENAMES_(T) declares a list of n typenames.
-#define GTEST_0_TYPENAMES_(T)
-#define GTEST_1_TYPENAMES_(T) typename T##0
-#define GTEST_2_TYPENAMES_(T) typename T##0, typename T##1
-#define GTEST_3_TYPENAMES_(T) typename T##0, typename T##1, typename T##2
-#define GTEST_4_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \
- typename T##3
-#define GTEST_5_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \
- typename T##3, typename T##4
-#define GTEST_6_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \
- typename T##3, typename T##4, typename T##5
-#define GTEST_7_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \
- typename T##3, typename T##4, typename T##5, typename T##6
-#define GTEST_8_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \
- typename T##3, typename T##4, typename T##5, typename T##6, typename T##7
-#define GTEST_9_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \
- typename T##3, typename T##4, typename T##5, typename T##6, \
- typename T##7, typename T##8
-#define GTEST_10_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \
- typename T##3, typename T##4, typename T##5, typename T##6, \
- typename T##7, typename T##8, typename T##9
-
-// In theory, defining stuff in the ::std namespace is undefined
-// behavior. We can do this as we are playing the role of a standard
-// library vendor.
-namespace std {
-namespace tr1 {
-
-template <typename T0 = void, typename T1 = void, typename T2 = void,
- typename T3 = void, typename T4 = void, typename T5 = void,
- typename T6 = void, typename T7 = void, typename T8 = void,
- typename T9 = void>
-class tuple;
-
-// Anything in namespace gtest_internal is Google Test's INTERNAL
-// IMPLEMENTATION DETAIL and MUST NOT BE USED DIRECTLY in user code.
-namespace gtest_internal {
-
-// ByRef<T>::type is T if T is a reference; otherwise it's const T&.
-template <typename T>
-struct ByRef { typedef const T& type; }; // NOLINT
-template <typename T>
-struct ByRef<T&> { typedef T& type; }; // NOLINT
-
-// A handy wrapper for ByRef.
-#define GTEST_BY_REF_(T) typename ::std::tr1::gtest_internal::ByRef<T>::type
-
-// AddRef<T>::type is T if T is a reference; otherwise it's T&. This
-// is the same as tr1::add_reference<T>::type.
-template <typename T>
-struct AddRef { typedef T& type; }; // NOLINT
-template <typename T>
-struct AddRef<T&> { typedef T& type; }; // NOLINT
-
-// A handy wrapper for AddRef.
-#define GTEST_ADD_REF_(T) typename ::std::tr1::gtest_internal::AddRef<T>::type
-
-// A helper for implementing get<k>().
-template <int k> class Get;
-
-// A helper for implementing tuple_element<k, T>. kIndexValid is true
-// iff k < the number of fields in tuple type T.
-template <bool kIndexValid, int kIndex, class Tuple>
-struct TupleElement;
-
-template <GTEST_10_TYPENAMES_(T)>
-struct TupleElement<true, 0, GTEST_10_TUPLE_(T) > {
- typedef T0 type;
-};
-
-template <GTEST_10_TYPENAMES_(T)>
-struct TupleElement<true, 1, GTEST_10_TUPLE_(T) > {
- typedef T1 type;
-};
-
-template <GTEST_10_TYPENAMES_(T)>
-struct TupleElement<true, 2, GTEST_10_TUPLE_(T) > {
- typedef T2 type;
-};
-
-template <GTEST_10_TYPENAMES_(T)>
-struct TupleElement<true, 3, GTEST_10_TUPLE_(T) > {
- typedef T3 type;
-};
-
-template <GTEST_10_TYPENAMES_(T)>
-struct TupleElement<true, 4, GTEST_10_TUPLE_(T) > {
- typedef T4 type;
-};
-
-template <GTEST_10_TYPENAMES_(T)>
-struct TupleElement<true, 5, GTEST_10_TUPLE_(T) > {
- typedef T5 type;
-};
-
-template <GTEST_10_TYPENAMES_(T)>
-struct TupleElement<true, 6, GTEST_10_TUPLE_(T) > {
- typedef T6 type;
-};
-
-template <GTEST_10_TYPENAMES_(T)>
-struct TupleElement<true, 7, GTEST_10_TUPLE_(T) > {
- typedef T7 type;
-};
-
-template <GTEST_10_TYPENAMES_(T)>
-struct TupleElement<true, 8, GTEST_10_TUPLE_(T) > {
- typedef T8 type;
-};
-
-template <GTEST_10_TYPENAMES_(T)>
-struct TupleElement<true, 9, GTEST_10_TUPLE_(T) > {
- typedef T9 type;
-};
-
-} // namespace gtest_internal
-
-template <>
-class tuple<> {
- public:
- tuple() {}
- tuple(const tuple& /* t */) {}
- tuple& operator=(const tuple& /* t */) { return *this; }
-};
-
-template <GTEST_1_TYPENAMES_(T)>
-class GTEST_1_TUPLE_(T) {
- public:
- template <int k> friend class gtest_internal::Get;
-
- tuple() : f0_() {}
-
- explicit tuple(GTEST_BY_REF_(T0) f0) : f0_(f0) {}
-
- tuple(const tuple& t) : f0_(t.f0_) {}
-
- template <GTEST_1_TYPENAMES_(U)>
- tuple(const GTEST_1_TUPLE_(U)& t) : f0_(t.f0_) {}
-
- tuple& operator=(const tuple& t) { return CopyFrom(t); }
-
- template <GTEST_1_TYPENAMES_(U)>
- tuple& operator=(const GTEST_1_TUPLE_(U)& t) {
- return CopyFrom(t);
- }
-
- GTEST_DECLARE_TUPLE_AS_FRIEND_
-
- template <GTEST_1_TYPENAMES_(U)>
- tuple& CopyFrom(const GTEST_1_TUPLE_(U)& t) {
- f0_ = t.f0_;
- return *this;
- }
-
- T0 f0_;
-};
-
-template <GTEST_2_TYPENAMES_(T)>
-class GTEST_2_TUPLE_(T) {
- public:
- template <int k> friend class gtest_internal::Get;
-
- tuple() : f0_(), f1_() {}
-
- explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1) : f0_(f0),
- f1_(f1) {}
-
- tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_) {}
-
- template <GTEST_2_TYPENAMES_(U)>
- tuple(const GTEST_2_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_) {}
- template <typename U0, typename U1>
- tuple(const ::std::pair<U0, U1>& p) : f0_(p.first), f1_(p.second) {}
-
- tuple& operator=(const tuple& t) { return CopyFrom(t); }
-
- template <GTEST_2_TYPENAMES_(U)>
- tuple& operator=(const GTEST_2_TUPLE_(U)& t) {
- return CopyFrom(t);
- }
- template <typename U0, typename U1>
- tuple& operator=(const ::std::pair<U0, U1>& p) {
- f0_ = p.first;
- f1_ = p.second;
- return *this;
- }
-
- GTEST_DECLARE_TUPLE_AS_FRIEND_
-
- template <GTEST_2_TYPENAMES_(U)>
- tuple& CopyFrom(const GTEST_2_TUPLE_(U)& t) {
- f0_ = t.f0_;
- f1_ = t.f1_;
- return *this;
- }
-
- T0 f0_;
- T1 f1_;
-};
-
-template <GTEST_3_TYPENAMES_(T)>
-class GTEST_3_TUPLE_(T) {
- public:
- template <int k> friend class gtest_internal::Get;
-
- tuple() : f0_(), f1_(), f2_() {}
-
- explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1,
- GTEST_BY_REF_(T2) f2) : f0_(f0), f1_(f1), f2_(f2) {}
-
- tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_) {}
-
- template <GTEST_3_TYPENAMES_(U)>
- tuple(const GTEST_3_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_) {}
-
- tuple& operator=(const tuple& t) { return CopyFrom(t); }
-
- template <GTEST_3_TYPENAMES_(U)>
- tuple& operator=(const GTEST_3_TUPLE_(U)& t) {
- return CopyFrom(t);
- }
-
- GTEST_DECLARE_TUPLE_AS_FRIEND_
-
- template <GTEST_3_TYPENAMES_(U)>
- tuple& CopyFrom(const GTEST_3_TUPLE_(U)& t) {
- f0_ = t.f0_;
- f1_ = t.f1_;
- f2_ = t.f2_;
- return *this;
- }
-
- T0 f0_;
- T1 f1_;
- T2 f2_;
-};
-
-template <GTEST_4_TYPENAMES_(T)>
-class GTEST_4_TUPLE_(T) {
- public:
- template <int k> friend class gtest_internal::Get;
-
- tuple() : f0_(), f1_(), f2_(), f3_() {}
-
- explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1,
- GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3) : f0_(f0), f1_(f1), f2_(f2),
- f3_(f3) {}
-
- tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_) {}
-
- template <GTEST_4_TYPENAMES_(U)>
- tuple(const GTEST_4_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_),
- f3_(t.f3_) {}
-
- tuple& operator=(const tuple& t) { return CopyFrom(t); }
-
- template <GTEST_4_TYPENAMES_(U)>
- tuple& operator=(const GTEST_4_TUPLE_(U)& t) {
- return CopyFrom(t);
- }
-
- GTEST_DECLARE_TUPLE_AS_FRIEND_
-
- template <GTEST_4_TYPENAMES_(U)>
- tuple& CopyFrom(const GTEST_4_TUPLE_(U)& t) {
- f0_ = t.f0_;
- f1_ = t.f1_;
- f2_ = t.f2_;
- f3_ = t.f3_;
- return *this;
- }
-
- T0 f0_;
- T1 f1_;
- T2 f2_;
- T3 f3_;
-};
-
-template <GTEST_5_TYPENAMES_(T)>
-class GTEST_5_TUPLE_(T) {
- public:
- template <int k> friend class gtest_internal::Get;
-
- tuple() : f0_(), f1_(), f2_(), f3_(), f4_() {}
-
- explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1,
- GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3,
- GTEST_BY_REF_(T4) f4) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4) {}
-
- tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_),
- f4_(t.f4_) {}
-
- template <GTEST_5_TYPENAMES_(U)>
- tuple(const GTEST_5_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_),
- f3_(t.f3_), f4_(t.f4_) {}
-
- tuple& operator=(const tuple& t) { return CopyFrom(t); }
-
- template <GTEST_5_TYPENAMES_(U)>
- tuple& operator=(const GTEST_5_TUPLE_(U)& t) {
- return CopyFrom(t);
- }
-
- GTEST_DECLARE_TUPLE_AS_FRIEND_
-
- template <GTEST_5_TYPENAMES_(U)>
- tuple& CopyFrom(const GTEST_5_TUPLE_(U)& t) {
- f0_ = t.f0_;
- f1_ = t.f1_;
- f2_ = t.f2_;
- f3_ = t.f3_;
- f4_ = t.f4_;
- return *this;
- }
-
- T0 f0_;
- T1 f1_;
- T2 f2_;
- T3 f3_;
- T4 f4_;
-};
-
-template <GTEST_6_TYPENAMES_(T)>
-class GTEST_6_TUPLE_(T) {
- public:
- template <int k> friend class gtest_internal::Get;
-
- tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_() {}
-
- explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1,
- GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4,
- GTEST_BY_REF_(T5) f5) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4),
- f5_(f5) {}
-
- tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_),
- f4_(t.f4_), f5_(t.f5_) {}
-
- template <GTEST_6_TYPENAMES_(U)>
- tuple(const GTEST_6_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_),
- f3_(t.f3_), f4_(t.f4_), f5_(t.f5_) {}
-
- tuple& operator=(const tuple& t) { return CopyFrom(t); }
-
- template <GTEST_6_TYPENAMES_(U)>
- tuple& operator=(const GTEST_6_TUPLE_(U)& t) {
- return CopyFrom(t);
- }
-
- GTEST_DECLARE_TUPLE_AS_FRIEND_
-
- template <GTEST_6_TYPENAMES_(U)>
- tuple& CopyFrom(const GTEST_6_TUPLE_(U)& t) {
- f0_ = t.f0_;
- f1_ = t.f1_;
- f2_ = t.f2_;
- f3_ = t.f3_;
- f4_ = t.f4_;
- f5_ = t.f5_;
- return *this;
- }
-
- T0 f0_;
- T1 f1_;
- T2 f2_;
- T3 f3_;
- T4 f4_;
- T5 f5_;
-};
-
-template <GTEST_7_TYPENAMES_(T)>
-class GTEST_7_TUPLE_(T) {
- public:
- template <int k> friend class gtest_internal::Get;
-
- tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_() {}
-
- explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1,
- GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4,
- GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6) : f0_(f0), f1_(f1), f2_(f2),
- f3_(f3), f4_(f4), f5_(f5), f6_(f6) {}
-
- tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_),
- f4_(t.f4_), f5_(t.f5_), f6_(t.f6_) {}
-
- template <GTEST_7_TYPENAMES_(U)>
- tuple(const GTEST_7_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_),
- f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_) {}
-
- tuple& operator=(const tuple& t) { return CopyFrom(t); }
-
- template <GTEST_7_TYPENAMES_(U)>
- tuple& operator=(const GTEST_7_TUPLE_(U)& t) {
- return CopyFrom(t);
- }
-
- GTEST_DECLARE_TUPLE_AS_FRIEND_
-
- template <GTEST_7_TYPENAMES_(U)>
- tuple& CopyFrom(const GTEST_7_TUPLE_(U)& t) {
- f0_ = t.f0_;
- f1_ = t.f1_;
- f2_ = t.f2_;
- f3_ = t.f3_;
- f4_ = t.f4_;
- f5_ = t.f5_;
- f6_ = t.f6_;
- return *this;
- }
-
- T0 f0_;
- T1 f1_;
- T2 f2_;
- T3 f3_;
- T4 f4_;
- T5 f5_;
- T6 f6_;
-};
-
-template <GTEST_8_TYPENAMES_(T)>
-class GTEST_8_TUPLE_(T) {
- public:
- template <int k> friend class gtest_internal::Get;
-
- tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_(), f7_() {}
-
- explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1,
- GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4,
- GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6,
- GTEST_BY_REF_(T7) f7) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4),
- f5_(f5), f6_(f6), f7_(f7) {}
-
- tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_),
- f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_) {}
-
- template <GTEST_8_TYPENAMES_(U)>
- tuple(const GTEST_8_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_),
- f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_) {}
-
- tuple& operator=(const tuple& t) { return CopyFrom(t); }
-
- template <GTEST_8_TYPENAMES_(U)>
- tuple& operator=(const GTEST_8_TUPLE_(U)& t) {
- return CopyFrom(t);
- }
-
- GTEST_DECLARE_TUPLE_AS_FRIEND_
-
- template <GTEST_8_TYPENAMES_(U)>
- tuple& CopyFrom(const GTEST_8_TUPLE_(U)& t) {
- f0_ = t.f0_;
- f1_ = t.f1_;
- f2_ = t.f2_;
- f3_ = t.f3_;
- f4_ = t.f4_;
- f5_ = t.f5_;
- f6_ = t.f6_;
- f7_ = t.f7_;
- return *this;
- }
-
- T0 f0_;
- T1 f1_;
- T2 f2_;
- T3 f3_;
- T4 f4_;
- T5 f5_;
- T6 f6_;
- T7 f7_;
-};
-
-template <GTEST_9_TYPENAMES_(T)>
-class GTEST_9_TUPLE_(T) {
- public:
- template <int k> friend class gtest_internal::Get;
-
- tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_(), f7_(), f8_() {}
-
- explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1,
- GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4,
- GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6, GTEST_BY_REF_(T7) f7,
- GTEST_BY_REF_(T8) f8) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4),
- f5_(f5), f6_(f6), f7_(f7), f8_(f8) {}
-
- tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_),
- f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_) {}
-
- template <GTEST_9_TYPENAMES_(U)>
- tuple(const GTEST_9_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_),
- f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_) {}
-
- tuple& operator=(const tuple& t) { return CopyFrom(t); }
-
- template <GTEST_9_TYPENAMES_(U)>
- tuple& operator=(const GTEST_9_TUPLE_(U)& t) {
- return CopyFrom(t);
- }
-
- GTEST_DECLARE_TUPLE_AS_FRIEND_
-
- template <GTEST_9_TYPENAMES_(U)>
- tuple& CopyFrom(const GTEST_9_TUPLE_(U)& t) {
- f0_ = t.f0_;
- f1_ = t.f1_;
- f2_ = t.f2_;
- f3_ = t.f3_;
- f4_ = t.f4_;
- f5_ = t.f5_;
- f6_ = t.f6_;
- f7_ = t.f7_;
- f8_ = t.f8_;
- return *this;
- }
-
- T0 f0_;
- T1 f1_;
- T2 f2_;
- T3 f3_;
- T4 f4_;
- T5 f5_;
- T6 f6_;
- T7 f7_;
- T8 f8_;
-};
-
-template <GTEST_10_TYPENAMES_(T)>
-class tuple {
- public:
- template <int k> friend class gtest_internal::Get;
-
- tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_(), f7_(), f8_(),
- f9_() {}
-
- explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1,
- GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4,
- GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6, GTEST_BY_REF_(T7) f7,
- GTEST_BY_REF_(T8) f8, GTEST_BY_REF_(T9) f9) : f0_(f0), f1_(f1), f2_(f2),
- f3_(f3), f4_(f4), f5_(f5), f6_(f6), f7_(f7), f8_(f8), f9_(f9) {}
-
- tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_),
- f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_), f9_(t.f9_) {}
-
- template <GTEST_10_TYPENAMES_(U)>
- tuple(const GTEST_10_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_),
- f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_),
- f9_(t.f9_) {}
-
- tuple& operator=(const tuple& t) { return CopyFrom(t); }
-
- template <GTEST_10_TYPENAMES_(U)>
- tuple& operator=(const GTEST_10_TUPLE_(U)& t) {
- return CopyFrom(t);
- }
-
- GTEST_DECLARE_TUPLE_AS_FRIEND_
-
- template <GTEST_10_TYPENAMES_(U)>
- tuple& CopyFrom(const GTEST_10_TUPLE_(U)& t) {
- f0_ = t.f0_;
- f1_ = t.f1_;
- f2_ = t.f2_;
- f3_ = t.f3_;
- f4_ = t.f4_;
- f5_ = t.f5_;
- f6_ = t.f6_;
- f7_ = t.f7_;
- f8_ = t.f8_;
- f9_ = t.f9_;
- return *this;
- }
-
- T0 f0_;
- T1 f1_;
- T2 f2_;
- T3 f3_;
- T4 f4_;
- T5 f5_;
- T6 f6_;
- T7 f7_;
- T8 f8_;
- T9 f9_;
-};
-
-// 6.1.3.2 Tuple creation functions.
-
-// Known limitations: we don't support passing an
-// std::tr1::reference_wrapper<T> to make_tuple(). And we don't
-// implement tie().
-
-inline tuple<> make_tuple() { return tuple<>(); }
-
-template <GTEST_1_TYPENAMES_(T)>
-inline GTEST_1_TUPLE_(T) make_tuple(const T0& f0) {
- return GTEST_1_TUPLE_(T)(f0);
-}
-
-template <GTEST_2_TYPENAMES_(T)>
-inline GTEST_2_TUPLE_(T) make_tuple(const T0& f0, const T1& f1) {
- return GTEST_2_TUPLE_(T)(f0, f1);
-}
-
-template <GTEST_3_TYPENAMES_(T)>
-inline GTEST_3_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2) {
- return GTEST_3_TUPLE_(T)(f0, f1, f2);
-}
-
-template <GTEST_4_TYPENAMES_(T)>
-inline GTEST_4_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2,
- const T3& f3) {
- return GTEST_4_TUPLE_(T)(f0, f1, f2, f3);
-}
-
-template <GTEST_5_TYPENAMES_(T)>
-inline GTEST_5_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2,
- const T3& f3, const T4& f4) {
- return GTEST_5_TUPLE_(T)(f0, f1, f2, f3, f4);
-}
-
-template <GTEST_6_TYPENAMES_(T)>
-inline GTEST_6_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2,
- const T3& f3, const T4& f4, const T5& f5) {
- return GTEST_6_TUPLE_(T)(f0, f1, f2, f3, f4, f5);
-}
-
-template <GTEST_7_TYPENAMES_(T)>
-inline GTEST_7_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2,
- const T3& f3, const T4& f4, const T5& f5, const T6& f6) {
- return GTEST_7_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6);
-}
-
-template <GTEST_8_TYPENAMES_(T)>
-inline GTEST_8_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2,
- const T3& f3, const T4& f4, const T5& f5, const T6& f6, const T7& f7) {
- return GTEST_8_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6, f7);
-}
-
-template <GTEST_9_TYPENAMES_(T)>
-inline GTEST_9_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2,
- const T3& f3, const T4& f4, const T5& f5, const T6& f6, const T7& f7,
- const T8& f8) {
- return GTEST_9_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6, f7, f8);
-}
-
-template <GTEST_10_TYPENAMES_(T)>
-inline GTEST_10_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2,
- const T3& f3, const T4& f4, const T5& f5, const T6& f6, const T7& f7,
- const T8& f8, const T9& f9) {
- return GTEST_10_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6, f7, f8, f9);
-}
-
-// 6.1.3.3 Tuple helper classes.
-
-template <typename Tuple> struct tuple_size;
-
-template <GTEST_0_TYPENAMES_(T)>
-struct tuple_size<GTEST_0_TUPLE_(T) > {
- static const int value = 0;
-};
-
-template <GTEST_1_TYPENAMES_(T)>
-struct tuple_size<GTEST_1_TUPLE_(T) > {
- static const int value = 1;
-};
-
-template <GTEST_2_TYPENAMES_(T)>
-struct tuple_size<GTEST_2_TUPLE_(T) > {
- static const int value = 2;
-};
-
-template <GTEST_3_TYPENAMES_(T)>
-struct tuple_size<GTEST_3_TUPLE_(T) > {
- static const int value = 3;
-};
-
-template <GTEST_4_TYPENAMES_(T)>
-struct tuple_size<GTEST_4_TUPLE_(T) > {
- static const int value = 4;
-};
-
-template <GTEST_5_TYPENAMES_(T)>
-struct tuple_size<GTEST_5_TUPLE_(T) > {
- static const int value = 5;
-};
-
-template <GTEST_6_TYPENAMES_(T)>
-struct tuple_size<GTEST_6_TUPLE_(T) > {
- static const int value = 6;
-};
-
-template <GTEST_7_TYPENAMES_(T)>
-struct tuple_size<GTEST_7_TUPLE_(T) > {
- static const int value = 7;
-};
-
-template <GTEST_8_TYPENAMES_(T)>
-struct tuple_size<GTEST_8_TUPLE_(T) > {
- static const int value = 8;
-};
-
-template <GTEST_9_TYPENAMES_(T)>
-struct tuple_size<GTEST_9_TUPLE_(T) > {
- static const int value = 9;
-};
-
-template <GTEST_10_TYPENAMES_(T)>
-struct tuple_size<GTEST_10_TUPLE_(T) > {
- static const int value = 10;
-};
-
-template <int k, class Tuple>
-struct tuple_element {
- typedef typename gtest_internal::TupleElement<
- k < (tuple_size<Tuple>::value), k, Tuple>::type type;
-};
-
-#define GTEST_TUPLE_ELEMENT_(k, Tuple) typename tuple_element<k, Tuple >::type
-
-// 6.1.3.4 Element access.
-
-namespace gtest_internal {
-
-template <>
-class Get<0> {
- public:
- template <class Tuple>
- static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(0, Tuple))
- Field(Tuple& t) { return t.f0_; } // NOLINT
-
- template <class Tuple>
- static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(0, Tuple))
- ConstField(const Tuple& t) { return t.f0_; }
-};
-
-template <>
-class Get<1> {
- public:
- template <class Tuple>
- static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(1, Tuple))
- Field(Tuple& t) { return t.f1_; } // NOLINT
-
- template <class Tuple>
- static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(1, Tuple))
- ConstField(const Tuple& t) { return t.f1_; }
-};
-
-template <>
-class Get<2> {
- public:
- template <class Tuple>
- static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(2, Tuple))
- Field(Tuple& t) { return t.f2_; } // NOLINT
-
- template <class Tuple>
- static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(2, Tuple))
- ConstField(const Tuple& t) { return t.f2_; }
-};
-
-template <>
-class Get<3> {
- public:
- template <class Tuple>
- static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(3, Tuple))
- Field(Tuple& t) { return t.f3_; } // NOLINT
-
- template <class Tuple>
- static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(3, Tuple))
- ConstField(const Tuple& t) { return t.f3_; }
-};
-
-template <>
-class Get<4> {
- public:
- template <class Tuple>
- static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(4, Tuple))
- Field(Tuple& t) { return t.f4_; } // NOLINT
-
- template <class Tuple>
- static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(4, Tuple))
- ConstField(const Tuple& t) { return t.f4_; }
-};
-
-template <>
-class Get<5> {
- public:
- template <class Tuple>
- static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(5, Tuple))
- Field(Tuple& t) { return t.f5_; } // NOLINT
-
- template <class Tuple>
- static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(5, Tuple))
- ConstField(const Tuple& t) { return t.f5_; }
-};
-
-template <>
-class Get<6> {
- public:
- template <class Tuple>
- static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(6, Tuple))
- Field(Tuple& t) { return t.f6_; } // NOLINT
-
- template <class Tuple>
- static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(6, Tuple))
- ConstField(const Tuple& t) { return t.f6_; }
-};
-
-template <>
-class Get<7> {
- public:
- template <class Tuple>
- static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(7, Tuple))
- Field(Tuple& t) { return t.f7_; } // NOLINT
-
- template <class Tuple>
- static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(7, Tuple))
- ConstField(const Tuple& t) { return t.f7_; }
-};
-
-template <>
-class Get<8> {
- public:
- template <class Tuple>
- static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(8, Tuple))
- Field(Tuple& t) { return t.f8_; } // NOLINT
-
- template <class Tuple>
- static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(8, Tuple))
- ConstField(const Tuple& t) { return t.f8_; }
-};
-
-template <>
-class Get<9> {
- public:
- template <class Tuple>
- static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(9, Tuple))
- Field(Tuple& t) { return t.f9_; } // NOLINT
-
- template <class Tuple>
- static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(9, Tuple))
- ConstField(const Tuple& t) { return t.f9_; }
-};
-
-} // namespace gtest_internal
-
-template <int k, GTEST_10_TYPENAMES_(T)>
-GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(k, GTEST_10_TUPLE_(T)))
-get(GTEST_10_TUPLE_(T)& t) {
- return gtest_internal::Get<k>::Field(t);
-}
-
-template <int k, GTEST_10_TYPENAMES_(T)>
-GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(k, GTEST_10_TUPLE_(T)))
-get(const GTEST_10_TUPLE_(T)& t) {
- return gtest_internal::Get<k>::ConstField(t);
-}
-
-// 6.1.3.5 Relational operators
-
-// We only implement == and !=, as we don't have a need for the rest yet.
-
-namespace gtest_internal {
-
-// SameSizeTuplePrefixComparator<k, k>::Eq(t1, t2) returns true if the
-// first k fields of t1 equals the first k fields of t2.
-// SameSizeTuplePrefixComparator(k1, k2) would be a compiler error if
-// k1 != k2.
-template <int kSize1, int kSize2>
-struct SameSizeTuplePrefixComparator;
-
-template <>
-struct SameSizeTuplePrefixComparator<0, 0> {
- template <class Tuple1, class Tuple2>
- static bool Eq(const Tuple1& /* t1 */, const Tuple2& /* t2 */) {
- return true;
- }
-};
-
-template <int k>
-struct SameSizeTuplePrefixComparator<k, k> {
- template <class Tuple1, class Tuple2>
- static bool Eq(const Tuple1& t1, const Tuple2& t2) {
- return SameSizeTuplePrefixComparator<k - 1, k - 1>::Eq(t1, t2) &&
- ::std::tr1::get<k - 1>(t1) == ::std::tr1::get<k - 1>(t2);
- }
-};
-
-} // namespace gtest_internal
-
-template <GTEST_10_TYPENAMES_(T), GTEST_10_TYPENAMES_(U)>
-inline bool operator==(const GTEST_10_TUPLE_(T)& t,
- const GTEST_10_TUPLE_(U)& u) {
- return gtest_internal::SameSizeTuplePrefixComparator<
- tuple_size<GTEST_10_TUPLE_(T) >::value,
- tuple_size<GTEST_10_TUPLE_(U) >::value>::Eq(t, u);
-}
-
-template <GTEST_10_TYPENAMES_(T), GTEST_10_TYPENAMES_(U)>
-inline bool operator!=(const GTEST_10_TUPLE_(T)& t,
- const GTEST_10_TUPLE_(U)& u) { return !(t == u); }
-
-// 6.1.4 Pairs.
-// Unimplemented.
-
-} // namespace tr1
-} // namespace std
-
-#undef GTEST_0_TUPLE_
-#undef GTEST_1_TUPLE_
-#undef GTEST_2_TUPLE_
-#undef GTEST_3_TUPLE_
-#undef GTEST_4_TUPLE_
-#undef GTEST_5_TUPLE_
-#undef GTEST_6_TUPLE_
-#undef GTEST_7_TUPLE_
-#undef GTEST_8_TUPLE_
-#undef GTEST_9_TUPLE_
-#undef GTEST_10_TUPLE_
-
-#undef GTEST_0_TYPENAMES_
-#undef GTEST_1_TYPENAMES_
-#undef GTEST_2_TYPENAMES_
-#undef GTEST_3_TYPENAMES_
-#undef GTEST_4_TYPENAMES_
-#undef GTEST_5_TYPENAMES_
-#undef GTEST_6_TYPENAMES_
-#undef GTEST_7_TYPENAMES_
-#undef GTEST_8_TYPENAMES_
-#undef GTEST_9_TYPENAMES_
-#undef GTEST_10_TYPENAMES_
-
-#undef GTEST_DECLARE_TUPLE_AS_FRIEND_
-#undef GTEST_BY_REF_
-#undef GTEST_ADD_REF_
-#undef GTEST_TUPLE_ELEMENT_
-
-#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_
diff --git a/extern/gtest/include/gtest/internal/gtest-type-util.h b/extern/gtest/include/gtest/internal/gtest-type-util.h
index e46f7cfcb48..3d7542d1fb3 100644
--- a/extern/gtest/include/gtest/internal/gtest-type-util.h
+++ b/extern/gtest/include/gtest/internal/gtest-type-util.h
@@ -30,17 +30,17 @@
// 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: wan@google.com (Zhanyong Wan)
// Type utilities needed for implementing typed and type-parameterized
// tests. This file is generated by a SCRIPT. DO NOT EDIT BY HAND!
//
// Currently we support at most 50 types in a list, and at most 50
-// type-parameterized tests in one type-parameterized test case.
+// type-parameterized tests in one type-parameterized test suite.
// Please contact googletestframework@googlegroups.com if you need
// more.
+// GOOGLETEST_CM0001 DO NOT DELETE
+
#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_
#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_
@@ -57,6 +57,22 @@
namespace testing {
namespace internal {
+// Canonicalizes a given name with respect to the Standard C++ Library.
+// This handles removing the inline namespace within `std` that is
+// used by various standard libraries (e.g., `std::__1`). Names outside
+// of namespace std are returned unmodified.
+inline std::string CanonicalizeForStdLibVersioning(std::string s) {
+ static const char prefix[] = "std::__";
+ if (s.compare(0, strlen(prefix), prefix) == 0) {
+ std::string::size_type end = s.find("::", strlen(prefix));
+ if (end != s.npos) {
+ // Erase everything between the initial `std` and the second `::`.
+ s.erase(strlen("std"), end - strlen("std"));
+ }
+ }
+ return s;
+}
+
// GetTypeName<T>() returns a human-readable name of type T.
// NB: This function is also used in Google Mock, so don't move it inside of
// the typed-test-only section below.
@@ -72,10 +88,10 @@ std::string GetTypeName() {
# if GTEST_HAS_CXXABI_H_
using abi::__cxa_demangle;
# endif // GTEST_HAS_CXXABI_H_
- char* const readable_name = __cxa_demangle(name, 0, 0, &status);
+ char* const readable_name = __cxa_demangle(name, nullptr, nullptr, &status);
const std::string name_str(status == 0 ? readable_name : name);
free(readable_name);
- return name_str;
+ return CanonicalizeForStdLibVersioning(name_str);
# else
return name;
# endif // GTEST_HAS_CXXABI_H_ || __HP_aCC
@@ -89,18 +105,6 @@ std::string GetTypeName() {
#if GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P
-// AssertyTypeEq<T1, T2>::type is defined iff T1 and T2 are the same
-// type. This can be used as a compile-time assertion to ensure that
-// two types are equal.
-
-template <typename T1, typename T2>
-struct AssertTypeEq;
-
-template <typename T>
-struct AssertTypeEq<T, T> {
- typedef bool type;
-};
-
// A unique type used as the default value for the arguments of class
// template Types. This allows us to simulate variadic templates
// (e.g. Types<int>, Type<int, double>, and etc), which C++ doesn't
@@ -3295,8 +3299,8 @@ struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
};
// The TypeList template makes it possible to use either a single type
-// or a Types<...> list in TYPED_TEST_CASE() and
-// INSTANTIATE_TYPED_TEST_CASE_P().
+// or a Types<...> list in TYPED_TEST_SUITE() and
+// INSTANTIATE_TYPED_TEST_SUITE_P().
template <typename T>
struct TypeList {
diff --git a/extern/gtest/src/gtest-all.cc b/extern/gtest/src/gtest-all.cc
index 0a9cee52233..ad292905cf3 100644
--- a/extern/gtest/src/gtest-all.cc
+++ b/extern/gtest/src/gtest-all.cc
@@ -26,10 +26,9 @@
// 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: mheule@google.com (Markus Heule)
-//
-// Google C++ Testing Framework (Google Test)
+// Google C++ Testing and Mocking Framework (Google Test)
//
// Sometimes it's desirable to build Google Test by compiling a single file.
// This file serves this purpose.
@@ -42,6 +41,7 @@
#include "src/gtest.cc"
#include "src/gtest-death-test.cc"
#include "src/gtest-filepath.cc"
+#include "src/gtest-matchers.cc"
#include "src/gtest-port.cc"
#include "src/gtest-printers.cc"
#include "src/gtest-test-part.cc"
diff --git a/extern/gtest/src/gtest-death-test.cc b/extern/gtest/src/gtest-death-test.cc
index a01a3698308..da09a1cfc23 100644
--- a/extern/gtest/src/gtest-death-test.cc
+++ b/extern/gtest/src/gtest-death-test.cc
@@ -26,12 +26,14 @@
// 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: wan@google.com (Zhanyong Wan), vladl@google.com (Vlad Losev)
+
//
// This file implements death tests.
#include "gtest/gtest-death-test.h"
+
+#include <utility>
+
#include "gtest/internal/gtest-port.h"
#include "gtest/internal/custom/gtest.h"
@@ -62,26 +64,36 @@
# include <spawn.h>
# endif // GTEST_OS_QNX
+# if GTEST_OS_FUCHSIA
+# include <lib/fdio/fd.h>
+# include <lib/fdio/io.h>
+# include <lib/fdio/spawn.h>
+# include <lib/zx/channel.h>
+# include <lib/zx/port.h>
+# include <lib/zx/process.h>
+# include <lib/zx/socket.h>
+# include <zircon/processargs.h>
+# include <zircon/syscalls.h>
+# include <zircon/syscalls/policy.h>
+# include <zircon/syscalls/port.h>
+# endif // GTEST_OS_FUCHSIA
+
#endif // GTEST_HAS_DEATH_TEST
#include "gtest/gtest-message.h"
#include "gtest/internal/gtest-string.h"
-
-// Indicates that this translation unit is part of Google Test's
-// implementation. It must come before gtest-internal-inl.h is
-// included, or there will be a compiler error. This trick exists to
-// prevent the accidental inclusion of gtest-internal-inl.h in the
-// user's code.
-#define GTEST_IMPLEMENTATION_ 1
#include "src/gtest-internal-inl.h"
-#undef GTEST_IMPLEMENTATION_
namespace testing {
// Constants.
// The default death test style.
-static const char kDefaultDeathTestStyle[] = "fast";
+//
+// This is defined in internal/gtest-port.h as "fast", but can be overridden by
+// a definition in internal/custom/gtest-port.h. The recommended value, which is
+// used internally at Google, is "threadsafe".
+static const char kDefaultDeathTestStyle[] = GTEST_DEFAULT_DEATH_TEST_STYLE;
GTEST_DEFINE_string_(
death_test_style,
@@ -110,8 +122,8 @@ GTEST_DEFINE_string_(
"Indicates the file, line number, temporal index of "
"the single death test to run, and a file descriptor to "
"which a success code may be sent, all separated by "
- "the '|' characters. This flag is specified if and only if the current "
- "process is a sub-process launched for running a thread-safe "
+ "the '|' characters. This flag is specified if and only if the "
+ "current process is a sub-process launched for running a thread-safe "
"death test. FOR INTERNAL USE ONLY.");
} // namespace internal
@@ -121,7 +133,7 @@ namespace internal {
// Valid only for fast death tests. Indicates the code is running in the
// child process of a fast style death test.
-# if !GTEST_OS_WINDOWS
+# if !GTEST_OS_WINDOWS && !GTEST_OS_FUCHSIA
static bool g_in_fast_death_test_child = false;
# endif
@@ -131,10 +143,10 @@ static bool g_in_fast_death_test_child = false;
// tests. IMPORTANT: This is an internal utility. Using it may break the
// implementation of death tests. User code MUST NOT use it.
bool InDeathTestChild() {
-# if GTEST_OS_WINDOWS
+# if GTEST_OS_WINDOWS || GTEST_OS_FUCHSIA
- // On Windows, death tests are thread-safe regardless of the value of the
- // death_test_style flag.
+ // On Windows and Fuchsia, death tests are thread-safe regardless of the value
+ // of the death_test_style flag.
return !GTEST_FLAG(internal_run_death_test).empty();
# else
@@ -154,7 +166,7 @@ ExitedWithCode::ExitedWithCode(int exit_code) : exit_code_(exit_code) {
// ExitedWithCode function-call operator.
bool ExitedWithCode::operator()(int exit_status) const {
-# if GTEST_OS_WINDOWS
+# if GTEST_OS_WINDOWS || GTEST_OS_FUCHSIA
return exit_status == exit_code_;
@@ -162,10 +174,10 @@ bool ExitedWithCode::operator()(int exit_status) const {
return WIFEXITED(exit_status) && WEXITSTATUS(exit_status) == exit_code_;
-# endif // GTEST_OS_WINDOWS
+# endif // GTEST_OS_WINDOWS || GTEST_OS_FUCHSIA
}
-# if !GTEST_OS_WINDOWS
+# if !GTEST_OS_WINDOWS && !GTEST_OS_FUCHSIA
// KilledBySignal constructor.
KilledBySignal::KilledBySignal(int signum) : signum_(signum) {
}
@@ -182,7 +194,7 @@ bool KilledBySignal::operator()(int exit_status) const {
# endif // defined(GTEST_KILLED_BY_SIGNAL_OVERRIDE_)
return WIFSIGNALED(exit_status) && WTERMSIG(exit_status) == signum_;
}
-# endif // !GTEST_OS_WINDOWS
+# endif // !GTEST_OS_WINDOWS && !GTEST_OS_FUCHSIA
namespace internal {
@@ -193,7 +205,7 @@ namespace internal {
static std::string ExitSummary(int exit_code) {
Message m;
-# if GTEST_OS_WINDOWS
+# if GTEST_OS_WINDOWS || GTEST_OS_FUCHSIA
m << "Exited with exit status " << exit_code;
@@ -209,7 +221,7 @@ static std::string ExitSummary(int exit_code) {
m << " (core dumped)";
}
# endif
-# endif // GTEST_OS_WINDOWS
+# endif // GTEST_OS_WINDOWS || GTEST_OS_FUCHSIA
return m.GetString();
}
@@ -220,7 +232,7 @@ bool ExitedUnsuccessfully(int exit_status) {
return !ExitedWithCode(0)(exit_status);
}
-# if !GTEST_OS_WINDOWS
+# if !GTEST_OS_WINDOWS && !GTEST_OS_FUCHSIA
// Generates a textual failure message when a death test finds more than
// one thread running, or cannot determine the number of threads, prior
// to executing the given statement. It is the responsibility of the
@@ -229,13 +241,19 @@ static std::string DeathTestThreadWarning(size_t thread_count) {
Message msg;
msg << "Death tests use fork(), which is unsafe particularly"
<< " in a threaded context. For this test, " << GTEST_NAME_ << " ";
- if (thread_count == 0)
+ if (thread_count == 0) {
msg << "couldn't detect the number of threads.";
- else
+ } else {
msg << "detected " << thread_count << " threads.";
+ }
+ msg << " See "
+ "https://github.com/google/googletest/blob/master/googletest/docs/"
+ "advanced.md#death-tests-and-threads"
+ << " for more explanation and suggested solutions, especially if"
+ << " this is the last message you see before your test times out.";
return msg.GetString();
}
-# endif // !GTEST_OS_WINDOWS
+# endif // !GTEST_OS_WINDOWS && !GTEST_OS_FUCHSIA
// Flag characters for reporting a death test that did not die.
static const char kDeathTestLived = 'L';
@@ -243,6 +261,13 @@ static const char kDeathTestReturned = 'R';
static const char kDeathTestThrew = 'T';
static const char kDeathTestInternalError = 'I';
+#if GTEST_OS_FUCHSIA
+
+// File descriptor used for the pipe in the child process.
+static const int kFuchsiaReadPipeFd = 3;
+
+#endif
+
// An enumeration describing all of the possible ways that a death test can
// conclude. DIED means that the process died while executing the test
// code; LIVED means that process lived beyond the end of the test code;
@@ -250,8 +275,6 @@ static const char kDeathTestInternalError = 'I';
// statement, which is not allowed; THREW means that the test statement
// returned control by throwing an exception. IN_PROGRESS means the test
// has not yet concluded.
-// TODO(vladl@google.com): Unify names and possibly values for
-// AbortReason, DeathTestOutcome, and flag characters above.
enum DeathTestOutcome { IN_PROGRESS, DIED, LIVED, RETURNED, THREW };
// Routine for aborting the program which is safe to call from an
@@ -259,13 +282,13 @@ enum DeathTestOutcome { IN_PROGRESS, DIED, LIVED, RETURNED, THREW };
// message is propagated back to the parent process. Otherwise, the
// message is simply printed to stderr. In either case, the program
// then exits with status 1.
-void DeathTestAbort(const std::string& message) {
+static void DeathTestAbort(const std::string& message) {
// On a POSIX system, this function may be called from a threadsafe-style
// death test child process, which operates on a very small stack. Use
// the heap for any additional non-minuscule memory requirements.
const InternalRunDeathTestFlag* const flag =
GetUnitTestImpl()->internal_run_death_test_flag();
- if (flag != NULL) {
+ if (flag != nullptr) {
FILE* parent = posix::FDOpen(flag->write_fd(), "w");
fputc(kDeathTestInternalError, parent);
fprintf(parent, "%s", message.c_str());
@@ -345,7 +368,7 @@ static void FailFromInternalError(int fd) {
// for the current test.
DeathTest::DeathTest() {
TestInfo* const info = GetUnitTestImpl()->current_test_info();
- if (info == NULL) {
+ if (info == nullptr) {
DeathTestAbort("Cannot run a death test outside of a TEST or "
"TEST_F construct");
}
@@ -353,10 +376,11 @@ DeathTest::DeathTest() {
// Creates and returns a death test by dispatching to the current
// death test factory.
-bool DeathTest::Create(const char* statement, const RE* regex,
- const char* file, int line, DeathTest** test) {
+bool DeathTest::Create(const char* statement,
+ Matcher<const std::string&> matcher, const char* file,
+ int line, DeathTest** test) {
return GetUnitTestImpl()->death_test_factory()->Create(
- statement, regex, file, line, test);
+ statement, std::move(matcher), file, line, test);
}
const char* DeathTest::LastMessage() {
@@ -372,9 +396,9 @@ std::string DeathTest::last_death_test_message_;
// Provides cross platform implementation for some death functionality.
class DeathTestImpl : public DeathTest {
protected:
- DeathTestImpl(const char* a_statement, const RE* a_regex)
+ DeathTestImpl(const char* a_statement, Matcher<const std::string&> matcher)
: statement_(a_statement),
- regex_(a_regex),
+ matcher_(std::move(matcher)),
spawned_(false),
status_(-1),
outcome_(IN_PROGRESS),
@@ -382,13 +406,12 @@ class DeathTestImpl : public DeathTest {
write_fd_(-1) {}
// read_fd_ is expected to be closed and cleared by a derived class.
- ~DeathTestImpl() { GTEST_DEATH_TEST_CHECK_(read_fd_ == -1); }
+ ~DeathTestImpl() override { GTEST_DEATH_TEST_CHECK_(read_fd_ == -1); }
- void Abort(AbortReason reason);
- virtual bool Passed(bool status_ok);
+ void Abort(AbortReason reason) override;
+ bool Passed(bool status_ok) override;
const char* statement() const { return statement_; }
- const RE* regex() const { return regex_; }
bool spawned() const { return spawned_; }
void set_spawned(bool is_spawned) { spawned_ = is_spawned; }
int status() const { return status_; }
@@ -406,13 +429,15 @@ class DeathTestImpl : public DeathTest {
// case of unexpected codes.
void ReadAndInterpretStatusByte();
+ // Returns stderr output from the child process.
+ virtual std::string GetErrorLogs();
+
private:
// The textual content of the code this object is testing. This class
// doesn't own this string and should not attempt to delete it.
const char* const statement_;
- // The regular expression which test output must match. DeathTestImpl
- // doesn't own this object and should not attempt to delete it.
- const RE* const regex_;
+ // A matcher that's expected to match the stderr output by the child process.
+ Matcher<const std::string&> matcher_;
// True if the death test child process has been successfully spawned.
bool spawned_;
// The exit status of the child process.
@@ -474,6 +499,10 @@ void DeathTestImpl::ReadAndInterpretStatusByte() {
set_read_fd(-1);
}
+std::string DeathTestImpl::GetErrorLogs() {
+ return GetCapturedStderr();
+}
+
// Signals that the death test code which should have exited, didn't.
// Should be called only in a death test child process.
// Writes a status byte to the child's status file descriptor, then
@@ -527,22 +556,21 @@ static ::std::string FormatDeathTestOutput(const ::std::string& output) {
// in the format specified by wait(2). On Windows, this is the
// value supplied to the ExitProcess() API or a numeric code
// of the exception that terminated the program.
-// regex: A regular expression object to be applied to
-// the test's captured standard error output; the death test
-// fails if it does not match.
+// matcher_: A matcher that's expected to match the stderr output by the child
+// process.
//
// Argument:
// status_ok: true if exit_status is acceptable in the context of
// this particular death test, which fails if it is false
//
-// Returns true iff all of the above conditions are met. Otherwise, the
-// first failing condition, in the order given above, is the one that is
+// Returns true if and only if all of the above conditions are met. Otherwise,
+// the first failing condition, in the order given above, is the one that is
// reported. Also sets the last death test message string.
bool DeathTestImpl::Passed(bool status_ok) {
if (!spawned())
return false;
- const std::string error_message = GetCapturedStderr();
+ const std::string error_message = GetErrorLogs();
bool success = false;
Message buffer;
@@ -563,13 +591,15 @@ bool DeathTestImpl::Passed(bool status_ok) {
break;
case DIED:
if (status_ok) {
- const bool matched = RE::PartialMatch(error_message.c_str(), *regex());
- if (matched) {
+ if (matcher_.Matches(error_message)) {
success = true;
} else {
+ std::ostringstream stream;
+ matcher_.DescribeTo(&stream);
buffer << " Result: died but not with expected error.\n"
- << " Expected: " << regex()->pattern() << "\n"
- << "Actual msg:\n" << FormatDeathTestOutput(error_message);
+ << " Expected: " << stream.str() << "\n"
+ << "Actual msg:\n"
+ << FormatDeathTestOutput(error_message);
}
} else {
buffer << " Result: died but not with expected exit code:\n"
@@ -618,11 +648,11 @@ bool DeathTestImpl::Passed(bool status_ok) {
//
class WindowsDeathTest : public DeathTestImpl {
public:
- WindowsDeathTest(const char* a_statement,
- const RE* a_regex,
- const char* file,
- int line)
- : DeathTestImpl(a_statement, a_regex), file_(file), line_(line) {}
+ WindowsDeathTest(const char* a_statement, Matcher<const std::string&> matcher,
+ const char* file, int line)
+ : DeathTestImpl(a_statement, std::move(matcher)),
+ file_(file),
+ line_(line) {}
// All of these virtual functions are inherited from DeathTest.
virtual int Wait();
@@ -699,7 +729,7 @@ DeathTest::TestRole WindowsDeathTest::AssumeRole() {
const TestInfo* const info = impl->current_test_info();
const int death_test_index = info->result()->death_test_count();
- if (flag != NULL) {
+ if (flag != nullptr) {
// ParseInternalRunDeathTestFlag() has performed all the necessary
// processing.
set_write_fd(flag->write_fd());
@@ -708,8 +738,8 @@ DeathTest::TestRole WindowsDeathTest::AssumeRole() {
// WindowsDeathTest uses an anonymous pipe to communicate results of
// a death test.
- SECURITY_ATTRIBUTES handles_are_inheritable = {
- sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
+ SECURITY_ATTRIBUTES handles_are_inheritable = {sizeof(SECURITY_ATTRIBUTES),
+ nullptr, TRUE};
HANDLE read_handle, write_handle;
GTEST_DEATH_TEST_CHECK_(
::CreatePipe(&read_handle, &write_handle, &handles_are_inheritable,
@@ -720,13 +750,13 @@ DeathTest::TestRole WindowsDeathTest::AssumeRole() {
write_handle_.Reset(write_handle);
event_handle_.Reset(::CreateEvent(
&handles_are_inheritable,
- TRUE, // The event will automatically reset to non-signaled state.
- FALSE, // The initial state is non-signalled.
- NULL)); // The even is unnamed.
- GTEST_DEATH_TEST_CHECK_(event_handle_.Get() != NULL);
- const std::string filter_flag =
- std::string("--") + GTEST_FLAG_PREFIX_ + kFilterFlag + "=" +
- info->test_case_name() + "." + info->name();
+ TRUE, // The event will automatically reset to non-signaled state.
+ FALSE, // The initial state is non-signalled.
+ nullptr)); // The even is unnamed.
+ GTEST_DEATH_TEST_CHECK_(event_handle_.Get() != nullptr);
+ const std::string filter_flag = std::string("--") + GTEST_FLAG_PREFIX_ +
+ kFilterFlag + "=" + info->test_suite_name() +
+ "." + info->name();
const std::string internal_flag =
std::string("--") + GTEST_FLAG_PREFIX_ + kInternalRunDeathTestFlag +
"=" + file_ + "|" + StreamableToString(line_) + "|" +
@@ -739,10 +769,9 @@ DeathTest::TestRole WindowsDeathTest::AssumeRole() {
"|" + StreamableToString(reinterpret_cast<size_t>(event_handle_.Get()));
char executable_path[_MAX_PATH + 1]; // NOLINT
- GTEST_DEATH_TEST_CHECK_(
- _MAX_PATH + 1 != ::GetModuleFileNameA(NULL,
- executable_path,
- _MAX_PATH));
+ GTEST_DEATH_TEST_CHECK_(_MAX_PATH + 1 != ::GetModuleFileNameA(nullptr,
+ executable_path,
+ _MAX_PATH));
std::string command_line =
std::string(::GetCommandLineA()) + " " + filter_flag + " \"" +
@@ -763,33 +792,290 @@ DeathTest::TestRole WindowsDeathTest::AssumeRole() {
startup_info.hStdError = ::GetStdHandle(STD_ERROR_HANDLE);
PROCESS_INFORMATION process_info;
- GTEST_DEATH_TEST_CHECK_(::CreateProcessA(
- executable_path,
- const_cast<char*>(command_line.c_str()),
- NULL, // Retuned process handle is not inheritable.
- NULL, // Retuned thread handle is not inheritable.
- TRUE, // Child inherits all inheritable handles (for write_handle_).
- 0x0, // Default creation flags.
- NULL, // Inherit the parent's environment.
- UnitTest::GetInstance()->original_working_dir(),
- &startup_info,
- &process_info) != FALSE);
+ GTEST_DEATH_TEST_CHECK_(
+ ::CreateProcessA(
+ executable_path, const_cast<char*>(command_line.c_str()),
+ nullptr, // Retuned process handle is not inheritable.
+ nullptr, // Retuned thread handle is not inheritable.
+ TRUE, // Child inherits all inheritable handles (for write_handle_).
+ 0x0, // Default creation flags.
+ nullptr, // Inherit the parent's environment.
+ UnitTest::GetInstance()->original_working_dir(), &startup_info,
+ &process_info) != FALSE);
child_handle_.Reset(process_info.hProcess);
::CloseHandle(process_info.hThread);
set_spawned(true);
return OVERSEE_TEST;
}
-# else // We are not on Windows.
+
+# elif GTEST_OS_FUCHSIA
+
+class FuchsiaDeathTest : public DeathTestImpl {
+ public:
+ FuchsiaDeathTest(const char* a_statement, Matcher<const std::string&> matcher,
+ const char* file, int line)
+ : DeathTestImpl(a_statement, std::move(matcher)),
+ file_(file),
+ line_(line) {}
+
+ // All of these virtual functions are inherited from DeathTest.
+ int Wait() override;
+ TestRole AssumeRole() override;
+ std::string GetErrorLogs() override;
+
+ private:
+ // The name of the file in which the death test is located.
+ const char* const file_;
+ // The line number on which the death test is located.
+ const int line_;
+ // The stderr data captured by the child process.
+ std::string captured_stderr_;
+
+ zx::process child_process_;
+ zx::channel exception_channel_;
+ zx::socket stderr_socket_;
+};
+
+// Utility class for accumulating command-line arguments.
+class Arguments {
+ public:
+ Arguments() { args_.push_back(nullptr); }
+
+ ~Arguments() {
+ for (std::vector<char*>::iterator i = args_.begin(); i != args_.end();
+ ++i) {
+ free(*i);
+ }
+ }
+ void AddArgument(const char* argument) {
+ args_.insert(args_.end() - 1, posix::StrDup(argument));
+ }
+
+ template <typename Str>
+ void AddArguments(const ::std::vector<Str>& arguments) {
+ for (typename ::std::vector<Str>::const_iterator i = arguments.begin();
+ i != arguments.end();
+ ++i) {
+ args_.insert(args_.end() - 1, posix::StrDup(i->c_str()));
+ }
+ }
+ char* const* Argv() {
+ return &args_[0];
+ }
+
+ int size() {
+ return args_.size() - 1;
+ }
+
+ private:
+ std::vector<char*> args_;
+};
+
+// Waits for the child in a death test to exit, returning its exit
+// status, or 0 if no child process exists. As a side effect, sets the
+// outcome data member.
+int FuchsiaDeathTest::Wait() {
+ const int kProcessKey = 0;
+ const int kSocketKey = 1;
+ const int kExceptionKey = 2;
+
+ if (!spawned())
+ return 0;
+
+ // Create a port to wait for socket/task/exception events.
+ zx_status_t status_zx;
+ zx::port port;
+ status_zx = zx::port::create(0, &port);
+ GTEST_DEATH_TEST_CHECK_(status_zx == ZX_OK);
+
+ // Register to wait for the child process to terminate.
+ status_zx = child_process_.wait_async(
+ port, kProcessKey, ZX_PROCESS_TERMINATED, ZX_WAIT_ASYNC_ONCE);
+ GTEST_DEATH_TEST_CHECK_(status_zx == ZX_OK);
+
+ // Register to wait for the socket to be readable or closed.
+ status_zx = stderr_socket_.wait_async(
+ port, kSocketKey, ZX_SOCKET_READABLE | ZX_SOCKET_PEER_CLOSED,
+ ZX_WAIT_ASYNC_ONCE);
+ GTEST_DEATH_TEST_CHECK_(status_zx == ZX_OK);
+
+ // Register to wait for an exception.
+ status_zx = exception_channel_.wait_async(
+ port, kExceptionKey, ZX_CHANNEL_READABLE, ZX_WAIT_ASYNC_ONCE);
+ GTEST_DEATH_TEST_CHECK_(status_zx == ZX_OK);
+
+ bool process_terminated = false;
+ bool socket_closed = false;
+ do {
+ zx_port_packet_t packet = {};
+ status_zx = port.wait(zx::time::infinite(), &packet);
+ GTEST_DEATH_TEST_CHECK_(status_zx == ZX_OK);
+
+ if (packet.key == kExceptionKey) {
+ // Process encountered an exception. Kill it directly rather than
+ // letting other handlers process the event. We will get a kProcessKey
+ // event when the process actually terminates.
+ status_zx = child_process_.kill();
+ GTEST_DEATH_TEST_CHECK_(status_zx == ZX_OK);
+ } else if (packet.key == kProcessKey) {
+ // Process terminated.
+ GTEST_DEATH_TEST_CHECK_(ZX_PKT_IS_SIGNAL_ONE(packet.type));
+ GTEST_DEATH_TEST_CHECK_(packet.signal.observed & ZX_PROCESS_TERMINATED);
+ process_terminated = true;
+ } else if (packet.key == kSocketKey) {
+ GTEST_DEATH_TEST_CHECK_(ZX_PKT_IS_SIGNAL_ONE(packet.type));
+ if (packet.signal.observed & ZX_SOCKET_READABLE) {
+ // Read data from the socket.
+ constexpr size_t kBufferSize = 1024;
+ do {
+ size_t old_length = captured_stderr_.length();
+ size_t bytes_read = 0;
+ captured_stderr_.resize(old_length + kBufferSize);
+ status_zx = stderr_socket_.read(
+ 0, &captured_stderr_.front() + old_length, kBufferSize,
+ &bytes_read);
+ captured_stderr_.resize(old_length + bytes_read);
+ } while (status_zx == ZX_OK);
+ if (status_zx == ZX_ERR_PEER_CLOSED) {
+ socket_closed = true;
+ } else {
+ GTEST_DEATH_TEST_CHECK_(status_zx == ZX_ERR_SHOULD_WAIT);
+ status_zx = stderr_socket_.wait_async(
+ port, kSocketKey, ZX_SOCKET_READABLE | ZX_SOCKET_PEER_CLOSED,
+ ZX_WAIT_ASYNC_ONCE);
+ GTEST_DEATH_TEST_CHECK_(status_zx == ZX_OK);
+ }
+ } else {
+ GTEST_DEATH_TEST_CHECK_(packet.signal.observed & ZX_SOCKET_PEER_CLOSED);
+ socket_closed = true;
+ }
+ }
+ } while (!process_terminated && !socket_closed);
+
+ ReadAndInterpretStatusByte();
+
+ zx_info_process_t buffer;
+ status_zx = child_process_.get_info(
+ ZX_INFO_PROCESS, &buffer, sizeof(buffer), nullptr, nullptr);
+ GTEST_DEATH_TEST_CHECK_(status_zx == ZX_OK);
+
+ GTEST_DEATH_TEST_CHECK_(buffer.exited);
+ set_status(buffer.return_code);
+ return status();
+}
+
+// The AssumeRole process for a Fuchsia death test. It creates a child
+// process with the same executable as the current process to run the
+// death test. The child process is given the --gtest_filter and
+// --gtest_internal_run_death_test flags such that it knows to run the
+// current death test only.
+DeathTest::TestRole FuchsiaDeathTest::AssumeRole() {
+ const UnitTestImpl* const impl = GetUnitTestImpl();
+ const InternalRunDeathTestFlag* const flag =
+ impl->internal_run_death_test_flag();
+ const TestInfo* const info = impl->current_test_info();
+ const int death_test_index = info->result()->death_test_count();
+
+ if (flag != nullptr) {
+ // ParseInternalRunDeathTestFlag() has performed all the necessary
+ // processing.
+ set_write_fd(kFuchsiaReadPipeFd);
+ return EXECUTE_TEST;
+ }
+
+ // Flush the log buffers since the log streams are shared with the child.
+ FlushInfoLog();
+
+ // Build the child process command line.
+ const std::string filter_flag = std::string("--") + GTEST_FLAG_PREFIX_ +
+ kFilterFlag + "=" + info->test_suite_name() +
+ "." + info->name();
+ const std::string internal_flag =
+ std::string("--") + GTEST_FLAG_PREFIX_ + kInternalRunDeathTestFlag + "="
+ + file_ + "|"
+ + StreamableToString(line_) + "|"
+ + StreamableToString(death_test_index);
+ Arguments args;
+ args.AddArguments(GetInjectableArgvs());
+ args.AddArgument(filter_flag.c_str());
+ args.AddArgument(internal_flag.c_str());
+
+ // Build the pipe for communication with the child.
+ zx_status_t status;
+ zx_handle_t child_pipe_handle;
+ int child_pipe_fd;
+ status = fdio_pipe_half(&child_pipe_fd, &child_pipe_handle);
+ GTEST_DEATH_TEST_CHECK_(status == ZX_OK);
+ set_read_fd(child_pipe_fd);
+
+ // Set the pipe handle for the child.
+ fdio_spawn_action_t spawn_actions[2] = {};
+ fdio_spawn_action_t* add_handle_action = &spawn_actions[0];
+ add_handle_action->action = FDIO_SPAWN_ACTION_ADD_HANDLE;
+ add_handle_action->h.id = PA_HND(PA_FD, kFuchsiaReadPipeFd);
+ add_handle_action->h.handle = child_pipe_handle;
+
+ // Create a socket pair will be used to receive the child process' stderr.
+ zx::socket stderr_producer_socket;
+ status =
+ zx::socket::create(0, &stderr_producer_socket, &stderr_socket_);
+ GTEST_DEATH_TEST_CHECK_(status >= 0);
+ int stderr_producer_fd = -1;
+ status =
+ fdio_fd_create(stderr_producer_socket.release(), &stderr_producer_fd);
+ GTEST_DEATH_TEST_CHECK_(status >= 0);
+
+ // Make the stderr socket nonblocking.
+ GTEST_DEATH_TEST_CHECK_(fcntl(stderr_producer_fd, F_SETFL, 0) == 0);
+
+ fdio_spawn_action_t* add_stderr_action = &spawn_actions[1];
+ add_stderr_action->action = FDIO_SPAWN_ACTION_CLONE_FD;
+ add_stderr_action->fd.local_fd = stderr_producer_fd;
+ add_stderr_action->fd.target_fd = STDERR_FILENO;
+
+ // Create a child job.
+ zx_handle_t child_job = ZX_HANDLE_INVALID;
+ status = zx_job_create(zx_job_default(), 0, & child_job);
+ GTEST_DEATH_TEST_CHECK_(status == ZX_OK);
+ zx_policy_basic_t policy;
+ policy.condition = ZX_POL_NEW_ANY;
+ policy.policy = ZX_POL_ACTION_ALLOW;
+ status = zx_job_set_policy(
+ child_job, ZX_JOB_POL_RELATIVE, ZX_JOB_POL_BASIC, &policy, 1);
+ GTEST_DEATH_TEST_CHECK_(status == ZX_OK);
+
+ // Create an exception channel attached to the |child_job|, to allow
+ // us to suppress the system default exception handler from firing.
+ status =
+ zx_task_create_exception_channel(
+ child_job, 0, exception_channel_.reset_and_get_address());
+ GTEST_DEATH_TEST_CHECK_(status == ZX_OK);
+
+ // Spawn the child process.
+ status = fdio_spawn_etc(
+ child_job, FDIO_SPAWN_CLONE_ALL, args.Argv()[0], args.Argv(), nullptr,
+ 2, spawn_actions, child_process_.reset_and_get_address(), nullptr);
+ GTEST_DEATH_TEST_CHECK_(status == ZX_OK);
+
+ set_spawned(true);
+ return OVERSEE_TEST;
+}
+
+std::string FuchsiaDeathTest::GetErrorLogs() {
+ return captured_stderr_;
+}
+
+#else // We are neither on Windows, nor on Fuchsia.
// ForkingDeathTest provides implementations for most of the abstract
// methods of the DeathTest interface. Only the AssumeRole method is
// left undefined.
class ForkingDeathTest : public DeathTestImpl {
public:
- ForkingDeathTest(const char* statement, const RE* regex);
+ ForkingDeathTest(const char* statement, Matcher<const std::string&> matcher);
// All of these virtual functions are inherited from DeathTest.
- virtual int Wait();
+ int Wait() override;
protected:
void set_child_pid(pid_t child_pid) { child_pid_ = child_pid; }
@@ -800,9 +1086,9 @@ class ForkingDeathTest : public DeathTestImpl {
};
// Constructs a ForkingDeathTest.
-ForkingDeathTest::ForkingDeathTest(const char* a_statement, const RE* a_regex)
- : DeathTestImpl(a_statement, a_regex),
- child_pid_(-1) {}
+ForkingDeathTest::ForkingDeathTest(const char* a_statement,
+ Matcher<const std::string&> matcher)
+ : DeathTestImpl(a_statement, std::move(matcher)), child_pid_(-1) {}
// Waits for the child in a death test to exit, returning its exit
// status, or 0 if no child process exists. As a side effect, sets the
@@ -823,9 +1109,9 @@ int ForkingDeathTest::Wait() {
// in the child process.
class NoExecDeathTest : public ForkingDeathTest {
public:
- NoExecDeathTest(const char* a_statement, const RE* a_regex) :
- ForkingDeathTest(a_statement, a_regex) { }
- virtual TestRole AssumeRole();
+ NoExecDeathTest(const char* a_statement, Matcher<const std::string&> matcher)
+ : ForkingDeathTest(a_statement, std::move(matcher)) {}
+ TestRole AssumeRole() override;
};
// The AssumeRole process for a fork-and-run death test. It implements a
@@ -878,16 +1164,18 @@ DeathTest::TestRole NoExecDeathTest::AssumeRole() {
// only this specific death test to be run.
class ExecDeathTest : public ForkingDeathTest {
public:
- ExecDeathTest(const char* a_statement, const RE* a_regex,
- const char* file, int line) :
- ForkingDeathTest(a_statement, a_regex), file_(file), line_(line) { }
- virtual TestRole AssumeRole();
+ ExecDeathTest(const char* a_statement, Matcher<const std::string&> matcher,
+ const char* file, int line)
+ : ForkingDeathTest(a_statement, std::move(matcher)),
+ file_(file),
+ line_(line) {}
+ TestRole AssumeRole() override;
+
private:
- static ::std::vector<testing::internal::string>
- GetArgvsForDeathTestChildProcess() {
- ::std::vector<testing::internal::string> args = GetInjectableArgvs();
+ static ::std::vector<std::string> GetArgvsForDeathTestChildProcess() {
+ ::std::vector<std::string> args = GetInjectableArgvs();
# if defined(GTEST_EXTRA_DEATH_TEST_COMMAND_LINE_ARGS_)
- ::std::vector<testing::internal::string> extra_args =
+ ::std::vector<std::string> extra_args =
GTEST_EXTRA_DEATH_TEST_COMMAND_LINE_ARGS_();
args.insert(args.end(), extra_args.begin(), extra_args.end());
# endif // defined(GTEST_EXTRA_DEATH_TEST_COMMAND_LINE_ARGS_)
@@ -902,9 +1190,7 @@ class ExecDeathTest : public ForkingDeathTest {
// Utility class for accumulating command-line arguments.
class Arguments {
public:
- Arguments() {
- args_.push_back(NULL);
- }
+ Arguments() { args_.push_back(nullptr); }
~Arguments() {
for (std::vector<char*>::iterator i = args_.begin(); i != args_.end();
@@ -986,6 +1272,7 @@ static int ExecDeathTestChildMain(void* child_arg) {
}
# endif // !GTEST_OS_QNX
+# if GTEST_HAS_CLONE
// Two utility routines that together determine the direction the stack
// grows.
// This could be accomplished more elegantly by a single recursive
@@ -995,20 +1282,26 @@ static int ExecDeathTestChildMain(void* child_arg) {
// GTEST_NO_INLINE_ is required to prevent GCC 4.6 from inlining
// StackLowerThanAddress into StackGrowsDown, which then doesn't give
// correct answer.
-void StackLowerThanAddress(const void* ptr, bool* result) GTEST_NO_INLINE_;
-void StackLowerThanAddress(const void* ptr, bool* result) {
+static void StackLowerThanAddress(const void* ptr,
+ bool* result) GTEST_NO_INLINE_;
+// HWAddressSanitizer add a random tag to the MSB of the local variable address,
+// making comparison result unpredictable.
+GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_
+static void StackLowerThanAddress(const void* ptr, bool* result) {
int dummy;
*result = (&dummy < ptr);
}
// Make sure AddressSanitizer does not tamper with the stack here.
GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_
-bool StackGrowsDown() {
+GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_
+static bool StackGrowsDown() {
int dummy;
bool result;
StackLowerThanAddress(&dummy, &result);
return result;
}
+# endif // GTEST_HAS_CLONE
// Spawns a child process with the same executable as the current process in
// a thread-safe manner and instructs it to run the death test. The
@@ -1046,7 +1339,8 @@ static pid_t ExecDeathTestSpawnChild(char* const* argv, int close_fd) {
fd_flags | FD_CLOEXEC));
struct inheritance inherit = {0};
// spawn is a system call.
- child_pid = spawn(args.argv[0], 0, NULL, &inherit, args.argv, GetEnviron());
+ child_pid =
+ spawn(args.argv[0], 0, nullptr, &inherit, args.argv, GetEnviron());
// Restores the current working directory.
GTEST_DEATH_TEST_CHECK_(fchdir(cwd_fd) != -1);
GTEST_DEATH_TEST_CHECK_SYSCALL_(close(cwd_fd));
@@ -1070,9 +1364,9 @@ static pid_t ExecDeathTestSpawnChild(char* const* argv, int close_fd) {
if (!use_fork) {
static const bool stack_grows_down = StackGrowsDown();
- const size_t stack_size = getpagesize();
+ const auto stack_size = static_cast<size_t>(getpagesize());
// MMAP_ANONYMOUS is not defined on Mac, so we use MAP_ANON instead.
- void* const stack = mmap(NULL, stack_size, PROT_READ | PROT_WRITE,
+ void* const stack = mmap(nullptr, stack_size, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE, -1, 0);
GTEST_DEATH_TEST_CHECK_(stack != MAP_FAILED);
@@ -1086,8 +1380,9 @@ static pid_t ExecDeathTestSpawnChild(char* const* argv, int close_fd) {
void* const stack_top =
static_cast<char*>(stack) +
(stack_grows_down ? stack_size - kMaxStackAlignment : 0);
- GTEST_DEATH_TEST_CHECK_(stack_size > kMaxStackAlignment &&
- reinterpret_cast<intptr_t>(stack_top) % kMaxStackAlignment == 0);
+ GTEST_DEATH_TEST_CHECK_(
+ static_cast<size_t>(stack_size) > kMaxStackAlignment &&
+ reinterpret_cast<uintptr_t>(stack_top) % kMaxStackAlignment == 0);
child_pid = clone(&ExecDeathTestChildMain, stack_top, SIGCHLD, &args);
@@ -1104,7 +1399,7 @@ static pid_t ExecDeathTestSpawnChild(char* const* argv, int close_fd) {
# endif // GTEST_OS_QNX
# if GTEST_OS_LINUX
GTEST_DEATH_TEST_CHECK_SYSCALL_(
- sigaction(SIGPROF, &saved_sigprof_action, NULL));
+ sigaction(SIGPROF, &saved_sigprof_action, nullptr));
# endif // GTEST_OS_LINUX
GTEST_DEATH_TEST_CHECK_(child_pid != -1);
@@ -1122,7 +1417,7 @@ DeathTest::TestRole ExecDeathTest::AssumeRole() {
const TestInfo* const info = impl->current_test_info();
const int death_test_index = info->result()->death_test_count();
- if (flag != NULL) {
+ if (flag != nullptr) {
set_write_fd(flag->write_fd());
return EXECUTE_TEST;
}
@@ -1133,9 +1428,9 @@ DeathTest::TestRole ExecDeathTest::AssumeRole() {
// it be closed when the child process does an exec:
GTEST_DEATH_TEST_CHECK_(fcntl(pipe_fd[1], F_SETFD, 0) != -1);
- const std::string filter_flag =
- std::string("--") + GTEST_FLAG_PREFIX_ + kFilterFlag + "="
- + info->test_case_name() + "." + info->name();
+ const std::string filter_flag = std::string("--") + GTEST_FLAG_PREFIX_ +
+ kFilterFlag + "=" + info->test_suite_name() +
+ "." + info->name();
const std::string internal_flag =
std::string("--") + GTEST_FLAG_PREFIX_ + kInternalRunDeathTestFlag + "="
+ file_ + "|" + StreamableToString(line_) + "|"
@@ -1168,7 +1463,8 @@ DeathTest::TestRole ExecDeathTest::AssumeRole() {
// by the "test" argument to its address. If the test should be
// skipped, sets that pointer to NULL. Returns true, unless the
// flag is set to an invalid value.
-bool DefaultDeathTestFactory::Create(const char* statement, const RE* regex,
+bool DefaultDeathTestFactory::Create(const char* statement,
+ Matcher<const std::string&> matcher,
const char* file, int line,
DeathTest** test) {
UnitTestImpl* const impl = GetUnitTestImpl();
@@ -1177,7 +1473,7 @@ bool DefaultDeathTestFactory::Create(const char* statement, const RE* regex,
const int death_test_index = impl->current_test_info()
->increment_death_test_count();
- if (flag != NULL) {
+ if (flag != nullptr) {
if (death_test_index > flag->index()) {
DeathTest::set_last_death_test_message(
"Death test count (" + StreamableToString(death_test_index)
@@ -1188,7 +1484,7 @@ bool DefaultDeathTestFactory::Create(const char* statement, const RE* regex,
if (!(flag->file() == file && flag->line() == line &&
flag->index() == death_test_index)) {
- *test = NULL;
+ *test = nullptr;
return true;
}
}
@@ -1197,15 +1493,22 @@ bool DefaultDeathTestFactory::Create(const char* statement, const RE* regex,
if (GTEST_FLAG(death_test_style) == "threadsafe" ||
GTEST_FLAG(death_test_style) == "fast") {
- *test = new WindowsDeathTest(statement, regex, file, line);
+ *test = new WindowsDeathTest(statement, std::move(matcher), file, line);
+ }
+
+# elif GTEST_OS_FUCHSIA
+
+ if (GTEST_FLAG(death_test_style) == "threadsafe" ||
+ GTEST_FLAG(death_test_style) == "fast") {
+ *test = new FuchsiaDeathTest(statement, std::move(matcher), file, line);
}
# else
if (GTEST_FLAG(death_test_style) == "threadsafe") {
- *test = new ExecDeathTest(statement, regex, file, line);
+ *test = new ExecDeathTest(statement, std::move(matcher), file, line);
} else if (GTEST_FLAG(death_test_style) == "fast") {
- *test = new NoExecDeathTest(statement, regex);
+ *test = new NoExecDeathTest(statement, std::move(matcher));
}
# endif // GTEST_OS_WINDOWS
@@ -1224,7 +1527,7 @@ bool DefaultDeathTestFactory::Create(const char* statement, const RE* regex,
// Recreates the pipe and event handles from the provided parameters,
// signals the event, and returns a file descriptor wrapped around the pipe
// handle. This function is called in the child process only.
-int GetStatusFileDescriptor(unsigned int parent_process_id,
+static int GetStatusFileDescriptor(unsigned int parent_process_id,
size_t write_handle_as_size_t,
size_t event_handle_as_size_t) {
AutoHandle parent_process_handle(::OpenProcess(PROCESS_DUP_HANDLE,
@@ -1235,15 +1538,13 @@ int GetStatusFileDescriptor(unsigned int parent_process_id,
StreamableToString(parent_process_id));
}
- // TODO(vladl@google.com): Replace the following check with a
- // compile-time assertion when available.
GTEST_CHECK_(sizeof(HANDLE) <= sizeof(size_t));
const HANDLE write_handle =
reinterpret_cast<HANDLE>(write_handle_as_size_t);
HANDLE dup_write_handle;
- // The newly initialized handle is accessible only in in the parent
+ // The newly initialized handle is accessible only in the parent
// process. To obtain one accessible within the child, we need to use
// DuplicateHandle.
if (!::DuplicateHandle(parent_process_handle.Get(), write_handle,
@@ -1292,7 +1593,7 @@ int GetStatusFileDescriptor(unsigned int parent_process_id,
// initialized from the GTEST_FLAG(internal_run_death_test) flag if
// the flag is specified; otherwise returns NULL.
InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag() {
- if (GTEST_FLAG(internal_run_death_test) == "") return NULL;
+ if (GTEST_FLAG(internal_run_death_test) == "") return nullptr;
// GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we
// can use it here.
@@ -1320,6 +1621,16 @@ InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag() {
write_fd = GetStatusFileDescriptor(parent_process_id,
write_handle_as_size_t,
event_handle_as_size_t);
+
+# elif GTEST_OS_FUCHSIA
+
+ if (fields.size() != 3
+ || !ParseNaturalNumber(fields[1], &line)
+ || !ParseNaturalNumber(fields[2], &index)) {
+ DeathTestAbort("Bad --gtest_internal_run_death_test flag: "
+ + GTEST_FLAG(internal_run_death_test));
+ }
+
# else
if (fields.size() != 4
diff --git a/extern/gtest/src/gtest-filepath.cc b/extern/gtest/src/gtest-filepath.cc
index 0292dc11957..bd7b99ff03e 100644
--- a/extern/gtest/src/gtest-filepath.cc
+++ b/extern/gtest/src/gtest-filepath.cc
@@ -26,28 +26,25 @@
// 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.
-//
-// Authors: keith.ray@gmail.com (Keith Ray)
-#include "gtest/gtest-message.h"
#include "gtest/internal/gtest-filepath.h"
-#include "gtest/internal/gtest-port.h"
#include <stdlib.h>
+#include "gtest/internal/gtest-port.h"
+#include "gtest/gtest-message.h"
#if GTEST_OS_WINDOWS_MOBILE
# include <windows.h>
#elif GTEST_OS_WINDOWS
# include <direct.h>
# include <io.h>
-#elif GTEST_OS_SYMBIAN
-// Symbian OpenC has PATH_MAX in sys/syslimits.h
-# include <sys/syslimits.h>
#else
# include <limits.h>
# include <climits> // Some Linux distributions define PATH_MAX here.
#endif // GTEST_OS_WINDOWS_MOBILE
+#include "gtest/internal/gtest-string.h"
+
#if GTEST_OS_WINDOWS
# define GTEST_PATH_MAX_ _MAX_PATH
#elif defined(PATH_MAX)
@@ -58,8 +55,6 @@
# define GTEST_PATH_MAX_ _POSIX_PATH_MAX
#endif // GTEST_OS_WINDOWS
-#include "gtest/internal/gtest-string.h"
-
namespace testing {
namespace internal {
@@ -97,13 +92,14 @@ static bool IsPathSeparator(char c) {
// Returns the current working directory, or "" if unsuccessful.
FilePath FilePath::GetCurrentDir() {
-#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_PHONE || GTEST_OS_WINDOWS_RT
- // Windows CE doesn't have a current directory, so we just return
+#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_PHONE || \
+ GTEST_OS_WINDOWS_RT || ARDUINO || defined(ESP_PLATFORM)
+ // These platforms do not have a current directory, so we just return
// something reasonable.
return FilePath(kCurrentDirectoryString);
#elif GTEST_OS_WINDOWS
char cwd[GTEST_PATH_MAX_ + 1] = { '\0' };
- return FilePath(_getcwd(cwd, sizeof(cwd)) == NULL ? "" : cwd);
+ return FilePath(_getcwd(cwd, sizeof(cwd)) == nullptr ? "" : cwd);
#else
char cwd[GTEST_PATH_MAX_ + 1] = { '\0' };
char* result = getcwd(cwd, sizeof(cwd));
@@ -111,9 +107,9 @@ FilePath FilePath::GetCurrentDir() {
// getcwd will likely fail in NaCl due to the sandbox, so return something
// reasonable. The user may have provided a shim implementation for getcwd,
// however, so fallback only when failure is detected.
- return FilePath(result == NULL ? kCurrentDirectoryString : cwd);
+ return FilePath(result == nullptr ? kCurrentDirectoryString : cwd);
# endif // GTEST_OS_NACL
- return FilePath(result == NULL ? "" : cwd);
+ return FilePath(result == nullptr ? "" : cwd);
#endif // GTEST_OS_WINDOWS_MOBILE
}
@@ -130,7 +126,7 @@ FilePath FilePath::RemoveExtension(const char* extension) const {
return *this;
}
-// Returns a pointer to the last occurence of a valid path separator in
+// Returns a pointer to the last occurrence of a valid path separator in
// the FilePath. On Windows, for example, both '/' and '\' are valid path
// separators. Returns NULL if no path separator was found.
const char* FilePath::FindLastPathSeparator() const {
@@ -138,8 +134,8 @@ const char* FilePath::FindLastPathSeparator() const {
#if GTEST_HAS_ALT_PATH_SEP_
const char* const last_alt_sep = strrchr(c_str(), kAlternatePathSeparator);
// Comparing two pointers of which only one is NULL is undefined.
- if (last_alt_sep != NULL &&
- (last_sep == NULL || last_alt_sep > last_sep)) {
+ if (last_alt_sep != nullptr &&
+ (last_sep == nullptr || last_alt_sep > last_sep)) {
return last_alt_sep;
}
#endif
@@ -167,7 +163,7 @@ FilePath FilePath::RemoveFileName() const {
const char* const last_sep = FindLastPathSeparator();
std::string dir;
if (last_sep) {
- dir = std::string(c_str(), last_sep + 1 - c_str());
+ dir = std::string(c_str(), static_cast<size_t>(last_sep + 1 - c_str()));
} else {
dir = kCurrentDirectoryString;
}
@@ -252,9 +248,6 @@ bool FilePath::DirectoryExists() const {
// root directory per disk drive.)
bool FilePath::IsRootDirectory() const {
#if GTEST_OS_WINDOWS
- // TODO(wan@google.com): on Windows a network share like
- // \\server\share can be a root directory, although it cannot be the
- // current directory. Handle this properly.
return pathname_.length() == 3 && IsAbsolutePath();
#else
return pathname_.length() == 1 && IsPathSeparator(pathname_.c_str()[0]);
@@ -326,7 +319,7 @@ bool FilePath::CreateFolder() const {
#if GTEST_OS_WINDOWS_MOBILE
FilePath removed_sep(this->RemoveTrailingPathSeparator());
LPCWSTR unicode = String::AnsiToUtf16(removed_sep.c_str());
- int result = CreateDirectory(unicode, NULL) ? 0 : -1;
+ int result = CreateDirectory(unicode, nullptr) ? 0 : -1;
delete [] unicode;
#elif GTEST_OS_WINDOWS
int result = _mkdir(pathname_.c_str());
@@ -352,9 +345,8 @@ FilePath FilePath::RemoveTrailingPathSeparator() const {
// Removes any redundant separators that might be in the pathname.
// For example, "bar///foo" becomes "bar/foo". Does not eliminate other
// redundancies that might be in a pathname involving "." or "..".
-// TODO(wan@google.com): handle Windows network shares (e.g. \\server\share).
void FilePath::Normalize() {
- if (pathname_.c_str() == NULL) {
+ if (pathname_.c_str() == nullptr) {
pathname_ = "";
return;
}
diff --git a/extern/gtest/src/gtest-internal-inl.h b/extern/gtest/src/gtest-internal-inl.h
index ed8a682a964..8ed70daab09 100644
--- a/extern/gtest/src/gtest-internal-inl.h
+++ b/extern/gtest/src/gtest-internal-inl.h
@@ -27,24 +27,13 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-// Utility functions and classes used by the Google C++ testing framework.
-//
-// Author: wan@google.com (Zhanyong Wan)
-//
+// Utility functions and classes used by the Google C++ testing framework.//
// This file contains purely Google Test's internal implementation. Please
// DO NOT #INCLUDE IT IN A USER PROGRAM.
#ifndef GTEST_SRC_GTEST_INTERNAL_INL_H_
#define GTEST_SRC_GTEST_INTERNAL_INL_H_
-// GTEST_IMPLEMENTATION_ is defined to 1 iff the current translation unit is
-// part of Google Test's implementation; otherwise it's undefined.
-#if !GTEST_IMPLEMENTATION_
-// If this file is included from the user's code, just say no.
-# error "gtest-internal-inl.h is part of Google Test's internal implementation."
-# error "It must not be included except by Google Test itself."
-#endif // GTEST_IMPLEMENTATION_
-
#ifndef _WIN32_WCE
# include <errno.h>
#endif // !_WIN32_WCE
@@ -53,6 +42,7 @@
#include <string.h> // For memmove.
#include <algorithm>
+#include <memory>
#include <string>
#include <vector>
@@ -67,9 +57,12 @@
# include <windows.h> // NOLINT
#endif // GTEST_OS_WINDOWS
-#include "gtest/gtest.h" // NOLINT
+#include "gtest/gtest.h"
#include "gtest/gtest-spi.h"
+GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
+/* class A needs to have dll-interface to be used by clients of class B */)
+
namespace testing {
// Declares the flags.
@@ -94,6 +87,7 @@ const char kFilterFlag[] = "filter";
const char kListTestsFlag[] = "list_tests";
const char kOutputFlag[] = "output";
const char kPrintTimeFlag[] = "print_time";
+const char kPrintUTF8Flag[] = "print_utf8";
const char kRandomSeedFlag[] = "random_seed";
const char kRepeatFlag[] = "repeat";
const char kShuffleFlag[] = "shuffle";
@@ -105,14 +99,14 @@ const char kFlagfileFlag[] = "flagfile";
// A valid random seed must be in [1, kMaxRandomSeed].
const int kMaxRandomSeed = 99999;
-// g_help_flag is true iff the --help flag or an equivalent form is
-// specified on the command line.
+// g_help_flag is true if and only if the --help flag or an equivalent form
+// is specified on the command line.
GTEST_API_ extern bool g_help_flag;
// Returns the current time in milliseconds.
GTEST_API_ TimeInMillis GetTimeInMillis();
-// Returns true iff Google Test should use colors in the output.
+// Returns true if and only if Google Test should use colors in the output.
GTEST_API_ bool ShouldUseColor(bool stdout_is_tty);
// Formats the given time in milliseconds as seconds.
@@ -174,6 +168,7 @@ class GTestFlagSaver {
list_tests_ = GTEST_FLAG(list_tests);
output_ = GTEST_FLAG(output);
print_time_ = GTEST_FLAG(print_time);
+ print_utf8_ = GTEST_FLAG(print_utf8);
random_seed_ = GTEST_FLAG(random_seed);
repeat_ = GTEST_FLAG(repeat);
shuffle_ = GTEST_FLAG(shuffle);
@@ -195,6 +190,7 @@ class GTestFlagSaver {
GTEST_FLAG(list_tests) = list_tests_;
GTEST_FLAG(output) = output_;
GTEST_FLAG(print_time) = print_time_;
+ GTEST_FLAG(print_utf8) = print_utf8_;
GTEST_FLAG(random_seed) = random_seed_;
GTEST_FLAG(repeat) = repeat_;
GTEST_FLAG(shuffle) = shuffle_;
@@ -216,6 +212,7 @@ class GTestFlagSaver {
bool list_tests_;
std::string output_;
bool print_time_;
+ bool print_utf8_;
internal::Int32 random_seed_;
internal::Int32 repeat_;
bool shuffle_;
@@ -234,7 +231,7 @@ GTEST_API_ std::string CodePointToUtf8(UInt32 code_point);
// Converts a wide string to a narrow string in UTF-8 encoding.
// The wide string is assumed to have the following encoding:
-// UTF-16 if sizeof(wchar_t) == 2 (on Windows, Cygwin, Symbian OS)
+// UTF-16 if sizeof(wchar_t) == 2 (on Windows, Cygwin)
// UTF-32 if sizeof(wchar_t) == 4 (on Linux)
// Parameter str points to a null-terminated wide string.
// Parameter num_chars may additionally limit the number
@@ -269,8 +266,8 @@ GTEST_API_ bool ShouldShard(const char* total_shards_str,
GTEST_API_ Int32 Int32FromEnvOrDie(const char* env_var, Int32 default_val);
// Given the total number of shards, the shard index, and the test id,
-// returns true iff the test should be run on this shard. The test id is
-// some arbitrary but unique non-negative integer assigned to each test
+// returns true if and only if the test should be run on this shard. The test id
+// is some arbitrary but unique non-negative integer assigned to each test
// method. Assumes that 0 <= shard_index < total_shards.
GTEST_API_ bool ShouldRunTestOnShard(
int total_shards, int shard_index, int test_id);
@@ -301,7 +298,8 @@ void ForEach(const Container& c, Functor functor) {
// in range [0, v.size()).
template <typename E>
inline E GetElementOr(const std::vector<E>& v, int i, E default_value) {
- return (i < 0 || i >= static_cast<int>(v.size())) ? default_value : v[i];
+ return (i < 0 || i >= static_cast<int>(v.size())) ? default_value
+ : v[static_cast<size_t>(i)];
}
// Performs an in-place shuffle of a range of the vector's elements.
@@ -323,8 +321,11 @@ void ShuffleRange(internal::Random* random, int begin, int end,
// http://en.wikipedia.org/wiki/Fisher-Yates_shuffle
for (int range_width = end - begin; range_width >= 2; range_width--) {
const int last_in_range = begin + range_width - 1;
- const int selected = begin + random->Generate(range_width);
- std::swap((*v)[selected], (*v)[last_in_range]);
+ const int selected =
+ begin +
+ static_cast<int>(random->Generate(static_cast<UInt32>(range_width)));
+ std::swap((*v)[static_cast<size_t>(selected)],
+ (*v)[static_cast<size_t>(last_in_range)]);
}
}
@@ -351,7 +352,7 @@ class TestPropertyKeyIs {
// TestPropertyKeyIs has NO default constructor.
explicit TestPropertyKeyIs(const std::string& key) : key_(key) {}
- // Returns true iff the test name of test property matches on key_.
+ // Returns true if and only if the test name of test property matches on key_.
bool operator()(const TestProperty& test_property) const {
return test_property.key() == key_;
}
@@ -384,17 +385,17 @@ class GTEST_API_ UnitTestOptions {
// Functions for processing the gtest_filter flag.
- // Returns true iff the wildcard pattern matches the string. The
- // first ':' or '\0' character in pattern marks the end of it.
+ // Returns true if and only if the wildcard pattern matches the string.
+ // The first ':' or '\0' character in pattern marks the end of it.
//
// This recursive algorithm isn't very efficient, but is clear and
// works well enough for matching test names, which are short.
static bool PatternMatchesString(const char *pattern, const char *str);
- // Returns true iff the user-specified filter matches the test case
- // name and the test name.
- static bool FilterMatchesTest(const std::string &test_case_name,
- const std::string &test_name);
+ // Returns true if and only if the user-specified filter matches the test
+ // suite name and the test name.
+ static bool FilterMatchesTest(const std::string& test_suite_name,
+ const std::string& test_name);
#if GTEST_OS_WINDOWS
// Function for supporting the gtest_catch_exception flag.
@@ -426,7 +427,7 @@ class OsStackTraceGetterInterface {
// in the trace.
// skip_count - the number of top frames to be skipped; doesn't count
// against max_depth.
- virtual string CurrentStackTrace(int max_depth, int skip_count) = 0;
+ virtual std::string CurrentStackTrace(int max_depth, int skip_count) = 0;
// UponLeavingGTest() should be called immediately before Google Test calls
// user code. It saves some information about the current stack that
@@ -446,10 +447,20 @@ class OsStackTraceGetter : public OsStackTraceGetterInterface {
public:
OsStackTraceGetter() {}
- virtual string CurrentStackTrace(int max_depth, int skip_count);
- virtual void UponLeavingGTest();
+ std::string CurrentStackTrace(int max_depth, int skip_count) override;
+ void UponLeavingGTest() override;
private:
+#if GTEST_HAS_ABSL
+ Mutex mutex_; // Protects all internal state.
+
+ // We save the stack frame below the frame that calls user code.
+ // We do this because the address of the frame immediately below
+ // the user code changes between the call to UponLeavingGTest()
+ // and any calls to the stack trace code from within the user code.
+ void* caller_frame_ = nullptr;
+#endif // GTEST_HAS_ABSL
+
GTEST_DISALLOW_COPY_AND_ASSIGN_(OsStackTraceGetter);
};
@@ -468,7 +479,7 @@ class DefaultGlobalTestPartResultReporter
explicit DefaultGlobalTestPartResultReporter(UnitTestImpl* unit_test);
// Implements the TestPartResultReporterInterface. Reports the test part
// result in the current test.
- virtual void ReportTestPartResult(const TestPartResult& result);
+ void ReportTestPartResult(const TestPartResult& result) override;
private:
UnitTestImpl* const unit_test_;
@@ -484,7 +495,7 @@ class DefaultPerThreadTestPartResultReporter
explicit DefaultPerThreadTestPartResultReporter(UnitTestImpl* unit_test);
// Implements the TestPartResultReporterInterface. The implementation just
// delegates to the current global test part result reporter of *unit_test_.
- virtual void ReportTestPartResult(const TestPartResult& result);
+ void ReportTestPartResult(const TestPartResult& result) override;
private:
UnitTestImpl* const unit_test_;
@@ -522,22 +533,25 @@ class GTEST_API_ UnitTestImpl {
void SetTestPartResultReporterForCurrentThread(
TestPartResultReporterInterface* reporter);
- // Gets the number of successful test cases.
- int successful_test_case_count() const;
+ // Gets the number of successful test suites.
+ int successful_test_suite_count() const;
- // Gets the number of failed test cases.
- int failed_test_case_count() const;
+ // Gets the number of failed test suites.
+ int failed_test_suite_count() const;
- // Gets the number of all test cases.
- int total_test_case_count() const;
+ // Gets the number of all test suites.
+ int total_test_suite_count() const;
- // Gets the number of all test cases that contain at least one test
+ // Gets the number of all test suites that contain at least one test
// that should run.
- int test_case_to_run_count() const;
+ int test_suite_to_run_count() const;
// Gets the number of successful tests.
int successful_test_count() const;
+ // Gets the number of skipped tests.
+ int skipped_test_count() const;
+
// Gets the number of failed tests.
int failed_test_count() const;
@@ -563,27 +577,33 @@ class GTEST_API_ UnitTestImpl {
// Gets the elapsed time, in milliseconds.
TimeInMillis elapsed_time() const { return elapsed_time_; }
- // Returns true iff the unit test passed (i.e. all test cases passed).
+ // Returns true if and only if the unit test passed (i.e. all test suites
+ // passed).
bool Passed() const { return !Failed(); }
- // Returns true iff the unit test failed (i.e. some test case failed
- // or something outside of all tests failed).
+ // Returns true if and only if the unit test failed (i.e. some test suite
+ // failed or something outside of all tests failed).
bool Failed() const {
- return failed_test_case_count() > 0 || ad_hoc_test_result()->Failed();
+ return failed_test_suite_count() > 0 || ad_hoc_test_result()->Failed();
}
- // Gets the i-th test case among all the test cases. i can range from 0 to
- // total_test_case_count() - 1. If i is not in that range, returns NULL.
- const TestCase* GetTestCase(int i) const {
- const int index = GetElementOr(test_case_indices_, i, -1);
- return index < 0 ? NULL : test_cases_[i];
+ // Gets the i-th test suite among all the test suites. i can range from 0 to
+ // total_test_suite_count() - 1. If i is not in that range, returns NULL.
+ const TestSuite* GetTestSuite(int i) const {
+ const int index = GetElementOr(test_suite_indices_, i, -1);
+ return index < 0 ? nullptr : test_suites_[static_cast<size_t>(i)];
}
- // Gets the i-th test case among all the test cases. i can range from 0 to
- // total_test_case_count() - 1. If i is not in that range, returns NULL.
- TestCase* GetMutableTestCase(int i) {
- const int index = GetElementOr(test_case_indices_, i, -1);
- return index < 0 ? NULL : test_cases_[index];
+ // Legacy API is deprecated but still available
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+ const TestCase* GetTestCase(int i) const { return GetTestSuite(i); }
+#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+
+ // Gets the i-th test suite among all the test suites. i can range from 0 to
+ // total_test_suite_count() - 1. If i is not in that range, returns NULL.
+ TestSuite* GetMutableSuiteCase(int i) {
+ const int index = GetElementOr(test_suite_indices_, i, -1);
+ return index < 0 ? nullptr : test_suites_[static_cast<size_t>(index)];
}
// Provides access to the event listener list.
@@ -620,30 +640,38 @@ class GTEST_API_ UnitTestImpl {
// trace but Bar() and CurrentOsStackTraceExceptTop() won't.
std::string CurrentOsStackTraceExceptTop(int skip_count) GTEST_NO_INLINE_;
- // Finds and returns a TestCase with the given name. If one doesn't
+ // Finds and returns a TestSuite with the given name. If one doesn't
// exist, creates one and returns it.
//
// Arguments:
//
- // test_case_name: name of the test case
+ // test_suite_name: name of the test suite
// type_param: the name of the test's type parameter, or NULL if
// this is not a typed or a type-parameterized test.
- // set_up_tc: pointer to the function that sets up the test case
- // tear_down_tc: pointer to the function that tears down the test case
- TestCase* GetTestCase(const char* test_case_name,
- const char* type_param,
- Test::SetUpTestCaseFunc set_up_tc,
- Test::TearDownTestCaseFunc tear_down_tc);
+ // set_up_tc: pointer to the function that sets up the test suite
+ // tear_down_tc: pointer to the function that tears down the test suite
+ TestSuite* GetTestSuite(const char* test_suite_name, const char* type_param,
+ internal::SetUpTestSuiteFunc set_up_tc,
+ internal::TearDownTestSuiteFunc tear_down_tc);
+
+// Legacy API is deprecated but still available
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+ TestCase* GetTestCase(const char* test_case_name, const char* type_param,
+ internal::SetUpTestSuiteFunc set_up_tc,
+ internal::TearDownTestSuiteFunc tear_down_tc) {
+ return GetTestSuite(test_case_name, type_param, set_up_tc, tear_down_tc);
+ }
+#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
// Adds a TestInfo to the unit test.
//
// Arguments:
//
- // set_up_tc: pointer to the function that sets up the test case
- // tear_down_tc: pointer to the function that tears down the test case
+ // set_up_tc: pointer to the function that sets up the test suite
+ // tear_down_tc: pointer to the function that tears down the test suite
// test_info: the TestInfo object
- void AddTestInfo(Test::SetUpTestCaseFunc set_up_tc,
- Test::TearDownTestCaseFunc tear_down_tc,
+ void AddTestInfo(internal::SetUpTestSuiteFunc set_up_tc,
+ internal::TearDownTestSuiteFunc tear_down_tc,
TestInfo* test_info) {
// In order to support thread-safe death tests, we need to
// remember the original working directory when the test program
@@ -658,23 +686,20 @@ class GTEST_API_ UnitTestImpl {
<< "Failed to get the current working directory.";
}
- GetTestCase(test_info->test_case_name(),
- test_info->type_param(),
- set_up_tc,
- tear_down_tc)->AddTestInfo(test_info);
+ GetTestSuite(test_info->test_suite_name(), test_info->type_param(),
+ set_up_tc, tear_down_tc)
+ ->AddTestInfo(test_info);
}
-#if GTEST_HAS_PARAM_TEST
- // Returns ParameterizedTestCaseRegistry object used to keep track of
+ // Returns ParameterizedTestSuiteRegistry object used to keep track of
// value-parameterized tests and instantiate and register them.
- internal::ParameterizedTestCaseRegistry& parameterized_test_registry() {
+ internal::ParameterizedTestSuiteRegistry& parameterized_test_registry() {
return parameterized_test_registry_;
}
-#endif // GTEST_HAS_PARAM_TEST
- // Sets the TestCase object for the test that's currently running.
- void set_current_test_case(TestCase* a_current_test_case) {
- current_test_case_ = a_current_test_case;
+ // Sets the TestSuite object for the test that's currently running.
+ void set_current_test_suite(TestSuite* a_current_test_suite) {
+ current_test_suite_ = a_current_test_suite;
}
// Sets the TestInfo object for the test that's currently running. If
@@ -685,7 +710,7 @@ class GTEST_API_ UnitTestImpl {
}
// Registers all parameterized tests defined using TEST_P and
- // INSTANTIATE_TEST_CASE_P, creating regular tests for each test/parameter
+ // INSTANTIATE_TEST_SUITE_P, creating regular tests for each test/parameter
// combination. This method can be called more then once; it has guards
// protecting from registering the tests more then once. If
// value-parameterized tests are disabled, RegisterParameterizedTests is
@@ -700,7 +725,7 @@ class GTEST_API_ UnitTestImpl {
// Clears the results of all tests, except the ad hoc tests.
void ClearNonAdHocTestResult() {
- ForEach(test_cases_, TestCase::ClearTestCaseResult);
+ ForEach(test_suites_, TestSuite::ClearTestSuiteResult);
}
// Clears the results of ad-hoc test assertions.
@@ -709,7 +734,7 @@ class GTEST_API_ UnitTestImpl {
}
// Adds a TestProperty to the current TestResult object when invoked in a
- // context of a test or a test case, or to the global property set. If the
+ // context of a test or a test suite, or to the global property set. If the
// result already contains a property with the same key, the value will be
// updated.
void RecordProperty(const TestProperty& test_property);
@@ -721,7 +746,7 @@ class GTEST_API_ UnitTestImpl {
// Matches the full name of each test against the user-specified
// filter to decide whether the test should run, then records the
- // result in each TestCase and TestInfo object.
+ // result in each TestSuite and TestInfo object.
// If shard_tests == HONOR_SHARDING_PROTOCOL, further filters tests
// based on sharding variables in the environment.
// Returns the number of tests that should run.
@@ -730,7 +755,7 @@ class GTEST_API_ UnitTestImpl {
// Prints the names of the tests matching the user-specified filter flag.
void ListTestsMatchingFilter();
- const TestCase* current_test_case() const { return current_test_case_; }
+ const TestSuite* current_test_suite() const { return current_test_suite_; }
TestInfo* current_test_info() { return current_test_info_; }
const TestInfo* current_test_info() const { return current_test_info_; }
@@ -791,11 +816,11 @@ class GTEST_API_ UnitTestImpl {
// Gets the random number generator.
internal::Random* random() { return &random_; }
- // Shuffles all test cases, and the tests within each test case,
+ // Shuffles all test suites, and the tests within each test suite,
// making sure that death tests are still run first.
void ShuffleTests();
- // Restores the test cases and tests to their order before the first shuffle.
+ // Restores the test suites and tests to their order before the first shuffle.
void UnshuffleTests();
// Returns the value of GTEST_FLAG(catch_exceptions) at the moment
@@ -835,33 +860,31 @@ class GTEST_API_ UnitTestImpl {
// before/after the tests are run.
std::vector<Environment*> environments_;
- // The vector of TestCases in their original order. It owns the
+ // The vector of TestSuites in their original order. It owns the
// elements in the vector.
- std::vector<TestCase*> test_cases_;
+ std::vector<TestSuite*> test_suites_;
- // Provides a level of indirection for the test case list to allow
- // easy shuffling and restoring the test case order. The i-th
- // element of this vector is the index of the i-th test case in the
+ // Provides a level of indirection for the test suite list to allow
+ // easy shuffling and restoring the test suite order. The i-th
+ // element of this vector is the index of the i-th test suite in the
// shuffled order.
- std::vector<int> test_case_indices_;
+ std::vector<int> test_suite_indices_;
-#if GTEST_HAS_PARAM_TEST
// ParameterizedTestRegistry object used to register value-parameterized
// tests.
- internal::ParameterizedTestCaseRegistry parameterized_test_registry_;
+ internal::ParameterizedTestSuiteRegistry parameterized_test_registry_;
// Indicates whether RegisterParameterizedTests() has been called already.
bool parameterized_tests_registered_;
-#endif // GTEST_HAS_PARAM_TEST
- // Index of the last death test case registered. Initially -1.
- int last_death_test_case_;
+ // Index of the last death test suite registered. Initially -1.
+ int last_death_test_suite_;
- // This points to the TestCase for the currently running test. It
- // changes as Google Test goes through one test case after another.
+ // This points to the TestSuite for the currently running test. It
+ // changes as Google Test goes through one test suite after another.
// When no test is running, this is set to NULL and Google Test
// stores assertion results in ad_hoc_test_result_. Initially NULL.
- TestCase* current_test_case_;
+ TestSuite* current_test_suite_;
// This points to the TestInfo for the currently running test. It
// changes as Google Test goes through one test after another. When
@@ -889,7 +912,7 @@ class GTEST_API_ UnitTestImpl {
// desired.
OsStackTraceGetterInterface* os_stack_trace_getter_;
- // True iff PostFlagParsingInit() has been called.
+ // True if and only if PostFlagParsingInit() has been called.
bool post_flag_parse_init_performed_;
// The random number seed used at the beginning of the test run.
@@ -908,8 +931,8 @@ class GTEST_API_ UnitTestImpl {
#if GTEST_HAS_DEATH_TEST
// The decomposed components of the gtest_internal_run_death_test flag,
// parsed when RUN_ALL_TESTS is called.
- internal::scoped_ptr<InternalRunDeathTestFlag> internal_run_death_test_flag_;
- internal::scoped_ptr<internal::DeathTestFactory> death_test_factory_;
+ std::unique_ptr<InternalRunDeathTestFlag> internal_run_death_test_flag_;
+ std::unique_ptr<internal::DeathTestFactory> death_test_factory_;
#endif // GTEST_HAS_DEATH_TEST
// A per-thread stack of traces created by the SCOPED_TRACE() macro.
@@ -992,8 +1015,6 @@ bool ParseNaturalNumber(const ::std::string& str, Integer* number) {
const bool parse_success = *end == '\0' && errno == 0;
- // TODO(vladl@google.com): Convert this to compile time assertion when it is
- // available.
GTEST_CHECK_(sizeof(Integer) <= sizeof(parsed));
const Integer result = static_cast<Integer>(parsed);
@@ -1032,7 +1053,7 @@ class TestResultAccessor {
#if GTEST_CAN_STREAM_RESULTS_
// Streams test results to the given port on the given host machine.
-class GTEST_API_ StreamingListener : public EmptyTestEventListener {
+class StreamingListener : public EmptyTestEventListener {
public:
// Abstract base class for writing strings to a socket.
class AbstractSocketWriter {
@@ -1040,37 +1061,35 @@ class GTEST_API_ StreamingListener : public EmptyTestEventListener {
virtual ~AbstractSocketWriter() {}
// Sends a string to the socket.
- virtual void Send(const string& message) = 0;
+ virtual void Send(const std::string& message) = 0;
// Closes the socket.
virtual void CloseConnection() {}
// Sends a string and a newline to the socket.
- void SendLn(const string& message) {
- Send(message + "\n");
- }
+ void SendLn(const std::string& message) { Send(message + "\n"); }
};
// Concrete class for actually writing strings to a socket.
class SocketWriter : public AbstractSocketWriter {
public:
- SocketWriter(const string& host, const string& port)
+ SocketWriter(const std::string& host, const std::string& port)
: sockfd_(-1), host_name_(host), port_num_(port) {
MakeConnection();
}
- virtual ~SocketWriter() {
+ ~SocketWriter() override {
if (sockfd_ != -1)
CloseConnection();
}
// Sends a string to the socket.
- virtual void Send(const string& message) {
+ void Send(const std::string& message) override {
GTEST_CHECK_(sockfd_ != -1)
<< "Send() can be called only when there is a connection.";
- const int len = static_cast<int>(message.length());
- if (write(sockfd_, message.c_str(), len) != len) {
+ const auto len = static_cast<size_t>(message.length());
+ if (write(sockfd_, message.c_str(), len) != static_cast<ssize_t>(len)) {
GTEST_LOG_(WARNING)
<< "stream_result_to: failed to stream to "
<< host_name_ << ":" << port_num_;
@@ -1082,7 +1101,7 @@ class GTEST_API_ StreamingListener : public EmptyTestEventListener {
void MakeConnection();
// Closes the socket.
- void CloseConnection() {
+ void CloseConnection() override {
GTEST_CHECK_(sockfd_ != -1)
<< "CloseConnection() can be called only when there is a connection.";
@@ -1091,26 +1110,28 @@ class GTEST_API_ StreamingListener : public EmptyTestEventListener {
}
int sockfd_; // socket file descriptor
- const string host_name_;
- const string port_num_;
+ const std::string host_name_;
+ const std::string port_num_;
GTEST_DISALLOW_COPY_AND_ASSIGN_(SocketWriter);
}; // class SocketWriter
// Escapes '=', '&', '%', and '\n' characters in str as "%xx".
- static string UrlEncode(const char* str);
+ static std::string UrlEncode(const char* str);
- StreamingListener(const string& host, const string& port)
- : socket_writer_(new SocketWriter(host, port)) { Start(); }
+ StreamingListener(const std::string& host, const std::string& port)
+ : socket_writer_(new SocketWriter(host, port)) {
+ Start();
+ }
explicit StreamingListener(AbstractSocketWriter* socket_writer)
: socket_writer_(socket_writer) { Start(); }
- void OnTestProgramStart(const UnitTest& /* unit_test */) {
+ void OnTestProgramStart(const UnitTest& /* unit_test */) override {
SendLn("event=TestProgramStart");
}
- void OnTestProgramEnd(const UnitTest& unit_test) {
+ void OnTestProgramEnd(const UnitTest& unit_test) override {
// Note that Google Test current only report elapsed time for each
// test iteration, not for the entire test program.
SendLn("event=TestProgramEnd&passed=" + FormatBool(unit_test.Passed()));
@@ -1119,42 +1140,47 @@ class GTEST_API_ StreamingListener : public EmptyTestEventListener {
socket_writer_->CloseConnection();
}
- void OnTestIterationStart(const UnitTest& /* unit_test */, int iteration) {
+ void OnTestIterationStart(const UnitTest& /* unit_test */,
+ int iteration) override {
SendLn("event=TestIterationStart&iteration=" +
StreamableToString(iteration));
}
- void OnTestIterationEnd(const UnitTest& unit_test, int /* iteration */) {
+ void OnTestIterationEnd(const UnitTest& unit_test,
+ int /* iteration */) override {
SendLn("event=TestIterationEnd&passed=" +
FormatBool(unit_test.Passed()) + "&elapsed_time=" +
StreamableToString(unit_test.elapsed_time()) + "ms");
}
- void OnTestCaseStart(const TestCase& test_case) {
+ // Note that "event=TestCaseStart" is a wire format and has to remain
+ // "case" for compatibilty
+ void OnTestCaseStart(const TestCase& test_case) override {
SendLn(std::string("event=TestCaseStart&name=") + test_case.name());
}
- void OnTestCaseEnd(const TestCase& test_case) {
- SendLn("event=TestCaseEnd&passed=" + FormatBool(test_case.Passed())
- + "&elapsed_time=" + StreamableToString(test_case.elapsed_time())
- + "ms");
+ // Note that "event=TestCaseEnd" is a wire format and has to remain
+ // "case" for compatibilty
+ void OnTestCaseEnd(const TestCase& test_case) override {
+ SendLn("event=TestCaseEnd&passed=" + FormatBool(test_case.Passed()) +
+ "&elapsed_time=" + StreamableToString(test_case.elapsed_time()) +
+ "ms");
}
- void OnTestStart(const TestInfo& test_info) {
+ void OnTestStart(const TestInfo& test_info) override {
SendLn(std::string("event=TestStart&name=") + test_info.name());
}
- void OnTestEnd(const TestInfo& test_info) {
+ void OnTestEnd(const TestInfo& test_info) override {
SendLn("event=TestEnd&passed=" +
FormatBool((test_info.result())->Passed()) +
"&elapsed_time=" +
StreamableToString((test_info.result())->elapsed_time()) + "ms");
}
- void OnTestPartResult(const TestPartResult& test_part_result) {
+ void OnTestPartResult(const TestPartResult& test_part_result) override {
const char* file_name = test_part_result.file_name();
- if (file_name == NULL)
- file_name = "";
+ if (file_name == nullptr) file_name = "";
SendLn("event=TestPartResult&file=" + UrlEncode(file_name) +
"&line=" + StreamableToString(test_part_result.line_number()) +
"&message=" + UrlEncode(test_part_result.message()));
@@ -1162,15 +1188,15 @@ class GTEST_API_ StreamingListener : public EmptyTestEventListener {
private:
// Sends the given message and a newline to the socket.
- void SendLn(const string& message) { socket_writer_->SendLn(message); }
+ void SendLn(const std::string& message) { socket_writer_->SendLn(message); }
// Called at the start of streaming to notify the receiver what
// protocol we are using.
void Start() { SendLn("gtest_streaming_protocol_version=1.0"); }
- string FormatBool(bool value) { return value ? "1" : "0"; }
+ std::string FormatBool(bool value) { return value ? "1" : "0"; }
- const scoped_ptr<AbstractSocketWriter> socket_writer_;
+ const std::unique_ptr<AbstractSocketWriter> socket_writer_;
GTEST_DISALLOW_COPY_AND_ASSIGN_(StreamingListener);
}; // class StreamingListener
@@ -1180,4 +1206,6 @@ class GTEST_API_ StreamingListener : public EmptyTestEventListener {
} // namespace internal
} // namespace testing
+GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251
+
#endif // GTEST_SRC_GTEST_INTERNAL_INL_H_
diff --git a/extern/gtest/src/gtest-matchers.cc b/extern/gtest/src/gtest-matchers.cc
new file mode 100644
index 00000000000..7d2fb6851ec
--- /dev/null
+++ b/extern/gtest/src/gtest-matchers.cc
@@ -0,0 +1,97 @@
+// Copyright 2007, Google Inc.
+// All rights reserved.
+//
+// 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.
+
+// The Google C++ Testing and Mocking Framework (Google Test)
+//
+// This file implements just enough of the matcher interface to allow
+// EXPECT_DEATH and friends to accept a matcher argument.
+
+#include "gtest/internal/gtest-internal.h"
+#include "gtest/internal/gtest-port.h"
+#include "gtest/gtest-matchers.h"
+
+#include <string>
+
+namespace testing {
+
+// Constructs a matcher that matches a const std::string& whose value is
+// equal to s.
+Matcher<const std::string&>::Matcher(const std::string& s) { *this = Eq(s); }
+
+// Constructs a matcher that matches a const std::string& whose value is
+// equal to s.
+Matcher<const std::string&>::Matcher(const char* s) {
+ *this = Eq(std::string(s));
+}
+
+// Constructs a matcher that matches a std::string whose value is equal to
+// s.
+Matcher<std::string>::Matcher(const std::string& s) { *this = Eq(s); }
+
+// Constructs a matcher that matches a std::string whose value is equal to
+// s.
+Matcher<std::string>::Matcher(const char* s) { *this = Eq(std::string(s)); }
+
+#if GTEST_HAS_ABSL
+// Constructs a matcher that matches a const absl::string_view& whose value is
+// equal to s.
+Matcher<const absl::string_view&>::Matcher(const std::string& s) {
+ *this = Eq(s);
+}
+
+// Constructs a matcher that matches a const absl::string_view& whose value is
+// equal to s.
+Matcher<const absl::string_view&>::Matcher(const char* s) {
+ *this = Eq(std::string(s));
+}
+
+// Constructs a matcher that matches a const absl::string_view& whose value is
+// equal to s.
+Matcher<const absl::string_view&>::Matcher(absl::string_view s) {
+ *this = Eq(std::string(s));
+}
+
+// Constructs a matcher that matches a absl::string_view whose value is equal to
+// s.
+Matcher<absl::string_view>::Matcher(const std::string& s) { *this = Eq(s); }
+
+// Constructs a matcher that matches a absl::string_view whose value is equal to
+// s.
+Matcher<absl::string_view>::Matcher(const char* s) {
+ *this = Eq(std::string(s));
+}
+
+// Constructs a matcher that matches a absl::string_view whose value is equal to
+// s.
+Matcher<absl::string_view>::Matcher(absl::string_view s) {
+ *this = Eq(std::string(s));
+}
+#endif // GTEST_HAS_ABSL
+
+} // namespace testing
diff --git a/extern/gtest/src/gtest-port.cc b/extern/gtest/src/gtest-port.cc
index e5bf3dd2be4..fc5ba6becc5 100644
--- a/extern/gtest/src/gtest-port.cc
+++ b/extern/gtest/src/gtest-port.cc
@@ -26,22 +26,25 @@
// 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: wan@google.com (Zhanyong Wan)
+
#include "gtest/internal/gtest-port.h"
#include <limits.h>
-#include <stdlib.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include <fstream>
+#include <memory>
#if GTEST_OS_WINDOWS
# include <windows.h>
# include <io.h>
# include <sys/stat.h>
# include <map> // Used in ThreadLocal.
+# ifdef _MSC_VER
+# include <crtdbg.h>
+# endif // _MSC_VER
#else
# include <unistd.h>
#endif // GTEST_OS_WINDOWS
@@ -52,6 +55,14 @@
# include <mach/vm_map.h>
#endif // GTEST_OS_MAC
+#if GTEST_OS_DRAGONFLY || GTEST_OS_FREEBSD || GTEST_OS_GNU_KFREEBSD || \
+ GTEST_OS_NETBSD || GTEST_OS_OPENBSD
+# include <sys/sysctl.h>
+# if GTEST_OS_DRAGONFLY || GTEST_OS_FREEBSD || GTEST_OS_GNU_KFREEBSD
+# include <sys/user.h>
+# endif
+#endif
+
#if GTEST_OS_QNX
# include <devctl.h>
# include <fcntl.h>
@@ -63,19 +74,16 @@
# include <sys/types.h>
#endif // GTEST_OS_AIX
+#if GTEST_OS_FUCHSIA
+# include <zircon/process.h>
+# include <zircon/syscalls.h>
+#endif // GTEST_OS_FUCHSIA
+
#include "gtest/gtest-spi.h"
#include "gtest/gtest-message.h"
#include "gtest/internal/gtest-internal.h"
#include "gtest/internal/gtest-string.h"
-
-// Indicates that this translation unit is part of Google Test's
-// implementation. It must come before gtest-internal-inl.h is
-// included, or there will be a compiler error. This trick exists to
-// prevent the accidental inclusion of gtest-internal-inl.h in the
-// user's code.
-#define GTEST_IMPLEMENTATION_ 1
#include "src/gtest-internal-inl.h"
-#undef GTEST_IMPLEMENTATION_
namespace testing {
namespace internal {
@@ -93,7 +101,7 @@ const int kStdErrFileno = STDERR_FILENO;
namespace {
template <typename T>
-T ReadProcFileField(const string& filename, int field) {
+T ReadProcFileField(const std::string& filename, int field) {
std::string dummy;
std::ifstream file(filename.c_str());
while (field-- > 0) {
@@ -107,9 +115,9 @@ T ReadProcFileField(const string& filename, int field) {
// Returns the number of active threads, or 0 when there is an error.
size_t GetThreadCount() {
- const string filename =
+ const std::string filename =
(Message() << "/proc/" << getpid() << "/stat").GetString();
- return ReadProcFileField<int>(filename, 19);
+ return ReadProcFileField<size_t>(filename, 19);
}
#elif GTEST_OS_MAC
@@ -131,6 +139,81 @@ size_t GetThreadCount() {
}
}
+#elif GTEST_OS_DRAGONFLY || GTEST_OS_FREEBSD || GTEST_OS_GNU_KFREEBSD || \
+ GTEST_OS_NETBSD
+
+#if GTEST_OS_NETBSD
+#undef KERN_PROC
+#define KERN_PROC KERN_PROC2
+#define kinfo_proc kinfo_proc2
+#endif
+
+#if GTEST_OS_DRAGONFLY
+#define KP_NLWP(kp) (kp.kp_nthreads)
+#elif GTEST_OS_FREEBSD || GTEST_OS_GNU_KFREEBSD
+#define KP_NLWP(kp) (kp.ki_numthreads)
+#elif GTEST_OS_NETBSD
+#define KP_NLWP(kp) (kp.p_nlwps)
+#endif
+
+// Returns the number of threads running in the process, or 0 to indicate that
+// we cannot detect it.
+size_t GetThreadCount() {
+ int mib[] = {
+ CTL_KERN,
+ KERN_PROC,
+ KERN_PROC_PID,
+ getpid(),
+#if GTEST_OS_NETBSD
+ sizeof(struct kinfo_proc),
+ 1,
+#endif
+ };
+ u_int miblen = sizeof(mib) / sizeof(mib[0]);
+ struct kinfo_proc info;
+ size_t size = sizeof(info);
+ if (sysctl(mib, miblen, &info, &size, NULL, 0)) {
+ return 0;
+ }
+ return static_cast<size_t>(KP_NLWP(info));
+}
+#elif GTEST_OS_OPENBSD
+
+// Returns the number of threads running in the process, or 0 to indicate that
+// we cannot detect it.
+size_t GetThreadCount() {
+ int mib[] = {
+ CTL_KERN,
+ KERN_PROC,
+ KERN_PROC_PID | KERN_PROC_SHOW_THREADS,
+ getpid(),
+ sizeof(struct kinfo_proc),
+ 0,
+ };
+ u_int miblen = sizeof(mib) / sizeof(mib[0]);
+
+ // get number of structs
+ size_t size;
+ if (sysctl(mib, miblen, NULL, &size, NULL, 0)) {
+ return 0;
+ }
+ mib[5] = size / mib[4];
+
+ // populate array of structs
+ struct kinfo_proc info[mib[5]];
+ if (sysctl(mib, miblen, &info, &size, NULL, 0)) {
+ return 0;
+ }
+
+ // exclude empty members
+ int nthreads = 0;
+ for (int i = 0; i < size / mib[4]; i++) {
+ if (info[i].p_tid != -1)
+ nthreads++;
+ }
+ return nthreads;
+}
+
#elif GTEST_OS_QNX
// Returns the number of threads running in the process, or 0 to indicate that
@@ -142,7 +225,7 @@ size_t GetThreadCount() {
}
procfs_info process_info;
const int status =
- devctl(fd, DCMD_PROC_INFO, &process_info, sizeof(process_info), NULL);
+ devctl(fd, DCMD_PROC_INFO, &process_info, sizeof(process_info), nullptr);
close(fd);
if (status == EOK) {
return static_cast<size_t>(process_info.num_threads);
@@ -156,7 +239,7 @@ size_t GetThreadCount() {
size_t GetThreadCount() {
struct procentry64 entry;
pid_t pid = getpid();
- int status = getprocs64(&entry, sizeof(entry), NULL, 0, &pid, 1);
+ int status = getprocs64(&entry, sizeof(entry), nullptr, 0, &pid, 1);
if (status == 1) {
return entry.pi_thcount;
} else {
@@ -164,6 +247,25 @@ size_t GetThreadCount() {
}
}
+#elif GTEST_OS_FUCHSIA
+
+size_t GetThreadCount() {
+ int dummy_buffer;
+ size_t avail;
+ zx_status_t status = zx_object_get_info(
+ zx_process_self(),
+ ZX_INFO_PROCESS_THREADS,
+ &dummy_buffer,
+ 0,
+ nullptr,
+ &avail);
+ if (status == ZX_OK) {
+ return avail;
+ } else {
+ return 0;
+ }
+}
+
#else
size_t GetThreadCount() {
@@ -177,7 +279,7 @@ size_t GetThreadCount() {
#if GTEST_IS_THREADSAFE && GTEST_OS_WINDOWS
void SleepMilliseconds(int n) {
- ::Sleep(n);
+ ::Sleep(static_cast<DWORD>(n));
}
AutoHandle::AutoHandle()
@@ -215,15 +317,15 @@ void AutoHandle::Reset(HANDLE handle) {
bool AutoHandle::IsCloseable() const {
// Different Windows APIs may use either of these values to represent an
// invalid handle.
- return handle_ != NULL && handle_ != INVALID_HANDLE_VALUE;
+ return handle_ != nullptr && handle_ != INVALID_HANDLE_VALUE;
}
Notification::Notification()
- : event_(::CreateEvent(NULL, // Default security attributes.
- TRUE, // Do not reset automatically.
- FALSE, // Initially unset.
- NULL)) { // Anonymous event.
- GTEST_CHECK_(event_.Get() != NULL);
+ : event_(::CreateEvent(nullptr, // Default security attributes.
+ TRUE, // Do not reset automatically.
+ FALSE, // Initially unset.
+ nullptr)) { // Anonymous event.
+ GTEST_CHECK_(event_.Get() != nullptr);
}
void Notification::Notify() {
@@ -246,13 +348,10 @@ Mutex::Mutex()
Mutex::~Mutex() {
// Static mutexes are leaked intentionally. It is not thread-safe to try
// to clean them up.
- // TODO(yukawa): Switch to Slim Reader/Writer (SRW) Locks, which requires
- // nothing to clean it up but is available only on Vista and later.
- // http://msdn.microsoft.com/en-us/library/windows/desktop/aa904937.aspx
if (type_ == kDynamic) {
::DeleteCriticalSection(critical_section_);
delete critical_section_;
- critical_section_ = NULL;
+ critical_section_ = nullptr;
}
}
@@ -279,6 +378,41 @@ void Mutex::AssertHeld() {
<< "The current thread is not holding the mutex @" << this;
}
+namespace {
+
+#ifdef _MSC_VER
+// Use the RAII idiom to flag mem allocs that are intentionally never
+// deallocated. The motivation is to silence the false positive mem leaks
+// that are reported by the debug version of MS's CRT which can only detect
+// if an alloc is missing a matching deallocation.
+// Example:
+// MemoryIsNotDeallocated memory_is_not_deallocated;
+// critical_section_ = new CRITICAL_SECTION;
+//
+class MemoryIsNotDeallocated
+{
+ public:
+ MemoryIsNotDeallocated() : old_crtdbg_flag_(0) {
+ old_crtdbg_flag_ = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
+ // Set heap allocation block type to _IGNORE_BLOCK so that MS debug CRT
+ // doesn't report mem leak if there's no matching deallocation.
+ _CrtSetDbgFlag(old_crtdbg_flag_ & ~_CRTDBG_ALLOC_MEM_DF);
+ }
+
+ ~MemoryIsNotDeallocated() {
+ // Restore the original _CRTDBG_ALLOC_MEM_DF flag
+ _CrtSetDbgFlag(old_crtdbg_flag_);
+ }
+
+ private:
+ int old_crtdbg_flag_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(MemoryIsNotDeallocated);
+};
+#endif // _MSC_VER
+
+} // namespace
+
// Initializes owner_thread_id_ and critical_section_ in static mutexes.
void Mutex::ThreadSafeLazyInit() {
// Dynamic mutexes are initialized in the constructor.
@@ -289,7 +423,13 @@ void Mutex::ThreadSafeLazyInit() {
// If critical_section_init_phase_ was 0 before the exchange, we
// are the first to test it and need to perform the initialization.
owner_thread_id_ = 0;
- critical_section_ = new CRITICAL_SECTION;
+ {
+ // Use RAII to flag that following mem alloc is never deallocated.
+#ifdef _MSC_VER
+ MemoryIsNotDeallocated memory_is_not_deallocated;
+#endif // _MSC_VER
+ critical_section_ = new CRITICAL_SECTION;
+ }
::InitializeCriticalSection(critical_section_);
// Updates the critical_section_init_phase_ to 2 to signal
// initialization complete.
@@ -328,17 +468,16 @@ class ThreadWithParamSupport : public ThreadWithParamBase {
Notification* thread_can_start) {
ThreadMainParam* param = new ThreadMainParam(runnable, thread_can_start);
DWORD thread_id;
- // TODO(yukawa): Consider to use _beginthreadex instead.
HANDLE thread_handle = ::CreateThread(
- NULL, // Default security.
- 0, // Default stack size.
+ nullptr, // Default security.
+ 0, // Default stack size.
&ThreadWithParamSupport::ThreadMain,
- param, // Parameter to ThreadMainStatic
- 0x0, // Default creation flags.
+ param, // Parameter to ThreadMainStatic
+ 0x0, // Default creation flags.
&thread_id); // Need a valid pointer for the call to work under Win98.
- GTEST_CHECK_(thread_handle != NULL) << "CreateThread failed with error "
- << ::GetLastError() << ".";
- if (thread_handle == NULL) {
+ GTEST_CHECK_(thread_handle != nullptr)
+ << "CreateThread failed with error " << ::GetLastError() << ".";
+ if (thread_handle == nullptr) {
delete param;
}
return thread_handle;
@@ -350,15 +489,15 @@ class ThreadWithParamSupport : public ThreadWithParamBase {
: runnable_(runnable),
thread_can_start_(thread_can_start) {
}
- scoped_ptr<Runnable> runnable_;
+ std::unique_ptr<Runnable> runnable_;
// Does not own.
Notification* thread_can_start_;
};
static DWORD WINAPI ThreadMain(void* ptr) {
// Transfers ownership.
- scoped_ptr<ThreadMainParam> param(static_cast<ThreadMainParam*>(ptr));
- if (param->thread_can_start_ != NULL)
+ std::unique_ptr<ThreadMainParam> param(static_cast<ThreadMainParam*>(ptr));
+ if (param->thread_can_start_ != nullptr)
param->thread_can_start_->WaitForNotification();
param->runnable_->Run();
return 0;
@@ -416,7 +555,7 @@ class ThreadLocalRegistryImpl {
thread_local_values
.insert(std::make_pair(
thread_local_instance,
- linked_ptr<ThreadLocalValueHolderBase>(
+ std::shared_ptr<ThreadLocalValueHolderBase>(
thread_local_instance->NewValueForCurrentThread())))
.first;
}
@@ -425,7 +564,7 @@ class ThreadLocalRegistryImpl {
static void OnThreadLocalDestroyed(
const ThreadLocalBase* thread_local_instance) {
- std::vector<linked_ptr<ThreadLocalValueHolderBase> > value_holders;
+ std::vector<std::shared_ptr<ThreadLocalValueHolderBase> > value_holders;
// Clean up the ThreadLocalValues data structure while holding the lock, but
// defer the destruction of the ThreadLocalValueHolderBases.
{
@@ -453,7 +592,7 @@ class ThreadLocalRegistryImpl {
static void OnThreadExit(DWORD thread_id) {
GTEST_CHECK_(thread_id != 0) << ::GetLastError();
- std::vector<linked_ptr<ThreadLocalValueHolderBase> > value_holders;
+ std::vector<std::shared_ptr<ThreadLocalValueHolderBase> > value_holders;
// Clean up the ThreadIdToThreadLocals data structure while holding the
// lock, but defer the destruction of the ThreadLocalValueHolderBases.
{
@@ -480,7 +619,8 @@ class ThreadLocalRegistryImpl {
private:
// In a particular thread, maps a ThreadLocal object to its value.
typedef std::map<const ThreadLocalBase*,
- linked_ptr<ThreadLocalValueHolderBase> > ThreadLocalValues;
+ std::shared_ptr<ThreadLocalValueHolderBase> >
+ ThreadLocalValues;
// Stores all ThreadIdToThreadLocals having values in a thread, indexed by
// thread's ID.
typedef std::map<DWORD, ThreadLocalValues> ThreadIdToThreadLocals;
@@ -495,18 +635,17 @@ class ThreadLocalRegistryImpl {
HANDLE thread = ::OpenThread(SYNCHRONIZE | THREAD_QUERY_INFORMATION,
FALSE,
thread_id);
- GTEST_CHECK_(thread != NULL);
- // We need to to pass a valid thread ID pointer into CreateThread for it
+ GTEST_CHECK_(thread != nullptr);
+ // We need to pass a valid thread ID pointer into CreateThread for it
// to work correctly under Win98.
DWORD watcher_thread_id;
HANDLE watcher_thread = ::CreateThread(
- NULL, // Default security.
- 0, // Default stack size
+ nullptr, // Default security.
+ 0, // Default stack size
&ThreadLocalRegistryImpl::WatcherThreadFunc,
reinterpret_cast<LPVOID>(new ThreadIdAndHandle(thread_id, thread)),
- CREATE_SUSPENDED,
- &watcher_thread_id);
- GTEST_CHECK_(watcher_thread != NULL);
+ CREATE_SUSPENDED, &watcher_thread_id);
+ GTEST_CHECK_(watcher_thread != nullptr);
// Give the watcher thread the same priority as ours to avoid being
// blocked by it.
::SetThreadPriority(watcher_thread,
@@ -531,7 +670,10 @@ class ThreadLocalRegistryImpl {
// Returns map of thread local instances.
static ThreadIdToThreadLocals* GetThreadLocalsMapLocked() {
mutex_.AssertHeld();
- static ThreadIdToThreadLocals* map = new ThreadIdToThreadLocals;
+#ifdef _MSC_VER
+ MemoryIsNotDeallocated memory_is_not_deallocated;
+#endif // _MSC_VER
+ static ThreadIdToThreadLocals* map = new ThreadIdToThreadLocals();
return map;
}
@@ -573,7 +715,7 @@ RE::~RE() {
free(const_cast<char*>(pattern_));
}
-// Returns true iff regular expression re matches the entire str.
+// Returns true if and only if regular expression re matches the entire str.
bool RE::FullMatch(const char* str, const RE& re) {
if (!re.is_valid_) return false;
@@ -581,8 +723,8 @@ bool RE::FullMatch(const char* str, const RE& re) {
return regexec(&re.full_regex_, str, 1, &match, 0) == 0;
}
-// Returns true iff regular expression re matches a substring of str
-// (including str itself).
+// Returns true if and only if regular expression re matches a substring of
+// str (including str itself).
bool RE::PartialMatch(const char* str, const RE& re) {
if (!re.is_valid_) return false;
@@ -622,14 +764,14 @@ void RE::Init(const char* regex) {
#elif GTEST_USES_SIMPLE_RE
-// Returns true iff ch appears anywhere in str (excluding the
+// Returns true if and only if ch appears anywhere in str (excluding the
// terminating '\0' character).
bool IsInSet(char ch, const char* str) {
- return ch != '\0' && strchr(str, ch) != NULL;
+ return ch != '\0' && strchr(str, ch) != nullptr;
}
-// Returns true iff ch belongs to the given classification. Unlike
-// similar functions in <ctype.h>, these aren't affected by the
+// Returns true if and only if ch belongs to the given classification.
+// Unlike similar functions in <ctype.h>, these aren't affected by the
// current locale.
bool IsAsciiDigit(char ch) { return '0' <= ch && ch <= '9'; }
bool IsAsciiPunct(char ch) {
@@ -642,13 +784,13 @@ bool IsAsciiWordChar(char ch) {
('0' <= ch && ch <= '9') || ch == '_';
}
-// Returns true iff "\\c" is a supported escape sequence.
+// Returns true if and only if "\\c" is a supported escape sequence.
bool IsValidEscape(char c) {
return (IsAsciiPunct(c) || IsInSet(c, "dDfnrsStvwW"));
}
-// Returns true iff the given atom (specified by escaped and pattern)
-// matches ch. The result is undefined if the atom is invalid.
+// Returns true if and only if the given atom (specified by escaped and
+// pattern) matches ch. The result is undefined if the atom is invalid.
bool AtomMatchesChar(bool escaped, char pattern_char, char ch) {
if (escaped) { // "\\p" where p is pattern_char.
switch (pattern_char) {
@@ -671,7 +813,7 @@ bool AtomMatchesChar(bool escaped, char pattern_char, char ch) {
}
// Helper function used by ValidateRegex() to format error messages.
-std::string FormatRegexSyntaxError(const char* regex, int index) {
+static std::string FormatRegexSyntaxError(const char* regex, int index) {
return (Message() << "Syntax error at index " << index
<< " in simple regular expression \"" << regex << "\": ").GetString();
}
@@ -679,17 +821,14 @@ std::string FormatRegexSyntaxError(const char* regex, int index) {
// Generates non-fatal failures and returns false if regex is invalid;
// otherwise returns true.
bool ValidateRegex(const char* regex) {
- if (regex == NULL) {
- // TODO(wan@google.com): fix the source file location in the
- // assertion failures to match where the regex is used in user
- // code.
+ if (regex == nullptr) {
ADD_FAILURE() << "NULL is not a valid simple regular expression.";
return false;
}
bool is_valid = true;
- // True iff ?, *, or + can follow the previous atom.
+ // True if and only if ?, *, or + can follow the previous atom.
bool prev_repeatable = false;
for (int i = 0; regex[i]; i++) {
if (regex[i] == '\\') { // An escape sequence
@@ -765,8 +904,8 @@ bool MatchRepetitionAndRegexAtHead(
return false;
}
-// Returns true iff regex matches a prefix of str. regex must be a
-// valid simple regular expression and not start with "^", or the
+// Returns true if and only if regex matches a prefix of str. regex must
+// be a valid simple regular expression and not start with "^", or the
// result is undefined.
bool MatchRegexAtHead(const char* regex, const char* str) {
if (*regex == '\0') // An empty regex matches a prefix of anything.
@@ -796,8 +935,8 @@ bool MatchRegexAtHead(const char* regex, const char* str) {
}
}
-// Returns true iff regex matches any substring of str. regex must be
-// a valid simple regular expression, or the result is undefined.
+// Returns true if and only if regex matches any substring of str. regex must
+// be a valid simple regular expression, or the result is undefined.
//
// The algorithm is recursive, but the recursion depth doesn't exceed
// the regex length, so we won't need to worry about running out of
@@ -805,8 +944,7 @@ bool MatchRegexAtHead(const char* regex, const char* str) {
// exponential with respect to the regex length + the string length,
// but usually it's must faster (often close to linear).
bool MatchRegexAnywhere(const char* regex, const char* str) {
- if (regex == NULL || str == NULL)
- return false;
+ if (regex == nullptr || str == nullptr) return false;
if (*regex == '^')
return MatchRegexAtHead(regex + 1, str);
@@ -826,21 +964,21 @@ RE::~RE() {
free(const_cast<char*>(full_pattern_));
}
-// Returns true iff regular expression re matches the entire str.
+// Returns true if and only if regular expression re matches the entire str.
bool RE::FullMatch(const char* str, const RE& re) {
return re.is_valid_ && MatchRegexAnywhere(re.full_pattern_, str);
}
-// Returns true iff regular expression re matches a substring of str
-// (including str itself).
+// Returns true if and only if regular expression re matches a substring of
+// str (including str itself).
bool RE::PartialMatch(const char* str, const RE& re) {
return re.is_valid_ && MatchRegexAnywhere(re.pattern_, str);
}
// Initializes an RE from its string representation.
void RE::Init(const char* regex) {
- pattern_ = full_pattern_ = NULL;
- if (regex != NULL) {
+ pattern_ = full_pattern_ = nullptr;
+ if (regex != nullptr) {
pattern_ = posix::StrDup(regex);
}
@@ -878,7 +1016,7 @@ const char kUnknownFile[] = "unknown file";
// Formats a source file path and a line number as they would appear
// in an error message from the compiler used to compile this code.
GTEST_API_ ::std::string FormatFileLocation(const char* file, int line) {
- const std::string file_name(file == NULL ? kUnknownFile : file);
+ const std::string file_name(file == nullptr ? kUnknownFile : file);
if (line < 0) {
return file_name + ":";
@@ -897,7 +1035,7 @@ GTEST_API_ ::std::string FormatFileLocation(const char* file, int line) {
// to the file location it produces, unlike FormatFileLocation().
GTEST_API_ ::std::string FormatCompilerIndependentFileLocation(
const char* file, int line) {
- const std::string file_name(file == NULL ? kUnknownFile : file);
+ const std::string file_name(file == nullptr ? kUnknownFile : file);
if (line < 0)
return file_name;
@@ -923,9 +1061,10 @@ GTestLog::~GTestLog() {
posix::Abort();
}
}
+
// Disable Microsoft deprecation warnings for POSIX functions called from
// this class (creat, dup, dup2, and close)
-GTEST_DISABLE_MSC_WARNINGS_PUSH_(4996)
+GTEST_DISABLE_MSC_DEPRECATED_PUSH_()
#if GTEST_HAS_STREAM_REDIRECTION
@@ -963,20 +1102,22 @@ class CapturedStream {
// code as part of a regular standalone executable, which doesn't
// run in a Dalvik process (e.g. when running it through 'adb shell').
//
- // The location /sdcard is directly accessible from native code
- // and is the only location (unofficially) supported by the Android
- // team. It's generally a symlink to the real SD Card mount point
- // which can be /mnt/sdcard, /mnt/sdcard0, /system/media/sdcard, or
- // other OEM-customized locations. Never rely on these, and always
- // use /sdcard.
- char name_template[] = "/sdcard/gtest_captured_stream.XXXXXX";
+ // The location /data/local/tmp is directly accessible from native code.
+ // '/sdcard' and other variants cannot be relied on, as they are not
+ // guaranteed to be mounted, or may have a delay in mounting.
+ char name_template[] = "/data/local/tmp/gtest_captured_stream.XXXXXX";
# else
char name_template[] = "/tmp/captured_stream.XXXXXX";
# endif // GTEST_OS_LINUX_ANDROID
const int captured_fd = mkstemp(name_template);
+ if (captured_fd == -1) {
+ GTEST_LOG_(WARNING)
+ << "Failed to create tmp file " << name_template
+ << " for test; does the test have access to the /tmp directory?";
+ }
filename_ = name_template;
# endif // GTEST_OS_WINDOWS
- fflush(NULL);
+ fflush(nullptr);
dup2(captured_fd, fd_);
close(captured_fd);
}
@@ -988,13 +1129,17 @@ class CapturedStream {
std::string GetCapturedString() {
if (uncaptured_fd_ != -1) {
// Restores the original stream.
- fflush(NULL);
+ fflush(nullptr);
dup2(uncaptured_fd_, fd_);
close(uncaptured_fd_);
uncaptured_fd_ = -1;
}
FILE* const file = posix::FOpen(filename_.c_str(), "r");
+ if (file == nullptr) {
+ GTEST_LOG_(FATAL) << "Failed to open tmp file " << filename_
+ << " for capturing stream.";
+ }
const std::string content = ReadEntireFile(file);
posix::FClose(file);
return content;
@@ -1009,14 +1154,15 @@ class CapturedStream {
GTEST_DISALLOW_COPY_AND_ASSIGN_(CapturedStream);
};
-GTEST_DISABLE_MSC_WARNINGS_POP_()
+GTEST_DISABLE_MSC_DEPRECATED_POP_()
-static CapturedStream* g_captured_stderr = NULL;
-static CapturedStream* g_captured_stdout = NULL;
+static CapturedStream* g_captured_stderr = nullptr;
+static CapturedStream* g_captured_stdout = nullptr;
// Starts capturing an output stream (stdout/stderr).
-void CaptureStream(int fd, const char* stream_name, CapturedStream** stream) {
- if (*stream != NULL) {
+static void CaptureStream(int fd, const char* stream_name,
+ CapturedStream** stream) {
+ if (*stream != nullptr) {
GTEST_LOG_(FATAL) << "Only one " << stream_name
<< " capturer can exist at a time.";
}
@@ -1024,11 +1170,11 @@ void CaptureStream(int fd, const char* stream_name, CapturedStream** stream) {
}
// Stops capturing the output stream and returns the captured string.
-std::string GetCapturedStream(CapturedStream** captured_stream) {
+static std::string GetCapturedStream(CapturedStream** captured_stream) {
const std::string content = (*captured_stream)->GetCapturedString();
delete *captured_stream;
- *captured_stream = NULL;
+ *captured_stream = nullptr;
return content;
}
@@ -1055,23 +1201,9 @@ std::string GetCapturedStderr() {
#endif // GTEST_HAS_STREAM_REDIRECTION
-std::string TempDir() {
-#if GTEST_OS_WINDOWS_MOBILE
- return "\\temp\\";
-#elif GTEST_OS_WINDOWS
- const char* temp_dir = posix::GetEnv("TEMP");
- if (temp_dir == NULL || temp_dir[0] == '\0')
- return "\\temp\\";
- else if (temp_dir[strlen(temp_dir) - 1] == '\\')
- return temp_dir;
- else
- return std::string(temp_dir) + "\\";
-#elif GTEST_OS_LINUX_ANDROID
- return "/sdcard/";
-#else
- return "/tmp/";
-#endif // GTEST_OS_WINDOWS_MOBILE
-}
+
+
+
size_t GetFileSize(FILE* file) {
fseek(file, 0, SEEK_END);
@@ -1101,22 +1233,30 @@ std::string ReadEntireFile(FILE* file) {
}
#if GTEST_HAS_DEATH_TEST
+static const std::vector<std::string>* g_injected_test_argvs =
+ nullptr; // Owned.
-static const ::std::vector<testing::internal::string>* g_injected_test_argvs =
- NULL; // Owned.
-
-void SetInjectableArgvs(const ::std::vector<testing::internal::string>* argvs) {
- if (g_injected_test_argvs != argvs)
- delete g_injected_test_argvs;
- g_injected_test_argvs = argvs;
-}
-
-const ::std::vector<testing::internal::string>& GetInjectableArgvs() {
- if (g_injected_test_argvs != NULL) {
+std::vector<std::string> GetInjectableArgvs() {
+ if (g_injected_test_argvs != nullptr) {
return *g_injected_test_argvs;
}
return GetArgvs();
}
+
+void SetInjectableArgvs(const std::vector<std::string>* new_argvs) {
+ if (g_injected_test_argvs != new_argvs) delete g_injected_test_argvs;
+ g_injected_test_argvs = new_argvs;
+}
+
+void SetInjectableArgvs(const std::vector<std::string>& new_argvs) {
+ SetInjectableArgvs(
+ new std::vector<std::string>(new_argvs.begin(), new_argvs.end()));
+}
+
+void ClearInjectableArgvs() {
+ delete g_injected_test_argvs;
+ g_injected_test_argvs = nullptr;
+}
#endif // GTEST_HAS_DEATH_TEST
#if GTEST_OS_WINDOWS_MOBILE
@@ -1148,7 +1288,7 @@ static std::string FlagToEnvVar(const char* flag) {
// unchanged and returns false.
bool ParseInt32(const Message& src_text, const char* str, Int32* value) {
// Parses the environment variable as a decimal integer.
- char* end = NULL;
+ char* end = nullptr;
const long long_value = strtol(str, &end, 10); // NOLINT
// Has strtol() consumed all characters in the string?
@@ -1187,15 +1327,16 @@ bool ParseInt32(const Message& src_text, const char* str, Int32* value) {
// Reads and returns the Boolean environment variable corresponding to
// the given flag; if it's not set, returns default_value.
//
-// The value is considered true iff it's not "0".
+// The value is considered true if and only if it's not "0".
bool BoolFromGTestEnv(const char* flag, bool default_value) {
#if defined(GTEST_GET_BOOL_FROM_ENV_)
return GTEST_GET_BOOL_FROM_ENV_(flag, default_value);
-#endif // defined(GTEST_GET_BOOL_FROM_ENV_)
+#else
const std::string env_var = FlagToEnvVar(flag);
const char* const string_value = posix::GetEnv(env_var.c_str());
- return string_value == NULL ?
- default_value : strcmp(string_value, "0") != 0;
+ return string_value == nullptr ? default_value
+ : strcmp(string_value, "0") != 0;
+#endif // defined(GTEST_GET_BOOL_FROM_ENV_)
}
// Reads and returns a 32-bit integer stored in the environment
@@ -1204,10 +1345,10 @@ bool BoolFromGTestEnv(const char* flag, bool default_value) {
Int32 Int32FromGTestEnv(const char* flag, Int32 default_value) {
#if defined(GTEST_GET_INT32_FROM_ENV_)
return GTEST_GET_INT32_FROM_ENV_(flag, default_value);
-#endif // defined(GTEST_GET_INT32_FROM_ENV_)
+#else
const std::string env_var = FlagToEnvVar(flag);
const char* const string_value = posix::GetEnv(env_var.c_str());
- if (string_value == NULL) {
+ if (string_value == nullptr) {
// The environment variable is not set.
return default_value;
}
@@ -1222,37 +1363,36 @@ Int32 Int32FromGTestEnv(const char* flag, Int32 default_value) {
}
return result;
+#endif // defined(GTEST_GET_INT32_FROM_ENV_)
+}
+
+// As a special case for the 'output' flag, if GTEST_OUTPUT is not
+// set, we look for XML_OUTPUT_FILE, which is set by the Bazel build
+// system. The value of XML_OUTPUT_FILE is a filename without the
+// "xml:" prefix of GTEST_OUTPUT.
+// Note that this is meant to be called at the call site so it does
+// not check that the flag is 'output'
+// In essence this checks an env variable called XML_OUTPUT_FILE
+// and if it is set we prepend "xml:" to its value, if it not set we return ""
+std::string OutputFlagAlsoCheckEnvVar(){
+ std::string default_value_for_output_flag = "";
+ const char* xml_output_file_env = posix::GetEnv("XML_OUTPUT_FILE");
+ if (nullptr != xml_output_file_env) {
+ default_value_for_output_flag = std::string("xml:") + xml_output_file_env;
+ }
+ return default_value_for_output_flag;
}
// Reads and returns the string environment variable corresponding to
// the given flag; if it's not set, returns default_value.
-std::string StringFromGTestEnv(const char* flag, const char* default_value) {
+const char* StringFromGTestEnv(const char* flag, const char* default_value) {
#if defined(GTEST_GET_STRING_FROM_ENV_)
return GTEST_GET_STRING_FROM_ENV_(flag, default_value);
-#endif // defined(GTEST_GET_STRING_FROM_ENV_)
+#else
const std::string env_var = FlagToEnvVar(flag);
- const char* value = posix::GetEnv(env_var.c_str());
- if (value != NULL) {
- return value;
- }
-
- // As a special case for the 'output' flag, if GTEST_OUTPUT is not
- // set, we look for XML_OUTPUT_FILE, which is set by the Bazel build
- // system. The value of XML_OUTPUT_FILE is a filename without the
- // "xml:" prefix of GTEST_OUTPUT.
- //
- // The net priority order after flag processing is thus:
- // --gtest_output command line flag
- // GTEST_OUTPUT environment variable
- // XML_OUTPUT_FILE environment variable
- // 'default_value'
- if (strcmp(flag, "output") == 0) {
- value = posix::GetEnv("XML_OUTPUT_FILE");
- if (value != NULL) {
- return std::string("xml:") + value;
- }
- }
- return default_value;
+ const char* const value = posix::GetEnv(env_var.c_str());
+ return value == nullptr ? default_value : value;
+#endif // defined(GTEST_GET_STRING_FROM_ENV_)
}
} // namespace internal
diff --git a/extern/gtest/src/gtest-printers.cc b/extern/gtest/src/gtest-printers.cc
index a2df412f8a2..3337be312ea 100644
--- a/extern/gtest/src/gtest-printers.cc
+++ b/extern/gtest/src/gtest-printers.cc
@@ -26,10 +26,9 @@
// 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: wan@google.com (Zhanyong Wan)
-// Google Test - The Google C++ Testing Framework
+
+// Google Test - The Google C++ Testing and Mocking Framework
//
// This file implements a universal value printer that can print a
// value of any type T:
@@ -43,12 +42,13 @@
// defines Foo.
#include "gtest/gtest-printers.h"
-#include <ctype.h>
#include <stdio.h>
+#include <cctype>
#include <cwchar>
#include <ostream> // NOLINT
#include <string>
#include "gtest/internal/gtest-port.h"
+#include "src/gtest-internal-inl.h"
namespace testing {
@@ -59,6 +59,7 @@ using ::std::ostream;
// Prints a segment of bytes in the given object.
GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_
GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_
+GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_
GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_
void PrintByteSegmentInObjectTo(const unsigned char* obj_bytes, size_t start,
size_t count, ostream* os) {
@@ -89,7 +90,6 @@ void PrintBytesInObjectToImpl(const unsigned char* obj_bytes, size_t count,
// If the object size is bigger than kThreshold, we'll have to omit
// some details by printing only the first and the last kChunkSize
// bytes.
- // TODO(wan): let the user control the threshold using a flag.
if (count < kThreshold) {
PrintByteSegmentInObjectTo(obj_bytes, 0, count, os);
} else {
@@ -123,7 +123,7 @@ namespace internal {
// Depending on the value of a char (or wchar_t), we print it in one
// of three formats:
// - as is if it's a printable ASCII (e.g. 'a', '2', ' '),
-// - as a hexidecimal escape sequence (e.g. '\x7F'), or
+// - as a hexadecimal escape sequence (e.g. '\x7F'), or
// - as a special escape sequence (e.g. '\r', '\n').
enum CharFormat {
kAsIs,
@@ -144,7 +144,8 @@ inline bool IsPrintableAscii(wchar_t c) {
// which is the type of c.
template <typename UnsignedChar, typename Char>
static CharFormat PrintAsCharLiteralTo(Char c, ostream* os) {
- switch (static_cast<wchar_t>(c)) {
+ wchar_t w_c = static_cast<wchar_t>(c);
+ switch (w_c) {
case L'\0':
*os << "\\0";
break;
@@ -176,11 +177,14 @@ static CharFormat PrintAsCharLiteralTo(Char c, ostream* os) {
*os << "\\v";
break;
default:
- if (IsPrintableAscii(c)) {
+ if (IsPrintableAscii(w_c)) {
*os << static_cast<char>(c);
return kAsIs;
} else {
- *os << "\\x" + String::FormatHexInt(static_cast<UnsignedChar>(c));
+ ostream::fmtflags flags = os->flags();
+ *os << "\\x" << std::hex << std::uppercase
+ << static_cast<int>(static_cast<UnsignedChar>(c));
+ os->flags(flags);
return kHexEscape;
}
}
@@ -227,13 +231,13 @@ void PrintCharAndCodeTo(Char c, ostream* os) {
return;
*os << " (" << static_cast<int>(c);
- // For more convenience, we print c's code again in hexidecimal,
+ // For more convenience, we print c's code again in hexadecimal,
// unless c was already printed in the form '\x##' or the code is in
// [1, 9].
if (format == kHexEscape || (1 <= c && c <= 9)) {
// Do nothing.
} else {
- *os << ", 0x" << String::FormatHexInt(static_cast<UnsignedChar>(c));
+ *os << ", 0x" << String::FormatHexInt(static_cast<int>(c));
}
*os << ")";
}
@@ -258,12 +262,14 @@ void PrintTo(wchar_t wc, ostream* os) {
template <typename CharType>
GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_
GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_
+GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_
GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_
-static void PrintCharsAsStringTo(
+static CharFormat PrintCharsAsStringTo(
const CharType* begin, size_t len, ostream* os) {
const char* const kQuoteBegin = sizeof(CharType) == 1 ? "\"" : "L\"";
*os << kQuoteBegin;
bool is_previous_hex = false;
+ CharFormat print_format = kAsIs;
for (size_t index = 0; index < len; ++index) {
const CharType cur = begin[index];
if (is_previous_hex && IsXDigit(cur)) {
@@ -273,8 +279,13 @@ static void PrintCharsAsStringTo(
*os << "\" " << kQuoteBegin;
}
is_previous_hex = PrintAsStringLiteralTo(cur, os) == kHexEscape;
+ // Remember if any characters required hex escaping.
+ if (is_previous_hex) {
+ print_format = kHexEscape;
+ }
}
*os << "\"";
+ return print_format;
}
// Prints a (const) char/wchar_t array of 'len' elements, starting at address
@@ -282,6 +293,7 @@ static void PrintCharsAsStringTo(
template <typename CharType>
GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_
GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_
+GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_
GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_
static void UniversalPrintCharArray(
const CharType* begin, size_t len, ostream* os) {
@@ -318,7 +330,7 @@ void UniversalPrintArray(const wchar_t* begin, size_t len, ostream* os) {
// Prints the given C string to the ostream.
void PrintTo(const char* s, ostream* os) {
- if (s == NULL) {
+ if (s == nullptr) {
*os << "NULL";
} else {
*os << ImplicitCast_<const void*>(s) << " pointing to ";
@@ -335,32 +347,89 @@ void PrintTo(const char* s, ostream* os) {
#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED)
// Prints the given wide C string to the ostream.
void PrintTo(const wchar_t* s, ostream* os) {
- if (s == NULL) {
+ if (s == nullptr) {
*os << "NULL";
} else {
*os << ImplicitCast_<const void*>(s) << " pointing to ";
- PrintCharsAsStringTo(s, std::wcslen(s), os);
+ PrintCharsAsStringTo(s, wcslen(s), os);
}
}
#endif // wchar_t is native
-// Prints a ::string object.
-#if GTEST_HAS_GLOBAL_STRING
-void PrintStringTo(const ::string& s, ostream* os) {
- PrintCharsAsStringTo(s.data(), s.size(), os);
+namespace {
+
+bool ContainsUnprintableControlCodes(const char* str, size_t length) {
+ const unsigned char *s = reinterpret_cast<const unsigned char *>(str);
+
+ for (size_t i = 0; i < length; i++) {
+ unsigned char ch = *s++;
+ if (std::iscntrl(ch)) {
+ switch (ch) {
+ case '\t':
+ case '\n':
+ case '\r':
+ break;
+ default:
+ return true;
+ }
+ }
+ }
+ return false;
}
-#endif // GTEST_HAS_GLOBAL_STRING
-void PrintStringTo(const ::std::string& s, ostream* os) {
- PrintCharsAsStringTo(s.data(), s.size(), os);
+bool IsUTF8TrailByte(unsigned char t) { return 0x80 <= t && t<= 0xbf; }
+
+bool IsValidUTF8(const char* str, size_t length) {
+ const unsigned char *s = reinterpret_cast<const unsigned char *>(str);
+
+ for (size_t i = 0; i < length;) {
+ unsigned char lead = s[i++];
+
+ if (lead <= 0x7f) {
+ continue; // single-byte character (ASCII) 0..7F
+ }
+ if (lead < 0xc2) {
+ return false; // trail byte or non-shortest form
+ } else if (lead <= 0xdf && (i + 1) <= length && IsUTF8TrailByte(s[i])) {
+ ++i; // 2-byte character
+ } else if (0xe0 <= lead && lead <= 0xef && (i + 2) <= length &&
+ IsUTF8TrailByte(s[i]) &&
+ IsUTF8TrailByte(s[i + 1]) &&
+ // check for non-shortest form and surrogate
+ (lead != 0xe0 || s[i] >= 0xa0) &&
+ (lead != 0xed || s[i] < 0xa0)) {
+ i += 2; // 3-byte character
+ } else if (0xf0 <= lead && lead <= 0xf4 && (i + 3) <= length &&
+ IsUTF8TrailByte(s[i]) &&
+ IsUTF8TrailByte(s[i + 1]) &&
+ IsUTF8TrailByte(s[i + 2]) &&
+ // check for non-shortest form
+ (lead != 0xf0 || s[i] >= 0x90) &&
+ (lead != 0xf4 || s[i] < 0x90)) {
+ i += 3; // 4-byte character
+ } else {
+ return false;
+ }
+ }
+ return true;
}
-// Prints a ::wstring object.
-#if GTEST_HAS_GLOBAL_WSTRING
-void PrintWideStringTo(const ::wstring& s, ostream* os) {
- PrintCharsAsStringTo(s.data(), s.size(), os);
+void ConditionalPrintAsText(const char* str, size_t length, ostream* os) {
+ if (!ContainsUnprintableControlCodes(str, length) &&
+ IsValidUTF8(str, length)) {
+ *os << "\n As Text: \"" << str << "\"";
+ }
+}
+
+} // anonymous namespace
+
+void PrintStringTo(const ::std::string& s, ostream* os) {
+ if (PrintCharsAsStringTo(s.data(), s.size(), os) == kHexEscape) {
+ if (GTEST_FLAG(print_utf8)) {
+ ConditionalPrintAsText(s.data(), s.size(), os);
+ }
+ }
}
-#endif // GTEST_HAS_GLOBAL_WSTRING
#if GTEST_HAS_STD_WSTRING
void PrintWideStringTo(const ::std::wstring& s, ostream* os) {
diff --git a/extern/gtest/src/gtest-test-part.cc b/extern/gtest/src/gtest-test-part.cc
index fb0e35425e1..178317a6bcd 100644
--- a/extern/gtest/src/gtest-test-part.cc
+++ b/extern/gtest/src/gtest-test-part.cc
@@ -26,21 +26,12 @@
// 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: mheule@google.com (Markus Heule)
-//
-// The Google C++ Testing Framework (Google Test)
+// The Google C++ Testing and Mocking Framework (Google Test)
#include "gtest/gtest-test-part.h"
-
-// Indicates that this translation unit is part of Google Test's
-// implementation. It must come before gtest-internal-inl.h is
-// included, or there will be a compiler error. This trick exists to
-// prevent the accidental inclusion of gtest-internal-inl.h in the
-// user's code.
-#define GTEST_IMPLEMENTATION_ 1
#include "src/gtest-internal-inl.h"
-#undef GTEST_IMPLEMENTATION_
namespace testing {
@@ -50,18 +41,21 @@ using internal::GetUnitTestImpl;
// in it.
std::string TestPartResult::ExtractSummary(const char* message) {
const char* const stack_trace = strstr(message, internal::kStackTraceMarker);
- return stack_trace == NULL ? message :
- std::string(message, stack_trace);
+ return stack_trace == nullptr ? message : std::string(message, stack_trace);
}
// Prints a TestPartResult object.
std::ostream& operator<<(std::ostream& os, const TestPartResult& result) {
- return os
- << result.file_name() << ":" << result.line_number() << ": "
- << (result.type() == TestPartResult::kSuccess ? "Success" :
- result.type() == TestPartResult::kFatalFailure ? "Fatal failure" :
- "Non-fatal failure") << ":\n"
- << result.message() << std::endl;
+ return os << result.file_name() << ":" << result.line_number() << ": "
+ << (result.type() == TestPartResult::kSuccess
+ ? "Success"
+ : result.type() == TestPartResult::kSkip
+ ? "Skipped"
+ : result.type() == TestPartResult::kFatalFailure
+ ? "Fatal failure"
+ : "Non-fatal failure")
+ << ":\n"
+ << result.message() << std::endl;
}
// Appends a TestPartResult to the array.
@@ -76,7 +70,7 @@ const TestPartResult& TestPartResultArray::GetTestPartResult(int index) const {
internal::posix::Abort();
}
- return array_[index];
+ return array_[static_cast<size_t>(index)];
}
// Returns the number of TestPartResult objects in the array.
diff --git a/extern/gtest/src/gtest-typed-test.cc b/extern/gtest/src/gtest-typed-test.cc
index df1eef4754e..8677caf732b 100644
--- a/extern/gtest/src/gtest-typed-test.cc
+++ b/extern/gtest/src/gtest-typed-test.cc
@@ -26,10 +26,10 @@
// 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: wan@google.com (Zhanyong Wan)
+
#include "gtest/gtest-typed-test.h"
+
#include "gtest/gtest.h"
namespace testing {
@@ -48,7 +48,7 @@ static const char* SkipSpaces(const char* str) {
static std::vector<std::string> SplitIntoTestNames(const char* src) {
std::vector<std::string> name_vec;
src = SkipSpaces(src);
- for (; src != NULL; src = SkipComma(src)) {
+ for (; src != nullptr; src = SkipComma(src)) {
name_vec.push_back(StripTrailingSpaces(GetPrefixUntilComma(src)));
}
return name_vec;
@@ -57,7 +57,7 @@ static std::vector<std::string> SplitIntoTestNames(const char* src) {
// Verifies that registered_tests match the test names in
// registered_tests_; returns registered_tests if successful, or
// aborts the program otherwise.
-const char* TypedTestCasePState::VerifyRegisteredTestNames(
+const char* TypedTestSuitePState::VerifyRegisteredTestNames(
const char* file, int line, const char* registered_tests) {
typedef RegisteredTestsMap::const_iterator RegisteredTestIter;
registered_ = true;
@@ -89,7 +89,7 @@ const char* TypedTestCasePState::VerifyRegisteredTestNames(
tests.insert(name);
} else {
errors << "No test named " << name
- << " can be found in this test case.\n";
+ << " can be found in this test suite.\n";
}
}
diff --git a/extern/gtest/src/gtest.cc b/extern/gtest/src/gtest.cc
index d882ab2e36a..a5b4e5ac78c 100644
--- a/extern/gtest/src/gtest.cc
+++ b/extern/gtest/src/gtest.cc
@@ -26,10 +26,9 @@
// 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: wan@google.com (Zhanyong Wan)
-//
-// The Google C++ Testing Framework (Google Test)
+// The Google C++ Testing and Mocking Framework (Google Test)
#include "gtest/gtest.h"
#include "gtest/internal/custom/gtest.h"
@@ -55,8 +54,6 @@
#if GTEST_OS_LINUX
-// TODO(kenton@google.com): Use autoconf to detect availability of
-// gettimeofday().
# define GTEST_HAS_GETTIMEOFDAY_ 1
# include <fcntl.h> // NOLINT
@@ -69,10 +66,6 @@
# include <unistd.h> // NOLINT
# include <string>
-#elif GTEST_OS_SYMBIAN
-# define GTEST_HAS_GETTIMEOFDAY_ 1
-# include <sys/time.h> // NOLINT
-
#elif GTEST_OS_ZOS
# define GTEST_HAS_GETTIMEOFDAY_ 1
# include <sys/time.h> // NOLINT
@@ -87,6 +80,11 @@
#elif GTEST_OS_WINDOWS // We are on Windows proper.
+# include <windows.h> // NOLINT
+# undef min
+
+# include <crtdbg.h> // NOLINT
+# include <debugapi.h> // NOLINT
# include <io.h> // NOLINT
# include <sys/timeb.h> // NOLINT
# include <sys/types.h> // NOLINT
@@ -94,25 +92,13 @@
# if GTEST_OS_WINDOWS_MINGW
// MinGW has gettimeofday() but not _ftime64().
-// TODO(kenton@google.com): Use autoconf to detect availability of
-// gettimeofday().
-// TODO(kenton@google.com): There are other ways to get the time on
-// Windows, like GetTickCount() or GetSystemTimeAsFileTime(). MinGW
-// supports these. consider using them instead.
# define GTEST_HAS_GETTIMEOFDAY_ 1
# include <sys/time.h> // NOLINT
# endif // GTEST_OS_WINDOWS_MINGW
-// cpplint thinks that the header is already included, so we want to
-// silence it.
-# include <windows.h> // NOLINT
-# undef min
-
#else
// Assume other platforms have gettimeofday().
-// TODO(kenton@google.com): Use autoconf to detect availability of
-// gettimeofday().
# define GTEST_HAS_GETTIMEOFDAY_ 1
// cpplint thinks that the header is already included, so we want to
@@ -133,19 +119,25 @@
# include <sys/types.h> // NOLINT
#endif
-// Indicates that this translation unit is part of Google Test's
-// implementation. It must come before gtest-internal-inl.h is
-// included, or there will be a compiler error. This trick is to
-// prevent a user from accidentally including gtest-internal-inl.h in
-// his code.
-#define GTEST_IMPLEMENTATION_ 1
#include "src/gtest-internal-inl.h"
-#undef GTEST_IMPLEMENTATION_
#if GTEST_OS_WINDOWS
# define vsnprintf _vsnprintf
#endif // GTEST_OS_WINDOWS
+#if GTEST_OS_MAC
+#ifndef GTEST_OS_IOS
+#include <crt_externs.h>
+#endif
+#endif
+
+#if GTEST_HAS_ABSL
+#include "absl/debugging/failure_signal_handler.h"
+#include "absl/debugging/stacktrace.h"
+#include "absl/debugging/symbolize.h"
+#include "absl/strings/str_cat.h"
+#endif // GTEST_HAS_ABSL
+
namespace testing {
using internal::CountIf;
@@ -155,20 +147,22 @@ using internal::Shuffle;
// Constants.
-// A test whose test case name or test name matches this filter is
+// A test whose test suite name or test name matches this filter is
// disabled and not run.
static const char kDisableTestFilter[] = "DISABLED_*:*/DISABLED_*";
-// A test case whose name matches this filter is considered a death
-// test case and will be run before test cases whose name doesn't
+// A test suite whose name matches this filter is considered a death
+// test suite and will be run before test suites whose name doesn't
// match this filter.
-static const char kDeathTestCaseFilter[] = "*DeathTest:*DeathTest/*";
+static const char kDeathTestSuiteFilter[] = "*DeathTest:*DeathTest/*";
// A test filter that matches everything.
static const char kUniversalFilter[] = "*";
-// The default output file for XML output.
-static const char kDefaultOutputFile[] = "test_detail.xml";
+// The default output format.
+static const char kDefaultOutputFormat[] = "xml";
+// The default output file.
+static const char kDefaultOutputFile[] = "test_detail";
// The environment variable name for the test shard index.
static const char kTestShardIndex[] = "GTEST_SHARD_INDEX";
@@ -183,19 +177,35 @@ namespace internal {
// stack trace.
const char kStackTraceMarker[] = "\nStack trace:\n";
-// g_help_flag is true iff the --help flag or an equivalent form is
-// specified on the command line.
+// g_help_flag is true if and only if the --help flag or an equivalent form
+// is specified on the command line.
bool g_help_flag = false;
+// Utilty function to Open File for Writing
+static FILE* OpenFileForWriting(const std::string& output_file) {
+ FILE* fileout = nullptr;
+ FilePath output_file_path(output_file);
+ FilePath output_dir(output_file_path.RemoveFileName());
+
+ if (output_dir.CreateDirectoriesRecursively()) {
+ fileout = posix::FOpen(output_file.c_str(), "w");
+ }
+ if (fileout == nullptr) {
+ GTEST_LOG_(FATAL) << "Unable to open file \"" << output_file << "\"";
+ }
+ return fileout;
+}
+
} // namespace internal
+// Bazel passes in the argument to '--test_filter' via the TESTBRIDGE_TEST_ONLY
+// environment variable.
static const char* GetDefaultFilter() {
-#ifdef GTEST_TEST_FILTER_ENV_VAR_
- const char* const testbridge_test_only = getenv(GTEST_TEST_FILTER_ENV_VAR_);
- if (testbridge_test_only != NULL) {
+ const char* const testbridge_test_only =
+ internal::posix::GetEnv("TESTBRIDGE_TEST_ONLY");
+ if (testbridge_test_only != nullptr) {
return testbridge_test_only;
}
-#endif // GTEST_TEST_FILTER_ENV_VAR_
return kUniversalFilter;
}
@@ -205,15 +215,14 @@ GTEST_DEFINE_bool_(
"Run disabled tests too, in addition to the tests normally being run.");
GTEST_DEFINE_bool_(
- break_on_failure,
- internal::BoolFromGTestEnv("break_on_failure", false),
- "True iff a failed assertion should be a debugger break-point.");
+ break_on_failure, internal::BoolFromGTestEnv("break_on_failure", false),
+ "True if and only if a failed assertion should be a debugger "
+ "break-point.");
-GTEST_DEFINE_bool_(
- catch_exceptions,
- internal::BoolFromGTestEnv("catch_exceptions", true),
- "True iff " GTEST_NAME_
- " should catch exceptions and treat them as test failures.");
+GTEST_DEFINE_bool_(catch_exceptions,
+ internal::BoolFromGTestEnv("catch_exceptions", true),
+ "True if and only if " GTEST_NAME_
+ " should catch exceptions and treat them as test failures.");
GTEST_DEFINE_string_(
color,
@@ -232,26 +241,41 @@ GTEST_DEFINE_string_(
"exclude). A test is run if it matches one of the positive "
"patterns and does not match any of the negative patterns.");
+GTEST_DEFINE_bool_(
+ install_failure_signal_handler,
+ internal::BoolFromGTestEnv("install_failure_signal_handler", false),
+ "If true and supported on the current platform, " GTEST_NAME_ " should "
+ "install a signal handler that dumps debugging information when fatal "
+ "signals are raised.");
+
GTEST_DEFINE_bool_(list_tests, false,
"List all tests without running them.");
+// The net priority order after flag processing is thus:
+// --gtest_output command line flag
+// GTEST_OUTPUT environment variable
+// XML_OUTPUT_FILE environment variable
+// ''
GTEST_DEFINE_string_(
output,
- internal::StringFromGTestEnv("output", ""),
- "A format (currently must be \"xml\"), optionally followed "
- "by a colon and an output file name or directory. A directory "
- "is indicated by a trailing pathname separator. "
+ internal::StringFromGTestEnv("output",
+ internal::OutputFlagAlsoCheckEnvVar().c_str()),
+ "A format (defaults to \"xml\" but can be specified to be \"json\"), "
+ "optionally followed by a colon and an output file name or directory. "
+ "A directory is indicated by a trailing pathname separator. "
"Examples: \"xml:filename.xml\", \"xml::directoryname/\". "
"If a directory is specified, output files will be created "
"within that directory, with file-names based on the test "
"executable's name and, if necessary, made unique by adding "
"digits.");
-GTEST_DEFINE_bool_(
- print_time,
- internal::BoolFromGTestEnv("print_time", true),
- "True iff " GTEST_NAME_
- " should display elapsed time in text output.");
+GTEST_DEFINE_bool_(print_time, internal::BoolFromGTestEnv("print_time", true),
+ "True if and only if " GTEST_NAME_
+ " should display elapsed time in text output.");
+
+GTEST_DEFINE_bool_(print_utf8, internal::BoolFromGTestEnv("print_utf8", true),
+ "True if and only if " GTEST_NAME_
+ " prints UTF8 characters as text.");
GTEST_DEFINE_int32_(
random_seed,
@@ -265,16 +289,14 @@ GTEST_DEFINE_int32_(
"How many times to repeat each test. Specify a negative number "
"for repeating forever. Useful for shaking out flaky tests.");
-GTEST_DEFINE_bool_(
- show_internal_stack_frames, false,
- "True iff " GTEST_NAME_ " should include internal stack frames when "
- "printing test failure stack traces.");
+GTEST_DEFINE_bool_(show_internal_stack_frames, false,
+ "True if and only if " GTEST_NAME_
+ " should include internal stack frames when "
+ "printing test failure stack traces.");
-GTEST_DEFINE_bool_(
- shuffle,
- internal::BoolFromGTestEnv("shuffle", false),
- "True iff " GTEST_NAME_
- " should randomize tests' order on every run.");
+GTEST_DEFINE_bool_(shuffle, internal::BoolFromGTestEnv("shuffle", false),
+ "True if and only if " GTEST_NAME_
+ " should randomize tests' order on every run.");
GTEST_DEFINE_int32_(
stack_trace_depth,
@@ -294,7 +316,7 @@ GTEST_DEFINE_bool_(
internal::BoolFromGTestEnv("throw_on_failure", false),
"When this flag is specified, a failed assertion will throw an exception "
"if exceptions are enabled or exit the program with a non-zero code "
- "otherwise.");
+ "otherwise. For use with an external test framework.");
#if GTEST_USE_OWN_FLAGFILE_FLAG_
GTEST_DEFINE_string_(
@@ -310,7 +332,8 @@ namespace internal {
// than kMaxRange.
UInt32 Random::Generate(UInt32 range) {
// These constants are the same as are used in glibc's rand(3).
- state_ = (1103515245U*state_ + 12345U) % kMaxRange;
+ // Use wider types than necessary to prevent unsigned overflow diagnostics.
+ state_ = static_cast<UInt32>(1103515245ULL*state_ + 12345U) % kMaxRange;
GTEST_CHECK_(range > 0)
<< "Cannot generate a number in the range [0, 0).";
@@ -324,16 +347,16 @@ UInt32 Random::Generate(UInt32 range) {
return state_ % range;
}
-// GTestIsInitialized() returns true iff the user has initialized
+// GTestIsInitialized() returns true if and only if the user has initialized
// Google Test. Useful for catching the user mistake of not initializing
// Google Test before calling RUN_ALL_TESTS().
static bool GTestIsInitialized() { return GetArgvs().size() > 0; }
-// Iterates over a vector of TestCases, keeping a running sum of the
+// Iterates over a vector of TestSuites, keeping a running sum of the
// results of calling a given int-returning method on each.
// Returns the sum.
-static int SumOverTestCaseList(const std::vector<TestCase*>& case_list,
- int (TestCase::*method)() const) {
+static int SumOverTestSuiteList(const std::vector<TestSuite*>& case_list,
+ int (TestSuite::*method)() const) {
int sum = 0;
for (size_t i = 0; i < case_list.size(); i++) {
sum += (case_list[i]->*method)();
@@ -341,20 +364,20 @@ static int SumOverTestCaseList(const std::vector<TestCase*>& case_list,
return sum;
}
-// Returns true iff the test case passed.
-static bool TestCasePassed(const TestCase* test_case) {
- return test_case->should_run() && test_case->Passed();
+// Returns true if and only if the test suite passed.
+static bool TestSuitePassed(const TestSuite* test_suite) {
+ return test_suite->should_run() && test_suite->Passed();
}
-// Returns true iff the test case failed.
-static bool TestCaseFailed(const TestCase* test_case) {
- return test_case->should_run() && test_case->Failed();
+// Returns true if and only if the test suite failed.
+static bool TestSuiteFailed(const TestSuite* test_suite) {
+ return test_suite->should_run() && test_suite->Failed();
}
-// Returns true iff test_case contains at least one test that should
-// run.
-static bool ShouldRunTestCase(const TestCase* test_case) {
- return test_case->should_run();
+// Returns true if and only if test_suite contains at least one test that
+// should run.
+static bool ShouldRunTestSuite(const TestSuite* test_suite) {
+ return test_suite->should_run();
}
// AssertHelper constructor.
@@ -380,16 +403,16 @@ void AssertHelper::operator=(const Message& message) const {
); // NOLINT
}
-// Mutex for linked pointers.
-GTEST_API_ GTEST_DEFINE_STATIC_MUTEX_(g_linked_ptr_mutex);
-
// A copy of all command line arguments. Set by InitGoogleTest().
-::std::vector<testing::internal::string> g_argvs;
+static ::std::vector<std::string> g_argvs;
-const ::std::vector<testing::internal::string>& GetArgvs() {
+::std::vector<std::string> GetArgvs() {
#if defined(GTEST_CUSTOM_GET_ARGVS_)
- return GTEST_CUSTOM_GET_ARGVS_();
-#else // defined(GTEST_CUSTOM_GET_ARGVS_)
+ // GTEST_CUSTOM_GET_ARGVS_() may return a container of std::string or
+ // ::string. This code converts it to the appropriate type.
+ const auto& custom = GTEST_CUSTOM_GET_ARGVS_();
+ return ::std::vector<std::string>(custom.begin(), custom.end());
+#else // defined(GTEST_CUSTOM_GET_ARGVS_)
return g_argvs;
#endif // defined(GTEST_CUSTOM_GET_ARGVS_)
}
@@ -399,7 +422,7 @@ const ::std::vector<testing::internal::string>& GetArgvs() {
FilePath GetCurrentExecutableName() {
FilePath result;
-#if GTEST_OS_WINDOWS
+#if GTEST_OS_WINDOWS || GTEST_OS_OS2
result.Set(FilePath(GetArgvs()[0]).RemoveExtension("exe"));
#else
result.Set(FilePath(GetArgvs()[0]));
@@ -413,34 +436,32 @@ FilePath GetCurrentExecutableName() {
// Returns the output format, or "" for normal printed output.
std::string UnitTestOptions::GetOutputFormat() {
const char* const gtest_output_flag = GTEST_FLAG(output).c_str();
- if (gtest_output_flag == NULL) return std::string("");
-
const char* const colon = strchr(gtest_output_flag, ':');
- return (colon == NULL) ?
- std::string(gtest_output_flag) :
- std::string(gtest_output_flag, colon - gtest_output_flag);
+ return (colon == nullptr)
+ ? std::string(gtest_output_flag)
+ : std::string(gtest_output_flag,
+ static_cast<size_t>(colon - gtest_output_flag));
}
// Returns the name of the requested output file, or the default if none
// was explicitly specified.
std::string UnitTestOptions::GetAbsolutePathToOutputFile() {
const char* const gtest_output_flag = GTEST_FLAG(output).c_str();
- if (gtest_output_flag == NULL)
- return "";
+
+ std::string format = GetOutputFormat();
+ if (format.empty())
+ format = std::string(kDefaultOutputFormat);
const char* const colon = strchr(gtest_output_flag, ':');
- if (colon == NULL)
- return internal::FilePath::ConcatPaths(
+ if (colon == nullptr)
+ return internal::FilePath::MakeFileName(
internal::FilePath(
UnitTest::GetInstance()->original_working_dir()),
- internal::FilePath(kDefaultOutputFile)).string();
+ internal::FilePath(kDefaultOutputFile), 0,
+ format.c_str()).string();
internal::FilePath output_name(colon + 1);
if (!output_name.IsAbsolutePath())
- // TODO(wan@google.com): on Windows \some\path is not an absolute
- // path (as its meaning depends on the current drive), yet the
- // following logic for turning it into an absolute path is wrong.
- // Fix it.
output_name = internal::FilePath::ConcatPaths(
internal::FilePath(UnitTest::GetInstance()->original_working_dir()),
internal::FilePath(colon + 1));
@@ -454,8 +475,8 @@ std::string UnitTestOptions::GetAbsolutePathToOutputFile() {
return result.string();
}
-// Returns true iff the wildcard pattern matches the string. The
-// first ':' or '\0' character in pattern marks the end of it.
+// Returns true if and only if the wildcard pattern matches the string.
+// The first ':' or '\0' character in pattern marks the end of it.
//
// This recursive algorithm isn't very efficient, but is clear and
// works well enough for matching test names, which are short.
@@ -488,7 +509,7 @@ bool UnitTestOptions::MatchesFilter(
cur_pattern = strchr(cur_pattern, ':');
// Returns if no more pattern can be found.
- if (cur_pattern == NULL) {
+ if (cur_pattern == nullptr) {
return false;
}
@@ -497,11 +518,11 @@ bool UnitTestOptions::MatchesFilter(
}
}
-// Returns true iff the user-specified filter matches the test case
-// name and the test name.
-bool UnitTestOptions::FilterMatchesTest(const std::string &test_case_name,
- const std::string &test_name) {
- const std::string& full_name = test_case_name + "." + test_name.c_str();
+// Returns true if and only if the user-specified filter matches the test
+// suite name and the test name.
+bool UnitTestOptions::FilterMatchesTest(const std::string& test_suite_name,
+ const std::string& test_name) {
+ const std::string& full_name = test_suite_name + "." + test_name.c_str();
// Split --gtest_filter at '-', if there is one, to separate into
// positive filter and negative filter portions
@@ -509,7 +530,7 @@ bool UnitTestOptions::FilterMatchesTest(const std::string &test_case_name,
const char* const dash = strchr(p, '-');
std::string positive;
std::string negative;
- if (dash == NULL) {
+ if (dash == nullptr) {
positive = GTEST_FLAG(filter).c_str(); // Whole string is a positive filter
negative = "";
} else {
@@ -628,12 +649,12 @@ extern const TypeId kTestTypeIdInGoogleTest = GetTestTypeId();
// This predicate-formatter checks that 'results' contains a test part
// failure of the given type and that the failure message contains the
// given substring.
-AssertionResult HasOneFailure(const char* /* results_expr */,
- const char* /* type_expr */,
- const char* /* substr_expr */,
- const TestPartResultArray& results,
- TestPartResult::Type type,
- const string& substr) {
+static AssertionResult HasOneFailure(const char* /* results_expr */,
+ const char* /* type_expr */,
+ const char* /* substr_expr */,
+ const TestPartResultArray& results,
+ TestPartResult::Type type,
+ const std::string& substr) {
const std::string expected(type == TestPartResult::kFatalFailure ?
"1 fatal failure" :
"1 non-fatal failure");
@@ -654,7 +675,7 @@ AssertionResult HasOneFailure(const char* /* results_expr */,
<< r;
}
- if (strstr(r.message(), substr.c_str()) == NULL) {
+ if (strstr(r.message(), substr.c_str()) == nullptr) {
return AssertionFailure() << "Expected: " << expected << " containing \""
<< substr << "\"\n"
<< " Actual:\n"
@@ -667,13 +688,10 @@ AssertionResult HasOneFailure(const char* /* results_expr */,
// The constructor of SingleFailureChecker remembers where to look up
// test part results, what type of failure we expect, and what
// substring the failure message should contain.
-SingleFailureChecker:: SingleFailureChecker(
- const TestPartResultArray* results,
- TestPartResult::Type type,
- const string& substr)
- : results_(results),
- type_(type),
- substr_(substr) {}
+SingleFailureChecker::SingleFailureChecker(const TestPartResultArray* results,
+ TestPartResult::Type type,
+ const std::string& substr)
+ : results_(results), type_(type), substr_(substr) {}
// The destructor of SingleFailureChecker verifies that the given
// TestPartResultArray contains exactly one failure that has the given
@@ -726,61 +744,66 @@ void UnitTestImpl::SetTestPartResultReporterForCurrentThread(
per_thread_test_part_result_reporter_.set(reporter);
}
-// Gets the number of successful test cases.
-int UnitTestImpl::successful_test_case_count() const {
- return CountIf(test_cases_, TestCasePassed);
+// Gets the number of successful test suites.
+int UnitTestImpl::successful_test_suite_count() const {
+ return CountIf(test_suites_, TestSuitePassed);
}
-// Gets the number of failed test cases.
-int UnitTestImpl::failed_test_case_count() const {
- return CountIf(test_cases_, TestCaseFailed);
+// Gets the number of failed test suites.
+int UnitTestImpl::failed_test_suite_count() const {
+ return CountIf(test_suites_, TestSuiteFailed);
}
-// Gets the number of all test cases.
-int UnitTestImpl::total_test_case_count() const {
- return static_cast<int>(test_cases_.size());
+// Gets the number of all test suites.
+int UnitTestImpl::total_test_suite_count() const {
+ return static_cast<int>(test_suites_.size());
}
-// Gets the number of all test cases that contain at least one test
+// Gets the number of all test suites that contain at least one test
// that should run.
-int UnitTestImpl::test_case_to_run_count() const {
- return CountIf(test_cases_, ShouldRunTestCase);
+int UnitTestImpl::test_suite_to_run_count() const {
+ return CountIf(test_suites_, ShouldRunTestSuite);
}
// Gets the number of successful tests.
int UnitTestImpl::successful_test_count() const {
- return SumOverTestCaseList(test_cases_, &TestCase::successful_test_count);
+ return SumOverTestSuiteList(test_suites_, &TestSuite::successful_test_count);
+}
+
+// Gets the number of skipped tests.
+int UnitTestImpl::skipped_test_count() const {
+ return SumOverTestSuiteList(test_suites_, &TestSuite::skipped_test_count);
}
// Gets the number of failed tests.
int UnitTestImpl::failed_test_count() const {
- return SumOverTestCaseList(test_cases_, &TestCase::failed_test_count);
+ return SumOverTestSuiteList(test_suites_, &TestSuite::failed_test_count);
}
// Gets the number of disabled tests that will be reported in the XML report.
int UnitTestImpl::reportable_disabled_test_count() const {
- return SumOverTestCaseList(test_cases_,
- &TestCase::reportable_disabled_test_count);
+ return SumOverTestSuiteList(test_suites_,
+ &TestSuite::reportable_disabled_test_count);
}
// Gets the number of disabled tests.
int UnitTestImpl::disabled_test_count() const {
- return SumOverTestCaseList(test_cases_, &TestCase::disabled_test_count);
+ return SumOverTestSuiteList(test_suites_, &TestSuite::disabled_test_count);
}
// Gets the number of tests to be printed in the XML report.
int UnitTestImpl::reportable_test_count() const {
- return SumOverTestCaseList(test_cases_, &TestCase::reportable_test_count);
+ return SumOverTestSuiteList(test_suites_, &TestSuite::reportable_test_count);
}
// Gets the number of all tests.
int UnitTestImpl::total_test_count() const {
- return SumOverTestCaseList(test_cases_, &TestCase::total_test_count);
+ return SumOverTestSuiteList(test_suites_, &TestSuite::total_test_count);
}
// Gets the number of tests that should run.
int UnitTestImpl::test_to_run_count() const {
- return SumOverTestCaseList(test_cases_, &TestCase::test_to_run_count);
+ return SumOverTestSuiteList(test_suites_, &TestSuite::test_to_run_count);
}
// Returns the current OS stack trace as an std::string.
@@ -814,8 +837,6 @@ TimeInMillis GetTimeInMillis() {
SYSTEMTIME now_systime;
FILETIME now_filetime;
ULARGE_INTEGER now_int64;
- // TODO(kenton@google.com): Shouldn't this just use
- // GetSystemTimeAsFileTime()?
GetSystemTime(&now_systime);
if (SystemTimeToFileTime(&now_systime, &now_filetime)) {
now_int64.LowPart = now_filetime.dwLowDateTime;
@@ -830,16 +851,14 @@ TimeInMillis GetTimeInMillis() {
// MSVC 8 deprecates _ftime64(), so we want to suppress warning 4996
// (deprecated function) there.
- // TODO(kenton@google.com): Use GetTickCount()? Or use
- // SystemTimeToFileTime()
- GTEST_DISABLE_MSC_WARNINGS_PUSH_(4996)
+ GTEST_DISABLE_MSC_DEPRECATED_PUSH_()
_ftime64(&now);
- GTEST_DISABLE_MSC_WARNINGS_POP_()
+ GTEST_DISABLE_MSC_DEPRECATED_POP_()
return static_cast<TimeInMillis>(now.time) * 1000 + now.millitm;
#elif GTEST_HAS_GETTIMEOFDAY_
struct timeval now;
- gettimeofday(&now, NULL);
+ gettimeofday(&now, nullptr);
return static_cast<TimeInMillis>(now.tv_sec) * 1000 + now.tv_usec / 1000;
#else
# error "Don't know how to get the current time on your system."
@@ -856,11 +875,10 @@ TimeInMillis GetTimeInMillis() {
// value using delete[]. Returns the wide string, or NULL if the
// input is NULL.
LPCWSTR String::AnsiToUtf16(const char* ansi) {
- if (!ansi) return NULL;
+ if (!ansi) return nullptr;
const int length = strlen(ansi);
const int unicode_length =
- MultiByteToWideChar(CP_ACP, 0, ansi, length,
- NULL, 0);
+ MultiByteToWideChar(CP_ACP, 0, ansi, length, nullptr, 0);
WCHAR* unicode = new WCHAR[unicode_length + 1];
MultiByteToWideChar(CP_ACP, 0, ansi, length,
unicode, unicode_length);
@@ -873,33 +891,33 @@ LPCWSTR String::AnsiToUtf16(const char* ansi) {
// value using delete[]. Returns the ANSI string, or NULL if the
// input is NULL.
const char* String::Utf16ToAnsi(LPCWSTR utf16_str) {
- if (!utf16_str) return NULL;
- const int ansi_length =
- WideCharToMultiByte(CP_ACP, 0, utf16_str, -1,
- NULL, 0, NULL, NULL);
+ if (!utf16_str) return nullptr;
+ const int ansi_length = WideCharToMultiByte(CP_ACP, 0, utf16_str, -1, nullptr,
+ 0, nullptr, nullptr);
char* ansi = new char[ansi_length + 1];
- WideCharToMultiByte(CP_ACP, 0, utf16_str, -1,
- ansi, ansi_length, NULL, NULL);
+ WideCharToMultiByte(CP_ACP, 0, utf16_str, -1, ansi, ansi_length, nullptr,
+ nullptr);
ansi[ansi_length] = 0;
return ansi;
}
#endif // GTEST_OS_WINDOWS_MOBILE
-// Compares two C strings. Returns true iff they have the same content.
+// Compares two C strings. Returns true if and only if they have the same
+// content.
//
// Unlike strcmp(), this function can handle NULL argument(s). A NULL
// C string is considered different to any non-NULL C string,
// including the empty string.
bool String::CStringEquals(const char * lhs, const char * rhs) {
- if ( lhs == NULL ) return rhs == NULL;
+ if (lhs == nullptr) return rhs == nullptr;
- if ( rhs == NULL ) return false;
+ if (rhs == nullptr) return false;
return strcmp(lhs, rhs) == 0;
}
-#if GTEST_HAS_STD_WSTRING || GTEST_HAS_GLOBAL_WSTRING
+#if GTEST_HAS_STD_WSTRING
// Converts an array of wide chars to a narrow string using the UTF-8
// encoding, and streams the result to the given Message object.
@@ -917,7 +935,7 @@ static void StreamWideCharsToMessage(const wchar_t* wstr, size_t length,
}
}
-#endif // GTEST_HAS_STD_WSTRING || GTEST_HAS_GLOBAL_WSTRING
+#endif // GTEST_HAS_STD_WSTRING
void SplitString(const ::std::string& str, char delimiter,
::std::vector< ::std::string>* dest) {
@@ -967,15 +985,6 @@ Message& Message::operator <<(const ::std::wstring& wstr) {
}
#endif // GTEST_HAS_STD_WSTRING
-#if GTEST_HAS_GLOBAL_WSTRING
-// Converts the given wide string to a narrow string using the UTF-8
-// encoding, and streams the result to this Message object.
-Message& Message::operator <<(const ::wstring& wstr) {
- internal::StreamWideCharsToMessage(wstr.c_str(), wstr.length(), this);
- return *this;
-}
-#endif // GTEST_HAS_GLOBAL_WSTRING
-
// Gets the text streamed to this object so far as an std::string.
// Each '\0' character in the buffer is replaced with "\\0".
std::string Message::GetString() const {
@@ -986,10 +995,9 @@ std::string Message::GetString() const {
// Used in EXPECT_TRUE/FALSE(assertion_result).
AssertionResult::AssertionResult(const AssertionResult& other)
: success_(other.success_),
- message_(other.message_.get() != NULL ?
- new ::std::string(*other.message_) :
- static_cast< ::std::string*>(NULL)) {
-}
+ message_(other.message_.get() != nullptr
+ ? new ::std::string(*other.message_)
+ : static_cast< ::std::string*>(nullptr)) {}
// Swaps two AssertionResults.
void AssertionResult::swap(AssertionResult& other) {
@@ -1001,8 +1009,7 @@ void AssertionResult::swap(AssertionResult& other) {
// Returns the assertion's negation. Used with EXPECT/ASSERT_FALSE.
AssertionResult AssertionResult::operator!() const {
AssertionResult negation(!success_);
- if (message_.get() != NULL)
- negation << *message_;
+ if (message_.get() != nullptr) negation << *message_;
return negation;
}
@@ -1171,7 +1178,7 @@ class Hunk {
// Print a unified diff header for one hunk.
// The format is
// "@@ -<left_start>,<left_length> +<right_start>,<right_length> @@"
- // where the left/right parts are ommitted if unnecessary.
+ // where the left/right parts are omitted if unnecessary.
void PrintHeader(std::ostream* ss) const {
*ss << "@@ ";
if (removes_) {
@@ -1228,9 +1235,10 @@ std::string CreateUnifiedDiff(const std::vector<std::string>& left,
for (; edit_i < edits.size(); ++edit_i) {
if (n_suffix >= context) {
// Continue only if the next hunk is very close.
- std::vector<EditType>::const_iterator it = edits.begin() + edit_i;
+ auto it = edits.begin() + static_cast<int>(edit_i);
while (it != edits.end() && *it == kMatch) ++it;
- if (it == edits.end() || (it - edits.begin()) - edit_i >= context) {
+ if (it == edits.end() ||
+ static_cast<size_t>(it - edits.begin()) - edit_i >= context) {
// There is no next edit or it is too far away.
break;
}
@@ -1306,7 +1314,7 @@ std::vector<std::string> SplitEscapedString(const std::string& str) {
// lhs_value: "5"
// rhs_value: "6"
//
-// The ignoring_case parameter is true iff the assertion is a
+// The ignoring_case parameter is true if and only if the assertion is a
// *_STRCASEEQ*. When it's true, the string "Ignoring case" will
// be inserted into the message.
AssertionResult EqFailure(const char* lhs_expression,
@@ -1315,13 +1323,14 @@ AssertionResult EqFailure(const char* lhs_expression,
const std::string& rhs_value,
bool ignoring_case) {
Message msg;
- msg << " Expected: " << lhs_expression;
+ msg << "Expected equality of these values:";
+ msg << "\n " << lhs_expression;
if (lhs_value != lhs_expression) {
- msg << "\n Which is: " << lhs_value;
+ msg << "\n Which is: " << lhs_value;
}
- msg << "\nTo be equal to: " << rhs_expression;
+ msg << "\n " << rhs_expression;
if (rhs_value != rhs_expression) {
- msg << "\n Which is: " << rhs_value;
+ msg << "\n Which is: " << rhs_value;
}
if (ignoring_case) {
@@ -1368,8 +1377,6 @@ AssertionResult DoubleNearPredFormat(const char* expr1,
const double diff = fabs(val1 - val2);
if (diff <= abs_error) return AssertionSuccess();
- // TODO(wan): do not print the value of an expression if it's
- // already a literal.
return AssertionFailure()
<< "The difference between " << expr1 << " and " << expr2
<< " is " << diff << ", which exceeds " << abs_error_expr << ", where\n"
@@ -1550,22 +1557,20 @@ namespace {
// Helper functions for implementing IsSubString() and IsNotSubstring().
-// This group of overloaded functions return true iff needle is a
-// substring of haystack. NULL is considered a substring of itself
-// only.
+// This group of overloaded functions return true if and only if needle
+// is a substring of haystack. NULL is considered a substring of
+// itself only.
bool IsSubstringPred(const char* needle, const char* haystack) {
- if (needle == NULL || haystack == NULL)
- return needle == haystack;
+ if (needle == nullptr || haystack == nullptr) return needle == haystack;
- return strstr(haystack, needle) != NULL;
+ return strstr(haystack, needle) != nullptr;
}
bool IsSubstringPred(const wchar_t* needle, const wchar_t* haystack) {
- if (needle == NULL || haystack == NULL)
- return needle == haystack;
+ if (needle == nullptr || haystack == nullptr) return needle == haystack;
- return wcsstr(haystack, needle) != NULL;
+ return wcsstr(haystack, needle) != nullptr;
}
// StringType here can be either ::std::string or ::std::wstring.
@@ -1663,7 +1668,7 @@ namespace {
AssertionResult HRESULTFailureHelper(const char* expr,
const char* expected,
long hr) { // NOLINT
-# if GTEST_OS_WINDOWS_MOBILE
+# if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_TV_TITLE
// Windows CE doesn't support FormatMessage.
const char error_text[] = "";
@@ -1679,12 +1684,12 @@ AssertionResult HRESULTFailureHelper(const char* expr,
// Gets the system's human readable message string for this HRESULT.
char error_text[kBufSize] = { '\0' };
DWORD message_length = ::FormatMessageA(kFlags,
- 0, // no source, we're asking system
- hr, // the error
- 0, // no line width restrictions
+ 0, // no source, we're asking system
+ static_cast<DWORD>(hr), // the error
+ 0, // no line width restrictions
error_text, // output buffer
- kBufSize, // buf size
- NULL); // no arguments for inserts
+ kBufSize, // buf size
+ nullptr); // no arguments for inserts
// Trims tailing white space (FormatMessage leaves a trailing CR-LF)
for (; message_length && IsSpace(error_text[message_length - 1]);
--message_length) {
@@ -1720,7 +1725,7 @@ AssertionResult IsHRESULTFailure(const char* expr, long hr) { // NOLINT
// Utility functions for encoding Unicode text (wide strings) in
// UTF-8.
-// A Unicode code-point can have upto 21 bits, and is encoded in UTF-8
+// A Unicode code-point can have up to 21 bits, and is encoded in UTF-8
// like this:
//
// Code-point length Encoding
@@ -1758,7 +1763,7 @@ inline UInt32 ChopLowBits(UInt32* bits, int n) {
// to "(Invalid Unicode 0xXXXXXXXX)".
std::string CodePointToUtf8(UInt32 code_point) {
if (code_point > kMaxCodePoint4) {
- return "(Invalid Unicode 0x" + String::FormatHexInt(code_point) + ")";
+ return "(Invalid Unicode 0x" + String::FormatHexUInt32(code_point) + ")";
}
char str[5]; // Big enough for the largest valid code point.
@@ -1784,9 +1789,9 @@ std::string CodePointToUtf8(UInt32 code_point) {
return str;
}
-// The following two functions only make sense if the the system
+// The following two functions only make sense if the system
// uses UTF-16 for wide string encoding. All supported systems
-// with 16 bit wchar_t (Windows, Cygwin, Symbian OS) do use UTF-16.
+// with 16 bit wchar_t (Windows, Cygwin) do use UTF-16.
// Determines if the arguments constitute UTF-16 surrogate pair
// and thus should be combined into a single Unicode code point
@@ -1799,17 +1804,20 @@ inline bool IsUtf16SurrogatePair(wchar_t first, wchar_t second) {
// Creates a Unicode code point from UTF16 surrogate pair.
inline UInt32 CreateCodePointFromUtf16SurrogatePair(wchar_t first,
wchar_t second) {
+ const auto first_u = static_cast<UInt32>(first);
+ const auto second_u = static_cast<UInt32>(second);
const UInt32 mask = (1 << 10) - 1;
- return (sizeof(wchar_t) == 2) ?
- (((first & mask) << 10) | (second & mask)) + 0x10000 :
- // This function should not be called when the condition is
- // false, but we provide a sensible default in case it is.
- static_cast<UInt32>(first);
+ return (sizeof(wchar_t) == 2)
+ ? (((first_u & mask) << 10) | (second_u & mask)) + 0x10000
+ :
+ // This function should not be called when the condition is
+ // false, but we provide a sensible default in case it is.
+ first_u;
}
// Converts a wide string to a narrow string in UTF-8 encoding.
// The wide string is assumed to have the following encoding:
-// UTF-16 if sizeof(wchar_t) == 2 (on Windows, Cygwin, Symbian OS)
+// UTF-16 if sizeof(wchar_t) == 2 (on Windows, Cygwin)
// UTF-32 if sizeof(wchar_t) == 4 (on Linux)
// Parameter str points to a null-terminated wide string.
// Parameter num_chars may additionally limit the number
@@ -1846,21 +1854,21 @@ std::string WideStringToUtf8(const wchar_t* str, int num_chars) {
// Converts a wide C string to an std::string using the UTF-8 encoding.
// NULL will be converted to "(null)".
std::string String::ShowWideCString(const wchar_t * wide_c_str) {
- if (wide_c_str == NULL) return "(null)";
+ if (wide_c_str == nullptr) return "(null)";
return internal::WideStringToUtf8(wide_c_str, -1);
}
-// Compares two wide C strings. Returns true iff they have the same
-// content.
+// Compares two wide C strings. Returns true if and only if they have the
+// same content.
//
// Unlike wcscmp(), this function can handle NULL argument(s). A NULL
// C string is considered different to any non-NULL C string,
// including the empty string.
bool String::WideCStringEquals(const wchar_t * lhs, const wchar_t * rhs) {
- if (lhs == NULL) return rhs == NULL;
+ if (lhs == nullptr) return rhs == nullptr;
- if (rhs == NULL) return false;
+ if (rhs == nullptr) return false;
return wcscmp(lhs, rhs) == 0;
}
@@ -1896,37 +1904,35 @@ AssertionResult CmpHelperSTRNE(const char* s1_expression,
<< " vs " << PrintToString(s2);
}
-// Compares two C strings, ignoring case. Returns true iff they have
+// Compares two C strings, ignoring case. Returns true if and only if they have
// the same content.
//
// Unlike strcasecmp(), this function can handle NULL argument(s). A
// NULL C string is considered different to any non-NULL C string,
// including the empty string.
bool String::CaseInsensitiveCStringEquals(const char * lhs, const char * rhs) {
- if (lhs == NULL)
- return rhs == NULL;
- if (rhs == NULL)
- return false;
+ if (lhs == nullptr) return rhs == nullptr;
+ if (rhs == nullptr) return false;
return posix::StrCaseCmp(lhs, rhs) == 0;
}
- // Compares two wide C strings, ignoring case. Returns true iff they
- // have the same content.
- //
- // Unlike wcscasecmp(), this function can handle NULL argument(s).
- // A NULL C string is considered different to any non-NULL wide C string,
- // including the empty string.
- // NB: The implementations on different platforms slightly differ.
- // On windows, this method uses _wcsicmp which compares according to LC_CTYPE
- // environment variable. On GNU platform this method uses wcscasecmp
- // which compares according to LC_CTYPE category of the current locale.
- // On MacOS X, it uses towlower, which also uses LC_CTYPE category of the
- // current locale.
+// Compares two wide C strings, ignoring case. Returns true if and only if they
+// have the same content.
+//
+// Unlike wcscasecmp(), this function can handle NULL argument(s).
+// A NULL C string is considered different to any non-NULL wide C string,
+// including the empty string.
+// NB: The implementations on different platforms slightly differ.
+// On windows, this method uses _wcsicmp which compares according to LC_CTYPE
+// environment variable. On GNU platform this method uses wcscasecmp
+// which compares according to LC_CTYPE category of the current locale.
+// On MacOS X, it uses towlower, which also uses LC_CTYPE category of the
+// current locale.
bool String::CaseInsensitiveWideCStringEquals(const wchar_t* lhs,
const wchar_t* rhs) {
- if (lhs == NULL) return rhs == NULL;
+ if (lhs == nullptr) return rhs == nullptr;
- if (rhs == NULL) return false;
+ if (rhs == nullptr) return false;
#if GTEST_OS_WINDOWS
return _wcsicmp(lhs, rhs) == 0;
@@ -1937,14 +1943,14 @@ bool String::CaseInsensitiveWideCStringEquals(const wchar_t* lhs,
// Other unknown OSes may not define it either.
wint_t left, right;
do {
- left = towlower(*lhs++);
- right = towlower(*rhs++);
+ left = towlower(static_cast<wint_t>(*lhs++));
+ right = towlower(static_cast<wint_t>(*rhs++));
} while (left && left == right);
return left == right;
#endif // OS selector
}
-// Returns true iff str ends with the given suffix, ignoring case.
+// Returns true if and only if str ends with the given suffix, ignoring case.
// Any string is considered to end with an empty suffix.
bool String::EndsWithCaseInsensitive(
const std::string& str, const std::string& suffix) {
@@ -1963,12 +1969,17 @@ std::string String::FormatIntWidth2(int value) {
}
// Formats an int value as "%X".
-std::string String::FormatHexInt(int value) {
+std::string String::FormatHexUInt32(UInt32 value) {
std::stringstream ss;
ss << std::hex << std::uppercase << value;
return ss.str();
}
+// Formats an int value as "%X".
+std::string String::FormatHexInt(int value) {
+ return FormatHexUInt32(static_cast<UInt32>(value));
+}
+
// Formats a byte as "%02X".
std::string String::FormatByte(unsigned char value) {
std::stringstream ss;
@@ -1985,7 +1996,7 @@ std::string StringStreamToString(::std::stringstream* ss) {
const char* const end = start + str.length();
std::string result;
- result.reserve(2 * (end - start));
+ result.reserve(static_cast<size_t>(2 * (end - start)));
for (const char* ch = start; ch != end; ++ch) {
if (*ch == '\0') {
result += "\\0"; // Replaces NUL with "\\0";
@@ -2015,9 +2026,7 @@ std::string AppendUserMessage(const std::string& gtest_msg,
// Creates an empty TestResult.
TestResult::TestResult()
- : death_test_count_(0),
- elapsed_time_(0) {
-}
+ : death_test_count_(0), start_timestamp_(0), elapsed_time_(0) {}
// D'tor.
TestResult::~TestResult() {
@@ -2029,7 +2038,7 @@ TestResult::~TestResult() {
const TestPartResult& TestResult::GetTestPartResult(int i) const {
if (i < 0 || i >= total_part_count())
internal::posix::Abort();
- return test_part_results_.at(i);
+ return test_part_results_.at(static_cast<size_t>(i));
}
// Returns the i-th test property. i can range from 0 to
@@ -2038,7 +2047,7 @@ const TestPartResult& TestResult::GetTestPartResult(int i) const {
const TestProperty& TestResult::GetTestProperty(int i) const {
if (i < 0 || i >= test_property_count())
internal::posix::Abort();
- return test_properties_.at(i);
+ return test_properties_.at(static_cast<size_t>(i));
}
// Clears the test part results.
@@ -2086,23 +2095,18 @@ static const char* const kReservedTestSuitesAttributes[] = {
// The list of reserved attributes used in the <testsuite> element of XML
// output.
static const char* const kReservedTestSuiteAttributes[] = {
- "disabled",
- "errors",
- "failures",
- "name",
- "tests",
- "time"
-};
+ "disabled", "errors", "failures", "name", "tests", "time", "timestamp"};
// The list of reserved attributes used in the <testcase> element of XML output.
static const char* const kReservedTestCaseAttributes[] = {
- "classname",
- "name",
- "status",
- "time",
- "type_param",
- "value_param"
-};
+ "classname", "name", "status", "time", "type_param",
+ "value_param", "file", "line"};
+
+// Use a slightly different set for allowed output to ensure existing tests can
+// still RecordProperty("result") or "RecordProperty(timestamp")
+static const char* const kReservedOutputTestCaseAttributes[] = {
+ "classname", "name", "status", "time", "type_param",
+ "value_param", "file", "line", "result", "timestamp"};
template <int kSize>
std::vector<std::string> ArrayAsVector(const char* const (&array)[kSize]) {
@@ -2124,6 +2128,22 @@ static std::vector<std::string> GetReservedAttributesForElement(
return std::vector<std::string>();
}
+// TODO(jdesprez): Merge the two getReserved attributes once skip is improved
+static std::vector<std::string> GetReservedOutputAttributesForElement(
+ const std::string& xml_element) {
+ if (xml_element == "testsuites") {
+ return ArrayAsVector(kReservedTestSuitesAttributes);
+ } else if (xml_element == "testsuite") {
+ return ArrayAsVector(kReservedTestSuiteAttributes);
+ } else if (xml_element == "testcase") {
+ return ArrayAsVector(kReservedOutputTestCaseAttributes);
+ } else {
+ GTEST_CHECK_(false) << "Unrecognized xml_element provided: " << xml_element;
+ }
+ // This code is unreachable but some compilers may not realizes that.
+ return std::vector<std::string>();
+}
+
static std::string FormatWordList(const std::vector<std::string>& words) {
Message word_list;
for (size_t i = 0; i < words.size(); ++i) {
@@ -2138,8 +2158,9 @@ static std::string FormatWordList(const std::vector<std::string>& words) {
return word_list.GetString();
}
-bool ValidateTestPropertyName(const std::string& property_name,
- const std::vector<std::string>& reserved_names) {
+static bool ValidateTestPropertyName(
+ const std::string& property_name,
+ const std::vector<std::string>& reserved_names) {
if (std::find(reserved_names.begin(), reserved_names.end(), property_name) !=
reserved_names.end()) {
ADD_FAILURE() << "Reserved key used in RecordProperty(): " << property_name
@@ -2166,7 +2187,17 @@ void TestResult::Clear() {
elapsed_time_ = 0;
}
-// Returns true iff the test failed.
+// Returns true off the test part was skipped.
+static bool TestPartSkipped(const TestPartResult& result) {
+ return result.skipped();
+}
+
+// Returns true if and only if the test was skipped.
+bool TestResult::Skipped() const {
+ return !Failed() && CountIf(test_part_results_, TestPartSkipped) > 0;
+}
+
+// Returns true if and only if the test failed.
bool TestResult::Failed() const {
for (int i = 0; i < total_part_count(); ++i) {
if (GetTestPartResult(i).failed())
@@ -2175,22 +2206,22 @@ bool TestResult::Failed() const {
return false;
}
-// Returns true iff the test part fatally failed.
+// Returns true if and only if the test part fatally failed.
static bool TestPartFatallyFailed(const TestPartResult& result) {
return result.fatally_failed();
}
-// Returns true iff the test fatally failed.
+// Returns true if and only if the test fatally failed.
bool TestResult::HasFatalFailure() const {
return CountIf(test_part_results_, TestPartFatallyFailed) > 0;
}
-// Returns true iff the test part non-fatally failed.
+// Returns true if and only if the test part non-fatally failed.
static bool TestPartNonfatallyFailed(const TestPartResult& result) {
return result.nonfatally_failed();
}
-// Returns true iff the test has a non-fatal failure.
+// Returns true if and only if the test has a non-fatal failure.
bool TestResult::HasNonfatalFailure() const {
return CountIf(test_part_results_, TestPartNonfatallyFailed) > 0;
}
@@ -2253,25 +2284,25 @@ void ReportFailureInUnknownLocation(TestPartResult::Type result_type,
// AddTestPartResult.
UnitTest::GetInstance()->AddTestPartResult(
result_type,
- NULL, // No info about the source file where the exception occurred.
- -1, // We have no info on which line caused the exception.
+ nullptr, // No info about the source file where the exception occurred.
+ -1, // We have no info on which line caused the exception.
message,
- ""); // No stack trace, either.
+ ""); // No stack trace, either.
}
} // namespace internal
-// Google Test requires all tests in the same test case to use the same test
+// Google Test requires all tests in the same test suite to use the same test
// fixture class. This function checks if the current test has the
-// same fixture class as the first test in the current test case. If
+// same fixture class as the first test in the current test suite. If
// yes, it returns true; otherwise it generates a Google Test failure and
// returns false.
bool Test::HasSameFixtureClass() {
internal::UnitTestImpl* const impl = internal::GetUnitTestImpl();
- const TestCase* const test_case = impl->current_test_case();
+ const TestSuite* const test_suite = impl->current_test_suite();
- // Info about the first test in the current test case.
- const TestInfo* const first_test_info = test_case->test_info_list()[0];
+ // Info about the first test in the current test suite.
+ const TestInfo* const first_test_info = test_suite->test_info_list()[0];
const internal::TypeId first_fixture_id = first_test_info->fixture_class_id_;
const char* const first_test_name = first_test_info->name();
@@ -2287,7 +2318,7 @@ bool Test::HasSameFixtureClass() {
const bool this_is_TEST = this_fixture_id == internal::GetTestTypeId();
if (first_is_TEST || this_is_TEST) {
- // Both TEST and TEST_F appear in same test case, which is incorrect.
+ // Both TEST and TEST_F appear in same test suite, which is incorrect.
// Tell the user how to fix this.
// Gets the name of the TEST and the name of the TEST_F. Note
@@ -2299,9 +2330,9 @@ bool Test::HasSameFixtureClass() {
first_is_TEST ? this_test_name : first_test_name;
ADD_FAILURE()
- << "All tests in the same test case must use the same test fixture\n"
- << "class, so mixing TEST_F and TEST in the same test case is\n"
- << "illegal. In test case " << this_test_info->test_case_name()
+ << "All tests in the same test suite must use the same test fixture\n"
+ << "class, so mixing TEST_F and TEST in the same test suite is\n"
+ << "illegal. In test suite " << this_test_info->test_suite_name()
<< ",\n"
<< "test " << TEST_F_name << " is defined using TEST_F but\n"
<< "test " << TEST_name << " is defined using TEST. You probably\n"
@@ -2311,15 +2342,15 @@ bool Test::HasSameFixtureClass() {
// Two fixture classes with the same name appear in two different
// namespaces, which is not allowed. Tell the user how to fix this.
ADD_FAILURE()
- << "All tests in the same test case must use the same test fixture\n"
- << "class. However, in test case "
- << this_test_info->test_case_name() << ",\n"
- << "you defined test " << first_test_name
- << " and test " << this_test_name << "\n"
+ << "All tests in the same test suite must use the same test fixture\n"
+ << "class. However, in test suite "
+ << this_test_info->test_suite_name() << ",\n"
+ << "you defined test " << first_test_name << " and test "
+ << this_test_name << "\n"
<< "using two different test fixture classes. This can happen if\n"
<< "the two classes are from different namespaces or translation\n"
<< "units and have the same name. You should probably rename one\n"
- << "of the classes to put the tests into different test cases.";
+ << "of the classes to put the tests into different test suites.";
}
return false;
}
@@ -2352,7 +2383,7 @@ namespace internal {
static std::string FormatCxxExceptionMessage(const char* description,
const char* location) {
Message message;
- if (description != NULL) {
+ if (description != nullptr) {
message << "C++ exception with description \"" << description << "\"";
} else {
message << "Unknown C++ exception";
@@ -2436,6 +2467,8 @@ Result HandleExceptionsInMethodIfSupported(
#if GTEST_HAS_EXCEPTIONS
try {
return HandleSehExceptionsInMethodIfSupported(object, method, location);
+ } catch (const AssertionException&) { // NOLINT
+ // This failure was reported already.
} catch (const internal::GoogleTestFailureException&) { // NOLINT
// This exception type can only be thrown by a failed Google
// Test assertion with the intention of letting another testing
@@ -2448,7 +2481,7 @@ Result HandleExceptionsInMethodIfSupported(
} catch (...) { // NOLINT
internal::ReportFailureInUnknownLocation(
TestPartResult::kFatalFailure,
- FormatCxxExceptionMessage(NULL, location));
+ FormatCxxExceptionMessage(nullptr, location));
}
return static_cast<Result>(0);
#else
@@ -2468,8 +2501,9 @@ void Test::Run() {
internal::UnitTestImpl* const impl = internal::GetUnitTestImpl();
impl->os_stack_trace_getter()->UponLeavingGTest();
internal::HandleExceptionsInMethodIfSupported(this, &Test::SetUp, "SetUp()");
- // We will run the test only if SetUp() was successful.
- if (!HasFatalFailure()) {
+ // We will run the test only if SetUp() was successful and didn't call
+ // GTEST_SKIP().
+ if (!HasFatalFailure() && !IsSkipped()) {
impl->os_stack_trace_getter()->UponLeavingGTest();
internal::HandleExceptionsInMethodIfSupported(
this, &Test::TestBody, "the test body");
@@ -2483,32 +2517,36 @@ void Test::Run() {
this, &Test::TearDown, "TearDown()");
}
-// Returns true iff the current test has a fatal failure.
+// Returns true if and only if the current test has a fatal failure.
bool Test::HasFatalFailure() {
return internal::GetUnitTestImpl()->current_test_result()->HasFatalFailure();
}
-// Returns true iff the current test has a non-fatal failure.
+// Returns true if and only if the current test has a non-fatal failure.
bool Test::HasNonfatalFailure() {
return internal::GetUnitTestImpl()->current_test_result()->
HasNonfatalFailure();
}
+// Returns true if and only if the current test was skipped.
+bool Test::IsSkipped() {
+ return internal::GetUnitTestImpl()->current_test_result()->Skipped();
+}
+
// class TestInfo
// Constructs a TestInfo object. It assumes ownership of the test factory
// object.
-TestInfo::TestInfo(const std::string& a_test_case_name,
- const std::string& a_name,
- const char* a_type_param,
+TestInfo::TestInfo(const std::string& a_test_suite_name,
+ const std::string& a_name, const char* a_type_param,
const char* a_value_param,
internal::CodeLocation a_code_location,
internal::TypeId fixture_class_id,
internal::TestFactoryBase* factory)
- : test_case_name_(a_test_case_name),
+ : test_suite_name_(a_test_suite_name),
name_(a_name),
- type_param_(a_type_param ? new std::string(a_type_param) : NULL),
- value_param_(a_value_param ? new std::string(a_value_param) : NULL),
+ type_param_(a_type_param ? new std::string(a_type_param) : nullptr),
+ value_param_(a_value_param ? new std::string(a_value_param) : nullptr),
location_(a_code_location),
fixture_class_id_(fixture_class_id),
should_run_(false),
@@ -2527,7 +2565,7 @@ namespace internal {
//
// Arguments:
//
-// test_case_name: name of the test case
+// test_suite_name: name of the test suite
// name: name of the test
// type_param: the name of the test's type parameter, or NULL if
// this is not a typed or a type-parameterized test.
@@ -2535,49 +2573,40 @@ namespace internal {
// or NULL if this is not a value-parameterized test.
// code_location: code location where the test is defined
// fixture_class_id: ID of the test fixture class
-// set_up_tc: pointer to the function that sets up the test case
-// tear_down_tc: pointer to the function that tears down the test case
+// set_up_tc: pointer to the function that sets up the test suite
+// tear_down_tc: pointer to the function that tears down the test suite
// factory: pointer to the factory that creates a test object.
// The newly created TestInfo instance will assume
// ownership of the factory object.
TestInfo* MakeAndRegisterTestInfo(
- const char* test_case_name,
- const char* name,
- const char* type_param,
- const char* value_param,
- CodeLocation code_location,
- TypeId fixture_class_id,
- SetUpTestCaseFunc set_up_tc,
- TearDownTestCaseFunc tear_down_tc,
- TestFactoryBase* factory) {
+ const char* test_suite_name, const char* name, const char* type_param,
+ const char* value_param, CodeLocation code_location,
+ TypeId fixture_class_id, SetUpTestSuiteFunc set_up_tc,
+ TearDownTestSuiteFunc tear_down_tc, TestFactoryBase* factory) {
TestInfo* const test_info =
- new TestInfo(test_case_name, name, type_param, value_param,
+ new TestInfo(test_suite_name, name, type_param, value_param,
code_location, fixture_class_id, factory);
GetUnitTestImpl()->AddTestInfo(set_up_tc, tear_down_tc, test_info);
return test_info;
}
-#if GTEST_HAS_PARAM_TEST
-void ReportInvalidTestCaseType(const char* test_case_name,
- CodeLocation code_location) {
+void ReportInvalidTestSuiteType(const char* test_suite_name,
+ CodeLocation code_location) {
Message errors;
errors
- << "Attempted redefinition of test case " << test_case_name << ".\n"
- << "All tests in the same test case must use the same test fixture\n"
- << "class. However, in test case " << test_case_name << ", you tried\n"
+ << "Attempted redefinition of test suite " << test_suite_name << ".\n"
+ << "All tests in the same test suite must use the same test fixture\n"
+ << "class. However, in test suite " << test_suite_name << ", you tried\n"
<< "to define a test using a fixture class different from the one\n"
<< "used earlier. This can happen if the two fixture classes are\n"
<< "from different namespaces and have the same name. You should\n"
<< "probably rename one of the classes to put the tests into different\n"
- << "test cases.";
+ << "test suites.";
- fprintf(stderr, "%s %s",
- FormatFileLocation(code_location.file.c_str(),
- code_location.line).c_str(),
- errors.GetString().c_str());
+ GTEST_LOG_(ERROR) << FormatFileLocation(code_location.file.c_str(),
+ code_location.line)
+ << " " << errors.GetString();
}
-#endif // GTEST_HAS_PARAM_TEST
-
} // namespace internal
namespace {
@@ -2585,7 +2614,7 @@ namespace {
// A predicate that checks the test name of a TestInfo against a known
// value.
//
-// This is used for implementation of the TestCase class only. We put
+// This is used for implementation of the TestSuite class only. We put
// it in the anonymous namespace to prevent polluting the outer
// namespace.
//
@@ -2598,7 +2627,7 @@ class TestNameIs {
explicit TestNameIs(const char* name)
: name_(name) {}
- // Returns true iff the test name of test_info matches name_.
+ // Returns true if and only if the test name of test_info matches name_.
bool operator()(const TestInfo * test_info) const {
return test_info && test_info->name() == name_;
}
@@ -2612,15 +2641,13 @@ class TestNameIs {
namespace internal {
// This method expands all parameterized tests registered with macros TEST_P
-// and INSTANTIATE_TEST_CASE_P into regular tests and registers those.
+// and INSTANTIATE_TEST_SUITE_P into regular tests and registers those.
// This will be done just once during the program runtime.
void UnitTestImpl::RegisterParameterizedTests() {
-#if GTEST_HAS_PARAM_TEST
if (!parameterized_tests_registered_) {
parameterized_test_registry_.RegisterTests();
parameterized_tests_registered_ = true;
}
-#endif
}
} // namespace internal
@@ -2648,19 +2675,23 @@ void TestInfo::Run() {
factory_, &internal::TestFactoryBase::CreateTest,
"the test fixture's constructor");
- // Runs the test only if the test object was created and its
- // constructor didn't generate a fatal failure.
- if ((test != NULL) && !Test::HasFatalFailure()) {
+ // Runs the test if the constructor didn't generate a fatal failure or invoke
+ // GTEST_SKIP().
+ // Note that the object will not be null
+ if (!Test::HasFatalFailure() && !Test::IsSkipped()) {
// This doesn't throw as all user code that can throw are wrapped into
// exception handling code.
test->Run();
}
- // Deletes the test object.
- impl->os_stack_trace_getter()->UponLeavingGTest();
- internal::HandleExceptionsInMethodIfSupported(
- test, &Test::DeleteSelf_, "the test fixture's destructor");
+ if (test != nullptr) {
+ // Deletes the test object.
+ impl->os_stack_trace_getter()->UponLeavingGTest();
+ internal::HandleExceptionsInMethodIfSupported(
+ test, &Test::DeleteSelf_, "the test fixture's destructor");
+ }
+ result_.set_start_timestamp(start);
result_.set_elapsed_time(internal::GetTimeInMillis() - start);
// Notifies the unit test event listener that a test has just finished.
@@ -2668,134 +2699,151 @@ void TestInfo::Run() {
// Tells UnitTest to stop associating assertion results to this
// test.
- impl->set_current_test_info(NULL);
+ impl->set_current_test_info(nullptr);
}
-// class TestCase
+// class TestSuite
-// Gets the number of successful tests in this test case.
-int TestCase::successful_test_count() const {
+// Gets the number of successful tests in this test suite.
+int TestSuite::successful_test_count() const {
return CountIf(test_info_list_, TestPassed);
}
-// Gets the number of failed tests in this test case.
-int TestCase::failed_test_count() const {
+// Gets the number of successful tests in this test suite.
+int TestSuite::skipped_test_count() const {
+ return CountIf(test_info_list_, TestSkipped);
+}
+
+// Gets the number of failed tests in this test suite.
+int TestSuite::failed_test_count() const {
return CountIf(test_info_list_, TestFailed);
}
// Gets the number of disabled tests that will be reported in the XML report.
-int TestCase::reportable_disabled_test_count() const {
+int TestSuite::reportable_disabled_test_count() const {
return CountIf(test_info_list_, TestReportableDisabled);
}
-// Gets the number of disabled tests in this test case.
-int TestCase::disabled_test_count() const {
+// Gets the number of disabled tests in this test suite.
+int TestSuite::disabled_test_count() const {
return CountIf(test_info_list_, TestDisabled);
}
// Gets the number of tests to be printed in the XML report.
-int TestCase::reportable_test_count() const {
+int TestSuite::reportable_test_count() const {
return CountIf(test_info_list_, TestReportable);
}
-// Get the number of tests in this test case that should run.
-int TestCase::test_to_run_count() const {
+// Get the number of tests in this test suite that should run.
+int TestSuite::test_to_run_count() const {
return CountIf(test_info_list_, ShouldRunTest);
}
// Gets the number of all tests.
-int TestCase::total_test_count() const {
+int TestSuite::total_test_count() const {
return static_cast<int>(test_info_list_.size());
}
-// Creates a TestCase with the given name.
+// Creates a TestSuite with the given name.
//
// Arguments:
//
-// name: name of the test case
-// a_type_param: the name of the test case's type parameter, or NULL if
-// this is not a typed or a type-parameterized test case.
-// set_up_tc: pointer to the function that sets up the test case
-// tear_down_tc: pointer to the function that tears down the test case
-TestCase::TestCase(const char* a_name, const char* a_type_param,
- Test::SetUpTestCaseFunc set_up_tc,
- Test::TearDownTestCaseFunc tear_down_tc)
+// name: name of the test suite
+// a_type_param: the name of the test suite's type parameter, or NULL if
+// this is not a typed or a type-parameterized test suite.
+// set_up_tc: pointer to the function that sets up the test suite
+// tear_down_tc: pointer to the function that tears down the test suite
+TestSuite::TestSuite(const char* a_name, const char* a_type_param,
+ internal::SetUpTestSuiteFunc set_up_tc,
+ internal::TearDownTestSuiteFunc tear_down_tc)
: name_(a_name),
- type_param_(a_type_param ? new std::string(a_type_param) : NULL),
+ type_param_(a_type_param ? new std::string(a_type_param) : nullptr),
set_up_tc_(set_up_tc),
tear_down_tc_(tear_down_tc),
should_run_(false),
- elapsed_time_(0) {
-}
+ start_timestamp_(0),
+ elapsed_time_(0) {}
-// Destructor of TestCase.
-TestCase::~TestCase() {
+// Destructor of TestSuite.
+TestSuite::~TestSuite() {
// Deletes every Test in the collection.
ForEach(test_info_list_, internal::Delete<TestInfo>);
}
// Returns the i-th test among all the tests. i can range from 0 to
// total_test_count() - 1. If i is not in that range, returns NULL.
-const TestInfo* TestCase::GetTestInfo(int i) const {
+const TestInfo* TestSuite::GetTestInfo(int i) const {
const int index = GetElementOr(test_indices_, i, -1);
- return index < 0 ? NULL : test_info_list_[index];
+ return index < 0 ? nullptr : test_info_list_[static_cast<size_t>(index)];
}
// Returns the i-th test among all the tests. i can range from 0 to
// total_test_count() - 1. If i is not in that range, returns NULL.
-TestInfo* TestCase::GetMutableTestInfo(int i) {
+TestInfo* TestSuite::GetMutableTestInfo(int i) {
const int index = GetElementOr(test_indices_, i, -1);
- return index < 0 ? NULL : test_info_list_[index];
+ return index < 0 ? nullptr : test_info_list_[static_cast<size_t>(index)];
}
-// Adds a test to this test case. Will delete the test upon
-// destruction of the TestCase object.
-void TestCase::AddTestInfo(TestInfo * test_info) {
+// Adds a test to this test suite. Will delete the test upon
+// destruction of the TestSuite object.
+void TestSuite::AddTestInfo(TestInfo* test_info) {
test_info_list_.push_back(test_info);
test_indices_.push_back(static_cast<int>(test_indices_.size()));
}
-// Runs every test in this TestCase.
-void TestCase::Run() {
+// Runs every test in this TestSuite.
+void TestSuite::Run() {
if (!should_run_) return;
internal::UnitTestImpl* const impl = internal::GetUnitTestImpl();
- impl->set_current_test_case(this);
+ impl->set_current_test_suite(this);
TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater();
+ // Call both legacy and the new API
+ repeater->OnTestSuiteStart(*this);
+// Legacy API is deprecated but still available
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI
repeater->OnTestCaseStart(*this);
+#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI
+
impl->os_stack_trace_getter()->UponLeavingGTest();
internal::HandleExceptionsInMethodIfSupported(
- this, &TestCase::RunSetUpTestCase, "SetUpTestCase()");
+ this, &TestSuite::RunSetUpTestSuite, "SetUpTestSuite()");
- const internal::TimeInMillis start = internal::GetTimeInMillis();
+ start_timestamp_ = internal::GetTimeInMillis();
for (int i = 0; i < total_test_count(); i++) {
GetMutableTestInfo(i)->Run();
}
- elapsed_time_ = internal::GetTimeInMillis() - start;
+ elapsed_time_ = internal::GetTimeInMillis() - start_timestamp_;
impl->os_stack_trace_getter()->UponLeavingGTest();
internal::HandleExceptionsInMethodIfSupported(
- this, &TestCase::RunTearDownTestCase, "TearDownTestCase()");
+ this, &TestSuite::RunTearDownTestSuite, "TearDownTestSuite()");
+ // Call both legacy and the new API
+ repeater->OnTestSuiteEnd(*this);
+// Legacy API is deprecated but still available
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI
repeater->OnTestCaseEnd(*this);
- impl->set_current_test_case(NULL);
+#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI
+
+ impl->set_current_test_suite(nullptr);
}
-// Clears the results of all tests in this test case.
-void TestCase::ClearResult() {
+// Clears the results of all tests in this test suite.
+void TestSuite::ClearResult() {
ad_hoc_test_result_.Clear();
ForEach(test_info_list_, TestInfo::ClearTestResult);
}
-// Shuffles the tests in this test case.
-void TestCase::ShuffleTests(internal::Random* random) {
+// Shuffles the tests in this test suite.
+void TestSuite::ShuffleTests(internal::Random* random) {
Shuffle(random, &test_indices_);
}
// Restores the test order to before the first shuffle.
-void TestCase::UnshuffleTests() {
+void TestSuite::UnshuffleTests() {
for (size_t i = 0; i < test_indices_.size(); i++) {
test_indices_[i] = static_cast<int>(i);
}
@@ -2818,9 +2866,9 @@ static std::string FormatTestCount(int test_count) {
return FormatCountableNoun(test_count, "test", "tests");
}
-// Formats the count of test cases.
-static std::string FormatTestCaseCount(int test_case_count) {
- return FormatCountableNoun(test_case_count, "test case", "test cases");
+// Formats the count of test suites.
+static std::string FormatTestSuiteCount(int test_suite_count) {
+ return FormatCountableNoun(test_suite_count, "test suite", "test suites");
}
// Converts a TestPartResult::Type enum to human-friendly string
@@ -2829,6 +2877,8 @@ static std::string FormatTestCaseCount(int test_case_count) {
// between the two when viewing the test result.
static const char * TestPartResultTypeToString(TestPartResult::Type type) {
switch (type) {
+ case TestPartResult::kSkip:
+ return "Skipped";
case TestPartResult::kSuccess:
return "Success";
@@ -2876,19 +2926,11 @@ static void PrintTestPartResult(const TestPartResult& test_part_result) {
}
// class PrettyUnitTestResultPrinter
-
-enum GTestColor {
- COLOR_DEFAULT,
- COLOR_RED,
- COLOR_GREEN,
- COLOR_YELLOW
-};
-
#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE && \
- !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT
+ !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT && !GTEST_OS_WINDOWS_MINGW
// Returns the character attribute for the given color.
-WORD GetColorAttribute(GTestColor color) {
+static WORD GetColorAttribute(GTestColor color) {
switch (color) {
case COLOR_RED: return FOREGROUND_RED;
case COLOR_GREEN: return FOREGROUND_GREEN;
@@ -2897,27 +2939,59 @@ WORD GetColorAttribute(GTestColor color) {
}
}
+static int GetBitOffset(WORD color_mask) {
+ if (color_mask == 0) return 0;
+
+ int bitOffset = 0;
+ while ((color_mask & 1) == 0) {
+ color_mask >>= 1;
+ ++bitOffset;
+ }
+ return bitOffset;
+}
+
+static WORD GetNewColor(GTestColor color, WORD old_color_attrs) {
+ // Let's reuse the BG
+ static const WORD background_mask = BACKGROUND_BLUE | BACKGROUND_GREEN |
+ BACKGROUND_RED | BACKGROUND_INTENSITY;
+ static const WORD foreground_mask = FOREGROUND_BLUE | FOREGROUND_GREEN |
+ FOREGROUND_RED | FOREGROUND_INTENSITY;
+ const WORD existing_bg = old_color_attrs & background_mask;
+
+ WORD new_color =
+ GetColorAttribute(color) | existing_bg | FOREGROUND_INTENSITY;
+ static const int bg_bitOffset = GetBitOffset(background_mask);
+ static const int fg_bitOffset = GetBitOffset(foreground_mask);
+
+ if (((new_color & background_mask) >> bg_bitOffset) ==
+ ((new_color & foreground_mask) >> fg_bitOffset)) {
+ new_color ^= FOREGROUND_INTENSITY; // invert intensity
+ }
+ return new_color;
+}
+
#else
// Returns the ANSI color code for the given color. COLOR_DEFAULT is
// an invalid input.
-const char* GetAnsiColorCode(GTestColor color) {
+static const char* GetAnsiColorCode(GTestColor color) {
switch (color) {
case COLOR_RED: return "1";
case COLOR_GREEN: return "2";
case COLOR_YELLOW: return "3";
- default: return NULL;
- };
+ default:
+ return nullptr;
+ }
}
#endif // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE
-// Returns true iff Google Test should use colors in the output.
+// Returns true if and only if Google Test should use colors in the output.
bool ShouldUseColor(bool stdout_is_tty) {
const char* const gtest_color = GTEST_FLAG(color).c_str();
if (String::CaseInsensitiveCStringEquals(gtest_color, "auto")) {
-#if GTEST_OS_WINDOWS
+#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MINGW
// On Windows the TERM variable is usually not set, but the
// console there does support colors.
return stdout_is_tty;
@@ -2957,15 +3031,14 @@ void ColoredPrintf(GTestColor color, const char* fmt, ...) {
va_list args;
va_start(args, fmt);
-#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN || GTEST_OS_ZOS || \
- GTEST_OS_IOS || GTEST_OS_WINDOWS_PHONE || GTEST_OS_WINDOWS_RT
+#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_ZOS || GTEST_OS_IOS || \
+ GTEST_OS_WINDOWS_PHONE || GTEST_OS_WINDOWS_RT || defined(ESP_PLATFORM)
const bool use_color = AlwaysFalse();
#else
static const bool in_color_mode =
ShouldUseColor(posix::IsATTY(posix::FileNo(stdout)) != 0);
const bool use_color = in_color_mode && (color != COLOR_DEFAULT);
-#endif // GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN || GTEST_OS_ZOS
- // The '!= 0' comparison is necessary to satisfy MSVC 7.1.
+#endif // GTEST_OS_WINDOWS_MOBILE || GTEST_OS_ZOS
if (!use_color) {
vprintf(fmt, args);
@@ -2974,20 +3047,21 @@ void ColoredPrintf(GTestColor color, const char* fmt, ...) {
}
#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE && \
- !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT
+ !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT && !GTEST_OS_WINDOWS_MINGW
const HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE);
// Gets the current text color.
CONSOLE_SCREEN_BUFFER_INFO buffer_info;
GetConsoleScreenBufferInfo(stdout_handle, &buffer_info);
const WORD old_color_attrs = buffer_info.wAttributes;
+ const WORD new_color = GetNewColor(color, old_color_attrs);
// We need to flush the stream buffers into the console before each
// SetConsoleTextAttribute call lest it affect the text that is already
// printed but has not yet reached the console.
fflush(stdout);
- SetConsoleTextAttribute(stdout_handle,
- GetColorAttribute(color) | FOREGROUND_INTENSITY);
+ SetConsoleTextAttribute(stdout_handle, new_color);
+
vprintf(fmt, args);
fflush(stdout);
@@ -3001,23 +3075,22 @@ void ColoredPrintf(GTestColor color, const char* fmt, ...) {
va_end(args);
}
-// Text printed in Google Test's text output and --gunit_list_tests
+// Text printed in Google Test's text output and --gtest_list_tests
// output to label the type parameter and value parameter for a test.
static const char kTypeParamLabel[] = "TypeParam";
static const char kValueParamLabel[] = "GetParam()";
-void PrintFullTestCommentIfPresent(const TestInfo& test_info) {
+static void PrintFullTestCommentIfPresent(const TestInfo& test_info) {
const char* const type_param = test_info.type_param();
const char* const value_param = test_info.value_param();
- if (type_param != NULL || value_param != NULL) {
+ if (type_param != nullptr || value_param != nullptr) {
printf(", where ");
- if (type_param != NULL) {
+ if (type_param != nullptr) {
printf("%s = %s", kTypeParamLabel, type_param);
- if (value_param != NULL)
- printf(" and ");
+ if (value_param != nullptr) printf(" and ");
}
- if (value_param != NULL) {
+ if (value_param != nullptr) {
printf("%s = %s", kValueParamLabel, value_param);
}
}
@@ -3029,27 +3102,39 @@ void PrintFullTestCommentIfPresent(const TestInfo& test_info) {
class PrettyUnitTestResultPrinter : public TestEventListener {
public:
PrettyUnitTestResultPrinter() {}
- static void PrintTestName(const char * test_case, const char * test) {
- printf("%s.%s", test_case, test);
+ static void PrintTestName(const char* test_suite, const char* test) {
+ printf("%s.%s", test_suite, test);
}
// The following methods override what's in the TestEventListener class.
- virtual void OnTestProgramStart(const UnitTest& /*unit_test*/) {}
- virtual void OnTestIterationStart(const UnitTest& unit_test, int iteration);
- virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test);
- virtual void OnEnvironmentsSetUpEnd(const UnitTest& /*unit_test*/) {}
- virtual void OnTestCaseStart(const TestCase& test_case);
- virtual void OnTestStart(const TestInfo& test_info);
- virtual void OnTestPartResult(const TestPartResult& result);
- virtual void OnTestEnd(const TestInfo& test_info);
- virtual void OnTestCaseEnd(const TestCase& test_case);
- virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test);
- virtual void OnEnvironmentsTearDownEnd(const UnitTest& /*unit_test*/) {}
- virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration);
- virtual void OnTestProgramEnd(const UnitTest& /*unit_test*/) {}
+ void OnTestProgramStart(const UnitTest& /*unit_test*/) override {}
+ void OnTestIterationStart(const UnitTest& unit_test, int iteration) override;
+ void OnEnvironmentsSetUpStart(const UnitTest& unit_test) override;
+ void OnEnvironmentsSetUpEnd(const UnitTest& /*unit_test*/) override {}
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+ void OnTestCaseStart(const TestCase& test_case) override;
+#else
+ void OnTestSuiteStart(const TestSuite& test_suite) override;
+#endif // OnTestCaseStart
+
+ void OnTestStart(const TestInfo& test_info) override;
+
+ void OnTestPartResult(const TestPartResult& result) override;
+ void OnTestEnd(const TestInfo& test_info) override;
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+ void OnTestCaseEnd(const TestCase& test_case) override;
+#else
+ void OnTestSuiteEnd(const TestSuite& test_suite) override;
+#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+
+ void OnEnvironmentsTearDownStart(const UnitTest& unit_test) override;
+ void OnEnvironmentsTearDownEnd(const UnitTest& /*unit_test*/) override {}
+ void OnTestIterationEnd(const UnitTest& unit_test, int iteration) override;
+ void OnTestProgramEnd(const UnitTest& /*unit_test*/) override {}
private:
static void PrintFailedTests(const UnitTest& unit_test);
+ static void PrintSkippedTests(const UnitTest& unit_test);
};
// Fired before each iteration of tests starts.
@@ -3084,7 +3169,7 @@ void PrettyUnitTestResultPrinter::OnTestIterationStart(
ColoredPrintf(COLOR_GREEN, "[==========] ");
printf("Running %s from %s.\n",
FormatTestCount(unit_test.test_to_run_count()).c_str(),
- FormatTestCaseCount(unit_test.test_case_to_run_count()).c_str());
+ FormatTestSuiteCount(unit_test.test_suite_to_run_count()).c_str());
fflush(stdout);
}
@@ -3095,22 +3180,38 @@ void PrettyUnitTestResultPrinter::OnEnvironmentsSetUpStart(
fflush(stdout);
}
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
void PrettyUnitTestResultPrinter::OnTestCaseStart(const TestCase& test_case) {
const std::string counts =
FormatCountableNoun(test_case.test_to_run_count(), "test", "tests");
ColoredPrintf(COLOR_GREEN, "[----------] ");
printf("%s from %s", counts.c_str(), test_case.name());
- if (test_case.type_param() == NULL) {
+ if (test_case.type_param() == nullptr) {
printf("\n");
} else {
printf(", where %s = %s\n", kTypeParamLabel, test_case.type_param());
}
fflush(stdout);
}
+#else
+void PrettyUnitTestResultPrinter::OnTestSuiteStart(
+ const TestSuite& test_suite) {
+ const std::string counts =
+ FormatCountableNoun(test_suite.test_to_run_count(), "test", "tests");
+ ColoredPrintf(COLOR_GREEN, "[----------] ");
+ printf("%s from %s", counts.c_str(), test_suite.name());
+ if (test_suite.type_param() == nullptr) {
+ printf("\n");
+ } else {
+ printf(", where %s = %s\n", kTypeParamLabel, test_suite.type_param());
+ }
+ fflush(stdout);
+}
+#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
void PrettyUnitTestResultPrinter::OnTestStart(const TestInfo& test_info) {
ColoredPrintf(COLOR_GREEN, "[ RUN ] ");
- PrintTestName(test_info.test_case_name(), test_info.name());
+ PrintTestName(test_info.test_suite_name(), test_info.name());
printf("\n");
fflush(stdout);
}
@@ -3118,22 +3219,29 @@ void PrettyUnitTestResultPrinter::OnTestStart(const TestInfo& test_info) {
// Called after an assertion failure.
void PrettyUnitTestResultPrinter::OnTestPartResult(
const TestPartResult& result) {
- // If the test part succeeded, we don't need to do anything.
- if (result.type() == TestPartResult::kSuccess)
- return;
-
- // Print failure message from the assertion (e.g. expected this and got that).
- PrintTestPartResult(result);
- fflush(stdout);
+ switch (result.type()) {
+ // If the test part succeeded, or was skipped,
+ // we don't need to do anything.
+ case TestPartResult::kSkip:
+ case TestPartResult::kSuccess:
+ return;
+ default:
+ // Print failure message from the assertion
+ // (e.g. expected this and got that).
+ PrintTestPartResult(result);
+ fflush(stdout);
+ }
}
void PrettyUnitTestResultPrinter::OnTestEnd(const TestInfo& test_info) {
if (test_info.result()->Passed()) {
ColoredPrintf(COLOR_GREEN, "[ OK ] ");
+ } else if (test_info.result()->Skipped()) {
+ ColoredPrintf(COLOR_GREEN, "[ SKIPPED ] ");
} else {
ColoredPrintf(COLOR_RED, "[ FAILED ] ");
}
- PrintTestName(test_info.test_case_name(), test_info.name());
+ PrintTestName(test_info.test_suite_name(), test_info.name());
if (test_info.result()->Failed())
PrintFullTestCommentIfPresent(test_info);
@@ -3146,17 +3254,29 @@ void PrettyUnitTestResultPrinter::OnTestEnd(const TestInfo& test_info) {
fflush(stdout);
}
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
void PrettyUnitTestResultPrinter::OnTestCaseEnd(const TestCase& test_case) {
if (!GTEST_FLAG(print_time)) return;
const std::string counts =
FormatCountableNoun(test_case.test_to_run_count(), "test", "tests");
ColoredPrintf(COLOR_GREEN, "[----------] ");
- printf("%s from %s (%s ms total)\n\n",
- counts.c_str(), test_case.name(),
+ printf("%s from %s (%s ms total)\n\n", counts.c_str(), test_case.name(),
internal::StreamableToString(test_case.elapsed_time()).c_str());
fflush(stdout);
}
+#else
+void PrettyUnitTestResultPrinter::OnTestSuiteEnd(const TestSuite& test_suite) {
+ if (!GTEST_FLAG(print_time)) return;
+
+ const std::string counts =
+ FormatCountableNoun(test_suite.test_to_run_count(), "test", "tests");
+ ColoredPrintf(COLOR_GREEN, "[----------] ");
+ printf("%s from %s (%s ms total)\n\n", counts.c_str(), test_suite.name(),
+ internal::StreamableToString(test_suite.elapsed_time()).c_str());
+ fflush(stdout);
+}
+#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
void PrettyUnitTestResultPrinter::OnEnvironmentsTearDownStart(
const UnitTest& /*unit_test*/) {
@@ -3172,30 +3292,54 @@ void PrettyUnitTestResultPrinter::PrintFailedTests(const UnitTest& unit_test) {
return;
}
- for (int i = 0; i < unit_test.total_test_case_count(); ++i) {
- const TestCase& test_case = *unit_test.GetTestCase(i);
- if (!test_case.should_run() || (test_case.failed_test_count() == 0)) {
+ for (int i = 0; i < unit_test.total_test_suite_count(); ++i) {
+ const TestSuite& test_suite = *unit_test.GetTestSuite(i);
+ if (!test_suite.should_run() || (test_suite.failed_test_count() == 0)) {
continue;
}
- for (int j = 0; j < test_case.total_test_count(); ++j) {
- const TestInfo& test_info = *test_case.GetTestInfo(j);
- if (!test_info.should_run() || test_info.result()->Passed()) {
+ for (int j = 0; j < test_suite.total_test_count(); ++j) {
+ const TestInfo& test_info = *test_suite.GetTestInfo(j);
+ if (!test_info.should_run() || !test_info.result()->Failed()) {
continue;
}
ColoredPrintf(COLOR_RED, "[ FAILED ] ");
- printf("%s.%s", test_case.name(), test_info.name());
+ printf("%s.%s", test_suite.name(), test_info.name());
PrintFullTestCommentIfPresent(test_info);
printf("\n");
}
}
}
+// Internal helper for printing the list of skipped tests.
+void PrettyUnitTestResultPrinter::PrintSkippedTests(const UnitTest& unit_test) {
+ const int skipped_test_count = unit_test.skipped_test_count();
+ if (skipped_test_count == 0) {
+ return;
+ }
+
+ for (int i = 0; i < unit_test.total_test_suite_count(); ++i) {
+ const TestSuite& test_suite = *unit_test.GetTestSuite(i);
+ if (!test_suite.should_run() || (test_suite.skipped_test_count() == 0)) {
+ continue;
+ }
+ for (int j = 0; j < test_suite.total_test_count(); ++j) {
+ const TestInfo& test_info = *test_suite.GetTestInfo(j);
+ if (!test_info.should_run() || !test_info.result()->Skipped()) {
+ continue;
+ }
+ ColoredPrintf(COLOR_GREEN, "[ SKIPPED ] ");
+ printf("%s.%s", test_suite.name(), test_info.name());
+ printf("\n");
+ }
+ }
+}
+
void PrettyUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test,
int /*iteration*/) {
ColoredPrintf(COLOR_GREEN, "[==========] ");
printf("%s from %s ran.",
FormatTestCount(unit_test.test_to_run_count()).c_str(),
- FormatTestCaseCount(unit_test.test_case_to_run_count()).c_str());
+ FormatTestSuiteCount(unit_test.test_suite_to_run_count()).c_str());
if (GTEST_FLAG(print_time)) {
printf(" (%s ms total)",
internal::StreamableToString(unit_test.elapsed_time()).c_str());
@@ -3204,6 +3348,13 @@ void PrettyUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test,
ColoredPrintf(COLOR_GREEN, "[ PASSED ] ");
printf("%s.\n", FormatTestCount(unit_test.successful_test_count()).c_str());
+ const int skipped_test_count = unit_test.skipped_test_count();
+ if (skipped_test_count > 0) {
+ ColoredPrintf(COLOR_GREEN, "[ SKIPPED ] ");
+ printf("%s, listed below:\n", FormatTestCount(skipped_test_count).c_str());
+ PrintSkippedTests(unit_test);
+ }
+
int num_failures = unit_test.failed_test_count();
if (!unit_test.Passed()) {
const int failed_test_count = unit_test.failed_test_count();
@@ -3236,7 +3387,7 @@ void PrettyUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test,
class TestEventRepeater : public TestEventListener {
public:
TestEventRepeater() : forwarding_enabled_(true) {}
- virtual ~TestEventRepeater();
+ ~TestEventRepeater() override;
void Append(TestEventListener *listener);
TestEventListener* Release(TestEventListener* listener);
@@ -3245,19 +3396,27 @@ class TestEventRepeater : public TestEventListener {
bool forwarding_enabled() const { return forwarding_enabled_; }
void set_forwarding_enabled(bool enable) { forwarding_enabled_ = enable; }
- virtual void OnTestProgramStart(const UnitTest& unit_test);
- virtual void OnTestIterationStart(const UnitTest& unit_test, int iteration);
- virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test);
- virtual void OnEnvironmentsSetUpEnd(const UnitTest& unit_test);
- virtual void OnTestCaseStart(const TestCase& test_case);
- virtual void OnTestStart(const TestInfo& test_info);
- virtual void OnTestPartResult(const TestPartResult& result);
- virtual void OnTestEnd(const TestInfo& test_info);
- virtual void OnTestCaseEnd(const TestCase& test_case);
- virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test);
- virtual void OnEnvironmentsTearDownEnd(const UnitTest& unit_test);
- virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration);
- virtual void OnTestProgramEnd(const UnitTest& unit_test);
+ void OnTestProgramStart(const UnitTest& unit_test) override;
+ void OnTestIterationStart(const UnitTest& unit_test, int iteration) override;
+ void OnEnvironmentsSetUpStart(const UnitTest& unit_test) override;
+ void OnEnvironmentsSetUpEnd(const UnitTest& unit_test) override;
+// Legacy API is deprecated but still available
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+ void OnTestCaseStart(const TestSuite& parameter) override;
+#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+ void OnTestSuiteStart(const TestSuite& parameter) override;
+ void OnTestStart(const TestInfo& test_info) override;
+ void OnTestPartResult(const TestPartResult& result) override;
+ void OnTestEnd(const TestInfo& test_info) override;
+// Legacy API is deprecated but still available
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+ void OnTestCaseEnd(const TestCase& parameter) override;
+#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+ void OnTestSuiteEnd(const TestSuite& parameter) override;
+ void OnEnvironmentsTearDownStart(const UnitTest& unit_test) override;
+ void OnEnvironmentsTearDownEnd(const UnitTest& unit_test) override;
+ void OnTestIterationEnd(const UnitTest& unit_test, int iteration) override;
+ void OnTestProgramEnd(const UnitTest& unit_test) override;
private:
// Controls whether events will be forwarded to listeners_. Set to false
@@ -3277,16 +3436,15 @@ void TestEventRepeater::Append(TestEventListener *listener) {
listeners_.push_back(listener);
}
-// TODO(vladl@google.com): Factor the search functionality into Vector::Find.
TestEventListener* TestEventRepeater::Release(TestEventListener *listener) {
for (size_t i = 0; i < listeners_.size(); ++i) {
if (listeners_[i] == listener) {
- listeners_.erase(listeners_.begin() + i);
+ listeners_.erase(listeners_.begin() + static_cast<int>(i));
return listener;
}
}
- return NULL;
+ return nullptr;
}
// Since most methods are very similar, use macros to reduce boilerplate.
@@ -3301,25 +3459,33 @@ void TestEventRepeater::Name(const Type& parameter) { \
}
// This defines a member that forwards the call to all listeners in reverse
// order.
-#define GTEST_REVERSE_REPEATER_METHOD_(Name, Type) \
-void TestEventRepeater::Name(const Type& parameter) { \
- if (forwarding_enabled_) { \
- for (int i = static_cast<int>(listeners_.size()) - 1; i >= 0; i--) { \
- listeners_[i]->Name(parameter); \
- } \
- } \
-}
+#define GTEST_REVERSE_REPEATER_METHOD_(Name, Type) \
+ void TestEventRepeater::Name(const Type& parameter) { \
+ if (forwarding_enabled_) { \
+ for (size_t i = listeners_.size(); i != 0; i--) { \
+ listeners_[i - 1]->Name(parameter); \
+ } \
+ } \
+ }
GTEST_REPEATER_METHOD_(OnTestProgramStart, UnitTest)
GTEST_REPEATER_METHOD_(OnEnvironmentsSetUpStart, UnitTest)
-GTEST_REPEATER_METHOD_(OnTestCaseStart, TestCase)
+// Legacy API is deprecated but still available
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+GTEST_REPEATER_METHOD_(OnTestCaseStart, TestSuite)
+#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+GTEST_REPEATER_METHOD_(OnTestSuiteStart, TestSuite)
GTEST_REPEATER_METHOD_(OnTestStart, TestInfo)
GTEST_REPEATER_METHOD_(OnTestPartResult, TestPartResult)
GTEST_REPEATER_METHOD_(OnEnvironmentsTearDownStart, UnitTest)
GTEST_REVERSE_REPEATER_METHOD_(OnEnvironmentsSetUpEnd, UnitTest)
GTEST_REVERSE_REPEATER_METHOD_(OnEnvironmentsTearDownEnd, UnitTest)
GTEST_REVERSE_REPEATER_METHOD_(OnTestEnd, TestInfo)
-GTEST_REVERSE_REPEATER_METHOD_(OnTestCaseEnd, TestCase)
+// Legacy API is deprecated but still available
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+GTEST_REVERSE_REPEATER_METHOD_(OnTestCaseEnd, TestSuite)
+#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+GTEST_REVERSE_REPEATER_METHOD_(OnTestSuiteEnd, TestSuite)
GTEST_REVERSE_REPEATER_METHOD_(OnTestProgramEnd, UnitTest)
#undef GTEST_REPEATER_METHOD_
@@ -3337,8 +3503,8 @@ void TestEventRepeater::OnTestIterationStart(const UnitTest& unit_test,
void TestEventRepeater::OnTestIterationEnd(const UnitTest& unit_test,
int iteration) {
if (forwarding_enabled_) {
- for (int i = static_cast<int>(listeners_.size()) - 1; i >= 0; i--) {
- listeners_[i]->OnTestIterationEnd(unit_test, iteration);
+ for (size_t i = listeners_.size(); i > 0; i--) {
+ listeners_[i - 1]->OnTestIterationEnd(unit_test, iteration);
}
}
}
@@ -3350,7 +3516,12 @@ class XmlUnitTestResultPrinter : public EmptyTestEventListener {
public:
explicit XmlUnitTestResultPrinter(const char* output_file);
- virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration);
+ void OnTestIterationEnd(const UnitTest& unit_test, int iteration) override;
+ void ListTestsMatchingFilter(const std::vector<TestSuite*>& test_suites);
+
+ // Prints an XML summary of all unit tests.
+ static void PrintXmlTestsList(std::ostream* stream,
+ const std::vector<TestSuite*>& test_suites);
private:
// Is c a whitespace character that is normalized to a space character
@@ -3395,12 +3566,12 @@ class XmlUnitTestResultPrinter : public EmptyTestEventListener {
// Streams an XML representation of a TestInfo object.
static void OutputXmlTestInfo(::std::ostream* stream,
- const char* test_case_name,
+ const char* test_suite_name,
const TestInfo& test_info);
- // Prints an XML representation of a TestCase object
- static void PrintXmlTestCase(::std::ostream* stream,
- const TestCase& test_case);
+ // Prints an XML representation of a TestSuite object
+ static void PrintXmlTestSuite(::std::ostream* stream,
+ const TestSuite& test_suite);
// Prints an XML summary of unit_test to output stream out.
static void PrintXmlUnitTest(::std::ostream* stream,
@@ -3412,6 +3583,11 @@ class XmlUnitTestResultPrinter : public EmptyTestEventListener {
// to delimit this attribute from prior attributes.
static std::string TestPropertiesAsXmlAttributes(const TestResult& result);
+ // Streams an XML representation of the test properties of a TestResult
+ // object.
+ static void OutputXmlTestProperties(std::ostream* stream,
+ const TestResult& result);
+
// The output file.
const std::string output_file_;
@@ -3421,46 +3597,30 @@ class XmlUnitTestResultPrinter : public EmptyTestEventListener {
// Creates a new XmlUnitTestResultPrinter.
XmlUnitTestResultPrinter::XmlUnitTestResultPrinter(const char* output_file)
: output_file_(output_file) {
- if (output_file_.c_str() == NULL || output_file_.empty()) {
- fprintf(stderr, "XML output file may not be null\n");
- fflush(stderr);
- exit(EXIT_FAILURE);
+ if (output_file_.empty()) {
+ GTEST_LOG_(FATAL) << "XML output file may not be null";
}
}
// Called after the unit test ends.
void XmlUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test,
int /*iteration*/) {
- FILE* xmlout = NULL;
- FilePath output_file(output_file_);
- FilePath output_dir(output_file.RemoveFileName());
-
- if (output_dir.CreateDirectoriesRecursively()) {
- xmlout = posix::FOpen(output_file_.c_str(), "w");
- }
- if (xmlout == NULL) {
- // TODO(wan): report the reason of the failure.
- //
- // We don't do it for now as:
- //
- // 1. There is no urgent need for it.
- // 2. It's a bit involved to make the errno variable thread-safe on
- // all three operating systems (Linux, Windows, and Mac OS).
- // 3. To interpret the meaning of errno in a thread-safe way,
- // we need the strerror_r() function, which is not available on
- // Windows.
- fprintf(stderr,
- "Unable to open file \"%s\"\n",
- output_file_.c_str());
- fflush(stderr);
- exit(EXIT_FAILURE);
- }
+ FILE* xmlout = OpenFileForWriting(output_file_);
std::stringstream stream;
PrintXmlUnitTest(&stream, unit_test);
fprintf(xmlout, "%s", StringStreamToString(&stream).c_str());
fclose(xmlout);
}
+void XmlUnitTestResultPrinter::ListTestsMatchingFilter(
+ const std::vector<TestSuite*>& test_suites) {
+ FILE* xmlout = OpenFileForWriting(output_file_);
+ std::stringstream stream;
+ PrintXmlTestsList(&stream, test_suites);
+ fprintf(xmlout, "%s", StringStreamToString(&stream).c_str());
+ fclose(xmlout);
+}
+
// Returns an XML-escaped copy of the input string str. If is_attribute
// is true, the text is meant to appear as an attribute value, and
// normalizable whitespace is preserved by replacing it with character
@@ -3471,8 +3631,6 @@ void XmlUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test,
// module will consist of ordinary English text.
// If this module is ever modified to produce version 1.1 XML output,
// most invalid characters can be retained using character references.
-// TODO(wan): It might be nice to have a minimally invasive, human-readable
-// escaping scheme for invalid characters, rather than dropping them.
std::string XmlUnitTestResultPrinter::EscapeXml(
const std::string& str, bool is_attribute) {
Message m;
@@ -3532,11 +3690,12 @@ std::string XmlUnitTestResultPrinter::RemoveInvalidXmlCharacters(
// The following routines generate an XML representation of a UnitTest
// object.
+// GOOGLETEST_CM0009 DO NOT DELETE
//
// This is how Google Test concepts map to the DTD:
//
// <testsuites name="AllTests"> <-- corresponds to a UnitTest object
-// <testsuite name="testcase-name"> <-- corresponds to a TestCase object
+// <testsuite name="testcase-name"> <-- corresponds to a TestSuite object
// <testcase name="test-name"> <-- corresponds to a TestInfo object
// <failure message="...">...</failure>
// <failure message="...">...</failure>
@@ -3560,12 +3719,11 @@ static bool PortableLocaltime(time_t seconds, struct tm* out) {
// MINGW <time.h> provides neither localtime_r nor localtime_s, but uses
// Windows' localtime(), which has a thread-local tm buffer.
struct tm* tm_ptr = localtime(&seconds); // NOLINT
- if (tm_ptr == NULL)
- return false;
+ if (tm_ptr == nullptr) return false;
*out = *tm_ptr;
return true;
#else
- return localtime_r(&seconds, out) != NULL;
+ return localtime_r(&seconds, out) != nullptr;
#endif
}
@@ -3591,7 +3749,7 @@ void XmlUnitTestResultPrinter::OutputXmlCDataSection(::std::ostream* stream,
*stream << "<![CDATA[";
for (;;) {
const char* const next_segment = strstr(segment, "]]>");
- if (next_segment != NULL) {
+ if (next_segment != nullptr) {
stream->write(
segment, static_cast<std::streamsize>(next_segment - segment));
*stream << "]]>]]&gt;<![CDATA[";
@@ -3610,7 +3768,7 @@ void XmlUnitTestResultPrinter::OutputXmlAttribute(
const std::string& name,
const std::string& value) {
const std::vector<std::string>& allowed_names =
- GetReservedAttributesForElement(element_name);
+ GetReservedOutputAttributesForElement(element_name);
GTEST_CHECK_(std::find(allowed_names.begin(), allowed_names.end(), name) !=
allowed_names.end())
@@ -3621,30 +3779,47 @@ void XmlUnitTestResultPrinter::OutputXmlAttribute(
}
// Prints an XML representation of a TestInfo object.
-// TODO(wan): There is also value in printing properties with the plain printer.
void XmlUnitTestResultPrinter::OutputXmlTestInfo(::std::ostream* stream,
- const char* test_case_name,
+ const char* test_suite_name,
const TestInfo& test_info) {
const TestResult& result = *test_info.result();
- const std::string kTestcase = "testcase";
+ const std::string kTestsuite = "testcase";
+
+ if (test_info.is_in_another_shard()) {
+ return;
+ }
*stream << " <testcase";
- OutputXmlAttribute(stream, kTestcase, "name", test_info.name());
+ OutputXmlAttribute(stream, kTestsuite, "name", test_info.name());
- if (test_info.value_param() != NULL) {
- OutputXmlAttribute(stream, kTestcase, "value_param",
+ if (test_info.value_param() != nullptr) {
+ OutputXmlAttribute(stream, kTestsuite, "value_param",
test_info.value_param());
}
- if (test_info.type_param() != NULL) {
- OutputXmlAttribute(stream, kTestcase, "type_param", test_info.type_param());
+ if (test_info.type_param() != nullptr) {
+ OutputXmlAttribute(stream, kTestsuite, "type_param",
+ test_info.type_param());
+ }
+ if (GTEST_FLAG(list_tests)) {
+ OutputXmlAttribute(stream, kTestsuite, "file", test_info.file());
+ OutputXmlAttribute(stream, kTestsuite, "line",
+ StreamableToString(test_info.line()));
+ *stream << " />\n";
+ return;
}
- OutputXmlAttribute(stream, kTestcase, "status",
+ OutputXmlAttribute(stream, kTestsuite, "status",
test_info.should_run() ? "run" : "notrun");
- OutputXmlAttribute(stream, kTestcase, "time",
+ OutputXmlAttribute(stream, kTestsuite, "result",
+ test_info.should_run()
+ ? (result.Skipped() ? "skipped" : "completed")
+ : "suppressed");
+ OutputXmlAttribute(stream, kTestsuite, "time",
FormatTimeInMillisAsSeconds(result.elapsed_time()));
- OutputXmlAttribute(stream, kTestcase, "classname", test_case_name);
- *stream << TestPropertiesAsXmlAttributes(result);
+ OutputXmlAttribute(
+ stream, kTestsuite, "timestamp",
+ FormatEpochTimeInMillisAsIso8601(result.start_timestamp()));
+ OutputXmlAttribute(stream, kTestsuite, "classname", test_suite_name);
int failures = 0;
for (int i = 0; i < result.total_part_count(); ++i) {
@@ -3653,46 +3828,56 @@ void XmlUnitTestResultPrinter::OutputXmlTestInfo(::std::ostream* stream,
if (++failures == 1) {
*stream << ">\n";
}
- const string location = internal::FormatCompilerIndependentFileLocation(
- part.file_name(), part.line_number());
- const string summary = location + "\n" + part.summary();
+ const std::string location =
+ internal::FormatCompilerIndependentFileLocation(part.file_name(),
+ part.line_number());
+ const std::string summary = location + "\n" + part.summary();
*stream << " <failure message=\""
<< EscapeXmlAttribute(summary.c_str())
<< "\" type=\"\">";
- const string detail = location + "\n" + part.message();
+ const std::string detail = location + "\n" + part.message();
OutputXmlCDataSection(stream, RemoveInvalidXmlCharacters(detail).c_str());
*stream << "</failure>\n";
}
}
- if (failures == 0)
+ if (failures == 0 && result.test_property_count() == 0) {
*stream << " />\n";
- else
+ } else {
+ if (failures == 0) {
+ *stream << ">\n";
+ }
+ OutputXmlTestProperties(stream, result);
*stream << " </testcase>\n";
+ }
}
-// Prints an XML representation of a TestCase object
-void XmlUnitTestResultPrinter::PrintXmlTestCase(std::ostream* stream,
- const TestCase& test_case) {
+// Prints an XML representation of a TestSuite object
+void XmlUnitTestResultPrinter::PrintXmlTestSuite(std::ostream* stream,
+ const TestSuite& test_suite) {
const std::string kTestsuite = "testsuite";
*stream << " <" << kTestsuite;
- OutputXmlAttribute(stream, kTestsuite, "name", test_case.name());
+ OutputXmlAttribute(stream, kTestsuite, "name", test_suite.name());
OutputXmlAttribute(stream, kTestsuite, "tests",
- StreamableToString(test_case.reportable_test_count()));
- OutputXmlAttribute(stream, kTestsuite, "failures",
- StreamableToString(test_case.failed_test_count()));
- OutputXmlAttribute(
- stream, kTestsuite, "disabled",
- StreamableToString(test_case.reportable_disabled_test_count()));
- OutputXmlAttribute(stream, kTestsuite, "errors", "0");
- OutputXmlAttribute(stream, kTestsuite, "time",
- FormatTimeInMillisAsSeconds(test_case.elapsed_time()));
- *stream << TestPropertiesAsXmlAttributes(test_case.ad_hoc_test_result())
- << ">\n";
-
- for (int i = 0; i < test_case.total_test_count(); ++i) {
- if (test_case.GetTestInfo(i)->is_reportable())
- OutputXmlTestInfo(stream, test_case.name(), *test_case.GetTestInfo(i));
+ StreamableToString(test_suite.reportable_test_count()));
+ if (!GTEST_FLAG(list_tests)) {
+ OutputXmlAttribute(stream, kTestsuite, "failures",
+ StreamableToString(test_suite.failed_test_count()));
+ OutputXmlAttribute(
+ stream, kTestsuite, "disabled",
+ StreamableToString(test_suite.reportable_disabled_test_count()));
+ OutputXmlAttribute(stream, kTestsuite, "errors", "0");
+ OutputXmlAttribute(stream, kTestsuite, "time",
+ FormatTimeInMillisAsSeconds(test_suite.elapsed_time()));
+ OutputXmlAttribute(
+ stream, kTestsuite, "timestamp",
+ FormatEpochTimeInMillisAsIso8601(test_suite.start_timestamp()));
+ *stream << TestPropertiesAsXmlAttributes(test_suite.ad_hoc_test_result());
+ }
+ *stream << ">\n";
+ for (int i = 0; i < test_suite.total_test_count(); ++i) {
+ if (test_suite.GetTestInfo(i)->is_reportable())
+ OutputXmlTestInfo(stream, test_suite.name(), *test_suite.GetTestInfo(i));
}
*stream << " </" << kTestsuite << ">\n";
}
@@ -3713,25 +3898,46 @@ void XmlUnitTestResultPrinter::PrintXmlUnitTest(std::ostream* stream,
stream, kTestsuites, "disabled",
StreamableToString(unit_test.reportable_disabled_test_count()));
OutputXmlAttribute(stream, kTestsuites, "errors", "0");
+ OutputXmlAttribute(stream, kTestsuites, "time",
+ FormatTimeInMillisAsSeconds(unit_test.elapsed_time()));
OutputXmlAttribute(
stream, kTestsuites, "timestamp",
FormatEpochTimeInMillisAsIso8601(unit_test.start_timestamp()));
- OutputXmlAttribute(stream, kTestsuites, "time",
- FormatTimeInMillisAsSeconds(unit_test.elapsed_time()));
if (GTEST_FLAG(shuffle)) {
OutputXmlAttribute(stream, kTestsuites, "random_seed",
StreamableToString(unit_test.random_seed()));
}
-
*stream << TestPropertiesAsXmlAttributes(unit_test.ad_hoc_test_result());
OutputXmlAttribute(stream, kTestsuites, "name", "AllTests");
*stream << ">\n";
- for (int i = 0; i < unit_test.total_test_case_count(); ++i) {
- if (unit_test.GetTestCase(i)->reportable_test_count() > 0)
- PrintXmlTestCase(stream, *unit_test.GetTestCase(i));
+ for (int i = 0; i < unit_test.total_test_suite_count(); ++i) {
+ if (unit_test.GetTestSuite(i)->reportable_test_count() > 0)
+ PrintXmlTestSuite(stream, *unit_test.GetTestSuite(i));
+ }
+ *stream << "</" << kTestsuites << ">\n";
+}
+
+void XmlUnitTestResultPrinter::PrintXmlTestsList(
+ std::ostream* stream, const std::vector<TestSuite*>& test_suites) {
+ const std::string kTestsuites = "testsuites";
+
+ *stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
+ *stream << "<" << kTestsuites;
+
+ int total_tests = 0;
+ for (auto test_suite : test_suites) {
+ total_tests += test_suite->total_test_count();
+ }
+ OutputXmlAttribute(stream, kTestsuites, "tests",
+ StreamableToString(total_tests));
+ OutputXmlAttribute(stream, kTestsuites, "name", "AllTests");
+ *stream << ">\n";
+
+ for (auto test_suite : test_suites) {
+ PrintXmlTestSuite(stream, *test_suite);
}
*stream << "</" << kTestsuites << ">\n";
}
@@ -3749,8 +3955,403 @@ std::string XmlUnitTestResultPrinter::TestPropertiesAsXmlAttributes(
return attributes.GetString();
}
+void XmlUnitTestResultPrinter::OutputXmlTestProperties(
+ std::ostream* stream, const TestResult& result) {
+ const std::string kProperties = "properties";
+ const std::string kProperty = "property";
+
+ if (result.test_property_count() <= 0) {
+ return;
+ }
+
+ *stream << "<" << kProperties << ">\n";
+ for (int i = 0; i < result.test_property_count(); ++i) {
+ const TestProperty& property = result.GetTestProperty(i);
+ *stream << "<" << kProperty;
+ *stream << " name=\"" << EscapeXmlAttribute(property.key()) << "\"";
+ *stream << " value=\"" << EscapeXmlAttribute(property.value()) << "\"";
+ *stream << "/>\n";
+ }
+ *stream << "</" << kProperties << ">\n";
+}
+
// End XmlUnitTestResultPrinter
+// This class generates an JSON output file.
+class JsonUnitTestResultPrinter : public EmptyTestEventListener {
+ public:
+ explicit JsonUnitTestResultPrinter(const char* output_file);
+
+ void OnTestIterationEnd(const UnitTest& unit_test, int iteration) override;
+
+ // Prints an JSON summary of all unit tests.
+ static void PrintJsonTestList(::std::ostream* stream,
+ const std::vector<TestSuite*>& test_suites);
+
+ private:
+ // Returns an JSON-escaped copy of the input string str.
+ static std::string EscapeJson(const std::string& str);
+
+ //// Verifies that the given attribute belongs to the given element and
+ //// streams the attribute as JSON.
+ static void OutputJsonKey(std::ostream* stream,
+ const std::string& element_name,
+ const std::string& name,
+ const std::string& value,
+ const std::string& indent,
+ bool comma = true);
+ static void OutputJsonKey(std::ostream* stream,
+ const std::string& element_name,
+ const std::string& name,
+ int value,
+ const std::string& indent,
+ bool comma = true);
+
+ // Streams a JSON representation of a TestInfo object.
+ static void OutputJsonTestInfo(::std::ostream* stream,
+ const char* test_suite_name,
+ const TestInfo& test_info);
+
+ // Prints a JSON representation of a TestSuite object
+ static void PrintJsonTestSuite(::std::ostream* stream,
+ const TestSuite& test_suite);
+
+ // Prints a JSON summary of unit_test to output stream out.
+ static void PrintJsonUnitTest(::std::ostream* stream,
+ const UnitTest& unit_test);
+
+ // Produces a string representing the test properties in a result as
+ // a JSON dictionary.
+ static std::string TestPropertiesAsJson(const TestResult& result,
+ const std::string& indent);
+
+ // The output file.
+ const std::string output_file_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(JsonUnitTestResultPrinter);
+};
+
+// Creates a new JsonUnitTestResultPrinter.
+JsonUnitTestResultPrinter::JsonUnitTestResultPrinter(const char* output_file)
+ : output_file_(output_file) {
+ if (output_file_.empty()) {
+ GTEST_LOG_(FATAL) << "JSON output file may not be null";
+ }
+}
+
+void JsonUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test,
+ int /*iteration*/) {
+ FILE* jsonout = OpenFileForWriting(output_file_);
+ std::stringstream stream;
+ PrintJsonUnitTest(&stream, unit_test);
+ fprintf(jsonout, "%s", StringStreamToString(&stream).c_str());
+ fclose(jsonout);
+}
+
+// Returns an JSON-escaped copy of the input string str.
+std::string JsonUnitTestResultPrinter::EscapeJson(const std::string& str) {
+ Message m;
+
+ for (size_t i = 0; i < str.size(); ++i) {
+ const char ch = str[i];
+ switch (ch) {
+ case '\\':
+ case '"':
+ case '/':
+ m << '\\' << ch;
+ break;
+ case '\b':
+ m << "\\b";
+ break;
+ case '\t':
+ m << "\\t";
+ break;
+ case '\n':
+ m << "\\n";
+ break;
+ case '\f':
+ m << "\\f";
+ break;
+ case '\r':
+ m << "\\r";
+ break;
+ default:
+ if (ch < ' ') {
+ m << "\\u00" << String::FormatByte(static_cast<unsigned char>(ch));
+ } else {
+ m << ch;
+ }
+ break;
+ }
+ }
+
+ return m.GetString();
+}
+
+// The following routines generate an JSON representation of a UnitTest
+// object.
+
+// Formats the given time in milliseconds as seconds.
+static std::string FormatTimeInMillisAsDuration(TimeInMillis ms) {
+ ::std::stringstream ss;
+ ss << (static_cast<double>(ms) * 1e-3) << "s";
+ return ss.str();
+}
+
+// Converts the given epoch time in milliseconds to a date string in the
+// RFC3339 format, without the timezone information.
+static std::string FormatEpochTimeInMillisAsRFC3339(TimeInMillis ms) {
+ struct tm time_struct;
+ if (!PortableLocaltime(static_cast<time_t>(ms / 1000), &time_struct))
+ return "";
+ // YYYY-MM-DDThh:mm:ss
+ return StreamableToString(time_struct.tm_year + 1900) + "-" +
+ String::FormatIntWidth2(time_struct.tm_mon + 1) + "-" +
+ String::FormatIntWidth2(time_struct.tm_mday) + "T" +
+ String::FormatIntWidth2(time_struct.tm_hour) + ":" +
+ String::FormatIntWidth2(time_struct.tm_min) + ":" +
+ String::FormatIntWidth2(time_struct.tm_sec) + "Z";
+}
+
+static inline std::string Indent(size_t width) {
+ return std::string(width, ' ');
+}
+
+void JsonUnitTestResultPrinter::OutputJsonKey(
+ std::ostream* stream,
+ const std::string& element_name,
+ const std::string& name,
+ const std::string& value,
+ const std::string& indent,
+ bool comma) {
+ const std::vector<std::string>& allowed_names =
+ GetReservedOutputAttributesForElement(element_name);
+
+ GTEST_CHECK_(std::find(allowed_names.begin(), allowed_names.end(), name) !=
+ allowed_names.end())
+ << "Key \"" << name << "\" is not allowed for value \"" << element_name
+ << "\".";
+
+ *stream << indent << "\"" << name << "\": \"" << EscapeJson(value) << "\"";
+ if (comma)
+ *stream << ",\n";
+}
+
+void JsonUnitTestResultPrinter::OutputJsonKey(
+ std::ostream* stream,
+ const std::string& element_name,
+ const std::string& name,
+ int value,
+ const std::string& indent,
+ bool comma) {
+ const std::vector<std::string>& allowed_names =
+ GetReservedOutputAttributesForElement(element_name);
+
+ GTEST_CHECK_(std::find(allowed_names.begin(), allowed_names.end(), name) !=
+ allowed_names.end())
+ << "Key \"" << name << "\" is not allowed for value \"" << element_name
+ << "\".";
+
+ *stream << indent << "\"" << name << "\": " << StreamableToString(value);
+ if (comma)
+ *stream << ",\n";
+}
+
+// Prints a JSON representation of a TestInfo object.
+void JsonUnitTestResultPrinter::OutputJsonTestInfo(::std::ostream* stream,
+ const char* test_suite_name,
+ const TestInfo& test_info) {
+ const TestResult& result = *test_info.result();
+ const std::string kTestsuite = "testcase";
+ const std::string kIndent = Indent(10);
+
+ *stream << Indent(8) << "{\n";
+ OutputJsonKey(stream, kTestsuite, "name", test_info.name(), kIndent);
+
+ if (test_info.value_param() != nullptr) {
+ OutputJsonKey(stream, kTestsuite, "value_param", test_info.value_param(),
+ kIndent);
+ }
+ if (test_info.type_param() != nullptr) {
+ OutputJsonKey(stream, kTestsuite, "type_param", test_info.type_param(),
+ kIndent);
+ }
+ if (GTEST_FLAG(list_tests)) {
+ OutputJsonKey(stream, kTestsuite, "file", test_info.file(), kIndent);
+ OutputJsonKey(stream, kTestsuite, "line", test_info.line(), kIndent, false);
+ *stream << "\n" << Indent(8) << "}";
+ return;
+ }
+
+ OutputJsonKey(stream, kTestsuite, "status",
+ test_info.should_run() ? "RUN" : "NOTRUN", kIndent);
+ OutputJsonKey(stream, kTestsuite, "result",
+ test_info.should_run()
+ ? (result.Skipped() ? "SKIPPED" : "COMPLETED")
+ : "SUPPRESSED",
+ kIndent);
+ OutputJsonKey(stream, kTestsuite, "timestamp",
+ FormatEpochTimeInMillisAsRFC3339(result.start_timestamp()),
+ kIndent);
+ OutputJsonKey(stream, kTestsuite, "time",
+ FormatTimeInMillisAsDuration(result.elapsed_time()), kIndent);
+ OutputJsonKey(stream, kTestsuite, "classname", test_suite_name, kIndent,
+ false);
+ *stream << TestPropertiesAsJson(result, kIndent);
+
+ int failures = 0;
+ for (int i = 0; i < result.total_part_count(); ++i) {
+ const TestPartResult& part = result.GetTestPartResult(i);
+ if (part.failed()) {
+ *stream << ",\n";
+ if (++failures == 1) {
+ *stream << kIndent << "\"" << "failures" << "\": [\n";
+ }
+ const std::string location =
+ internal::FormatCompilerIndependentFileLocation(part.file_name(),
+ part.line_number());
+ const std::string message = EscapeJson(location + "\n" + part.message());
+ *stream << kIndent << " {\n"
+ << kIndent << " \"failure\": \"" << message << "\",\n"
+ << kIndent << " \"type\": \"\"\n"
+ << kIndent << " }";
+ }
+ }
+
+ if (failures > 0)
+ *stream << "\n" << kIndent << "]";
+ *stream << "\n" << Indent(8) << "}";
+}
+
+// Prints an JSON representation of a TestSuite object
+void JsonUnitTestResultPrinter::PrintJsonTestSuite(
+ std::ostream* stream, const TestSuite& test_suite) {
+ const std::string kTestsuite = "testsuite";
+ const std::string kIndent = Indent(6);
+
+ *stream << Indent(4) << "{\n";
+ OutputJsonKey(stream, kTestsuite, "name", test_suite.name(), kIndent);
+ OutputJsonKey(stream, kTestsuite, "tests", test_suite.reportable_test_count(),
+ kIndent);
+ if (!GTEST_FLAG(list_tests)) {
+ OutputJsonKey(stream, kTestsuite, "failures",
+ test_suite.failed_test_count(), kIndent);
+ OutputJsonKey(stream, kTestsuite, "disabled",
+ test_suite.reportable_disabled_test_count(), kIndent);
+ OutputJsonKey(stream, kTestsuite, "errors", 0, kIndent);
+ OutputJsonKey(
+ stream, kTestsuite, "timestamp",
+ FormatEpochTimeInMillisAsRFC3339(test_suite.start_timestamp()),
+ kIndent);
+ OutputJsonKey(stream, kTestsuite, "time",
+ FormatTimeInMillisAsDuration(test_suite.elapsed_time()),
+ kIndent, false);
+ *stream << TestPropertiesAsJson(test_suite.ad_hoc_test_result(), kIndent)
+ << ",\n";
+ }
+
+ *stream << kIndent << "\"" << kTestsuite << "\": [\n";
+
+ bool comma = false;
+ for (int i = 0; i < test_suite.total_test_count(); ++i) {
+ if (test_suite.GetTestInfo(i)->is_reportable()) {
+ if (comma) {
+ *stream << ",\n";
+ } else {
+ comma = true;
+ }
+ OutputJsonTestInfo(stream, test_suite.name(), *test_suite.GetTestInfo(i));
+ }
+ }
+ *stream << "\n" << kIndent << "]\n" << Indent(4) << "}";
+}
+
+// Prints a JSON summary of unit_test to output stream out.
+void JsonUnitTestResultPrinter::PrintJsonUnitTest(std::ostream* stream,
+ const UnitTest& unit_test) {
+ const std::string kTestsuites = "testsuites";
+ const std::string kIndent = Indent(2);
+ *stream << "{\n";
+
+ OutputJsonKey(stream, kTestsuites, "tests", unit_test.reportable_test_count(),
+ kIndent);
+ OutputJsonKey(stream, kTestsuites, "failures", unit_test.failed_test_count(),
+ kIndent);
+ OutputJsonKey(stream, kTestsuites, "disabled",
+ unit_test.reportable_disabled_test_count(), kIndent);
+ OutputJsonKey(stream, kTestsuites, "errors", 0, kIndent);
+ if (GTEST_FLAG(shuffle)) {
+ OutputJsonKey(stream, kTestsuites, "random_seed", unit_test.random_seed(),
+ kIndent);
+ }
+ OutputJsonKey(stream, kTestsuites, "timestamp",
+ FormatEpochTimeInMillisAsRFC3339(unit_test.start_timestamp()),
+ kIndent);
+ OutputJsonKey(stream, kTestsuites, "time",
+ FormatTimeInMillisAsDuration(unit_test.elapsed_time()), kIndent,
+ false);
+
+ *stream << TestPropertiesAsJson(unit_test.ad_hoc_test_result(), kIndent)
+ << ",\n";
+
+ OutputJsonKey(stream, kTestsuites, "name", "AllTests", kIndent);
+ *stream << kIndent << "\"" << kTestsuites << "\": [\n";
+
+ bool comma = false;
+ for (int i = 0; i < unit_test.total_test_suite_count(); ++i) {
+ if (unit_test.GetTestSuite(i)->reportable_test_count() > 0) {
+ if (comma) {
+ *stream << ",\n";
+ } else {
+ comma = true;
+ }
+ PrintJsonTestSuite(stream, *unit_test.GetTestSuite(i));
+ }
+ }
+
+ *stream << "\n" << kIndent << "]\n" << "}\n";
+}
+
+void JsonUnitTestResultPrinter::PrintJsonTestList(
+ std::ostream* stream, const std::vector<TestSuite*>& test_suites) {
+ const std::string kTestsuites = "testsuites";
+ const std::string kIndent = Indent(2);
+ *stream << "{\n";
+ int total_tests = 0;
+ for (auto test_suite : test_suites) {
+ total_tests += test_suite->total_test_count();
+ }
+ OutputJsonKey(stream, kTestsuites, "tests", total_tests, kIndent);
+
+ OutputJsonKey(stream, kTestsuites, "name", "AllTests", kIndent);
+ *stream << kIndent << "\"" << kTestsuites << "\": [\n";
+
+ for (size_t i = 0; i < test_suites.size(); ++i) {
+ if (i != 0) {
+ *stream << ",\n";
+ }
+ PrintJsonTestSuite(stream, *test_suites[i]);
+ }
+
+ *stream << "\n"
+ << kIndent << "]\n"
+ << "}\n";
+}
+// Produces a string representing the test properties in a result as
+// a JSON dictionary.
+std::string JsonUnitTestResultPrinter::TestPropertiesAsJson(
+ const TestResult& result, const std::string& indent) {
+ Message attributes;
+ for (int i = 0; i < result.test_property_count(); ++i) {
+ const TestProperty& property = result.GetTestProperty(i);
+ attributes << ",\n" << indent << "\"" << property.key() << "\": "
+ << "\"" << EscapeJson(property.value()) << "\"";
+ }
+ return attributes.GetString();
+}
+
+// End JsonUnitTestResultPrinter
+
#if GTEST_CAN_STREAM_RESULTS_
// Checks if str contains '=', '&', '%' or '\n' characters. If yes,
@@ -3758,8 +4359,8 @@ std::string XmlUnitTestResultPrinter::TestPropertiesAsXmlAttributes(
// example, replaces "=" with "%3D". This algorithm is O(strlen(str))
// in both time and space -- important as the input str may contain an
// arbitrarily long test failure message and stack trace.
-string StreamingListener::UrlEncode(const char* str) {
- string result;
+std::string StreamingListener::UrlEncode(const char* str) {
+ std::string result;
result.reserve(strlen(str) + 1);
for (char ch = *str; ch != '\0'; ch = *++str) {
switch (ch) {
@@ -3785,7 +4386,7 @@ void StreamingListener::SocketWriter::MakeConnection() {
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC; // To allow both IPv4 and IPv6 addresses.
hints.ai_socktype = SOCK_STREAM;
- addrinfo* servinfo = NULL;
+ addrinfo* servinfo = nullptr;
// Use the getaddrinfo() to get a linked list of IP addresses for
// the given host name.
@@ -3797,7 +4398,7 @@ void StreamingListener::SocketWriter::MakeConnection() {
}
// Loop through all the results and connect to the first we can.
- for (addrinfo* cur_addr = servinfo; sockfd_ == -1 && cur_addr != NULL;
+ for (addrinfo* cur_addr = servinfo; sockfd_ == -1 && cur_addr != nullptr;
cur_addr = cur_addr->ai_next) {
sockfd_ = socket(
cur_addr->ai_family, cur_addr->ai_socktype, cur_addr->ai_protocol);
@@ -3821,47 +4422,82 @@ void StreamingListener::SocketWriter::MakeConnection() {
// End of class Streaming Listener
#endif // GTEST_CAN_STREAM_RESULTS__
-// Class ScopedTrace
+// class OsStackTraceGetter
-// Pushes the given source file location and message onto a per-thread
-// trace stack maintained by Google Test.
-ScopedTrace::ScopedTrace(const char* file, int line, const Message& message)
- GTEST_LOCK_EXCLUDED_(&UnitTest::mutex_) {
- TraceInfo trace;
- trace.file = file;
- trace.line = line;
- trace.message = message.GetString();
+const char* const OsStackTraceGetterInterface::kElidedFramesMarker =
+ "... " GTEST_NAME_ " internal frames ...";
- UnitTest::GetInstance()->PushGTestTrace(trace);
-}
+std::string OsStackTraceGetter::CurrentStackTrace(int max_depth, int skip_count)
+ GTEST_LOCK_EXCLUDED_(mutex_) {
+#if GTEST_HAS_ABSL
+ std::string result;
-// Pops the info pushed by the c'tor.
-ScopedTrace::~ScopedTrace()
- GTEST_LOCK_EXCLUDED_(&UnitTest::mutex_) {
- UnitTest::GetInstance()->PopGTestTrace();
-}
+ if (max_depth <= 0) {
+ return result;
+ }
+ max_depth = std::min(max_depth, kMaxStackTraceDepth);
-// class OsStackTraceGetter
+ std::vector<void*> raw_stack(max_depth);
+ // Skips the frames requested by the caller, plus this function.
+ const int raw_stack_size =
+ absl::GetStackTrace(&raw_stack[0], max_depth, skip_count + 1);
-const char* const OsStackTraceGetterInterface::kElidedFramesMarker =
- "... " GTEST_NAME_ " internal frames ...";
+ void* caller_frame = nullptr;
+ {
+ MutexLock lock(&mutex_);
+ caller_frame = caller_frame_;
+ }
+
+ for (int i = 0; i < raw_stack_size; ++i) {
+ if (raw_stack[i] == caller_frame &&
+ !GTEST_FLAG(show_internal_stack_frames)) {
+ // Add a marker to the trace and stop adding frames.
+ absl::StrAppend(&result, kElidedFramesMarker, "\n");
+ break;
+ }
+
+ char tmp[1024];
+ const char* symbol = "(unknown)";
+ if (absl::Symbolize(raw_stack[i], tmp, sizeof(tmp))) {
+ symbol = tmp;
+ }
+
+ char line[1024];
+ snprintf(line, sizeof(line), " %p: %s\n", raw_stack[i], symbol);
+ result += line;
+ }
-string OsStackTraceGetter::CurrentStackTrace(int /*max_depth*/,
- int /*skip_count*/) {
+ return result;
+
+#else // !GTEST_HAS_ABSL
+ static_cast<void>(max_depth);
+ static_cast<void>(skip_count);
return "";
+#endif // GTEST_HAS_ABSL
}
-void OsStackTraceGetter::UponLeavingGTest() {}
+void OsStackTraceGetter::UponLeavingGTest() GTEST_LOCK_EXCLUDED_(mutex_) {
+#if GTEST_HAS_ABSL
+ void* caller_frame = nullptr;
+ if (absl::GetStackTrace(&caller_frame, 1, 3) <= 0) {
+ caller_frame = nullptr;
+ }
+
+ MutexLock lock(&mutex_);
+ caller_frame_ = caller_frame;
+#endif // GTEST_HAS_ABSL
+}
// A helper class that creates the premature-exit file in its
// constructor and deletes the file in its destructor.
class ScopedPrematureExitFile {
public:
explicit ScopedPrematureExitFile(const char* premature_exit_filepath)
- : premature_exit_filepath_(premature_exit_filepath) {
+ : premature_exit_filepath_(premature_exit_filepath ?
+ premature_exit_filepath : "") {
// If a path to the premature-exit file is specified...
- if (premature_exit_filepath != NULL && *premature_exit_filepath != '\0') {
+ if (!premature_exit_filepath_.empty()) {
// create the file with a single "0" character in it. I/O
// errors are ignored as there's nothing better we can do and we
// don't want to fail the test because of this.
@@ -3872,13 +4508,18 @@ class ScopedPrematureExitFile {
}
~ScopedPrematureExitFile() {
- if (premature_exit_filepath_ != NULL && *premature_exit_filepath_ != '\0') {
- remove(premature_exit_filepath_);
+ if (!premature_exit_filepath_.empty()) {
+ int retval = remove(premature_exit_filepath_.c_str());
+ if (retval) {
+ GTEST_LOG_(ERROR) << "Failed to remove premature exit filepath \""
+ << premature_exit_filepath_ << "\" with error "
+ << retval;
+ }
}
}
private:
- const char* const premature_exit_filepath_;
+ const std::string premature_exit_filepath_;
GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedPrematureExitFile);
};
@@ -3889,9 +4530,8 @@ class ScopedPrematureExitFile {
TestEventListeners::TestEventListeners()
: repeater_(new internal::TestEventRepeater()),
- default_result_printer_(NULL),
- default_xml_generator_(NULL) {
-}
+ default_result_printer_(nullptr),
+ default_xml_generator_(nullptr) {}
TestEventListeners::~TestEventListeners() { delete repeater_; }
@@ -3908,9 +4548,9 @@ void TestEventListeners::Append(TestEventListener* listener) {
// NULL if the listener is not found in the list.
TestEventListener* TestEventListeners::Release(TestEventListener* listener) {
if (listener == default_result_printer_)
- default_result_printer_ = NULL;
+ default_result_printer_ = nullptr;
else if (listener == default_xml_generator_)
- default_xml_generator_ = NULL;
+ default_xml_generator_ = nullptr;
return repeater_->Release(listener);
}
@@ -3929,8 +4569,7 @@ void TestEventListeners::SetDefaultResultPrinter(TestEventListener* listener) {
// list.
delete Release(default_result_printer_);
default_result_printer_ = listener;
- if (listener != NULL)
- Append(listener);
+ if (listener != nullptr) Append(listener);
}
}
@@ -3945,8 +4584,7 @@ void TestEventListeners::SetDefaultXmlGenerator(TestEventListener* listener) {
// list.
delete Release(default_xml_generator_);
default_xml_generator_ = listener;
- if (listener != NULL)
- Append(listener);
+ if (listener != nullptr) Append(listener);
}
}
@@ -3970,52 +4608,66 @@ void TestEventListeners::SuppressEventForwarding() {
// call this before main() starts, from which point on the return
// value will never change.
UnitTest* UnitTest::GetInstance() {
- // When compiled with MSVC 7.1 in optimized mode, destroying the
- // UnitTest object upon exiting the program messes up the exit code,
- // causing successful tests to appear failed. We have to use a
- // different implementation in this case to bypass the compiler bug.
- // This implementation makes the compiler happy, at the cost of
- // leaking the UnitTest object.
-
// CodeGear C++Builder insists on a public destructor for the
// default implementation. Use this implementation to keep good OO
// design with private destructor.
-#if (_MSC_VER == 1310 && !defined(_DEBUG)) || defined(__BORLANDC__)
+#if defined(__BORLANDC__)
static UnitTest* const instance = new UnitTest;
return instance;
#else
static UnitTest instance;
return &instance;
-#endif // (_MSC_VER == 1310 && !defined(_DEBUG)) || defined(__BORLANDC__)
+#endif // defined(__BORLANDC__)
}
-// Gets the number of successful test cases.
-int UnitTest::successful_test_case_count() const {
- return impl()->successful_test_case_count();
+// Gets the number of successful test suites.
+int UnitTest::successful_test_suite_count() const {
+ return impl()->successful_test_suite_count();
}
-// Gets the number of failed test cases.
-int UnitTest::failed_test_case_count() const {
- return impl()->failed_test_case_count();
+// Gets the number of failed test suites.
+int UnitTest::failed_test_suite_count() const {
+ return impl()->failed_test_suite_count();
}
-// Gets the number of all test cases.
-int UnitTest::total_test_case_count() const {
- return impl()->total_test_case_count();
+// Gets the number of all test suites.
+int UnitTest::total_test_suite_count() const {
+ return impl()->total_test_suite_count();
}
-// Gets the number of all test cases that contain at least one test
+// Gets the number of all test suites that contain at least one test
// that should run.
+int UnitTest::test_suite_to_run_count() const {
+ return impl()->test_suite_to_run_count();
+}
+
+// Legacy API is deprecated but still available
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+int UnitTest::successful_test_case_count() const {
+ return impl()->successful_test_suite_count();
+}
+int UnitTest::failed_test_case_count() const {
+ return impl()->failed_test_suite_count();
+}
+int UnitTest::total_test_case_count() const {
+ return impl()->total_test_suite_count();
+}
int UnitTest::test_case_to_run_count() const {
- return impl()->test_case_to_run_count();
+ return impl()->test_suite_to_run_count();
}
+#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
// Gets the number of successful tests.
int UnitTest::successful_test_count() const {
return impl()->successful_test_count();
}
+// Gets the number of skipped tests.
+int UnitTest::skipped_test_count() const {
+ return impl()->skipped_test_count();
+}
+
// Gets the number of failed tests.
int UnitTest::failed_test_count() const { return impl()->failed_test_count(); }
@@ -4051,29 +4703,37 @@ internal::TimeInMillis UnitTest::elapsed_time() const {
return impl()->elapsed_time();
}
-// Returns true iff the unit test passed (i.e. all test cases passed).
+// Returns true if and only if the unit test passed (i.e. all test suites
+// passed).
bool UnitTest::Passed() const { return impl()->Passed(); }
-// Returns true iff the unit test failed (i.e. some test case failed
-// or something outside of all tests failed).
+// Returns true if and only if the unit test failed (i.e. some test suite
+// failed or something outside of all tests failed).
bool UnitTest::Failed() const { return impl()->Failed(); }
-// Gets the i-th test case among all the test cases. i can range from 0 to
-// total_test_case_count() - 1. If i is not in that range, returns NULL.
+// Gets the i-th test suite among all the test suites. i can range from 0 to
+// total_test_suite_count() - 1. If i is not in that range, returns NULL.
+const TestSuite* UnitTest::GetTestSuite(int i) const {
+ return impl()->GetTestSuite(i);
+}
+
+// Legacy API is deprecated but still available
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
const TestCase* UnitTest::GetTestCase(int i) const {
return impl()->GetTestCase(i);
}
+#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
// Returns the TestResult containing information on test failures and
-// properties logged outside of individual test cases.
+// properties logged outside of individual test suites.
const TestResult& UnitTest::ad_hoc_test_result() const {
return *impl()->ad_hoc_test_result();
}
-// Gets the i-th test case among all the test cases. i can range from 0 to
-// total_test_case_count() - 1. If i is not in that range, returns NULL.
-TestCase* UnitTest::GetMutableTestCase(int i) {
- return impl()->GetMutableTestCase(i);
+// Gets the i-th test suite among all the test suites. i can range from 0 to
+// total_test_suite_count() - 1. If i is not in that range, returns NULL.
+TestSuite* UnitTest::GetMutableTestSuite(int i) {
+ return impl()->GetMutableSuiteCase(i);
}
// Returns the list of event listeners that can be used to track events
@@ -4093,8 +4753,8 @@ TestEventListeners& UnitTest::listeners() {
// We don't protect this under mutex_, as we only support calling it
// from the main thread.
Environment* UnitTest::AddEnvironment(Environment* env) {
- if (env == NULL) {
- return NULL;
+ if (env == nullptr) {
+ return nullptr;
}
impl_->environments().push_back(env);
@@ -4118,25 +4778,24 @@ void UnitTest::AddTestPartResult(
if (impl_->gtest_trace_stack().size() > 0) {
msg << "\n" << GTEST_NAME_ << " trace:";
- for (int i = static_cast<int>(impl_->gtest_trace_stack().size());
- i > 0; --i) {
+ for (size_t i = impl_->gtest_trace_stack().size(); i > 0; --i) {
const internal::TraceInfo& trace = impl_->gtest_trace_stack()[i - 1];
msg << "\n" << internal::FormatFileLocation(trace.file, trace.line)
<< " " << trace.message;
}
}
- if (os_stack_trace.c_str() != NULL && !os_stack_trace.empty()) {
+ if (os_stack_trace.c_str() != nullptr && !os_stack_trace.empty()) {
msg << internal::kStackTraceMarker << os_stack_trace;
}
- const TestPartResult result =
- TestPartResult(result_type, file_name, line_number,
- msg.GetString().c_str());
+ const TestPartResult result = TestPartResult(
+ result_type, file_name, line_number, msg.GetString().c_str());
impl_->GetTestPartResultReporterForCurrentThread()->
ReportTestPartResult(result);
- if (result_type != TestPartResult::kSuccess) {
+ if (result_type != TestPartResult::kSuccess &&
+ result_type != TestPartResult::kSkip) {
// gtest_break_on_failure takes precedence over
// gtest_throw_on_failure. This allows a user to set the latter
// in the code (perhaps in order to use Google Test assertions
@@ -4148,12 +4807,16 @@ void UnitTest::AddTestPartResult(
// when a failure happens and both the --gtest_break_on_failure and
// the --gtest_catch_exceptions flags are specified.
DebugBreak();
+#elif (!defined(__native_client__)) && \
+ ((defined(__clang__) || defined(__GNUC__)) && \
+ (defined(__x86_64__) || defined(__i386__)))
+ // with clang/gcc we can achieve the same effect on x86 by invoking int3
+ asm("int3");
#else
- // Dereference NULL through a volatile pointer to prevent the compiler
+ // Dereference nullptr through a volatile pointer to prevent the compiler
// from removing. We use this rather than abort() or __builtin_trap() for
- // portability: Symbian doesn't implement abort() well, and some debuggers
- // don't correctly trap abort().
- *static_cast<volatile int*>(NULL) = 1;
+ // portability: some debuggers don't correctly trap abort().
+ *static_cast<volatile int*>(nullptr) = 1;
#endif // GTEST_OS_WINDOWS
} else if (GTEST_FLAG(throw_on_failure)) {
#if GTEST_HAS_EXCEPTIONS
@@ -4168,8 +4831,8 @@ void UnitTest::AddTestPartResult(
}
// Adds a TestProperty to the current TestResult object when invoked from
-// inside a test, to current TestCase's ad_hoc_test_result_ when invoked
-// from SetUpTestCase or TearDownTestCase, or to the global property set
+// inside a test, to current TestSuite's ad_hoc_test_result_ when invoked
+// from SetUpTestSuite or TearDownTestSuite, or to the global property set
// when invoked elsewhere. If the result already contains a property with
// the same key, the value will be updated.
void UnitTest::RecordProperty(const std::string& key,
@@ -4208,14 +4871,15 @@ int UnitTest::Run() {
// that understands the premature-exit-file protocol to report the
// test as having failed.
const internal::ScopedPrematureExitFile premature_exit_file(
- in_death_test_child_process ?
- NULL : internal::posix::GetEnv("TEST_PREMATURE_EXIT_FILE"));
+ in_death_test_child_process
+ ? nullptr
+ : internal::posix::GetEnv("TEST_PREMATURE_EXIT_FILE"));
// Captures the value of GTEST_FLAG(catch_exceptions). This value will be
// used for the duration of the program.
impl()->set_catch_exceptions(GTEST_FLAG(catch_exceptions));
-#if GTEST_HAS_SEH
+#if GTEST_OS_WINDOWS
// Either the user wants Google Test to catch exceptions thrown by the
// tests or this is executing in the context of death test child
// process. In either case the user does not want to see pop-up dialogs
@@ -4234,25 +4898,29 @@ int UnitTest::Run() {
_set_error_mode(_OUT_TO_STDERR);
# endif
-# if _MSC_VER >= 1400 && !GTEST_OS_WINDOWS_MOBILE
+# if defined(_MSC_VER) && !GTEST_OS_WINDOWS_MOBILE
// In the debug version, Visual Studio pops up a separate dialog
// offering a choice to debug the aborted program. We need to suppress
// this dialog or it will pop up for every EXPECT/ASSERT_DEATH statement
// executed. Google Test will notify the user of any unexpected
// failure via stderr.
- //
- // VC++ doesn't define _set_abort_behavior() prior to the version 8.0.
- // Users of prior VC versions shall suffer the agony and pain of
- // clicking through the countless debug dialogs.
- // TODO(vladl@google.com): find a way to suppress the abort dialog() in the
- // debug mode when compiled with VC 7.1 or lower.
if (!GTEST_FLAG(break_on_failure))
_set_abort_behavior(
0x0, // Clear the following flags:
_WRITE_ABORT_MSG | _CALL_REPORTFAULT); // pop-up window, core dump.
# endif
+
+ // In debug mode, the Windows CRT can crash with an assertion over invalid
+ // input (e.g. passing an invalid file descriptor). The default handling
+ // for these assertions is to pop up a dialog and wait for user input.
+ // Instead ask the CRT to dump such assertions to stderr non-interactively.
+ if (!IsDebuggerPresent()) {
+ (void)_CrtSetReportMode(_CRT_ASSERT,
+ _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
+ (void)_CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
+ }
}
-#endif // GTEST_HAS_SEH
+#endif // GTEST_OS_WINDOWS
return internal::HandleExceptionsInMethodIfSupported(
impl(),
@@ -4266,13 +4934,22 @@ const char* UnitTest::original_working_dir() const {
return impl_->original_working_dir_.c_str();
}
-// Returns the TestCase object for the test that's currently running,
+// Returns the TestSuite object for the test that's currently running,
// or NULL if no test is running.
+const TestSuite* UnitTest::current_test_suite() const
+ GTEST_LOCK_EXCLUDED_(mutex_) {
+ internal::MutexLock lock(&mutex_);
+ return impl_->current_test_suite();
+}
+
+// Legacy API is still available but deprecated
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
const TestCase* UnitTest::current_test_case() const
GTEST_LOCK_EXCLUDED_(mutex_) {
internal::MutexLock lock(&mutex_);
- return impl_->current_test_case();
+ return impl_->current_test_suite();
}
+#endif
// Returns the TestInfo object for the test that's currently running,
// or NULL if no test is running.
@@ -4285,15 +4962,12 @@ const TestInfo* UnitTest::current_test_info() const
// Returns the random seed used at the start of the current test run.
int UnitTest::random_seed() const { return impl_->random_seed(); }
-#if GTEST_HAS_PARAM_TEST
-// Returns ParameterizedTestCaseRegistry object used to keep track of
+// Returns ParameterizedTestSuiteRegistry object used to keep track of
// value-parameterized tests and instantiate and register them.
-internal::ParameterizedTestCaseRegistry&
- UnitTest::parameterized_test_registry()
- GTEST_LOCK_EXCLUDED_(mutex_) {
+internal::ParameterizedTestSuiteRegistry&
+UnitTest::parameterized_test_registry() GTEST_LOCK_EXCLUDED_(mutex_) {
return impl_->parameterized_test_registry();
}
-#endif // GTEST_HAS_PARAM_TEST
// Creates an empty UnitTest.
UnitTest::UnitTest() {
@@ -4325,25 +4999,22 @@ namespace internal {
UnitTestImpl::UnitTestImpl(UnitTest* parent)
: parent_(parent),
GTEST_DISABLE_MSC_WARNINGS_PUSH_(4355 /* using this in initializer */)
- default_global_test_part_result_reporter_(this),
+ default_global_test_part_result_reporter_(this),
default_per_thread_test_part_result_reporter_(this),
- GTEST_DISABLE_MSC_WARNINGS_POP_()
- global_test_part_result_repoter_(
+ GTEST_DISABLE_MSC_WARNINGS_POP_() global_test_part_result_repoter_(
&default_global_test_part_result_reporter_),
per_thread_test_part_result_reporter_(
&default_per_thread_test_part_result_reporter_),
-#if GTEST_HAS_PARAM_TEST
parameterized_test_registry_(),
parameterized_tests_registered_(false),
-#endif // GTEST_HAS_PARAM_TEST
- last_death_test_case_(-1),
- current_test_case_(NULL),
- current_test_info_(NULL),
+ last_death_test_suite_(-1),
+ current_test_suite_(nullptr),
+ current_test_info_(nullptr),
ad_hoc_test_result_(),
- os_stack_trace_getter_(NULL),
+ os_stack_trace_getter_(nullptr),
post_flag_parse_init_performed_(false),
random_seed_(0), // Will be overridden by the flag before first use.
- random_(0), // Will be reseeded before first use.
+ random_(0), // Will be reseeded before first use.
start_timestamp_(0),
elapsed_time_(0),
#if GTEST_HAS_DEATH_TEST
@@ -4355,8 +5026,8 @@ UnitTestImpl::UnitTestImpl(UnitTest* parent)
}
UnitTestImpl::~UnitTestImpl() {
- // Deletes every TestCase.
- ForEach(test_cases_, internal::Delete<TestCase>);
+ // Deletes every TestSuite.
+ ForEach(test_suites_, internal::Delete<TestSuite>);
// Deletes every Environment.
ForEach(environments_, internal::Delete<Environment>);
@@ -4365,20 +5036,20 @@ UnitTestImpl::~UnitTestImpl() {
}
// Adds a TestProperty to the current TestResult object when invoked in a
-// context of a test, to current test case's ad_hoc_test_result when invoke
-// from SetUpTestCase/TearDownTestCase, or to the global property set
+// context of a test, to current test suite's ad_hoc_test_result when invoke
+// from SetUpTestSuite/TearDownTestSuite, or to the global property set
// otherwise. If the result already contains a property with the same key,
// the value will be updated.
void UnitTestImpl::RecordProperty(const TestProperty& test_property) {
std::string xml_element;
TestResult* test_result; // TestResult appropriate for property recording.
- if (current_test_info_ != NULL) {
+ if (current_test_info_ != nullptr) {
xml_element = "testcase";
test_result = &(current_test_info_->result_);
- } else if (current_test_case_ != NULL) {
+ } else if (current_test_suite_ != nullptr) {
xml_element = "testsuite";
- test_result = &(current_test_case_->ad_hoc_test_result_);
+ test_result = &(current_test_suite_->ad_hoc_test_result_);
} else {
xml_element = "testsuites";
test_result = &ad_hoc_test_result_;
@@ -4390,7 +5061,7 @@ void UnitTestImpl::RecordProperty(const TestProperty& test_property) {
// Disables event forwarding if the control is currently in a death test
// subprocess. Must not be called before InitGoogleTest.
void UnitTestImpl::SuppressTestEventsIfInSubprocess() {
- if (internal_run_death_test_flag_.get() != NULL)
+ if (internal_run_death_test_flag_.get() != nullptr)
listeners()->SuppressEventForwarding();
}
#endif // GTEST_HAS_DEATH_TEST
@@ -4402,10 +5073,12 @@ void UnitTestImpl::ConfigureXmlOutput() {
if (output_format == "xml") {
listeners()->SetDefaultXmlGenerator(new XmlUnitTestResultPrinter(
UnitTestOptions::GetAbsolutePathToOutputFile().c_str()));
+ } else if (output_format == "json") {
+ listeners()->SetDefaultXmlGenerator(new JsonUnitTestResultPrinter(
+ UnitTestOptions::GetAbsolutePathToOutputFile().c_str()));
} else if (output_format != "") {
- printf("WARNING: unrecognized output format \"%s\" ignored.\n",
- output_format.c_str());
- fflush(stdout);
+ GTEST_LOG_(WARNING) << "WARNING: unrecognized output format \""
+ << output_format << "\" ignored.";
}
}
@@ -4420,9 +5093,8 @@ void UnitTestImpl::ConfigureStreamingOutput() {
listeners()->Append(new StreamingListener(target.substr(0, pos),
target.substr(pos+1)));
} else {
- printf("WARNING: unrecognized streaming target \"%s\" ignored.\n",
- target.c_str());
- fflush(stdout);
+ GTEST_LOG_(WARNING) << "unrecognized streaming target \"" << target
+ << "\" ignored.";
}
}
}
@@ -4461,77 +5133,83 @@ void UnitTestImpl::PostFlagParsingInit() {
// Configures listeners for streaming test results to the specified server.
ConfigureStreamingOutput();
#endif // GTEST_CAN_STREAM_RESULTS_
+
+#if GTEST_HAS_ABSL
+ if (GTEST_FLAG(install_failure_signal_handler)) {
+ absl::FailureSignalHandlerOptions options;
+ absl::InstallFailureSignalHandler(options);
+ }
+#endif // GTEST_HAS_ABSL
}
}
-// A predicate that checks the name of a TestCase against a known
+// A predicate that checks the name of a TestSuite against a known
// value.
//
// This is used for implementation of the UnitTest class only. We put
// it in the anonymous namespace to prevent polluting the outer
// namespace.
//
-// TestCaseNameIs is copyable.
-class TestCaseNameIs {
+// TestSuiteNameIs is copyable.
+class TestSuiteNameIs {
public:
// Constructor.
- explicit TestCaseNameIs(const std::string& name)
- : name_(name) {}
+ explicit TestSuiteNameIs(const std::string& name) : name_(name) {}
- // Returns true iff the name of test_case matches name_.
- bool operator()(const TestCase* test_case) const {
- return test_case != NULL && strcmp(test_case->name(), name_.c_str()) == 0;
+ // Returns true if and only if the name of test_suite matches name_.
+ bool operator()(const TestSuite* test_suite) const {
+ return test_suite != nullptr &&
+ strcmp(test_suite->name(), name_.c_str()) == 0;
}
private:
std::string name_;
};
-// Finds and returns a TestCase with the given name. If one doesn't
+// Finds and returns a TestSuite with the given name. If one doesn't
// exist, creates one and returns it. It's the CALLER'S
// RESPONSIBILITY to ensure that this function is only called WHEN THE
// TESTS ARE NOT SHUFFLED.
//
// Arguments:
//
-// test_case_name: name of the test case
-// type_param: the name of the test case's type parameter, or NULL if
-// this is not a typed or a type-parameterized test case.
-// set_up_tc: pointer to the function that sets up the test case
-// tear_down_tc: pointer to the function that tears down the test case
-TestCase* UnitTestImpl::GetTestCase(const char* test_case_name,
- const char* type_param,
- Test::SetUpTestCaseFunc set_up_tc,
- Test::TearDownTestCaseFunc tear_down_tc) {
- // Can we find a TestCase with the given name?
- const std::vector<TestCase*>::const_iterator test_case =
- std::find_if(test_cases_.begin(), test_cases_.end(),
- TestCaseNameIs(test_case_name));
-
- if (test_case != test_cases_.end())
- return *test_case;
+// test_suite_name: name of the test suite
+// type_param: the name of the test suite's type parameter, or NULL if
+// this is not a typed or a type-parameterized test suite.
+// set_up_tc: pointer to the function that sets up the test suite
+// tear_down_tc: pointer to the function that tears down the test suite
+TestSuite* UnitTestImpl::GetTestSuite(
+ const char* test_suite_name, const char* type_param,
+ internal::SetUpTestSuiteFunc set_up_tc,
+ internal::TearDownTestSuiteFunc tear_down_tc) {
+ // Can we find a TestSuite with the given name?
+ const auto test_suite =
+ std::find_if(test_suites_.rbegin(), test_suites_.rend(),
+ TestSuiteNameIs(test_suite_name));
+
+ if (test_suite != test_suites_.rend()) return *test_suite;
// No. Let's create one.
- TestCase* const new_test_case =
- new TestCase(test_case_name, type_param, set_up_tc, tear_down_tc);
-
- // Is this a death test case?
- if (internal::UnitTestOptions::MatchesFilter(test_case_name,
- kDeathTestCaseFilter)) {
- // Yes. Inserts the test case after the last death test case
- // defined so far. This only works when the test cases haven't
+ auto* const new_test_suite =
+ new TestSuite(test_suite_name, type_param, set_up_tc, tear_down_tc);
+
+ // Is this a death test suite?
+ if (internal::UnitTestOptions::MatchesFilter(test_suite_name,
+ kDeathTestSuiteFilter)) {
+ // Yes. Inserts the test suite after the last death test suite
+ // defined so far. This only works when the test suites haven't
// been shuffled. Otherwise we may end up running a death test
// after a non-death test.
- ++last_death_test_case_;
- test_cases_.insert(test_cases_.begin() + last_death_test_case_,
- new_test_case);
+ ++last_death_test_suite_;
+ test_suites_.insert(test_suites_.begin() + last_death_test_suite_,
+ new_test_suite);
} else {
// No. Appends to the end of the list.
- test_cases_.push_back(new_test_case);
+ test_suites_.push_back(new_test_suite);
}
- test_case_indices_.push_back(static_cast<int>(test_case_indices_.size()));
- return new_test_case;
+ test_suite_indices_.push_back(static_cast<int>(test_suite_indices_.size()));
+ return new_test_suite;
}
// Helpers for setting up / tearing down the given environment. They
@@ -4549,13 +5227,9 @@ static void TearDownEnvironment(Environment* env) { env->TearDown(); }
// All other functions called from RunAllTests() may safely assume that
// parameterized tests are ready to be counted and run.
bool UnitTestImpl::RunAllTests() {
- // Makes sure InitGoogleTest() was called.
- if (!GTestIsInitialized()) {
- printf("%s",
- "\nThis test program did NOT call ::testing::InitGoogleTest "
- "before calling RUN_ALL_TESTS(). Please fix it.\n");
- return false;
- }
+ // True if and only if Google Test is initialized before RUN_ALL_TESTS() is
+ // called.
+ const bool gtest_is_initialized_before_run_all_tests = GTestIsInitialized();
// Do not run any test if the --help flag was specified.
if (g_help_flag)
@@ -4570,12 +5244,13 @@ bool UnitTestImpl::RunAllTests() {
// protocol.
internal::WriteToShardStatusFileIfNeeded();
- // True iff we are in a subprocess for running a thread-safe-style
+ // True if and only if we are in a subprocess for running a thread-safe-style
// death test.
bool in_subprocess_for_death_test = false;
#if GTEST_HAS_DEATH_TEST
- in_subprocess_for_death_test = (internal_run_death_test_flag_.get() != NULL);
+ in_subprocess_for_death_test =
+ (internal_run_death_test_flag_.get() != nullptr);
# if defined(GTEST_EXTRA_DEATH_TEST_CHILD_SETUP_)
if (in_subprocess_for_death_test) {
GTEST_EXTRA_DEATH_TEST_CHILD_SETUP_();
@@ -4602,7 +5277,7 @@ bool UnitTestImpl::RunAllTests() {
random_seed_ = GTEST_FLAG(shuffle) ?
GetRandomSeedFromFlag(GTEST_FLAG(random_seed)) : 0;
- // True iff at least one test has failed.
+ // True if and only if at least one test has failed.
bool failed = false;
TestEventListener* repeater = listeners()->repeater();
@@ -4614,17 +5289,17 @@ bool UnitTestImpl::RunAllTests() {
// when we are inside the subprocess of a death test.
const int repeat = in_subprocess_for_death_test ? 1 : GTEST_FLAG(repeat);
// Repeats forever if the repeat count is negative.
- const bool forever = repeat < 0;
- for (int i = 0; forever || i != repeat; i++) {
+ const bool gtest_repeat_forever = repeat < 0;
+ for (int i = 0; gtest_repeat_forever || i != repeat; i++) {
// We want to preserve failures generated by ad-hoc test
// assertions executed before RUN_ALL_TESTS().
ClearNonAdHocTestResult();
const TimeInMillis start = GetTimeInMillis();
- // Shuffles test cases and tests if requested.
+ // Shuffles test suites and tests if requested.
if (has_tests_to_run && GTEST_FLAG(shuffle)) {
- random()->Reseed(random_seed_);
+ random()->Reseed(static_cast<UInt32>(random_seed_));
// This should be done before calling OnTestIterationStart(),
// such that a test event listener can see the actual test order
// in the event.
@@ -4634,19 +5309,33 @@ bool UnitTestImpl::RunAllTests() {
// Tells the unit test event listeners that the tests are about to start.
repeater->OnTestIterationStart(*parent_, i);
- // Runs each test case if there is at least one test to run.
+ // Runs each test suite if there is at least one test to run.
if (has_tests_to_run) {
// Sets up all environments beforehand.
repeater->OnEnvironmentsSetUpStart(*parent_);
ForEach(environments_, SetUpEnvironment);
repeater->OnEnvironmentsSetUpEnd(*parent_);
- // Runs the tests only if there was no fatal failure during global
- // set-up.
- if (!Test::HasFatalFailure()) {
- for (int test_index = 0; test_index < total_test_case_count();
+ // Runs the tests only if there was no fatal failure or skip triggered
+ // during global set-up.
+ if (Test::IsSkipped()) {
+ // Emit diagnostics when global set-up calls skip, as it will not be
+ // emitted by default.
+ TestResult& test_result =
+ *internal::GetUnitTestImpl()->current_test_result();
+ for (int j = 0; j < test_result.total_part_count(); ++j) {
+ const TestPartResult& test_part_result =
+ test_result.GetTestPartResult(j);
+ if (test_part_result.type() == TestPartResult::kSkip) {
+ const std::string& result = test_part_result.message();
+ printf("%s\n", result.c_str());
+ }
+ }
+ fflush(stdout);
+ } else if (!Test::HasFatalFailure()) {
+ for (int test_index = 0; test_index < total_test_suite_count();
test_index++) {
- GetMutableTestCase(test_index)->Run();
+ GetMutableSuiteCase(test_index)->Run();
}
}
@@ -4683,6 +5372,20 @@ bool UnitTestImpl::RunAllTests() {
repeater->OnTestProgramEnd(*parent_);
+ if (!gtest_is_initialized_before_run_all_tests) {
+ ColoredPrintf(
+ COLOR_RED,
+ "\nIMPORTANT NOTICE - DO NOT IGNORE:\n"
+ "This test program did NOT call " GTEST_INIT_GOOGLE_TEST_NAME_
+ "() before calling RUN_ALL_TESTS(). This is INVALID. Soon " GTEST_NAME_
+ " will start to enforce the valid usage. "
+ "Please fix it ASAP, or IT WILL START TO FAIL.\n"); // NOLINT
+#if GTEST_FOR_GOOGLE_
+ ColoredPrintf(COLOR_RED,
+ "For more details, see http://wiki/Main/ValidGUnitMain.\n");
+#endif // GTEST_FOR_GOOGLE_
+ }
+
return !failed;
}
@@ -4692,9 +5395,9 @@ bool UnitTestImpl::RunAllTests() {
// be created, prints an error and exits.
void WriteToShardStatusFileIfNeeded() {
const char* const test_shard_file = posix::GetEnv(kTestShardStatusFile);
- if (test_shard_file != NULL) {
+ if (test_shard_file != nullptr) {
FILE* const file = posix::FOpen(test_shard_file, "w");
- if (file == NULL) {
+ if (file == nullptr) {
ColoredPrintf(COLOR_RED,
"Could not write to the test shard status file \"%s\" "
"specified by the %s environment variable.\n",
@@ -4729,7 +5432,7 @@ bool ShouldShard(const char* total_shards_env,
<< "Invalid environment variables: you have "
<< kTestShardIndex << " = " << shard_index
<< ", but have left " << kTestTotalShards << " unset.\n";
- ColoredPrintf(COLOR_RED, msg.GetString().c_str());
+ ColoredPrintf(COLOR_RED, "%s", msg.GetString().c_str());
fflush(stdout);
exit(EXIT_FAILURE);
} else if (total_shards != -1 && shard_index == -1) {
@@ -4737,7 +5440,7 @@ bool ShouldShard(const char* total_shards_env,
<< "Invalid environment variables: you have "
<< kTestTotalShards << " = " << total_shards
<< ", but have left " << kTestShardIndex << " unset.\n";
- ColoredPrintf(COLOR_RED, msg.GetString().c_str());
+ ColoredPrintf(COLOR_RED, "%s", msg.GetString().c_str());
fflush(stdout);
exit(EXIT_FAILURE);
} else if (shard_index < 0 || shard_index >= total_shards) {
@@ -4746,7 +5449,7 @@ bool ShouldShard(const char* total_shards_env,
<< kTestShardIndex << " < " << kTestTotalShards
<< ", but you have " << kTestShardIndex << "=" << shard_index
<< ", " << kTestTotalShards << "=" << total_shards << ".\n";
- ColoredPrintf(COLOR_RED, msg.GetString().c_str());
+ ColoredPrintf(COLOR_RED, "%s", msg.GetString().c_str());
fflush(stdout);
exit(EXIT_FAILURE);
}
@@ -4759,7 +5462,7 @@ bool ShouldShard(const char* total_shards_env,
// and aborts.
Int32 Int32FromEnvOrDie(const char* var, Int32 default_val) {
const char* str_val = posix::GetEnv(var);
- if (str_val == NULL) {
+ if (str_val == nullptr) {
return default_val;
}
@@ -4772,8 +5475,8 @@ Int32 Int32FromEnvOrDie(const char* var, Int32 default_val) {
}
// Given the total number of shards, the shard index, and the test id,
-// returns true iff the test should be run on this shard. The test id is
-// some arbitrary but unique non-negative integer assigned to each test
+// returns true if and only if the test should be run on this shard. The test id
+// is some arbitrary but unique non-negative integer assigned to each test
// method. Assumes that 0 <= shard_index < total_shards.
bool ShouldRunTestOnShard(int total_shards, int shard_index, int test_id) {
return (test_id % total_shards) == shard_index;
@@ -4781,11 +5484,11 @@ bool ShouldRunTestOnShard(int total_shards, int shard_index, int test_id) {
// Compares the name of each test with the user-specified filter to
// decide whether the test should be run, then records the result in
-// each TestCase and TestInfo object.
+// each TestSuite and TestInfo object.
// If shard_tests == true, further filters tests based on sharding
// variables in the environment - see
-// http://code.google.com/p/googletest/wiki/GoogleTestAdvancedGuide.
-// Returns the number of tests that should run.
+// https://github.com/google/googletest/blob/master/googletest/docs/advanced.md
+// . Returns the number of tests that should run.
int UnitTestImpl::FilterTests(ReactionToSharding shard_tests) {
const Int32 total_shards = shard_tests == HONOR_SHARDING_PROTOCOL ?
Int32FromEnvOrDie(kTestTotalShards, -1) : -1;
@@ -4798,42 +5501,40 @@ int UnitTestImpl::FilterTests(ReactionToSharding shard_tests) {
// this shard.
int num_runnable_tests = 0;
int num_selected_tests = 0;
- for (size_t i = 0; i < test_cases_.size(); i++) {
- TestCase* const test_case = test_cases_[i];
- const std::string &test_case_name = test_case->name();
- test_case->set_should_run(false);
+ for (auto* test_suite : test_suites_) {
+ const std::string& test_suite_name = test_suite->name();
+ test_suite->set_should_run(false);
- for (size_t j = 0; j < test_case->test_info_list().size(); j++) {
- TestInfo* const test_info = test_case->test_info_list()[j];
+ for (size_t j = 0; j < test_suite->test_info_list().size(); j++) {
+ TestInfo* const test_info = test_suite->test_info_list()[j];
const std::string test_name(test_info->name());
- // A test is disabled if test case name or test name matches
+ // A test is disabled if test suite name or test name matches
// kDisableTestFilter.
- const bool is_disabled =
- internal::UnitTestOptions::MatchesFilter(test_case_name,
- kDisableTestFilter) ||
- internal::UnitTestOptions::MatchesFilter(test_name,
- kDisableTestFilter);
+ const bool is_disabled = internal::UnitTestOptions::MatchesFilter(
+ test_suite_name, kDisableTestFilter) ||
+ internal::UnitTestOptions::MatchesFilter(
+ test_name, kDisableTestFilter);
test_info->is_disabled_ = is_disabled;
- const bool matches_filter =
- internal::UnitTestOptions::FilterMatchesTest(test_case_name,
- test_name);
+ const bool matches_filter = internal::UnitTestOptions::FilterMatchesTest(
+ test_suite_name, test_name);
test_info->matches_filter_ = matches_filter;
const bool is_runnable =
(GTEST_FLAG(also_run_disabled_tests) || !is_disabled) &&
matches_filter;
- const bool is_selected = is_runnable &&
- (shard_tests == IGNORE_SHARDING_PROTOCOL ||
- ShouldRunTestOnShard(total_shards, shard_index,
- num_runnable_tests));
+ const bool is_in_another_shard =
+ shard_tests != IGNORE_SHARDING_PROTOCOL &&
+ !ShouldRunTestOnShard(total_shards, shard_index, num_runnable_tests);
+ test_info->is_in_another_shard_ = is_in_another_shard;
+ const bool is_selected = is_runnable && !is_in_another_shard;
num_runnable_tests += is_runnable;
num_selected_tests += is_selected;
test_info->should_run_ = is_selected;
- test_case->set_should_run(test_case->should_run() || is_selected);
+ test_suite->set_should_run(test_suite->should_run() || is_selected);
}
}
return num_selected_tests;
@@ -4844,7 +5545,7 @@ int UnitTestImpl::FilterTests(ReactionToSharding shard_tests) {
// max_length characters, only prints the first max_length characters
// and "...".
static void PrintOnOneLine(const char* str, int max_length) {
- if (str != NULL) {
+ if (str != nullptr) {
for (int i = 0; *str != '\0'; ++str) {
if (i >= max_length) {
printf("...");
@@ -4866,27 +5567,25 @@ void UnitTestImpl::ListTestsMatchingFilter() {
// Print at most this many characters for each type/value parameter.
const int kMaxParamLength = 250;
- for (size_t i = 0; i < test_cases_.size(); i++) {
- const TestCase* const test_case = test_cases_[i];
- bool printed_test_case_name = false;
+ for (auto* test_suite : test_suites_) {
+ bool printed_test_suite_name = false;
- for (size_t j = 0; j < test_case->test_info_list().size(); j++) {
- const TestInfo* const test_info =
- test_case->test_info_list()[j];
+ for (size_t j = 0; j < test_suite->test_info_list().size(); j++) {
+ const TestInfo* const test_info = test_suite->test_info_list()[j];
if (test_info->matches_filter_) {
- if (!printed_test_case_name) {
- printed_test_case_name = true;
- printf("%s.", test_case->name());
- if (test_case->type_param() != NULL) {
+ if (!printed_test_suite_name) {
+ printed_test_suite_name = true;
+ printf("%s.", test_suite->name());
+ if (test_suite->type_param() != nullptr) {
printf(" # %s = ", kTypeParamLabel);
// We print the type parameter on a single line to make
// the output easy to parse by a program.
- PrintOnOneLine(test_case->type_param(), kMaxParamLength);
+ PrintOnOneLine(test_suite->type_param(), kMaxParamLength);
}
printf("\n");
}
printf(" %s", test_info->name());
- if (test_info->value_param() != NULL) {
+ if (test_info->value_param() != nullptr) {
printf(" # %s = ", kValueParamLabel);
// We print the value parameter on a single line to make the
// output easy to parse by a program.
@@ -4897,6 +5596,23 @@ void UnitTestImpl::ListTestsMatchingFilter() {
}
}
fflush(stdout);
+ const std::string& output_format = UnitTestOptions::GetOutputFormat();
+ if (output_format == "xml" || output_format == "json") {
+ FILE* fileout = OpenFileForWriting(
+ UnitTestOptions::GetAbsolutePathToOutputFile().c_str());
+ std::stringstream stream;
+ if (output_format == "xml") {
+ XmlUnitTestResultPrinter(
+ UnitTestOptions::GetAbsolutePathToOutputFile().c_str())
+ .PrintXmlTestsList(&stream, test_suites_);
+ } else if (output_format == "json") {
+ JsonUnitTestResultPrinter(
+ UnitTestOptions::GetAbsolutePathToOutputFile().c_str())
+ .PrintJsonTestList(&stream, test_suites_);
+ }
+ fprintf(fileout, "%s", StringStreamToString(&stream).c_str());
+ fclose(fileout);
+ }
}
// Sets the OS stack trace getter.
@@ -4916,7 +5632,7 @@ void UnitTestImpl::set_os_stack_trace_getter(
// otherwise, creates an OsStackTraceGetter, makes it the current
// getter, and returns it.
OsStackTraceGetterInterface* UnitTestImpl::os_stack_trace_getter() {
- if (os_stack_trace_getter_ == NULL) {
+ if (os_stack_trace_getter_ == nullptr) {
#ifdef GTEST_OS_STACK_TRACE_GETTER_
os_stack_trace_getter_ = new GTEST_OS_STACK_TRACE_GETTER_;
#else
@@ -4927,36 +5643,40 @@ OsStackTraceGetterInterface* UnitTestImpl::os_stack_trace_getter() {
return os_stack_trace_getter_;
}
-// Returns the TestResult for the test that's currently running, or
-// the TestResult for the ad hoc test if no test is running.
+// Returns the most specific TestResult currently running.
TestResult* UnitTestImpl::current_test_result() {
- return current_test_info_ ?
- &(current_test_info_->result_) : &ad_hoc_test_result_;
+ if (current_test_info_ != nullptr) {
+ return &current_test_info_->result_;
+ }
+ if (current_test_suite_ != nullptr) {
+ return &current_test_suite_->ad_hoc_test_result_;
+ }
+ return &ad_hoc_test_result_;
}
-// Shuffles all test cases, and the tests within each test case,
+// Shuffles all test suites, and the tests within each test suite,
// making sure that death tests are still run first.
void UnitTestImpl::ShuffleTests() {
- // Shuffles the death test cases.
- ShuffleRange(random(), 0, last_death_test_case_ + 1, &test_case_indices_);
+ // Shuffles the death test suites.
+ ShuffleRange(random(), 0, last_death_test_suite_ + 1, &test_suite_indices_);
- // Shuffles the non-death test cases.
- ShuffleRange(random(), last_death_test_case_ + 1,
- static_cast<int>(test_cases_.size()), &test_case_indices_);
+ // Shuffles the non-death test suites.
+ ShuffleRange(random(), last_death_test_suite_ + 1,
+ static_cast<int>(test_suites_.size()), &test_suite_indices_);
- // Shuffles the tests inside each test case.
- for (size_t i = 0; i < test_cases_.size(); i++) {
- test_cases_[i]->ShuffleTests(random());
+ // Shuffles the tests inside each test suite.
+ for (auto& test_suite : test_suites_) {
+ test_suite->ShuffleTests(random());
}
}
-// Restores the test cases and tests to their order before the first shuffle.
+// Restores the test suites and tests to their order before the first shuffle.
void UnitTestImpl::UnshuffleTests() {
- for (size_t i = 0; i < test_cases_.size(); i++) {
- // Unshuffles the tests in each test case.
- test_cases_[i]->UnshuffleTests();
- // Resets the index of each test case.
- test_case_indices_[i] = static_cast<int>(i);
+ for (size_t i = 0; i < test_suites_.size(); i++) {
+ // Unshuffles the tests in each test suite.
+ test_suites_[i]->UnshuffleTests();
+ // Resets the index of each test suite.
+ test_suite_indices_[i] = static_cast<int>(i);
}
}
@@ -5012,16 +5732,15 @@ bool SkipPrefix(const char* prefix, const char** pstr) {
// part can be omitted.
//
// Returns the value of the flag, or NULL if the parsing failed.
-const char* ParseFlagValue(const char* str,
- const char* flag,
- bool def_optional) {
+static const char* ParseFlagValue(const char* str, const char* flag,
+ bool def_optional) {
// str and flag must not be NULL.
- if (str == NULL || flag == NULL) return NULL;
+ if (str == nullptr || flag == nullptr) return nullptr;
// The flag must start with "--" followed by GTEST_FLAG_PREFIX_.
const std::string flag_str = std::string("--") + GTEST_FLAG_PREFIX_ + flag;
const size_t flag_len = flag_str.length();
- if (strncmp(str, flag_str.c_str(), flag_len) != 0) return NULL;
+ if (strncmp(str, flag_str.c_str(), flag_len) != 0) return nullptr;
// Skips the flag name.
const char* flag_end = str + flag_len;
@@ -5034,7 +5753,7 @@ const char* ParseFlagValue(const char* str,
// If def_optional is true and there are more characters after the
// flag name, or if def_optional is false, there must be a '=' after
// the flag name.
- if (flag_end[0] != '=') return NULL;
+ if (flag_end[0] != '=') return nullptr;
// Returns the string after "=".
return flag_end + 1;
@@ -5050,12 +5769,12 @@ const char* ParseFlagValue(const char* str,
//
// On success, stores the value of the flag in *value, and returns
// true. On failure, returns false without changing *value.
-bool ParseBoolFlag(const char* str, const char* flag, bool* value) {
+static bool ParseBoolFlag(const char* str, const char* flag, bool* value) {
// Gets the value of the flag as a string.
const char* const value_str = ParseFlagValue(str, flag, true);
// Aborts if the parsing failed.
- if (value_str == NULL) return false;
+ if (value_str == nullptr) return false;
// Converts the string value to a bool.
*value = !(*value_str == '0' || *value_str == 'f' || *value_str == 'F');
@@ -5072,7 +5791,7 @@ bool ParseInt32Flag(const char* str, const char* flag, Int32* value) {
const char* const value_str = ParseFlagValue(str, flag, false);
// Aborts if the parsing failed.
- if (value_str == NULL) return false;
+ if (value_str == nullptr) return false;
// Sets *value to the value of the flag.
return ParseInt32(Message() << "The value of flag --" << flag,
@@ -5084,12 +5803,13 @@ bool ParseInt32Flag(const char* str, const char* flag, Int32* value) {
//
// On success, stores the value of the flag in *value, and returns
// true. On failure, returns false without changing *value.
-bool ParseStringFlag(const char* str, const char* flag, std::string* value) {
+template <typename String>
+static bool ParseStringFlag(const char* str, const char* flag, String* value) {
// Gets the value of the flag as a string.
const char* const value_str = ParseFlagValue(str, flag, false);
// Aborts if the parsing failed.
- if (value_str == NULL) return false;
+ if (value_str == nullptr) return false;
// Sets *value to the value of the flag.
*value = value_str;
@@ -5120,8 +5840,6 @@ static bool HasGoogleTestFlagPrefix(const char* str) {
// @Y changes the color to yellow.
// @D changes to the default terminal text color.
//
-// TODO(wan@google.com): Write tests for this once we add stdout
-// capturing to Google Test.
static void PrintColorEncoded(const char* str) {
GTestColor color = COLOR_DEFAULT; // The current color.
@@ -5131,7 +5849,7 @@ static void PrintColorEncoded(const char* str) {
// next segment.
for (;;) {
const char* p = strchr(str, '@');
- if (p == NULL) {
+ if (p == nullptr) {
ColoredPrintf(color, "%s", str);
return;
}
@@ -5186,24 +5904,25 @@ static const char kColorEncodedHelpMessage[] =
" Enable/disable colored output. The default is @Gauto@D.\n"
" -@G-" GTEST_FLAG_PREFIX_ "print_time=0@D\n"
" Don't print the elapsed time of each test.\n"
-" @G--" GTEST_FLAG_PREFIX_ "output=xml@Y[@G:@YDIRECTORY_PATH@G"
+" @G--" GTEST_FLAG_PREFIX_ "output=@Y(@Gjson@Y|@Gxml@Y)[@G:@YDIRECTORY_PATH@G"
GTEST_PATH_SEP_ "@Y|@G:@YFILE_PATH]@D\n"
-" Generate an XML report in the given directory or with the given file\n"
-" name. @YFILE_PATH@D defaults to @Gtest_details.xml@D.\n"
-#if GTEST_CAN_STREAM_RESULTS_
+" Generate a JSON or XML report in the given directory or with the given\n"
+" file name. @YFILE_PATH@D defaults to @Gtest_detail.xml@D.\n"
+# if GTEST_CAN_STREAM_RESULTS_
" @G--" GTEST_FLAG_PREFIX_ "stream_result_to=@YHOST@G:@YPORT@D\n"
" Stream test results to the given server.\n"
-#endif // GTEST_CAN_STREAM_RESULTS_
+# endif // GTEST_CAN_STREAM_RESULTS_
"\n"
"Assertion Behavior:\n"
-#if GTEST_HAS_DEATH_TEST && !GTEST_OS_WINDOWS
+# if GTEST_HAS_DEATH_TEST && !GTEST_OS_WINDOWS
" @G--" GTEST_FLAG_PREFIX_ "death_test_style=@Y(@Gfast@Y|@Gthreadsafe@Y)@D\n"
" Set the default death test style.\n"
-#endif // GTEST_HAS_DEATH_TEST && !GTEST_OS_WINDOWS
+# endif // GTEST_HAS_DEATH_TEST && !GTEST_OS_WINDOWS
" @G--" GTEST_FLAG_PREFIX_ "break_on_failure@D\n"
" Turn assertion failures into debugger break-points.\n"
" @G--" GTEST_FLAG_PREFIX_ "throw_on_failure@D\n"
-" Turn assertion failures into C++ exceptions.\n"
+" Turn assertion failures into C++ exceptions for use by an external\n"
+" test framework.\n"
" @G--" GTEST_FLAG_PREFIX_ "catch_exceptions=0@D\n"
" Do not report exceptions as test failures. Instead, allow them\n"
" to crash the program or throw a pop-up (on Windows).\n"
@@ -5220,7 +5939,7 @@ static const char kColorEncodedHelpMessage[] =
"(not one in your own code or tests), please report it to\n"
"@G<" GTEST_DEV_EMAIL_ ">@D.\n";
-bool ParseGoogleTestFlag(const char* const arg) {
+static bool ParseGoogleTestFlag(const char* const arg) {
return ParseBoolFlag(arg, kAlsoRunDisabledTestsFlag,
&GTEST_FLAG(also_run_disabled_tests)) ||
ParseBoolFlag(arg, kBreakOnFailureFlag,
@@ -5238,6 +5957,7 @@ bool ParseGoogleTestFlag(const char* const arg) {
ParseBoolFlag(arg, kListTestsFlag, &GTEST_FLAG(list_tests)) ||
ParseStringFlag(arg, kOutputFlag, &GTEST_FLAG(output)) ||
ParseBoolFlag(arg, kPrintTimeFlag, &GTEST_FLAG(print_time)) ||
+ ParseBoolFlag(arg, kPrintUTF8Flag, &GTEST_FLAG(print_utf8)) ||
ParseInt32Flag(arg, kRandomSeedFlag, &GTEST_FLAG(random_seed)) ||
ParseInt32Flag(arg, kRepeatFlag, &GTEST_FLAG(repeat)) ||
ParseBoolFlag(arg, kShuffleFlag, &GTEST_FLAG(shuffle)) ||
@@ -5250,14 +5970,11 @@ bool ParseGoogleTestFlag(const char* const arg) {
}
#if GTEST_USE_OWN_FLAGFILE_FLAG_
-void LoadFlagsFromFile(const std::string& path) {
+static void LoadFlagsFromFile(const std::string& path) {
FILE* flagfile = posix::FOpen(path.c_str(), "r");
if (!flagfile) {
- fprintf(stderr,
- "Unable to open file \"%s\"\n",
- GTEST_FLAG(flagfile).c_str());
- fflush(stderr);
- exit(EXIT_FAILURE);
+ GTEST_LOG_(FATAL) << "Unable to open file \"" << GTEST_FLAG(flagfile)
+ << "\"";
}
std::string contents(ReadEntireFile(flagfile));
posix::FClose(flagfile);
@@ -5331,6 +6048,17 @@ void ParseGoogleTestFlagsOnlyImpl(int* argc, CharType** argv) {
// other parts of Google Test.
void ParseGoogleTestFlagsOnly(int* argc, char** argv) {
ParseGoogleTestFlagsOnlyImpl(argc, argv);
+
+ // Fix the value of *_NSGetArgc() on macOS, but if and only if
+ // *_NSGetArgv() == argv
+ // Only applicable to char** version of argv
+#if GTEST_OS_MAC
+#ifndef GTEST_OS_IOS
+ if (*_NSGetArgv() == argv) {
+ *_NSGetArgc() = *argc;
+ }
+#endif
+#endif
}
void ParseGoogleTestFlagsOnly(int* argc, wchar_t** argv) {
ParseGoogleTestFlagsOnlyImpl(argc, argv);
@@ -5352,6 +6080,10 @@ void InitGoogleTestImpl(int* argc, CharType** argv) {
g_argvs.push_back(StreamableToString(argv[i]));
}
+#if GTEST_HAS_ABSL
+ absl::InitializeSymbolizer(g_argvs[0].c_str());
+#endif // GTEST_HAS_ABSL
+
ParseGoogleTestFlagsOnly(argc, argv);
GetUnitTestImpl()->PostFlagParsingInit();
}
@@ -5385,4 +6117,61 @@ void InitGoogleTest(int* argc, wchar_t** argv) {
#endif // defined(GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_)
}
+// This overloaded version can be used on Arduino/embedded platforms where
+// there is no argc/argv.
+void InitGoogleTest() {
+ // Since Arduino doesn't have a command line, fake out the argc/argv arguments
+ int argc = 1;
+ const auto arg0 = "dummy";
+ char* argv0 = const_cast<char*>(arg0);
+ char** argv = &argv0;
+
+#if defined(GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_)
+ GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_(&argc, argv);
+#else // defined(GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_)
+ internal::InitGoogleTestImpl(&argc, argv);
+#endif // defined(GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_)
+}
+
+std::string TempDir() {
+#if defined(GTEST_CUSTOM_TEMPDIR_FUNCTION_)
+ return GTEST_CUSTOM_TEMPDIR_FUNCTION_();
+#endif
+
+#if GTEST_OS_WINDOWS_MOBILE
+ return "\\temp\\";
+#elif GTEST_OS_WINDOWS
+ const char* temp_dir = internal::posix::GetEnv("TEMP");
+ if (temp_dir == nullptr || temp_dir[0] == '\0')
+ return "\\temp\\";
+ else if (temp_dir[strlen(temp_dir) - 1] == '\\')
+ return temp_dir;
+ else
+ return std::string(temp_dir) + "\\";
+#elif GTEST_OS_LINUX_ANDROID
+ return "/sdcard/";
+#else
+ return "/tmp/";
+#endif // GTEST_OS_WINDOWS_MOBILE
+}
+
+// Class ScopedTrace
+
+// Pushes the given source file location and message onto a per-thread
+// trace stack maintained by Google Test.
+void ScopedTrace::PushTrace(const char* file, int line, std::string message) {
+ internal::TraceInfo trace;
+ trace.file = file;
+ trace.line = line;
+ trace.message.swap(message);
+
+ UnitTest::GetInstance()->PushGTestTrace(trace);
+}
+
+// Pops the info pushed by the c'tor.
+ScopedTrace::~ScopedTrace()
+ GTEST_LOCK_EXCLUDED_(&UnitTest::mutex_) {
+ UnitTest::GetInstance()->PopGTestTrace();
+}
+
} // namespace testing
diff --git a/extern/gtest/src/gtest_main.cc b/extern/gtest/src/gtest_main.cc
index f3028225523..f6e1dd96fb3 100644
--- a/extern/gtest/src/gtest_main.cc
+++ b/extern/gtest/src/gtest_main.cc
@@ -27,12 +27,21 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#include <stdio.h>
-
+#include <cstdio>
#include "gtest/gtest.h"
+#ifdef ARDUINO
+void setup() {
+ testing::InitGoogleTest();
+}
+
+void loop() { RUN_ALL_TESTS(); }
+
+#else
+
GTEST_API_ int main(int argc, char **argv) {
- printf("Running main() from gtest_main.cc\n");
+ printf("Running main() from %s\n", __FILE__);
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
+#endif
diff --git a/extern/mantaflow/CMakeLists.txt b/extern/mantaflow/CMakeLists.txt
index bdee06349d2..8b7453bf4c5 100644
--- a/extern/mantaflow/CMakeLists.txt
+++ b/extern/mantaflow/CMakeLists.txt
@@ -54,6 +54,10 @@ if(WITH_OPENVDB)
add_definitions(-DOPENVDB_STATICLIB)
endif()
+if(WITH_OPENVDB_BLOSC)
+ add_definitions(-DOPENVDB_BLOSC=1)
+endif()
+
if(WIN32)
add_definitions(-D_USE_MATH_DEFINES)
endif()
@@ -106,10 +110,12 @@ set(SRC
${MANTA_PP}/fastmarch.cpp
${MANTA_PP}/fastmarch.h
${MANTA_PP}/fastmarch.h.reg.cpp
- ${MANTA_PP}/fileio/ioutil.cpp
${MANTA_PP}/fileio/iogrids.cpp
${MANTA_PP}/fileio/iomeshes.cpp
${MANTA_PP}/fileio/ioparticles.cpp
+ ${MANTA_PP}/fileio/ioutil.cpp
+ ${MANTA_PP}/fileio/iovdb.cpp
+ ${MANTA_PP}/fileio/mantaio.cpp
${MANTA_PP}/fileio/mantaio.h
${MANTA_PP}/fileio/mantaio.h.reg.cpp
${MANTA_PP}/fluidsolver.cpp
diff --git a/extern/mantaflow/helper/pwrapper/pconvert.cpp b/extern/mantaflow/helper/pwrapper/pconvert.cpp
index 9ada75519fc..861a2c070bd 100644
--- a/extern/mantaflow/helper/pwrapper/pconvert.cpp
+++ b/extern/mantaflow/helper/pwrapper/pconvert.cpp
@@ -96,6 +96,37 @@ template<> PyObject *toPy<PbClass *>(const PbClass_Ptr &obj)
{
return obj->getPyObject();
}
+template<> PyObject *toPy<std::vector<PbClass *>>(const std::vector<PbClass *> &vec)
+{
+ PyObject *listObj = PyList_New(vec.size());
+ if (!listObj)
+ throw logic_error("Unable to allocate memory for Python list");
+ for (unsigned int i = 0; i < vec.size(); i++) {
+ PbClass *pb = vec[i];
+ PyObject *item = pb->getPyObject();
+ if (!item) {
+ Py_DECREF(listObj);
+ throw logic_error("Unable to allocate memory for Python list");
+ }
+ PyList_SET_ITEM(listObj, i, item);
+ }
+ return listObj;
+}
+template<> PyObject *toPy<std::vector<float>>(const std::vector<float> &vec)
+{
+ PyObject *listObj = PyList_New(vec.size());
+ if (!listObj)
+ throw logic_error("Unable to allocate memory for Python list");
+ for (unsigned int i = 0; i < vec.size(); i++) {
+ PyObject *item = toPy<float>(vec[i]);
+ if (!item) {
+ Py_DECREF(listObj);
+ throw logic_error("Unable to allocate memory for Python list");
+ }
+ PyList_SET_ITEM(listObj, i, item);
+ }
+ return listObj;
+}
template<> float fromPy<float>(PyObject *obj)
{
@@ -125,6 +156,42 @@ template<> PyObject *fromPy<PyObject *>(PyObject *obj)
{
return obj;
}
+template<> PbClass *fromPy<PbClass *>(PyObject *obj)
+{
+ PbClass *pbo = Pb::objFromPy(obj);
+
+ if (!PyType_Check(obj))
+ return pbo;
+
+ const char *tname = ((PyTypeObject *)obj)->tp_name;
+ pbo->setName(tname);
+
+ return pbo;
+}
+template<> std::vector<PbClass *> fromPy<std::vector<PbClass *>>(PyObject *obj)
+{
+ std::vector<PbClass *> vec;
+ if (PyList_Check(obj)) {
+ int sz = PyList_Size(obj);
+ for (int i = 0; i < sz; ++i) {
+ PyObject *lobj = PyList_GetItem(obj, i);
+ vec.push_back(fromPy<PbClass *>(lobj));
+ }
+ }
+ return vec;
+}
+template<> std::vector<float> fromPy<std::vector<float>>(PyObject *obj)
+{
+ std::vector<float> vec;
+ if (PyList_Check(obj)) {
+ int sz = PyList_Size(obj);
+ for (int i = 0; i < sz; ++i) {
+ PyObject *lobj = PyList_GetItem(obj, i);
+ vec.push_back(fromPy<float>(lobj));
+ }
+ }
+ return vec;
+}
template<> int fromPy<int>(PyObject *obj)
{
#if PY_MAJOR_VERSION <= 2
@@ -259,11 +326,10 @@ template<class T> T *tmpAlloc(PyObject *obj, std::vector<void *> *tmp)
{
if (!tmp)
throw Error("dynamic de-ref not supported for this type");
- void *ptr = malloc(sizeof(T));
- tmp->push_back(ptr);
- *((T *)ptr) = fromPy<T>(obj);
- return (T *)ptr;
+ T *ptr = new T(fromPy<T>(obj));
+ tmp->push_back(ptr);
+ return ptr;
}
template<> float *fromPyPtr<float>(PyObject *obj, std::vector<void *> *tmp)
{
@@ -301,6 +367,11 @@ template<> Vec4i *fromPyPtr<Vec4i>(PyObject *obj, std::vector<void *> *tmp)
{
return tmpAlloc<Vec4i>(obj, tmp);
}
+template<>
+std::vector<PbClass *> *fromPyPtr<std::vector<PbClass *>>(PyObject *obj, std::vector<void *> *tmp)
+{
+ return tmpAlloc<std::vector<PbClass *>>(obj, tmp);
+}
template<> bool isPy<float>(PyObject *obj)
{
@@ -404,6 +475,18 @@ template<> bool isPy<PbType>(PyObject *obj)
{
return PyType_Check(obj);
}
+template<> bool isPy<std::vector<PbClass *>>(PyObject *obj)
+{
+ if (PyList_Check(obj))
+ return true;
+ return false;
+}
+template<> bool isPy<std::vector<float>>(PyObject *obj)
+{
+ if (PyList_Check(obj))
+ return true;
+ return false;
+}
//******************************************************************************
// PbArgs class defs
@@ -417,7 +500,7 @@ PbArgs::PbArgs(PyObject *linarg, PyObject *dict) : mLinArgs(0), mKwds(0)
PbArgs::~PbArgs()
{
for (int i = 0; i < (int)mTmpStorage.size(); i++)
- free(mTmpStorage[i]);
+ operator delete(mTmpStorage[i]);
mTmpStorage.clear();
}
diff --git a/extern/mantaflow/helper/pwrapper/pconvert.h b/extern/mantaflow/helper/pwrapper/pconvert.h
index 9c72b8b57b9..87f4248f6f1 100644
--- a/extern/mantaflow/helper/pwrapper/pconvert.h
+++ b/extern/mantaflow/helper/pwrapper/pconvert.h
@@ -57,6 +57,10 @@ template<> Vec3 *fromPyPtr<Vec3>(PyObject *obj, std::vector<void *> *tmp);
template<> Vec3i *fromPyPtr<Vec3i>(PyObject *obj, std::vector<void *> *tmp);
template<> Vec4 *fromPyPtr<Vec4>(PyObject *obj, std::vector<void *> *tmp);
template<> Vec4i *fromPyPtr<Vec4i>(PyObject *obj, std::vector<void *> *tmp);
+template<>
+std::vector<PbClass *> *fromPyPtr<std::vector<PbClass *>>(PyObject *obj, std::vector<void *> *tmp);
+template<>
+std::vector<float> *fromPyPtr<std::vector<float>>(PyObject *obj, std::vector<void *> *tmp);
PyObject *incref(PyObject *obj);
template<class T> PyObject *toPy(const T &v)
@@ -99,6 +103,9 @@ template<> Vec4 fromPy<Vec4>(PyObject *obj);
template<> Vec4i fromPy<Vec4i>(PyObject *obj);
template<> PbType fromPy<PbType>(PyObject *obj);
template<> PbTypeVec fromPy<PbTypeVec>(PyObject *obj);
+template<> PbClass *fromPy<PbClass *>(PyObject *obj);
+template<> std::vector<PbClass *> fromPy<std::vector<PbClass *>>(PyObject *obj);
+template<> std::vector<float> fromPy<std::vector<float>>(PyObject *obj);
template<> PyObject *toPy<int>(const int &v);
template<> PyObject *toPy<std::string>(const std::string &val);
@@ -111,6 +118,8 @@ template<> PyObject *toPy<Vec4i>(const Vec4i &v);
template<> PyObject *toPy<Vec4>(const Vec4 &v);
typedef PbClass *PbClass_Ptr;
template<> PyObject *toPy<PbClass *>(const PbClass_Ptr &obj);
+template<> PyObject *toPy<std::vector<PbClass *>>(const std::vector<PbClass *> &vec);
+template<> PyObject *toPy<std::vector<float>>(const std::vector<float> &vec);
template<> bool isPy<float>(PyObject *obj);
template<> bool isPy<double>(PyObject *obj);
@@ -124,6 +133,8 @@ template<> bool isPy<Vec3i>(PyObject *obj);
template<> bool isPy<Vec4>(PyObject *obj);
template<> bool isPy<Vec4i>(PyObject *obj);
template<> bool isPy<PbType>(PyObject *obj);
+template<> bool isPy<std::vector<PbClass *>>(PyObject *obj);
+template<> bool isPy<std::vector<float>>(PyObject *obj);
//! Encapsulation of python arguments
class PbArgs {
diff --git a/extern/mantaflow/helper/util/randomstream.h b/extern/mantaflow/helper/util/randomstream.h
index 35b9c7d8858..6c20ddc6a14 100644
--- a/extern/mantaflow/helper/util/randomstream.h
+++ b/extern/mantaflow/helper/util/randomstream.h
@@ -16,13 +16,13 @@
#ifndef _RANDOMSTREAM_H
#define _RANDOMSTREAM_H
-namespace Manta {
-
#include <iostream>
#include <stdio.h>
#include <time.h>
#include "vectorbase.h"
+namespace Manta {
+
class MTRand {
// Data
public:
diff --git a/extern/mantaflow/preprocessed/fileio/iogrids.cpp b/extern/mantaflow/preprocessed/fileio/iogrids.cpp
index acd1bda5174..e2550d6db8b 100644
--- a/extern/mantaflow/preprocessed/fileio/iogrids.cpp
+++ b/extern/mantaflow/preprocessed/fileio/iogrids.cpp
@@ -27,10 +27,6 @@ extern "C" {
}
#endif
-#if OPENVDB == 1
-# include "openvdb/openvdb.h"
-#endif
-
#include "cnpy.h"
#include "mantaio.h"
#include "grid.h"
@@ -279,54 +275,87 @@ static int unifyGridType(int type)
// grid data
//*****************************************************************************
-template<class T> void writeGridTxt(const string &name, Grid<T> *grid)
+template<class T> int writeGridTxt(const string &name, Grid<T> *grid)
{
debMsg("writing grid " << grid->getName() << " to text file " << name, 1);
ofstream ofs(name.c_str());
if (!ofs.good())
errMsg("writeGridTxt: can't open file " << name);
+ return 0;
FOR_IJK(*grid)
{
ofs << Vec3i(i, j, k) << " = " << (*grid)(i, j, k) << "\n";
}
ofs.close();
+ return 1;
+}
+
+int writeGridsTxt(const string &name, std::vector<PbClass *> *grids)
+{
+ errMsg("writeGridsTxt: writing multiple grids to one .txt file not supported yet");
+ return 0;
}
-template<class T> void writeGridRaw(const string &name, Grid<T> *grid)
+int readGridsTxt(const string &name, std::vector<PbClass *> *grids)
+{
+ errMsg("readGridsTxt: writing multiple grids from one .txt file not supported yet");
+ return 0;
+}
+
+template<class T> int writeGridRaw(const string &name, Grid<T> *grid)
{
debMsg("writing grid " << grid->getName() << " to raw file " << name, 1);
#if NO_ZLIB != 1
gzFile gzf = (gzFile)safeGzopen(name.c_str(), "wb1"); // do some compression
- if (!gzf)
+ if (!gzf) {
errMsg("writeGridRaw: can't open file " << name);
+ return 0;
+ }
+
gzwrite(gzf, &((*grid)[0]), sizeof(T) * grid->getSizeX() * grid->getSizeY() * grid->getSizeZ());
- gzclose(gzf);
+ return (gzclose(gzf) == Z_OK);
#else
debMsg("file format not supported without zlib", 1);
+ return 0;
#endif
}
-template<class T> void readGridRaw(const string &name, Grid<T> *grid)
+template<class T> int readGridRaw(const string &name, Grid<T> *grid)
{
debMsg("reading grid " << grid->getName() << " from raw file " << name, 1);
#if NO_ZLIB != 1
gzFile gzf = (gzFile)safeGzopen(name.c_str(), "rb");
- if (!gzf)
+ if (!gzf) {
errMsg("readGridRaw: can't open file " << name);
+ return 0;
+ }
IndexInt bytes = sizeof(T) * grid->getSizeX() * grid->getSizeY() * grid->getSizeZ();
IndexInt readBytes = gzread(gzf, &((*grid)[0]), bytes);
assertMsg(bytes == readBytes,
"can't read raw file, stream length does not match, " << bytes << " vs " << readBytes);
- gzclose(gzf);
+ return (gzclose(gzf) == Z_OK);
#else
debMsg("file format not supported without zlib", 1);
+ return 0;
#endif
}
+int writeGridsRaw(const string &name, std::vector<PbClass *> *grids)
+{
+ errMsg("writeGridsRaw: writing multiple grids to one .raw file not supported yet");
+ return 0;
+}
+
+int readGridsRaw(const string &name, std::vector<PbClass *> *grids)
+{
+ errMsg("readGridsRaw: reading multiple grids from one .raw file not supported yet");
+ return 0;
+}
+
//! legacy headers for reading old files
typedef struct {
int dimX, dimY, dimZ;
@@ -473,7 +502,7 @@ void PbRegister_printUniFileInfoString()
// actual read/write functions
-template<class T> void writeGridUni(const string &name, Grid<T> *grid)
+template<class T> int writeGridUni(const string &name, Grid<T> *grid)
{
debMsg("Writing grid " << grid->getName() << " to uni file " << name, 1);
@@ -496,12 +525,16 @@ template<class T> void writeGridUni(const string &name, Grid<T> *grid)
head.elementType = 1;
else if (grid->getType() & GridBase::TypeVec3)
head.elementType = 2;
- else
+ else {
errMsg("writeGridUni: unknown element type");
+ return 0;
+ }
gzFile gzf = (gzFile)safeGzopen(name.c_str(), "wb1"); // do some compression
- if (!gzf)
+ if (!gzf) {
errMsg("writeGridUni: can't open file " << name);
+ return 0;
+ }
gzwrite(gzf, ID, 4);
# if FLOATINGPOINT_PRECISION != 1
@@ -515,21 +548,24 @@ template<class T> void writeGridUni(const string &name, Grid<T> *grid)
gzwrite(gzf, &head, sizeof(UniHeader));
gzwrite(gzf, ptr, sizeof(T) * head.dimX * head.dimY * head.dimZ);
# endif
- gzclose(gzf);
+ return (gzclose(gzf) == Z_OK);
#else
debMsg("file format not supported without zlib", 1);
+ return 0;
#endif
};
-template<class T> void readGridUni(const string &name, Grid<T> *grid)
+template<class T> int readGridUni(const string &name, Grid<T> *grid)
{
debMsg("Reading grid " << grid->getName() << " from uni file " << name, 1);
#if NO_ZLIB != 1
gzFile gzf = (gzFile)safeGzopen(name.c_str(), "rb");
- if (!gzf)
+ if (!gzf) {
errMsg("readGridUni: can't open file " << name);
+ return 0;
+ }
char ID[5] = {0, 0, 0, 0, 0};
gzread(gzf, ID, 4);
@@ -609,17 +645,44 @@ template<class T> void readGridUni(const string &name, Grid<T> *grid)
}
else {
errMsg("readGridUni: Unknown header '" << ID << "' ");
+ return 0;
}
- gzclose(gzf);
+ return (gzclose(gzf) == Z_OK);
#else
debMsg("file format not supported without zlib", 1);
+ return 0;
#endif
};
-template<class T> void writeGridVol(const string &name, Grid<T> *grid)
+int writeGridsUni(const string &name, std::vector<PbClass *> *grids)
+{
+ errMsg("writeGridsUni: writing multiple grids to one .uni file not supported yet");
+ return 0;
+}
+
+int readGridsUni(const string &name, std::vector<PbClass *> *grids)
+{
+ errMsg("readGridsUni: reading multiple grids from one .uni file not supported yet");
+ return 0;
+}
+
+template<class T> int writeGridVol(const string &name, Grid<T> *grid)
{
debMsg("writing grid " << grid->getName() << " to vol file " << name, 1);
errMsg("writeGridVol: Type not yet supported!");
+ return 0;
+}
+
+int writeGridsVol(const string &name, std::vector<PbClass *> *grids)
+{
+ errMsg("writeGridsVol: writing multiple grids to one .vol file not supported yet");
+ return 0;
+}
+
+int readGridsVol(const string &name, std::vector<PbClass *> *grids)
+{
+ errMsg("readGridsVol: reading multiple grids from one .vol file not supported yet");
+ return 0;
}
struct volHeader {
@@ -631,7 +694,7 @@ struct volHeader {
Vec3 bboxMin, bboxMax;
};
-template<> void writeGridVol<Real>(const string &name, Grid<Real> *grid)
+template<> int writeGridVol<Real>(const string &name, Grid<Real> *grid)
{
debMsg("writing real grid " << grid->getName() << " to vol file " << name, 1);
@@ -651,7 +714,7 @@ template<> void writeGridVol<Real>(const string &name, Grid<Real> *grid)
FILE *fp = fopen(name.c_str(), "wb");
if (fp == NULL) {
errMsg("writeGridVol: Cannot open '" << name << "'");
- return;
+ return 0;
}
fwrite(&header, sizeof(volHeader), 1, fp);
@@ -667,17 +730,17 @@ template<> void writeGridVol<Real>(const string &name, Grid<Real> *grid)
fwrite(&value, sizeof(float), 1, fp);
}
#endif
-
- fclose(fp);
+ return (!fclose(fp));
};
-template<class T> void readGridVol(const string &name, Grid<T> *grid)
+template<class T> int readGridVol(const string &name, Grid<T> *grid)
{
debMsg("writing grid " << grid->getName() << " to vol file " << name, 1);
errMsg("readGridVol: Type not yet supported!");
+ return 0;
}
-template<> void readGridVol<Real>(const string &name, Grid<Real> *grid)
+template<> int readGridVol<Real>(const string &name, Grid<Real> *grid)
{
debMsg("reading real grid " << grid->getName() << " from vol file " << name, 1);
@@ -685,7 +748,7 @@ template<> void readGridVol<Real>(const string &name, Grid<Real> *grid)
FILE *fp = fopen(name.c_str(), "rb");
if (fp == NULL) {
errMsg("readGridVol: Cannot open '" << name << "'");
- return;
+ return 0;
}
// note, only very basic file format checks here!
@@ -697,17 +760,17 @@ template<> void readGridVol<Real>(const string &name, Grid<Real> *grid)
<< grid->getSize());
#if FLOATINGPOINT_PRECISION != 1
errMsg("readGridVol: Double precision not yet supported");
+ return 0;
#else
const unsigned int s = sizeof(float) * header.dimX * header.dimY * header.dimZ;
assertMsg(fread(&((*grid)[0]), 1, s, fp) == s, "can't read file, no / not enough data");
#endif
-
- fclose(fp);
+ return (!fclose(fp));
};
// 4d grids IO
-template<class T> void writeGrid4dUni(const string &name, Grid4d<T> *grid)
+template<class T> int writeGrid4dUni(const string &name, Grid4d<T> *grid)
{
debMsg("writing grid4d " << grid->getName() << " to uni file " << name, 1);
@@ -733,12 +796,16 @@ template<class T> void writeGrid4dUni(const string &name, Grid4d<T> *grid)
head.elementType = 2;
else if (grid->getType() & Grid4dBase::TypeVec4)
head.elementType = 2;
- else
+ else {
errMsg("writeGrid4dUni: unknown element type");
+ return 0;
+ }
gzFile gzf = (gzFile)safeGzopen(name.c_str(), "wb1"); // do some compression
- if (!gzf)
+ if (!gzf) {
errMsg("writeGrid4dUni: can't open file " << name);
+ return 0;
+ }
gzwrite(gzf, ID, 4);
# if FLOATINGPOINT_PRECISION != 1
@@ -753,16 +820,17 @@ template<class T> void writeGrid4dUni(const string &name, Grid4d<T> *grid)
gzwrite(gzf, ptr, sizeof(T) * head.dimX * head.dimY * head.dimZ * 1);
}
# endif
- gzclose(gzf);
+ return (gzclose(gzf) == Z_OK);
#else
debMsg("file format not supported without zlib", 1);
+ return 0;
#endif
};
//! note, reading 4d uni grids is slightly more complicated than 3d ones
//! as it optionally supports sliced reading
template<class T>
-void readGrid4dUni(
+int readGrid4dUni(
const string &name, Grid4d<T> *grid, int readTslice, Grid4d<T> *slice, void **fileHandle)
{
if (grid)
@@ -779,8 +847,10 @@ void readGrid4dUni(
// optionally - reuse file handle, if valid one is passed in fileHandle pointer...
if ((!fileHandle) || (fileHandle && (*fileHandle == NULL))) {
gzf = (gzFile)safeGzopen(name.c_str(), "rb");
- if (!gzf)
+ if (!gzf) {
errMsg("readGrid4dUni: can't open file " << name);
+ return 0;
+ }
gzread(gzf, ID, 4);
if (fileHandle) {
@@ -792,7 +862,7 @@ void readGrid4dUni(
gzf = (gzFile)(*fileHandle);
void *ptr = &((*slice)[0]);
gzread(gzf, ptr, sizeof(T) * slice->getStrideT() * 1); // quick and dirty...
- return;
+ return 1;
}
if ((!strcmp(ID, "M4T2")) || (!strcmp(ID, "M4T3"))) {
@@ -867,6 +937,7 @@ void readGrid4dUni(
# if FLOATINGPOINT_PRECISION != 1
errMsg("readGrid4dUni: NYI (2)"); // slice read not yet supported for double
+ return 0;
# else
assertMsg(slice, "No 3d slice grid data given");
assertMsg(readTslice < head.dimT,
@@ -884,10 +955,12 @@ void readGrid4dUni(
}
if (!fileHandle) {
- gzclose(gzf);
+ return (gzclose(gzf) == Z_OK);
}
+ return 1;
#else
debMsg("file format not supported without zlib", 1);
+ return 0;
#endif
};
void readGrid4dUniCleanup(void **fileHandle)
@@ -900,296 +973,61 @@ void readGrid4dUniCleanup(void **fileHandle)
}
}
-template<class T> void writeGrid4dRaw(const string &name, Grid4d<T> *grid)
+template<class T> int writeGrid4dRaw(const string &name, Grid4d<T> *grid)
{
debMsg("writing grid4d " << grid->getName() << " to raw file " << name, 1);
#if NO_ZLIB != 1
gzFile gzf = (gzFile)safeGzopen(name.c_str(), "wb1"); // do some compression
- if (!gzf)
+ if (!gzf) {
errMsg("writeGrid4dRaw: can't open file " << name);
+ return 0;
+ }
gzwrite(gzf,
&((*grid)[0]),
sizeof(T) * grid->getSizeX() * grid->getSizeY() * grid->getSizeZ() * grid->getSizeT());
- gzclose(gzf);
+ return (gzclose(gzf) == Z_OK);
#else
debMsg("file format not supported without zlib", 1);
+ return 0;
#endif
}
-template<class T> void readGrid4dRaw(const string &name, Grid4d<T> *grid)
+template<class T> int readGrid4dRaw(const string &name, Grid4d<T> *grid)
{
debMsg("reading grid4d " << grid->getName() << " from raw file " << name, 1);
#if NO_ZLIB != 1
gzFile gzf = (gzFile)safeGzopen(name.c_str(), "rb");
- if (!gzf)
+ if (!gzf) {
errMsg("readGrid4dRaw: can't open file " << name);
-
+ return 0;
+ }
IndexInt bytes = sizeof(T) * grid->getSizeX() * grid->getSizeY() * grid->getSizeZ() *
grid->getSizeT();
IndexInt readBytes = gzread(gzf, &((*grid)[0]), bytes);
assertMsg(bytes == readBytes,
"can't read raw file, stream length does not match, " << bytes << " vs " << readBytes);
- gzclose(gzf);
+ return (gzclose(gzf) == Z_OK);
#else
debMsg("file format not supported without zlib", 1);
+ return 0;
#endif
}
//*****************************************************************************
-// optional openvdb export
-
-#if OPENVDB == 1
-
-template<class T> void writeGridVDB(const string &name, Grid<T> *grid)
-{
- debMsg("Writing grid " << grid->getName() << " to vdb file " << name << " not yet supported!",
- 1);
-}
-
-template<class T> void readGridVDB(const string &name, Grid<T> *grid)
-{
- debMsg("Reading grid " << grid->getName() << " from vdb file " << name << " not yet supported!",
- 1);
-}
-
-template<> void writeGridVDB(const string &name, Grid<int> *grid)
-{
- debMsg("Writing int grid " << grid->getName() << " to vdb file " << name, 1);
-
- // Create an empty int32-point grid with background value 0.
- openvdb::initialize();
- openvdb::Int32Grid::Ptr gridVDB = openvdb::Int32Grid::create();
- gridVDB->setTransform(
- openvdb::math::Transform::createLinearTransform(1. / grid->getSizeX())); // voxel size
-
- // Get an accessor for coordinate-based access to voxels.
- openvdb::Int32Grid::Accessor accessor = gridVDB->getAccessor();
-
- gridVDB->setGridClass(openvdb::GRID_UNKNOWN);
-
- // Name the grid "density".
- gridVDB->setName(grid->getName());
-
- openvdb::io::File file(name);
-
- FOR_IJK(*grid)
- {
- openvdb::Coord xyz(i, j, k);
- accessor.setValue(xyz, (*grid)(i, j, k));
- }
-
- // Add the grid pointer to a container.
- openvdb::GridPtrVec gridsVDB;
- gridsVDB.push_back(gridVDB);
-
- // Write out the contents of the container.
- file.write(gridsVDB);
- file.close();
-}
-
-template<> void readGridVDB(const string &name, Grid<int> *grid)
-{
- debMsg("Reading int grid " << grid->getName() << " from vdb file " << name, 1);
-
- openvdb::initialize();
- openvdb::io::File file(name);
- file.open();
-
- openvdb::GridBase::Ptr baseGrid;
- for (openvdb::io::File::NameIterator nameIter = file.beginName(); nameIter != file.endName();
- ++nameIter) {
-# ifndef BLENDER
- // Read in only the grid we are interested in.
- if (nameIter.gridName() == grid->getName()) {
- baseGrid = file.readGrid(nameIter.gridName());
- }
- else {
- debMsg("skipping grid " << nameIter.gridName(), 1);
- }
-# else
- // For Blender, skip name check and pick first grid from loop
- baseGrid = file.readGrid(nameIter.gridName());
- break;
-# endif
- }
- file.close();
- openvdb::Int32Grid::Ptr gridVDB = openvdb::gridPtrCast<openvdb::Int32Grid>(baseGrid);
-
- openvdb::Int32Grid::Accessor accessor = gridVDB->getAccessor();
-
- FOR_IJK(*grid)
- {
- openvdb::Coord xyz(i, j, k);
- int v = accessor.getValue(xyz);
- (*grid)(i, j, k) = v;
- }
-}
-
-template<> void writeGridVDB(const string &name, Grid<Real> *grid)
-{
- debMsg("Writing real grid " << grid->getName() << " to vdb file " << name, 1);
-
- // Create an empty floating-point grid with background value 0.
- openvdb::initialize();
- openvdb::FloatGrid::Ptr gridVDB = openvdb::FloatGrid::create();
- gridVDB->setTransform(
- openvdb::math::Transform::createLinearTransform(1. / grid->getSizeX())); // voxel size
-
- // Get an accessor for coordinate-based access to voxels.
- openvdb::FloatGrid::Accessor accessor = gridVDB->getAccessor();
-
- // Identify the grid as a level set.
- gridVDB->setGridClass(openvdb::GRID_FOG_VOLUME);
-
- // Name the grid "density".
- gridVDB->setName(grid->getName());
-
- openvdb::io::File file(name);
-
- FOR_IJK(*grid)
- {
- openvdb::Coord xyz(i, j, k);
- accessor.setValue(xyz, (*grid)(i, j, k));
- }
-
- // Add the grid pointer to a container.
- openvdb::GridPtrVec gridsVDB;
- gridsVDB.push_back(gridVDB);
-
- // Write out the contents of the container.
- file.write(gridsVDB);
- file.close();
-};
-
-template<> void readGridVDB(const string &name, Grid<Real> *grid)
-{
- debMsg("Reading real grid " << grid->getName() << " from vdb file " << name, 1);
-
- openvdb::initialize();
- openvdb::io::File file(name);
- file.open();
-
- openvdb::GridBase::Ptr baseGrid;
- for (openvdb::io::File::NameIterator nameIter = file.beginName(); nameIter != file.endName();
- ++nameIter) {
-# ifndef BLENDER
- // Read in only the grid we are interested in.
- if (nameIter.gridName() == grid->getName()) {
- baseGrid = file.readGrid(nameIter.gridName());
- }
- else {
- debMsg("skipping grid " << nameIter.gridName(), 1);
- }
-# else
- // For Blender, skip name check and pick first grid from loop
- baseGrid = file.readGrid(nameIter.gridName());
- break;
-# endif
- }
- file.close();
- openvdb::FloatGrid::Ptr gridVDB = openvdb::gridPtrCast<openvdb::FloatGrid>(baseGrid);
-
- openvdb::FloatGrid::Accessor accessor = gridVDB->getAccessor();
-
- FOR_IJK(*grid)
- {
- openvdb::Coord xyz(i, j, k);
- float v = accessor.getValue(xyz);
- (*grid)(i, j, k) = v;
- }
-};
-
-template<> void writeGridVDB(const string &name, Grid<Vec3> *grid)
-{
- debMsg("Writing vec3 grid " << grid->getName() << " to vdb file " << name, 1);
-
- openvdb::initialize();
- openvdb::Vec3SGrid::Ptr gridVDB = openvdb::Vec3SGrid::create();
- // note , warning - velocity content currently not scaled...
- gridVDB->setTransform(
- openvdb::math::Transform::createLinearTransform(1. / grid->getSizeX())); // voxel size
- openvdb::Vec3SGrid::Accessor accessor = gridVDB->getAccessor();
-
- // MAC or regular vec grid?
- if (grid->getType() & GridBase::TypeMAC)
- gridVDB->setGridClass(openvdb::GRID_STAGGERED);
- else
- gridVDB->setGridClass(openvdb::GRID_UNKNOWN);
-
- gridVDB->setName(grid->getName());
-
- openvdb::io::File file(name);
- FOR_IJK(*grid)
- {
- openvdb::Coord xyz(i, j, k);
- Vec3 v = (*grid)(i, j, k);
- openvdb::Vec3f vo((float)v[0], (float)v[1], (float)v[2]);
- accessor.setValue(xyz, vo);
- }
-
- openvdb::GridPtrVec gridsVDB;
- gridsVDB.push_back(gridVDB);
-
- file.write(gridsVDB);
- file.close();
-};
-
-template<> void readGridVDB(const string &name, Grid<Vec3> *grid)
-{
- debMsg("Reading vec3 grid " << grid->getName() << " from vdb file " << name, 1);
-
- openvdb::initialize();
- openvdb::io::File file(name);
- file.open();
-
- openvdb::GridBase::Ptr baseGrid;
- for (openvdb::io::File::NameIterator nameIter = file.beginName(); nameIter != file.endName();
- ++nameIter) {
-# ifndef BLENDER
- // Read in only the grid we are interested in.
- if (nameIter.gridName() == grid->getName()) {
- baseGrid = file.readGrid(nameIter.gridName());
- }
- else {
- debMsg("skipping grid " << nameIter.gridName(), 1);
- }
-# else
- // For Blender, skip name check and pick first grid from loop
- baseGrid = file.readGrid(nameIter.gridName());
- break;
-# endif
- }
- file.close();
- openvdb::Vec3SGrid::Ptr gridVDB = openvdb::gridPtrCast<openvdb::Vec3SGrid>(baseGrid);
-
- openvdb::Vec3SGrid::Accessor accessor = gridVDB->getAccessor();
-
- FOR_IJK(*grid)
- {
- openvdb::Coord xyz(i, j, k);
- openvdb::Vec3f v = accessor.getValue(xyz);
- (*grid)(i, j, k).x = (float)v[0];
- (*grid)(i, j, k).y = (float)v[1];
- (*grid)(i, j, k).z = (float)v[2];
- }
-};
-
-#endif // OPENVDB==1
-
-//*****************************************************************************
// npz file support (warning - read works, but write generates uncompressed npz; i.e. not
// recommended for large volumes)
-template<class T> void writeGridNumpy(const string &name, Grid<T> *grid)
+template<class T> int writeGridNumpy(const string &name, Grid<T> *grid)
{
#if NO_ZLIB == 1
debMsg("file format not supported without zlib", 1);
- return;
+ return 0;
#endif
#if FLOATINGPOINT_PRECISION != 1
errMsg("writeGridNumpy: Double precision not yet supported");
+ return 0;
#endif
// find suffix to differentiate between npy <-> npz , TODO: check for actual "npy" string
@@ -1211,8 +1049,10 @@ template<class T> void writeGridNumpy(const string &name, Grid<T> *grid)
uDim = 1;
else if (grid->getType() & GridBase::TypeVec3 || grid->getType() & GridBase::TypeMAC)
uDim = 3;
- else
+ else {
errMsg("writeGridNumpy: unknown element type");
+ return 0;
+ }
const std::vector<size_t> shape = {static_cast<size_t>(grid->getSizeZ()),
static_cast<size_t>(grid->getSizeY()),
@@ -1234,16 +1074,18 @@ template<class T> void writeGridNumpy(const string &name, Grid<T> *grid)
else {
cnpy::npy_save(name, &grid[0], shape, "w");
}
+ return 1;
};
-template<class T> void readGridNumpy(const string &name, Grid<T> *grid)
+template<class T> int readGridNumpy(const string &name, Grid<T> *grid)
{
#if NO_ZLIB == 1
debMsg("file format not supported without zlib", 1);
- return;
+ return 0;
#endif
#if FLOATINGPOINT_PRECISION != 1
errMsg("readGridNumpy: Double precision not yet supported");
+ return 0;
#endif
// find suffix to differentiate between npy <-> npz
@@ -1279,8 +1121,10 @@ template<class T> void readGridNumpy(const string &name, Grid<T> *grid)
uDim = 1;
else if (grid->getType() & GridBase::TypeVec3 || grid->getType() & GridBase::TypeMAC)
uDim = 3;
- else
+ else {
errMsg("readGridNumpy: unknown element type");
+ return 0;
+ }
assertMsg(gridArr.shape[3] == uDim,
"grid data dim doesn't match, " << gridArr.shape[3] << " vs " << uDim);
@@ -1299,8 +1143,21 @@ template<class T> void readGridNumpy(const string &name, Grid<T> *grid)
memcpy(&((*grid)[0]),
gridArr.data<T>(),
sizeof(T) * grid->getSizeX() * grid->getSizeY() * grid->getSizeZ());
+ return 1;
};
+int writeGridsNumpy(const string &name, std::vector<PbClass *> *grids)
+{
+ errMsg("writeGridsNumpy: writing multiple grids to one .npz file not supported yet");
+ return 0;
+}
+
+int readGridsNumpy(const string &name, std::vector<PbClass *> *grids)
+{
+ errMsg("readGridsNumpy: reading multiple grids from one .npz file not supported yet");
+ return 0;
+}
+
// adopted from getUniFileSize
void getNpzFileSize(
const string &name, int &x, int &y, int &z, int *t = NULL, std::string *info = NULL)
@@ -1521,77 +1378,63 @@ void PbRegister_quantizeGridVec3()
}
// explicit instantiation
-template void writeGridRaw<int>(const string &name, Grid<int> *grid);
-template void writeGridRaw<Real>(const string &name, Grid<Real> *grid);
-template void writeGridRaw<Vec3>(const string &name, Grid<Vec3> *grid);
-template void writeGridUni<int>(const string &name, Grid<int> *grid);
-template void writeGridUni<Real>(const string &name, Grid<Real> *grid);
-template void writeGridUni<Vec3>(const string &name, Grid<Vec3> *grid);
-template void writeGridVol<int>(const string &name, Grid<int> *grid);
-template void writeGridVol<Vec3>(const string &name, Grid<Vec3> *grid);
-template void writeGridTxt<int>(const string &name, Grid<int> *grid);
-template void writeGridTxt<Real>(const string &name, Grid<Real> *grid);
-template void writeGridTxt<Vec3>(const string &name, Grid<Vec3> *grid);
-
-template void readGridRaw<int>(const string &name, Grid<int> *grid);
-template void readGridRaw<Real>(const string &name, Grid<Real> *grid);
-template void readGridRaw<Vec3>(const string &name, Grid<Vec3> *grid);
-template void readGridUni<int>(const string &name, Grid<int> *grid);
-template void readGridUni<Real>(const string &name, Grid<Real> *grid);
-template void readGridUni<Vec3>(const string &name, Grid<Vec3> *grid);
-template void readGridVol<int>(const string &name, Grid<int> *grid);
-template void readGridVol<Vec3>(const string &name, Grid<Vec3> *grid);
-
-template void readGrid4dUni<int>(
+template int writeGridRaw<int>(const string &name, Grid<int> *grid);
+template int writeGridRaw<Real>(const string &name, Grid<Real> *grid);
+template int writeGridRaw<Vec3>(const string &name, Grid<Vec3> *grid);
+template int writeGridUni<int>(const string &name, Grid<int> *grid);
+template int writeGridUni<Real>(const string &name, Grid<Real> *grid);
+template int writeGridUni<Vec3>(const string &name, Grid<Vec3> *grid);
+template int writeGridVol<int>(const string &name, Grid<int> *grid);
+template int writeGridVol<Vec3>(const string &name, Grid<Vec3> *grid);
+template int writeGridTxt<int>(const string &name, Grid<int> *grid);
+template int writeGridTxt<Real>(const string &name, Grid<Real> *grid);
+template int writeGridTxt<Vec3>(const string &name, Grid<Vec3> *grid);
+
+template int readGridRaw<int>(const string &name, Grid<int> *grid);
+template int readGridRaw<Real>(const string &name, Grid<Real> *grid);
+template int readGridRaw<Vec3>(const string &name, Grid<Vec3> *grid);
+template int readGridUni<int>(const string &name, Grid<int> *grid);
+template int readGridUni<Real>(const string &name, Grid<Real> *grid);
+template int readGridUni<Vec3>(const string &name, Grid<Vec3> *grid);
+template int readGridVol<int>(const string &name, Grid<int> *grid);
+template int readGridVol<Vec3>(const string &name, Grid<Vec3> *grid);
+
+template int readGrid4dUni<int>(
const string &name, Grid4d<int> *grid, int readTslice, Grid4d<int> *slice, void **fileHandle);
-template void readGrid4dUni<Real>(const string &name,
- Grid4d<Real> *grid,
- int readTslice,
- Grid4d<Real> *slice,
- void **fileHandle);
-template void readGrid4dUni<Vec3>(const string &name,
- Grid4d<Vec3> *grid,
- int readTslice,
- Grid4d<Vec3> *slice,
- void **fileHandle);
-template void readGrid4dUni<Vec4>(const string &name,
- Grid4d<Vec4> *grid,
- int readTslice,
- Grid4d<Vec4> *slice,
- void **fileHandle);
-template void writeGrid4dUni<int>(const string &name, Grid4d<int> *grid);
-template void writeGrid4dUni<Real>(const string &name, Grid4d<Real> *grid);
-template void writeGrid4dUni<Vec3>(const string &name, Grid4d<Vec3> *grid);
-template void writeGrid4dUni<Vec4>(const string &name, Grid4d<Vec4> *grid);
-
-template void readGrid4dRaw<int>(const string &name, Grid4d<int> *grid);
-template void readGrid4dRaw<Real>(const string &name, Grid4d<Real> *grid);
-template void readGrid4dRaw<Vec3>(const string &name, Grid4d<Vec3> *grid);
-template void readGrid4dRaw<Vec4>(const string &name, Grid4d<Vec4> *grid);
-template void writeGrid4dRaw<int>(const string &name, Grid4d<int> *grid);
-template void writeGrid4dRaw<Real>(const string &name, Grid4d<Real> *grid);
-template void writeGrid4dRaw<Vec3>(const string &name, Grid4d<Vec3> *grid);
-template void writeGrid4dRaw<Vec4>(const string &name, Grid4d<Vec4> *grid);
-
-template void writeGridNumpy<int>(const string &name, Grid<int> *grid);
-template void writeGridNumpy<Real>(const string &name, Grid<Real> *grid);
-template void writeGridNumpy<Vec3>(const string &name, Grid<Vec3> *grid);
-template void readGridNumpy<int>(const string &name, Grid<int> *grid);
-template void readGridNumpy<Real>(const string &name, Grid<Real> *grid);
-template void readGridNumpy<Vec3>(const string &name, Grid<Vec3> *grid);
-
-#if OPENVDB == 1
-template void writeGridVDB<int>(const string &name, Grid<int> *grid);
-template void writeGridVDB<Vec3>(const string &name, Grid<Vec3> *grid);
-template void writeGridVDB<Real>(const string &name, Grid<Real> *grid);
-
-template void readGridVDB<int>(const string &name, Grid<int> *grid);
-template void readGridVDB<Vec3>(const string &name, Grid<Vec3> *grid);
-template void readGridVDB<Real>(const string &name, Grid<Real> *grid);
-#endif // OPENVDB==1
+template int readGrid4dUni<Real>(const string &name,
+ Grid4d<Real> *grid,
+ int readTslice,
+ Grid4d<Real> *slice,
+ void **fileHandle);
+template int readGrid4dUni<Vec3>(const string &name,
+ Grid4d<Vec3> *grid,
+ int readTslice,
+ Grid4d<Vec3> *slice,
+ void **fileHandle);
+template int readGrid4dUni<Vec4>(const string &name,
+ Grid4d<Vec4> *grid,
+ int readTslice,
+ Grid4d<Vec4> *slice,
+ void **fileHandle);
+template int writeGrid4dUni<int>(const string &name, Grid4d<int> *grid);
+template int writeGrid4dUni<Real>(const string &name, Grid4d<Real> *grid);
+template int writeGrid4dUni<Vec3>(const string &name, Grid4d<Vec3> *grid);
+template int writeGrid4dUni<Vec4>(const string &name, Grid4d<Vec4> *grid);
+
+template int readGrid4dRaw<int>(const string &name, Grid4d<int> *grid);
+template int readGrid4dRaw<Real>(const string &name, Grid4d<Real> *grid);
+template int readGrid4dRaw<Vec3>(const string &name, Grid4d<Vec3> *grid);
+template int readGrid4dRaw<Vec4>(const string &name, Grid4d<Vec4> *grid);
+template int writeGrid4dRaw<int>(const string &name, Grid4d<int> *grid);
+template int writeGrid4dRaw<Real>(const string &name, Grid4d<Real> *grid);
+template int writeGrid4dRaw<Vec3>(const string &name, Grid4d<Vec3> *grid);
+template int writeGrid4dRaw<Vec4>(const string &name, Grid4d<Vec4> *grid);
+
+template int writeGridNumpy<int>(const string &name, Grid<int> *grid);
+template int writeGridNumpy<Real>(const string &name, Grid<Real> *grid);
+template int writeGridNumpy<Vec3>(const string &name, Grid<Vec3> *grid);
+template int readGridNumpy<int>(const string &name, Grid<int> *grid);
+template int readGridNumpy<Real>(const string &name, Grid<Real> *grid);
+template int readGridNumpy<Vec3>(const string &name, Grid<Vec3> *grid);
} // namespace Manta
-
-namespace Manta {
-
-}
diff --git a/extern/mantaflow/preprocessed/fileio/iomeshes.cpp b/extern/mantaflow/preprocessed/fileio/iomeshes.cpp
index 906b849fffb..1c50376de77 100644
--- a/extern/mantaflow/preprocessed/fileio/iomeshes.cpp
+++ b/extern/mantaflow/preprocessed/fileio/iomeshes.cpp
@@ -146,21 +146,25 @@ void mdataReadConvert<Vec3>(gzFile &gzf, MeshDataImpl<Vec3> &mdata, void *ptr, i
// mesh data
//*****************************************************************************
-void readBobjFile(const string &name, Mesh *mesh, bool append)
+int readBobjFile(const string &name, Mesh *mesh, bool append)
{
debMsg("reading mesh file " << name, 1);
if (!append)
mesh->clear();
- else
+ else {
errMsg("readBobj: append not yet implemented!");
+ return 0;
+ }
#if NO_ZLIB != 1
const Real dx = mesh->getParent()->getDx();
const Vec3 gs = toVec3(mesh->getParent()->getGridSize());
gzFile gzf = (gzFile)safeGzopen(name.c_str(), "rb1"); // do some compression
- if (!gzf)
+ if (!gzf) {
errMsg("readBobj: unable to open file");
+ return 0;
+ }
// read vertices
int num = 0;
@@ -198,15 +202,16 @@ void readBobjFile(const string &name, Mesh *mesh, bool append)
}
}
// note - vortex sheet info ignored for now... (see writeBobj)
- gzclose(gzf);
debMsg("read mesh , triangles " << mesh->numTris() << ", vertices " << mesh->numNodes() << " ",
1);
+ return (gzclose(gzf) == Z_OK);
#else
debMsg("file format not supported without zlib", 1);
+ return 0;
#endif
}
-void writeBobjFile(const string &name, Mesh *mesh)
+int writeBobjFile(const string &name, Mesh *mesh)
{
debMsg("writing mesh file " << name, 1);
#if NO_ZLIB != 1
@@ -214,8 +219,10 @@ void writeBobjFile(const string &name, Mesh *mesh)
const Vec3i gs = mesh->getParent()->getGridSize();
gzFile gzf = (gzFile)safeGzopen(name.c_str(), "wb1"); // do some compression
- if (!gzf)
+ if (!gzf) {
errMsg("writeBobj: unable to open file");
+ return 0;
+ }
// write vertices
int numVerts = mesh->numNodes();
@@ -292,18 +299,21 @@ void writeBobjFile(const string &name, Mesh *mesh)
}
}
- gzclose(gzf);
+ return (gzclose(gzf) == Z_OK);
#else
debMsg("file format not supported without zlib", 1);
+ return 0;
#endif
}
-void readObjFile(const std::string &name, Mesh *mesh, bool append)
+int readObjFile(const std::string &name, Mesh *mesh, bool append)
{
ifstream ifs(name.c_str());
- if (!ifs.good())
+ if (!ifs.good()) {
errMsg("can't open file '" + name + "'");
+ return 0;
+ }
if (!append)
mesh->clear();
@@ -323,8 +333,10 @@ void readObjFile(const std::string &name, Mesh *mesh, bool append)
}
else if (id == "vn") {
// normals
- if (!mesh->numNodes())
+ if (!mesh->numNodes()) {
errMsg("invalid amount of nodes");
+ return 0;
+ }
Node n = mesh->nodes(cnt);
ifs >> n.normal.x >> n.normal.y >> n.normal.z;
cnt++;
@@ -349,8 +361,10 @@ void readObjFile(const std::string &name, Mesh *mesh, bool append)
if (face.find('/') != string::npos)
face = face.substr(0, face.find('/')); // ignore other indices
int idx = atoi(face.c_str()) - 1;
- if (idx < 0)
+ if (idx < 0) {
errMsg("invalid face encountered");
+ return 0;
+ }
idx += nodebase;
t.c[i] = idx;
}
@@ -363,17 +377,20 @@ void readObjFile(const std::string &name, Mesh *mesh, bool append)
getline(ifs, id);
}
ifs.close();
+ return 1;
}
// write regular .obj file, in line with bobj.gz output (but only verts & tris for now)
-void writeObjFile(const string &name, Mesh *mesh)
+int writeObjFile(const string &name, Mesh *mesh)
{
const Real dx = mesh->getParent()->getDx();
const Vec3i gs = mesh->getParent()->getGridSize();
ofstream ofs(name.c_str());
- if (!ofs.good())
+ if (!ofs.good()) {
errMsg("writeObjFile: can't open file " << name);
+ return 0;
+ }
ofs << "o MantaMesh\n";
@@ -405,16 +422,19 @@ void writeObjFile(const string &name, Mesh *mesh)
}
ofs.close();
+ return 1;
}
-template<class T> void readMdataUni(const std::string &name, MeshDataImpl<T> *mdata)
+template<class T> int readMdataUni(const std::string &name, MeshDataImpl<T> *mdata)
{
debMsg("reading mesh data " << mdata->getName() << " from uni file " << name, 1);
#if NO_ZLIB != 1
gzFile gzf = (gzFile)safeGzopen(name.c_str(), "rb");
- if (!gzf)
+ if (!gzf) {
errMsg("can't open file " << name);
+ return 0;
+ }
char ID[5] = {0, 0, 0, 0, 0};
gzread(gzf, ID, 4);
@@ -440,13 +460,14 @@ template<class T> void readMdataUni(const std::string &name, MeshDataImpl<T> *md
<< readBytes);
# endif
}
- gzclose(gzf);
+ return (gzclose(gzf) == Z_OK);
#else
debMsg("file format not supported without zlib", 1);
+ return 0;
#endif
}
-template<class T> void writeMdataUni(const std::string &name, MeshDataImpl<T> *mdata)
+template<class T> int writeMdataUni(const std::string &name, MeshDataImpl<T> *mdata)
{
debMsg("writing mesh data " << mdata->getName() << " to uni file " << name, 1);
@@ -461,8 +482,10 @@ template<class T> void writeMdataUni(const std::string &name, MeshDataImpl<T> *m
head.timestamp = stamp.time;
gzFile gzf = (gzFile)safeGzopen(name.c_str(), "wb1"); // do some compression
- if (!gzf)
+ if (!gzf) {
errMsg("can't open file " << name);
+ return 0;
+ }
gzwrite(gzf, ID, 4);
# if FLOATINGPOINT_PRECISION != 1
@@ -474,19 +497,20 @@ template<class T> void writeMdataUni(const std::string &name, MeshDataImpl<T> *m
gzwrite(gzf, &head, sizeof(UniMeshHeader));
gzwrite(gzf, &(mdata->get(0)), sizeof(T) * head.dim);
# endif
- gzclose(gzf);
+ return (gzclose(gzf) == Z_OK);
#else
debMsg("file format not supported without zlib", 1);
+ return 0;
#endif
};
// explicit instantiation
-template void writeMdataUni<int>(const std::string &name, MeshDataImpl<int> *mdata);
-template void writeMdataUni<Real>(const std::string &name, MeshDataImpl<Real> *mdata);
-template void writeMdataUni<Vec3>(const std::string &name, MeshDataImpl<Vec3> *mdata);
-template void readMdataUni<int>(const std::string &name, MeshDataImpl<int> *mdata);
-template void readMdataUni<Real>(const std::string &name, MeshDataImpl<Real> *mdata);
-template void readMdataUni<Vec3>(const std::string &name, MeshDataImpl<Vec3> *mdata);
+template int writeMdataUni<int>(const std::string &name, MeshDataImpl<int> *mdata);
+template int writeMdataUni<Real>(const std::string &name, MeshDataImpl<Real> *mdata);
+template int writeMdataUni<Vec3>(const std::string &name, MeshDataImpl<Vec3> *mdata);
+template int readMdataUni<int>(const std::string &name, MeshDataImpl<int> *mdata);
+template int readMdataUni<Real>(const std::string &name, MeshDataImpl<Real> *mdata);
+template int readMdataUni<Vec3>(const std::string &name, MeshDataImpl<Vec3> *mdata);
} // namespace Manta
diff --git a/extern/mantaflow/preprocessed/fileio/ioparticles.cpp b/extern/mantaflow/preprocessed/fileio/ioparticles.cpp
index 2eab485beb3..84283a25b07 100644
--- a/extern/mantaflow/preprocessed/fileio/ioparticles.cpp
+++ b/extern/mantaflow/preprocessed/fileio/ioparticles.cpp
@@ -158,7 +158,7 @@ void pdataReadConvert<Vec3>(gzFile &gzf,
static const int PartSysSize = sizeof(Vector3D<float>) + sizeof(int);
-void writeParticlesUni(const std::string &name, const BasicParticleSystem *parts)
+int writeParticlesUni(const std::string &name, const BasicParticleSystem *parts)
{
debMsg("writing particles " << parts->getName() << " to uni file " << name, 1);
@@ -177,8 +177,10 @@ void writeParticlesUni(const std::string &name, const BasicParticleSystem *parts
head.timestamp = stamp.time;
gzFile gzf = (gzFile)safeGzopen(name.c_str(), "wb1"); // do some compression
- if (!gzf)
+ if (!gzf) {
errMsg("can't open file " << name);
+ return 0;
+ }
gzwrite(gzf, ID, 4);
# if FLOATINGPOINT_PRECISION != 1
@@ -195,26 +197,30 @@ void writeParticlesUni(const std::string &name, const BasicParticleSystem *parts
gzwrite(gzf, &head, sizeof(UniPartHeader));
gzwrite(gzf, &((*parts)[0]), PartSysSize * head.dim);
# endif
- gzclose(gzf);
+ return (gzclose(gzf) == Z_OK);
#else
debMsg("file format not supported without zlib", 1);
+ return 0;
#endif
};
-void readParticlesUni(const std::string &name, BasicParticleSystem *parts)
+int readParticlesUni(const std::string &name, BasicParticleSystem *parts)
{
debMsg("reading particles " << parts->getName() << " from uni file " << name, 1);
#if NO_ZLIB != 1
gzFile gzf = (gzFile)safeGzopen(name.c_str(), "rb");
- if (!gzf)
+ if (!gzf) {
errMsg("can't open file " << name);
+ return 0;
+ }
char ID[5] = {0, 0, 0, 0, 0};
gzread(gzf, ID, 4);
if (!strcmp(ID, "PB01")) {
errMsg("particle uni file format v01 not supported anymore");
+ return 0;
}
else if (!strcmp(ID, "PB02")) {
// current file format
@@ -249,13 +255,14 @@ void readParticlesUni(const std::string &name, BasicParticleSystem *parts)
parts->transformPositions(Vec3i(head.dimX, head.dimY, head.dimZ),
parts->getParent()->getGridSize());
}
- gzclose(gzf);
+ return (gzclose(gzf) == Z_OK);
#else
debMsg("file format not supported without zlib", 1);
+ return 0;
#endif
};
-template<class T> void writePdataUni(const std::string &name, ParticleDataImpl<T> *pdata)
+template<class T> int writePdataUni(const std::string &name, ParticleDataImpl<T> *pdata)
{
debMsg("writing particle data " << pdata->getName() << " to uni file " << name, 1);
@@ -274,8 +281,10 @@ template<class T> void writePdataUni(const std::string &name, ParticleDataImpl<T
head.timestamp = stamp.time;
gzFile gzf = (gzFile)safeGzopen(name.c_str(), "wb1"); // do some compression
- if (!gzf)
+ if (!gzf) {
errMsg("can't open file " << name);
+ return 0;
+ }
gzwrite(gzf, ID, 4);
# if FLOATINGPOINT_PRECISION != 1
@@ -287,21 +296,24 @@ template<class T> void writePdataUni(const std::string &name, ParticleDataImpl<T
gzwrite(gzf, &head, sizeof(UniPartHeader));
gzwrite(gzf, &(pdata->get(0)), sizeof(T) * head.dim);
# endif
- gzclose(gzf);
+ return (gzclose(gzf) == Z_OK);
#else
debMsg("file format not supported without zlib", 1);
+ return 0;
#endif
};
-template<class T> void readPdataUni(const std::string &name, ParticleDataImpl<T> *pdata)
+template<class T> int readPdataUni(const std::string &name, ParticleDataImpl<T> *pdata)
{
debMsg("reading particle data " << pdata->getName() << " from uni file " << name, 1);
#if NO_ZLIB != 1
gzFile gzf = (gzFile)safeGzopen(name.c_str(), "rb");
- if (!gzf)
+ if (!gzf) {
errMsg("can't open file " << name);
+ return 0;
+ }
char ID[5] = {0, 0, 0, 0, 0};
gzread(gzf, ID, 4);
@@ -327,18 +339,19 @@ template<class T> void readPdataUni(const std::string &name, ParticleDataImpl<T>
<< readBytes);
# endif
}
- gzclose(gzf);
+ return (gzclose(gzf) == Z_OK);
#else
debMsg("file format not supported without zlib", 1);
+ return 0;
#endif
}
// explicit instantiation
-template void writePdataUni<int>(const std::string &name, ParticleDataImpl<int> *pdata);
-template void writePdataUni<Real>(const std::string &name, ParticleDataImpl<Real> *pdata);
-template void writePdataUni<Vec3>(const std::string &name, ParticleDataImpl<Vec3> *pdata);
-template void readPdataUni<int>(const std::string &name, ParticleDataImpl<int> *pdata);
-template void readPdataUni<Real>(const std::string &name, ParticleDataImpl<Real> *pdata);
-template void readPdataUni<Vec3>(const std::string &name, ParticleDataImpl<Vec3> *pdata);
+template int writePdataUni<int>(const std::string &name, ParticleDataImpl<int> *pdata);
+template int writePdataUni<Real>(const std::string &name, ParticleDataImpl<Real> *pdata);
+template int writePdataUni<Vec3>(const std::string &name, ParticleDataImpl<Vec3> *pdata);
+template int readPdataUni<int>(const std::string &name, ParticleDataImpl<int> *pdata);
+template int readPdataUni<Real>(const std::string &name, ParticleDataImpl<Real> *pdata);
+template int readPdataUni<Vec3>(const std::string &name, ParticleDataImpl<Vec3> *pdata);
} // namespace Manta
diff --git a/extern/mantaflow/preprocessed/fileio/ioutil.cpp b/extern/mantaflow/preprocessed/fileio/ioutil.cpp
index e04633c5634..cc63cf87ac1 100644
--- a/extern/mantaflow/preprocessed/fileio/ioutil.cpp
+++ b/extern/mantaflow/preprocessed/fileio/ioutil.cpp
@@ -18,6 +18,10 @@
#include "mantaio.h"
+#if OPENVDB == 1
+# include "openvdb/openvdb.h"
+#endif
+
#if NO_ZLIB != 1
extern "C" {
# include <zlib.h>
@@ -40,7 +44,7 @@ static wstring stringToWstring(const char *str)
MultiByteToWideChar(CP_UTF8, 0, str, strlen(str), &strWide[0], length_wc);
return strWide;
}
-# endif
+# endif // WIN32==1
void *safeGzopen(const char *filename, const char *mode)
{
@@ -55,6 +59,54 @@ void *safeGzopen(const char *filename, const char *mode)
return gzfile;
}
-#endif
+#endif // NO_ZLIB != 1
+
+#if defined(OPENVDB)
+// Convert from OpenVDB value to Manta value.
+template<class S, class T> void convertFrom(S &in, T *out)
+{
+ errMsg("OpenVDB convertFrom Warning: Unsupported type conversion");
+}
+
+template<> void convertFrom(int &in, int *out)
+{
+ (*out) = in;
+}
+
+template<> void convertFrom(float &in, Real *out)
+{
+ (*out) = (Real)in;
+}
+
+template<> void convertFrom(openvdb::Vec3s &in, Vec3 *out)
+{
+ (*out).x = in.x();
+ (*out).y = in.y();
+ (*out).z = in.z();
+}
+
+// Convert to OpenVDB value from Manta value.
+template<class S, class T> void convertTo(S *out, T &in)
+{
+ errMsg("OpenVDB convertTo Warning: Unsupported type conversion");
+}
+
+template<> void convertTo(int *out, int &in)
+{
+ (*out) = in;
+}
+
+template<> void convertTo(float *out, Real &in)
+{
+ (*out) = (float)in;
+}
+
+template<> void convertTo(openvdb::Vec3s *out, Vec3 &in)
+{
+ (*out).x() = in.x;
+ (*out).y() = in.y;
+ (*out).z() = in.z;
+}
+#endif // OPENVDB==1
} // namespace
diff --git a/extern/mantaflow/preprocessed/fileio/iovdb.cpp b/extern/mantaflow/preprocessed/fileio/iovdb.cpp
new file mode 100644
index 00000000000..b31f7e0e760
--- /dev/null
+++ b/extern/mantaflow/preprocessed/fileio/iovdb.cpp
@@ -0,0 +1,618 @@
+
+
+// DO NOT EDIT !
+// This file is generated using the MantaFlow preprocessor (prep generate).
+
+/******************************************************************************
+ *
+ * MantaFlow fluid solver framework
+ * Copyright 2020 Sebastian Barschkis, Nils Thuerey
+ *
+ * This program is free software, distributed under the terms of the
+ * Apache License, Version 2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Loading and writing grids and particles from and to OpenVDB files.
+ *
+ ******************************************************************************/
+
+#include <iostream>
+#include <fstream>
+#include <cstdlib>
+#include <cstring>
+
+#include "mantaio.h"
+#include "grid.h"
+#include "vector4d.h"
+#include "grid4d.h"
+#include "particle.h"
+
+#if OPENVDB == 1
+# include "openvdb/openvdb.h"
+# include <openvdb/points/PointConversion.h>
+# include <openvdb/points/PointCount.h>
+#endif
+
+#define POSITION_NAME "P"
+#define FLAG_NAME "U"
+
+using namespace std;
+
+namespace Manta {
+
+#if OPENVDB == 1
+
+template<class GridType, class T> void importVDB(typename GridType::Ptr from, Grid<T> *to)
+{
+ using ValueT = typename GridType::ValueType;
+ typename GridType::Accessor accessor = from->getAccessor();
+
+ FOR_IJK(*to)
+ {
+ openvdb::Coord xyz(i, j, k);
+ ValueT vdbValue = accessor.getValue(xyz);
+ T toMantaValue;
+ convertFrom(vdbValue, &toMantaValue);
+ to->set(i, j, k, toMantaValue);
+ }
+}
+
+template<class VDBType, class T>
+void importVDB(VDBType vdbValue, ParticleDataImpl<T> *to, int index, float voxelSize)
+{
+ (void)voxelSize; // Unused
+ T toMantaValue;
+ convertFrom(vdbValue, &toMantaValue);
+ to->set(index, toMantaValue);
+}
+
+void importVDB(openvdb::points::PointDataGrid::Ptr from,
+ BasicParticleSystem *to,
+ std::vector<ParticleDataBase *> &toPData,
+ float voxelSize)
+{
+ openvdb::Index64 count = openvdb::points::pointCount(from->tree());
+ to->resizeAll(count);
+
+ int cnt = 0;
+ for (auto leafIter = from->tree().cbeginLeaf(); leafIter; ++leafIter) {
+ const openvdb::points::AttributeArray &positionArray = leafIter->constAttributeArray(
+ POSITION_NAME);
+ const openvdb::points::AttributeArray &flagArray = leafIter->constAttributeArray(FLAG_NAME);
+
+ openvdb::points::AttributeHandle<openvdb::Vec3s> positionHandle(positionArray);
+ openvdb::points::AttributeHandle<int> flagHandle(flagArray);
+
+ // Get vdb handles to pdata objects in pdata list
+ std::vector<std::tuple<int, openvdb::points::AttributeHandle<int>>> pDataHandlesInt;
+ std::vector<std::tuple<int, openvdb::points::AttributeHandle<float>>> pDataHandlesReal;
+ std::vector<std::tuple<int, openvdb::points::AttributeHandle<openvdb::Vec3s>>>
+ pDataHandlesVec3;
+
+ int pDataIndex = 0;
+ for (ParticleDataBase *pdb : toPData) {
+ std::string name = pdb->getName();
+ const openvdb::points::AttributeArray &pDataArray = leafIter->constAttributeArray(name);
+
+ if (pdb->getType() == ParticleDataBase::TypeInt) {
+ openvdb::points::AttributeHandle<int> intHandle(pDataArray);
+ std::tuple<int, openvdb::points::AttributeHandle<int>> tuple = std::make_tuple(pDataIndex,
+ intHandle);
+ pDataHandlesInt.push_back(tuple);
+ }
+ else if (pdb->getType() == ParticleDataBase::TypeReal) {
+ openvdb::points::AttributeHandle<float> floatHandle(pDataArray);
+ std::tuple<int, openvdb::points::AttributeHandle<float>> tuple = std::make_tuple(
+ pDataIndex, floatHandle);
+ pDataHandlesReal.push_back(tuple);
+ }
+ else if (pdb->getType() == ParticleDataBase::TypeVec3) {
+ openvdb::points::AttributeHandle<openvdb::Vec3s> vec3Handle(pDataArray);
+ std::tuple<int, openvdb::points::AttributeHandle<openvdb::Vec3s>> tuple = std::make_tuple(
+ pDataIndex, vec3Handle);
+ pDataHandlesVec3.push_back(tuple);
+ }
+ else {
+ errMsg("importVDB: unknown ParticleDataBase type");
+ }
+ ++pDataIndex;
+ }
+
+ for (auto indexIter = leafIter->beginIndexOn(); indexIter; ++indexIter) {
+ // Extract the voxel-space position of the point (always between (-0.5, -0.5, -0.5) and (0.5,
+ // 0.5, 0.5)).
+ openvdb::Vec3s voxelPosition = positionHandle.get(*indexIter);
+ const openvdb::Vec3d xyz = indexIter.getCoord().asVec3d();
+ // Compute the world-space position of the point.
+ openvdb::Vec3f worldPosition = from->transform().indexToWorld(voxelPosition + xyz);
+ int flag = flagHandle.get(*indexIter);
+
+ Vec3 toMantaValue;
+ convertFrom(worldPosition, &toMantaValue);
+ (*to)[cnt].pos = toMantaValue;
+ (*to)[cnt].pos /= voxelSize; // convert from world space to grid space
+ (*to)[cnt].flag = flag;
+
+ for (std::tuple<int, openvdb::points::AttributeHandle<int>> tuple : pDataHandlesInt) {
+ int pDataIndex = std::get<0>(tuple);
+ int vdbValue = std::get<1>(tuple).get(*indexIter);
+
+ ParticleDataImpl<int> *pdi = dynamic_cast<ParticleDataImpl<int> *>(toPData[pDataIndex]);
+ importVDB<int, int>(vdbValue, pdi, cnt, voxelSize);
+ }
+ for (std::tuple<int, openvdb::points::AttributeHandle<float>> tuple : pDataHandlesReal) {
+ int pDataIndex = std::get<0>(tuple);
+ float vdbValue = std::get<1>(tuple).get(*indexIter);
+
+ ParticleDataImpl<Real> *pdi = dynamic_cast<ParticleDataImpl<Real> *>(toPData[pDataIndex]);
+ importVDB<float, Real>(vdbValue, pdi, cnt, voxelSize);
+ }
+ for (std::tuple<int, openvdb::points::AttributeHandle<openvdb::Vec3s>> tuple :
+ pDataHandlesVec3) {
+ int pDataIndex = std::get<0>(tuple);
+ openvdb::Vec3f voxelPosition = std::get<1>(tuple).get(*indexIter);
+
+ ParticleDataImpl<Vec3> *pdi = dynamic_cast<ParticleDataImpl<Vec3> *>(toPData[pDataIndex]);
+ importVDB<openvdb::Vec3s, Vec3>(voxelPosition, pdi, cnt, voxelSize);
+ }
+ ++cnt;
+ }
+ }
+}
+
+template<class GridType>
+static void setGridOptions(typename GridType::Ptr grid,
+ string name,
+ openvdb::GridClass cls,
+ float voxelSize,
+ bool precisionHalf)
+{
+ grid->setTransform(openvdb::math::Transform::createLinearTransform(voxelSize));
+ grid->setGridClass(cls);
+ grid->setName(name);
+ grid->setSaveFloatAsHalf(precisionHalf);
+}
+
+template<class T, class GridType> typename GridType::Ptr exportVDB(Grid<T> *from)
+{
+ using ValueT = typename GridType::ValueType;
+ typename GridType::Ptr to = GridType::create();
+ typename GridType::Accessor accessor = to->getAccessor();
+
+ FOR_IJK(*from)
+ {
+ openvdb::Coord xyz(i, j, k);
+ T fromMantaValue = (*from)(i, j, k);
+ ValueT vdbValue;
+ convertTo(&vdbValue, fromMantaValue);
+ accessor.setValue(xyz, vdbValue);
+ }
+ return to;
+}
+
+template<class MantaType, class VDBType>
+void exportVDB(ParticleDataImpl<MantaType> *from,
+ openvdb::points::PointDataGrid::Ptr to,
+ openvdb::tools::PointIndexGrid::Ptr pIndex,
+ bool skipDeletedParts)
+{
+ std::vector<VDBType> vdbValues;
+ std::string name = from->getName();
+
+ FOR_PARTS(*from)
+ {
+ // Optionally, skip exporting particles that have been marked as deleted
+ BasicParticleSystem *pp = dynamic_cast<BasicParticleSystem *>(from->getParticleSys());
+ if (skipDeletedParts && !pp->isActive(idx)) {
+ continue;
+ }
+ MantaType fromMantaValue = (*from)[idx];
+ VDBType vdbValue;
+ convertTo(&vdbValue, fromMantaValue);
+ vdbValues.push_back(vdbValue);
+ }
+
+ openvdb::NamePair attribute =
+ openvdb::points::TypedAttributeArray<VDBType, openvdb::points::NullCodec>::attributeType();
+ openvdb::points::appendAttribute(to->tree(), name, attribute);
+
+ // Create a wrapper around the vdb values vector.
+ const openvdb::points::PointAttributeVector<VDBType> wrapper(vdbValues);
+
+ // Populate the attribute on the points
+ openvdb::points::populateAttribute<openvdb::points::PointDataTree,
+ openvdb::tools::PointIndexTree,
+ openvdb::points::PointAttributeVector<VDBType>>(
+ to->tree(), pIndex->tree(), name, wrapper);
+}
+
+openvdb::points::PointDataGrid::Ptr exportVDB(BasicParticleSystem *from,
+ std::vector<ParticleDataBase *> &fromPData,
+ bool skipDeletedParts,
+ float voxelSize)
+{
+ std::vector<openvdb::Vec3s> positions;
+ std::vector<int> flags;
+
+ FOR_PARTS(*from)
+ {
+ // Optionally, skip exporting particles that have been marked as deleted
+ if (skipDeletedParts && !from->isActive(idx)) {
+ continue;
+ }
+ Vector3D<float> pos = toVec3f((*from)[idx].pos);
+ pos *= voxelSize; // convert from grid space to world space
+ openvdb::Vec3s posVDB(pos.x, pos.y, pos.z);
+ positions.push_back(posVDB);
+
+ int flag = (*from)[idx].flag;
+ flags.push_back(flag);
+ }
+
+ const openvdb::points::PointAttributeVector<openvdb::Vec3s> positionsWrapper(positions);
+ openvdb::math::Transform::Ptr transform = openvdb::math::Transform::createLinearTransform(
+ voxelSize);
+
+ openvdb::tools::PointIndexGrid::Ptr pointIndexGrid =
+ openvdb::tools::createPointIndexGrid<openvdb::tools::PointIndexGrid>(positionsWrapper,
+ *transform);
+
+ // TODO (sebbas): Use custom codec for attributes?
+ // using Codec = openvdb::points::FixedPointCodec</*1-byte=*/false, openvdb::points::UnitRange>;
+ openvdb::points::PointDataGrid::Ptr to =
+ openvdb::points::createPointDataGrid<openvdb::points::NullCodec /*Codec*/,
+ openvdb::points::PointDataGrid>(
+ *pointIndexGrid, positionsWrapper, *transform);
+
+ openvdb::NamePair flagAttribute =
+ openvdb::points::TypedAttributeArray<int,
+ openvdb::points::NullCodec /*Codec*/>::attributeType();
+ openvdb::points::appendAttribute(to->tree(), FLAG_NAME, flagAttribute);
+ // Create a wrapper around the flag vector.
+ openvdb::points::PointAttributeVector<int> flagWrapper(flags);
+ // Populate the "flag" attribute on the points
+ openvdb::points::populateAttribute<openvdb::points::PointDataTree,
+ openvdb::tools::PointIndexTree,
+ openvdb::points::PointAttributeVector<int>>(
+ to->tree(), pointIndexGrid->tree(), FLAG_NAME, flagWrapper);
+
+ // Add all already buffered pdata to this particle grid
+ for (ParticleDataBase *pdb : fromPData) {
+ if (pdb->getType() == ParticleDataBase::TypeInt) {
+ debMsg("Writing int particle data '" << pdb->getName() << "'", 1);
+ ParticleDataImpl<int> *pdi = dynamic_cast<ParticleDataImpl<int> *>(pdb);
+ exportVDB<int, int>(pdi, to, pointIndexGrid, skipDeletedParts);
+ }
+ else if (pdb->getType() == ParticleDataBase::TypeReal) {
+ debMsg("Writing real particle data '" << pdb->getName() << "'", 1);
+ ParticleDataImpl<Real> *pdi = dynamic_cast<ParticleDataImpl<Real> *>(pdb);
+ exportVDB<Real, float>(pdi, to, pointIndexGrid, skipDeletedParts);
+ }
+ else if (pdb->getType() == ParticleDataBase::TypeVec3) {
+ debMsg("Writing Vec3 particle data '" << pdb->getName() << "'", 1);
+ ParticleDataImpl<Vec3> *pdi = dynamic_cast<ParticleDataImpl<Vec3> *>(pdb);
+ exportVDB<Vec3, openvdb::Vec3s>(pdi, to, pointIndexGrid, skipDeletedParts);
+ }
+ else {
+ errMsg("exportVDB: unknown ParticleDataBase type");
+ }
+ }
+ return to;
+}
+
+static void registerCustomCodecs()
+{
+ using Codec = openvdb::points::FixedPointCodec</*1-byte=*/false, openvdb::points::UnitRange>;
+ openvdb::points::TypedAttributeArray<int, Codec>::registerType();
+}
+
+int writeObjectsVDB(const string &filename,
+ std::vector<PbClass *> *objects,
+ float worldSize,
+ bool skipDeletedParts,
+ int compression,
+ bool precisionHalf)
+{
+ openvdb::initialize();
+ openvdb::io::File file(filename);
+ openvdb::GridPtrVec gridsVDB;
+
+ // TODO (sebbas): Use custom codec for flag attribute?
+ // Register codecs one, this makes sure custom attributes can be read
+ // registerCustomCodecs();
+
+ std::vector<ParticleDataBase *> pdbBuffer;
+
+ for (std::vector<PbClass *>::iterator iter = objects->begin(); iter != objects->end(); ++iter) {
+ openvdb::GridClass gClass = openvdb::GRID_UNKNOWN;
+ openvdb::GridBase::Ptr vdbGrid;
+
+ PbClass *object = dynamic_cast<PbClass *>(*iter);
+ const Real dx = object->getParent()->getDx();
+ const Real voxelSize = worldSize * dx;
+ const string objectName = object->getName();
+
+ if (GridBase *mantaGrid = dynamic_cast<GridBase *>(*iter)) {
+
+ if (mantaGrid->getType() & GridBase::TypeInt) {
+ debMsg("Writing int grid '" << mantaGrid->getName() << "' to vdb file " << filename, 1);
+ Grid<int> *mantaIntGrid = (Grid<int> *)mantaGrid;
+ vdbGrid = exportVDB<int, openvdb::Int32Grid>(mantaIntGrid);
+ gridsVDB.push_back(vdbGrid);
+ }
+ else if (mantaGrid->getType() & GridBase::TypeReal) {
+ debMsg("Writing real grid '" << mantaGrid->getName() << "' to vdb file " << filename, 1);
+ gClass = (mantaGrid->getType() & GridBase::TypeLevelset) ? openvdb::GRID_LEVEL_SET :
+ openvdb::GRID_FOG_VOLUME;
+ Grid<Real> *mantaRealGrid = (Grid<Real> *)mantaGrid;
+ vdbGrid = exportVDB<Real, openvdb::FloatGrid>(mantaRealGrid);
+ gridsVDB.push_back(vdbGrid);
+ }
+ else if (mantaGrid->getType() & GridBase::TypeVec3) {
+ debMsg("Writing vec3 grid '" << mantaGrid->getName() << "' to vdb file " << filename, 1);
+ gClass = (mantaGrid->getType() & GridBase::TypeMAC) ? openvdb::GRID_STAGGERED :
+ openvdb::GRID_UNKNOWN;
+ Grid<Vec3> *mantaVec3Grid = (Grid<Vec3> *)mantaGrid;
+ vdbGrid = exportVDB<Vec3, openvdb::Vec3SGrid>(mantaVec3Grid);
+ gridsVDB.push_back(vdbGrid);
+ }
+ else {
+ errMsg("writeObjectsVDB: unknown grid type");
+ return 0;
+ }
+ }
+ else if (BasicParticleSystem *mantaPP = dynamic_cast<BasicParticleSystem *>(*iter)) {
+ debMsg("Writing particle system '" << mantaPP->getName()
+ << "' (and buffered pData) to vdb file " << filename,
+ 1);
+ vdbGrid = exportVDB(mantaPP, pdbBuffer, skipDeletedParts, voxelSize);
+ gridsVDB.push_back(vdbGrid);
+ pdbBuffer.clear();
+ }
+ // Particle data will only be saved if there is a particle system too.
+ else if (ParticleDataBase *mantaPPImpl = dynamic_cast<ParticleDataBase *>(*iter)) {
+ debMsg("Buffering particle data '" << mantaPPImpl->getName() << "' to vdb file " << filename,
+ 1);
+ pdbBuffer.push_back(mantaPPImpl);
+ }
+ else {
+ errMsg("writeObjectsVDB: Unsupported Python object. Cannot write to .vdb file " << filename);
+ return 0;
+ }
+
+ // Set additional grid attributes, e.g. name, grid class, compression level, etc.
+ if (vdbGrid) {
+ setGridOptions<openvdb::GridBase>(vdbGrid, objectName, gClass, voxelSize, precisionHalf);
+ }
+ }
+
+ // Give out a warning if pData items were present but could not be saved due to missing particle
+ // system.
+ if (!pdbBuffer.empty()) {
+ for (ParticleDataBase *pdb : pdbBuffer) {
+ debMsg("writeObjectsVDB Warning: Particle data '"
+ << pdb->getName()
+ << "' has not been saved. It's parent particle system was needs to be given too.",
+ 1);
+ }
+ }
+
+ // Write only if the is at least one grid, optionally write with compression.
+ if (gridsVDB.size()) {
+ int vdb_flags = openvdb::io::COMPRESS_ACTIVE_MASK;
+ switch (compression) {
+ case COMPRESSION_NONE: {
+ vdb_flags = openvdb::io::COMPRESS_NONE;
+ break;
+ }
+ case COMPRESSION_ZIP: {
+ vdb_flags |= openvdb::io::COMPRESS_ZIP;
+ break;
+ }
+ case COMPRESSION_BLOSC: {
+# if OPENVDB_BLOSC == 1
+ vdb_flags |= openvdb::io::COMPRESS_BLOSC;
+# else
+ debMsg("OpenVDB was built without Blosc support, using Zip compression instead", 1);
+ vdb_flags |= openvdb::io::COMPRESS_ZIP;
+# endif // OPENVDB_BLOSC==1
+ break;
+ }
+ }
+ file.setCompression(vdb_flags);
+ file.write(gridsVDB);
+ }
+ file.close();
+ return 1;
+}
+
+int readObjectsVDB(const string &filename, std::vector<PbClass *> *objects, float worldSize)
+{
+
+ openvdb::initialize();
+ openvdb::io::File file(filename);
+ openvdb::GridPtrVec gridsVDB;
+
+ // TODO (sebbas): Use custom codec for flag attribute?
+ // Register codecs one, this makes sure custom attributes can be read
+ // registerCustomCodecs();
+
+ try {
+ file.setCopyMaxBytes(0);
+ file.open();
+ gridsVDB = *(file.getGrids());
+ openvdb::MetaMap::Ptr metadata = file.getMetadata();
+ (void)metadata; // Unused for now
+ }
+ catch (const openvdb::IoError &e) {
+ debMsg("readObjectsVDB: Could not open vdb file " << filename, 1);
+ file.close();
+ return 0;
+ }
+ file.close();
+
+ // A buffer to store a handle to pData objects. These will be read alongside a particle system.
+ std::vector<ParticleDataBase *> pdbBuffer;
+
+ for (std::vector<PbClass *>::iterator iter = objects->begin(); iter != objects->end(); ++iter) {
+
+ if (gridsVDB.empty()) {
+ debMsg("readObjectsVDB: No vdb grids in file " << filename, 1);
+ }
+ // If there is just one grid in this file, load it regardless of name match (to vdb caches per
+ // grid).
+ bool onlyGrid = (gridsVDB.size() == 1);
+
+ PbClass *object = dynamic_cast<PbClass *>(*iter);
+ const Real dx = object->getParent()->getDx();
+ const Real voxelSize = worldSize * dx;
+
+ // Particle data objects are treated separately - buffered and inserted when reading the
+ // particle system
+ if (ParticleDataBase *mantaPPImpl = dynamic_cast<ParticleDataBase *>(*iter)) {
+ debMsg("Buffering particle data '" << mantaPPImpl->getName() << "' from vdb file "
+ << filename,
+ 1);
+ pdbBuffer.push_back(mantaPPImpl);
+ continue;
+ }
+
+ // For every manta object, we loop through the vdb grid list and check for a match
+ for (const openvdb::GridBase::Ptr &vdbGrid : gridsVDB) {
+ bool nameMatch = (vdbGrid->getName() == (*iter)->getName());
+
+ // Sanity checks: Only load valid grids and make sure names match.
+ if (!vdbGrid) {
+ debMsg("Skipping invalid vdb grid '" << vdbGrid->getName() << "' in file " << filename, 1);
+ continue;
+ }
+ if (!nameMatch && !onlyGrid) {
+ continue;
+ }
+ if (GridBase *mantaGrid = dynamic_cast<GridBase *>(*iter)) {
+
+ if (mantaGrid->getType() & GridBase::TypeInt) {
+ debMsg("Reading into grid '" << mantaGrid->getName() << "' from int grid '"
+ << vdbGrid->getName() << "' in vdb file " << filename,
+ 1);
+ openvdb::Int32Grid::Ptr vdbIntGrid = openvdb::gridPtrCast<openvdb::Int32Grid>(vdbGrid);
+ Grid<int> *mantaIntGrid = (Grid<int> *)mantaGrid;
+ importVDB<openvdb::Int32Grid, int>(vdbIntGrid, mantaIntGrid);
+ }
+ else if (mantaGrid->getType() & GridBase::TypeReal) {
+ debMsg("Reading into grid '" << mantaGrid->getName() << "' from real grid '"
+ << vdbGrid->getName() << "' in vdb file " << filename,
+ 1);
+ openvdb::FloatGrid::Ptr vdbFloatGrid = openvdb::gridPtrCast<openvdb::FloatGrid>(vdbGrid);
+ Grid<Real> *mantaRealGrid = (Grid<Real> *)mantaGrid;
+ importVDB<openvdb::FloatGrid, Real>(vdbFloatGrid, mantaRealGrid);
+ }
+ else if (mantaGrid->getType() & GridBase::TypeVec3) {
+ debMsg("Reading into grid '" << mantaGrid->getName() << "' from vec3 grid '"
+ << vdbGrid->getName() << "' in vdb file " << filename,
+ 1);
+ openvdb::Vec3SGrid::Ptr vdbVec3Grid = openvdb::gridPtrCast<openvdb::Vec3SGrid>(vdbGrid);
+ Grid<Vec3> *mantaVec3Grid = (Grid<Vec3> *)mantaGrid;
+ importVDB<openvdb::Vec3SGrid, Vec3>(vdbVec3Grid, mantaVec3Grid);
+ }
+ else {
+ errMsg("readObjectsVDB: unknown grid type");
+ return 0;
+ }
+ }
+ else if (BasicParticleSystem *mantaPP = dynamic_cast<BasicParticleSystem *>(*iter)) {
+ debMsg("Reading into particle system '" << mantaPP->getName() << "' from particle system '"
+ << vdbGrid->getName() << "' in vdb file "
+ << filename,
+ 1);
+ openvdb::points::PointDataGrid::Ptr vdbPointGrid =
+ openvdb::gridPtrCast<openvdb::points::PointDataGrid>(vdbGrid);
+ importVDB(vdbPointGrid, mantaPP, pdbBuffer, voxelSize);
+ pdbBuffer.clear();
+ }
+ else {
+ errMsg("readObjectsVDB: Unsupported Python object. Cannot read from .vdb file "
+ << filename);
+ return 0;
+ }
+ }
+ }
+
+ // Give out a warning if pData items were present but could not be read due to missing particle
+ // system.
+ if (!pdbBuffer.empty()) {
+ for (ParticleDataBase *pdb : pdbBuffer) {
+ debMsg("readObjectsVDB Warning: Particle data '"
+ << pdb->getName()
+ << "' has not been read. The parent particle system needs to be given too.",
+ 1);
+ }
+ }
+
+ return 1;
+}
+
+template void importVDB<int, int>(int vdbValue,
+ ParticleDataImpl<int> *to,
+ int index,
+ float voxelSize = 1.0);
+template void importVDB<float, Real>(float vdbValue,
+ ParticleDataImpl<Real> *to,
+ int index,
+ float voxelSize = 1.0);
+template void importVDB<openvdb::Vec3f, Vec3>(openvdb::Vec3s vdbValue,
+ ParticleDataImpl<Vec3> *to,
+ int index,
+ float voxelSize = 1.0);
+
+void importVDB(openvdb::points::PointDataGrid::Ptr from,
+ BasicParticleSystem *to,
+ std::vector<ParticleDataBase *> &toPData,
+ float voxelSize = 1.0);
+template void importVDB<openvdb::Int32Grid, int>(openvdb::Int32Grid::Ptr from, Grid<int> *to);
+template void importVDB<openvdb::FloatGrid, Real>(openvdb::FloatGrid::Ptr from, Grid<Real> *to);
+template void importVDB<openvdb::Vec3SGrid, Vec3>(openvdb::Vec3SGrid::Ptr from, Grid<Vec3> *to);
+
+template openvdb::Int32Grid::Ptr exportVDB<int, openvdb::Int32Grid>(Grid<int> *from);
+template openvdb::FloatGrid::Ptr exportVDB<Real, openvdb::FloatGrid>(Grid<Real> *from);
+template openvdb::Vec3SGrid::Ptr exportVDB<Vec3, openvdb::Vec3SGrid>(Grid<Vec3> *from);
+
+openvdb::points::PointDataGrid::Ptr exportVDB(BasicParticleSystem *from,
+ std::vector<ParticleDataBase *> &fromPData,
+ bool skipDeletedParts = false,
+ float voxelSize = 1.0);
+template void exportVDB<int, int>(ParticleDataImpl<int> *from,
+ openvdb::points::PointDataGrid::Ptr to,
+ openvdb::tools::PointIndexGrid::Ptr pIndex,
+ bool skipDeletedParts = false);
+template void exportVDB<Real, float>(ParticleDataImpl<Real> *from,
+ openvdb::points::PointDataGrid::Ptr to,
+ openvdb::tools::PointIndexGrid::Ptr pIndex,
+ bool skipDeletedParts = false);
+template void exportVDB<Vec3, openvdb::Vec3s>(ParticleDataImpl<Vec3> *from,
+ openvdb::points::PointDataGrid::Ptr to,
+ openvdb::tools::PointIndexGrid::Ptr pIndex,
+ bool skipDeletedParts = false);
+
+#else
+
+int writeObjectsVDB(const string &filename,
+ std::vector<PbClass *> *objects,
+ float worldSize,
+ bool skipDeletedParts,
+ int compression,
+ bool precisionHalf)
+{
+ errMsg("Cannot save to .vdb file. Mantaflow has not been built with OpenVDB support.");
+ return 0;
+}
+
+int readObjectsVDB(const string &filename, std::vector<PbClass *> *objects, float worldSize)
+{
+ errMsg("Cannot load from .vdb file. Mantaflow has not been built with OpenVDB support.");
+ return 0;
+}
+
+#endif // OPENVDB==1
+
+} // namespace Manta
diff --git a/extern/mantaflow/preprocessed/fileio/mantaio.cpp b/extern/mantaflow/preprocessed/fileio/mantaio.cpp
new file mode 100644
index 00000000000..fd2b36bf7cb
--- /dev/null
+++ b/extern/mantaflow/preprocessed/fileio/mantaio.cpp
@@ -0,0 +1,144 @@
+
+
+// DO NOT EDIT !
+// This file is generated using the MantaFlow preprocessor (prep generate).
+
+/******************************************************************************
+ *
+ * MantaFlow fluid solver framework
+ * Copyright 2020 Sebastian Barschkis, Nils Thuerey
+ *
+ * This program is free software, distributed under the terms of the
+ * Apache License, Version 2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * General functions that make use of functions from other io files.
+ *
+ ******************************************************************************/
+
+#include "mantaio.h"
+
+using namespace std;
+
+namespace Manta {
+
+int load(const string &name, std::vector<PbClass *> &objects, float worldSize = 1.0)
+{
+ if (name.find_last_of('.') == string::npos)
+ errMsg("file '" + name + "' does not have an extension");
+ string ext = name.substr(name.find_last_of('.'));
+
+ if (ext == ".raw")
+ return readGridsRaw(name, &objects);
+ else if (ext == ".uni")
+ return readGridsUni(name, &objects);
+ else if (ext == ".vol")
+ return readGridsVol(name, &objects);
+ if (ext == ".vdb")
+ return readObjectsVDB(name, &objects, worldSize);
+ else if (ext == ".npz")
+ return readGridsNumpy(name, &objects);
+ else if (ext == ".txt")
+ return readGridsTxt(name, &objects);
+ else
+ errMsg("file '" + name + "' filetype not supported");
+ return 0;
+}
+static PyObject *_W_0(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
+{
+ try {
+ PbArgs _args(_linargs, _kwds);
+ FluidSolver *parent = _args.obtainParent();
+ bool noTiming = _args.getOpt<bool>("notiming", -1, 0);
+ pbPreparePlugin(parent, "load", !noTiming);
+ PyObject *_retval = 0;
+ {
+ ArgLocker _lock;
+ const string &name = _args.get<string>("name", 0, &_lock);
+ std::vector<PbClass *> &objects = *_args.getPtr<std::vector<PbClass *>>(
+ "objects", 1, &_lock);
+ float worldSize = _args.getOpt<float>("worldSize", 2, 1.0, &_lock);
+ _retval = toPy(load(name, objects, worldSize));
+ _args.check();
+ }
+ pbFinalizePlugin(parent, "load", !noTiming);
+ return _retval;
+ }
+ catch (std::exception &e) {
+ pbSetError("load", e.what());
+ return 0;
+ }
+}
+static const Pb::Register _RP_load("", "load", _W_0);
+extern "C" {
+void PbRegister_load()
+{
+ KEEP_UNUSED(_RP_load);
+}
+}
+
+int save(const string &name,
+ std::vector<PbClass *> &objects,
+ float worldSize = 1.0,
+ bool skipDeletedParts = false,
+ int compression = COMPRESSION_ZIP,
+ bool precisionHalf = true)
+{
+ if (name.find_last_of('.') == string::npos)
+ errMsg("file '" + name + "' does not have an extension");
+ string ext = name.substr(name.find_last_of('.'));
+
+ if (ext == ".raw")
+ return writeGridsRaw(name, &objects);
+ else if (ext == ".uni")
+ return writeGridsUni(name, &objects);
+ else if (ext == ".vol")
+ return writeGridsVol(name, &objects);
+ if (ext == ".vdb")
+ return writeObjectsVDB(
+ name, &objects, worldSize, skipDeletedParts, compression, precisionHalf);
+ else if (ext == ".npz")
+ return writeGridsNumpy(name, &objects);
+ else if (ext == ".txt")
+ return writeGridsTxt(name, &objects);
+ else
+ errMsg("file '" + name + "' filetype not supported");
+ return 0;
+}
+static PyObject *_W_1(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
+{
+ try {
+ PbArgs _args(_linargs, _kwds);
+ FluidSolver *parent = _args.obtainParent();
+ bool noTiming = _args.getOpt<bool>("notiming", -1, 0);
+ pbPreparePlugin(parent, "save", !noTiming);
+ PyObject *_retval = 0;
+ {
+ ArgLocker _lock;
+ const string &name = _args.get<string>("name", 0, &_lock);
+ std::vector<PbClass *> &objects = *_args.getPtr<std::vector<PbClass *>>(
+ "objects", 1, &_lock);
+ float worldSize = _args.getOpt<float>("worldSize", 2, 1.0, &_lock);
+ bool skipDeletedParts = _args.getOpt<bool>("skipDeletedParts", 3, false, &_lock);
+ int compression = _args.getOpt<int>("compression", 4, COMPRESSION_ZIP, &_lock);
+ bool precisionHalf = _args.getOpt<bool>("precisionHalf", 5, true, &_lock);
+ _retval = toPy(save(name, objects, worldSize, skipDeletedParts, compression, precisionHalf));
+ _args.check();
+ }
+ pbFinalizePlugin(parent, "save", !noTiming);
+ return _retval;
+ }
+ catch (std::exception &e) {
+ pbSetError("save", e.what());
+ return 0;
+ }
+}
+static const Pb::Register _RP_save("", "save", _W_1);
+extern "C" {
+void PbRegister_save()
+{
+ KEEP_UNUSED(_RP_save);
+}
+}
+
+} // namespace Manta
diff --git a/extern/mantaflow/preprocessed/fileio/mantaio.h b/extern/mantaflow/preprocessed/fileio/mantaio.h
index fbfe4bdd5d4..8b543ad4f93 100644
--- a/extern/mantaflow/preprocessed/fileio/mantaio.h
+++ b/extern/mantaflow/preprocessed/fileio/mantaio.h
@@ -21,62 +21,98 @@
#include <string>
+#include "manta.h"
+
+// OpenVDB compression flags
+#define COMPRESSION_NONE 0
+#define COMPRESSION_ZIP 1
+#define COMPRESSION_BLOSC 2
+
namespace Manta {
-// forward decl.
+// Forward declations
class Mesh;
class FlagGrid;
+class GridBase;
template<class T> class Grid;
template<class T> class Grid4d;
class BasicParticleSystem;
template<class T> class ParticleDataImpl;
template<class T> class MeshDataImpl;
-void writeObjFile(const std::string &name, Mesh *mesh);
-void writeBobjFile(const std::string &name, Mesh *mesh);
-void readObjFile(const std::string &name, Mesh *mesh, bool append);
-void readBobjFile(const std::string &name, Mesh *mesh, bool append);
-
-template<class T> void writeGridRaw(const std::string &name, Grid<T> *grid);
-template<class T> void writeGridUni(const std::string &name, Grid<T> *grid);
-template<class T> void writeGridVol(const std::string &name, Grid<T> *grid);
-template<class T> void writeGridTxt(const std::string &name, Grid<T> *grid);
-
-#if OPENVDB == 1
-template<class T> void writeGridVDB(const std::string &name, Grid<T> *grid);
-template<class T> void readGridVDB(const std::string &name, Grid<T> *grid);
-#endif // OPENVDB==1
-template<class T> void writeGridNumpy(const std::string &name, Grid<T> *grid);
-template<class T> void readGridNumpy(const std::string &name, Grid<T> *grid);
-
-template<class T> void readGridUni(const std::string &name, Grid<T> *grid);
-template<class T> void readGridRaw(const std::string &name, Grid<T> *grid);
-template<class T> void readGridVol(const std::string &name, Grid<T> *grid);
-
-template<class T> void writeGrid4dUni(const std::string &name, Grid4d<T> *grid);
+// Obj format
+int writeObjFile(const std::string &name, Mesh *mesh);
+int writeBobjFile(const std::string &name, Mesh *mesh);
+int readObjFile(const std::string &name, Mesh *mesh, bool append);
+int readBobjFile(const std::string &name, Mesh *mesh, bool append);
+
+// Other formats (Raw, Uni, Vol)
+template<class T> int readGridUni(const std::string &name, Grid<T> *grid);
+template<class T> int readGridRaw(const std::string &name, Grid<T> *grid);
+template<class T> int readGridVol(const std::string &name, Grid<T> *grid);
+int readGridsRaw(const std::string &name, std::vector<PbClass *> *grids);
+int readGridsUni(const std::string &name, std::vector<PbClass *> *grids);
+int readGridsVol(const std::string &name, std::vector<PbClass *> *grids);
+int readGridsTxt(const std::string &name, std::vector<PbClass *> *grids);
+
+template<class T> int writeGridRaw(const std::string &name, Grid<T> *grid);
+template<class T> int writeGridUni(const std::string &name, Grid<T> *grid);
+template<class T> int writeGridVol(const std::string &name, Grid<T> *grid);
+template<class T> int writeGridTxt(const std::string &name, Grid<T> *grid);
+int writeGridsRaw(const std::string &name, std::vector<PbClass *> *grids);
+int writeGridsUni(const std::string &name, std::vector<PbClass *> *grids);
+int writeGridsVol(const std::string &name, std::vector<PbClass *> *grids);
+int writeGridsTxt(const std::string &name, std::vector<PbClass *> *grids);
+
+// OpenVDB
+int writeObjectsVDB(const std::string &filename,
+ std::vector<PbClass *> *objects,
+ float scale = 1.0,
+ bool skipDeletedParts = false,
+ int compression = COMPRESSION_ZIP,
+ bool precisionHalf = true);
+int readObjectsVDB(const std::string &filename,
+ std::vector<PbClass *> *objects,
+ float scale = 1.0);
+
+// Numpy
+template<class T> int writeGridNumpy(const std::string &name, Grid<T> *grid);
+template<class T> int readGridNumpy(const std::string &name, Grid<T> *grid);
+
+int writeGridsNumpy(const std::string &name, std::vector<PbClass *> *grids);
+int readGridsNumpy(const std::string &name, std::vector<PbClass *> *grids);
+
+// 4D Grids
+template<class T> int writeGrid4dUni(const std::string &name, Grid4d<T> *grid);
template<class T>
-void readGrid4dUni(const std::string &name,
- Grid4d<T> *grid,
- int readTslice = -1,
- Grid4d<T> *slice = NULL,
- void **fileHandle = NULL);
+int readGrid4dUni(const std::string &name,
+ Grid4d<T> *grid,
+ int readTslice = -1,
+ Grid4d<T> *slice = NULL,
+ void **fileHandle = NULL);
void readGrid4dUniCleanup(void **fileHandle);
-template<class T> void writeGrid4dRaw(const std::string &name, Grid4d<T> *grid);
-template<class T> void readGrid4dRaw(const std::string &name, Grid4d<T> *grid);
+template<class T> int writeGrid4dRaw(const std::string &name, Grid4d<T> *grid);
+template<class T> int readGrid4dRaw(const std::string &name, Grid4d<T> *grid);
-void writeParticlesUni(const std::string &name, const BasicParticleSystem *parts);
-void readParticlesUni(const std::string &name, BasicParticleSystem *parts);
+// Particles + particle data
+int writeParticlesUni(const std::string &name, const BasicParticleSystem *parts);
+int readParticlesUni(const std::string &name, BasicParticleSystem *parts);
-template<class T> void writePdataUni(const std::string &name, ParticleDataImpl<T> *pdata);
-template<class T> void readPdataUni(const std::string &name, ParticleDataImpl<T> *pdata);
+template<class T> int writePdataUni(const std::string &name, ParticleDataImpl<T> *pdata);
+template<class T> int readPdataUni(const std::string &name, ParticleDataImpl<T> *pdata);
-template<class T> void writeMdataUni(const std::string &name, MeshDataImpl<T> *mdata);
-template<class T> void readMdataUni(const std::string &name, MeshDataImpl<T> *mdata);
+// Mesh data
+template<class T> int writeMdataUni(const std::string &name, MeshDataImpl<T> *mdata);
+template<class T> int readMdataUni(const std::string &name, MeshDataImpl<T> *mdata);
+// Helpers
void getUniFileSize(
const std::string &name, int &x, int &y, int &z, int *t = NULL, std::string *info = NULL);
-
void *safeGzopen(const char *filename, const char *mode);
+#if OPENVDB == 1
+template<class S, class T> void convertFrom(S &in, T *out);
+template<class S, class T> void convertTo(S *out, T &in);
+#endif
} // namespace Manta
diff --git a/extern/mantaflow/preprocessed/gitinfo.h b/extern/mantaflow/preprocessed/gitinfo.h
index 023974fd6cd..73ff70b10a0 100644
--- a/extern/mantaflow/preprocessed/gitinfo.h
+++ b/extern/mantaflow/preprocessed/gitinfo.h
@@ -1,3 +1,3 @@
-#define MANTA_GIT_VERSION "commit b61bf9efa7a1d8ca98635076a7e9f2c4dacb2914"
+#define MANTA_GIT_VERSION "commit d80d3c821de74315ab26b5efd153d41477b976c4"
diff --git a/extern/mantaflow/preprocessed/grid.cpp b/extern/mantaflow/preprocessed/grid.cpp
index 0ea3afb91f4..76716beb1ac 100644
--- a/extern/mantaflow/preprocessed/grid.cpp
+++ b/extern/mantaflow/preprocessed/grid.cpp
@@ -122,48 +122,52 @@ template<class T> void Grid<T>::swap(Grid<T> &other)
mData = dswap;
}
-template<class T> void Grid<T>::load(string name)
+template<class T> int Grid<T>::load(string name)
{
if (name.find_last_of('.') == string::npos)
errMsg("file '" + name + "' does not have an extension");
string ext = name.substr(name.find_last_of('.'));
if (ext == ".raw")
- readGridRaw(name, this);
+ return readGridRaw(name, this);
else if (ext == ".uni")
- readGridUni(name, this);
+ return readGridUni(name, this);
else if (ext == ".vol")
- readGridVol(name, this);
+ return readGridVol(name, this);
else if (ext == ".npz")
- readGridNumpy(name, this);
-#if OPENVDB == 1
- else if (ext == ".vdb")
- readGridVDB(name, this);
-#endif // OPENVDB==1
+ return readGridNumpy(name, this);
+ else if (ext == ".vdb") {
+ std::vector<PbClass *> grids;
+ grids.push_back(this);
+ return readObjectsVDB(name, &grids);
+ }
else
errMsg("file '" + name + "' filetype not supported");
+ return 0;
}
-template<class T> void Grid<T>::save(string name)
+template<class T> int Grid<T>::save(string name)
{
if (name.find_last_of('.') == string::npos)
errMsg("file '" + name + "' does not have an extension");
string ext = name.substr(name.find_last_of('.'));
if (ext == ".raw")
- writeGridRaw(name, this);
+ return writeGridRaw(name, this);
else if (ext == ".uni")
- writeGridUni(name, this);
+ return writeGridUni(name, this);
else if (ext == ".vol")
- writeGridVol(name, this);
-#if OPENVDB == 1
- else if (ext == ".vdb")
- writeGridVDB(name, this);
-#endif // OPENVDB==1
+ return writeGridVol(name, this);
else if (ext == ".npz")
- writeGridNumpy(name, this);
+ return writeGridNumpy(name, this);
+ else if (ext == ".vdb") {
+ std::vector<PbClass *> grids;
+ grids.push_back(this);
+ return writeObjectsVDB(name, &grids);
+ }
else if (ext == ".txt")
- writeGridTxt(name, this);
+ return writeGridTxt(name, this);
else
errMsg("file '" + name + "' filetype not supported");
+ return 0;
}
//******************************************************************************
diff --git a/extern/mantaflow/preprocessed/grid.h b/extern/mantaflow/preprocessed/grid.h
index fe386cfc269..a7aac80891a 100644
--- a/extern/mantaflow/preprocessed/grid.h
+++ b/extern/mantaflow/preprocessed/grid.h
@@ -409,7 +409,7 @@ template<class T> class Grid : public GridBase {
typedef T BASETYPE;
typedef GridBase BASETYPE_GRID;
- void save(std::string name);
+ int save(std::string name);
static PyObject *_W_10(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
@@ -422,8 +422,7 @@ template<class T> class Grid : public GridBase {
ArgLocker _lock;
std::string name = _args.get<std::string>("name", 0, &_lock);
pbo->_args.copy(_args);
- _retval = getPyNone();
- pbo->save(name);
+ _retval = toPy(pbo->save(name));
pbo->_args.check();
}
pbFinalizePlugin(pbo->getParent(), "Grid::save", !noTiming);
@@ -435,7 +434,7 @@ template<class T> class Grid : public GridBase {
}
}
- void load(std::string name);
+ int load(std::string name);
static PyObject *_W_11(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
@@ -448,8 +447,7 @@ template<class T> class Grid : public GridBase {
ArgLocker _lock;
std::string name = _args.get<std::string>("name", 0, &_lock);
pbo->_args.copy(_args);
- _retval = getPyNone();
- pbo->load(name);
+ _retval = toPy(pbo->load(name));
pbo->_args.check();
}
pbFinalizePlugin(pbo->getParent(), "Grid::load", !noTiming);
@@ -554,6 +552,12 @@ template<class T> class Grid : public GridBase {
return mData[idx];
}
+ //! set data
+ inline void set(int i, int j, int k, T &val)
+ {
+ mData[index(i, j, k)] = val;
+ }
+
// interpolated access
inline T getInterpolated(const Vec3 &pos) const
{
diff --git a/extern/mantaflow/preprocessed/grid4d.cpp b/extern/mantaflow/preprocessed/grid4d.cpp
index 41d69b2d33a..48f80d90b9f 100644
--- a/extern/mantaflow/preprocessed/grid4d.cpp
+++ b/extern/mantaflow/preprocessed/grid4d.cpp
@@ -121,30 +121,32 @@ template<class T> void Grid4d<T>::swap(Grid4d<T> &other)
mData = dswap;
}
-template<class T> void Grid4d<T>::load(string name)
+template<class T> int Grid4d<T>::load(string name)
{
if (name.find_last_of('.') == string::npos)
errMsg("file '" + name + "' does not have an extension");
string ext = name.substr(name.find_last_of('.'));
if (ext == ".uni")
- readGrid4dUni(name, this);
+ return readGrid4dUni(name, this);
else if (ext == ".raw")
- readGrid4dRaw(name, this);
+ return readGrid4dRaw(name, this);
else
errMsg("file '" + name + "' filetype not supported");
+ return 0;
}
-template<class T> void Grid4d<T>::save(string name)
+template<class T> int Grid4d<T>::save(string name)
{
if (name.find_last_of('.') == string::npos)
errMsg("file '" + name + "' does not have an extension");
string ext = name.substr(name.find_last_of('.'));
if (ext == ".uni")
- writeGrid4dUni(name, this);
+ return writeGrid4dUni(name, this);
else if (ext == ".raw")
- writeGrid4dRaw(name, this);
+ return writeGrid4dRaw(name, this);
else
errMsg("file '" + name + "' filetype not supported");
+ return 0;
}
//******************************************************************************
diff --git a/extern/mantaflow/preprocessed/grid4d.h b/extern/mantaflow/preprocessed/grid4d.h
index c3a98788da3..ee36c681ec5 100644
--- a/extern/mantaflow/preprocessed/grid4d.h
+++ b/extern/mantaflow/preprocessed/grid4d.h
@@ -372,7 +372,7 @@ template<class T> class Grid4d : public Grid4dBase {
typedef T BASETYPE;
typedef Grid4dBase BASETYPE_GRID;
- void save(std::string name);
+ int save(std::string name);
static PyObject *_W_9(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
@@ -385,8 +385,7 @@ template<class T> class Grid4d : public Grid4dBase {
ArgLocker _lock;
std::string name = _args.get<std::string>("name", 0, &_lock);
pbo->_args.copy(_args);
- _retval = getPyNone();
- pbo->save(name);
+ _retval = toPy(pbo->save(name));
pbo->_args.check();
}
pbFinalizePlugin(pbo->getParent(), "Grid4d::save", !noTiming);
@@ -398,7 +397,7 @@ template<class T> class Grid4d : public Grid4dBase {
}
}
- void load(std::string name);
+ int load(std::string name);
static PyObject *_W_10(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
@@ -411,8 +410,7 @@ template<class T> class Grid4d : public Grid4dBase {
ArgLocker _lock;
std::string name = _args.get<std::string>("name", 0, &_lock);
pbo->_args.copy(_args);
- _retval = getPyNone();
- pbo->load(name);
+ _retval = toPy(pbo->load(name));
pbo->_args.check();
}
pbFinalizePlugin(pbo->getParent(), "Grid4d::load", !noTiming);
diff --git a/extern/mantaflow/preprocessed/particle.cpp b/extern/mantaflow/preprocessed/particle.cpp
index 41af5d3ba81..c719fc8f27d 100644
--- a/extern/mantaflow/preprocessed/particle.cpp
+++ b/extern/mantaflow/preprocessed/particle.cpp
@@ -214,20 +214,26 @@ void BasicParticleSystem::writeParticlesRawVelocityGz(const string name) const
#endif
}
-void BasicParticleSystem::load(const string name)
+int BasicParticleSystem::load(const string name)
{
if (name.find_last_of('.') == string::npos)
errMsg("file '" + name + "' does not have an extension");
string ext = name.substr(name.find_last_of('.'));
if (ext == ".uni")
- readParticlesUni(name, this);
+ return readParticlesUni(name, this);
+ else if (ext == ".vdb") {
+ std::vector<PbClass *> parts;
+ parts.push_back(this);
+ return readObjectsVDB(name, &parts);
+ }
else if (ext == ".raw") // raw = uni for now
- readParticlesUni(name, this);
+ return readParticlesUni(name, this);
else
errMsg("particle '" + name + "' filetype not supported for loading");
+ return 0;
}
-void BasicParticleSystem::save(const string name) const
+int BasicParticleSystem::save(const string name)
{
if (name.find_last_of('.') == string::npos)
errMsg("file '" + name + "' does not have an extension");
@@ -235,16 +241,22 @@ void BasicParticleSystem::save(const string name) const
if (ext == ".txt")
this->writeParticlesText(name);
else if (ext == ".uni")
- writeParticlesUni(name, this);
+ return writeParticlesUni(name, this);
else if (ext == ".raw") // raw = uni for now
- writeParticlesUni(name, this);
- // raw data formats, very basic for simple data transfer to other programs
+ return writeParticlesUni(name, this);
+ else if (ext == ".vdb") {
+ std::vector<PbClass *> parts;
+ parts.push_back(this);
+ return writeObjectsVDB(name, &parts);
+ // raw data formats, very basic for simple data transfer to other programs
+ }
else if (ext == ".posgz")
this->writeParticlesRawPositionsGz(name);
else if (ext == ".velgz")
this->writeParticlesRawVelocityGz(name);
else
errMsg("particle '" + name + "' filetype not supported for saving");
+ return 0;
}
void BasicParticleSystem::printParts(IndexInt start, IndexInt stop, bool printIndex)
@@ -372,30 +384,42 @@ template<> void ParticleDataImpl<Vec3>::initNewValue(IndexInt idx, Vec3 pos)
}
}
-template<typename T> void ParticleDataImpl<T>::load(string name)
+template<typename T> int ParticleDataImpl<T>::load(string name)
{
if (name.find_last_of('.') == string::npos)
errMsg("file '" + name + "' does not have an extension");
string ext = name.substr(name.find_last_of('.'));
if (ext == ".uni")
- readPdataUni<T>(name, this);
+ return readPdataUni<T>(name, this);
+ else if (ext == ".vdb") {
+ std::vector<PbClass *> parts;
+ parts.push_back(this);
+ return readObjectsVDB(name, &parts);
+ }
else if (ext == ".raw") // raw = uni for now
- readPdataUni<T>(name, this);
+ return readPdataUni<T>(name, this);
else
errMsg("particle data '" + name + "' filetype not supported for loading");
+ return 0;
}
-template<typename T> void ParticleDataImpl<T>::save(string name)
+template<typename T> int ParticleDataImpl<T>::save(string name)
{
if (name.find_last_of('.') == string::npos)
errMsg("file '" + name + "' does not have an extension");
string ext = name.substr(name.find_last_of('.'));
if (ext == ".uni")
- writePdataUni<T>(name, this);
+ return writePdataUni<T>(name, this);
+ else if (ext == ".vdb") {
+ std::vector<PbClass *> parts;
+ parts.push_back(this);
+ return writeObjectsVDB(name, &parts);
+ }
else if (ext == ".raw") // raw = uni for now
- writePdataUni<T>(name, this);
+ return writePdataUni<T>(name, this);
else
errMsg("particle data '" + name + "' filetype not supported for saving");
+ return 0;
}
// specializations
diff --git a/extern/mantaflow/preprocessed/particle.h b/extern/mantaflow/preprocessed/particle.h
index d9dd3f49c38..0be141ed26f 100644
--- a/extern/mantaflow/preprocessed/particle.h
+++ b/extern/mantaflow/preprocessed/particle.h
@@ -646,7 +646,7 @@ class BasicParticleSystem : public ParticleSystem<BasicParticleData> {
}
//! file io
- void save(const std::string name) const;
+ int save(const std::string name);
static PyObject *_W_13(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
@@ -659,8 +659,7 @@ class BasicParticleSystem : public ParticleSystem<BasicParticleData> {
ArgLocker _lock;
const std::string name = _args.get<std::string>("name", 0, &_lock);
pbo->_args.copy(_args);
- _retval = getPyNone();
- pbo->save(name);
+ _retval = toPy(pbo->save(name));
pbo->_args.check();
}
pbFinalizePlugin(pbo->getParent(), "BasicParticleSystem::save", !noTiming);
@@ -672,7 +671,7 @@ class BasicParticleSystem : public ParticleSystem<BasicParticleData> {
}
}
- void load(const std::string name);
+ int load(const std::string name);
static PyObject *_W_14(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
@@ -685,8 +684,7 @@ class BasicParticleSystem : public ParticleSystem<BasicParticleData> {
ArgLocker _lock;
const std::string name = _args.get<std::string>("name", 0, &_lock);
pbo->_args.copy(_args);
- _retval = getPyNone();
- pbo->load(name);
+ _retval = toPy(pbo->load(name));
pbo->_args.check();
}
pbFinalizePlugin(pbo->getParent(), "BasicParticleSystem::load", !noTiming);
@@ -1022,11 +1020,15 @@ class ParticleDataBase : public PbClass {
return;
}
- //! set base pointer
+ //! set / get base pointer to parent particle system
void setParticleSys(ParticleBase *set)
{
mpParticleSys = set;
}
+ ParticleBase *getParticleSys()
+ {
+ return mpParticleSys;
+ }
//! debugging
inline void checkPartIndex(IndexInt idx) const;
@@ -1094,6 +1096,13 @@ template<class T> class ParticleDataImpl : public ParticleDataBase {
return mData[idx];
}
+ //! set data
+ inline void set(const IndexInt idx, T &val)
+ {
+ DEBUG_ONLY(checkPartIndex(idx));
+ mData[idx] = val;
+ }
+
//! set all values to 0, note - different from particleSystem::clear! doesnt modify size of array
//! (has to stay in sync with parent system)
void clear();
@@ -1716,7 +1725,7 @@ template<class T> class ParticleDataImpl : public ParticleDataBase {
}
//! file io
- void save(const std::string name);
+ int save(const std::string name);
static PyObject *_W_46(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
@@ -1729,8 +1738,7 @@ template<class T> class ParticleDataImpl : public ParticleDataBase {
ArgLocker _lock;
const std::string name = _args.get<std::string>("name", 0, &_lock);
pbo->_args.copy(_args);
- _retval = getPyNone();
- pbo->save(name);
+ _retval = toPy(pbo->save(name));
pbo->_args.check();
}
pbFinalizePlugin(pbo->getParent(), "ParticleDataImpl::save", !noTiming);
@@ -1742,7 +1750,7 @@ template<class T> class ParticleDataImpl : public ParticleDataBase {
}
}
- void load(const std::string name);
+ int load(const std::string name);
static PyObject *_W_47(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
@@ -1755,8 +1763,7 @@ template<class T> class ParticleDataImpl : public ParticleDataBase {
ArgLocker _lock;
const std::string name = _args.get<std::string>("name", 0, &_lock);
pbo->_args.copy(_args);
- _retval = getPyNone();
- pbo->load(name);
+ _retval = toPy(pbo->load(name));
pbo->_args.check();
}
pbFinalizePlugin(pbo->getParent(), "ParticleDataImpl::load", !noTiming);
diff --git a/extern/mantaflow/preprocessed/plugin/secondaryparticles.cpp b/extern/mantaflow/preprocessed/plugin/secondaryparticles.cpp
index 5e24b6f28db..5aae7285f1a 100644
--- a/extern/mantaflow/preprocessed/plugin/secondaryparticles.cpp
+++ b/extern/mantaflow/preprocessed/plugin/secondaryparticles.cpp
@@ -118,7 +118,9 @@ struct knFlipComputeSecondaryParticlePotentials : public KernelBase {
for (IndexInt x = i - radius; x <= i + radius; x++) {
for (IndexInt y = j - radius; y <= j + radius; y++) {
for (IndexInt z = k - radius; z <= k + radius; z++) {
- if ((x == i && y == j && z == k) || !flags.isInBounds(Vec3i(x, y, z)) ||
+ // ensure that xyz is in bounds: use bnd=1 to ensure that vel.getCentered() always has a
+ // neighbor cell
+ if ((x == i && y == j && z == k) || !flags.isInBounds(Vec3i(x, y, z), 1) ||
(flags(x, y, z) & jtype))
continue;
diff --git a/extern/mantaflow/preprocessed/python/defines.py.reg.cpp b/extern/mantaflow/preprocessed/python/defines.py.reg.cpp
index 1866957534c..f5b439b9cff 100644
--- a/extern/mantaflow/preprocessed/python/defines.py.reg.cpp
+++ b/extern/mantaflow/preprocessed/python/defines.py.reg.cpp
@@ -15,7 +15,8 @@ static const Pb::Register _reg(
"4\nTypeInflow = 8\nTypeOutflow = 16\nTypeStick = 64\nTypeReserved = 256\n\n# "
"integration mode\nIntEuler = 0\nIntRK2 = 1\nIntRK4 = 2\n\n# CG preconditioner\nPcNone "
" = 0\nPcMIC = 1\nPcMGDynamic = 2\nPcMGStatic = 3\n\n# particles\nPtypeSpray = "
- "2\nPtypeBubble = 4\nPtypeFoam = 8\nPtypeTracer = 16\n\n\n\n\n");
+ "2\nPtypeBubble = 4\nPtypeFoam = 8\nPtypeTracer = 16\n\n# OpenVDB export "
+ "flags\nCompression_None = 0\nCompression_Zip = 1\nCompression_Blosc = 2\n\n\n\n\n");
extern "C" {
void PbRegister_file_0()
{
diff --git a/extern/mantaflow/preprocessed/registration.cpp b/extern/mantaflow/preprocessed/registration.cpp
index c5bdddf4a18..6c1e68e6523 100644
--- a/extern/mantaflow/preprocessed/registration.cpp
+++ b/extern/mantaflow/preprocessed/registration.cpp
@@ -45,6 +45,8 @@ extern void PbRegister_printUniFileInfoString();
extern void PbRegister_getNpzFileSize();
extern void PbRegister_quantizeGrid();
extern void PbRegister_quantizeGridVec3();
+extern void PbRegister_load();
+extern void PbRegister_save();
extern void PbRegister_resetPhiInObs();
extern void PbRegister_advectSemiLagrange();
extern void PbRegister_addGravity();
@@ -238,6 +240,8 @@ void MantaEnsureRegistration()
PbRegister_getNpzFileSize();
PbRegister_quantizeGrid();
PbRegister_quantizeGridVec3();
+ PbRegister_load();
+ PbRegister_save();
PbRegister_resetPhiInObs();
PbRegister_advectSemiLagrange();
PbRegister_addGravity();
diff --git a/intern/cycles/CMakeLists.txt b/intern/cycles/CMakeLists.txt
index 121c8bdad6e..e5a5e9773d3 100644
--- a/intern/cycles/CMakeLists.txt
+++ b/intern/cycles/CMakeLists.txt
@@ -286,6 +286,7 @@ include_directories(
${OPENEXR_INCLUDE_DIR}
${OPENEXR_INCLUDE_DIRS}
${PUGIXML_INCLUDE_DIR}
+ ${TBB_INCLUDE_DIRS}
)
if(CYCLES_STANDALONE_REPOSITORY)
diff --git a/intern/cycles/blender/CMakeLists.txt b/intern/cycles/blender/CMakeLists.txt
index 496e8e9310b..2316800e21e 100644
--- a/intern/cycles/blender/CMakeLists.txt
+++ b/intern/cycles/blender/CMakeLists.txt
@@ -92,10 +92,6 @@ if(WITH_MOD_FLUID)
add_definitions(-DWITH_FLUID)
endif()
-if(WITH_NEW_OBJECT_TYPES)
- add_definitions(-DWITH_NEW_OBJECT_TYPES)
-endif()
-
if(WITH_OPENVDB)
add_definitions(-DWITH_OPENVDB ${OPENVDB_DEFINITIONS})
list(APPEND INC_SYS
@@ -106,6 +102,13 @@ if(WITH_OPENVDB)
)
endif()
+if(WITH_OPENIMAGEDENOISE)
+ add_definitions(-DWITH_OPENIMAGEDENOISE)
+ list(APPEND INC_SYS
+ ${OPENIMAGEDENOISE_INCLUDE_DIRS}
+ )
+endif()
+
blender_add_lib(bf_intern_cycles "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
# avoid link failure with clang 3.4 debug
diff --git a/intern/cycles/blender/addon/engine.py b/intern/cycles/blender/addon/engine.py
index e7ea5e7a1f6..7566ca28dd7 100644
--- a/intern/cycles/blender/addon/engine.py
+++ b/intern/cycles/blender/addon/engine.py
@@ -179,7 +179,8 @@ def reset(engine, data, depsgraph):
import _cycles
import bpy
- if bpy.app.debug_value == 256:
+ prefs = bpy.context.preferences
+ if prefs.experimental.use_cycles_debug and prefs.view.show_developer_ui:
_cycles.debug_flags_update(depsgraph.scene.as_pointer())
else:
_cycles.debug_flags_reset()
diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py
index 1635afab210..840efb65d96 100644
--- a/intern/cycles/blender/addon/properties.py
+++ b/intern/cycles/blender/addon/properties.py
@@ -55,8 +55,7 @@ enum_displacement_methods = (
enum_bvh_layouts = (
('BVH2', "BVH2", "", 1),
- ('BVH4', "BVH4", "", 2),
- ('BVH8', "BVH8", "", 4),
+ ('EMBREE', "Embree", "", 4),
)
enum_bvh_types = (
@@ -78,20 +77,9 @@ enum_panorama_types = (
('MIRRORBALL', "Mirror Ball", "Uses the mirror ball mapping"),
)
-enum_curve_primitives = (
- ('TRIANGLES', "Triangles", "Create triangle geometry around strands"),
- ('LINE_SEGMENTS', "Line Segments", "Use line segment primitives"),
- ('CURVE_SEGMENTS', "Curve Segments", "Use segmented cardinal curve primitives"),
-)
-
-enum_triangle_curves = (
- ('CAMERA_TRIANGLES', "Planes", "Create individual triangles forming planes that face camera"),
- ('TESSELLATED_TRIANGLES', "Tessellated", "Create mesh surrounding each strand"),
-)
-
enum_curve_shape = (
- ('RIBBONS', "Ribbons", "Ignore thickness of each strand"),
- ('THICK', "Thick", "Use thickness of strand when rendering"),
+ ('RIBBONS', "Rounded Ribbons", "Render hair as flat ribbon with rounded normals, for fast rendering"),
+ ('THICK', "3D Curves", "Render hair as 3D curve, for accurate results when viewing hair close up"),
)
enum_tile_order = (
@@ -194,10 +182,36 @@ enum_aov_types = (
('COLOR', "Color", "Write a Color pass", 1),
)
-enum_viewport_denoising = (
- ('NONE', "None", "Disable viewport denoising", 0),
- ('OPTIX', "OptiX AI-Accelerated", "Use the OptiX denoiser running on the GPU (requires at least one compatible OptiX device)", 1),
-)
+def enum_openimagedenoise_denoiser(self, context):
+ if _cycles.with_openimagedenoise:
+ return [('OPENIMAGEDENOISE', "OpenImageDenoise", "Use Intel OpenImageDenoise AI denoiser running on the CPU", 4)]
+ return []
+
+def enum_optix_denoiser(self, context):
+ if not context or bool(context.preferences.addons[__package__].preferences.get_devices_for_type('OPTIX')):
+ return [('OPTIX', "OptiX", "Use the OptiX AI denoiser with GPU acceleration, only available on NVIDIA GPUs", 2)]
+ return []
+
+def enum_preview_denoiser(self, context):
+ optix_items = enum_optix_denoiser(self, context)
+ oidn_items = enum_openimagedenoise_denoiser(self, context)
+
+ if len(optix_items):
+ auto_label = "Fastest (Optix)"
+ elif len(oidn_items):
+ auto_label = "Fastest (OpenImageDenoise)"
+ else:
+ auto_label = "None"
+
+ items = [('AUTO', auto_label, "Use the fastest available denoiser for viewport rendering", 0)]
+ items += optix_items
+ items += oidn_items
+ return items
+
+def enum_denoiser(self, context):
+ items = [('NLM', "NLM", "Cycles native non-local means denoiser, running on any compute device", 1)]
+ items += enum_optix_denoiser(self, context)
+ return items
enum_denoising_optix_input_passes = (
('RGB', "Color", "Use only color as input", 1),
@@ -236,11 +250,29 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
description="Pause all viewport preview renders",
default=False,
)
- preview_denoising: EnumProperty(
- name="Viewport Denoising",
- description="Denoise the image after each preview update with the selected denoiser engine",
- items=enum_viewport_denoising,
- default='NONE',
+
+ use_denoising: BoolProperty(
+ name="Use Denoising",
+ description="Denoise the rendered image",
+ default=False,
+ )
+ use_preview_denoising: BoolProperty(
+ name="Use Viewport Denoising",
+ description="Denoise the image in the 3D viewport",
+ default=False,
+ )
+
+ denoiser: EnumProperty(
+ name="Denoiser",
+ description="Denoise the image with the selected denoiser",
+ items=enum_denoiser,
+ default=1,
+ )
+ preview_denoiser: EnumProperty(
+ name="Viewport Denoiser",
+ description="Denoise the image after each preview update with the selected denoiser",
+ items=enum_preview_denoiser,
+ default=0,
)
use_square_samples: BoolProperty(
@@ -256,7 +288,7 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
default=128,
)
preview_samples: IntProperty(
- name="Preview Samples",
+ name="Viewport Samples",
description="Number of samples to render in the viewport, unlimited if 0",
min=0, max=(1 << 24),
default=32,
@@ -476,7 +508,7 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
subtype='PIXEL'
)
preview_dicing_rate: FloatProperty(
- name="Preview Dicing Rate",
+ name="Viewport Dicing Rate",
description="Size of a micropolygon in pixels during preview render",
min=0.1, max=1000.0, soft_min=0.5,
default=8.0,
@@ -629,11 +661,6 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
items=enum_bvh_types,
default='DYNAMIC_BVH',
)
- use_bvh_embree: BoolProperty(
- name="Use Embree",
- description="Use Embree as ray accelerator",
- default=False,
- )
debug_use_spatial_splits: BoolProperty(
name="Use Spatial Splits",
description="Use BVH spatial splits: longer builder time, faster render",
@@ -786,7 +813,7 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
debug_bvh_layout: EnumProperty(
name="BVH Layout",
items=enum_bvh_layouts,
- default='BVH8',
+ default='EMBREE',
)
debug_use_cpu_split_kernel: BoolProperty(name="Split Kernel", default=False)
@@ -1241,39 +1268,17 @@ class CyclesObjectSettings(bpy.types.PropertyGroup):
class CyclesCurveRenderSettings(bpy.types.PropertyGroup):
- primitive: EnumProperty(
- name="Primitive",
- description="Type of primitive used for hair rendering",
- items=enum_curve_primitives,
- default='LINE_SEGMENTS',
- )
shape: EnumProperty(
name="Shape",
description="Form of hair",
items=enum_curve_shape,
- default='THICK',
- )
- cull_backfacing: BoolProperty(
- name="Cull Back-faces",
- description="Do not test the back-face of each strand",
- default=True,
- )
- use_curves: BoolProperty(
- name="Use Cycles Hair Rendering",
- description="Activate Cycles hair rendering for particle system",
- default=True,
- )
- resolution: IntProperty(
- name="Resolution",
- description="Resolution of generated mesh",
- min=3, max=64,
- default=3,
+ default='RIBBONS',
)
subdivisions: IntProperty(
name="Subdivisions",
description="Number of subdivisions used in Cardinal curve intersection (power of 2)",
min=0, max=24,
- default=4,
+ default=2,
)
@classmethod
@@ -1369,7 +1374,7 @@ class CyclesRenderLayerSettings(bpy.types.PropertyGroup):
use_denoising: BoolProperty(
name="Use Denoising",
description="Denoise the rendered image",
- default=False,
+ default=True,
update=update_render_passes,
)
denoising_diffuse_direct: BoolProperty(
@@ -1439,12 +1444,6 @@ class CyclesRenderLayerSettings(bpy.types.PropertyGroup):
default=0,
)
- use_optix_denoising: BoolProperty(
- name="OptiX AI-Accelerated",
- description="Use the OptiX denoiser to denoise the rendered image",
- default=False,
- update=update_render_passes,
- )
denoising_optix_input_passes: EnumProperty(
name="Input Passes",
description="Passes handed over to the OptiX denoiser (this can have different effects on the denoised image)",
diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py
index 78a44881743..b049d0bf2b4 100644
--- a/intern/cycles/blender/addon/ui.py
+++ b/intern/cycles/blender/addon/ui.py
@@ -112,10 +112,6 @@ def show_device_active(context):
return True
return context.preferences.addons[__package__].preferences.has_active_device()
-def show_optix_denoising(context):
- # OptiX AI denoiser can be used when at least one device supports OptiX
- return bool(context.preferences.addons[__package__].preferences.get_devices_for_type('OPTIX'))
-
def draw_samples_info(layout, context):
cscene = context.scene.cycles
@@ -190,11 +186,6 @@ class CYCLES_RENDER_PT_sampling(CyclesButtonsPanel, Panel):
col.prop(cscene, "aa_samples", text="Render")
col.prop(cscene, "preview_aa_samples", text="Viewport")
- # Viewport denoising is currently only supported with OptiX
- if show_optix_denoising(context):
- col = layout.column()
- col.prop(cscene, "preview_denoising")
-
if not use_branched_path(context):
draw_samples_info(layout, context)
@@ -256,6 +247,39 @@ class CYCLES_RENDER_PT_sampling_adaptive(CyclesButtonsPanel, Panel):
col.prop(cscene, "adaptive_threshold", text="Noise Threshold")
col.prop(cscene, "adaptive_min_samples", text="Min Samples")
+
+class CYCLES_RENDER_PT_sampling_denoising(CyclesButtonsPanel, Panel):
+ bl_label = "Denoising"
+ bl_parent_id = "CYCLES_RENDER_PT_sampling"
+ bl_options = {'DEFAULT_CLOSED'}
+
+ def draw(self, context):
+ layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False
+
+ scene = context.scene
+ cscene = scene.cycles
+
+ heading = layout.column(align=True, heading="Render")
+ row = heading.row(align=True)
+ row.prop(cscene, "use_denoising", text="")
+ sub = row.row()
+ sub.active = cscene.use_denoising
+ sub.prop(cscene, "denoiser", text="")
+
+ heading = layout.column(align=False, heading="Viewport")
+ row = heading.row(align=True)
+ row.prop(cscene, "use_preview_denoising", text="")
+ sub = row.row()
+ sub.active = cscene.use_preview_denoising
+ sub.prop(cscene, "preview_denoiser", text="")
+
+ sub = heading.row(align=True)
+ sub.active = cscene.use_preview_denoising
+ sub.prop(cscene, "preview_denoising_start_sample", text="Start Sample")
+
+
class CYCLES_RENDER_PT_sampling_advanced(CyclesButtonsPanel, Panel):
bl_label = "Advanced"
bl_parent_id = "CYCLES_RENDER_PT_sampling"
@@ -387,13 +411,6 @@ class CYCLES_RENDER_PT_hair(CyclesButtonsPanel, Panel):
bl_label = "Hair"
bl_options = {'DEFAULT_CLOSED'}
- def draw_header(self, context):
- layout = self.layout
- scene = context.scene
- ccscene = scene.cycles_curves
-
- layout.prop(ccscene, "use_curves", text="")
-
def draw(self, context):
layout = self.layout
layout.use_property_split = True
@@ -402,18 +419,10 @@ class CYCLES_RENDER_PT_hair(CyclesButtonsPanel, Panel):
scene = context.scene
ccscene = scene.cycles_curves
- layout.active = ccscene.use_curves
-
col = layout.column()
col.prop(ccscene, "shape", text="Shape")
- if not (ccscene.primitive in {'CURVE_SEGMENTS', 'LINE_SEGMENTS'} and ccscene.shape == 'RIBBONS'):
- col.prop(ccscene, "cull_backfacing", text="Cull back-faces")
- col.prop(ccscene, "primitive", text="Primitive")
-
- if ccscene.primitive == 'TRIANGLES' and ccscene.shape == 'THICK':
- col.prop(ccscene, "resolution", text="Resolution")
- elif ccscene.primitive == 'CURVE_SEGMENTS':
- col.prop(ccscene, "subdivisions", text="Curve subdivisions")
+ if ccscene.shape == 'RIBBONS':
+ col.prop(ccscene, "subdivisions", text="Curve Subdivisions")
class CYCLES_RENDER_PT_volumes(CyclesButtonsPanel, Panel):
@@ -693,16 +702,20 @@ class CYCLES_RENDER_PT_performance_acceleration_structure(CyclesButtonsPanel, Pa
col = layout.column()
- if _cycles.with_embree:
- row = col.row()
- row.active = use_cpu(context)
- row.prop(cscene, "use_bvh_embree")
+ use_embree = False
+ if use_cpu(context):
+ use_embree = _cycles.with_embree
+ if not use_embree:
+ sub = col.column(align=True)
+ sub.label(text="Cycles built without Embree support")
+ sub.label(text="CPU raytracing performance will be poor")
+
col.prop(cscene, "debug_use_spatial_splits")
sub = col.column()
- sub.active = not cscene.use_bvh_embree or not _cycles.with_embree
+ sub.active = not use_embree
sub.prop(cscene, "debug_use_hair_bvh")
sub = col.column()
- sub.active = not cscene.debug_use_spatial_splits and not cscene.use_bvh_embree
+ sub.active = not cscene.debug_use_spatial_splits and not use_embree
sub.prop(cscene, "debug_bvh_time_steps")
@@ -741,11 +754,6 @@ class CYCLES_RENDER_PT_performance_viewport(CyclesButtonsPanel, Panel):
col.prop(rd, "preview_pixel_size", text="Pixel Size")
col.prop(cscene, "preview_start_resolution", text="Start Pixels")
- if show_optix_denoising(context):
- sub = col.row(align=True)
- sub.active = cscene.preview_denoising != 'NONE'
- sub.prop(cscene, "preview_denoising_start_sample", text="Denoising Start Sample")
-
class CYCLES_RENDER_PT_filter(CyclesButtonsPanel, Panel):
bl_label = "Filter"
@@ -968,12 +976,17 @@ class CYCLES_RENDER_PT_denoising(CyclesButtonsPanel, Panel):
bl_context = "view_layer"
bl_options = {'DEFAULT_CLOSED'}
+ @classmethod
+ def poll(cls, context):
+ cscene = context.scene.cycles
+ return CyclesButtonsPanel.poll(context) and cscene.use_denoising
+
def draw_header(self, context):
scene = context.scene
view_layer = context.view_layer
cycles_view_layer = view_layer.cycles
- layout = self.layout
+ layout = self.layout
layout.prop(cycles_view_layer, "use_denoising", text="")
def draw(self, context):
@@ -984,18 +997,17 @@ class CYCLES_RENDER_PT_denoising(CyclesButtonsPanel, Panel):
scene = context.scene
view_layer = context.view_layer
cycles_view_layer = view_layer.cycles
+ denoiser = scene.cycles.denoiser
- layout.active = cycles_view_layer.use_denoising
+ layout.active = denoiser != 'NONE' and cycles_view_layer.use_denoising
col = layout.column()
- if show_optix_denoising(context):
- col.prop(cycles_view_layer, "use_optix_denoising")
- col.separator(factor=2.0)
-
- if cycles_view_layer.use_optix_denoising:
- col.prop(cycles_view_layer, "denoising_optix_input_passes")
- return
+ if denoiser == 'OPTIX':
+ col.prop(cycles_view_layer, "denoising_optix_input_passes")
+ return
+ elif denoiser == 'OPENIMAGEDENOISE':
+ return
col.prop(cycles_view_layer, "denoising_radius", text="Radius")
@@ -1190,6 +1202,7 @@ class CYCLES_OBJECT_PT_motion_blur(CyclesButtonsPanel, Panel):
def draw(self, context):
layout = self.layout
+ layout.use_property_split = True
rd = context.scene.render
# scene = context.scene
@@ -1199,10 +1212,10 @@ class CYCLES_OBJECT_PT_motion_blur(CyclesButtonsPanel, Panel):
layout.active = (rd.use_motion_blur and cob.use_motion_blur)
- row = layout.row()
+ col = layout.column()
+ col.prop(cob, "motion_steps", text="Steps")
if ob.type != 'CAMERA':
- row.prop(cob, "use_deform_motion", text="Deformation")
- row.prop(cob, "motion_steps", text="Steps")
+ col.prop(cob, "use_deform_motion", text="Deformation")
def has_geometry_visibility(ob):
@@ -1575,17 +1588,18 @@ class CYCLES_WORLD_PT_ray_visibility(CyclesButtonsPanel, Panel):
def draw(self, context):
layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False
world = context.world
visibility = world.cycles_visibility
- flow = layout.column_flow()
-
- flow.prop(visibility, "camera")
- flow.prop(visibility, "diffuse")
- flow.prop(visibility, "glossy")
- flow.prop(visibility, "transmission")
- flow.prop(visibility, "scatter")
+ col = layout.column()
+ col.prop(visibility, "camera")
+ col.prop(visibility, "diffuse")
+ col.prop(visibility, "glossy")
+ col.prop(visibility, "transmission")
+ col.prop(visibility, "scatter")
class CYCLES_WORLD_PT_settings(CyclesButtonsPanel, Panel):
@@ -1975,7 +1989,10 @@ class CYCLES_RENDER_PT_debug(CyclesButtonsPanel, Panel):
@classmethod
def poll(cls, context):
- return CyclesButtonsPanel.poll(context) and bpy.app.debug_value == 256
+ prefs = bpy.context.preferences
+ return (CyclesButtonsPanel.poll(context)
+ and prefs.experimental.use_cycles_debug
+ and prefs.view.show_developer_ui)
def draw(self, context):
layout = self.layout
@@ -2248,6 +2265,7 @@ classes = (
CYCLES_RENDER_PT_sampling,
CYCLES_RENDER_PT_sampling_sub_samples,
CYCLES_RENDER_PT_sampling_adaptive,
+ CYCLES_RENDER_PT_sampling_denoising,
CYCLES_RENDER_PT_sampling_advanced,
CYCLES_RENDER_PT_light_paths,
CYCLES_RENDER_PT_light_paths_max_bounces,
diff --git a/intern/cycles/blender/blender_camera.cpp b/intern/cycles/blender/blender_camera.cpp
index 40a1a2c2edc..011678a7a65 100644
--- a/intern/cycles/blender/blender_camera.cpp
+++ b/intern/cycles/blender/blender_camera.cpp
@@ -867,13 +867,13 @@ void BlenderSync::sync_view(BL::SpaceView3D &b_v3d,
}
}
-BufferParams BlenderSync::get_buffer_params(BL::Scene &b_scene,
- BL::RenderSettings &b_render,
+BufferParams BlenderSync::get_buffer_params(BL::RenderSettings &b_render,
BL::SpaceView3D &b_v3d,
BL::RegionView3D &b_rv3d,
Camera *cam,
int width,
- int height)
+ int height,
+ const bool use_denoiser)
{
BufferParams params;
bool use_border = false;
@@ -907,8 +907,7 @@ BufferParams BlenderSync::get_buffer_params(BL::Scene &b_scene,
PassType display_pass = update_viewport_display_passes(b_v3d, params.passes);
/* Can only denoise the combined image pass */
- params.denoising_data_pass = display_pass == PASS_COMBINED &&
- update_viewport_display_denoising(b_v3d, b_scene);
+ params.denoising_data_pass = display_pass == PASS_COMBINED && use_denoiser;
return params;
}
diff --git a/intern/cycles/blender/blender_curves.cpp b/intern/cycles/blender/blender_curves.cpp
index 847a43c5f34..82c99631a89 100644
--- a/intern/cycles/blender/blender_curves.cpp
+++ b/intern/cycles/blender/blender_curves.cpp
@@ -18,7 +18,6 @@
#include "render/camera.h"
#include "render/curves.h"
#include "render/hair.h"
-#include "render/mesh.h"
#include "render/object.h"
#include "render/scene.h"
@@ -39,27 +38,6 @@ ParticleCurveData::~ParticleCurveData()
{
}
-static void interp_weights(float t, float data[4])
-{
- /* Cardinal curve interpolation */
- float t2 = t * t;
- float t3 = t2 * t;
- float fc = 0.71f;
-
- data[0] = -fc * t3 + 2.0f * fc * t2 - fc * t;
- data[1] = (2.0f - fc) * t3 + (fc - 3.0f) * t2 + 1.0f;
- data[2] = (fc - 2.0f) * t3 + (3.0f - 2.0f * fc) * t2 + fc * t;
- data[3] = fc * t3 - fc * t2;
-}
-
-static void curveinterp_v3_v3v3v3v3(
- float3 *p, float3 *v1, float3 *v2, float3 *v3, float3 *v4, const float w[4])
-{
- p->x = v1->x * w[0] + v2->x * w[1] + v3->x * w[2] + v4->x * w[3];
- p->y = v1->y * w[0] + v2->y * w[1] + v3->y * w[2] + v4->y * w[3];
- p->z = v1->z * w[0] + v2->z * w[1] + v3->z * w[2] + v4->z * w[3];
-}
-
static float shaperadius(float shape, float root, float tip, float time)
{
assert(time >= 0.0f);
@@ -77,43 +55,13 @@ static float shaperadius(float shape, float root, float tip, float time)
/* curve functions */
-static void InterpolateKeySegments(
- int seg, int segno, int key, int curve, float3 *keyloc, float *time, ParticleCurveData *CData)
-{
- float3 ckey_loc1 = CData->curvekey_co[key];
- float3 ckey_loc2 = ckey_loc1;
- float3 ckey_loc3 = CData->curvekey_co[key + 1];
- float3 ckey_loc4 = ckey_loc3;
-
- if (key > CData->curve_firstkey[curve])
- ckey_loc1 = CData->curvekey_co[key - 1];
-
- if (key < CData->curve_firstkey[curve] + CData->curve_keynum[curve] - 2)
- ckey_loc4 = CData->curvekey_co[key + 2];
-
- float time1 = CData->curvekey_time[key] / CData->curve_length[curve];
- float time2 = CData->curvekey_time[key + 1] / CData->curve_length[curve];
-
- float dfra = (time2 - time1) / (float)segno;
-
- if (time)
- *time = (dfra * seg) + time1;
-
- float t[4];
-
- interp_weights((float)seg / (float)segno, t);
-
- if (keyloc)
- curveinterp_v3_v3v3v3v3(keyloc, &ckey_loc1, &ckey_loc2, &ckey_loc3, &ckey_loc4, t);
-}
-
static bool ObtainCacheParticleData(
- Geometry *geom, BL::Mesh *b_mesh, BL::Object *b_ob, ParticleCurveData *CData, bool background)
+ Hair *hair, BL::Mesh *b_mesh, BL::Object *b_ob, ParticleCurveData *CData, bool background)
{
int curvenum = 0;
int keyno = 0;
- if (!(geom && b_mesh && b_ob && CData))
+ if (!(hair && b_mesh && b_ob && CData))
return false;
Transform tfm = get_transform(b_ob->matrix_world());
@@ -129,7 +77,7 @@ static bool ObtainCacheParticleData(
if ((b_part.render_type() == BL::ParticleSettings::render_type_PATH) &&
(b_part.type() == BL::ParticleSettings::type_HAIR)) {
- int shader = clamp(b_part.material() - 1, 0, geom->used_shaders.size() - 1);
+ int shader = clamp(b_part.material() - 1, 0, hair->used_shaders.size() - 1);
int display_step = background ? b_part.render_step() : b_part.display_step();
int totparts = b_psys.particles.length();
int totchild = background ? b_psys.child_particles.length() :
@@ -203,14 +151,14 @@ static bool ObtainCacheParticleData(
return true;
}
-static bool ObtainCacheParticleUV(Geometry *geom,
+static bool ObtainCacheParticleUV(Hair *hair,
BL::Mesh *b_mesh,
BL::Object *b_ob,
ParticleCurveData *CData,
bool background,
int uv_num)
{
- if (!(geom && b_mesh && b_ob && CData))
+ if (!(hair && b_mesh && b_ob && CData))
return false;
CData->curve_uv.clear();
@@ -266,14 +214,14 @@ static bool ObtainCacheParticleUV(Geometry *geom,
return true;
}
-static bool ObtainCacheParticleVcol(Geometry *geom,
+static bool ObtainCacheParticleVcol(Hair *hair,
BL::Mesh *b_mesh,
BL::Object *b_ob,
ParticleCurveData *CData,
bool background,
int vcol_num)
{
- if (!(geom && b_mesh && b_ob && CData))
+ if (!(hair && b_mesh && b_ob && CData))
return false;
CData->curve_vcol.clear();
@@ -314,7 +262,7 @@ static bool ObtainCacheParticleVcol(Geometry *geom,
BL::Mesh::vertex_colors_iterator l;
b_mesh->vertex_colors.begin(l);
- float3 vcol = make_float3(0.0f, 0.0f, 0.0f);
+ float4 vcol = make_float4(0.0f, 0.0f, 0.0f, 1.0f);
if (b_mesh->vertex_colors.length())
b_psys.mcol_on_emitter(psmd, *b_pa, pa_no, vcol_num, &vcol.x);
CData->curve_vcol.push_back_slow(vcol);
@@ -329,272 +277,6 @@ static bool ObtainCacheParticleVcol(Geometry *geom,
return true;
}
-static void ExportCurveTrianglePlanes(Mesh *mesh,
- ParticleCurveData *CData,
- float3 RotCam,
- bool is_ortho)
-{
- int vertexno = mesh->verts.size();
- int vertexindex = vertexno;
- int numverts = 0, numtris = 0;
-
- /* compute and reserve size of arrays */
- for (int sys = 0; sys < CData->psys_firstcurve.size(); sys++) {
- for (int curve = CData->psys_firstcurve[sys];
- curve < CData->psys_firstcurve[sys] + CData->psys_curvenum[sys];
- curve++) {
- numverts += 2 + (CData->curve_keynum[curve] - 1) * 2;
- numtris += (CData->curve_keynum[curve] - 1) * 2;
- }
- }
-
- mesh->reserve_mesh(mesh->verts.size() + numverts, mesh->num_triangles() + numtris);
-
- /* actually export */
- for (int sys = 0; sys < CData->psys_firstcurve.size(); sys++) {
- for (int curve = CData->psys_firstcurve[sys];
- curve < CData->psys_firstcurve[sys] + CData->psys_curvenum[sys];
- curve++) {
- float3 xbasis;
- float3 v1;
- float time = 0.0f;
- float3 ickey_loc = CData->curvekey_co[CData->curve_firstkey[curve]];
- float radius = shaperadius(
- CData->psys_shape[sys], CData->psys_rootradius[sys], CData->psys_tipradius[sys], 0.0f);
- v1 = CData->curvekey_co[CData->curve_firstkey[curve] + 1] -
- CData->curvekey_co[CData->curve_firstkey[curve]];
- if (is_ortho)
- xbasis = normalize(cross(RotCam, v1));
- else
- xbasis = normalize(cross(RotCam - ickey_loc, v1));
- float3 ickey_loc_shfl = ickey_loc - radius * xbasis;
- float3 ickey_loc_shfr = ickey_loc + radius * xbasis;
- mesh->add_vertex(ickey_loc_shfl);
- mesh->add_vertex(ickey_loc_shfr);
- vertexindex += 2;
-
- for (int curvekey = CData->curve_firstkey[curve] + 1;
- curvekey < CData->curve_firstkey[curve] + CData->curve_keynum[curve];
- curvekey++) {
- ickey_loc = CData->curvekey_co[curvekey];
-
- if (curvekey == CData->curve_firstkey[curve] + CData->curve_keynum[curve] - 1)
- v1 = CData->curvekey_co[curvekey] -
- CData->curvekey_co[max(curvekey - 1, CData->curve_firstkey[curve])];
- else
- v1 = CData->curvekey_co[curvekey + 1] - CData->curvekey_co[curvekey - 1];
-
- time = CData->curvekey_time[curvekey] / CData->curve_length[curve];
- radius = shaperadius(
- CData->psys_shape[sys], CData->psys_rootradius[sys], CData->psys_tipradius[sys], time);
-
- if (curvekey == CData->curve_firstkey[curve] + CData->curve_keynum[curve] - 1)
- radius = shaperadius(CData->psys_shape[sys],
- CData->psys_rootradius[sys],
- CData->psys_tipradius[sys],
- 0.95f);
-
- if (CData->psys_closetip[sys] &&
- (curvekey == CData->curve_firstkey[curve] + CData->curve_keynum[curve] - 1))
- radius = shaperadius(CData->psys_shape[sys], CData->psys_rootradius[sys], 0.0f, 0.95f);
-
- if (is_ortho)
- xbasis = normalize(cross(RotCam, v1));
- else
- xbasis = normalize(cross(RotCam - ickey_loc, v1));
- float3 ickey_loc_shfl = ickey_loc - radius * xbasis;
- float3 ickey_loc_shfr = ickey_loc + radius * xbasis;
- mesh->add_vertex(ickey_loc_shfl);
- mesh->add_vertex(ickey_loc_shfr);
- mesh->add_triangle(
- vertexindex - 2, vertexindex, vertexindex - 1, CData->psys_shader[sys], true);
- mesh->add_triangle(
- vertexindex + 1, vertexindex - 1, vertexindex, CData->psys_shader[sys], true);
- vertexindex += 2;
- }
- }
- }
-
- mesh->resize_mesh(mesh->verts.size(), mesh->num_triangles());
- mesh->attributes.remove(ATTR_STD_VERTEX_NORMAL);
- mesh->attributes.remove(ATTR_STD_FACE_NORMAL);
- mesh->add_face_normals();
- mesh->add_vertex_normals();
- mesh->attributes.remove(ATTR_STD_FACE_NORMAL);
-
- /* texture coords still needed */
-}
-
-static void ExportCurveTriangleGeometry(Mesh *mesh, ParticleCurveData *CData, int resolution)
-{
- int vertexno = mesh->verts.size();
- int vertexindex = vertexno;
- int numverts = 0, numtris = 0;
-
- /* compute and reserve size of arrays */
- for (int sys = 0; sys < CData->psys_firstcurve.size(); sys++) {
- for (int curve = CData->psys_firstcurve[sys];
- curve < CData->psys_firstcurve[sys] + CData->psys_curvenum[sys];
- curve++) {
- numverts += (CData->curve_keynum[curve] - 1) * resolution + resolution;
- numtris += (CData->curve_keynum[curve] - 1) * 2 * resolution;
- }
- }
-
- mesh->reserve_mesh(mesh->verts.size() + numverts, mesh->num_triangles() + numtris);
-
- /* actually export */
- for (int sys = 0; sys < CData->psys_firstcurve.size(); sys++) {
- for (int curve = CData->psys_firstcurve[sys];
- curve < CData->psys_firstcurve[sys] + CData->psys_curvenum[sys];
- curve++) {
- float3 firstxbasis = cross(make_float3(1.0f, 0.0f, 0.0f),
- CData->curvekey_co[CData->curve_firstkey[curve] + 1] -
- CData->curvekey_co[CData->curve_firstkey[curve]]);
- if (!is_zero(firstxbasis))
- firstxbasis = normalize(firstxbasis);
- else
- firstxbasis = normalize(cross(make_float3(0.0f, 1.0f, 0.0f),
- CData->curvekey_co[CData->curve_firstkey[curve] + 1] -
- CData->curvekey_co[CData->curve_firstkey[curve]]));
-
- for (int curvekey = CData->curve_firstkey[curve];
- curvekey < CData->curve_firstkey[curve] + CData->curve_keynum[curve] - 1;
- curvekey++) {
- float3 xbasis = firstxbasis;
- float3 v1;
- float3 v2;
-
- if (curvekey == CData->curve_firstkey[curve]) {
- v1 = CData->curvekey_co[min(
- curvekey + 2, CData->curve_firstkey[curve] + CData->curve_keynum[curve] - 1)] -
- CData->curvekey_co[curvekey + 1];
- v2 = CData->curvekey_co[curvekey + 1] - CData->curvekey_co[curvekey];
- }
- else if (curvekey == CData->curve_firstkey[curve] + CData->curve_keynum[curve] - 1) {
- v1 = CData->curvekey_co[curvekey] - CData->curvekey_co[curvekey - 1];
- v2 = CData->curvekey_co[curvekey - 1] -
- CData->curvekey_co[max(curvekey - 2, CData->curve_firstkey[curve])];
- }
- else {
- v1 = CData->curvekey_co[curvekey + 1] - CData->curvekey_co[curvekey];
- v2 = CData->curvekey_co[curvekey] - CData->curvekey_co[curvekey - 1];
- }
-
- xbasis = cross(v1, v2);
-
- if (len_squared(xbasis) >= 0.05f * len_squared(v1) * len_squared(v2)) {
- firstxbasis = normalize(xbasis);
- break;
- }
- }
-
- for (int curvekey = CData->curve_firstkey[curve];
- curvekey < CData->curve_firstkey[curve] + CData->curve_keynum[curve] - 1;
- curvekey++) {
- int subv = 1;
- float3 xbasis;
- float3 ybasis;
- float3 v1;
- float3 v2;
-
- if (curvekey == CData->curve_firstkey[curve]) {
- subv = 0;
- v1 = CData->curvekey_co[min(
- curvekey + 2, CData->curve_firstkey[curve] + CData->curve_keynum[curve] - 1)] -
- CData->curvekey_co[curvekey + 1];
- v2 = CData->curvekey_co[curvekey + 1] - CData->curvekey_co[curvekey];
- }
- else if (curvekey == CData->curve_firstkey[curve] + CData->curve_keynum[curve] - 1) {
- v1 = CData->curvekey_co[curvekey] - CData->curvekey_co[curvekey - 1];
- v2 = CData->curvekey_co[curvekey - 1] -
- CData->curvekey_co[max(curvekey - 2, CData->curve_firstkey[curve])];
- }
- else {
- v1 = CData->curvekey_co[curvekey + 1] - CData->curvekey_co[curvekey];
- v2 = CData->curvekey_co[curvekey] - CData->curvekey_co[curvekey - 1];
- }
-
- xbasis = cross(v1, v2);
-
- if (len_squared(xbasis) >= 0.05f * len_squared(v1) * len_squared(v2)) {
- xbasis = normalize(xbasis);
- firstxbasis = xbasis;
- }
- else
- xbasis = firstxbasis;
-
- ybasis = normalize(cross(xbasis, v2));
-
- for (; subv <= 1; subv++) {
- float3 ickey_loc = make_float3(0.0f, 0.0f, 0.0f);
- float time = 0.0f;
-
- InterpolateKeySegments(subv, 1, curvekey, curve, &ickey_loc, &time, CData);
-
- float radius = shaperadius(CData->psys_shape[sys],
- CData->psys_rootradius[sys],
- CData->psys_tipradius[sys],
- time);
-
- if ((curvekey == CData->curve_firstkey[curve] + CData->curve_keynum[curve] - 2) &&
- (subv == 1))
- radius = shaperadius(CData->psys_shape[sys],
- CData->psys_rootradius[sys],
- CData->psys_tipradius[sys],
- 0.95f);
-
- if (CData->psys_closetip[sys] && (subv == 1) &&
- (curvekey == CData->curve_firstkey[curve] + CData->curve_keynum[curve] - 2))
- radius = shaperadius(CData->psys_shape[sys], CData->psys_rootradius[sys], 0.0f, 0.95f);
-
- float angle = M_2PI_F / (float)resolution;
- for (int section = 0; section < resolution; section++) {
- float3 ickey_loc_shf = ickey_loc + radius * (cosf(angle * section) * xbasis +
- sinf(angle * section) * ybasis);
- mesh->add_vertex(ickey_loc_shf);
- }
-
- if (subv != 0) {
- for (int section = 0; section < resolution - 1; section++) {
- mesh->add_triangle(vertexindex - resolution + section,
- vertexindex + section,
- vertexindex - resolution + section + 1,
- CData->psys_shader[sys],
- true);
- mesh->add_triangle(vertexindex + section + 1,
- vertexindex - resolution + section + 1,
- vertexindex + section,
- CData->psys_shader[sys],
- true);
- }
- mesh->add_triangle(vertexindex - 1,
- vertexindex + resolution - 1,
- vertexindex - resolution,
- CData->psys_shader[sys],
- true);
- mesh->add_triangle(vertexindex,
- vertexindex - resolution,
- vertexindex + resolution - 1,
- CData->psys_shader[sys],
- true);
- }
- vertexindex += resolution;
- }
- }
- }
- }
-
- mesh->resize_mesh(mesh->verts.size(), mesh->num_triangles());
- mesh->attributes.remove(ATTR_STD_VERTEX_NORMAL);
- mesh->attributes.remove(ATTR_STD_FACE_NORMAL);
- mesh->add_face_normals();
- mesh->add_vertex_normals();
- mesh->attributes.remove(ATTR_STD_FACE_NORMAL);
-
- /* texture coords still needed */
-}
-
static void ExportCurveSegments(Scene *scene, Hair *hair, ParticleCurveData *CData)
{
int num_keys = 0;
@@ -823,154 +505,8 @@ static void ExportCurveSegmentsMotion(Hair *hair, ParticleCurveData *CData, int
}
}
-static void ExportCurveTriangleUV(ParticleCurveData *CData, int resol, float2 *uvdata)
-{
- if (uvdata == NULL)
- return;
- int vertexindex = 0;
-
- for (int sys = 0; sys < CData->psys_firstcurve.size(); sys++) {
- for (int curve = CData->psys_firstcurve[sys];
- curve < CData->psys_firstcurve[sys] + CData->psys_curvenum[sys];
- curve++) {
- for (int curvekey = CData->curve_firstkey[curve];
- curvekey < CData->curve_firstkey[curve] + CData->curve_keynum[curve] - 1;
- curvekey++) {
- for (int section = 0; section < resol; section++) {
- uvdata[vertexindex] = CData->curve_uv[curve];
- vertexindex++;
- uvdata[vertexindex] = CData->curve_uv[curve];
- vertexindex++;
- uvdata[vertexindex] = CData->curve_uv[curve];
- vertexindex++;
- uvdata[vertexindex] = CData->curve_uv[curve];
- vertexindex++;
- uvdata[vertexindex] = CData->curve_uv[curve];
- vertexindex++;
- uvdata[vertexindex] = CData->curve_uv[curve];
- vertexindex++;
- }
- }
- }
- }
-}
-
-static void ExportCurveTriangleVcol(ParticleCurveData *CData, int resol, uchar4 *cdata)
-{
- if (cdata == NULL)
- return;
-
- int vertexindex = 0;
-
- for (int sys = 0; sys < CData->psys_firstcurve.size(); sys++) {
- for (int curve = CData->psys_firstcurve[sys];
- curve < CData->psys_firstcurve[sys] + CData->psys_curvenum[sys];
- curve++) {
- for (int curvekey = CData->curve_firstkey[curve];
- curvekey < CData->curve_firstkey[curve] + CData->curve_keynum[curve] - 1;
- curvekey++) {
- for (int section = 0; section < resol; section++) {
- /* Encode vertex color using the sRGB curve. */
- cdata[vertexindex] = color_float_to_byte(
- color_srgb_to_linear_v3(CData->curve_vcol[curve]));
- vertexindex++;
- cdata[vertexindex] = color_float_to_byte(
- color_srgb_to_linear_v3(CData->curve_vcol[curve]));
- vertexindex++;
- cdata[vertexindex] = color_float_to_byte(
- color_srgb_to_linear_v3(CData->curve_vcol[curve]));
- vertexindex++;
- cdata[vertexindex] = color_float_to_byte(
- color_srgb_to_linear_v3(CData->curve_vcol[curve]));
- vertexindex++;
- cdata[vertexindex] = color_float_to_byte(
- color_srgb_to_linear_v3(CData->curve_vcol[curve]));
- vertexindex++;
- cdata[vertexindex] = color_float_to_byte(
- color_srgb_to_linear_v3(CData->curve_vcol[curve]));
- vertexindex++;
- }
- }
- }
- }
-}
-
/* Hair Curve Sync */
-void BlenderSync::sync_curve_settings(BL::Depsgraph &b_depsgraph)
-{
- PointerRNA csscene = RNA_pointer_get(&b_scene.ptr, "cycles_curves");
-
- CurveSystemManager *curve_system_manager = scene->curve_system_manager;
- CurveSystemManager prev_curve_system_manager = *curve_system_manager;
-
- curve_system_manager->use_curves = get_boolean(csscene, "use_curves");
-
- curve_system_manager->primitive = (CurvePrimitiveType)get_enum(
- csscene, "primitive", CURVE_NUM_PRIMITIVE_TYPES, CURVE_LINE_SEGMENTS);
- curve_system_manager->curve_shape = (CurveShapeType)get_enum(
- csscene, "shape", CURVE_NUM_SHAPE_TYPES, CURVE_THICK);
- curve_system_manager->resolution = get_int(csscene, "resolution");
- curve_system_manager->subdivisions = get_int(csscene, "subdivisions");
- curve_system_manager->use_backfacing = !get_boolean(csscene, "cull_backfacing");
-
- /* Triangles */
- if (curve_system_manager->primitive == CURVE_TRIANGLES) {
- /* camera facing planes */
- if (curve_system_manager->curve_shape == CURVE_RIBBON) {
- curve_system_manager->triangle_method = CURVE_CAMERA_TRIANGLES;
- curve_system_manager->resolution = 1;
- }
- else if (curve_system_manager->curve_shape == CURVE_THICK) {
- curve_system_manager->triangle_method = CURVE_TESSELATED_TRIANGLES;
- }
- }
- /* Line Segments */
- else if (curve_system_manager->primitive == CURVE_LINE_SEGMENTS) {
- if (curve_system_manager->curve_shape == CURVE_RIBBON) {
- /* tangent shading */
- curve_system_manager->line_method = CURVE_UNCORRECTED;
- curve_system_manager->use_encasing = true;
- curve_system_manager->use_backfacing = false;
- curve_system_manager->use_tangent_normal_geometry = true;
- }
- else if (curve_system_manager->curve_shape == CURVE_THICK) {
- curve_system_manager->line_method = CURVE_ACCURATE;
- curve_system_manager->use_encasing = false;
- curve_system_manager->use_tangent_normal_geometry = false;
- }
- }
- /* Curve Segments */
- else if (curve_system_manager->primitive == CURVE_SEGMENTS) {
- if (curve_system_manager->curve_shape == CURVE_RIBBON) {
- curve_system_manager->primitive = CURVE_RIBBONS;
- curve_system_manager->use_backfacing = false;
- }
- }
-
- if (curve_system_manager->modified_mesh(prev_curve_system_manager)) {
- BL::Depsgraph::objects_iterator b_ob;
-
- for (b_depsgraph.objects.begin(b_ob); b_ob != b_data.objects.end(); ++b_ob) {
- if (object_is_mesh(*b_ob)) {
- BL::Object::particle_systems_iterator b_psys;
- for (b_ob->particle_systems.begin(b_psys); b_psys != b_ob->particle_systems.end();
- ++b_psys) {
- if ((b_psys->settings().render_type() == BL::ParticleSettings::render_type_PATH) &&
- (b_psys->settings().type() == BL::ParticleSettings::type_HAIR)) {
- BL::ID key = BKE_object_is_modified(*b_ob) ? *b_ob : b_ob->data();
- geometry_map.set_recalc(key);
- object_map.set_recalc(*b_ob);
- }
- }
- }
- }
- }
-
- if (curve_system_manager->modified(prev_curve_system_manager))
- curve_system_manager->tag_update(scene);
-}
-
bool BlenderSync::object_has_particle_hair(BL::Object b_ob)
{
/* Test if the object has a particle modifier with hair. */
@@ -994,78 +530,38 @@ bool BlenderSync::object_has_particle_hair(BL::Object b_ob)
/* Old particle hair. */
void BlenderSync::sync_particle_hair(
- Geometry *geom, BL::Mesh &b_mesh, BL::Object &b_ob, bool motion, int motion_step)
+ Hair *hair, BL::Mesh &b_mesh, BL::Object &b_ob, bool motion, int motion_step)
{
- Hair *hair = (geom->type == Geometry::HAIR) ? static_cast<Hair *>(geom) : NULL;
- Mesh *mesh = (geom->type == Geometry::MESH) ? static_cast<Mesh *>(geom) : NULL;
-
/* obtain general settings */
if (b_ob.mode() == b_ob.mode_PARTICLE_EDIT || b_ob.mode() == b_ob.mode_EDIT) {
return;
}
- const int triangle_method = scene->curve_system_manager->triangle_method;
- const int resolution = scene->curve_system_manager->resolution;
- int used_res = 1;
-
/* extract particle hair data - should be combined with connecting to mesh later*/
ParticleCurveData CData;
- ObtainCacheParticleData(geom, &b_mesh, &b_ob, &CData, !preview);
-
- /* add hair geometry to mesh */
- if (mesh) {
- if (triangle_method == CURVE_CAMERA_TRIANGLES) {
- /* obtain camera parameters */
- float3 RotCam;
- Camera *camera = scene->camera;
- Transform &ctfm = camera->matrix;
- if (camera->type == CAMERA_ORTHOGRAPHIC) {
- RotCam = -make_float3(ctfm.x.z, ctfm.y.z, ctfm.z.z);
- }
- else {
- Transform tfm = get_transform(b_ob.matrix_world());
- Transform itfm = transform_quick_inverse(tfm);
- RotCam = transform_point(&itfm, make_float3(ctfm.x.w, ctfm.y.w, ctfm.z.w));
- }
- bool is_ortho = camera->type == CAMERA_ORTHOGRAPHIC;
- ExportCurveTrianglePlanes(mesh, &CData, RotCam, is_ortho);
- }
- else {
- ExportCurveTriangleGeometry(mesh, &CData, resolution);
- used_res = resolution;
- }
- }
- else {
- if (motion)
- ExportCurveSegmentsMotion(hair, &CData, motion_step);
- else
- ExportCurveSegments(scene, hair, &CData);
- }
+ ObtainCacheParticleData(hair, &b_mesh, &b_ob, &CData, !preview);
+
+ /* add hair geometry */
+ if (motion)
+ ExportCurveSegmentsMotion(hair, &CData, motion_step);
+ else
+ ExportCurveSegments(scene, hair, &CData);
/* generated coordinates from first key. we should ideally get this from
* blender to handle deforming objects */
if (!motion) {
- if (geom->need_attribute(scene, ATTR_STD_GENERATED)) {
+ if (hair->need_attribute(scene, ATTR_STD_GENERATED)) {
float3 loc, size;
mesh_texture_space(b_mesh, loc, size);
- if (mesh) {
- Attribute *attr_generated = mesh->attributes.add(ATTR_STD_GENERATED);
- float3 *generated = attr_generated->data_float3();
-
- for (size_t i = 0; i < mesh->verts.size(); i++)
- generated[i] = mesh->verts[i] * size - loc;
- }
- else {
- Attribute *attr_generated = hair->attributes.add(ATTR_STD_GENERATED);
- float3 *generated = attr_generated->data_float3();
+ Attribute *attr_generated = hair->attributes.add(ATTR_STD_GENERATED);
+ float3 *generated = attr_generated->data_float3();
- for (size_t i = 0; i < hair->num_curves(); i++) {
- float3 co = hair->curve_keys[hair->get_curve(i).first_key];
- generated[i] = co * size - loc;
- }
+ for (size_t i = 0; i < hair->num_curves(); i++) {
+ float3 co = hair->curve_keys[hair->get_curve(i).first_key];
+ generated[i] = co * size - loc;
}
}
}
@@ -1076,32 +572,22 @@ void BlenderSync::sync_particle_hair(
int vcol_num = 0;
for (b_mesh.vertex_colors.begin(l); l != b_mesh.vertex_colors.end(); ++l, vcol_num++) {
- if (!geom->need_attribute(scene, ustring(l->name().c_str())))
+ if (!hair->need_attribute(scene, ustring(l->name().c_str())))
continue;
- ObtainCacheParticleVcol(geom, &b_mesh, &b_ob, &CData, !preview, vcol_num);
+ ObtainCacheParticleVcol(hair, &b_mesh, &b_ob, &CData, !preview, vcol_num);
- if (mesh) {
- Attribute *attr_vcol = mesh->attributes.add(
- ustring(l->name().c_str()), TypeDesc::TypeColor, ATTR_ELEMENT_CORNER_BYTE);
+ Attribute *attr_vcol = hair->attributes.add(
+ ustring(l->name().c_str()), TypeRGBA, ATTR_ELEMENT_CURVE);
- uchar4 *cdata = attr_vcol->data_uchar4();
+ float4 *fdata = attr_vcol->data_float4();
- ExportCurveTriangleVcol(&CData, used_res, cdata);
- }
- else {
- Attribute *attr_vcol = hair->attributes.add(
- ustring(l->name().c_str()), TypeDesc::TypeColor, ATTR_ELEMENT_CURVE);
-
- float3 *fdata = attr_vcol->data_float3();
+ if (fdata) {
+ size_t i = 0;
- if (fdata) {
- size_t i = 0;
-
- /* Encode vertex color using the sRGB curve. */
- for (size_t curve = 0; curve < CData.curve_vcol.size(); curve++) {
- fdata[i++] = color_srgb_to_linear_v3(CData.curve_vcol[curve]);
- }
+ /* Encode vertex color using the sRGB curve. */
+ for (size_t curve = 0; curve < CData.curve_vcol.size(); curve++) {
+ fdata[i++] = color_srgb_to_linear_v4(CData.curve_vcol[curve]);
}
}
}
@@ -1118,35 +604,23 @@ void BlenderSync::sync_particle_hair(
ustring name = ustring(l->name().c_str());
/* UV map */
- if (geom->need_attribute(scene, name) || geom->need_attribute(scene, std)) {
+ if (hair->need_attribute(scene, name) || hair->need_attribute(scene, std)) {
Attribute *attr_uv;
- ObtainCacheParticleUV(geom, &b_mesh, &b_ob, &CData, !preview, uv_num);
+ ObtainCacheParticleUV(hair, &b_mesh, &b_ob, &CData, !preview, uv_num);
- if (mesh) {
- if (active_render)
- attr_uv = mesh->attributes.add(std, name);
- else
- attr_uv = mesh->attributes.add(name, TypeFloat2, ATTR_ELEMENT_CORNER);
-
- float2 *uv = attr_uv->data_float2();
-
- ExportCurveTriangleUV(&CData, used_res, uv);
- }
- else {
- if (active_render)
- attr_uv = hair->attributes.add(std, name);
- else
- attr_uv = hair->attributes.add(name, TypeFloat2, ATTR_ELEMENT_CURVE);
+ if (active_render)
+ attr_uv = hair->attributes.add(std, name);
+ else
+ attr_uv = hair->attributes.add(name, TypeFloat2, ATTR_ELEMENT_CURVE);
- float2 *uv = attr_uv->data_float2();
+ float2 *uv = attr_uv->data_float2();
- if (uv) {
- size_t i = 0;
+ if (uv) {
+ size_t i = 0;
- for (size_t curve = 0; curve < CData.curve_uv.size(); curve++) {
- uv[i++] = CData.curve_uv[curve];
- }
+ for (size_t curve = 0; curve < CData.curve_uv.size(); curve++) {
+ uv[i++] = CData.curve_uv[curve];
}
}
}
@@ -1154,7 +628,6 @@ void BlenderSync::sync_particle_hair(
}
}
-#ifdef WITH_NEW_OBJECT_TYPES
static float4 hair_point_as_float4(BL::HairPoint b_point)
{
float4 mP = float3_to_float4(get_float3(b_point.co()));
@@ -1320,12 +793,10 @@ static void export_hair_curves_motion(Hair *hair, BL::Hair b_hair, int motion_st
export_hair_motion_validate_attribute(hair, motion_step, num_motion_keys, have_motion);
}
}
-#endif /* WITH_NEW_OBJECT_TYPES */
/* Hair object. */
void BlenderSync::sync_hair(Hair *hair, BL::Object &b_ob, bool motion, int motion_step)
{
-#ifdef WITH_NEW_OBJECT_TYPES
/* Convert Blender hair to Cycles curves. */
BL::Hair b_hair(b_ob.data());
if (motion) {
@@ -1334,97 +805,70 @@ void BlenderSync::sync_hair(Hair *hair, BL::Object &b_ob, bool motion, int motio
else {
export_hair_curves(scene, hair, b_hair);
}
-#else
- (void)hair;
- (void)b_ob;
- (void)motion;
- (void)motion_step;
-#endif /* WITH_NEW_OBJECT_TYPES */
}
void BlenderSync::sync_hair(BL::Depsgraph b_depsgraph,
BL::Object b_ob,
- Geometry *geom,
+ Hair *hair,
const vector<Shader *> &used_shaders)
{
- Hair *hair = (geom->type == Geometry::HAIR) ? static_cast<Hair *>(geom) : NULL;
- Mesh *mesh = (geom->type == Geometry::MESH) ? static_cast<Mesh *>(geom) : NULL;
-
/* Compares curve_keys rather than strands in order to handle quick hair
* adjustments in dynamic BVH - other methods could probably do this better. */
array<float3> oldcurve_keys;
array<float> oldcurve_radius;
- array<int> oldtriangles;
- if (hair) {
- oldcurve_keys.steal_data(hair->curve_keys);
- oldcurve_radius.steal_data(hair->curve_radius);
- }
- else {
- oldtriangles.steal_data(mesh->triangles);
- }
+ oldcurve_keys.steal_data(hair->curve_keys);
+ oldcurve_radius.steal_data(hair->curve_radius);
- geom->clear();
- geom->used_shaders = used_shaders;
+ hair->clear();
+ hair->used_shaders = used_shaders;
- if (view_layer.use_hair && scene->curve_system_manager->use_curves) {
-#ifdef WITH_NEW_OBJECT_TYPES
+ if (view_layer.use_hair) {
if (b_ob.type() == BL::Object::type_HAIR) {
/* Hair object. */
sync_hair(hair, b_ob, false);
- assert(mesh == NULL);
}
- else
-#endif
- {
+ else {
/* Particle hair. */
- bool need_undeformed = geom->need_attribute(scene, ATTR_STD_GENERATED);
+ bool need_undeformed = hair->need_attribute(scene, ATTR_STD_GENERATED);
BL::Mesh b_mesh = object_to_mesh(
b_data, b_ob, b_depsgraph, need_undeformed, Mesh::SUBDIVISION_NONE);
if (b_mesh) {
- sync_particle_hair(geom, b_mesh, b_ob, false);
+ sync_particle_hair(hair, b_mesh, b_ob, false);
free_object_to_mesh(b_data, b_ob, b_mesh);
}
}
}
/* tag update */
- const bool rebuild = (hair && ((oldcurve_keys != hair->curve_keys) ||
- (oldcurve_radius != hair->curve_radius))) ||
- (mesh && (oldtriangles != mesh->triangles));
+ const bool rebuild = ((oldcurve_keys != hair->curve_keys) ||
+ (oldcurve_radius != hair->curve_radius));
- geom->tag_update(scene, rebuild);
+ hair->tag_update(scene, rebuild);
}
void BlenderSync::sync_hair_motion(BL::Depsgraph b_depsgraph,
BL::Object b_ob,
- Geometry *geom,
+ Hair *hair,
int motion_step)
{
- Hair *hair = (geom->type == Geometry::HAIR) ? static_cast<Hair *>(geom) : NULL;
- Mesh *mesh = (geom->type == Geometry::MESH) ? static_cast<Mesh *>(geom) : NULL;
-
/* Skip if nothing exported. */
- if ((hair && hair->num_keys() == 0) || (mesh && mesh->verts.size() == 0)) {
+ if (hair->num_keys() == 0) {
return;
}
/* Export deformed coordinates. */
if (ccl::BKE_object_is_deform_modified(b_ob, b_scene, preview)) {
-#ifdef WITH_NEW_OBJECT_TYPES
if (b_ob.type() == BL::Object::type_HAIR) {
/* Hair object. */
sync_hair(hair, b_ob, true, motion_step);
- assert(mesh == NULL);
return;
}
- else
-#endif
- {
+ else {
/* Particle hair. */
BL::Mesh b_mesh = object_to_mesh(b_data, b_ob, b_depsgraph, false, Mesh::SUBDIVISION_NONE);
if (b_mesh) {
- sync_particle_hair(geom, b_mesh, b_ob, true, motion_step);
+ sync_particle_hair(hair, b_mesh, b_ob, true, motion_step);
free_object_to_mesh(b_data, b_ob, b_mesh);
return;
}
@@ -1432,12 +876,7 @@ void BlenderSync::sync_hair_motion(BL::Depsgraph b_depsgraph,
}
/* No deformation on this frame, copy coordinates if other frames did have it. */
- if (hair) {
- hair->copy_center_to_motion_step(motion_step);
- }
- else {
- mesh->copy_center_to_motion_step(motion_step);
- }
+ hair->copy_center_to_motion_step(motion_step);
}
CCL_NAMESPACE_END
diff --git a/intern/cycles/blender/blender_device.cpp b/intern/cycles/blender/blender_device.cpp
index ac52948806c..fb9ab9e8c97 100644
--- a/intern/cycles/blender/blender_device.cpp
+++ b/intern/cycles/blender/blender_device.cpp
@@ -21,13 +21,6 @@
CCL_NAMESPACE_BEGIN
-enum DenoiserType {
- DENOISER_NONE = 0,
- DENOISER_OPTIX = 1,
-
- DENOISER_NUM
-};
-
enum ComputeDevice {
COMPUTE_DEVICE_CPU = 0,
COMPUTE_DEVICE_CUDA = 1,
@@ -120,49 +113,6 @@ DeviceInfo blender_device_info(BL::Preferences &b_preferences, BL::Scene &b_scen
}
}
- /* Ensure there is an OptiX device when using the OptiX denoiser. */
- bool use_optix_denoising = get_enum(cscene, "preview_denoising", DENOISER_NUM, DENOISER_NONE) ==
- DENOISER_OPTIX &&
- !background;
- BL::Scene::view_layers_iterator b_view_layer;
- for (b_scene.view_layers.begin(b_view_layer); b_view_layer != b_scene.view_layers.end();
- ++b_view_layer) {
- PointerRNA crl = RNA_pointer_get(&b_view_layer->ptr, "cycles");
- if (get_boolean(crl, "use_optix_denoising")) {
- use_optix_denoising = true;
- }
- }
-
- if (use_optix_denoising && device.type != DEVICE_OPTIX) {
- vector<DeviceInfo> optix_devices = Device::available_devices(DEVICE_MASK_OPTIX);
- if (!optix_devices.empty()) {
- /* Convert to a special multi device with separate denoising devices. */
- if (device.multi_devices.empty()) {
- device.multi_devices.push_back(device);
- }
-
- /* Try to use the same physical devices for denoising. */
- for (const DeviceInfo &cuda_device : device.multi_devices) {
- if (cuda_device.type == DEVICE_CUDA) {
- for (const DeviceInfo &optix_device : optix_devices) {
- if (cuda_device.num == optix_device.num) {
- device.id += optix_device.id;
- device.denoising_devices.push_back(optix_device);
- break;
- }
- }
- }
- }
-
- if (device.denoising_devices.empty()) {
- /* Simply use the first available OptiX device. */
- const DeviceInfo optix_device = optix_devices.front();
- device.id += optix_device.id; /* Uniquely identify this special multi device. */
- device.denoising_devices.push_back(optix_device);
- }
- }
- }
-
return device;
}
diff --git a/intern/cycles/blender/blender_geometry.cpp b/intern/cycles/blender/blender_geometry.cpp
index 7ca35cff961..f7e4623024d 100644
--- a/intern/cycles/blender/blender_geometry.cpp
+++ b/intern/cycles/blender/blender_geometry.cpp
@@ -40,17 +40,9 @@ Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph,
BL::Material material_override = view_layer.material_override;
Shader *default_shader = (b_ob.type() == BL::Object::type_VOLUME) ? scene->default_volume :
scene->default_surface;
-#ifdef WITH_NEW_OBJECT_TYPES
- Geometry::Type geom_type = ((b_ob.type() == BL::Object::type_HAIR || use_particle_hair) &&
- (scene->curve_system_manager->primitive != CURVE_TRIANGLES)) ?
+ Geometry::Type geom_type = (b_ob.type() == BL::Object::type_HAIR || use_particle_hair) ?
Geometry::HAIR :
Geometry::MESH;
-#else
- Geometry::Type geom_type = ((use_particle_hair) &&
- (scene->curve_system_manager->primitive != CURVE_TRIANGLES)) ?
- Geometry::HAIR :
- Geometry::MESH;
-#endif
/* Find shader indices. */
vector<Shader *> used_shaders;
@@ -129,12 +121,9 @@ Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph,
geom->name = ustring(b_ob_data.name().c_str());
-#ifdef WITH_NEW_OBJECT_TYPES
if (b_ob.type() == BL::Object::type_HAIR || use_particle_hair) {
-#else
- if (use_particle_hair) {
-#endif
- sync_hair(b_depsgraph, b_ob, geom, used_shaders);
+ Hair *hair = static_cast<Hair *>(geom);
+ sync_hair(b_depsgraph, b_ob, hair, used_shaders);
}
else if (b_ob.type() == BL::Object::type_VOLUME || object_fluid_gas_domain_find(b_ob)) {
Mesh *mesh = static_cast<Mesh *>(geom);
@@ -173,12 +162,9 @@ void BlenderSync::sync_geometry_motion(BL::Depsgraph &b_depsgraph,
return;
}
-#ifdef WITH_NEW_OBJECT_TYPES
if (b_ob.type() == BL::Object::type_HAIR || use_particle_hair) {
-#else
- if (use_particle_hair) {
-#endif
- sync_hair_motion(b_depsgraph, b_ob, geom, motion_step);
+ Hair *hair = static_cast<Hair *>(geom);
+ sync_hair_motion(b_depsgraph, b_ob, hair, motion_step);
}
else if (b_ob.type() == BL::Object::type_VOLUME || object_fluid_gas_domain_find(b_ob)) {
/* No volume motion blur support yet. */
diff --git a/intern/cycles/blender/blender_mesh.cpp b/intern/cycles/blender/blender_mesh.cpp
index a6f380a9ae7..49407799fcd 100644
--- a/intern/cycles/blender/blender_mesh.cpp
+++ b/intern/cycles/blender/blender_mesh.cpp
@@ -278,25 +278,59 @@ static void mikk_compute_tangents(
genTangSpaceDefault(&context);
}
+/* Create sculpt vertex color attributes. */
+static void attr_create_sculpt_vertex_color(Scene *scene,
+ Mesh *mesh,
+ BL::Mesh &b_mesh,
+ bool subdivision)
+{
+ BL::Mesh::sculpt_vertex_colors_iterator l;
+
+ for (b_mesh.sculpt_vertex_colors.begin(l); l != b_mesh.sculpt_vertex_colors.end(); ++l) {
+ const bool active_render = l->active_render();
+ AttributeStandard vcol_std = (active_render) ? ATTR_STD_VERTEX_COLOR : ATTR_STD_NONE;
+ ustring vcol_name = ustring(l->name().c_str());
+
+ const bool need_vcol = mesh->need_attribute(scene, vcol_name) ||
+ mesh->need_attribute(scene, vcol_std);
+
+ if (!need_vcol) {
+ continue;
+ }
+
+ AttributeSet &attributes = (subdivision) ? mesh->subd_attributes : mesh->attributes;
+ Attribute *vcol_attr = attributes.add(vcol_name, TypeRGBA, ATTR_ELEMENT_VERTEX);
+ vcol_attr->std = vcol_std;
+
+ float4 *cdata = vcol_attr->data_float4();
+ int numverts = b_mesh.vertices.length();
+
+ for (int i = 0; i < numverts; i++) {
+ *(cdata++) = get_float4(l->data[i].color());
+ }
+ }
+}
+
/* Create vertex color attributes. */
static void attr_create_vertex_color(Scene *scene, Mesh *mesh, BL::Mesh &b_mesh, bool subdivision)
{
- if (subdivision) {
- BL::Mesh::vertex_colors_iterator l;
+ BL::Mesh::vertex_colors_iterator l;
- for (b_mesh.vertex_colors.begin(l); l != b_mesh.vertex_colors.end(); ++l) {
- const bool active_render = l->active_render();
- AttributeStandard vcol_std = (active_render) ? ATTR_STD_VERTEX_COLOR : ATTR_STD_NONE;
- ustring vcol_name = ustring(l->name().c_str());
+ for (b_mesh.vertex_colors.begin(l); l != b_mesh.vertex_colors.end(); ++l) {
+ const bool active_render = l->active_render();
+ AttributeStandard vcol_std = (active_render) ? ATTR_STD_VERTEX_COLOR : ATTR_STD_NONE;
+ ustring vcol_name = ustring(l->name().c_str());
- const bool need_vcol = mesh->need_attribute(scene, vcol_name) ||
- mesh->need_attribute(scene, vcol_std);
+ const bool need_vcol = mesh->need_attribute(scene, vcol_name) ||
+ mesh->need_attribute(scene, vcol_std);
- if (!need_vcol) {
- continue;
- }
+ if (!need_vcol) {
+ continue;
+ }
- Attribute *vcol_attr = NULL;
+ Attribute *vcol_attr = NULL;
+
+ if (subdivision) {
if (active_render) {
vcol_attr = mesh->subd_attributes.add(vcol_std, vcol_name);
}
@@ -316,22 +350,7 @@ static void attr_create_vertex_color(Scene *scene, Mesh *mesh, BL::Mesh &b_mesh,
}
}
}
- }
- else {
- BL::Mesh::vertex_colors_iterator l;
- for (b_mesh.vertex_colors.begin(l); l != b_mesh.vertex_colors.end(); ++l) {
- const bool active_render = l->active_render();
- AttributeStandard vcol_std = (active_render) ? ATTR_STD_VERTEX_COLOR : ATTR_STD_NONE;
- ustring vcol_name = ustring(l->name().c_str());
-
- const bool need_vcol = mesh->need_attribute(scene, vcol_name) ||
- mesh->need_attribute(scene, vcol_std);
-
- if (!need_vcol) {
- continue;
- }
-
- Attribute *vcol_attr = NULL;
+ else {
if (active_render) {
vcol_attr = mesh->attributes.add(vcol_std, vcol_name);
}
@@ -828,6 +847,7 @@ static void create_mesh(Scene *scene,
*/
attr_create_pointiness(scene, mesh, b_mesh, subdivision);
attr_create_vertex_color(scene, mesh, b_mesh, subdivision);
+ attr_create_sculpt_vertex_color(scene, mesh, b_mesh, subdivision);
attr_create_random_per_island(scene, mesh, b_mesh, subdivision);
if (subdivision) {
diff --git a/intern/cycles/blender/blender_object.cpp b/intern/cycles/blender/blender_object.cpp
index c28586d0f63..d3a37563ef4 100644
--- a/intern/cycles/blender/blender_object.cpp
+++ b/intern/cycles/blender/blender_object.cpp
@@ -69,11 +69,7 @@ bool BlenderSync::object_is_mesh(BL::Object &b_ob)
BL::Object::type_enum type = b_ob.type();
-#ifdef WITH_NEW_OBJECT_TYPES
if (type == BL::Object::type_VOLUME || type == BL::Object::type_HAIR) {
-#else
- if (type == BL::Object::type_VOLUME) {
-#endif
/* Will be exported attached to mesh. */
return true;
}
diff --git a/intern/cycles/blender/blender_python.cpp b/intern/cycles/blender/blender_python.cpp
index 0be19dbffd1..3e595c3ee52 100644
--- a/intern/cycles/blender/blender_python.cpp
+++ b/intern/cycles/blender/blender_python.cpp
@@ -31,8 +31,10 @@
#include "util/util_logging.h"
#include "util/util_md5.h"
#include "util/util_opengl.h"
+#include "util/util_openimagedenoise.h"
#include "util/util_path.h"
#include "util/util_string.h"
+#include "util/util_task.h"
#include "util/util_types.h"
#ifdef WITH_OSL
@@ -1075,5 +1077,14 @@ void *CCL_python_module_init()
Py_INCREF(Py_False);
#endif /* WITH_EMBREE */
+ if (ccl::openimagedenoise_supported()) {
+ PyModule_AddObject(mod, "with_openimagedenoise", Py_True);
+ Py_INCREF(Py_True);
+ }
+ else {
+ PyModule_AddObject(mod, "with_openimagedenoise", Py_False);
+ Py_INCREF(Py_False);
+ }
+
return (void *)mod;
}
diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp
index dbe87ce2b13..391a1b8f473 100644
--- a/intern/cycles/blender/blender_session.cpp
+++ b/intern/cycles/blender/blender_session.cpp
@@ -158,7 +158,7 @@ void BlenderSession::create_session()
/* set buffer parameters */
BufferParams buffer_params = BlenderSync::get_buffer_params(
- b_scene, b_render, b_v3d, b_rv3d, scene->camera, width, height);
+ b_render, b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use);
session->reset(buffer_params, session_params.samples);
b_engine.use_highlight_tiles(session_params.progressive_refine == false);
@@ -239,8 +239,13 @@ void BlenderSession::reset_session(BL::BlendData &b_data, BL::Depsgraph &b_depsg
BL::SpaceView3D b_null_space_view3d(PointerRNA_NULL);
BL::RegionView3D b_null_region_view3d(PointerRNA_NULL);
- BufferParams buffer_params = BlenderSync::get_buffer_params(
- b_scene, b_render, b_null_space_view3d, b_null_region_view3d, scene->camera, width, height);
+ BufferParams buffer_params = BlenderSync::get_buffer_params(b_render,
+ b_null_space_view3d,
+ b_null_region_view3d,
+ scene->camera,
+ width,
+ height,
+ session_params.denoising.use);
session->reset(buffer_params, session_params.samples);
b_engine.use_highlight_tiles(session_params.progressive_refine == false);
@@ -468,14 +473,13 @@ void BlenderSession::render(BL::Depsgraph &b_depsgraph_)
session->update_render_tile_cb = function_bind(
&BlenderSession::update_render_tile, this, _1, _2);
+ BL::ViewLayer b_view_layer = b_depsgraph.view_layer_eval();
+
/* get buffer parameters */
SessionParams session_params = BlenderSync::get_session_params(
- b_engine, b_userpref, b_scene, background);
+ b_engine, b_userpref, b_scene, background, b_view_layer);
BufferParams buffer_params = BlenderSync::get_buffer_params(
- b_scene, b_render, b_v3d, b_rv3d, scene->camera, width, height);
-
- /* render each layer */
- BL::ViewLayer b_view_layer = b_depsgraph.view_layer_eval();
+ b_render, b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use);
/* temporary render result to find needed passes and views */
BL::RenderResult b_rr = begin_render_result(
@@ -485,35 +489,26 @@ void BlenderSession::render(BL::Depsgraph &b_depsgraph_)
BL::RenderLayer b_rlay = *b_single_rlay;
b_rlay_name = b_view_layer.name();
- /* add passes */
- vector<Pass> passes = sync->sync_render_passes(
- b_rlay, b_view_layer, session_params.adaptive_sampling);
- buffer_params.passes = passes;
+ /* Update denoising parameters. */
+ session->set_denoising(session_params.denoising);
- PointerRNA crl = RNA_pointer_get(&b_view_layer.ptr, "cycles");
- bool use_denoising = get_boolean(crl, "use_denoising");
- bool use_optix_denoising = get_boolean(crl, "use_optix_denoising");
- bool write_denoising_passes = get_boolean(crl, "denoising_store_passes");
+ bool use_denoising = session_params.denoising.use;
+ bool store_denoising_passes = session_params.denoising.store_passes;
- buffer_params.denoising_data_pass = use_denoising || write_denoising_passes;
+ buffer_params.denoising_data_pass = use_denoising || store_denoising_passes;
buffer_params.denoising_clean_pass = (scene->film->denoising_flags & DENOISING_CLEAN_ALL_PASSES);
- buffer_params.denoising_prefiltered_pass = write_denoising_passes && !use_optix_denoising;
-
- session->params.run_denoising = use_denoising || write_denoising_passes;
- session->params.full_denoising = use_denoising && !use_optix_denoising;
- session->params.optix_denoising = use_denoising && use_optix_denoising;
- session->params.write_denoising_passes = write_denoising_passes && !use_optix_denoising;
- session->params.denoising.radius = get_int(crl, "denoising_radius");
- session->params.denoising.strength = get_float(crl, "denoising_strength");
- session->params.denoising.feature_strength = get_float(crl, "denoising_feature_strength");
- session->params.denoising.relative_pca = get_boolean(crl, "denoising_relative_pca");
- session->params.denoising.optix_input_passes = get_enum(crl, "denoising_optix_input_passes");
- session->tile_manager.schedule_denoising = session->params.run_denoising;
+ buffer_params.denoising_prefiltered_pass = store_denoising_passes &&
+ session_params.denoising.type == DENOISER_NLM;
scene->film->denoising_data_pass = buffer_params.denoising_data_pass;
scene->film->denoising_clean_pass = buffer_params.denoising_clean_pass;
scene->film->denoising_prefiltered_pass = buffer_params.denoising_prefiltered_pass;
+ /* Add passes */
+ vector<Pass> passes = sync->sync_render_passes(
+ b_rlay, b_view_layer, session_params.adaptive_sampling, session_params.denoising);
+ buffer_params.passes = passes;
+
scene->film->pass_alpha_threshold = b_view_layer.pass_alpha_threshold();
scene->film->tag_passes_update(scene, passes);
scene->film->tag_update(scene);
@@ -798,7 +793,7 @@ void BlenderSession::synchronize(BL::Depsgraph &b_depsgraph_)
/* increase samples, but never decrease */
session->set_samples(session_params.samples);
- session->set_denoising_start_sample(session_params.denoising_start_sample);
+ session->set_denoising_start_sample(session_params.denoising.start_sample);
session->set_pause(session_pause);
/* copy recalc flags, outside of mutex so we can decide to do the real
@@ -831,21 +826,17 @@ void BlenderSession::synchronize(BL::Depsgraph &b_depsgraph_)
/* get buffer parameters */
BufferParams buffer_params = BlenderSync::get_buffer_params(
- b_scene, b_render, b_v3d, b_rv3d, scene->camera, width, height);
+ b_render, b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use);
- if (session_params.device.type != DEVICE_OPTIX &&
- session_params.device.denoising_devices.empty()) {
- /* cannot use OptiX denoising when it is not supported by the device. */
- buffer_params.denoising_data_pass = false;
- }
- else {
- session->set_denoising(buffer_params.denoising_data_pass, true);
+ if (!buffer_params.denoising_data_pass) {
+ session_params.denoising.use = false;
}
+ session->set_denoising(session_params.denoising);
+
+ /* Update film if denoising data was enabled or disabled. */
if (scene->film->denoising_data_pass != buffer_params.denoising_data_pass) {
scene->film->denoising_data_pass = buffer_params.denoising_data_pass;
-
- /* Force a scene and session reset below. */
scene->film->tag_update(scene);
}
@@ -917,7 +908,7 @@ bool BlenderSession::draw(int w, int h)
SessionParams session_params = BlenderSync::get_session_params(
b_engine, b_userpref, b_scene, background);
BufferParams buffer_params = BlenderSync::get_buffer_params(
- b_scene, b_render, b_v3d, b_rv3d, scene->camera, width, height);
+ b_render, b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use);
bool session_pause = BlenderSync::get_session_pause(b_scene, background);
if (session_pause == false) {
@@ -935,7 +926,7 @@ bool BlenderSession::draw(int w, int h)
/* draw */
BufferParams buffer_params = BlenderSync::get_buffer_params(
- b_scene, b_render, b_v3d, b_rv3d, scene->camera, width, height);
+ b_render, b_v3d, b_rv3d, scene->camera, width, height, session->params.denoising.use);
DeviceDrawParams draw_params;
if (session->params.display_buffer_linear) {
diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp
index f207d8ae07f..19d2730dc93 100644
--- a/intern/cycles/blender/blender_shader.cpp
+++ b/intern/cycles/blender/blender_shader.cpp
@@ -813,6 +813,14 @@ static ShaderNode *add_node(Scene *scene,
sky->sun_direction = normalize(get_float3(b_sky_node.sun_direction()));
sky->turbidity = b_sky_node.turbidity();
sky->ground_albedo = b_sky_node.ground_albedo();
+ sky->sun_disc = b_sky_node.sun_disc();
+ sky->sun_size = b_sky_node.sun_size();
+ sky->sun_elevation = b_sky_node.sun_elevation();
+ sky->sun_rotation = b_sky_node.sun_rotation();
+ sky->altitude = b_sky_node.altitude();
+ sky->air_density = b_sky_node.air_density();
+ sky->dust_density = b_sky_node.dust_density();
+ sky->ozone_density = b_sky_node.ozone_density();
BL::TexMapping b_texture_mapping(b_sky_node.texture_mapping());
get_tex_mapping(&sky->tex_mapping, b_texture_mapping);
node = sky;
diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp
index 09813dc8c05..bf065cc5492 100644
--- a/intern/cycles/blender/blender_sync.cpp
+++ b/intern/cycles/blender/blender_sync.cpp
@@ -38,6 +38,7 @@
#include "util/util_foreach.h"
#include "util/util_hash.h"
#include "util/util_opengl.h"
+#include "util/util_openimagedenoise.h"
CCL_NAMESPACE_BEGIN
@@ -212,7 +213,6 @@ void BlenderSync::sync_data(BL::RenderSettings &b_render,
sync_film(b_v3d);
sync_shaders(b_depsgraph, b_v3d);
sync_images();
- sync_curve_settings(b_depsgraph);
geometry_synced.clear(); /* use for objects and motion sync */
@@ -538,7 +538,8 @@ int BlenderSync::get_denoising_pass(BL::RenderPass &b_pass)
vector<Pass> BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay,
BL::ViewLayer &b_view_layer,
- bool adaptive_sampling)
+ bool adaptive_sampling,
+ const DenoiseParams &denoising)
{
vector<Pass> passes;
@@ -555,16 +556,13 @@ vector<Pass> BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay,
Pass::add(pass_type, passes, b_pass.name().c_str());
}
- PointerRNA crp = RNA_pointer_get(&b_view_layer.ptr, "cycles");
- bool use_denoising = get_boolean(crp, "use_denoising");
- bool use_optix_denoising = get_boolean(crp, "use_optix_denoising");
- bool write_denoising_passes = get_boolean(crp, "denoising_store_passes");
+ PointerRNA crl = RNA_pointer_get(&b_view_layer.ptr, "cycles");
scene->film->denoising_flags = 0;
- if (use_denoising || write_denoising_passes) {
- if (!use_optix_denoising) {
+ if (denoising.use || denoising.store_passes) {
+ if (denoising.type == DENOISER_NLM) {
#define MAP_OPTION(name, flag) \
- if (!get_boolean(crp, name)) \
+ if (!get_boolean(crl, name)) \
scene->film->denoising_flags |= flag;
MAP_OPTION("denoising_diffuse_direct", DENOISING_CLEAN_DIFFUSE_DIR);
MAP_OPTION("denoising_diffuse_indirect", DENOISING_CLEAN_DIFFUSE_IND);
@@ -577,11 +575,11 @@ vector<Pass> BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay,
b_engine.add_pass("Noisy Image", 4, "RGBA", b_view_layer.name().c_str());
}
- if (write_denoising_passes) {
+ if (denoising.store_passes) {
b_engine.add_pass("Denoising Normal", 3, "XYZ", b_view_layer.name().c_str());
b_engine.add_pass("Denoising Albedo", 3, "RGB", b_view_layer.name().c_str());
b_engine.add_pass("Denoising Depth", 1, "Z", b_view_layer.name().c_str());
- if (!use_optix_denoising) {
+ if (denoising.type == DENOISER_NLM) {
b_engine.add_pass("Denoising Shadowing", 1, "X", b_view_layer.name().c_str());
b_engine.add_pass("Denoising Variance", 3, "RGB", b_view_layer.name().c_str());
b_engine.add_pass("Denoising Intensity", 1, "X", b_view_layer.name().c_str());
@@ -593,46 +591,46 @@ vector<Pass> BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay,
}
#ifdef __KERNEL_DEBUG__
- if (get_boolean(crp, "pass_debug_bvh_traversed_nodes")) {
+ if (get_boolean(crl, "pass_debug_bvh_traversed_nodes")) {
b_engine.add_pass("Debug BVH Traversed Nodes", 1, "X", b_view_layer.name().c_str());
Pass::add(PASS_BVH_TRAVERSED_NODES, passes, "Debug BVH Traversed Nodes");
}
- if (get_boolean(crp, "pass_debug_bvh_traversed_instances")) {
+ if (get_boolean(crl, "pass_debug_bvh_traversed_instances")) {
b_engine.add_pass("Debug BVH Traversed Instances", 1, "X", b_view_layer.name().c_str());
Pass::add(PASS_BVH_TRAVERSED_INSTANCES, passes, "Debug BVH Traversed Instances");
}
- if (get_boolean(crp, "pass_debug_bvh_intersections")) {
+ if (get_boolean(crl, "pass_debug_bvh_intersections")) {
b_engine.add_pass("Debug BVH Intersections", 1, "X", b_view_layer.name().c_str());
Pass::add(PASS_BVH_INTERSECTIONS, passes, "Debug BVH Intersections");
}
- if (get_boolean(crp, "pass_debug_ray_bounces")) {
+ if (get_boolean(crl, "pass_debug_ray_bounces")) {
b_engine.add_pass("Debug Ray Bounces", 1, "X", b_view_layer.name().c_str());
Pass::add(PASS_RAY_BOUNCES, passes, "Debug Ray Bounces");
}
#endif
- if (get_boolean(crp, "pass_debug_render_time")) {
+ if (get_boolean(crl, "pass_debug_render_time")) {
b_engine.add_pass("Debug Render Time", 1, "X", b_view_layer.name().c_str());
Pass::add(PASS_RENDER_TIME, passes, "Debug Render Time");
}
- if (get_boolean(crp, "pass_debug_sample_count")) {
+ if (get_boolean(crl, "pass_debug_sample_count")) {
b_engine.add_pass("Debug Sample Count", 1, "X", b_view_layer.name().c_str());
Pass::add(PASS_SAMPLE_COUNT, passes, "Debug Sample Count");
}
- if (get_boolean(crp, "use_pass_volume_direct")) {
+ if (get_boolean(crl, "use_pass_volume_direct")) {
b_engine.add_pass("VolumeDir", 3, "RGB", b_view_layer.name().c_str());
Pass::add(PASS_VOLUME_DIRECT, passes, "VolumeDir");
}
- if (get_boolean(crp, "use_pass_volume_indirect")) {
+ if (get_boolean(crl, "use_pass_volume_indirect")) {
b_engine.add_pass("VolumeInd", 3, "RGB", b_view_layer.name().c_str());
Pass::add(PASS_VOLUME_INDIRECT, passes, "VolumeInd");
}
/* Cryptomatte stores two ID/weight pairs per RGBA layer.
* User facing parameter is the number of pairs. */
- int crypto_depth = divide_up(min(16, get_int(crp, "pass_crypto_depth")), 2);
+ int crypto_depth = divide_up(min(16, get_int(crl, "pass_crypto_depth")), 2);
scene->film->cryptomatte_depth = crypto_depth;
scene->film->cryptomatte_passes = CRYPT_NONE;
- if (get_boolean(crp, "use_pass_crypto_object")) {
+ if (get_boolean(crl, "use_pass_crypto_object")) {
for (int i = 0; i < crypto_depth; i++) {
string passname = cryptomatte_prefix + string_printf("Object%02d", i);
b_engine.add_pass(passname.c_str(), 4, "RGBA", b_view_layer.name().c_str());
@@ -641,7 +639,7 @@ vector<Pass> BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay,
scene->film->cryptomatte_passes = (CryptomatteType)(scene->film->cryptomatte_passes |
CRYPT_OBJECT);
}
- if (get_boolean(crp, "use_pass_crypto_material")) {
+ if (get_boolean(crl, "use_pass_crypto_material")) {
for (int i = 0; i < crypto_depth; i++) {
string passname = cryptomatte_prefix + string_printf("Material%02d", i);
b_engine.add_pass(passname.c_str(), 4, "RGBA", b_view_layer.name().c_str());
@@ -650,7 +648,7 @@ vector<Pass> BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay,
scene->film->cryptomatte_passes = (CryptomatteType)(scene->film->cryptomatte_passes |
CRYPT_MATERIAL);
}
- if (get_boolean(crp, "use_pass_crypto_asset")) {
+ if (get_boolean(crl, "use_pass_crypto_asset")) {
for (int i = 0; i < crypto_depth; i++) {
string passname = cryptomatte_prefix + string_printf("Asset%02d", i);
b_engine.add_pass(passname.c_str(), 4, "RGBA", b_view_layer.name().c_str());
@@ -659,19 +657,19 @@ vector<Pass> BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay,
scene->film->cryptomatte_passes = (CryptomatteType)(scene->film->cryptomatte_passes |
CRYPT_ASSET);
}
- if (get_boolean(crp, "pass_crypto_accurate") && scene->film->cryptomatte_passes != CRYPT_NONE) {
+ if (get_boolean(crl, "pass_crypto_accurate") && scene->film->cryptomatte_passes != CRYPT_NONE) {
scene->film->cryptomatte_passes = (CryptomatteType)(scene->film->cryptomatte_passes |
CRYPT_ACCURATE);
}
if (adaptive_sampling) {
Pass::add(PASS_ADAPTIVE_AUX_BUFFER, passes);
- if (!get_boolean(crp, "pass_debug_sample_count")) {
+ if (!get_boolean(crl, "pass_debug_sample_count")) {
Pass::add(PASS_SAMPLE_COUNT, passes);
}
}
- RNA_BEGIN (&crp, b_aov, "aovs") {
+ RNA_BEGIN (&crl, b_aov, "aovs") {
bool is_color = (get_enum(b_aov, "type") == 1);
string name = get_string(b_aov, "name");
@@ -732,6 +730,11 @@ SceneParams BlenderSync::get_scene_params(BL::Scene &b_scene, bool background)
params.use_bvh_unaligned_nodes = RNA_boolean_get(&cscene, "debug_use_hair_bvh");
params.num_bvh_time_steps = RNA_int_get(&cscene, "debug_bvh_time_steps");
+ PointerRNA csscene = RNA_pointer_get(&b_scene.ptr, "cycles_curves");
+ params.hair_subdivisions = get_int(csscene, "subdivisions");
+ params.hair_shape = (CurveShapeType)get_enum(
+ csscene, "shape", CURVE_NUM_SHAPE_TYPES, CURVE_THICK);
+
if (background && params.shadingsystem != SHADINGSYSTEM_OSL)
params.persistent_data = r.use_persistent_data();
else
@@ -751,20 +754,7 @@ SceneParams BlenderSync::get_scene_params(BL::Scene &b_scene, bool background)
params.texture_limit = 0;
}
- /* TODO(sergey): Once OSL supports per-microarchitecture optimization get
- * rid of this.
- */
- if (params.shadingsystem == SHADINGSYSTEM_OSL) {
- params.bvh_layout = BVH_LAYOUT_BVH4;
- }
- else {
- params.bvh_layout = DebugFlags().cpu.bvh_layout;
- }
-
-#ifdef WITH_EMBREE
- params.bvh_layout = RNA_boolean_get(&cscene, "use_bvh_embree") ? BVH_LAYOUT_EMBREE :
- params.bvh_layout;
-#endif
+ params.bvh_layout = DebugFlags().cpu.bvh_layout;
params.background = background;
@@ -782,7 +772,8 @@ bool BlenderSync::get_session_pause(BL::Scene &b_scene, bool background)
SessionParams BlenderSync::get_session_params(BL::RenderEngine &b_engine,
BL::Preferences &b_preferences,
BL::Scene &b_scene,
- bool background)
+ bool background,
+ BL::ViewLayer b_view_layer)
{
SessionParams params;
PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
@@ -860,9 +851,22 @@ SessionParams BlenderSync::get_session_params(BL::RenderEngine &b_engine,
params.tile_order = TILE_BOTTOM_TO_TOP;
}
- /* other parameters */
+ /* Denoising */
+ params.denoising = get_denoise_params(b_scene, b_view_layer, background);
+
+ if (params.denoising.use) {
+ /* Add additional denoising devices if we are rendering and denoising
+ * with different devices. */
+ params.device.add_denoising_devices(params.denoising.type);
+
+ /* Check if denoiser is supported by device. */
+ if (!(params.device.denoisers & params.denoising.type)) {
+ params.denoising.use = false;
+ }
+ }
+
+ /* Viewport Performance */
params.start_resolution = get_int(cscene, "preview_start_resolution");
- params.denoising_start_sample = get_int(cscene, "preview_denoising_start_sample");
params.pixel_size = b_engine.get_preview_pixel_size(b_scene);
/* other parameters */
@@ -915,4 +919,55 @@ SessionParams BlenderSync::get_session_params(BL::RenderEngine &b_engine,
return params;
}
+DenoiseParams BlenderSync::get_denoise_params(BL::Scene &b_scene,
+ BL::ViewLayer &b_view_layer,
+ bool background)
+{
+ DenoiseParams denoising;
+ PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
+
+ if (background) {
+ /* Final Render Denoising */
+ denoising.use = get_boolean(cscene, "use_denoising");
+ denoising.type = (DenoiserType)get_enum(cscene, "denoiser", DENOISER_NUM, DENOISER_NONE);
+
+ if (b_view_layer) {
+ PointerRNA clayer = RNA_pointer_get(&b_view_layer.ptr, "cycles");
+ if (!get_boolean(clayer, "use_denoising")) {
+ denoising.use = false;
+ }
+
+ denoising.radius = get_int(clayer, "denoising_radius");
+ denoising.strength = get_float(clayer, "denoising_strength");
+ denoising.feature_strength = get_float(clayer, "denoising_feature_strength");
+ denoising.relative_pca = get_boolean(clayer, "denoising_relative_pca");
+ denoising.optix_input_passes = get_enum(clayer, "denoising_optix_input_passes");
+
+ denoising.store_passes = get_boolean(clayer, "denoising_store_passes");
+ }
+ }
+ else {
+ /* Viewport Denoising */
+ denoising.use = get_boolean(cscene, "use_preview_denoising");
+ denoising.type = (DenoiserType)get_enum(
+ cscene, "preview_denoiser", DENOISER_NUM, DENOISER_NONE);
+ denoising.start_sample = get_int(cscene, "preview_denoising_start_sample");
+
+ /* Auto select fastest denoiser. */
+ if (denoising.type == DENOISER_NONE) {
+ if (!Device::available_devices(DEVICE_MASK_OPTIX).empty()) {
+ denoising.type = DENOISER_OPTIX;
+ }
+ else if (openimagedenoise_supported()) {
+ denoising.type = DENOISER_OPENIMAGEDENOISE;
+ }
+ else {
+ denoising.use = false;
+ }
+ }
+ }
+
+ return denoising;
+}
+
CCL_NAMESPACE_END
diff --git a/intern/cycles/blender/blender_sync.h b/intern/cycles/blender/blender_sync.h
index 341281b18ee..0214d9eb3b8 100644
--- a/intern/cycles/blender/blender_sync.h
+++ b/intern/cycles/blender/blender_sync.h
@@ -75,7 +75,8 @@ class BlenderSync {
void sync_view_layer(BL::SpaceView3D &b_v3d, BL::ViewLayer &b_view_layer);
vector<Pass> sync_render_passes(BL::RenderLayer &b_render_layer,
BL::ViewLayer &b_view_layer,
- bool adaptive_sampling);
+ bool adaptive_sampling,
+ const DenoiseParams &denoising);
void sync_integrator();
void sync_camera(BL::RenderSettings &b_render,
BL::Object &b_override,
@@ -94,23 +95,29 @@ class BlenderSync {
/* get parameters */
static SceneParams get_scene_params(BL::Scene &b_scene, bool background);
- static SessionParams get_session_params(BL::RenderEngine &b_engine,
- BL::Preferences &b_userpref,
- BL::Scene &b_scene,
- bool background);
+ static SessionParams get_session_params(
+ BL::RenderEngine &b_engine,
+ BL::Preferences &b_userpref,
+ BL::Scene &b_scene,
+ bool background,
+ BL::ViewLayer b_view_layer = BL::ViewLayer(PointerRNA_NULL));
static bool get_session_pause(BL::Scene &b_scene, bool background);
- static BufferParams get_buffer_params(BL::Scene &b_scene,
- BL::RenderSettings &b_render,
+ static BufferParams get_buffer_params(BL::RenderSettings &b_render,
BL::SpaceView3D &b_v3d,
BL::RegionView3D &b_rv3d,
Camera *cam,
int width,
- int height);
+ int height,
+ const bool use_denoiser);
static PassType get_pass_type(BL::RenderPass &b_pass);
static int get_denoising_pass(BL::RenderPass &b_pass);
private:
+ static DenoiseParams get_denoise_params(BL::Scene &b_scene,
+ BL::ViewLayer &b_view_layer,
+ bool background);
+
/* sync */
void sync_lights(BL::Depsgraph &b_depsgraph, bool update_all);
void sync_materials(BL::Depsgraph &b_depsgraph, bool update_all);
@@ -153,16 +160,12 @@ class BlenderSync {
/* Hair */
void sync_hair(BL::Depsgraph b_depsgraph,
BL::Object b_ob,
- Geometry *geom,
+ Hair *hair,
const vector<Shader *> &used_shaders);
- void sync_hair_motion(BL::Depsgraph b_depsgraph,
- BL::Object b_ob,
- Geometry *geom,
- int motion_step);
+ void sync_hair_motion(BL::Depsgraph b_depsgraph, BL::Object b_ob, Hair *hair, int motion_step);
void sync_hair(Hair *hair, BL::Object &b_ob, bool motion, int motion_step = 0);
void sync_particle_hair(
- Geometry *geom, BL::Mesh &b_mesh, BL::Object &b_ob, bool motion, int motion_step = 0);
- void sync_curve_settings(BL::Depsgraph &b_depsgraph);
+ Hair *hair, BL::Mesh &b_mesh, BL::Object &b_ob, bool motion, int motion_step = 0);
bool object_has_particle_hair(BL::Object b_ob);
/* Camera */
diff --git a/intern/cycles/blender/blender_viewport.cpp b/intern/cycles/blender/blender_viewport.cpp
index 93e84e28032..73ef5f94720 100644
--- a/intern/cycles/blender/blender_viewport.cpp
+++ b/intern/cycles/blender/blender_viewport.cpp
@@ -61,17 +61,6 @@ const bool BlenderViewportParameters::custom_viewport_parameters() const
return !(use_scene_world && use_scene_lights);
}
-bool BlenderViewportParameters::get_viewport_display_denoising(BL::SpaceView3D &b_v3d,
- BL::Scene &b_scene)
-{
- bool use_denoising = false;
- if (b_v3d) {
- PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
- use_denoising = get_enum(cscene, "preview_denoising") != 0;
- }
- return use_denoising;
-}
-
PassType BlenderViewportParameters::get_viewport_display_render_pass(BL::SpaceView3D &b_v3d)
{
PassType display_pass = PASS_NONE;
@@ -83,11 +72,6 @@ PassType BlenderViewportParameters::get_viewport_display_render_pass(BL::SpaceVi
return display_pass;
}
-bool update_viewport_display_denoising(BL::SpaceView3D &b_v3d, BL::Scene &b_scene)
-{
- return BlenderViewportParameters::get_viewport_display_denoising(b_v3d, b_scene);
-}
-
PassType update_viewport_display_passes(BL::SpaceView3D &b_v3d, vector<Pass> &passes)
{
if (b_v3d) {
diff --git a/intern/cycles/blender/blender_viewport.h b/intern/cycles/blender/blender_viewport.h
index 3e44e552f1d..7c6c9c4d274 100644
--- a/intern/cycles/blender/blender_viewport.h
+++ b/intern/cycles/blender/blender_viewport.h
@@ -44,15 +44,11 @@ class BlenderViewportParameters {
friend class BlenderSync;
public:
- /* Get whether to enable denoising data pass in viewport. */
- static bool get_viewport_display_denoising(BL::SpaceView3D &b_v3d, BL::Scene &b_scene);
/* Retrieve the render pass that needs to be displayed on the given `SpaceView3D`
* When the `b_v3d` parameter is not given `PASS_NONE` will be returned. */
static PassType get_viewport_display_render_pass(BL::SpaceView3D &b_v3d);
};
-bool update_viewport_display_denoising(BL::SpaceView3D &b_v3d, BL::Scene &b_scene);
-
PassType update_viewport_display_passes(BL::SpaceView3D &b_v3d, vector<Pass> &passes);
CCL_NAMESPACE_END
diff --git a/intern/cycles/blender/blender_volume.cpp b/intern/cycles/blender/blender_volume.cpp
index 4eed6be8c7c..80591e0eec8 100644
--- a/intern/cycles/blender/blender_volume.cpp
+++ b/intern/cycles/blender/blender_volume.cpp
@@ -35,8 +35,10 @@ CCL_NAMESPACE_BEGIN
class BlenderSmokeLoader : public ImageLoader {
public:
BlenderSmokeLoader(BL::Object &b_ob, AttributeStandard attribute)
- : b_domain(object_fluid_gas_domain_find(b_ob)), b_mesh(b_ob.data()), attribute(attribute)
+ : b_domain(object_fluid_gas_domain_find(b_ob)), attribute(attribute)
{
+ BL::Mesh b_mesh(b_ob.data());
+ mesh_texture_space(b_mesh, texspace_loc, texspace_size);
}
bool load_metadata(ImageMetaData &metadata) override
@@ -77,9 +79,7 @@ class BlenderSmokeLoader : public ImageLoader {
/* Create a matrix to transform from object space to mesh texture space.
* This does not work with deformations but that can probably only be done
* well with a volume grid mapping of coordinates. */
- float3 loc, size;
- mesh_texture_space(b_mesh, loc, size);
- metadata.transform_3d = transform_translate(-loc) * transform_scale(size);
+ metadata.transform_3d = transform_translate(-texspace_loc) * transform_scale(texspace_size);
metadata.use_transform_3d = true;
return true;
@@ -177,7 +177,7 @@ class BlenderSmokeLoader : public ImageLoader {
}
BL::FluidDomainSettings b_domain;
- BL::Mesh b_mesh;
+ float3 texspace_loc, texspace_size;
AttributeStandard attribute;
};
@@ -216,25 +216,16 @@ static void sync_smoke_volume(Scene *scene, BL::Object &b_ob, Mesh *mesh, float
class BlenderVolumeLoader : public VDBImageLoader {
public:
- BlenderVolumeLoader(BL::Volume b_volume, const string &grid_name)
- : VDBImageLoader(grid_name),
- b_volume(b_volume),
- b_volume_grid(PointerRNA_NULL),
- unload(false)
+ BlenderVolumeLoader(BL::BlendData &b_data, BL::Volume &b_volume, const string &grid_name)
+ : VDBImageLoader(grid_name), b_data(b_data), b_volume(b_volume), unload(false)
{
-#ifdef WITH_OPENVDB
- /* Find grid with matching name. */
- BL::Volume::grids_iterator b_grid_iter;
- for (b_volume.grids.begin(b_grid_iter); b_grid_iter != b_volume.grids.end(); ++b_grid_iter) {
- if (b_grid_iter->name() == grid_name) {
- b_volume_grid = *b_grid_iter;
- }
- }
-#endif
}
bool load_metadata(ImageMetaData &metadata) override
{
+ b_volume.grids.load(b_data.ptr.data);
+ BL::VolumeGrid b_volume_grid = find_grid();
+
if (!b_volume_grid) {
return false;
}
@@ -255,6 +246,9 @@ class BlenderVolumeLoader : public VDBImageLoader {
const size_t pixel_size,
const bool associate_alpha) override
{
+ b_volume.grids.load(b_data.ptr.data);
+ BL::VolumeGrid b_volume_grid = find_grid();
+
if (!b_volume_grid) {
return false;
}
@@ -266,19 +260,38 @@ class BlenderVolumeLoader : public VDBImageLoader {
{
/* TODO: detect multiple volume datablocks with the same filepath. */
const BlenderVolumeLoader &other_loader = (const BlenderVolumeLoader &)other;
- return b_volume == other_loader.b_volume && b_volume_grid == other_loader.b_volume_grid;
+ return b_volume == other_loader.b_volume && grid_name == other_loader.grid_name;
}
void cleanup() override
{
VDBImageLoader::cleanup();
+
+ BL::VolumeGrid b_volume_grid = find_grid();
if (b_volume_grid && unload) {
b_volume_grid.unload();
}
}
+ /* Find grid with matching name. Grid point not stored in the class since
+ * grids may be unloaded before we load the pixels, for example for motion
+ * blur where we move between frames. */
+ BL::VolumeGrid find_grid()
+ {
+#ifdef WITH_OPENVDB
+ BL::Volume::grids_iterator b_grid_iter;
+ for (b_volume.grids.begin(b_grid_iter); b_grid_iter != b_volume.grids.end(); ++b_grid_iter) {
+ if (b_grid_iter->name() == grid_name) {
+ return *b_grid_iter;
+ }
+ }
+#endif
+
+ return BL::VolumeGrid(PointerRNA_NULL);
+ }
+
+ BL::BlendData b_data;
BL::Volume b_volume;
- BL::VolumeGrid b_volume_grid;
bool unload;
};
@@ -325,7 +338,7 @@ static void sync_volume_object(BL::BlendData &b_data, BL::Object &b_ob, Scene *s
mesh->attributes.add(std) :
mesh->attributes.add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_VOXEL);
- ImageLoader *loader = new BlenderVolumeLoader(b_volume, name.string());
+ ImageLoader *loader = new BlenderVolumeLoader(b_data, b_volume, name.string());
ImageParams params;
params.frame = b_volume.grids.frame();
diff --git a/intern/cycles/bvh/CMakeLists.txt b/intern/cycles/bvh/CMakeLists.txt
index fb724704a84..8b8f3ca7265 100644
--- a/intern/cycles/bvh/CMakeLists.txt
+++ b/intern/cycles/bvh/CMakeLists.txt
@@ -9,8 +9,6 @@ set(INC_SYS
set(SRC
bvh.cpp
bvh2.cpp
- bvh4.cpp
- bvh8.cpp
bvh_binning.cpp
bvh_build.cpp
bvh_embree.cpp
@@ -24,8 +22,6 @@ set(SRC
set(SRC_HEADERS
bvh.h
bvh2.h
- bvh4.h
- bvh8.h
bvh_binning.h
bvh_build.h
bvh_embree.h
diff --git a/intern/cycles/bvh/bvh.cpp b/intern/cycles/bvh/bvh.cpp
index 0313bcd68b0..e9e67fd1305 100644
--- a/intern/cycles/bvh/bvh.cpp
+++ b/intern/cycles/bvh/bvh.cpp
@@ -22,17 +22,10 @@
#include "render/object.h"
#include "bvh/bvh2.h"
-#include "bvh/bvh4.h"
-#include "bvh/bvh8.h"
#include "bvh/bvh_build.h"
+#include "bvh/bvh_embree.h"
#include "bvh/bvh_node.h"
-
-#ifdef WITH_OPTIX
-# include "bvh/bvh_optix.h"
-#endif
-#ifdef WITH_EMBREE
-# include "bvh/bvh_embree.h"
-#endif
+#include "bvh/bvh_optix.h"
#include "util/util_foreach.h"
#include "util/util_logging.h"
@@ -47,10 +40,6 @@ const char *bvh_layout_name(BVHLayout layout)
switch (layout) {
case BVH_LAYOUT_BVH2:
return "BVH2";
- case BVH_LAYOUT_BVH4:
- return "BVH4";
- case BVH_LAYOUT_BVH8:
- return "BVH8";
case BVH_LAYOUT_NONE:
return "NONE";
case BVH_LAYOUT_EMBREE:
@@ -114,10 +103,6 @@ BVH *BVH::create(const BVHParams &params,
switch (params.bvh_layout) {
case BVH_LAYOUT_BVH2:
return new BVH2(params, geometry, objects);
- case BVH_LAYOUT_BVH4:
- return new BVH4(params, geometry, objects);
- case BVH_LAYOUT_BVH8:
- return new BVH8(params, geometry, objects);
case BVH_LAYOUT_EMBREE:
#ifdef WITH_EMBREE
return new BVHEmbree(params, geometry, objects);
@@ -337,13 +322,6 @@ void BVH::pack_primitives()
void BVH::pack_instances(size_t nodes_size, size_t leaf_nodes_size)
{
- /* The BVH's for instances are built separately, but for traversal all
- * BVH's are stored in global arrays. This function merges them into the
- * top level BVH, adjusting indexes and offsets where appropriate.
- */
- const bool use_qbvh = (params.bvh_layout == BVH_LAYOUT_BVH4);
- const bool use_obvh = (params.bvh_layout == BVH_LAYOUT_BVH8);
-
/* Adjust primitive index to point to the triangle in the global array, for
* geometry with transform applied and already in the top level BVH.
*/
@@ -506,53 +484,21 @@ void BVH::pack_instances(size_t nodes_size, size_t leaf_nodes_size)
for (size_t i = 0, j = 0; i < bvh_nodes_size; j++) {
size_t nsize, nsize_bbox;
if (bvh_nodes[i].x & PATH_RAY_NODE_UNALIGNED) {
- if (use_obvh) {
- nsize = BVH_UNALIGNED_ONODE_SIZE;
- nsize_bbox = BVH_UNALIGNED_ONODE_SIZE - 1;
- }
- else {
- nsize = use_qbvh ? BVH_UNALIGNED_QNODE_SIZE : BVH_UNALIGNED_NODE_SIZE;
- nsize_bbox = (use_qbvh) ? BVH_UNALIGNED_QNODE_SIZE - 1 : 0;
- }
+ nsize = BVH_UNALIGNED_NODE_SIZE;
+ nsize_bbox = 0;
}
else {
- if (use_obvh) {
- nsize = BVH_ONODE_SIZE;
- nsize_bbox = BVH_ONODE_SIZE - 1;
- }
- else {
- nsize = (use_qbvh) ? BVH_QNODE_SIZE : BVH_NODE_SIZE;
- nsize_bbox = (use_qbvh) ? BVH_QNODE_SIZE - 1 : 0;
- }
+ nsize = BVH_NODE_SIZE;
+ nsize_bbox = 0;
}
memcpy(pack_nodes + pack_nodes_offset, bvh_nodes + i, nsize_bbox * sizeof(int4));
/* Modify offsets into arrays */
int4 data = bvh_nodes[i + nsize_bbox];
-
- if (use_obvh) {
- int4 data1 = bvh_nodes[i + nsize_bbox - 1];
- data.z += (data.z < 0) ? -noffset_leaf : noffset;
- data.w += (data.w < 0) ? -noffset_leaf : noffset;
- data.x += (data.x < 0) ? -noffset_leaf : noffset;
- data.y += (data.y < 0) ? -noffset_leaf : noffset;
- data1.z += (data1.z < 0) ? -noffset_leaf : noffset;
- data1.w += (data1.w < 0) ? -noffset_leaf : noffset;
- data1.x += (data1.x < 0) ? -noffset_leaf : noffset;
- data1.y += (data1.y < 0) ? -noffset_leaf : noffset;
- pack_nodes[pack_nodes_offset + nsize_bbox] = data;
- pack_nodes[pack_nodes_offset + nsize_bbox - 1] = data1;
- }
- else {
- data.z += (data.z < 0) ? -noffset_leaf : noffset;
- data.w += (data.w < 0) ? -noffset_leaf : noffset;
- if (use_qbvh) {
- data.x += (data.x < 0) ? -noffset_leaf : noffset;
- data.y += (data.y < 0) ? -noffset_leaf : noffset;
- }
- pack_nodes[pack_nodes_offset + nsize_bbox] = data;
- }
+ data.z += (data.z < 0) ? -noffset_leaf : noffset;
+ data.w += (data.w < 0) ? -noffset_leaf : noffset;
+ pack_nodes[pack_nodes_offset + nsize_bbox] = data;
/* Usually this copies nothing, but we better
* be prepared for possible node size extension.
diff --git a/intern/cycles/bvh/bvh.h b/intern/cycles/bvh/bvh.h
index bdde38640c9..6639e06b0bc 100644
--- a/intern/cycles/bvh/bvh.h
+++ b/intern/cycles/bvh/bvh.h
@@ -76,7 +76,7 @@ struct PackedBVH {
}
};
-enum BVH_TYPE { bvh2, bvh4, bvh8 };
+enum BVH_TYPE { bvh2 };
/* BVH */
diff --git a/intern/cycles/bvh/bvh4.cpp b/intern/cycles/bvh/bvh4.cpp
deleted file mode 100644
index 143c3e54f94..00000000000
--- a/intern/cycles/bvh/bvh4.cpp
+++ /dev/null
@@ -1,447 +0,0 @@
-/*
- * Adapted from code copyright 2009-2010 NVIDIA Corporation
- * Modifications Copyright 2011, Blender Foundation.
- *
- * 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.
- */
-
-#include "bvh/bvh4.h"
-
-#include "render/mesh.h"
-#include "render/object.h"
-
-#include "bvh/bvh_node.h"
-#include "bvh/bvh_unaligned.h"
-
-CCL_NAMESPACE_BEGIN
-
-/* Can we avoid this somehow or make more generic?
- *
- * Perhaps we can merge nodes in actual tree and make our
- * life easier all over the place.
- */
-
-BVH4::BVH4(const BVHParams &params_,
- const vector<Geometry *> &geometry_,
- const vector<Object *> &objects_)
- : BVH(params_, geometry_, objects_)
-{
- params.bvh_layout = BVH_LAYOUT_BVH4;
-}
-
-namespace {
-
-BVHNode *bvh_node_merge_children_recursively(const BVHNode *node)
-{
- if (node->is_leaf()) {
- return new LeafNode(*reinterpret_cast<const LeafNode *>(node));
- }
- /* Collect nodes of one layer deeper, allowing us to have more children in an inner layer. */
- assert(node->num_children() <= 2);
- const BVHNode *children[4];
- const BVHNode *child0 = node->get_child(0);
- const BVHNode *child1 = node->get_child(1);
- int num_children = 0;
- if (child0->is_leaf()) {
- children[num_children++] = child0;
- }
- else {
- children[num_children++] = child0->get_child(0);
- children[num_children++] = child0->get_child(1);
- }
- if (child1->is_leaf()) {
- children[num_children++] = child1;
- }
- else {
- children[num_children++] = child1->get_child(0);
- children[num_children++] = child1->get_child(1);
- }
- /* Merge children in subtrees. */
- BVHNode *children4[4];
- for (int i = 0; i < num_children; ++i) {
- children4[i] = bvh_node_merge_children_recursively(children[i]);
- }
- /* Allocate new node. */
- BVHNode *node4 = new InnerNode(node->bounds, children4, num_children);
- /* TODO(sergey): Consider doing this from the InnerNode() constructor.
- * But in order to do this nicely need to think of how to pass all the
- * parameters there. */
- if (node->is_unaligned) {
- node4->is_unaligned = true;
- node4->aligned_space = new Transform();
- *node4->aligned_space = *node->aligned_space;
- }
- return node4;
-}
-
-} // namespace
-
-BVHNode *BVH4::widen_children_nodes(const BVHNode *root)
-{
- if (root == NULL) {
- return NULL;
- }
- if (root->is_leaf()) {
- return const_cast<BVHNode *>(root);
- }
- BVHNode *root4 = bvh_node_merge_children_recursively(root);
- /* TODO(sergey): Pack children nodes to parents which has less that 4
- * children. */
- return root4;
-}
-
-void BVH4::pack_leaf(const BVHStackEntry &e, const LeafNode *leaf)
-{
- float4 data[BVH_QNODE_LEAF_SIZE];
- memset(data, 0, sizeof(data));
- if (leaf->num_triangles() == 1 && pack.prim_index[leaf->lo] == -1) {
- /* object */
- data[0].x = __int_as_float(~(leaf->lo));
- data[0].y = __int_as_float(0);
- }
- else {
- /* triangle */
- data[0].x = __int_as_float(leaf->lo);
- data[0].y = __int_as_float(leaf->hi);
- }
- data[0].z = __uint_as_float(leaf->visibility);
- if (leaf->num_triangles() != 0) {
- data[0].w = __uint_as_float(pack.prim_type[leaf->lo]);
- }
-
- memcpy(&pack.leaf_nodes[e.idx], data, sizeof(float4) * BVH_QNODE_LEAF_SIZE);
-}
-
-void BVH4::pack_inner(const BVHStackEntry &e, const BVHStackEntry *en, int num)
-{
- bool has_unaligned = false;
- /* Check whether we have to create unaligned node or all nodes are aligned
- * and we can cut some corner here.
- */
- if (params.use_unaligned_nodes) {
- for (int i = 0; i < num; i++) {
- if (en[i].node->is_unaligned) {
- has_unaligned = true;
- break;
- }
- }
- }
- if (has_unaligned) {
- /* There's no unaligned children, pack into AABB node. */
- pack_unaligned_inner(e, en, num);
- }
- else {
- /* Create unaligned node with orientation transform for each of the
- * children.
- */
- pack_aligned_inner(e, en, num);
- }
-}
-
-void BVH4::pack_aligned_inner(const BVHStackEntry &e, const BVHStackEntry *en, int num)
-{
- BoundBox bounds[4];
- int child[4];
- for (int i = 0; i < num; ++i) {
- bounds[i] = en[i].node->bounds;
- child[i] = en[i].encodeIdx();
- }
- pack_aligned_node(
- e.idx, bounds, child, e.node->visibility, e.node->time_from, e.node->time_to, num);
-}
-
-void BVH4::pack_aligned_node(int idx,
- const BoundBox *bounds,
- const int *child,
- const uint visibility,
- const float time_from,
- const float time_to,
- const int num)
-{
- float4 data[BVH_QNODE_SIZE];
- memset(data, 0, sizeof(data));
-
- data[0].x = __uint_as_float(visibility & ~PATH_RAY_NODE_UNALIGNED);
- data[0].y = time_from;
- data[0].z = time_to;
-
- for (int i = 0; i < num; i++) {
- float3 bb_min = bounds[i].min;
- float3 bb_max = bounds[i].max;
-
- data[1][i] = bb_min.x;
- data[2][i] = bb_max.x;
- data[3][i] = bb_min.y;
- data[4][i] = bb_max.y;
- data[5][i] = bb_min.z;
- data[6][i] = bb_max.z;
-
- data[7][i] = __int_as_float(child[i]);
- }
-
- for (int i = num; i < 4; i++) {
- /* We store BB which would never be recorded as intersection
- * so kernel might safely assume there are always 4 child nodes.
- */
- data[1][i] = FLT_MAX;
- data[2][i] = -FLT_MAX;
-
- data[3][i] = FLT_MAX;
- data[4][i] = -FLT_MAX;
-
- data[5][i] = FLT_MAX;
- data[6][i] = -FLT_MAX;
-
- data[7][i] = __int_as_float(0);
- }
-
- memcpy(&pack.nodes[idx], data, sizeof(float4) * BVH_QNODE_SIZE);
-}
-
-void BVH4::pack_unaligned_inner(const BVHStackEntry &e, const BVHStackEntry *en, int num)
-{
- Transform aligned_space[4];
- BoundBox bounds[4];
- int child[4];
- for (int i = 0; i < num; ++i) {
- aligned_space[i] = en[i].node->get_aligned_space();
- bounds[i] = en[i].node->bounds;
- child[i] = en[i].encodeIdx();
- }
- pack_unaligned_node(e.idx,
- aligned_space,
- bounds,
- child,
- e.node->visibility,
- e.node->time_from,
- e.node->time_to,
- num);
-}
-
-void BVH4::pack_unaligned_node(int idx,
- const Transform *aligned_space,
- const BoundBox *bounds,
- const int *child,
- const uint visibility,
- const float time_from,
- const float time_to,
- const int num)
-{
- float4 data[BVH_UNALIGNED_QNODE_SIZE];
- memset(data, 0, sizeof(data));
-
- data[0].x = __uint_as_float(visibility | PATH_RAY_NODE_UNALIGNED);
- data[0].y = time_from;
- data[0].z = time_to;
-
- for (int i = 0; i < num; i++) {
- Transform space = BVHUnaligned::compute_node_transform(bounds[i], aligned_space[i]);
-
- data[1][i] = space.x.x;
- data[2][i] = space.x.y;
- data[3][i] = space.x.z;
-
- data[4][i] = space.y.x;
- data[5][i] = space.y.y;
- data[6][i] = space.y.z;
-
- data[7][i] = space.z.x;
- data[8][i] = space.z.y;
- data[9][i] = space.z.z;
-
- data[10][i] = space.x.w;
- data[11][i] = space.y.w;
- data[12][i] = space.z.w;
-
- data[13][i] = __int_as_float(child[i]);
- }
-
- for (int i = num; i < 4; i++) {
- /* We store BB which would never be recorded as intersection
- * so kernel might safely assume there are always 4 child nodes.
- */
-
- data[1][i] = NAN;
- data[2][i] = NAN;
- data[3][i] = NAN;
-
- data[4][i] = NAN;
- data[5][i] = NAN;
- data[6][i] = NAN;
-
- data[7][i] = NAN;
- data[8][i] = NAN;
- data[9][i] = NAN;
-
- data[10][i] = NAN;
- data[11][i] = NAN;
- data[12][i] = NAN;
-
- data[13][i] = __int_as_float(0);
- }
-
- memcpy(&pack.nodes[idx], data, sizeof(float4) * BVH_UNALIGNED_QNODE_SIZE);
-}
-
-/* Quad SIMD Nodes */
-
-void BVH4::pack_nodes(const BVHNode *root)
-{
- /* Calculate size of the arrays required. */
- const size_t num_nodes = root->getSubtreeSize(BVH_STAT_NODE_COUNT);
- const size_t num_leaf_nodes = root->getSubtreeSize(BVH_STAT_LEAF_COUNT);
- assert(num_leaf_nodes <= num_nodes);
- const size_t num_inner_nodes = num_nodes - num_leaf_nodes;
- size_t node_size;
- if (params.use_unaligned_nodes) {
- const size_t num_unaligned_nodes = root->getSubtreeSize(BVH_STAT_UNALIGNED_INNER_COUNT);
- node_size = (num_unaligned_nodes * BVH_UNALIGNED_QNODE_SIZE) +
- (num_inner_nodes - num_unaligned_nodes) * BVH_QNODE_SIZE;
- }
- else {
- node_size = num_inner_nodes * BVH_QNODE_SIZE;
- }
- /* Resize arrays. */
- pack.nodes.clear();
- pack.leaf_nodes.clear();
- /* For top level BVH, first merge existing BVH's so we know the offsets. */
- if (params.top_level) {
- pack_instances(node_size, num_leaf_nodes * BVH_QNODE_LEAF_SIZE);
- }
- else {
- pack.nodes.resize(node_size);
- pack.leaf_nodes.resize(num_leaf_nodes * BVH_QNODE_LEAF_SIZE);
- }
-
- int nextNodeIdx = 0, nextLeafNodeIdx = 0;
-
- vector<BVHStackEntry> stack;
- stack.reserve(BVHParams::MAX_DEPTH * 2);
- if (root->is_leaf()) {
- stack.push_back(BVHStackEntry(root, nextLeafNodeIdx++));
- }
- else {
- stack.push_back(BVHStackEntry(root, nextNodeIdx));
- nextNodeIdx += root->has_unaligned() ? BVH_UNALIGNED_QNODE_SIZE : BVH_QNODE_SIZE;
- }
-
- while (stack.size()) {
- BVHStackEntry e = stack.back();
- stack.pop_back();
-
- if (e.node->is_leaf()) {
- /* leaf node */
- const LeafNode *leaf = reinterpret_cast<const LeafNode *>(e.node);
- pack_leaf(e, leaf);
- }
- else {
- /* Inner node. */
- /* Collect nodes. */
- const BVHNode *children[4];
- const int num_children = e.node->num_children();
- /* Push entries on the stack. */
- for (int i = 0; i < num_children; ++i) {
- int idx;
- children[i] = e.node->get_child(i);
- assert(children[i] != NULL);
- if (children[i]->is_leaf()) {
- idx = nextLeafNodeIdx++;
- }
- else {
- idx = nextNodeIdx;
- nextNodeIdx += children[i]->has_unaligned() ? BVH_UNALIGNED_QNODE_SIZE : BVH_QNODE_SIZE;
- }
- stack.push_back(BVHStackEntry(children[i], idx));
- }
- /* Set node. */
- pack_inner(e, &stack[stack.size() - num_children], num_children);
- }
- }
-
- assert(node_size == nextNodeIdx);
- /* Root index to start traversal at, to handle case of single leaf node. */
- pack.root_index = (root->is_leaf()) ? -1 : 0;
-}
-
-void BVH4::refit_nodes()
-{
- assert(!params.top_level);
-
- BoundBox bbox = BoundBox::empty;
- uint visibility = 0;
- refit_node(0, (pack.root_index == -1) ? true : false, bbox, visibility);
-}
-
-void BVH4::refit_node(int idx, bool leaf, BoundBox &bbox, uint &visibility)
-{
- if (leaf) {
- /* Refit leaf node. */
- int4 *data = &pack.leaf_nodes[idx];
- int4 c = data[0];
-
- BVH::refit_primitives(c.x, c.y, bbox, visibility);
-
- /* TODO(sergey): This is actually a copy of pack_leaf(),
- * but this chunk of code only knows actual data and has
- * no idea about BVHNode.
- *
- * Would be nice to de-duplicate code, but trying to make
- * making code more general ends up in much nastier code
- * in my opinion so far.
- *
- * Same applies to the inner nodes case below.
- */
- float4 leaf_data[BVH_QNODE_LEAF_SIZE];
- leaf_data[0].x = __int_as_float(c.x);
- leaf_data[0].y = __int_as_float(c.y);
- leaf_data[0].z = __uint_as_float(visibility);
- leaf_data[0].w = __uint_as_float(c.w);
- memcpy(&pack.leaf_nodes[idx], leaf_data, sizeof(float4) * BVH_QNODE_LEAF_SIZE);
- }
- else {
- int4 *data = &pack.nodes[idx];
- bool is_unaligned = (data[0].x & PATH_RAY_NODE_UNALIGNED) != 0;
- int4 c;
- if (is_unaligned) {
- c = data[13];
- }
- else {
- c = data[7];
- }
- /* Refit inner node, set bbox from children. */
- BoundBox child_bbox[4] = {BoundBox::empty, BoundBox::empty, BoundBox::empty, BoundBox::empty};
- uint child_visibility[4] = {0};
- int num_nodes = 0;
-
- for (int i = 0; i < 4; ++i) {
- if (c[i] != 0) {
- refit_node((c[i] < 0) ? -c[i] - 1 : c[i], (c[i] < 0), child_bbox[i], child_visibility[i]);
- ++num_nodes;
- bbox.grow(child_bbox[i]);
- visibility |= child_visibility[i];
- }
- }
-
- if (is_unaligned) {
- Transform aligned_space[4] = {
- transform_identity(), transform_identity(), transform_identity(), transform_identity()};
- pack_unaligned_node(
- idx, aligned_space, child_bbox, &c[0], visibility, 0.0f, 1.0f, num_nodes);
- }
- else {
- pack_aligned_node(idx, child_bbox, &c[0], visibility, 0.0f, 1.0f, num_nodes);
- }
- }
-}
-
-CCL_NAMESPACE_END
diff --git a/intern/cycles/bvh/bvh4.h b/intern/cycles/bvh/bvh4.h
deleted file mode 100644
index afbb9007afb..00000000000
--- a/intern/cycles/bvh/bvh4.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Adapted from code copyright 2009-2010 NVIDIA Corporation
- * Modifications Copyright 2011, Blender Foundation.
- *
- * 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.
- */
-
-#ifndef __BVH4_H__
-#define __BVH4_H__
-
-#include "bvh/bvh.h"
-#include "bvh/bvh_params.h"
-
-#include "util/util_types.h"
-#include "util/util_vector.h"
-
-CCL_NAMESPACE_BEGIN
-
-class BVHNode;
-struct BVHStackEntry;
-class BVHParams;
-class BoundBox;
-class LeafNode;
-class Object;
-class Progress;
-
-#define BVH_QNODE_SIZE 8
-#define BVH_QNODE_LEAF_SIZE 1
-#define BVH_UNALIGNED_QNODE_SIZE 14
-
-/* BVH4
- *
- * Quad BVH, with each node having four children, to use with SIMD instructions.
- */
-class BVH4 : public BVH {
- protected:
- /* constructor */
- friend class BVH;
- BVH4(const BVHParams &params,
- const vector<Geometry *> &geometry,
- const vector<Object *> &objects);
-
- /* Building process. */
- virtual BVHNode *widen_children_nodes(const BVHNode *root) override;
-
- /* pack */
- void pack_nodes(const BVHNode *root) override;
-
- void pack_leaf(const BVHStackEntry &e, const LeafNode *leaf);
- void pack_inner(const BVHStackEntry &e, const BVHStackEntry *en, int num);
-
- void pack_aligned_inner(const BVHStackEntry &e, const BVHStackEntry *en, int num);
- void pack_aligned_node(int idx,
- const BoundBox *bounds,
- const int *child,
- const uint visibility,
- const float time_from,
- const float time_to,
- const int num);
-
- void pack_unaligned_inner(const BVHStackEntry &e, const BVHStackEntry *en, int num);
- void pack_unaligned_node(int idx,
- const Transform *aligned_space,
- const BoundBox *bounds,
- const int *child,
- const uint visibility,
- const float time_from,
- const float time_to,
- const int num);
-
- /* refit */
- void refit_nodes() override;
- void refit_node(int idx, bool leaf, BoundBox &bbox, uint &visibility);
-};
-
-CCL_NAMESPACE_END
-
-#endif /* __BVH4_H__ */
diff --git a/intern/cycles/bvh/bvh8.cpp b/intern/cycles/bvh/bvh8.cpp
deleted file mode 100644
index b805865b2c8..00000000000
--- a/intern/cycles/bvh/bvh8.cpp
+++ /dev/null
@@ -1,541 +0,0 @@
-/*
- * Original code Copyright 2017, Intel Corporation
- * Modifications Copyright 2018, Blender Foundation.
- *
- * 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 Intel Corporation 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.
- */
-
-#include "bvh/bvh8.h"
-
-#include "render/hair.h"
-#include "render/mesh.h"
-#include "render/object.h"
-
-#include "bvh/bvh_node.h"
-#include "bvh/bvh_unaligned.h"
-
-CCL_NAMESPACE_BEGIN
-
-BVH8::BVH8(const BVHParams &params_,
- const vector<Geometry *> &geometry_,
- const vector<Object *> &objects_)
- : BVH(params_, geometry_, objects_)
-{
-}
-
-namespace {
-
-BVHNode *bvh_node_merge_children_recursively(const BVHNode *node)
-{
- if (node->is_leaf()) {
- return new LeafNode(*reinterpret_cast<const LeafNode *>(node));
- }
- /* Collect nodes of two layer deeper, allowing us to have more childrem in
- * an inner layer. */
- assert(node->num_children() <= 2);
- const BVHNode *children[8];
- const BVHNode *child0 = node->get_child(0);
- const BVHNode *child1 = node->get_child(1);
- int num_children = 0;
- if (child0->is_leaf()) {
- children[num_children++] = child0;
- }
- else {
- const BVHNode *child00 = child0->get_child(0), *child01 = child0->get_child(1);
- if (child00->is_leaf()) {
- children[num_children++] = child00;
- }
- else {
- children[num_children++] = child00->get_child(0);
- children[num_children++] = child00->get_child(1);
- }
- if (child01->is_leaf()) {
- children[num_children++] = child01;
- }
- else {
- children[num_children++] = child01->get_child(0);
- children[num_children++] = child01->get_child(1);
- }
- }
- if (child1->is_leaf()) {
- children[num_children++] = child1;
- }
- else {
- const BVHNode *child10 = child1->get_child(0), *child11 = child1->get_child(1);
- if (child10->is_leaf()) {
- children[num_children++] = child10;
- }
- else {
- children[num_children++] = child10->get_child(0);
- children[num_children++] = child10->get_child(1);
- }
- if (child11->is_leaf()) {
- children[num_children++] = child11;
- }
- else {
- children[num_children++] = child11->get_child(0);
- children[num_children++] = child11->get_child(1);
- }
- }
- /* Merge children in subtrees. */
- BVHNode *children4[8];
- for (int i = 0; i < num_children; ++i) {
- children4[i] = bvh_node_merge_children_recursively(children[i]);
- }
- /* Allocate new node. */
- BVHNode *node8 = new InnerNode(node->bounds, children4, num_children);
- /* TODO(sergey): Consider doing this from the InnerNode() constructor.
- * But in order to do this nicely need to think of how to pass all the
- * parameters there. */
- if (node->is_unaligned) {
- node8->is_unaligned = true;
- node8->aligned_space = new Transform();
- *node8->aligned_space = *node->aligned_space;
- }
- return node8;
-}
-
-} // namespace
-
-BVHNode *BVH8::widen_children_nodes(const BVHNode *root)
-{
- if (root == NULL) {
- return NULL;
- }
- if (root->is_leaf()) {
- return const_cast<BVHNode *>(root);
- }
- BVHNode *root8 = bvh_node_merge_children_recursively(root);
- /* TODO(sergey): Pack children nodes to parents which has less that 4
- * children. */
- return root8;
-}
-
-void BVH8::pack_leaf(const BVHStackEntry &e, const LeafNode *leaf)
-{
- float4 data[BVH_ONODE_LEAF_SIZE];
- memset(data, 0, sizeof(data));
- if (leaf->num_triangles() == 1 && pack.prim_index[leaf->lo] == -1) {
- /* object */
- data[0].x = __int_as_float(~(leaf->lo));
- data[0].y = __int_as_float(0);
- }
- else {
- /* triangle */
- data[0].x = __int_as_float(leaf->lo);
- data[0].y = __int_as_float(leaf->hi);
- }
- data[0].z = __uint_as_float(leaf->visibility);
- if (leaf->num_triangles() != 0) {
- data[0].w = __uint_as_float(pack.prim_type[leaf->lo]);
- }
-
- memcpy(&pack.leaf_nodes[e.idx], data, sizeof(float4) * BVH_ONODE_LEAF_SIZE);
-}
-
-void BVH8::pack_inner(const BVHStackEntry &e, const BVHStackEntry *en, int num)
-{
- bool has_unaligned = false;
- /* Check whether we have to create unaligned node or all nodes are aligned
- * and we can cut some corner here.
- */
- if (params.use_unaligned_nodes) {
- for (int i = 0; i < num; i++) {
- if (en[i].node->is_unaligned) {
- has_unaligned = true;
- break;
- }
- }
- }
- if (has_unaligned) {
- /* There's no unaligned children, pack into AABB node. */
- pack_unaligned_inner(e, en, num);
- }
- else {
- /* Create unaligned node with orientation transform for each of the
- * children.
- */
- pack_aligned_inner(e, en, num);
- }
-}
-
-void BVH8::pack_aligned_inner(const BVHStackEntry &e, const BVHStackEntry *en, int num)
-{
- BoundBox bounds[8];
- int child[8];
- for (int i = 0; i < num; ++i) {
- bounds[i] = en[i].node->bounds;
- child[i] = en[i].encodeIdx();
- }
- pack_aligned_node(
- e.idx, bounds, child, e.node->visibility, e.node->time_from, e.node->time_to, num);
-}
-
-void BVH8::pack_aligned_node(int idx,
- const BoundBox *bounds,
- const int *child,
- const uint visibility,
- const float time_from,
- const float time_to,
- const int num)
-{
- float8 data[8];
- memset(data, 0, sizeof(data));
-
- data[0].a = __uint_as_float(visibility & ~PATH_RAY_NODE_UNALIGNED);
- data[0].b = time_from;
- data[0].c = time_to;
-
- for (int i = 0; i < num; i++) {
- float3 bb_min = bounds[i].min;
- float3 bb_max = bounds[i].max;
-
- data[1][i] = bb_min.x;
- data[2][i] = bb_max.x;
- data[3][i] = bb_min.y;
- data[4][i] = bb_max.y;
- data[5][i] = bb_min.z;
- data[6][i] = bb_max.z;
-
- data[7][i] = __int_as_float(child[i]);
- }
-
- for (int i = num; i < 8; i++) {
- /* We store BB which would never be recorded as intersection
- * so kernel might safely assume there are always 4 child nodes.
- */
- data[1][i] = FLT_MAX;
- data[2][i] = -FLT_MAX;
-
- data[3][i] = FLT_MAX;
- data[4][i] = -FLT_MAX;
-
- data[5][i] = FLT_MAX;
- data[6][i] = -FLT_MAX;
-
- data[7][i] = __int_as_float(0);
- }
-
- memcpy(&pack.nodes[idx], data, sizeof(float4) * BVH_ONODE_SIZE);
-}
-
-void BVH8::pack_unaligned_inner(const BVHStackEntry &e, const BVHStackEntry *en, int num)
-{
- Transform aligned_space[8];
- BoundBox bounds[8];
- int child[8];
- for (int i = 0; i < num; ++i) {
- aligned_space[i] = en[i].node->get_aligned_space();
- bounds[i] = en[i].node->bounds;
- child[i] = en[i].encodeIdx();
- }
- pack_unaligned_node(e.idx,
- aligned_space,
- bounds,
- child,
- e.node->visibility,
- e.node->time_from,
- e.node->time_to,
- num);
-}
-
-void BVH8::pack_unaligned_node(int idx,
- const Transform *aligned_space,
- const BoundBox *bounds,
- const int *child,
- const uint visibility,
- const float time_from,
- const float time_to,
- const int num)
-{
- float8 data[BVH_UNALIGNED_ONODE_SIZE];
- memset(data, 0, sizeof(data));
-
- data[0].a = __uint_as_float(visibility | PATH_RAY_NODE_UNALIGNED);
- data[0].b = time_from;
- data[0].c = time_to;
-
- for (int i = 0; i < num; i++) {
- Transform space = BVHUnaligned::compute_node_transform(bounds[i], aligned_space[i]);
-
- data[1][i] = space.x.x;
- data[2][i] = space.x.y;
- data[3][i] = space.x.z;
-
- data[4][i] = space.y.x;
- data[5][i] = space.y.y;
- data[6][i] = space.y.z;
-
- data[7][i] = space.z.x;
- data[8][i] = space.z.y;
- data[9][i] = space.z.z;
-
- data[10][i] = space.x.w;
- data[11][i] = space.y.w;
- data[12][i] = space.z.w;
-
- data[13][i] = __int_as_float(child[i]);
- }
-
- for (int i = num; i < 8; i++) {
- /* We store BB which would never be recorded as intersection
- * so kernel might safely assume there are always 4 child nodes.
- */
-
- data[1][i] = NAN;
- data[2][i] = NAN;
- data[3][i] = NAN;
-
- data[4][i] = NAN;
- data[5][i] = NAN;
- data[6][i] = NAN;
-
- data[7][i] = NAN;
- data[8][i] = NAN;
- data[9][i] = NAN;
-
- data[10][i] = NAN;
- data[11][i] = NAN;
- data[12][i] = NAN;
-
- data[13][i] = __int_as_float(0);
- }
-
- memcpy(&pack.nodes[idx], data, sizeof(float4) * BVH_UNALIGNED_ONODE_SIZE);
-}
-
-/* Quad SIMD Nodes */
-
-void BVH8::pack_nodes(const BVHNode *root)
-{
- /* Calculate size of the arrays required. */
- const size_t num_nodes = root->getSubtreeSize(BVH_STAT_NODE_COUNT);
- const size_t num_leaf_nodes = root->getSubtreeSize(BVH_STAT_LEAF_COUNT);
- assert(num_leaf_nodes <= num_nodes);
- const size_t num_inner_nodes = num_nodes - num_leaf_nodes;
- size_t node_size;
- if (params.use_unaligned_nodes) {
- const size_t num_unaligned_nodes = root->getSubtreeSize(BVH_STAT_UNALIGNED_INNER_COUNT);
- node_size = (num_unaligned_nodes * BVH_UNALIGNED_ONODE_SIZE) +
- (num_inner_nodes - num_unaligned_nodes) * BVH_ONODE_SIZE;
- }
- else {
- node_size = num_inner_nodes * BVH_ONODE_SIZE;
- }
- /* Resize arrays. */
- pack.nodes.clear();
- pack.leaf_nodes.clear();
- /* For top level BVH, first merge existing BVH's so we know the offsets. */
- if (params.top_level) {
- pack_instances(node_size, num_leaf_nodes * BVH_ONODE_LEAF_SIZE);
- }
- else {
- pack.nodes.resize(node_size);
- pack.leaf_nodes.resize(num_leaf_nodes * BVH_ONODE_LEAF_SIZE);
- }
-
- int nextNodeIdx = 0, nextLeafNodeIdx = 0;
-
- vector<BVHStackEntry> stack;
- stack.reserve(BVHParams::MAX_DEPTH * 2);
- if (root->is_leaf()) {
- stack.push_back(BVHStackEntry(root, nextLeafNodeIdx++));
- }
- else {
- stack.push_back(BVHStackEntry(root, nextNodeIdx));
- nextNodeIdx += root->has_unaligned() ? BVH_UNALIGNED_ONODE_SIZE : BVH_ONODE_SIZE;
- }
-
- while (stack.size()) {
- BVHStackEntry e = stack.back();
- stack.pop_back();
-
- if (e.node->is_leaf()) {
- /* leaf node */
- const LeafNode *leaf = reinterpret_cast<const LeafNode *>(e.node);
- pack_leaf(e, leaf);
- }
- else {
- /* Inner node. */
- /* Collect nodes. */
- const BVHNode *children[8];
- int num_children = e.node->num_children();
- /* Push entries on the stack. */
- for (int i = 0; i < num_children; ++i) {
- int idx;
- children[i] = e.node->get_child(i);
- if (children[i]->is_leaf()) {
- idx = nextLeafNodeIdx++;
- }
- else {
- idx = nextNodeIdx;
- nextNodeIdx += children[i]->has_unaligned() ? BVH_UNALIGNED_ONODE_SIZE : BVH_ONODE_SIZE;
- }
- stack.push_back(BVHStackEntry(children[i], idx));
- }
- /* Set node. */
- pack_inner(e, &stack[stack.size() - num_children], num_children);
- }
- }
-
- assert(node_size == nextNodeIdx);
- /* Root index to start traversal at, to handle case of single leaf node. */
- pack.root_index = (root->is_leaf()) ? -1 : 0;
-}
-
-void BVH8::refit_nodes()
-{
- assert(!params.top_level);
-
- BoundBox bbox = BoundBox::empty;
- uint visibility = 0;
- refit_node(0, (pack.root_index == -1) ? true : false, bbox, visibility);
-}
-
-void BVH8::refit_node(int idx, bool leaf, BoundBox &bbox, uint &visibility)
-{
- if (leaf) {
- int4 *data = &pack.leaf_nodes[idx];
- int4 c = data[0];
- /* Refit leaf node. */
- for (int prim = c.x; prim < c.y; prim++) {
- int pidx = pack.prim_index[prim];
- int tob = pack.prim_object[prim];
- Object *ob = objects[tob];
-
- if (pidx == -1) {
- /* Object instance. */
- bbox.grow(ob->bounds);
- }
- else {
- /* Primitives. */
- if (pack.prim_type[prim] & PRIMITIVE_ALL_CURVE) {
- /* Curves. */
- const Hair *hair = static_cast<const Hair *>(ob->geometry);
- int prim_offset = (params.top_level) ? hair->prim_offset : 0;
- Hair::Curve curve = hair->get_curve(pidx - prim_offset);
- int k = PRIMITIVE_UNPACK_SEGMENT(pack.prim_type[prim]);
-
- curve.bounds_grow(k, &hair->curve_keys[0], &hair->curve_radius[0], bbox);
-
- /* Motion curves. */
- if (hair->use_motion_blur) {
- Attribute *attr = hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
-
- if (attr) {
- size_t hair_size = hair->curve_keys.size();
- size_t steps = hair->motion_steps - 1;
- float3 *key_steps = attr->data_float3();
-
- for (size_t i = 0; i < steps; i++) {
- curve.bounds_grow(k, key_steps + i * hair_size, &hair->curve_radius[0], bbox);
- }
- }
- }
- }
- else {
- /* Triangles. */
- const Mesh *mesh = static_cast<const Mesh *>(ob->geometry);
- int prim_offset = (params.top_level) ? mesh->prim_offset : 0;
- Mesh::Triangle triangle = mesh->get_triangle(pidx - prim_offset);
- const float3 *vpos = &mesh->verts[0];
-
- triangle.bounds_grow(vpos, bbox);
-
- /* Motion triangles. */
- if (mesh->use_motion_blur) {
- Attribute *attr = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
-
- if (attr) {
- size_t mesh_size = mesh->verts.size();
- size_t steps = mesh->motion_steps - 1;
- float3 *vert_steps = attr->data_float3();
-
- for (size_t i = 0; i < steps; i++) {
- triangle.bounds_grow(vert_steps + i * mesh_size, bbox);
- }
- }
- }
- }
- }
-
- visibility |= ob->visibility;
- }
-
- float4 leaf_data[BVH_ONODE_LEAF_SIZE];
- leaf_data[0].x = __int_as_float(c.x);
- leaf_data[0].y = __int_as_float(c.y);
- leaf_data[0].z = __uint_as_float(visibility);
- leaf_data[0].w = __uint_as_float(c.w);
- memcpy(&pack.leaf_nodes[idx], leaf_data, sizeof(float4) * BVH_ONODE_LEAF_SIZE);
- }
- else {
- float8 *data = (float8 *)&pack.nodes[idx];
- bool is_unaligned = (__float_as_uint(data[0].a) & PATH_RAY_NODE_UNALIGNED) != 0;
- /* Refit inner node, set bbox from children. */
- BoundBox child_bbox[8] = {BoundBox::empty,
- BoundBox::empty,
- BoundBox::empty,
- BoundBox::empty,
- BoundBox::empty,
- BoundBox::empty,
- BoundBox::empty,
- BoundBox::empty};
- int child[8];
- uint child_visibility[8] = {0};
- int num_nodes = 0;
-
- for (int i = 0; i < 8; ++i) {
- child[i] = __float_as_int(data[(is_unaligned) ? 13 : 7][i]);
-
- if (child[i] != 0) {
- refit_node((child[i] < 0) ? -child[i] - 1 : child[i],
- (child[i] < 0),
- child_bbox[i],
- child_visibility[i]);
- ++num_nodes;
- bbox.grow(child_bbox[i]);
- visibility |= child_visibility[i];
- }
- }
-
- if (is_unaligned) {
- Transform aligned_space[8] = {transform_identity(),
- transform_identity(),
- transform_identity(),
- transform_identity(),
- transform_identity(),
- transform_identity(),
- transform_identity(),
- transform_identity()};
- pack_unaligned_node(
- idx, aligned_space, child_bbox, child, visibility, 0.0f, 1.0f, num_nodes);
- }
- else {
- pack_aligned_node(idx, child_bbox, child, visibility, 0.0f, 1.0f, num_nodes);
- }
- }
-}
-
-CCL_NAMESPACE_END
diff --git a/intern/cycles/bvh/bvh8.h b/intern/cycles/bvh/bvh8.h
deleted file mode 100644
index d23fa528e3e..00000000000
--- a/intern/cycles/bvh/bvh8.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Original code Copyright 2017, Intel Corporation
- * Modifications Copyright 2018, Blender Foundation.
- *
- * 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 Intel Corporation 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.
- */
-
-#ifndef __BVH8_H__
-#define __BVH8_H__
-
-#include "bvh/bvh.h"
-#include "bvh/bvh_params.h"
-
-#include "util/util_types.h"
-#include "util/util_vector.h"
-
-CCL_NAMESPACE_BEGIN
-
-class BVHNode;
-struct BVHStackEntry;
-class BVHParams;
-class BoundBox;
-class LeafNode;
-class Object;
-class Progress;
-
-#define BVH_ONODE_SIZE 16
-#define BVH_ONODE_LEAF_SIZE 1
-#define BVH_UNALIGNED_ONODE_SIZE 28
-
-/* BVH8
- *
- * Octo BVH, with each node having eight children, to use with SIMD instructions.
- */
-class BVH8 : public BVH {
- protected:
- /* constructor */
- friend class BVH;
- BVH8(const BVHParams &params,
- const vector<Geometry *> &geometry,
- const vector<Object *> &objects);
-
- /* Building process. */
- virtual BVHNode *widen_children_nodes(const BVHNode *root) override;
-
- /* pack */
- void pack_nodes(const BVHNode *root) override;
-
- void pack_leaf(const BVHStackEntry &e, const LeafNode *leaf);
- void pack_inner(const BVHStackEntry &e, const BVHStackEntry *en, int num);
-
- void pack_aligned_inner(const BVHStackEntry &e, const BVHStackEntry *en, int num);
- void pack_aligned_node(int idx,
- const BoundBox *bounds,
- const int *child,
- const uint visibility,
- const float time_from,
- const float time_to,
- const int num);
-
- void pack_unaligned_inner(const BVHStackEntry &e, const BVHStackEntry *en, int num);
- void pack_unaligned_node(int idx,
- const Transform *aligned_space,
- const BoundBox *bounds,
- const int *child,
- const uint visibility,
- const float time_from,
- const float time_to,
- const int num);
-
- /* refit */
- void refit_nodes() override;
- void refit_node(int idx, bool leaf, BoundBox &bbox, uint &visibility);
-};
-
-CCL_NAMESPACE_END
-
-#endif /* __BVH8_H__ */
diff --git a/intern/cycles/bvh/bvh_build.cpp b/intern/cycles/bvh/bvh_build.cpp
index 814b5ced5d2..86ab7b00815 100644
--- a/intern/cycles/bvh/bvh_build.cpp
+++ b/intern/cycles/bvh/bvh_build.cpp
@@ -39,48 +39,6 @@
CCL_NAMESPACE_BEGIN
-/* BVH Build Task */
-
-class BVHBuildTask : public Task {
- public:
- BVHBuildTask(
- BVHBuild *build, InnerNode *node, int child, const BVHObjectBinning &range, int level)
- : range_(range)
- {
- run = function_bind(&BVHBuild::thread_build_node, build, node, child, &range_, level);
- }
-
- private:
- BVHObjectBinning range_;
-};
-
-class BVHSpatialSplitBuildTask : public Task {
- public:
- BVHSpatialSplitBuildTask(BVHBuild *build,
- InnerNode *node,
- int child,
- const BVHRange &range,
- const vector<BVHReference> &references,
- int level)
- : range_(range),
- references_(references.begin() + range.start(), references.begin() + range.end())
- {
- range_.set_start(0);
- run = function_bind(&BVHBuild::thread_build_spatial_split_node,
- build,
- node,
- child,
- &range_,
- &references_,
- level,
- _1);
- }
-
- private:
- BVHRange range_;
- vector<BVHReference> references_;
-};
-
/* Constructor / Destructor */
BVHBuild::BVHBuild(const vector<Object *> &objects_,
@@ -201,6 +159,13 @@ void BVHBuild::add_reference_curves(BoundBox &root, BoundBox &center, Hair *hair
if (hair->has_motion_blur()) {
curve_attr_mP = hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
}
+
+ const PrimitiveType primitive_type =
+ (curve_attr_mP != NULL) ?
+ ((hair->curve_shape == CURVE_RIBBON) ? PRIMITIVE_MOTION_CURVE_RIBBON :
+ PRIMITIVE_MOTION_CURVE_THICK) :
+ ((hair->curve_shape == CURVE_RIBBON) ? PRIMITIVE_CURVE_RIBBON : PRIMITIVE_CURVE_THICK);
+
const size_t num_curves = hair->num_curves();
for (uint j = 0; j < num_curves; j++) {
const Hair::Curve curve = hair->get_curve(j);
@@ -211,7 +176,7 @@ void BVHBuild::add_reference_curves(BoundBox &root, BoundBox &center, Hair *hair
BoundBox bounds = BoundBox::empty;
curve.bounds_grow(k, &hair->curve_keys[0], curve_radius, bounds);
if (bounds.valid()) {
- int packed_type = PRIMITIVE_PACK_SEGMENT(PRIMITIVE_CURVE, k);
+ int packed_type = PRIMITIVE_PACK_SEGMENT(primitive_type, k);
references.push_back(BVHReference(bounds, j, i, packed_type));
root.grow(bounds);
center.grow(bounds.center2());
@@ -232,7 +197,7 @@ void BVHBuild::add_reference_curves(BoundBox &root, BoundBox &center, Hair *hair
curve.bounds_grow(k, key_steps + step * num_keys, curve_radius, bounds);
}
if (bounds.valid()) {
- int packed_type = PRIMITIVE_PACK_SEGMENT(PRIMITIVE_MOTION_CURVE, k);
+ int packed_type = PRIMITIVE_PACK_SEGMENT(primitive_type, k);
references.push_back(BVHReference(bounds, j, i, packed_type));
root.grow(bounds);
center.grow(bounds.center2());
@@ -288,7 +253,7 @@ void BVHBuild::add_reference_curves(BoundBox &root, BoundBox &center, Hair *hair
bounds.grow(curr_bounds);
if (bounds.valid()) {
const float prev_time = (float)(bvh_step - 1) * num_bvh_steps_inv_1;
- int packed_type = PRIMITIVE_PACK_SEGMENT(PRIMITIVE_MOTION_CURVE, k);
+ int packed_type = PRIMITIVE_PACK_SEGMENT(primitive_type, k);
references.push_back(BVHReference(bounds, j, i, packed_type, prev_time, curr_time));
root.grow(bounds);
center.grow(bounds.center2());
@@ -423,22 +388,6 @@ BVHNode *BVHBuild::run()
}
spatial_min_overlap = root.bounds().safe_area() * params.spatial_split_alpha;
- if (params.use_spatial_split) {
- /* NOTE: The API here tries to be as much ready for multi-threaded build
- * as possible, but at the same time it tries not to introduce any
- * changes in behavior for until all refactoring needed for threading is
- * finished.
- *
- * So we currently allocate single storage for now, which is only used by
- * the only thread working on the spatial BVH build.
- */
- spatial_storage.resize(TaskScheduler::num_threads() + 1);
- size_t num_bins = max(root.size(), (int)BVHParams::NUM_SPATIAL_BINS) - 1;
- foreach (BVHSpatialStorage &storage, spatial_storage) {
- storage.right_bounds.clear();
- }
- spatial_storage[0].right_bounds.resize(num_bins);
- }
spatial_free_index = 0;
need_prim_time = params.num_motion_curve_steps > 0 || params.num_motion_triangle_steps > 0;
@@ -465,7 +414,8 @@ BVHNode *BVHBuild::run()
if (params.use_spatial_split) {
/* Perform multithreaded spatial split build. */
- rootnode = build_node(root, &references, 0, 0);
+ BVHSpatialStorage *local_storage = &spatial_storage.local();
+ rootnode = build_node(root, references, 0, local_storage);
task_pool.wait_work();
}
else {
@@ -475,6 +425,9 @@ BVHNode *BVHBuild::run()
task_pool.wait_work();
}
+ /* clean up temporary memory usage by threads */
+ spatial_storage.clear();
+
/* delete if we canceled */
if (rootnode) {
if (progress.get_cancel()) {
@@ -529,41 +482,46 @@ void BVHBuild::progress_update()
progress_start_time = time_dt();
}
-void BVHBuild::thread_build_node(InnerNode *inner, int child, BVHObjectBinning *range, int level)
+void BVHBuild::thread_build_node(InnerNode *inner,
+ int child,
+ const BVHObjectBinning &range,
+ int level)
{
if (progress.get_cancel())
return;
/* build nodes */
- BVHNode *node = build_node(*range, level);
+ BVHNode *node = build_node(range, level);
/* set child in inner node */
inner->children[child] = node;
/* update progress */
- if (range->size() < THREAD_TASK_SIZE) {
+ if (range.size() < THREAD_TASK_SIZE) {
/*rotate(node, INT_MAX, 5);*/
thread_scoped_lock lock(build_mutex);
- progress_count += range->size();
+ progress_count += range.size();
progress_update();
}
}
void BVHBuild::thread_build_spatial_split_node(InnerNode *inner,
int child,
- BVHRange *range,
- vector<BVHReference> *references,
- int level,
- int thread_id)
+ const BVHRange &range,
+ vector<BVHReference> &references,
+ int level)
{
if (progress.get_cancel()) {
return;
}
+ /* Get per-thread memory for spatial split. */
+ BVHSpatialStorage *local_storage = &spatial_storage.local();
+
/* build nodes */
- BVHNode *node = build_node(*range, references, level, thread_id);
+ BVHNode *node = build_node(range, references, level, local_storage);
/* set child in inner node */
inner->children[child] = node;
@@ -586,14 +544,22 @@ bool BVHBuild::range_within_max_leaf_size(const BVHRange &range,
for (int i = 0; i < size; i++) {
const BVHReference &ref = references[range.start() + i];
- if (ref.prim_type() & PRIMITIVE_CURVE)
- num_curves++;
- if (ref.prim_type() & PRIMITIVE_MOTION_CURVE)
- num_motion_curves++;
- else if (ref.prim_type() & PRIMITIVE_TRIANGLE)
- num_triangles++;
- else if (ref.prim_type() & PRIMITIVE_MOTION_TRIANGLE)
- num_motion_triangles++;
+ if (ref.prim_type() & PRIMITIVE_ALL_CURVE) {
+ if (ref.prim_type() & PRIMITIVE_ALL_MOTION) {
+ num_motion_curves++;
+ }
+ else {
+ num_curves++;
+ }
+ }
+ else if (ref.prim_type() & PRIMITIVE_ALL_TRIANGLE) {
+ if (ref.prim_type() & PRIMITIVE_ALL_MOTION) {
+ num_motion_triangles++;
+ }
+ else {
+ num_triangles++;
+ }
+ }
}
return (num_triangles <= params.max_triangle_leaf_size) &&
@@ -675,8 +641,8 @@ BVHNode *BVHBuild::build_node(const BVHObjectBinning &range, int level)
/* Threaded build */
inner = new InnerNode(bounds);
- task_pool.push(new BVHBuildTask(this, inner, 0, left, level + 1), true);
- task_pool.push(new BVHBuildTask(this, inner, 1, right, level + 1), true);
+ task_pool.push([=] { thread_build_node(inner, 0, left, level + 1); });
+ task_pool.push([=] { thread_build_node(inner, 1, right, level + 1); });
}
if (do_unalinged_split) {
@@ -688,9 +654,9 @@ BVHNode *BVHBuild::build_node(const BVHObjectBinning &range, int level)
/* multithreaded spatial split builder */
BVHNode *BVHBuild::build_node(const BVHRange &range,
- vector<BVHReference> *references,
+ vector<BVHReference> &references,
int level,
- int thread_id)
+ BVHSpatialStorage *storage)
{
/* Update progress.
*
@@ -707,18 +673,17 @@ BVHNode *BVHBuild::build_node(const BVHRange &range,
if (!(range.size() > 0 && params.top_level && level == 0)) {
if (params.small_enough_for_leaf(range.size(), level)) {
progress_count += range.size();
- return create_leaf_node(range, *references);
+ return create_leaf_node(range, references);
}
}
/* Perform splitting test. */
- BVHSpatialStorage *storage = &spatial_storage[thread_id];
BVHMixedSplit split(this, storage, range, references, level);
if (!(range.size() > 0 && params.top_level && level == 0)) {
if (split.no_split) {
progress_count += range.size();
- return create_leaf_node(range, *references);
+ return create_leaf_node(range, references);
}
}
float leafSAH = params.sah_primitive_cost * split.leafSAH;
@@ -731,7 +696,7 @@ BVHNode *BVHBuild::build_node(const BVHRange &range,
Transform aligned_space;
bool do_unalinged_split = false;
if (params.use_unaligned_nodes && splitSAH > params.unaligned_split_threshold * leafSAH) {
- aligned_space = unaligned_heuristic.compute_aligned_space(range, &references->at(0));
+ aligned_space = unaligned_heuristic.compute_aligned_space(range, &references.at(0));
unaligned_split = BVHMixedSplit(
this, storage, range, references, level, &unaligned_heuristic, &aligned_space);
/* unalignedLeafSAH = params.sah_primitive_cost * split.leafSAH; */
@@ -757,8 +722,7 @@ BVHNode *BVHBuild::build_node(const BVHRange &range,
BoundBox bounds;
if (do_unalinged_split) {
- bounds = unaligned_heuristic.compute_aligned_boundbox(
- range, &references->at(0), aligned_space);
+ bounds = unaligned_heuristic.compute_aligned_boundbox(range, &references.at(0), aligned_space);
}
else {
bounds = range.bounds();
@@ -770,24 +734,35 @@ BVHNode *BVHBuild::build_node(const BVHRange &range,
/* Local build. */
/* Build left node. */
- vector<BVHReference> copy(references->begin() + right.start(),
- references->begin() + right.end());
+ vector<BVHReference> right_references(references.begin() + right.start(),
+ references.begin() + right.end());
right.set_start(0);
- BVHNode *leftnode = build_node(left, references, level + 1, thread_id);
+ BVHNode *leftnode = build_node(left, references, level + 1, storage);
/* Build right node. */
- BVHNode *rightnode = build_node(right, &copy, level + 1, thread_id);
+ BVHNode *rightnode = build_node(right, right_references, level + 1, storage);
inner = new InnerNode(bounds, leftnode, rightnode);
}
else {
/* Threaded build. */
inner = new InnerNode(bounds);
- task_pool.push(new BVHSpatialSplitBuildTask(this, inner, 0, left, *references, level + 1),
- true);
- task_pool.push(new BVHSpatialSplitBuildTask(this, inner, 1, right, *references, level + 1),
- true);
+
+ vector<BVHReference> left_references(references.begin() + left.start(),
+ references.begin() + left.end());
+ vector<BVHReference> right_references(references.begin() + right.start(),
+ references.begin() + right.end());
+ right.set_start(0);
+
+ /* Create tasks for left and right nodes, using copy for most arguments and
+ * move for reference to avoid memory copies. */
+ task_pool.push([=, refs = std::move(left_references)]() mutable {
+ thread_build_spatial_split_node(inner, 0, left, refs, level + 1);
+ });
+ task_pool.push([=, refs = std::move(right_references)]() mutable {
+ thread_build_spatial_split_node(inner, 1, right, refs, level + 1);
+ });
}
if (do_unalinged_split) {
diff --git a/intern/cycles/bvh/bvh_build.h b/intern/cycles/bvh/bvh_build.h
index 3fe4c3799e2..c35af083fbd 100644
--- a/intern/cycles/bvh/bvh_build.h
+++ b/intern/cycles/bvh/bvh_build.h
@@ -74,9 +74,9 @@ class BVHBuild {
/* Building. */
BVHNode *build_node(const BVHRange &range,
- vector<BVHReference> *references,
+ vector<BVHReference> &references,
int level,
- int thread_id);
+ BVHSpatialStorage *storage);
BVHNode *build_node(const BVHObjectBinning &range, int level);
BVHNode *create_leaf_node(const BVHRange &range, const vector<BVHReference> &references);
BVHNode *create_object_leaf_nodes(const BVHReference *ref, int start, int num);
@@ -86,13 +86,12 @@ class BVHBuild {
/* Threads. */
enum { THREAD_TASK_SIZE = 4096 };
- void thread_build_node(InnerNode *node, int child, BVHObjectBinning *range, int level);
+ void thread_build_node(InnerNode *node, int child, const BVHObjectBinning &range, int level);
void thread_build_spatial_split_node(InnerNode *node,
int child,
- BVHRange *range,
- vector<BVHReference> *references,
- int level,
- int thread_id);
+ const BVHRange &range,
+ vector<BVHReference> &references,
+ int level);
thread_mutex build_mutex;
/* Progress. */
@@ -127,7 +126,7 @@ class BVHBuild {
/* Spatial splitting. */
float spatial_min_overlap;
- vector<BVHSpatialStorage> spatial_storage;
+ enumerable_thread_specific<BVHSpatialStorage> spatial_storage;
size_t spatial_free_index;
thread_spin_lock spatial_spin_lock;
diff --git a/intern/cycles/bvh/bvh_embree.cpp b/intern/cycles/bvh/bvh_embree.cpp
index 6735202835b..17e1f86a589 100644
--- a/intern/cycles/bvh/bvh_embree.cpp
+++ b/intern/cycles/bvh/bvh_embree.cpp
@@ -47,9 +47,11 @@
# include "render/hair.h"
# include "render/mesh.h"
# include "render/object.h"
+
# include "util/util_foreach.h"
# include "util/util_logging.h"
# include "util/util_progress.h"
+# include "util/util_stats.h"
CCL_NAMESPACE_BEGIN
@@ -65,30 +67,9 @@ static_assert(Object::MAX_MOTION_STEPS == Geometry::MAX_MOTION_STEPS,
* as well as filtering for volume objects happen here.
* Cycles' own BVH does that directly inside the traversal calls.
*/
-static void rtc_filter_func(const RTCFilterFunctionNArguments *args)
-{
- /* Current implementation in Cycles assumes only single-ray intersection queries. */
- assert(args->N == 1);
-
- const RTCRay *ray = (RTCRay *)args->ray;
- const RTCHit *hit = (RTCHit *)args->hit;
- CCLIntersectContext *ctx = ((IntersectContext *)args->context)->userRayExt;
- KernelGlobals *kg = ctx->kg;
-
- /* Check if there is backfacing hair to ignore. */
- if (IS_HAIR(hit->geomID) && (kernel_data.curve.curveflags & CURVE_KN_INTERPOLATE) &&
- !(kernel_data.curve.curveflags & CURVE_KN_BACKFACING) &&
- !(kernel_data.curve.curveflags & CURVE_KN_RIBBONS)) {
- if (dot(make_float3(ray->dir_x, ray->dir_y, ray->dir_z),
- make_float3(hit->Ng_x, hit->Ng_y, hit->Ng_z)) > 0.0f) {
- *args->valid = 0;
- return;
- }
- }
-}
-
static void rtc_filter_occluded_func(const RTCFilterFunctionNArguments *args)
{
+ /* Current implementation in Cycles assumes only single-ray intersection queries. */
assert(args->N == 1);
const RTCRay *ray = (RTCRay *)args->ray;
@@ -96,17 +77,6 @@ static void rtc_filter_occluded_func(const RTCFilterFunctionNArguments *args)
CCLIntersectContext *ctx = ((IntersectContext *)args->context)->userRayExt;
KernelGlobals *kg = ctx->kg;
- /* For all ray types: Check if there is backfacing hair to ignore */
- if (IS_HAIR(hit->geomID) && (kernel_data.curve.curveflags & CURVE_KN_INTERPOLATE) &&
- !(kernel_data.curve.curveflags & CURVE_KN_BACKFACING) &&
- !(kernel_data.curve.curveflags & CURVE_KN_RIBBONS)) {
- if (dot(make_float3(ray->dir_x, ray->dir_y, ray->dir_z),
- make_float3(hit->Ng_x, hit->Ng_y, hit->Ng_z)) > 0.0f) {
- *args->valid = 0;
- return;
- }
- }
-
switch (ctx->type) {
case CCLIntersectContext::RAY_SHADOW_ALL: {
/* Append the intersection to the end of the array. */
@@ -168,7 +138,7 @@ static void rtc_filter_occluded_func(const RTCFilterFunctionNArguments *args)
}
/* Ignore curves. */
- if (hit->geomID & 1) {
+ if (IS_HAIR(hit->geomID)) {
/* This tells Embree to continue tracing. */
*args->valid = 0;
break;
@@ -249,6 +219,34 @@ static void rtc_filter_occluded_func(const RTCFilterFunctionNArguments *args)
}
}
+static void rtc_filter_func_thick_curve(const RTCFilterFunctionNArguments *args)
+{
+ const RTCRay *ray = (RTCRay *)args->ray;
+ RTCHit *hit = (RTCHit *)args->hit;
+
+ /* Always ignore backfacing intersections. */
+ if (dot(make_float3(ray->dir_x, ray->dir_y, ray->dir_z),
+ make_float3(hit->Ng_x, hit->Ng_y, hit->Ng_z)) > 0.0f) {
+ *args->valid = 0;
+ return;
+ }
+}
+
+static void rtc_filter_occluded_func_thick_curve(const RTCFilterFunctionNArguments *args)
+{
+ const RTCRay *ray = (RTCRay *)args->ray;
+ RTCHit *hit = (RTCHit *)args->hit;
+
+ /* Always ignore backfacing intersections. */
+ if (dot(make_float3(ray->dir_x, ray->dir_y, ray->dir_z),
+ make_float3(hit->Ng_x, hit->Ng_y, hit->Ng_z)) > 0.0f) {
+ *args->valid = 0;
+ return;
+ }
+
+ rtc_filter_occluded_func(args);
+}
+
static size_t unaccounted_mem = 0;
static bool rtc_memory_monitor_func(void *userPtr, const ssize_t bytes, const bool)
@@ -326,8 +324,6 @@ BVHEmbree::BVHEmbree(const BVHParams &params_,
stats(NULL),
curve_subdivisions(params.curve_subdivisions),
build_quality(RTC_BUILD_QUALITY_REFIT),
- use_curves(params_.curve_flags & CURVE_KN_INTERPOLATE),
- use_ribbons(params.curve_flags & CURVE_KN_RIBBONS),
dynamic_scene(true)
{
_MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
@@ -653,7 +649,6 @@ void BVHEmbree::add_triangles(const Object *ob, const Mesh *mesh, int i)
}
rtcSetGeometryUserData(geom_id, (void *)prim_offset);
- rtcSetGeometryIntersectFilterFunction(geom_id, rtc_filter_func);
rtcSetGeometryOccludedFilterFunction(geom_id, rtc_filter_occluded_func);
rtcSetGeometryMask(geom_id, ob->visibility_for_tracing());
@@ -724,9 +719,7 @@ void BVHEmbree::update_curve_vertex_buffer(RTCGeometry geom_id, const Hair *hair
/* Catmull-Rom splines need extra CVs at the beginning and end of each curve. */
size_t num_keys_embree = num_keys;
- if (use_curves) {
- num_keys_embree += num_curves * 2;
- }
+ num_keys_embree += num_curves * 2;
/* Copy the CV data to Embree */
const int t_mid = (num_motion_steps - 1) / 2;
@@ -746,45 +739,22 @@ void BVHEmbree::update_curve_vertex_buffer(RTCGeometry geom_id, const Hair *hair
assert(rtc_verts);
if (rtc_verts) {
- if (use_curves) {
- const size_t num_curves = hair->num_curves();
- for (size_t j = 0; j < num_curves; ++j) {
- Hair::Curve c = hair->get_curve(j);
- int fk = c.first_key;
- int k = 1;
- for (; k < c.num_keys + 1; ++k, ++fk) {
- rtc_verts[k] = float3_to_float4(verts[fk]);
- rtc_verts[k].w = curve_radius[fk];
- }
- /* Duplicate Embree's Catmull-Rom spline CVs at the start and end of each curve. */
- rtc_verts[0] = rtc_verts[1];
- rtc_verts[k] = rtc_verts[k - 1];
- rtc_verts += c.num_keys + 2;
- }
- }
- else {
- for (size_t j = 0; j < num_keys_embree; ++j) {
- rtc_verts[j] = float3_to_float4(verts[j]);
- rtc_verts[j].w = curve_radius[j];
+ const size_t num_curves = hair->num_curves();
+ for (size_t j = 0; j < num_curves; ++j) {
+ Hair::Curve c = hair->get_curve(j);
+ int fk = c.first_key;
+ int k = 1;
+ for (; k < c.num_keys + 1; ++k, ++fk) {
+ rtc_verts[k] = float3_to_float4(verts[fk]);
+ rtc_verts[k].w = curve_radius[fk];
}
+ /* Duplicate Embree's Catmull-Rom spline CVs at the start and end of each curve. */
+ rtc_verts[0] = rtc_verts[1];
+ rtc_verts[k] = rtc_verts[k - 1];
+ rtc_verts += c.num_keys + 2;
}
}
}
-# if RTC_VERSION >= 30900
- if (!use_curves) {
- unsigned char *flags = (unsigned char *)rtcSetNewGeometryBuffer(geom_id,
- RTC_BUFFER_TYPE_FLAGS,
- 0,
- RTC_FORMAT_UCHAR,
- sizeof(unsigned char),
- num_keys_embree);
- flags[0] = RTC_CURVE_FLAG_NEIGHBOR_RIGHT;
- ::memset(flags + 1,
- RTC_CURVE_FLAG_NEIGHBOR_RIGHT | RTC_CURVE_FLAG_NEIGHBOR_RIGHT,
- num_keys_embree - 2);
- flags[num_keys_embree - 1] = RTC_CURVE_FLAG_NEIGHBOR_LEFT;
- }
-# endif
}
void BVHEmbree::add_curves(const Object *ob, const Hair *hair, int i)
@@ -800,6 +770,12 @@ void BVHEmbree::add_curves(const Object *ob, const Hair *hair, int i)
}
const size_t num_motion_steps = min(num_geometry_motion_steps, RTC_MAX_TIME_STEP_COUNT);
+ const PrimitiveType primitive_type =
+ (num_motion_steps > 1) ?
+ ((hair->curve_shape == CURVE_RIBBON) ? PRIMITIVE_MOTION_CURVE_RIBBON :
+ PRIMITIVE_MOTION_CURVE_THICK) :
+ ((hair->curve_shape == CURVE_RIBBON) ? PRIMITIVE_CURVE_RIBBON : PRIMITIVE_CURVE_THICK);
+
assert(num_geometry_motion_steps <= RTC_MAX_TIME_STEP_COUNT);
const size_t num_curves = hair->num_curves();
@@ -820,21 +796,12 @@ void BVHEmbree::add_curves(const Object *ob, const Hair *hair, int i)
size_t prim_tri_index_size = pack.prim_index.size();
pack.prim_tri_index.resize(prim_tri_index_size + num_segments);
-# if RTC_VERSION >= 30900
- enum RTCGeometryType type = (!use_curves) ?
- (use_ribbons ? RTC_GEOMETRY_TYPE_FLAT_LINEAR_CURVE :
- RTC_GEOMETRY_TYPE_ROUND_LINEAR_CURVE) :
- (use_ribbons ? RTC_GEOMETRY_TYPE_FLAT_CATMULL_ROM_CURVE :
- RTC_GEOMETRY_TYPE_ROUND_CATMULL_ROM_CURVE);
-# else
- enum RTCGeometryType type = (!use_curves) ?
- RTC_GEOMETRY_TYPE_FLAT_LINEAR_CURVE :
- (use_ribbons ? RTC_GEOMETRY_TYPE_FLAT_CATMULL_ROM_CURVE :
- RTC_GEOMETRY_TYPE_ROUND_CATMULL_ROM_CURVE);
-# endif
+ enum RTCGeometryType type = (hair->curve_shape == CURVE_RIBBON ?
+ RTC_GEOMETRY_TYPE_FLAT_CATMULL_ROM_CURVE :
+ RTC_GEOMETRY_TYPE_ROUND_CATMULL_ROM_CURVE);
RTCGeometry geom_id = rtcNewGeometry(rtc_shared_device, type);
- rtcSetGeometryTessellationRate(geom_id, curve_subdivisions);
+ rtcSetGeometryTessellationRate(geom_id, curve_subdivisions + 1);
unsigned *rtc_indices = (unsigned *)rtcSetNewGeometryBuffer(
geom_id, RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT, sizeof(int), num_segments);
size_t rtc_index = 0;
@@ -842,14 +809,11 @@ void BVHEmbree::add_curves(const Object *ob, const Hair *hair, int i)
Hair::Curve c = hair->get_curve(j);
for (size_t k = 0; k < c.num_segments(); ++k) {
rtc_indices[rtc_index] = c.first_key + k;
- if (use_curves) {
- /* Room for extra CVs at Catmull-Rom splines. */
- rtc_indices[rtc_index] += j * 2;
- }
+ /* Room for extra CVs at Catmull-Rom splines. */
+ rtc_indices[rtc_index] += j * 2;
/* Cycles specific data. */
pack.prim_object[prim_object_size + rtc_index] = i;
- pack.prim_type[prim_type_size + rtc_index] = (PRIMITIVE_PACK_SEGMENT(
- num_motion_steps > 1 ? PRIMITIVE_MOTION_CURVE : PRIMITIVE_CURVE, k));
+ pack.prim_type[prim_type_size + rtc_index] = (PRIMITIVE_PACK_SEGMENT(primitive_type, k));
pack.prim_index[prim_index_size + rtc_index] = j;
pack.prim_tri_index[prim_tri_index_size + rtc_index] = rtc_index;
@@ -863,8 +827,13 @@ void BVHEmbree::add_curves(const Object *ob, const Hair *hair, int i)
update_curve_vertex_buffer(geom_id, hair);
rtcSetGeometryUserData(geom_id, (void *)prim_offset);
- rtcSetGeometryIntersectFilterFunction(geom_id, rtc_filter_func);
- rtcSetGeometryOccludedFilterFunction(geom_id, rtc_filter_occluded_func);
+ if (hair->curve_shape == CURVE_RIBBON) {
+ rtcSetGeometryOccludedFilterFunction(geom_id, rtc_filter_occluded_func);
+ }
+ else {
+ rtcSetGeometryIntersectFilterFunction(geom_id, rtc_filter_func_thick_curve);
+ rtcSetGeometryOccludedFilterFunction(geom_id, rtc_filter_occluded_func_thick_curve);
+ }
rtcSetGeometryMask(geom_id, ob->visibility_for_tracing());
rtcCommitGeometry(geom_id);
diff --git a/intern/cycles/bvh/bvh_embree.h b/intern/cycles/bvh/bvh_embree.h
index eb121d060b7..f60a1ca0102 100644
--- a/intern/cycles/bvh/bvh_embree.h
+++ b/intern/cycles/bvh/bvh_embree.h
@@ -81,7 +81,7 @@ class BVHEmbree : public BVH {
vector<RTCScene> delayed_delete_scenes;
int curve_subdivisions;
enum RTCBuildQuality build_quality;
- bool use_curves, use_ribbons, dynamic_scene;
+ bool dynamic_scene;
};
CCL_NAMESPACE_END
diff --git a/intern/cycles/bvh/bvh_optix.cpp b/intern/cycles/bvh/bvh_optix.cpp
index 740994b2ebc..ccb7ae08625 100644
--- a/intern/cycles/bvh/bvh_optix.cpp
+++ b/intern/cycles/bvh/bvh_optix.cpp
@@ -18,10 +18,14 @@
#ifdef WITH_OPTIX
# include "bvh/bvh_optix.h"
+
+# include "device/device.h"
+
# include "render/geometry.h"
# include "render/hair.h"
# include "render/mesh.h"
# include "render/object.h"
+
# include "util/util_foreach.h"
# include "util/util_logging.h"
# include "util/util_progress.h"
@@ -73,9 +77,12 @@ void BVHOptiX::pack_blas()
// 'pack.prim_time' is only used in geom_curve_intersect.h
// It is not needed because of OPTIX_MOTION_FLAG_[START|END]_VANISH
- uint type = PRIMITIVE_CURVE;
- if (hair->use_motion_blur && hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION))
- type = PRIMITIVE_MOTION_CURVE;
+ uint type = (hair->use_motion_blur &&
+ hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION)) ?
+ ((hair->curve_shape == CURVE_RIBBON) ? PRIMITIVE_MOTION_CURVE_RIBBON :
+ PRIMITIVE_MOTION_CURVE_THICK) :
+ ((hair->curve_shape == CURVE_RIBBON) ? PRIMITIVE_CURVE_RIBBON :
+ PRIMITIVE_CURVE_THICK);
for (size_t j = 0; j < num_curves; ++j) {
const Hair::Curve curve = hair->get_curve(j);
diff --git a/intern/cycles/bvh/bvh_params.h b/intern/cycles/bvh/bvh_params.h
index 5e2c4b63f1b..1a50742dc33 100644
--- a/intern/cycles/bvh/bvh_params.h
+++ b/intern/cycles/bvh/bvh_params.h
@@ -89,7 +89,6 @@ class BVHParams {
int bvh_type;
/* These are needed for Embree. */
- int curve_flags;
int curve_subdivisions;
/* fixed parameters */
@@ -122,7 +121,6 @@ class BVHParams {
bvh_type = 0;
- curve_flags = 0;
curve_subdivisions = 4;
}
diff --git a/intern/cycles/bvh/bvh_sort.cpp b/intern/cycles/bvh/bvh_sort.cpp
index 4498a759c08..b01785b547a 100644
--- a/intern/cycles/bvh/bvh_sort.cpp
+++ b/intern/cycles/bvh/bvh_sort.cpp
@@ -88,18 +88,6 @@ static void bvh_reference_sort_threaded(TaskPool *task_pool,
const int job_end,
const BVHReferenceCompare &compare);
-class BVHSortTask : public Task {
- public:
- BVHSortTask(TaskPool *task_pool,
- BVHReference *data,
- const int job_start,
- const int job_end,
- const BVHReferenceCompare &compare)
- {
- run = function_bind(bvh_reference_sort_threaded, task_pool, data, job_start, job_end, compare);
- }
-};
-
/* Multi-threaded reference sort. */
static void bvh_reference_sort_threaded(TaskPool *task_pool,
BVHReference *data,
@@ -158,7 +146,8 @@ static void bvh_reference_sort_threaded(TaskPool *task_pool,
have_work = false;
if (left < end) {
if (start < right) {
- task_pool->push(new BVHSortTask(task_pool, data, left, end, compare), true);
+ task_pool->push(
+ function_bind(bvh_reference_sort_threaded, task_pool, data, left, end, compare));
}
else {
start = left;
diff --git a/intern/cycles/bvh/bvh_split.cpp b/intern/cycles/bvh/bvh_split.cpp
index acdca0f13ad..4b21f852d7a 100644
--- a/intern/cycles/bvh/bvh_split.cpp
+++ b/intern/cycles/bvh/bvh_split.cpp
@@ -33,7 +33,7 @@ CCL_NAMESPACE_BEGIN
BVHObjectSplit::BVHObjectSplit(BVHBuild *builder,
BVHSpatialStorage *storage,
const BVHRange &range,
- vector<BVHReference> *references,
+ vector<BVHReference> &references,
float nodeSAH,
const BVHUnaligned *unaligned_heuristic,
const Transform *aligned_space)
@@ -43,7 +43,7 @@ BVHObjectSplit::BVHObjectSplit(BVHBuild *builder,
left_bounds(BoundBox::empty),
right_bounds(BoundBox::empty),
storage_(storage),
- references_(references),
+ references_(&references),
unaligned_heuristic_(unaligned_heuristic),
aligned_space_(aligned_space)
{
@@ -133,7 +133,7 @@ void BVHObjectSplit::split(BVHRange &left, BVHRange &right, const BVHRange &rang
BVHSpatialSplit::BVHSpatialSplit(const BVHBuild &builder,
BVHSpatialStorage *storage,
const BVHRange &range,
- vector<BVHReference> *references,
+ vector<BVHReference> &references,
float nodeSAH,
const BVHUnaligned *unaligned_heuristic,
const Transform *aligned_space)
@@ -141,7 +141,7 @@ BVHSpatialSplit::BVHSpatialSplit(const BVHBuild &builder,
dim(0),
pos(0.0f),
storage_(storage),
- references_(references),
+ references_(&references),
unaligned_heuristic_(unaligned_heuristic),
aligned_space_(aligned_space)
{
@@ -152,7 +152,7 @@ BVHSpatialSplit::BVHSpatialSplit(const BVHBuild &builder,
}
else {
range_bounds = unaligned_heuristic->compute_aligned_boundbox(
- range, &references->at(0), *aligned_space);
+ range, &references_->at(0), *aligned_space);
}
float3 origin = range_bounds.min;
diff --git a/intern/cycles/bvh/bvh_split.h b/intern/cycles/bvh/bvh_split.h
index 5f2e41cf343..28ff0e05fc3 100644
--- a/intern/cycles/bvh/bvh_split.h
+++ b/intern/cycles/bvh/bvh_split.h
@@ -44,7 +44,7 @@ class BVHObjectSplit {
BVHObjectSplit(BVHBuild *builder,
BVHSpatialStorage *storage,
const BVHRange &range,
- vector<BVHReference> *references,
+ vector<BVHReference> &references,
float nodeSAH,
const BVHUnaligned *unaligned_heuristic = NULL,
const Transform *aligned_space = NULL);
@@ -82,7 +82,7 @@ class BVHSpatialSplit {
BVHSpatialSplit(const BVHBuild &builder,
BVHSpatialStorage *storage,
const BVHRange &range,
- vector<BVHReference> *references,
+ vector<BVHReference> &references,
float nodeSAH,
const BVHUnaligned *unaligned_heuristic = NULL,
const Transform *aligned_space = NULL);
@@ -187,7 +187,7 @@ class BVHMixedSplit {
__forceinline BVHMixedSplit(BVHBuild *builder,
BVHSpatialStorage *storage,
const BVHRange &range,
- vector<BVHReference> *references,
+ vector<BVHReference> &references,
int level,
const BVHUnaligned *unaligned_heuristic = NULL,
const Transform *aligned_space = NULL)
@@ -197,7 +197,7 @@ class BVHMixedSplit {
}
else {
bounds = unaligned_heuristic->compute_aligned_boundbox(
- range, &references->at(0), *aligned_space);
+ range, &references.at(0), *aligned_space);
}
/* find split candidates. */
float area = bounds.safe_area();
@@ -220,7 +220,7 @@ class BVHMixedSplit {
/* leaf SAH is the lowest => create leaf. */
minSAH = min(min(leafSAH, object.sah), spatial.sah);
- no_split = (minSAH == leafSAH && builder->range_within_max_leaf_size(range, *references));
+ no_split = (minSAH == leafSAH && builder->range_within_max_leaf_size(range, references));
}
__forceinline void split(BVHBuild *builder,
diff --git a/intern/cycles/bvh/bvh_unaligned.cpp b/intern/cycles/bvh/bvh_unaligned.cpp
index f0995f343fe..c969b361643 100644
--- a/intern/cycles/bvh/bvh_unaligned.cpp
+++ b/intern/cycles/bvh/bvh_unaligned.cpp
@@ -68,7 +68,8 @@ bool BVHUnaligned::compute_aligned_space(const BVHReference &ref, Transform *ali
const Object *object = objects_[ref.prim_object()];
const int packed_type = ref.prim_type();
const int type = (packed_type & PRIMITIVE_ALL);
- if (type & PRIMITIVE_CURVE) {
+ /* No motion blur curves here, we can't fit them to aligned boxes well. */
+ if (type & (PRIMITIVE_CURVE_RIBBON | PRIMITIVE_CURVE_THICK)) {
const int curve_index = ref.prim_index();
const int segment = PRIMITIVE_UNPACK_SEGMENT(packed_type);
const Hair *hair = static_cast<const Hair *>(object->geometry);
@@ -93,7 +94,8 @@ BoundBox BVHUnaligned::compute_aligned_prim_boundbox(const BVHReference &prim,
const Object *object = objects_[prim.prim_object()];
const int packed_type = prim.prim_type();
const int type = (packed_type & PRIMITIVE_ALL);
- if (type & PRIMITIVE_CURVE) {
+ /* No motion blur curves here, we can't fit them to aligned boxes well. */
+ if (type & (PRIMITIVE_CURVE_RIBBON | PRIMITIVE_CURVE_THICK)) {
const int curve_index = prim.prim_index();
const int segment = PRIMITIVE_UNPACK_SEGMENT(packed_type);
const Hair *hair = static_cast<const Hair *>(object->geometry);
diff --git a/intern/cycles/device/CMakeLists.txt b/intern/cycles/device/CMakeLists.txt
index aa5b65a2b73..ca366722eb7 100644
--- a/intern/cycles/device/CMakeLists.txt
+++ b/intern/cycles/device/CMakeLists.txt
@@ -99,6 +99,18 @@ if(WITH_CYCLES_DEVICE_MULTI)
add_definitions(-DWITH_MULTI)
endif()
+if(WITH_OPENIMAGEDENOISE)
+ add_definitions(-DWITH_OPENIMAGEDENOISE)
+ add_definitions(-DOIDN_STATIC_LIB)
+ list(APPEND INC_SYS
+ ${OPENIMAGEDENOISE_INCLUDE_DIRS}
+ )
+ list(APPEND LIB
+ ${OPENIMAGEDENOISE_LIBRARIES}
+ ${TBB_LIBRARIES}
+ )
+endif()
+
include_directories(${INC})
include_directories(SYSTEM ${INC_SYS})
diff --git a/intern/cycles/device/cuda/device_cuda.h b/intern/cycles/device/cuda/device_cuda.h
index 1aa2fdd0967..e5e3e24165d 100644
--- a/intern/cycles/device/cuda/device_cuda.h
+++ b/intern/cycles/device/cuda/device_cuda.h
@@ -21,6 +21,7 @@
# include "device/device_split_kernel.h"
# include "util/util_map.h"
+# include "util/util_task.h"
# ifdef WITH_CUDA_DYNLOAD
# include "cuew.h"
@@ -96,9 +97,9 @@ class CUDADevice : public Device {
static bool have_precompiled_kernels();
- virtual bool show_samples() const;
+ virtual bool show_samples() const override;
- virtual BVHLayoutMask get_bvh_layout_mask() const;
+ virtual BVHLayoutMask get_bvh_layout_mask() const override;
void set_error(const string &error) override;
@@ -108,7 +109,7 @@ class CUDADevice : public Device {
bool support_device(const DeviceRequestedFeatures & /*requested_features*/);
- bool check_peer_access(Device *peer_device);
+ bool check_peer_access(Device *peer_device) override;
bool use_adaptive_compilation();
@@ -122,7 +123,7 @@ class CUDADevice : public Device {
const char *base = "cuda",
bool force_ptx = false);
- virtual bool load_kernels(const DeviceRequestedFeatures &requested_features);
+ virtual bool load_kernels(const DeviceRequestedFeatures &requested_features) override;
void load_functions();
@@ -140,19 +141,19 @@ class CUDADevice : public Device {
void generic_free(device_memory &mem);
- void mem_alloc(device_memory &mem);
+ void mem_alloc(device_memory &mem) override;
- void mem_copy_to(device_memory &mem);
+ void mem_copy_to(device_memory &mem) override;
- void mem_copy_from(device_memory &mem, int y, int w, int h, int elem);
+ void mem_copy_from(device_memory &mem, int y, int w, int h, int elem) override;
- void mem_zero(device_memory &mem);
+ void mem_zero(device_memory &mem) override;
- void mem_free(device_memory &mem);
+ void mem_free(device_memory &mem) override;
- device_ptr mem_alloc_sub_ptr(device_memory &mem, int offset, int /*size*/);
+ device_ptr mem_alloc_sub_ptr(device_memory &mem, int offset, int /*size*/) override;
- virtual void const_copy_to(const char *name, void *host, size_t size);
+ virtual void const_copy_to(const char *name, void *host, size_t size) override;
void global_alloc(device_memory &mem);
@@ -252,15 +253,15 @@ class CUDADevice : public Device {
int dw,
int dh,
bool transparent,
- const DeviceDrawParams &draw_params);
+ const DeviceDrawParams &draw_params) override;
- void thread_run(DeviceTask *task);
+ void thread_run(DeviceTask &task);
- virtual void task_add(DeviceTask &task);
+ virtual void task_add(DeviceTask &task) override;
- virtual void task_wait();
+ virtual void task_wait() override;
- virtual void task_cancel();
+ virtual void task_cancel() override;
};
CCL_NAMESPACE_END
diff --git a/intern/cycles/device/cuda/device_cuda_impl.cpp b/intern/cycles/device/cuda/device_cuda_impl.cpp
index 7aa63ff48c3..b9bbeb9a25b 100644
--- a/intern/cycles/device/cuda/device_cuda_impl.cpp
+++ b/intern/cycles/device/cuda/device_cuda_impl.cpp
@@ -105,7 +105,7 @@ class CUDASplitKernel : public DeviceSplitKernel {
virtual SplitKernelFunction *get_split_kernel_function(const string &kernel_name,
const DeviceRequestedFeatures &);
virtual int2 split_kernel_local_size();
- virtual int2 split_kernel_global_size(device_memory &kg, device_memory &data, DeviceTask *task);
+ virtual int2 split_kernel_global_size(device_memory &kg, device_memory &data, DeviceTask &task);
};
/* Utility to push/pop CUDA context. */
@@ -243,7 +243,7 @@ CUDADevice::CUDADevice(DeviceInfo &info, Stats &stats, Profiler &profiler, bool
CUDADevice::~CUDADevice()
{
- task_pool.stop();
+ task_pool.cancel();
delete split_kernel;
@@ -2326,11 +2326,11 @@ void CUDADevice::draw_pixels(device_memory &mem,
Device::draw_pixels(mem, y, w, h, width, height, dx, dy, dw, dh, transparent, draw_params);
}
-void CUDADevice::thread_run(DeviceTask *task)
+void CUDADevice::thread_run(DeviceTask &task)
{
CUDAContextScope scope(this);
- if (task->type == DeviceTask::RENDER) {
+ if (task.type == DeviceTask::RENDER) {
DeviceRequestedFeatures requested_features;
if (use_split_kernel()) {
if (split_kernel == NULL) {
@@ -2343,72 +2343,64 @@ void CUDADevice::thread_run(DeviceTask *task)
/* keep rendering tiles until done */
RenderTile tile;
- DenoisingTask denoising(this, *task);
+ DenoisingTask denoising(this, task);
- while (task->acquire_tile(this, tile, task->tile_types)) {
+ while (task.acquire_tile(this, tile, task.tile_types)) {
if (tile.task == RenderTile::PATH_TRACE) {
if (use_split_kernel()) {
device_only_memory<uchar> void_buffer(this, "void_buffer");
split_kernel->path_trace(task, tile, void_buffer, void_buffer);
}
else {
- render(*task, tile, work_tiles);
+ render(task, tile, work_tiles);
}
}
else if (tile.task == RenderTile::BAKE) {
- render(*task, tile, work_tiles);
+ render(task, tile, work_tiles);
}
else if (tile.task == RenderTile::DENOISE) {
tile.sample = tile.start_sample + tile.num_samples;
denoise(tile, denoising);
- task->update_progress(&tile, tile.w * tile.h);
+ task.update_progress(&tile, tile.w * tile.h);
}
- task->release_tile(tile);
+ task.release_tile(tile);
- if (task->get_cancel()) {
- if (task->need_finish_queue == false)
+ if (task.get_cancel()) {
+ if (task.need_finish_queue == false)
break;
}
}
work_tiles.free();
}
- else if (task->type == DeviceTask::SHADER) {
- shader(*task);
+ else if (task.type == DeviceTask::SHADER) {
+ shader(task);
cuda_assert(cuCtxSynchronize());
}
- else if (task->type == DeviceTask::DENOISE_BUFFER) {
+ else if (task.type == DeviceTask::DENOISE_BUFFER) {
RenderTile tile;
- tile.x = task->x;
- tile.y = task->y;
- tile.w = task->w;
- tile.h = task->h;
- tile.buffer = task->buffer;
- tile.sample = task->sample + task->num_samples;
- tile.num_samples = task->num_samples;
- tile.start_sample = task->sample;
- tile.offset = task->offset;
- tile.stride = task->stride;
- tile.buffers = task->buffers;
-
- DenoisingTask denoising(this, *task);
+ tile.x = task.x;
+ tile.y = task.y;
+ tile.w = task.w;
+ tile.h = task.h;
+ tile.buffer = task.buffer;
+ tile.sample = task.sample + task.num_samples;
+ tile.num_samples = task.num_samples;
+ tile.start_sample = task.sample;
+ tile.offset = task.offset;
+ tile.stride = task.stride;
+ tile.buffers = task.buffers;
+
+ DenoisingTask denoising(this, task);
denoise(tile, denoising);
- task->update_progress(&tile, tile.w * tile.h);
+ task.update_progress(&tile, tile.w * tile.h);
}
}
-class CUDADeviceTask : public DeviceTask {
- public:
- CUDADeviceTask(CUDADevice *device, DeviceTask &task) : DeviceTask(task)
- {
- run = function_bind(&CUDADevice::thread_run, device, this);
- }
-};
-
void CUDADevice::task_add(DeviceTask &task)
{
CUDAContextScope scope(this);
@@ -2424,7 +2416,10 @@ void CUDADevice::task_add(DeviceTask &task)
film_convert(task, task.buffer, task.rgba_byte, task.rgba_half);
}
else {
- task_pool.push(new CUDADeviceTask(this, task));
+ task_pool.push([=] {
+ DeviceTask task_copy = task;
+ thread_run(task_copy);
+ });
}
}
@@ -2652,7 +2647,7 @@ int2 CUDASplitKernel::split_kernel_local_size()
int2 CUDASplitKernel::split_kernel_global_size(device_memory &kg,
device_memory &data,
- DeviceTask * /*task*/)
+ DeviceTask & /*task*/)
{
CUDAContextScope scope(device);
size_t free;
diff --git a/intern/cycles/device/device.cpp b/intern/cycles/device/device.cpp
index 41dd7894d93..9dbb33980b4 100644
--- a/intern/cycles/device/device.cpp
+++ b/intern/cycles/device/device.cpp
@@ -77,7 +77,7 @@ std::ostream &operator<<(std::ostream &os, const DeviceRequestedFeatures &reques
/* Device */
-Device::~Device()
+Device::~Device() noexcept(false)
{
if (!background) {
if (vertex_buffer != 0) {
@@ -603,6 +603,7 @@ DeviceInfo Device::get_multi_device(const vector<DeviceInfo> &subdevices,
info.has_osl = true;
info.has_profiling = true;
info.has_peer_memory = false;
+ info.denoisers = DENOISER_ALL;
foreach (const DeviceInfo &device, subdevices) {
/* Ensure CPU device does not slow down GPU. */
@@ -647,6 +648,7 @@ DeviceInfo Device::get_multi_device(const vector<DeviceInfo> &subdevices,
info.has_osl &= device.has_osl;
info.has_profiling &= device.has_profiling;
info.has_peer_memory |= device.has_peer_memory;
+ info.denoisers &= device.denoisers;
}
return info;
@@ -667,4 +669,55 @@ void Device::free_memory()
network_devices.free_memory();
}
+/* DeviceInfo */
+
+void DeviceInfo::add_denoising_devices(DenoiserType denoiser_type)
+{
+ assert(denoising_devices.empty());
+
+ if (denoiser_type == DENOISER_OPTIX && type != DEVICE_OPTIX) {
+ vector<DeviceInfo> optix_devices = Device::available_devices(DEVICE_MASK_OPTIX);
+ if (!optix_devices.empty()) {
+ /* Convert to a special multi device with separate denoising devices. */
+ if (multi_devices.empty()) {
+ multi_devices.push_back(*this);
+ }
+
+ /* Try to use the same physical devices for denoising. */
+ for (const DeviceInfo &cuda_device : multi_devices) {
+ if (cuda_device.type == DEVICE_CUDA) {
+ for (const DeviceInfo &optix_device : optix_devices) {
+ if (cuda_device.num == optix_device.num) {
+ id += optix_device.id;
+ denoising_devices.push_back(optix_device);
+ break;
+ }
+ }
+ }
+ }
+
+ if (denoising_devices.empty()) {
+ /* Simply use the first available OptiX device. */
+ const DeviceInfo optix_device = optix_devices.front();
+ id += optix_device.id; /* Uniquely identify this special multi device. */
+ denoising_devices.push_back(optix_device);
+ }
+
+ denoisers = denoiser_type;
+ }
+ }
+ else if (denoiser_type == DENOISER_OPENIMAGEDENOISE && type != DEVICE_CPU) {
+ /* Convert to a special multi device with separate denoising devices. */
+ if (multi_devices.empty()) {
+ multi_devices.push_back(*this);
+ }
+
+ /* Add CPU denoising devices. */
+ DeviceInfo cpu_device = Device::available_devices(DEVICE_MASK_CPU).front();
+ denoising_devices.push_back(cpu_device);
+
+ denoisers = denoiser_type;
+ }
+}
+
CCL_NAMESPACE_END
diff --git a/intern/cycles/device/device.h b/intern/cycles/device/device.h
index dff981080a5..a5833369a17 100644
--- a/intern/cycles/device/device.h
+++ b/intern/cycles/device/device.h
@@ -83,6 +83,7 @@ class DeviceInfo {
bool use_split_kernel; /* Use split or mega kernel. */
bool has_profiling; /* Supports runtime collection of profiling info. */
bool has_peer_memory; /* GPU has P2P access to memory of another GPU. */
+ DenoiserTypeMask denoisers; /* Supported denoiser types. */
int cpu_threads;
vector<DeviceInfo> multi_devices;
vector<DeviceInfo> denoising_devices;
@@ -101,6 +102,7 @@ class DeviceInfo {
use_split_kernel = false;
has_profiling = false;
has_peer_memory = false;
+ denoisers = DENOISER_NONE;
}
bool operator==(const DeviceInfo &info)
@@ -110,6 +112,9 @@ class DeviceInfo {
(type == info.type && num == info.num && description == info.description));
return id == info.id;
}
+
+ /* Add additional devices needed for the specified denoiser. */
+ void add_denoising_devices(DenoiserType denoiser_type);
};
class DeviceRequestedFeatures {
@@ -132,6 +137,7 @@ class DeviceRequestedFeatures {
/* BVH/sampling kernel features. */
bool use_hair;
+ bool use_hair_thick;
bool use_object_motion;
bool use_camera_motion;
@@ -178,6 +184,7 @@ class DeviceRequestedFeatures {
max_nodes_group = 0;
nodes_features = 0;
use_hair = false;
+ use_hair_thick = false;
use_object_motion = false;
use_camera_motion = false;
use_baking = false;
@@ -200,6 +207,7 @@ class DeviceRequestedFeatures {
max_nodes_group == requested_features.max_nodes_group &&
nodes_features == requested_features.nodes_features &&
use_hair == requested_features.use_hair &&
+ use_hair_thick == requested_features.use_hair_thick &&
use_object_motion == requested_features.use_object_motion &&
use_camera_motion == requested_features.use_camera_motion &&
use_baking == requested_features.use_baking &&
@@ -319,7 +327,8 @@ class Device {
virtual void mem_free_sub_ptr(device_ptr /*ptr*/){};
public:
- virtual ~Device();
+ /* noexcept needed to silence TBB warning. */
+ virtual ~Device() noexcept(false);
/* info */
DeviceInfo info;
diff --git a/intern/cycles/device/device_cpu.cpp b/intern/cycles/device/device_cpu.cpp
index fc6febd8cee..8f68e66a1b4 100644
--- a/intern/cycles/device/device_cpu.cpp
+++ b/intern/cycles/device/device_cpu.cpp
@@ -51,10 +51,12 @@
#include "util/util_function.h"
#include "util/util_logging.h"
#include "util/util_map.h"
+#include "util/util_openimagedenoise.h"
#include "util/util_opengl.h"
#include "util/util_optimization.h"
#include "util/util_progress.h"
#include "util/util_system.h"
+#include "util/util_task.h"
#include "util/util_thread.h"
CCL_NAMESPACE_BEGIN
@@ -161,7 +163,7 @@ class CPUSplitKernel : public DeviceSplitKernel {
virtual SplitKernelFunction *get_split_kernel_function(const string &kernel_name,
const DeviceRequestedFeatures &);
virtual int2 split_kernel_local_size();
- virtual int2 split_kernel_global_size(device_memory &kg, device_memory &data, DeviceTask *task);
+ virtual int2 split_kernel_global_size(device_memory &kg, device_memory &data, DeviceTask &task);
virtual uint64_t state_buffer_size(device_memory &kg, device_memory &data, size_t num_threads);
};
@@ -176,6 +178,10 @@ class CPUDevice : public Device {
#ifdef WITH_OSL
OSLGlobals osl_globals;
#endif
+#ifdef WITH_OPENIMAGEDENOISE
+ oidn::DeviceRef oidn_device;
+ oidn::FilterRef oidn_filter;
+#endif
bool use_split_kernel;
@@ -332,7 +338,7 @@ class CPUDevice : public Device {
~CPUDevice()
{
- task_pool.stop();
+ task_pool.cancel();
texture_info.free();
}
@@ -344,17 +350,6 @@ class CPUDevice : public Device {
virtual BVHLayoutMask get_bvh_layout_mask() const
{
BVHLayoutMask bvh_layout_mask = BVH_LAYOUT_BVH2;
- if (DebugFlags().cpu.has_sse2() && system_cpu_support_sse2()) {
- bvh_layout_mask |= BVH_LAYOUT_BVH4;
- }
- /* MSVC does not support the -march=native switch and you always end up */
- /* with an sse2 kernel when you use WITH_KERNEL_NATIVE. We *cannot* feed */
- /* that kernel BVH8 even if the CPU flags would allow for it. */
-#if (defined(__x86_64__) || defined(_M_X64)) && !(defined(_MSC_VER) && defined(WITH_KERNEL_NATIVE))
- if (DebugFlags().cpu.has_avx2() && system_cpu_support_avx2()) {
- bvh_layout_mask |= BVH_LAYOUT_BVH8;
- }
-#endif
#ifdef WITH_EMBREE
bvh_layout_mask |= BVH_LAYOUT_EMBREE;
#endif /* WITH_EMBREE */
@@ -527,26 +522,18 @@ class CPUDevice : public Device {
#endif
}
- void thread_run(DeviceTask *task)
+ void thread_run(DeviceTask &task)
{
- if (task->type == DeviceTask::RENDER)
- thread_render(*task);
- else if (task->type == DeviceTask::SHADER)
- thread_shader(*task);
- else if (task->type == DeviceTask::FILM_CONVERT)
- thread_film_convert(*task);
- else if (task->type == DeviceTask::DENOISE_BUFFER)
- thread_denoise(*task);
+ if (task.type == DeviceTask::RENDER)
+ thread_render(task);
+ else if (task.type == DeviceTask::SHADER)
+ thread_shader(task);
+ else if (task.type == DeviceTask::FILM_CONVERT)
+ thread_film_convert(task);
+ else if (task.type == DeviceTask::DENOISE_BUFFER)
+ thread_denoise(task);
}
- class CPUDeviceTask : public DeviceTask {
- public:
- CPUDeviceTask(CPUDevice *device, DeviceTask &task) : DeviceTask(task)
- {
- run = function_bind(&CPUDevice::thread_run, device, this);
- }
- };
-
bool denoising_non_local_means(device_ptr image_ptr,
device_ptr guide_ptr,
device_ptr variance_ptr,
@@ -961,7 +948,71 @@ class CPUDevice : public Device {
}
}
- void denoise(DenoisingTask &denoising, RenderTile &tile)
+ void denoise_openimagedenoise(DeviceTask &task, RenderTile &rtile)
+ {
+#ifdef WITH_OPENIMAGEDENOISE
+ assert(openimagedenoise_supported());
+
+ /* Only one at a time, since OpenImageDenoise itself is multithreaded. */
+ static thread_mutex mutex;
+ thread_scoped_lock lock(mutex);
+
+ /* Create device and filter, cached for reuse. */
+ if (!oidn_device) {
+ oidn_device = oidn::newDevice();
+ oidn_device.commit();
+ }
+ if (!oidn_filter) {
+ oidn_filter = oidn_device.newFilter("RT");
+ }
+
+ /* Copy pixels from compute device to CPU (no-op for CPU device). */
+ rtile.buffers->buffer.copy_from_device();
+
+ /* Set images with appropriate stride for our interleaved pass storage. */
+ const struct {
+ const char *name;
+ int offset;
+ } passes[] = {{"color", task.pass_denoising_data + DENOISING_PASS_COLOR},
+ {"normal", task.pass_denoising_data + DENOISING_PASS_NORMAL},
+ {"albedo", task.pass_denoising_data + DENOISING_PASS_ALBEDO},
+ {"output", 0},
+ { NULL,
+ 0 }};
+
+ for (int i = 0; passes[i].name; i++) {
+ const int64_t offset = rtile.offset + rtile.x + rtile.y * rtile.stride;
+ const int64_t buffer_offset = (offset * task.pass_stride + passes[i].offset) * sizeof(float);
+ const int64_t pixel_stride = task.pass_stride * sizeof(float);
+ const int64_t row_stride = rtile.stride * pixel_stride;
+
+ oidn_filter.setImage(passes[i].name,
+ (char *)rtile.buffer + buffer_offset,
+ oidn::Format::Float3,
+ rtile.w,
+ rtile.h,
+ 0,
+ pixel_stride,
+ row_stride);
+ }
+
+ /* Execute filter. */
+ oidn_filter.set("hdr", true);
+ oidn_filter.set("srgb", false);
+ oidn_filter.commit();
+ oidn_filter.execute();
+
+ /* todo: it may be possible to avoid this copy, but we have to ensure that
+ * when other code copies data from the device it doesn't overwrite the
+ * denoiser buffers. */
+ rtile.buffers->buffer.copy_to_device();
+#else
+ (void)task;
+ (void)rtile;
+#endif
+ }
+
+ void denoise_nlm(DenoisingTask &denoising, RenderTile &tile)
{
ProfilingHelper profiling(denoising.profiler, PROFILING_DENOISING);
@@ -1019,15 +1070,14 @@ class CPUDevice : public Device {
}
}
- RenderTile tile;
- DenoisingTask denoising(this, task);
- denoising.profiler = &kg->profiler;
+ DenoisingTask *denoising = NULL;
+ RenderTile tile;
while (task.acquire_tile(this, tile, task.tile_types)) {
if (tile.task == RenderTile::PATH_TRACE) {
if (use_split_kernel) {
device_only_memory<uchar> void_buffer(this, "void_buffer");
- split_kernel->path_trace(&task, tile, kgbuffer, void_buffer);
+ split_kernel->path_trace(task, tile, kgbuffer, void_buffer);
}
else {
render(task, tile, kg);
@@ -1037,7 +1087,16 @@ class CPUDevice : public Device {
render(task, tile, kg);
}
else if (tile.task == RenderTile::DENOISE) {
- denoise(denoising, tile);
+ if (task.denoising.type == DENOISER_OPENIMAGEDENOISE) {
+ denoise_openimagedenoise(task, tile);
+ }
+ else if (task.denoising.type == DENOISER_NLM) {
+ if (denoising == NULL) {
+ denoising = new DenoisingTask(this, task);
+ denoising->profiler = &kg->profiler;
+ }
+ denoise_nlm(*denoising, tile);
+ }
task.update_progress(&tile, tile.w * tile.h);
}
@@ -1055,6 +1114,7 @@ class CPUDevice : public Device {
kg->~KernelGlobals();
kgbuffer.free();
delete split_kernel;
+ delete denoising;
}
void thread_denoise(DeviceTask &task)
@@ -1072,16 +1132,22 @@ class CPUDevice : public Device {
tile.stride = task.stride;
tile.buffers = task.buffers;
- DenoisingTask denoising(this, task);
+ if (task.denoising.type == DENOISER_OPENIMAGEDENOISE) {
+ denoise_openimagedenoise(task, tile);
+ }
+ else {
+ DenoisingTask denoising(this, task);
- ProfilingState denoising_profiler_state;
- profiler.add_state(&denoising_profiler_state);
- denoising.profiler = &denoising_profiler_state;
+ ProfilingState denoising_profiler_state;
+ profiler.add_state(&denoising_profiler_state);
+ denoising.profiler = &denoising_profiler_state;
- denoise(denoising, tile);
- task.update_progress(&tile, tile.w * tile.h);
+ denoise_nlm(denoising, tile);
+
+ profiler.remove_state(&denoising_profiler_state);
+ }
- profiler.remove_state(&denoising_profiler_state);
+ task.update_progress(&tile, tile.w * tile.h);
}
void thread_film_convert(DeviceTask &task)
@@ -1155,13 +1221,24 @@ class CPUDevice : public Device {
/* split task into smaller ones */
list<DeviceTask> tasks;
- if (task.type == DeviceTask::SHADER)
+ if (task.type == DeviceTask::DENOISE_BUFFER &&
+ task.denoising.type == DENOISER_OPENIMAGEDENOISE) {
+ /* Denoise entire buffer at once with OIDN, it has own threading. */
+ tasks.push_back(task);
+ }
+ else if (task.type == DeviceTask::SHADER) {
task.split(tasks, info.cpu_threads, 256);
- else
+ }
+ else {
task.split(tasks, info.cpu_threads);
+ }
- foreach (DeviceTask &task, tasks)
- task_pool.push(new CPUDeviceTask(this, task));
+ foreach (DeviceTask &task, tasks) {
+ task_pool.push([=] {
+ DeviceTask task_copy = task;
+ thread_run(task_copy);
+ });
+ }
}
void task_wait()
@@ -1326,7 +1403,7 @@ int2 CPUSplitKernel::split_kernel_local_size()
int2 CPUSplitKernel::split_kernel_global_size(device_memory & /*kg*/,
device_memory & /*data*/,
- DeviceTask * /*task*/)
+ DeviceTask & /*task*/)
{
return make_int2(1, 1);
}
@@ -1358,6 +1435,10 @@ void device_cpu_info(vector<DeviceInfo> &devices)
info.has_osl = true;
info.has_half_images = true;
info.has_profiling = true;
+ info.denoisers = DENOISER_NLM;
+ if (openimagedenoise_supported()) {
+ info.denoisers |= DENOISER_OPENIMAGEDENOISE;
+ }
devices.insert(devices.begin(), info);
}
diff --git a/intern/cycles/device/device_cuda.cpp b/intern/cycles/device/device_cuda.cpp
index 04c04761311..d9ffcceb06e 100644
--- a/intern/cycles/device/device_cuda.cpp
+++ b/intern/cycles/device/device_cuda.cpp
@@ -130,6 +130,7 @@ void device_cuda_info(vector<DeviceInfo> &devices)
info.has_half_images = (major >= 3);
info.has_volume_decoupled = false;
info.has_adaptive_stop_per_sample = false;
+ info.denoisers = DENOISER_NLM;
/* Check if the device has P2P access to any other device in the system. */
for (int peer_num = 0; peer_num < count && !info.has_peer_memory; peer_num++) {
diff --git a/intern/cycles/device/device_denoising.cpp b/intern/cycles/device/device_denoising.cpp
index ac17c02a427..89de80a5bcd 100644
--- a/intern/cycles/device/device_denoising.cpp
+++ b/intern/cycles/device/device_denoising.cpp
@@ -56,8 +56,8 @@ DenoisingTask::DenoisingTask(Device *device, const DeviceTask &task)
tile_info->frames[i] = task.denoising_frames[i - 1];
}
- write_passes = task.denoising_write_passes;
- do_filter = task.denoising_do_filter;
+ do_prefilter = task.denoising.store_passes && task.denoising.type == DENOISER_NLM;
+ do_filter = task.denoising.use && task.denoising.type == DENOISER_NLM;
}
DenoisingTask::~DenoisingTask()
@@ -91,7 +91,7 @@ void DenoisingTask::set_render_buffer(RenderTile *rtiles)
target_buffer.stride = rtiles[9].stride;
target_buffer.ptr = rtiles[9].buffer;
- if (write_passes && rtiles[9].buffers) {
+ if (do_prefilter && rtiles[9].buffers) {
target_buffer.denoising_output_offset =
rtiles[9].buffers->params.get_denoising_prefiltered_offset();
}
@@ -111,7 +111,7 @@ void DenoisingTask::setup_denoising_buffer()
rect = rect_clip(rect,
make_int4(tile_info->x[0], tile_info->y[0], tile_info->x[3], tile_info->y[3]));
- buffer.use_intensity = write_passes || (tile_info->num_frames > 1);
+ buffer.use_intensity = do_prefilter || (tile_info->num_frames > 1);
buffer.passes = buffer.use_intensity ? 15 : 14;
buffer.width = rect.z - rect.x;
buffer.stride = align_up(buffer.width, 4);
@@ -343,7 +343,7 @@ void DenoisingTask::run_denoising(RenderTile *tile)
reconstruct();
}
- if (write_passes) {
+ if (do_prefilter) {
write_buffer();
}
diff --git a/intern/cycles/device/device_denoising.h b/intern/cycles/device/device_denoising.h
index bd1d0193dbd..4c122e981eb 100644
--- a/intern/cycles/device/device_denoising.h
+++ b/intern/cycles/device/device_denoising.h
@@ -60,7 +60,7 @@ class DenoisingTask {
int4 rect;
int4 filter_area;
- bool write_passes;
+ bool do_prefilter;
bool do_filter;
struct DeviceFunctions {
diff --git a/intern/cycles/device/device_multi.cpp b/intern/cycles/device/device_multi.cpp
index 020b9e10e60..fd14bbdccc5 100644
--- a/intern/cycles/device/device_multi.cpp
+++ b/intern/cycles/device/device_multi.cpp
@@ -396,8 +396,8 @@ class MultiDevice : public Device {
size_t existing_size = mem.device_size;
/* This is a hack to only allocate the tile buffers on denoising devices
- * Similarily the tile buffers also need to be allocated separately on all devices so any
- * overlap rendered for denoising does not interfer with each other */
+ * Similarly the tile buffers also need to be allocated separately on all devices so any
+ * overlap rendered for denoising does not interfere with each other */
if (strcmp(mem.name, "RenderBuffers") == 0) {
vector<device_ptr> device_pointers;
device_pointers.reserve(devices.size());
diff --git a/intern/cycles/device/device_network.cpp b/intern/cycles/device/device_network.cpp
index 0933d51f321..8904b517e92 100644
--- a/intern/cycles/device/device_network.cpp
+++ b/intern/cycles/device/device_network.cpp
@@ -313,6 +313,7 @@ void device_network_info(vector<DeviceInfo> &devices)
info.has_volume_decoupled = false;
info.has_adaptive_stop_per_sample = false;
info.has_osl = false;
+ info.denoisers = DENOISER_NONE;
devices.push_back(info);
}
diff --git a/intern/cycles/device/device_opencl.cpp b/intern/cycles/device/device_opencl.cpp
index 8a0b128697f..39b9ef70192 100644
--- a/intern/cycles/device/device_opencl.cpp
+++ b/intern/cycles/device/device_opencl.cpp
@@ -120,6 +120,7 @@ void device_opencl_info(vector<DeviceInfo> &devices)
info.use_split_kernel = true;
info.has_volume_decoupled = false;
info.has_adaptive_stop_per_sample = false;
+ info.denoisers = DENOISER_NLM;
info.id = id;
/* Check OpenCL extensions */
diff --git a/intern/cycles/device/device_optix.cpp b/intern/cycles/device/device_optix.cpp
index fbf6a914744..ececca3df53 100644
--- a/intern/cycles/device/device_optix.cpp
+++ b/intern/cycles/device/device_optix.cpp
@@ -246,7 +246,7 @@ class OptiXDevice : public CUDADevice {
~OptiXDevice()
{
// Stop processing any more tasks
- task_pool.stop();
+ task_pool.cancel();
// Make CUDA context current
const CUDAContextScope scope(cuContext);
@@ -428,11 +428,20 @@ class OptiXDevice : public CUDADevice {
group_descs[PG_HITS].hitgroup.entryFunctionNameAH = "__anyhit__kernel_optix_shadow_all_hit";
if (requested_features.use_hair) {
- // Add curve intersection programs
group_descs[PG_HITD].hitgroup.moduleIS = optix_module;
- group_descs[PG_HITD].hitgroup.entryFunctionNameIS = "__intersection__curve";
group_descs[PG_HITS].hitgroup.moduleIS = optix_module;
- group_descs[PG_HITS].hitgroup.entryFunctionNameIS = "__intersection__curve";
+
+ // Add curve intersection programs
+ if (requested_features.use_hair_thick) {
+ // Slower programs for thick hair since that also slows down ribbons.
+ // Ideally this should not be needed.
+ group_descs[PG_HITD].hitgroup.entryFunctionNameIS = "__intersection__curve_all";
+ group_descs[PG_HITS].hitgroup.entryFunctionNameIS = "__intersection__curve_all";
+ }
+ else {
+ group_descs[PG_HITD].hitgroup.entryFunctionNameIS = "__intersection__curve_ribbon";
+ group_descs[PG_HITS].hitgroup.entryFunctionNameIS = "__intersection__curve_ribbon";
+ }
}
if (requested_features.use_subsurface || requested_features.use_shader_raytrace) {
@@ -712,7 +721,7 @@ class OptiXDevice : public CUDADevice {
const CUDAContextScope scope(cuContext);
// Choose between OptiX and NLM denoising
- if (task.denoising_use_optix) {
+ if (task.denoising.type == DENOISER_OPTIX) {
// Map neighboring tiles onto this device, indices are as following:
// Where index 4 is the center tile and index 9 is the target for the result.
// 0 1 2
@@ -1436,21 +1445,21 @@ class OptiXDevice : public CUDADevice {
KernelData *const data = (KernelData *)host;
*(OptixTraversableHandle *)&data->bvh.scene = tlas_handle;
- update_launch_params(name, offsetof(KernelParams, data), host, size);
+ update_launch_params(offsetof(KernelParams, data), host, size);
return;
}
// Update data storage pointers in launch parameters
# define KERNEL_TEX(data_type, tex_name) \
if (strcmp(name, #tex_name) == 0) { \
- update_launch_params(name, offsetof(KernelParams, tex_name), host, size); \
+ update_launch_params(offsetof(KernelParams, tex_name), host, size); \
return; \
}
# include "kernel/kernel_textures.h"
# undef KERNEL_TEX
}
- void update_launch_params(const char *name, size_t offset, void *data, size_t data_size)
+ void update_launch_params(size_t offset, void *data, size_t data_size)
{
const CUDAContextScope scope(cuContext);
@@ -1463,15 +1472,6 @@ class OptiXDevice : public CUDADevice {
void task_add(DeviceTask &task) override
{
- struct OptiXDeviceTask : public DeviceTask {
- OptiXDeviceTask(OptiXDevice *device, DeviceTask &task, int task_index) : DeviceTask(task)
- {
- // Using task index parameter instead of thread index, since number of CUDA streams may
- // differ from number of threads
- run = function_bind(&OptiXDevice::thread_run, device, *this, task_index);
- }
- };
-
// Upload texture information to device if it has changed since last launch
load_texture_info();
@@ -1483,7 +1483,10 @@ class OptiXDevice : public CUDADevice {
if (task.type == DeviceTask::DENOISE_BUFFER) {
// Execute denoising in a single thread (e.g. to avoid race conditions during creation)
- task_pool.push(new OptiXDeviceTask(this, task, 0));
+ task_pool.push([=] {
+ DeviceTask task_copy = task;
+ thread_run(task_copy, 0);
+ });
return;
}
@@ -1493,8 +1496,15 @@ class OptiXDevice : public CUDADevice {
// Queue tasks in internal task pool
int task_index = 0;
- for (DeviceTask &task : tasks)
- task_pool.push(new OptiXDeviceTask(this, task, task_index++));
+ for (DeviceTask &task : tasks) {
+ task_pool.push([=] {
+ // Using task index parameter instead of thread index, since number of CUDA streams may
+ // differ from number of threads
+ DeviceTask task_copy = task;
+ thread_run(task_copy, task_index);
+ });
+ task_index++;
+ }
}
void task_wait() override
@@ -1551,6 +1561,7 @@ void device_optix_info(const vector<DeviceInfo> &cuda_devices, vector<DeviceInfo
info.type = DEVICE_OPTIX;
info.id += "_OptiX";
+ info.denoisers |= DENOISER_OPTIX;
devices.push_back(info);
}
diff --git a/intern/cycles/device/device_split_kernel.cpp b/intern/cycles/device/device_split_kernel.cpp
index f22d8761058..4c288f60c16 100644
--- a/intern/cycles/device/device_split_kernel.cpp
+++ b/intern/cycles/device/device_split_kernel.cpp
@@ -145,7 +145,7 @@ size_t DeviceSplitKernel::max_elements_for_max_buffer_size(device_memory &kg,
return max_buffer_size / size_per_element;
}
-bool DeviceSplitKernel::path_trace(DeviceTask *task,
+bool DeviceSplitKernel::path_trace(DeviceTask &task,
RenderTile &tile,
device_memory &kgbuffer,
device_memory &kernel_data)
@@ -222,9 +222,9 @@ bool DeviceSplitKernel::path_trace(DeviceTask *task,
subtile.start_sample = tile.sample;
subtile.num_samples = samples_per_second;
- if (task->adaptive_sampling.use) {
- subtile.num_samples = task->adaptive_sampling.align_dynamic_samples(subtile.start_sample,
- subtile.num_samples);
+ if (task.adaptive_sampling.use) {
+ subtile.num_samples = task.adaptive_sampling.align_dynamic_samples(subtile.start_sample,
+ subtile.num_samples);
}
/* Don't go beyond requested number of samples. */
@@ -286,7 +286,7 @@ bool DeviceSplitKernel::path_trace(DeviceTask *task,
ENQUEUE_SPLIT_KERNEL(queue_enqueue, global_size, local_size);
ENQUEUE_SPLIT_KERNEL(buffer_update, global_size, local_size);
- if (task->get_cancel() && cancel_time == DBL_MAX) {
+ if (task.get_cancel() && cancel_time == DBL_MAX) {
/* Wait up to twice as many seconds for current samples to finish
* to avoid artifacts in render result from ending too soon.
*/
@@ -323,7 +323,7 @@ bool DeviceSplitKernel::path_trace(DeviceTask *task,
}
int filter_sample = tile.sample + subtile.num_samples - 1;
- if (task->adaptive_sampling.use && task->adaptive_sampling.need_filter(filter_sample)) {
+ if (task.adaptive_sampling.use && task.adaptive_sampling.need_filter(filter_sample)) {
size_t buffer_size[2];
buffer_size[0] = round_up(tile.w, local_size[0]);
buffer_size[1] = round_up(tile.h, local_size[1]);
@@ -352,16 +352,16 @@ bool DeviceSplitKernel::path_trace(DeviceTask *task,
#undef ENQUEUE_SPLIT_KERNEL
tile.sample += subtile.num_samples;
- task->update_progress(&tile, tile.w * tile.h * subtile.num_samples);
+ task.update_progress(&tile, tile.w * tile.h * subtile.num_samples);
time_multiplier = min(time_multiplier << 1, 10);
- if (task->get_cancel()) {
+ if (task.get_cancel()) {
return true;
}
}
- if (task->adaptive_sampling.use) {
+ if (task.adaptive_sampling.use) {
/* Reset the start samples. */
RenderTile subtile = tile;
subtile.start_sample = tile.start_sample;
diff --git a/intern/cycles/device/device_split_kernel.h b/intern/cycles/device/device_split_kernel.h
index 9d6b9efdd62..07a21b10299 100644
--- a/intern/cycles/device/device_split_kernel.h
+++ b/intern/cycles/device/device_split_kernel.h
@@ -109,7 +109,7 @@ class DeviceSplitKernel {
virtual ~DeviceSplitKernel();
bool load_kernels(const DeviceRequestedFeatures &requested_features);
- bool path_trace(DeviceTask *task,
+ bool path_trace(DeviceTask &task,
RenderTile &rtile,
device_memory &kgbuffer,
device_memory &kernel_data);
@@ -137,7 +137,7 @@ class DeviceSplitKernel {
virtual int2 split_kernel_local_size() = 0;
virtual int2 split_kernel_global_size(device_memory &kg,
device_memory &data,
- DeviceTask *task) = 0;
+ DeviceTask &task) = 0;
};
CCL_NAMESPACE_END
diff --git a/intern/cycles/device/device_task.cpp b/intern/cycles/device/device_task.cpp
index 7485e1b41de..6e7c184c6c9 100644
--- a/intern/cycles/device/device_task.cpp
+++ b/intern/cycles/device/device_task.cpp
@@ -50,7 +50,7 @@ DeviceTask::DeviceTask(Type type_)
last_update_time = time_dt();
}
-int DeviceTask::get_subtask_count(int num, int max_size)
+int DeviceTask::get_subtask_count(int num, int max_size) const
{
if (max_size != 0) {
int max_size_num;
@@ -78,7 +78,7 @@ int DeviceTask::get_subtask_count(int num, int max_size)
return num;
}
-void DeviceTask::split(list<DeviceTask> &tasks, int num, int max_size)
+void DeviceTask::split(list<DeviceTask> &tasks, int num, int max_size) const
{
num = get_subtask_count(num, max_size);
diff --git a/intern/cycles/device/device_task.h b/intern/cycles/device/device_task.h
index 8c4e682adb1..600973b8100 100644
--- a/intern/cycles/device/device_task.h
+++ b/intern/cycles/device/device_task.h
@@ -21,7 +21,6 @@
#include "util/util_function.h"
#include "util/util_list.h"
-#include "util/util_task.h"
CCL_NAMESPACE_BEGIN
@@ -32,8 +31,33 @@ class RenderBuffers;
class RenderTile;
class Tile;
+enum DenoiserType {
+ DENOISER_NLM = 1,
+ DENOISER_OPTIX = 2,
+ DENOISER_OPENIMAGEDENOISE = 4,
+ DENOISER_NUM,
+
+ DENOISER_NONE = 0,
+ DENOISER_ALL = ~0,
+};
+
+typedef int DenoiserTypeMask;
+
class DenoiseParams {
public:
+ /* Apply denoiser to image. */
+ bool use;
+ /* Output denoising data passes (possibly without applying the denoiser). */
+ bool store_passes;
+
+ /* Denoiser type. */
+ DenoiserType type;
+
+ /* Viewport start sample. */
+ int start_sample;
+
+ /** Native Denoiser **/
+
/* Pixel radius for neighboring pixels to take into account. */
int radius;
/* Controls neighbor pixel weighting for the denoising filter. */
@@ -47,18 +71,36 @@ class DenoiseParams {
int neighbor_frames;
/* Clamp the input to the range of +-1e8. Should be enough for any legitimate data. */
bool clamp_input;
+
+ /** Optix Denoiser **/
+
/* Passes handed over to the OptiX denoiser (default to color + albedo). */
int optix_input_passes;
DenoiseParams()
{
+ use = false;
+ store_passes = false;
+
+ type = DENOISER_NLM;
+
radius = 8;
strength = 0.5f;
feature_strength = 0.5f;
relative_pca = false;
neighbor_frames = 2;
clamp_input = true;
+
optix_input_passes = 2;
+
+ start_sample = 0;
+ }
+
+ /* Test if a denoising task needs to run, also to prefilter passes for the native
+ * denoiser when we are not applying denoising to the combined image. */
+ bool need_denoising_task() const
+ {
+ return (use || (store_passes && type == DENOISER_NLM));
}
};
@@ -75,7 +117,7 @@ class AdaptiveSampling {
int min_samples;
};
-class DeviceTask : public Task {
+class DeviceTask {
public:
typedef enum { RENDER, FILM_CONVERT, SHADER, DENOISE_BUFFER } Type;
Type type;
@@ -98,8 +140,8 @@ class DeviceTask : public Task {
explicit DeviceTask(Type type = RENDER);
- int get_subtask_count(int num, int max_size = 0);
- void split(list<DeviceTask> &tasks, int num, int max_size = 0);
+ int get_subtask_count(int num, int max_size = 0) const;
+ void split(list<DeviceTask> &tasks, int num, int max_size = 0) const;
void update_progress(RenderTile *rtile, int pixel_samples = -1);
@@ -116,10 +158,6 @@ class DeviceTask : public Task {
bool denoising_from_render;
vector<int> denoising_frames;
- bool denoising_do_filter;
- bool denoising_use_optix;
- bool denoising_write_passes;
-
int pass_stride;
int frame_stride;
int target_pass_stride;
diff --git a/intern/cycles/device/opencl/device_opencl.h b/intern/cycles/device/opencl/device_opencl.h
index 389268e1c2a..e0140996cf0 100644
--- a/intern/cycles/device/opencl/device_opencl.h
+++ b/intern/cycles/device/opencl/device_opencl.h
@@ -23,6 +23,7 @@
# include "util/util_map.h"
# include "util/util_param.h"
# include "util/util_string.h"
+# include "util/util_task.h"
# include "clew.h"
@@ -258,6 +259,8 @@ class OpenCLDevice : public Device {
TaskPool load_required_kernel_task_pool;
/* Task pool for optional kernels (feature kernels during foreground rendering) */
TaskPool load_kernel_task_pool;
+ std::atomic<int> load_kernel_num_compiling;
+
cl_context cxContext;
cl_command_queue cqCommandQueue;
cl_platform_id cpPlatform;
@@ -455,14 +458,6 @@ class OpenCLDevice : public Device {
void denoise(RenderTile &tile, DenoisingTask &denoising);
- class OpenCLDeviceTask : public DeviceTask {
- public:
- OpenCLDeviceTask(OpenCLDevice *device, DeviceTask &task) : DeviceTask(task)
- {
- run = function_bind(&OpenCLDevice::thread_run, device, this);
- }
- };
-
int get_split_task_count(DeviceTask & /*task*/)
{
return 1;
@@ -470,7 +465,10 @@ class OpenCLDevice : public Device {
void task_add(DeviceTask &task)
{
- task_pool.push(new OpenCLDeviceTask(this, task));
+ task_pool.push([=] {
+ DeviceTask task_copy = task;
+ thread_run(task_copy);
+ });
}
void task_wait()
@@ -483,7 +481,7 @@ class OpenCLDevice : public Device {
task_pool.cancel();
}
- void thread_run(DeviceTask *task);
+ void thread_run(DeviceTask &task);
virtual BVHLayoutMask get_bvh_layout_mask() const
{
diff --git a/intern/cycles/device/opencl/device_opencl_impl.cpp b/intern/cycles/device/opencl/device_opencl_impl.cpp
index beb3174b111..8c94815b193 100644
--- a/intern/cycles/device/opencl/device_opencl_impl.cpp
+++ b/intern/cycles/device/opencl/device_opencl_impl.cpp
@@ -542,7 +542,7 @@ class OpenCLSplitKernel : public DeviceSplitKernel {
virtual int2 split_kernel_global_size(device_memory &kg,
device_memory &data,
- DeviceTask * /*task*/)
+ DeviceTask & /*task*/)
{
cl_device_type type = OpenCLInfo::get_device_type(device->cdDevice);
/* Use small global size on CPU devices as it seems to be much faster. */
@@ -610,6 +610,7 @@ void OpenCLDevice::opencl_assert_err(cl_int err, const char *where)
OpenCLDevice::OpenCLDevice(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background)
: Device(info, stats, profiler, background),
+ load_kernel_num_compiling(0),
kernel_programs(this),
preview_programs(this),
memory_manager(this),
@@ -684,9 +685,9 @@ OpenCLDevice::OpenCLDevice(DeviceInfo &info, Stats &stats, Profiler &profiler, b
OpenCLDevice::~OpenCLDevice()
{
- task_pool.stop();
- load_required_kernel_task_pool.stop();
- load_kernel_task_pool.stop();
+ task_pool.cancel();
+ load_required_kernel_task_pool.cancel();
+ load_kernel_task_pool.cancel();
memory_manager.free();
@@ -798,7 +799,11 @@ bool OpenCLDevice::load_kernels(const DeviceRequestedFeatures &requested_feature
* internally within a single process. */
foreach (OpenCLProgram *program, programs) {
if (!program->load()) {
- load_kernel_task_pool.push(function_bind(&OpenCLProgram::compile, program));
+ load_kernel_num_compiling++;
+ load_kernel_task_pool.push([=] {
+ program->compile();
+ load_kernel_num_compiling--;
+ });
}
}
return true;
@@ -868,7 +873,7 @@ bool OpenCLDevice::wait_for_availability(const DeviceRequestedFeatures &requeste
* Better to check on device level than per kernel as mixing preview and
* non-preview kernels does not work due to different data types */
if (use_preview_kernels) {
- use_preview_kernels = !load_kernel_task_pool.finished();
+ use_preview_kernels = load_kernel_num_compiling.load() > 0;
}
}
return split_kernel->load_kernels(requested_features);
@@ -895,7 +900,7 @@ DeviceKernelStatus OpenCLDevice::get_active_kernel_switch_state()
return DEVICE_KERNEL_USING_FEATURE_KERNEL;
}
- bool other_kernels_finished = load_kernel_task_pool.finished();
+ bool other_kernels_finished = load_kernel_num_compiling.load() == 0;
if (use_preview_kernels) {
if (other_kernels_finished) {
return DEVICE_KERNEL_FEATURE_KERNEL_AVAILABLE;
@@ -1336,20 +1341,20 @@ void OpenCLDevice::flush_texture_buffers()
memory_manager.alloc("texture_info", texture_info);
}
-void OpenCLDevice::thread_run(DeviceTask *task)
+void OpenCLDevice::thread_run(DeviceTask &task)
{
flush_texture_buffers();
- if (task->type == DeviceTask::RENDER) {
+ if (task.type == DeviceTask::RENDER) {
RenderTile tile;
- DenoisingTask denoising(this, *task);
+ DenoisingTask denoising(this, task);
/* Allocate buffer for kernel globals */
device_only_memory<KernelGlobalsDummy> kgbuffer(this, "kernel_globals");
kgbuffer.alloc_to_device(1);
/* Keep rendering tiles until done. */
- while (task->acquire_tile(this, tile, task->tile_types)) {
+ while (task.acquire_tile(this, tile, task.tile_types)) {
if (tile.task == RenderTile::PATH_TRACE) {
assert(tile.task == RenderTile::PATH_TRACE);
scoped_timer timer(&tile.buffers->render_time);
@@ -1368,42 +1373,42 @@ void OpenCLDevice::thread_run(DeviceTask *task)
clFinish(cqCommandQueue);
}
else if (tile.task == RenderTile::BAKE) {
- bake(*task, tile);
+ bake(task, tile);
}
else if (tile.task == RenderTile::DENOISE) {
tile.sample = tile.start_sample + tile.num_samples;
denoise(tile, denoising);
- task->update_progress(&tile, tile.w * tile.h);
+ task.update_progress(&tile, tile.w * tile.h);
}
- task->release_tile(tile);
+ task.release_tile(tile);
}
kgbuffer.free();
}
- else if (task->type == DeviceTask::SHADER) {
- shader(*task);
+ else if (task.type == DeviceTask::SHADER) {
+ shader(task);
}
- else if (task->type == DeviceTask::FILM_CONVERT) {
- film_convert(*task, task->buffer, task->rgba_byte, task->rgba_half);
+ else if (task.type == DeviceTask::FILM_CONVERT) {
+ film_convert(task, task.buffer, task.rgba_byte, task.rgba_half);
}
- else if (task->type == DeviceTask::DENOISE_BUFFER) {
+ else if (task.type == DeviceTask::DENOISE_BUFFER) {
RenderTile tile;
- tile.x = task->x;
- tile.y = task->y;
- tile.w = task->w;
- tile.h = task->h;
- tile.buffer = task->buffer;
- tile.sample = task->sample + task->num_samples;
- tile.num_samples = task->num_samples;
- tile.start_sample = task->sample;
- tile.offset = task->offset;
- tile.stride = task->stride;
- tile.buffers = task->buffers;
-
- DenoisingTask denoising(this, *task);
+ tile.x = task.x;
+ tile.y = task.y;
+ tile.w = task.w;
+ tile.h = task.h;
+ tile.buffer = task.buffer;
+ tile.sample = task.sample + task.num_samples;
+ tile.num_samples = task.num_samples;
+ tile.start_sample = task.sample;
+ tile.offset = task.offset;
+ tile.stride = task.stride;
+ tile.buffers = task.buffers;
+
+ DenoisingTask denoising(this, task);
denoise(tile, denoising);
- task->update_progress(&tile, tile.w * tile.h);
+ task.update_progress(&tile, tile.w * tile.h);
}
}
@@ -1937,10 +1942,8 @@ void OpenCLDevice::bake(DeviceTask &task, RenderTile &rtile)
clFinish(cqCommandQueue);
}
-string OpenCLDevice::kernel_build_options(const string *debug_src)
+static bool kernel_build_opencl_2(cl_device_id cdDevice)
{
- string build_options = "-cl-no-signed-zeros -cl-mad-enable ";
-
/* Build with OpenCL 2.0 if available, this improves performance
* with AMD OpenCL drivers on Windows and Linux (legacy drivers).
* Note that OpenCL selects the highest 1.x version by default,
@@ -1948,10 +1951,36 @@ string OpenCLDevice::kernel_build_options(const string *debug_src)
int version_major, version_minor;
if (OpenCLInfo::get_device_version(cdDevice, &version_major, &version_minor)) {
if (version_major >= 2) {
- build_options += "-cl-std=CL2.0 ";
+ /* This appears to trigger a driver bug in Radeon RX cards with certain
+ * driver version, so don't use OpenCL 2.0 for those. */
+ string device_name = OpenCLInfo::get_readable_device_name(cdDevice);
+ if (string_startswith(device_name, "Radeon RX 4") ||
+ string_startswith(device_name, "Radeon (TM) RX 4") ||
+ string_startswith(device_name, "Radeon RX 5") ||
+ string_startswith(device_name, "Radeon (TM) RX 5")) {
+ char version[256] = "";
+ int driver_major, driver_minor;
+ clGetDeviceInfo(cdDevice, CL_DEVICE_VERSION, sizeof(version), &version, NULL);
+ if (sscanf(version, "OpenCL 2.0 AMD-APP (%d.%d)", &driver_major, &driver_minor) == 2) {
+ return !(driver_major == 3075 && driver_minor <= 12);
+ }
+ }
+
+ return true;
}
}
+ return false;
+}
+
+string OpenCLDevice::kernel_build_options(const string *debug_src)
+{
+ string build_options = "-cl-no-signed-zeros -cl-mad-enable ";
+
+ if (kernel_build_opencl_2(cdDevice)) {
+ build_options += "-cl-std=CL2.0 ";
+ }
+
if (platform_name == "NVIDIA CUDA") {
build_options +=
"-D__KERNEL_OPENCL_NVIDIA__ "
diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt
index 2e839a616e9..7cc0d32d521 100644
--- a/intern/cycles/kernel/CMakeLists.txt
+++ b/intern/cycles/kernel/CMakeLists.txt
@@ -81,18 +81,6 @@ set(SRC_BVH_HEADERS
bvh/bvh_types.h
bvh/bvh_volume.h
bvh/bvh_volume_all.h
- bvh/qbvh_nodes.h
- bvh/qbvh_shadow_all.h
- bvh/qbvh_local.h
- bvh/qbvh_traversal.h
- bvh/qbvh_volume.h
- bvh/qbvh_volume_all.h
- bvh/obvh_nodes.h
- bvh/obvh_shadow_all.h
- bvh/obvh_local.h
- bvh/obvh_traversal.h
- bvh/obvh_volume.h
- bvh/obvh_volume_all.h
bvh/bvh_embree.h
)
@@ -113,6 +101,8 @@ set(SRC_HEADERS
kernel_id_passes.h
kernel_jitter.h
kernel_light.h
+ kernel_light_background.h
+ kernel_light_common.h
kernel_math.h
kernel_montecarlo.h
kernel_passes.h
diff --git a/intern/cycles/kernel/bvh/bvh.h b/intern/cycles/kernel/bvh/bvh.h
index 9b9df883b62..80b58f46329 100644
--- a/intern/cycles/kernel/bvh/bvh.h
+++ b/intern/cycles/kernel/bvh/bvh.h
@@ -35,14 +35,6 @@ CCL_NAMESPACE_BEGIN
#ifndef __KERNEL_OPTIX__
-/* Common QBVH functions. */
-# ifdef __QBVH__
-# include "kernel/bvh/qbvh_nodes.h"
-# ifdef __KERNEL_AVX2__
-# include "kernel/bvh/obvh_nodes.h"
-# endif
-# endif
-
/* Regular BVH traversal */
# include "kernel/bvh/bvh_nodes.h"
@@ -51,27 +43,21 @@ CCL_NAMESPACE_BEGIN
# define BVH_FUNCTION_FEATURES 0
# include "kernel/bvh/bvh_traversal.h"
-# if defined(__INSTANCING__)
-# define BVH_FUNCTION_NAME bvh_intersect_instancing
-# define BVH_FUNCTION_FEATURES BVH_INSTANCING
-# include "kernel/bvh/bvh_traversal.h"
-# endif
-
# if defined(__HAIR__)
# define BVH_FUNCTION_NAME bvh_intersect_hair
-# define BVH_FUNCTION_FEATURES BVH_INSTANCING | BVH_HAIR
+# define BVH_FUNCTION_FEATURES BVH_HAIR
# include "kernel/bvh/bvh_traversal.h"
# endif
# if defined(__OBJECT_MOTION__)
# define BVH_FUNCTION_NAME bvh_intersect_motion
-# define BVH_FUNCTION_FEATURES BVH_INSTANCING | BVH_MOTION
+# define BVH_FUNCTION_FEATURES BVH_MOTION
# include "kernel/bvh/bvh_traversal.h"
# endif
# if defined(__HAIR__) && defined(__OBJECT_MOTION__)
# define BVH_FUNCTION_NAME bvh_intersect_hair_motion
-# define BVH_FUNCTION_FEATURES BVH_INSTANCING | BVH_HAIR | BVH_MOTION
+# define BVH_FUNCTION_FEATURES BVH_HAIR | BVH_MOTION
# include "kernel/bvh/bvh_traversal.h"
# endif
@@ -96,15 +82,9 @@ CCL_NAMESPACE_BEGIN
# define BVH_FUNCTION_FEATURES BVH_HAIR
# include "kernel/bvh/bvh_volume.h"
-# if defined(__INSTANCING__)
-# define BVH_FUNCTION_NAME bvh_intersect_volume_instancing
-# define BVH_FUNCTION_FEATURES BVH_INSTANCING | BVH_HAIR
-# include "kernel/bvh/bvh_volume.h"
-# endif
-
# if defined(__OBJECT_MOTION__)
# define BVH_FUNCTION_NAME bvh_intersect_volume_motion
-# define BVH_FUNCTION_FEATURES BVH_INSTANCING | BVH_MOTION | BVH_HAIR
+# define BVH_FUNCTION_FEATURES BVH_MOTION | BVH_HAIR
# include "kernel/bvh/bvh_volume.h"
# endif
# endif /* __VOLUME__ */
@@ -116,27 +96,21 @@ CCL_NAMESPACE_BEGIN
# define BVH_FUNCTION_FEATURES 0
# include "kernel/bvh/bvh_shadow_all.h"
-# if defined(__INSTANCING__)
-# define BVH_FUNCTION_NAME bvh_intersect_shadow_all_instancing
-# define BVH_FUNCTION_FEATURES BVH_INSTANCING
-# include "kernel/bvh/bvh_shadow_all.h"
-# endif
-
# if defined(__HAIR__)
# define BVH_FUNCTION_NAME bvh_intersect_shadow_all_hair
-# define BVH_FUNCTION_FEATURES BVH_INSTANCING | BVH_HAIR
+# define BVH_FUNCTION_FEATURES BVH_HAIR
# include "kernel/bvh/bvh_shadow_all.h"
# endif
# if defined(__OBJECT_MOTION__)
# define BVH_FUNCTION_NAME bvh_intersect_shadow_all_motion
-# define BVH_FUNCTION_FEATURES BVH_INSTANCING | BVH_MOTION
+# define BVH_FUNCTION_FEATURES BVH_MOTION
# include "kernel/bvh/bvh_shadow_all.h"
# endif
# if defined(__HAIR__) && defined(__OBJECT_MOTION__)
# define BVH_FUNCTION_NAME bvh_intersect_shadow_all_hair_motion
-# define BVH_FUNCTION_FEATURES BVH_INSTANCING | BVH_HAIR | BVH_MOTION
+# define BVH_FUNCTION_FEATURES BVH_HAIR | BVH_MOTION
# include "kernel/bvh/bvh_shadow_all.h"
# endif
# endif /* __SHADOW_RECORD_ALL__ */
@@ -148,15 +122,9 @@ CCL_NAMESPACE_BEGIN
# define BVH_FUNCTION_FEATURES BVH_HAIR
# include "kernel/bvh/bvh_volume_all.h"
-# if defined(__INSTANCING__)
-# define BVH_FUNCTION_NAME bvh_intersect_volume_all_instancing
-# define BVH_FUNCTION_FEATURES BVH_INSTANCING | BVH_HAIR
-# include "kernel/bvh/bvh_volume_all.h"
-# endif
-
# if defined(__OBJECT_MOTION__)
# define BVH_FUNCTION_NAME bvh_intersect_volume_all_motion
-# define BVH_FUNCTION_FEATURES BVH_INSTANCING | BVH_MOTION | BVH_HAIR
+# define BVH_FUNCTION_FEATURES BVH_MOTION | BVH_HAIR
# include "kernel/bvh/bvh_volume_all.h"
# endif
# endif /* __VOLUME_RECORD_ALL__ */
@@ -264,21 +232,8 @@ ccl_device_intersect bool scene_intersect(KernelGlobals *kg,
}
# endif /* __HAIR__ */
-# ifdef __KERNEL_CPU__
-# ifdef __INSTANCING__
- if (kernel_data.bvh.have_instancing) {
- return bvh_intersect_instancing(kg, ray, isect, visibility);
- }
-# endif /* __INSTANCING__ */
- return bvh_intersect(kg, ray, isect, visibility);
-# else /* __KERNEL_CPU__ */
-# ifdef __INSTANCING__
- return bvh_intersect_instancing(kg, ray, isect, visibility);
-# else
return bvh_intersect(kg, ray, isect, visibility);
-# endif /* __INSTANCING__ */
-# endif /* __KERNEL_CPU__ */
-#endif /* __KERNEL_OPTIX__ */
+#endif /* __KERNEL_OPTIX__ */
}
#ifdef __BVH_LOCAL__
@@ -476,21 +431,8 @@ ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals *kg,
}
# endif /* __HAIR__ */
-# ifdef __KERNEL_CPU__
-# ifdef __INSTANCING__
- if (kernel_data.bvh.have_instancing) {
- return bvh_intersect_shadow_all_instancing(kg, ray, isect, visibility, max_hits, num_hits);
- }
-# endif /* __INSTANCING__ */
return bvh_intersect_shadow_all(kg, ray, isect, visibility, max_hits, num_hits);
-# else
-# ifdef __INSTANCING__
- return bvh_intersect_shadow_all_instancing(kg, ray, isect, visibility, max_hits, num_hits);
-# else
- return bvh_intersect_shadow_all(kg, ray, isect, visibility, max_hits, num_hits);
-# endif /* __INSTANCING__ */
-# endif /* __KERNEL_CPU__ */
-# endif /* __KERNEL_OPTIX__ */
+# endif /* __KERNEL_OPTIX__ */
}
#endif /* __SHADOW_RECORD_ALL__ */
@@ -548,21 +490,8 @@ ccl_device_intersect bool scene_intersect_volume(KernelGlobals *kg,
}
# endif /* __OBJECT_MOTION__ */
-# ifdef __KERNEL_CPU__
-# ifdef __INSTANCING__
- if (kernel_data.bvh.have_instancing) {
- return bvh_intersect_volume_instancing(kg, ray, isect, visibility);
- }
-# endif /* __INSTANCING__ */
return bvh_intersect_volume(kg, ray, isect, visibility);
-# else /* __KERNEL_CPU__ */
-# ifdef __INSTANCING__
- return bvh_intersect_volume_instancing(kg, ray, isect, visibility);
-# else
- return bvh_intersect_volume(kg, ray, isect, visibility);
-# endif /* __INSTANCING__ */
-# endif /* __KERNEL_CPU__ */
-# endif /* __KERNEL_OPTIX__ */
+# endif /* __KERNEL_OPTIX__ */
}
#endif /* __VOLUME__ */
@@ -599,11 +528,6 @@ ccl_device_intersect uint scene_intersect_volume_all(KernelGlobals *kg,
}
# endif /* __OBJECT_MOTION__ */
-# ifdef __INSTANCING__
- if (kernel_data.bvh.have_instancing) {
- return bvh_intersect_volume_all_instancing(kg, ray, isect, max_hits, visibility);
- }
-# endif /* __INSTANCING__ */
return bvh_intersect_volume_all(kg, ray, isect, max_hits, visibility);
}
#endif /* __VOLUME_RECORD_ALL__ */
diff --git a/intern/cycles/kernel/bvh/bvh_local.h b/intern/cycles/kernel/bvh/bvh_local.h
index 7a069ef1108..4006c9c1632 100644
--- a/intern/cycles/kernel/bvh/bvh_local.h
+++ b/intern/cycles/kernel/bvh/bvh_local.h
@@ -17,13 +17,6 @@
* limitations under the License.
*/
-#ifdef __QBVH__
-# include "kernel/bvh/qbvh_local.h"
-# ifdef __KERNEL_AVX2__
-# include "kernel/bvh/obvh_local.h"
-# endif
-#endif
-
#if BVH_FEATURE(BVH_HAIR)
# define NODE_INTERSECT bvh_node_intersect
#else
@@ -88,26 +81,6 @@ ccl_device_inline
object = local_object;
}
-#if defined(__KERNEL_SSE2__)
- const shuffle_swap_t shuf_identity = shuffle_swap_identity();
- const shuffle_swap_t shuf_swap = shuffle_swap_swap();
-
- const ssef pn = cast(ssei(0, 0, 0x80000000, 0x80000000));
- ssef Psplat[3], idirsplat[3];
-# if BVH_FEATURE(BVH_HAIR)
- ssef tnear(0.0f), tfar(isect_t);
-# endif
- shuffle_swap_t shufflexyz[3];
-
- Psplat[0] = ssef(P.x);
- Psplat[1] = ssef(P.y);
- Psplat[2] = ssef(P.z);
-
- ssef tsplat(0.0f, 0.0f, -isect_t, -isect_t);
-
- gen_idirsplat_swap(pn, shuf_identity, shuf_swap, idir, idirsplat, shufflexyz);
-#endif
-
/* traversal loop */
do {
do {
@@ -117,33 +90,16 @@ ccl_device_inline
float dist[2];
float4 cnodes = kernel_tex_fetch(__bvh_nodes, node_addr + 0);
-#if !defined(__KERNEL_SSE2__)
traverse_mask = NODE_INTERSECT(kg,
P,
-# if BVH_FEATURE(BVH_HAIR)
+#if BVH_FEATURE(BVH_HAIR)
dir,
-# endif
+#endif
idir,
isect_t,
node_addr,
PATH_RAY_ALL_VISIBILITY,
dist);
-#else // __KERNEL_SSE2__
- traverse_mask = NODE_INTERSECT(kg,
- P,
- dir,
-# if BVH_FEATURE(BVH_HAIR)
- tnear,
- tfar,
-# endif
- tsplat,
- Psplat,
- idirsplat,
- shufflexyz,
- node_addr,
- PATH_RAY_ALL_VISIBILITY,
- dist);
-#endif // __KERNEL_SSE2__
node_addr = __float_as_int(cnodes.z);
node_addr_child1 = __float_as_int(cnodes.w);
@@ -247,20 +203,7 @@ ccl_device_inline bool BVH_FUNCTION_NAME(KernelGlobals *kg,
uint *lcg_state,
int max_hits)
{
- switch (kernel_data.bvh.bvh_layout) {
-#ifdef __KERNEL_AVX2__
- case BVH_LAYOUT_BVH8:
- return BVH_FUNCTION_FULL_NAME(OBVH)(kg, ray, local_isect, local_object, lcg_state, max_hits);
-#endif
-#ifdef __QBVH__
- case BVH_LAYOUT_BVH4:
- return BVH_FUNCTION_FULL_NAME(QBVH)(kg, ray, local_isect, local_object, lcg_state, max_hits);
-#endif
- case BVH_LAYOUT_BVH2:
- return BVH_FUNCTION_FULL_NAME(BVH)(kg, ray, local_isect, local_object, lcg_state, max_hits);
- }
- kernel_assert(!"Should not happen");
- return false;
+ return BVH_FUNCTION_FULL_NAME(BVH)(kg, ray, local_isect, local_object, lcg_state, max_hits);
}
#undef BVH_FUNCTION_NAME
diff --git a/intern/cycles/kernel/bvh/bvh_nodes.h b/intern/cycles/kernel/bvh/bvh_nodes.h
index db598d1c7fa..5367bdb633c 100644
--- a/intern/cycles/kernel/bvh/bvh_nodes.h
+++ b/intern/cycles/kernel/bvh/bvh_nodes.h
@@ -28,7 +28,6 @@ ccl_device_forceinline Transform bvh_unaligned_node_fetch_space(KernelGlobals *k
return space;
}
-#if !defined(__KERNEL_SSE2__)
ccl_device_forceinline int bvh_aligned_node_intersect(KernelGlobals *kg,
const float3 P,
const float3 idir,
@@ -39,9 +38,9 @@ ccl_device_forceinline int bvh_aligned_node_intersect(KernelGlobals *kg,
{
/* fetch node data */
-# ifdef __VISIBILITY_FLAG__
+#ifdef __VISIBILITY_FLAG__
float4 cnodes = kernel_tex_fetch(__bvh_nodes, node_addr + 0);
-# endif
+#endif
float4 node0 = kernel_tex_fetch(__bvh_nodes, node_addr + 1);
float4 node1 = kernel_tex_fetch(__bvh_nodes, node_addr + 2);
float4 node2 = kernel_tex_fetch(__bvh_nodes, node_addr + 3);
@@ -68,13 +67,13 @@ ccl_device_forceinline int bvh_aligned_node_intersect(KernelGlobals *kg,
dist[0] = c0min;
dist[1] = c1min;
-# ifdef __VISIBILITY_FLAG__
+#ifdef __VISIBILITY_FLAG__
/* this visibility test gives a 5% performance hit, how to solve? */
return (((c0max >= c0min) && (__float_as_uint(cnodes.x) & visibility)) ? 1 : 0) |
(((c1max >= c1min) && (__float_as_uint(cnodes.y) & visibility)) ? 2 : 0);
-# else
+#else
return ((c0max >= c0min) ? 1 : 0) | ((c1max >= c1min) ? 2 : 0);
-# endif
+#endif
}
ccl_device_forceinline bool bvh_unaligned_node_intersect_child(KernelGlobals *kg,
@@ -113,21 +112,21 @@ ccl_device_forceinline int bvh_unaligned_node_intersect(KernelGlobals *kg,
float dist[2])
{
int mask = 0;
-# ifdef __VISIBILITY_FLAG__
+#ifdef __VISIBILITY_FLAG__
float4 cnodes = kernel_tex_fetch(__bvh_nodes, node_addr + 0);
-# endif
+#endif
if (bvh_unaligned_node_intersect_child(kg, P, dir, t, node_addr, 0, &dist[0])) {
-# ifdef __VISIBILITY_FLAG__
+#ifdef __VISIBILITY_FLAG__
if ((__float_as_uint(cnodes.x) & visibility))
-# endif
+#endif
{
mask |= 1;
}
}
if (bvh_unaligned_node_intersect_child(kg, P, dir, t, node_addr, 1, &dist[1])) {
-# ifdef __VISIBILITY_FLAG__
+#ifdef __VISIBILITY_FLAG__
if ((__float_as_uint(cnodes.y) & visibility))
-# endif
+#endif
{
mask |= 2;
}
@@ -152,125 +151,3 @@ ccl_device_forceinline int bvh_node_intersect(KernelGlobals *kg,
return bvh_aligned_node_intersect(kg, P, idir, t, node_addr, visibility, dist);
}
}
-
-#else /* !defined(__KERNEL_SSE2__) */
-
-int ccl_device_forceinline bvh_aligned_node_intersect(KernelGlobals *kg,
- const float3 &P,
- const float3 &dir,
- const ssef &tsplat,
- const ssef Psplat[3],
- const ssef idirsplat[3],
- const shuffle_swap_t shufflexyz[3],
- const int node_addr,
- const uint visibility,
- float dist[2])
-{
- /* Intersect two child bounding boxes, SSE3 version adapted from Embree */
- const ssef pn = cast(ssei(0, 0, 0x80000000, 0x80000000));
-
- /* fetch node data */
- const ssef *bvh_nodes = (ssef *)kg->__bvh_nodes.data + node_addr;
-
- /* intersect ray against child nodes */
- const ssef tminmaxx = (shuffle_swap(bvh_nodes[1], shufflexyz[0]) - Psplat[0]) * idirsplat[0];
- const ssef tminmaxy = (shuffle_swap(bvh_nodes[2], shufflexyz[1]) - Psplat[1]) * idirsplat[1];
- const ssef tminmaxz = (shuffle_swap(bvh_nodes[3], shufflexyz[2]) - Psplat[2]) * idirsplat[2];
-
- /* calculate { c0min, c1min, -c0max, -c1max} */
- ssef minmax = max(max(tminmaxx, tminmaxy), max(tminmaxz, tsplat));
- const ssef tminmax = minmax ^ pn;
- const sseb lrhit = tminmax <= shuffle<2, 3, 0, 1>(tminmax);
-
- dist[0] = tminmax[0];
- dist[1] = tminmax[1];
-
- int mask = movemask(lrhit);
-
-# ifdef __VISIBILITY_FLAG__
- /* this visibility test gives a 5% performance hit, how to solve? */
- float4 cnodes = kernel_tex_fetch(__bvh_nodes, node_addr + 0);
- int cmask = (((mask & 1) && (__float_as_uint(cnodes.x) & visibility)) ? 1 : 0) |
- (((mask & 2) && (__float_as_uint(cnodes.y) & visibility)) ? 2 : 0);
- return cmask;
-# else
- return mask & 3;
-# endif
-}
-
-ccl_device_forceinline int bvh_unaligned_node_intersect(KernelGlobals *kg,
- const float3 P,
- const float3 dir,
- const ssef &isect_near,
- const ssef &isect_far,
- const int node_addr,
- const uint visibility,
- float dist[2])
-{
- Transform space0 = bvh_unaligned_node_fetch_space(kg, node_addr, 0);
- Transform space1 = bvh_unaligned_node_fetch_space(kg, node_addr, 1);
-
- float3 aligned_dir0 = transform_direction(&space0, dir),
- aligned_dir1 = transform_direction(&space1, dir);
- float3 aligned_P0 = transform_point(&space0, P), aligned_P1 = transform_point(&space1, P);
- float3 nrdir0 = -bvh_inverse_direction(aligned_dir0),
- nrdir1 = -bvh_inverse_direction(aligned_dir1);
-
- ssef lower_x = ssef(aligned_P0.x * nrdir0.x, aligned_P1.x * nrdir1.x, 0.0f, 0.0f),
- lower_y = ssef(aligned_P0.y * nrdir0.y, aligned_P1.y * nrdir1.y, 0.0f, 0.0f),
- lower_z = ssef(aligned_P0.z * nrdir0.z, aligned_P1.z * nrdir1.z, 0.0f, 0.0f);
-
- ssef upper_x = lower_x - ssef(nrdir0.x, nrdir1.x, 0.0f, 0.0f),
- upper_y = lower_y - ssef(nrdir0.y, nrdir1.y, 0.0f, 0.0f),
- upper_z = lower_z - ssef(nrdir0.z, nrdir1.z, 0.0f, 0.0f);
-
- ssef tnear_x = min(lower_x, upper_x);
- ssef tnear_y = min(lower_y, upper_y);
- ssef tnear_z = min(lower_z, upper_z);
- ssef tfar_x = max(lower_x, upper_x);
- ssef tfar_y = max(lower_y, upper_y);
- ssef tfar_z = max(lower_z, upper_z);
-
- const ssef tnear = max4(isect_near, tnear_x, tnear_y, tnear_z);
- const ssef tfar = min4(isect_far, tfar_x, tfar_y, tfar_z);
- sseb vmask = tnear <= tfar;
- dist[0] = tnear.f[0];
- dist[1] = tnear.f[1];
-
- int mask = (int)movemask(vmask);
-
-# ifdef __VISIBILITY_FLAG__
- /* this visibility test gives a 5% performance hit, how to solve? */
- float4 cnodes = kernel_tex_fetch(__bvh_nodes, node_addr + 0);
- int cmask = (((mask & 1) && (__float_as_uint(cnodes.x) & visibility)) ? 1 : 0) |
- (((mask & 2) && (__float_as_uint(cnodes.y) & visibility)) ? 2 : 0);
- return cmask;
-# else
- return mask & 3;
-# endif
-}
-
-ccl_device_forceinline int bvh_node_intersect(KernelGlobals *kg,
- const float3 &P,
- const float3 &dir,
- const ssef &isect_near,
- const ssef &isect_far,
- const ssef &tsplat,
- const ssef Psplat[3],
- const ssef idirsplat[3],
- const shuffle_swap_t shufflexyz[3],
- const int node_addr,
- const uint visibility,
- float dist[2])
-{
- float4 node = kernel_tex_fetch(__bvh_nodes, node_addr);
- if (__float_as_uint(node.x) & PATH_RAY_NODE_UNALIGNED) {
- return bvh_unaligned_node_intersect(
- kg, P, dir, isect_near, isect_far, node_addr, visibility, dist);
- }
- else {
- return bvh_aligned_node_intersect(
- kg, P, dir, tsplat, Psplat, idirsplat, shufflexyz, node_addr, visibility, dist);
- }
-}
-#endif /* !defined(__KERNEL_SSE2__) */
diff --git a/intern/cycles/kernel/bvh/bvh_shadow_all.h b/intern/cycles/kernel/bvh/bvh_shadow_all.h
index 268bb149970..dccd257d2de 100644
--- a/intern/cycles/kernel/bvh/bvh_shadow_all.h
+++ b/intern/cycles/kernel/bvh/bvh_shadow_all.h
@@ -17,13 +17,6 @@
* limitations under the License.
*/
-#ifdef __QBVH__
-# include "kernel/bvh/qbvh_shadow_all.h"
-# ifdef __KERNEL_AVX2__
-# include "kernel/bvh/obvh_shadow_all.h"
-# endif
-#endif
-
#if BVH_FEATURE(BVH_HAIR)
# define NODE_INTERSECT bvh_node_intersect
#else
@@ -34,7 +27,6 @@
* enabled/disabled. This way we can compile optimized versions for each case
* without new features slowing things down.
*
- * BVH_INSTANCING: object instancing
* BVH_HAIR: hair curve rendering
* BVH_MOTION: motion blur rendering
*/
@@ -76,33 +68,11 @@ ccl_device_inline
Transform ob_itfm;
#endif
-#if BVH_FEATURE(BVH_INSTANCING)
int num_hits_in_instance = 0;
-#endif
*num_hits = 0;
isect_array->t = tmax;
-#if defined(__KERNEL_SSE2__)
- const shuffle_swap_t shuf_identity = shuffle_swap_identity();
- const shuffle_swap_t shuf_swap = shuffle_swap_swap();
-
- const ssef pn = cast(ssei(0, 0, 0x80000000, 0x80000000));
- ssef Psplat[3], idirsplat[3];
-# if BVH_FEATURE(BVH_HAIR)
- ssef tnear(0.0f), tfar(isect_t);
-# endif
- shuffle_swap_t shufflexyz[3];
-
- Psplat[0] = ssef(P.x);
- Psplat[1] = ssef(P.y);
- Psplat[2] = ssef(P.z);
-
- ssef tsplat(0.0f, 0.0f, -isect_t, -isect_t);
-
- gen_idirsplat_swap(pn, shuf_identity, shuf_swap, idir, idirsplat, shufflexyz);
-#endif /* __KERNEL_SSE2__ */
-
/* traversal loop */
do {
do {
@@ -112,33 +82,16 @@ ccl_device_inline
float dist[2];
float4 cnodes = kernel_tex_fetch(__bvh_nodes, node_addr + 0);
-#if !defined(__KERNEL_SSE2__)
traverse_mask = NODE_INTERSECT(kg,
P,
-# if BVH_FEATURE(BVH_HAIR)
+#if BVH_FEATURE(BVH_HAIR)
dir,
-# endif
+#endif
idir,
isect_t,
node_addr,
visibility,
dist);
-#else // __KERNEL_SSE2__
- traverse_mask = NODE_INTERSECT(kg,
- P,
- dir,
-# if BVH_FEATURE(BVH_HAIR)
- tnear,
- tfar,
-# endif
- tsplat,
- Psplat,
- idirsplat,
- shufflexyz,
- node_addr,
- visibility,
- dist);
-#endif // __KERNEL_SSE2__
node_addr = __float_as_int(cnodes.z);
node_addr_child1 = __float_as_int(cnodes.w);
@@ -174,9 +127,7 @@ ccl_device_inline
float4 leaf = kernel_tex_fetch(__bvh_leaf_nodes, (-node_addr - 1));
int prim_addr = __float_as_int(leaf.x);
-#if BVH_FEATURE(BVH_INSTANCING)
if (prim_addr >= 0) {
-#endif
const int prim_addr2 = __float_as_int(leaf.y);
const uint type = __float_as_int(leaf.w);
const uint p_type = type & PRIMITIVE_ALL;
@@ -207,31 +158,13 @@ ccl_device_inline
}
#endif
#if BVH_FEATURE(BVH_HAIR)
- case PRIMITIVE_CURVE:
- case PRIMITIVE_MOTION_CURVE: {
+ case PRIMITIVE_CURVE_THICK:
+ case PRIMITIVE_MOTION_CURVE_THICK:
+ case PRIMITIVE_CURVE_RIBBON:
+ case PRIMITIVE_MOTION_CURVE_RIBBON: {
const uint curve_type = kernel_tex_fetch(__prim_type, prim_addr);
- if (kernel_data.curve.curveflags & CURVE_KN_INTERPOLATE) {
- hit = cardinal_curve_intersect(kg,
- isect_array,
- P,
- dir,
- visibility,
- object,
- prim_addr,
- ray->time,
- curve_type);
- }
- else {
- hit = curve_intersect(kg,
- isect_array,
- P,
- dir,
- visibility,
- object,
- prim_addr,
- ray->time,
- curve_type);
- }
+ hit = curve_intersect(
+ kg, isect_array, P, dir, visibility, object, prim_addr, ray->time, curve_type);
break;
}
#endif
@@ -276,9 +209,7 @@ ccl_device_inline
/* move on to next entry in intersections array */
isect_array++;
(*num_hits)++;
-#if BVH_FEATURE(BVH_INSTANCING)
num_hits_in_instance++;
-#endif
isect_array->t = isect_t;
}
@@ -286,32 +217,19 @@ ccl_device_inline
prim_addr++;
}
}
-#if BVH_FEATURE(BVH_INSTANCING)
else {
/* instance push */
object = kernel_tex_fetch(__prim_object, -prim_addr - 1);
-# if BVH_FEATURE(BVH_MOTION)
+#if BVH_FEATURE(BVH_MOTION)
isect_t = bvh_instance_motion_push(kg, object, ray, &P, &dir, &idir, isect_t, &ob_itfm);
-# else
+#else
isect_t = bvh_instance_push(kg, object, ray, &P, &dir, &idir, isect_t);
-# endif
+#endif
num_hits_in_instance = 0;
isect_array->t = isect_t;
-# if defined(__KERNEL_SSE2__)
- Psplat[0] = ssef(P.x);
- Psplat[1] = ssef(P.y);
- Psplat[2] = ssef(P.z);
-
- tsplat = ssef(0.0f, 0.0f, -isect_t, -isect_t);
-# if BVH_FEATURE(BVH_HAIR)
- tfar = ssef(isect_t);
-# endif
- gen_idirsplat_swap(pn, shuf_identity, shuf_swap, idir, idirsplat, shufflexyz);
-# endif
-
++stack_ptr;
kernel_assert(stack_ptr < BVH_STACK_SIZE);
traversal_stack[stack_ptr] = ENTRYPOINT_SENTINEL;
@@ -319,10 +237,8 @@ ccl_device_inline
node_addr = kernel_tex_fetch(__object_node, object);
}
}
-#endif /* FEATURE(BVH_INSTANCING) */
} while (node_addr != ENTRYPOINT_SENTINEL);
-#if BVH_FEATURE(BVH_INSTANCING)
if (stack_ptr >= 0) {
kernel_assert(object != OBJECT_NONE);
@@ -330,11 +246,11 @@ ccl_device_inline
if (num_hits_in_instance) {
float t_fac;
-# if BVH_FEATURE(BVH_MOTION)
+#if BVH_FEATURE(BVH_MOTION)
bvh_instance_motion_pop_factor(kg, object, ray, &P, &dir, &idir, &t_fac, &ob_itfm);
-# else
+#else
bvh_instance_pop_factor(kg, object, ray, &P, &dir, &idir, &t_fac);
-# endif
+#endif
/* scale isect->t to adjust for instancing */
for (int i = 0; i < num_hits_in_instance; i++) {
@@ -342,33 +258,20 @@ ccl_device_inline
}
}
else {
-# if BVH_FEATURE(BVH_MOTION)
+#if BVH_FEATURE(BVH_MOTION)
bvh_instance_motion_pop(kg, object, ray, &P, &dir, &idir, FLT_MAX, &ob_itfm);
-# else
+#else
bvh_instance_pop(kg, object, ray, &P, &dir, &idir, FLT_MAX);
-# endif
+#endif
}
isect_t = tmax;
isect_array->t = isect_t;
-# if defined(__KERNEL_SSE2__)
- Psplat[0] = ssef(P.x);
- Psplat[1] = ssef(P.y);
- Psplat[2] = ssef(P.z);
-
- tsplat = ssef(0.0f, 0.0f, -isect_t, -isect_t);
-# if BVH_FEATURE(BVH_HAIR)
- tfar = ssef(isect_t);
-# endif
- gen_idirsplat_swap(pn, shuf_identity, shuf_swap, idir, idirsplat, shufflexyz);
-# endif
-
object = OBJECT_NONE;
node_addr = traversal_stack[stack_ptr];
--stack_ptr;
}
-#endif /* FEATURE(BVH_INSTANCING) */
} while (node_addr != ENTRYPOINT_SENTINEL);
return false;
@@ -381,20 +284,7 @@ ccl_device_inline bool BVH_FUNCTION_NAME(KernelGlobals *kg,
const uint max_hits,
uint *num_hits)
{
- switch (kernel_data.bvh.bvh_layout) {
-#ifdef __KERNEL_AVX2__
- case BVH_LAYOUT_BVH8:
- return BVH_FUNCTION_FULL_NAME(OBVH)(kg, ray, isect_array, visibility, max_hits, num_hits);
-#endif
-#ifdef __QBVH__
- case BVH_LAYOUT_BVH4:
- return BVH_FUNCTION_FULL_NAME(QBVH)(kg, ray, isect_array, visibility, max_hits, num_hits);
-#endif
- case BVH_LAYOUT_BVH2:
- return BVH_FUNCTION_FULL_NAME(BVH)(kg, ray, isect_array, visibility, max_hits, num_hits);
- }
- kernel_assert(!"Should not happen");
- return false;
+ return BVH_FUNCTION_FULL_NAME(BVH)(kg, ray, isect_array, visibility, max_hits, num_hits);
}
#undef BVH_FUNCTION_NAME
diff --git a/intern/cycles/kernel/bvh/bvh_traversal.h b/intern/cycles/kernel/bvh/bvh_traversal.h
index 18afc6ae4eb..8b2699ab807 100644
--- a/intern/cycles/kernel/bvh/bvh_traversal.h
+++ b/intern/cycles/kernel/bvh/bvh_traversal.h
@@ -17,13 +17,6 @@
* limitations under the License.
*/
-#ifdef __QBVH__
-# include "kernel/bvh/qbvh_traversal.h"
-#endif
-#ifdef __KERNEL_AVX2__
-# include "kernel/bvh/obvh_traversal.h"
-#endif
-
#if BVH_FEATURE(BVH_HAIR)
# define NODE_INTERSECT bvh_node_intersect
#else
@@ -34,7 +27,6 @@
* enabled/disabled. This way we can compile optimized versions for each case
* without new features slowing things down.
*
- * BVH_INSTANCING: object instancing
* BVH_HAIR: hair curve rendering
* BVH_MOTION: motion blur rendering
*/
@@ -77,26 +69,6 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg,
BVH_DEBUG_INIT();
-#if defined(__KERNEL_SSE2__)
- const shuffle_swap_t shuf_identity = shuffle_swap_identity();
- const shuffle_swap_t shuf_swap = shuffle_swap_swap();
-
- const ssef pn = cast(ssei(0, 0, 0x80000000, 0x80000000));
- ssef Psplat[3], idirsplat[3];
-# if BVH_FEATURE(BVH_HAIR)
- ssef tnear(0.0f), tfar(isect->t);
-# endif
- shuffle_swap_t shufflexyz[3];
-
- Psplat[0] = ssef(P.x);
- Psplat[1] = ssef(P.y);
- Psplat[2] = ssef(P.z);
-
- ssef tsplat(0.0f, 0.0f, -isect->t, -isect->t);
-
- gen_idirsplat_swap(pn, shuf_identity, shuf_swap, idir, idirsplat, shufflexyz);
-#endif
-
/* traversal loop */
do {
do {
@@ -106,37 +78,18 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg,
float dist[2];
float4 cnodes = kernel_tex_fetch(__bvh_nodes, node_addr + 0);
-#if !defined(__KERNEL_SSE2__)
{
traverse_mask = NODE_INTERSECT(kg,
P,
-# if BVH_FEATURE(BVH_HAIR)
+#if BVH_FEATURE(BVH_HAIR)
dir,
-# endif
+#endif
idir,
isect->t,
node_addr,
visibility,
dist);
}
-#else // __KERNEL_SSE2__
- {
- traverse_mask = NODE_INTERSECT(kg,
- P,
- dir,
-# if BVH_FEATURE(BVH_HAIR)
- tnear,
- tfar,
-# endif
- tsplat,
- Psplat,
- idirsplat,
- shufflexyz,
- node_addr,
- visibility,
- dist);
- }
-#endif // __KERNEL_SSE2__
node_addr = __float_as_int(cnodes.z);
node_addr_child1 = __float_as_int(cnodes.w);
@@ -173,9 +126,7 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg,
float4 leaf = kernel_tex_fetch(__bvh_leaf_nodes, (-node_addr - 1));
int prim_addr = __float_as_int(leaf.x);
-#if BVH_FEATURE(BVH_INSTANCING)
if (prim_addr >= 0) {
-#endif
const int prim_addr2 = __float_as_int(leaf.y);
const uint type = __float_as_int(leaf.w);
@@ -191,17 +142,8 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg,
kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type);
if (triangle_intersect(kg, isect, P, dir, visibility, object, prim_addr)) {
/* shadow ray early termination */
-#if defined(__KERNEL_SSE2__)
if (visibility & PATH_RAY_SHADOW_OPAQUE)
return true;
- tsplat = ssef(0.0f, 0.0f, -isect->t, -isect->t);
-# if BVH_FEATURE(BVH_HAIR)
- tfar = ssef(isect->t);
-# endif
-#else
- if (visibility & PATH_RAY_SHADOW_OPAQUE)
- return true;
-#endif
}
}
break;
@@ -214,51 +156,28 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg,
if (motion_triangle_intersect(
kg, isect, P, dir, ray->time, visibility, object, prim_addr)) {
/* shadow ray early termination */
-# if defined(__KERNEL_SSE2__)
- if (visibility & PATH_RAY_SHADOW_OPAQUE)
- return true;
- tsplat = ssef(0.0f, 0.0f, -isect->t, -isect->t);
-# if BVH_FEATURE(BVH_HAIR)
- tfar = ssef(isect->t);
-# endif
-# else
if (visibility & PATH_RAY_SHADOW_OPAQUE)
return true;
-# endif
}
}
break;
}
#endif /* BVH_FEATURE(BVH_MOTION) */
#if BVH_FEATURE(BVH_HAIR)
- case PRIMITIVE_CURVE:
- case PRIMITIVE_MOTION_CURVE: {
+ case PRIMITIVE_CURVE_THICK:
+ case PRIMITIVE_MOTION_CURVE_THICK:
+ case PRIMITIVE_CURVE_RIBBON:
+ case PRIMITIVE_MOTION_CURVE_RIBBON: {
for (; prim_addr < prim_addr2; prim_addr++) {
BVH_DEBUG_NEXT_INTERSECTION();
const uint curve_type = kernel_tex_fetch(__prim_type, prim_addr);
kernel_assert((curve_type & PRIMITIVE_ALL) == (type & PRIMITIVE_ALL));
- bool hit;
- if (kernel_data.curve.curveflags & CURVE_KN_INTERPOLATE) {
- hit = cardinal_curve_intersect(
- kg, isect, P, dir, visibility, object, prim_addr, ray->time, curve_type);
- }
- else {
- hit = curve_intersect(
- kg, isect, P, dir, visibility, object, prim_addr, ray->time, curve_type);
- }
+ const bool hit = curve_intersect(
+ kg, isect, P, dir, visibility, object, prim_addr, ray->time, curve_type);
if (hit) {
/* shadow ray early termination */
-# if defined(__KERNEL_SSE2__)
if (visibility & PATH_RAY_SHADOW_OPAQUE)
return true;
- tsplat = ssef(0.0f, 0.0f, -isect->t, -isect->t);
-# if BVH_FEATURE(BVH_HAIR)
- tfar = ssef(isect->t);
-# endif
-# else
- if (visibility & PATH_RAY_SHADOW_OPAQUE)
- return true;
-# endif
}
}
break;
@@ -266,30 +185,16 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg,
#endif /* BVH_FEATURE(BVH_HAIR) */
}
}
-#if BVH_FEATURE(BVH_INSTANCING)
else {
/* instance push */
object = kernel_tex_fetch(__prim_object, -prim_addr - 1);
-# if BVH_FEATURE(BVH_MOTION)
+#if BVH_FEATURE(BVH_MOTION)
isect->t = bvh_instance_motion_push(
kg, object, ray, &P, &dir, &idir, isect->t, &ob_itfm);
-# else
+#else
isect->t = bvh_instance_push(kg, object, ray, &P, &dir, &idir, isect->t);
-# endif
-
-# if defined(__KERNEL_SSE2__)
- Psplat[0] = ssef(P.x);
- Psplat[1] = ssef(P.y);
- Psplat[2] = ssef(P.z);
-
- tsplat = ssef(0.0f, 0.0f, -isect->t, -isect->t);
-# if BVH_FEATURE(BVH_HAIR)
- tfar = ssef(isect->t);
-# endif
-
- gen_idirsplat_swap(pn, shuf_identity, shuf_swap, idir, idirsplat, shufflexyz);
-# endif
+#endif
++stack_ptr;
kernel_assert(stack_ptr < BVH_STACK_SIZE);
@@ -300,38 +205,22 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg,
BVH_DEBUG_NEXT_INSTANCE();
}
}
-#endif /* FEATURE(BVH_INSTANCING) */
} while (node_addr != ENTRYPOINT_SENTINEL);
-#if BVH_FEATURE(BVH_INSTANCING)
if (stack_ptr >= 0) {
kernel_assert(object != OBJECT_NONE);
/* instance pop */
-# if BVH_FEATURE(BVH_MOTION)
+#if BVH_FEATURE(BVH_MOTION)
isect->t = bvh_instance_motion_pop(kg, object, ray, &P, &dir, &idir, isect->t, &ob_itfm);
-# else
+#else
isect->t = bvh_instance_pop(kg, object, ray, &P, &dir, &idir, isect->t);
-# endif
-
-# if defined(__KERNEL_SSE2__)
- Psplat[0] = ssef(P.x);
- Psplat[1] = ssef(P.y);
- Psplat[2] = ssef(P.z);
-
- tsplat = ssef(0.0f, 0.0f, -isect->t, -isect->t);
-# if BVH_FEATURE(BVH_HAIR)
- tfar = ssef(isect->t);
-# endif
-
- gen_idirsplat_swap(pn, shuf_identity, shuf_swap, idir, idirsplat, shufflexyz);
-# endif
+#endif
object = OBJECT_NONE;
node_addr = traversal_stack[stack_ptr];
--stack_ptr;
}
-#endif /* FEATURE(BVH_INSTANCING) */
} while (node_addr != ENTRYPOINT_SENTINEL);
return (isect->prim != PRIM_NONE);
@@ -342,20 +231,7 @@ ccl_device_inline bool BVH_FUNCTION_NAME(KernelGlobals *kg,
Intersection *isect,
const uint visibility)
{
- switch (kernel_data.bvh.bvh_layout) {
-#ifdef __KERNEL_AVX2__
- case BVH_LAYOUT_BVH8:
- return BVH_FUNCTION_FULL_NAME(OBVH)(kg, ray, isect, visibility);
-#endif
-#ifdef __QBVH__
- case BVH_LAYOUT_BVH4:
- return BVH_FUNCTION_FULL_NAME(QBVH)(kg, ray, isect, visibility);
-#endif /* __QBVH__ */
- case BVH_LAYOUT_BVH2:
- return BVH_FUNCTION_FULL_NAME(BVH)(kg, ray, isect, visibility);
- }
- kernel_assert(!"Should not happen");
- return false;
+ return BVH_FUNCTION_FULL_NAME(BVH)(kg, ray, isect, visibility);
}
#undef BVH_FUNCTION_NAME
diff --git a/intern/cycles/kernel/bvh/bvh_types.h b/intern/cycles/kernel/bvh/bvh_types.h
index 84dc0dbaef5..b173568266b 100644
--- a/intern/cycles/kernel/bvh/bvh_types.h
+++ b/intern/cycles/kernel/bvh/bvh_types.h
@@ -31,13 +31,10 @@ CCL_NAMESPACE_BEGIN
/* 64 object BVH + 64 mesh BVH + 64 object node splitting */
#define BVH_STACK_SIZE 192
-#define BVH_QSTACK_SIZE 384
-#define BVH_OSTACK_SIZE 768
/* BVH intersection function variations */
-#define BVH_INSTANCING 1
-#define BVH_MOTION 2
-#define BVH_HAIR 4
+#define BVH_MOTION 1
+#define BVH_HAIR 2
#define BVH_NAME_JOIN(x, y) x##_##y
#define BVH_NAME_EVAL(x, y) BVH_NAME_JOIN(x, y)
diff --git a/intern/cycles/kernel/bvh/bvh_volume.h b/intern/cycles/kernel/bvh/bvh_volume.h
index c83b0d783f4..1f2ea47269b 100644
--- a/intern/cycles/kernel/bvh/bvh_volume.h
+++ b/intern/cycles/kernel/bvh/bvh_volume.h
@@ -17,13 +17,6 @@
* limitations under the License.
*/
-#ifdef __QBVH__
-# include "kernel/bvh/qbvh_volume.h"
-# ifdef __KERNEL_AVX2__
-# include "kernel/bvh/obvh_volume.h"
-# endif
-#endif
-
#if BVH_FEATURE(BVH_HAIR)
# define NODE_INTERSECT bvh_node_intersect
#else
@@ -34,7 +27,6 @@
* various features can be enabled/disabled. This way we can compile optimized
* versions for each case without new features slowing things down.
*
- * BVH_INSTANCING: object instancing
* BVH_MOTION: motion blur rendering
*/
@@ -79,26 +71,6 @@ ccl_device_inline
isect->prim = PRIM_NONE;
isect->object = OBJECT_NONE;
-#if defined(__KERNEL_SSE2__)
- const shuffle_swap_t shuf_identity = shuffle_swap_identity();
- const shuffle_swap_t shuf_swap = shuffle_swap_swap();
-
- const ssef pn = cast(ssei(0, 0, 0x80000000, 0x80000000));
- ssef Psplat[3], idirsplat[3];
-# if BVH_FEATURE(BVH_HAIR)
- ssef tnear(0.0f), tfar(isect->t);
-# endif
- shuffle_swap_t shufflexyz[3];
-
- Psplat[0] = ssef(P.x);
- Psplat[1] = ssef(P.y);
- Psplat[2] = ssef(P.z);
-
- ssef tsplat(0.0f, 0.0f, -isect->t, -isect->t);
-
- gen_idirsplat_swap(pn, shuf_identity, shuf_swap, idir, idirsplat, shufflexyz);
-#endif
-
/* traversal loop */
do {
do {
@@ -108,33 +80,16 @@ ccl_device_inline
float dist[2];
float4 cnodes = kernel_tex_fetch(__bvh_nodes, node_addr + 0);
-#if !defined(__KERNEL_SSE2__)
traverse_mask = NODE_INTERSECT(kg,
P,
-# if BVH_FEATURE(BVH_HAIR)
+#if BVH_FEATURE(BVH_HAIR)
dir,
-# endif
+#endif
idir,
isect->t,
node_addr,
visibility,
dist);
-#else // __KERNEL_SSE2__
- traverse_mask = NODE_INTERSECT(kg,
- P,
- dir,
-# if BVH_FEATURE(BVH_HAIR)
- tnear,
- tfar,
-# endif
- tsplat,
- Psplat,
- idirsplat,
- shufflexyz,
- node_addr,
- visibility,
- dist);
-#endif // __KERNEL_SSE2__
node_addr = __float_as_int(cnodes.z);
node_addr_child1 = __float_as_int(cnodes.w);
@@ -170,9 +125,7 @@ ccl_device_inline
float4 leaf = kernel_tex_fetch(__bvh_leaf_nodes, (-node_addr - 1));
int prim_addr = __float_as_int(leaf.x);
-#if BVH_FEATURE(BVH_INSTANCING)
if (prim_addr >= 0) {
-#endif
const int prim_addr2 = __float_as_int(leaf.y);
const uint type = __float_as_int(leaf.w);
@@ -222,31 +175,17 @@ ccl_device_inline
}
}
}
-#if BVH_FEATURE(BVH_INSTANCING)
else {
/* instance push */
object = kernel_tex_fetch(__prim_object, -prim_addr - 1);
int object_flag = kernel_tex_fetch(__object_flag, object);
if (object_flag & SD_OBJECT_HAS_VOLUME) {
-# if BVH_FEATURE(BVH_MOTION)
+#if BVH_FEATURE(BVH_MOTION)
isect->t = bvh_instance_motion_push(
kg, object, ray, &P, &dir, &idir, isect->t, &ob_itfm);
-# else
+#else
isect->t = bvh_instance_push(kg, object, ray, &P, &dir, &idir, isect->t);
-# endif
-
-# if defined(__KERNEL_SSE2__)
- Psplat[0] = ssef(P.x);
- Psplat[1] = ssef(P.y);
- Psplat[2] = ssef(P.z);
-
- tsplat = ssef(0.0f, 0.0f, -isect->t, -isect->t);
-# if BVH_FEATURE(BVH_HAIR)
- tfar = ssef(isect->t);
-# endif
-
- gen_idirsplat_swap(pn, shuf_identity, shuf_swap, idir, idirsplat, shufflexyz);
-# endif
+#endif
++stack_ptr;
kernel_assert(stack_ptr < BVH_STACK_SIZE);
@@ -262,38 +201,22 @@ ccl_device_inline
}
}
}
-#endif /* FEATURE(BVH_INSTANCING) */
} while (node_addr != ENTRYPOINT_SENTINEL);
-#if BVH_FEATURE(BVH_INSTANCING)
if (stack_ptr >= 0) {
kernel_assert(object != OBJECT_NONE);
/* instance pop */
-# if BVH_FEATURE(BVH_MOTION)
+#if BVH_FEATURE(BVH_MOTION)
isect->t = bvh_instance_motion_pop(kg, object, ray, &P, &dir, &idir, isect->t, &ob_itfm);
-# else
+#else
isect->t = bvh_instance_pop(kg, object, ray, &P, &dir, &idir, isect->t);
-# endif
-
-# if defined(__KERNEL_SSE2__)
- Psplat[0] = ssef(P.x);
- Psplat[1] = ssef(P.y);
- Psplat[2] = ssef(P.z);
-
- tsplat = ssef(0.0f, 0.0f, -isect->t, -isect->t);
-# if BVH_FEATURE(BVH_HAIR)
- tfar = ssef(isect->t);
-# endif
-
- gen_idirsplat_swap(pn, shuf_identity, shuf_swap, idir, idirsplat, shufflexyz);
-# endif
+#endif
object = OBJECT_NONE;
node_addr = traversal_stack[stack_ptr];
--stack_ptr;
}
-#endif /* FEATURE(BVH_MOTION) */
} while (node_addr != ENTRYPOINT_SENTINEL);
return (isect->prim != PRIM_NONE);
@@ -304,20 +227,7 @@ ccl_device_inline bool BVH_FUNCTION_NAME(KernelGlobals *kg,
Intersection *isect,
const uint visibility)
{
- switch (kernel_data.bvh.bvh_layout) {
-#ifdef __KERNEL_AVX2__
- case BVH_LAYOUT_BVH8:
- return BVH_FUNCTION_FULL_NAME(OBVH)(kg, ray, isect, visibility);
-#endif
-#ifdef __QBVH__
- case BVH_LAYOUT_BVH4:
- return BVH_FUNCTION_FULL_NAME(QBVH)(kg, ray, isect, visibility);
-#endif
- case BVH_LAYOUT_BVH2:
- return BVH_FUNCTION_FULL_NAME(BVH)(kg, ray, isect, visibility);
- }
- kernel_assert(!"Should not happen");
- return false;
+ return BVH_FUNCTION_FULL_NAME(BVH)(kg, ray, isect, visibility);
}
#undef BVH_FUNCTION_NAME
diff --git a/intern/cycles/kernel/bvh/bvh_volume_all.h b/intern/cycles/kernel/bvh/bvh_volume_all.h
index ae8c4d12e8a..a8664cc4331 100644
--- a/intern/cycles/kernel/bvh/bvh_volume_all.h
+++ b/intern/cycles/kernel/bvh/bvh_volume_all.h
@@ -17,13 +17,6 @@
* limitations under the License.
*/
-#ifdef __QBVH__
-# include "kernel/bvh/qbvh_volume_all.h"
-# ifdef __KERNEL_AVX2__
-# include "kernel/bvh/obvh_volume_all.h"
-# endif
-#endif
-
#if BVH_FEATURE(BVH_HAIR)
# define NODE_INTERSECT bvh_node_intersect
#else
@@ -34,7 +27,6 @@
* various features can be enabled/disabled. This way we can compile optimized
* versions for each case without new features slowing things down.
*
- * BVH_INSTANCING: object instancing
* BVH_MOTION: motion blur rendering
*/
@@ -76,33 +68,11 @@ ccl_device_inline
Transform ob_itfm;
#endif
-#if BVH_FEATURE(BVH_INSTANCING)
int num_hits_in_instance = 0;
-#endif
uint num_hits = 0;
isect_array->t = tmax;
-#if defined(__KERNEL_SSE2__)
- const shuffle_swap_t shuf_identity = shuffle_swap_identity();
- const shuffle_swap_t shuf_swap = shuffle_swap_swap();
-
- const ssef pn = cast(ssei(0, 0, 0x80000000, 0x80000000));
- ssef Psplat[3], idirsplat[3];
-# if BVH_FEATURE(BVH_HAIR)
- ssef tnear(0.0f), tfar(isect_t);
-# endif
- shuffle_swap_t shufflexyz[3];
-
- Psplat[0] = ssef(P.x);
- Psplat[1] = ssef(P.y);
- Psplat[2] = ssef(P.z);
-
- ssef tsplat(0.0f, 0.0f, -isect_t, -isect_t);
-
- gen_idirsplat_swap(pn, shuf_identity, shuf_swap, idir, idirsplat, shufflexyz);
-#endif /* __KERNEL_SSE2__ */
-
/* traversal loop */
do {
do {
@@ -112,33 +82,16 @@ ccl_device_inline
float dist[2];
float4 cnodes = kernel_tex_fetch(__bvh_nodes, node_addr + 0);
-#if !defined(__KERNEL_SSE2__)
traverse_mask = NODE_INTERSECT(kg,
P,
-# if BVH_FEATURE(BVH_HAIR)
+#if BVH_FEATURE(BVH_HAIR)
dir,
-# endif
+#endif
idir,
isect_t,
node_addr,
visibility,
dist);
-#else // __KERNEL_SSE2__
- traverse_mask = NODE_INTERSECT(kg,
- P,
- dir,
-# if BVH_FEATURE(BVH_HAIR)
- tnear,
- tfar,
-# endif
- tsplat,
- Psplat,
- idirsplat,
- shufflexyz,
- node_addr,
- visibility,
- dist);
-#endif // __KERNEL_SSE2__
node_addr = __float_as_int(cnodes.z);
node_addr_child1 = __float_as_int(cnodes.w);
@@ -174,9 +127,7 @@ ccl_device_inline
float4 leaf = kernel_tex_fetch(__bvh_leaf_nodes, (-node_addr - 1));
int prim_addr = __float_as_int(leaf.x);
-#if BVH_FEATURE(BVH_INSTANCING)
if (prim_addr >= 0) {
-#endif
const int prim_addr2 = __float_as_int(leaf.y);
const uint type = __float_as_int(leaf.w);
bool hit;
@@ -204,25 +155,21 @@ ccl_device_inline
/* Move on to next entry in intersections array. */
isect_array++;
num_hits++;
-#if BVH_FEATURE(BVH_INSTANCING)
num_hits_in_instance++;
-#endif
isect_array->t = isect_t;
if (num_hits == max_hits) {
-#if BVH_FEATURE(BVH_INSTANCING)
if (object != OBJECT_NONE) {
-# if BVH_FEATURE(BVH_MOTION)
+#if BVH_FEATURE(BVH_MOTION)
float t_fac = 1.0f / len(transform_direction(&ob_itfm, dir));
-# else
+#else
Transform itfm = object_fetch_transform(
kg, object, OBJECT_INVERSE_TRANSFORM);
float t_fac = 1.0f / len(transform_direction(&itfm, dir));
-# endif
+#endif
for (int i = 0; i < num_hits_in_instance; i++) {
(isect_array - i - 1)->t *= t_fac;
}
}
-#endif /* BVH_FEATURE(BVH_INSTANCING) */
return num_hits;
}
}
@@ -248,25 +195,21 @@ ccl_device_inline
/* Move on to next entry in intersections array. */
isect_array++;
num_hits++;
-# if BVH_FEATURE(BVH_INSTANCING)
num_hits_in_instance++;
-# endif
isect_array->t = isect_t;
if (num_hits == max_hits) {
-# if BVH_FEATURE(BVH_INSTANCING)
if (object != OBJECT_NONE) {
-# if BVH_FEATURE(BVH_MOTION)
+# if BVH_FEATURE(BVH_MOTION)
float t_fac = 1.0f / len(transform_direction(&ob_itfm, dir));
-# else
+# else
Transform itfm = object_fetch_transform(
kg, object, OBJECT_INVERSE_TRANSFORM);
float t_fac = 1.0f / len(transform_direction(&itfm, dir));
-# endif
+# endif
for (int i = 0; i < num_hits_in_instance; i++) {
(isect_array - i - 1)->t *= t_fac;
}
}
-# endif /* BVH_FEATURE(BVH_INSTANCING) */
return num_hits;
}
}
@@ -279,35 +222,21 @@ ccl_device_inline
}
}
}
-#if BVH_FEATURE(BVH_INSTANCING)
else {
/* instance push */
object = kernel_tex_fetch(__prim_object, -prim_addr - 1);
int object_flag = kernel_tex_fetch(__object_flag, object);
if (object_flag & SD_OBJECT_HAS_VOLUME) {
-# if BVH_FEATURE(BVH_MOTION)
+#if BVH_FEATURE(BVH_MOTION)
isect_t = bvh_instance_motion_push(
kg, object, ray, &P, &dir, &idir, isect_t, &ob_itfm);
-# else
+#else
isect_t = bvh_instance_push(kg, object, ray, &P, &dir, &idir, isect_t);
-# endif
+#endif
num_hits_in_instance = 0;
isect_array->t = isect_t;
-# if defined(__KERNEL_SSE2__)
- Psplat[0] = ssef(P.x);
- Psplat[1] = ssef(P.y);
- Psplat[2] = ssef(P.z);
-
- tsplat = ssef(0.0f, 0.0f, -isect_t, -isect_t);
-# if BVH_FEATURE(BVH_HAIR)
- tfar = ssef(isect_t);
-# endif
-
- gen_idirsplat_swap(pn, shuf_identity, shuf_swap, idir, idirsplat, shufflexyz);
-# endif
-
++stack_ptr;
kernel_assert(stack_ptr < BVH_STACK_SIZE);
traversal_stack[stack_ptr] = ENTRYPOINT_SENTINEL;
@@ -322,55 +251,39 @@ ccl_device_inline
}
}
}
-#endif /* FEATURE(BVH_INSTANCING) */
} while (node_addr != ENTRYPOINT_SENTINEL);
-#if BVH_FEATURE(BVH_INSTANCING)
if (stack_ptr >= 0) {
kernel_assert(object != OBJECT_NONE);
/* Instance pop. */
if (num_hits_in_instance) {
float t_fac;
-# if BVH_FEATURE(BVH_MOTION)
+#if BVH_FEATURE(BVH_MOTION)
bvh_instance_motion_pop_factor(kg, object, ray, &P, &dir, &idir, &t_fac, &ob_itfm);
-# else
+#else
bvh_instance_pop_factor(kg, object, ray, &P, &dir, &idir, &t_fac);
-# endif
+#endif
/* Scale isect->t to adjust for instancing. */
for (int i = 0; i < num_hits_in_instance; i++) {
(isect_array - i - 1)->t *= t_fac;
}
}
else {
-# if BVH_FEATURE(BVH_MOTION)
+#if BVH_FEATURE(BVH_MOTION)
bvh_instance_motion_pop(kg, object, ray, &P, &dir, &idir, FLT_MAX, &ob_itfm);
-# else
+#else
bvh_instance_pop(kg, object, ray, &P, &dir, &idir, FLT_MAX);
-# endif
+#endif
}
isect_t = tmax;
isect_array->t = isect_t;
-# if defined(__KERNEL_SSE2__)
- Psplat[0] = ssef(P.x);
- Psplat[1] = ssef(P.y);
- Psplat[2] = ssef(P.z);
-
- tsplat = ssef(0.0f, 0.0f, -isect_t, -isect_t);
-# if BVH_FEATURE(BVH_HAIR)
- tfar = ssef(isect_t);
-# endif
-
- gen_idirsplat_swap(pn, shuf_identity, shuf_swap, idir, idirsplat, shufflexyz);
-# endif
-
object = OBJECT_NONE;
node_addr = traversal_stack[stack_ptr];
--stack_ptr;
}
-#endif /* FEATURE(BVH_INSTANCING) */
} while (node_addr != ENTRYPOINT_SENTINEL);
return num_hits;
@@ -382,20 +295,7 @@ ccl_device_inline uint BVH_FUNCTION_NAME(KernelGlobals *kg,
const uint max_hits,
const uint visibility)
{
- switch (kernel_data.bvh.bvh_layout) {
-#ifdef __KERNEL_AVX2__
- case BVH_LAYOUT_BVH8:
- return BVH_FUNCTION_FULL_NAME(OBVH)(kg, ray, isect_array, max_hits, visibility);
-#endif
-#ifdef __QBVH__
- case BVH_LAYOUT_BVH4:
- return BVH_FUNCTION_FULL_NAME(QBVH)(kg, ray, isect_array, max_hits, visibility);
-#endif
- case BVH_LAYOUT_BVH2:
- return BVH_FUNCTION_FULL_NAME(BVH)(kg, ray, isect_array, max_hits, visibility);
- }
- kernel_assert(!"Should not happen");
- return 0;
+ return BVH_FUNCTION_FULL_NAME(BVH)(kg, ray, isect_array, max_hits, visibility);
}
#undef BVH_FUNCTION_NAME
diff --git a/intern/cycles/kernel/bvh/obvh_local.h b/intern/cycles/kernel/bvh/obvh_local.h
deleted file mode 100644
index e6bb548bc5b..00000000000
--- a/intern/cycles/kernel/bvh/obvh_local.h
+++ /dev/null
@@ -1,398 +0,0 @@
-/*
- * Copyright 2011-2013 Blender Foundation
- *
- * 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.
- */
-
-/* This is a template BVH traversal function for subsurface scattering, where
- * various features can be enabled/disabled. This way we can compile optimized
- * versions for each case without new features slowing things down.
- *
- * BVH_MOTION: motion blur rendering
- */
-
-#if BVH_FEATURE(BVH_HAIR)
-# define NODE_INTERSECT obvh_node_intersect
-#else
-# define NODE_INTERSECT obvh_aligned_node_intersect
-#endif
-
-ccl_device bool BVH_FUNCTION_FULL_NAME(OBVH)(KernelGlobals *kg,
- const Ray *ray,
- LocalIntersection *local_isect,
- int local_object,
- uint *lcg_state,
- int max_hits)
-{
- /* Traversal stack in CUDA thread-local memory. */
- OBVHStackItem traversal_stack[BVH_OSTACK_SIZE];
- traversal_stack[0].addr = ENTRYPOINT_SENTINEL;
-
- /* Traversal variables in registers. */
- int stack_ptr = 0;
- int node_addr = kernel_tex_fetch(__object_node, local_object);
-
- /* Ray parameters in registers. */
- float3 P = ray->P;
- float3 dir = bvh_clamp_direction(ray->D);
- float3 idir = bvh_inverse_direction(dir);
- int object = OBJECT_NONE;
- float isect_t = ray->t;
-
- if (local_isect != NULL) {
- local_isect->num_hits = 0;
- }
- kernel_assert((local_isect == NULL) == (max_hits == 0));
-
- const int object_flag = kernel_tex_fetch(__object_flag, local_object);
- if (!(object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
-#if BVH_FEATURE(BVH_MOTION)
- Transform ob_itfm;
- isect_t = bvh_instance_motion_push(kg, local_object, ray, &P, &dir, &idir, isect_t, &ob_itfm);
-#else
- isect_t = bvh_instance_push(kg, local_object, ray, &P, &dir, &idir, isect_t);
-#endif
- object = local_object;
- }
-
- avxf tnear(0.0f), tfar(isect_t);
-#if BVH_FEATURE(BVH_HAIR)
- avx3f dir4(avxf(dir.x), avxf(dir.y), avxf(dir.z));
-#endif
- avx3f idir4(avxf(idir.x), avxf(idir.y), avxf(idir.z));
-
-#ifdef __KERNEL_AVX2__
- float3 P_idir = P * idir;
- avx3f P_idir4(P_idir.x, P_idir.y, P_idir.z);
-#endif
-#if BVH_FEATURE(BVH_HAIR) || !defined(__KERNEL_AVX2__)
- avx3f org4(avxf(P.x), avxf(P.y), avxf(P.z));
-#endif
-
- /* Offsets to select the side that becomes the lower or upper bound. */
- int near_x, near_y, near_z;
- int far_x, far_y, far_z;
- obvh_near_far_idx_calc(idir, &near_x, &near_y, &near_z, &far_x, &far_y, &far_z);
-
- /* Traversal loop. */
- do {
- do {
- /* Traverse internal nodes. */
- while (node_addr >= 0 && node_addr != ENTRYPOINT_SENTINEL) {
- avxf dist;
- int child_mask = NODE_INTERSECT(kg,
- tnear,
- tfar,
-#ifdef __KERNEL_AVX2__
- P_idir4,
-#endif
-#if BVH_FEATURE(BVH_HAIR) || !defined(__KERNEL_AVX2__)
- org4,
-#endif
-#if BVH_FEATURE(BVH_HAIR)
- dir4,
-#endif
- idir4,
- near_x,
- near_y,
- near_z,
- far_x,
- far_y,
- far_z,
- node_addr,
- &dist);
-
- if (child_mask != 0) {
- float4 inodes = kernel_tex_fetch(__bvh_nodes, node_addr + 0);
- avxf cnodes;
-#if BVH_FEATURE(BVH_HAIR)
- if (__float_as_uint(inodes.x) & PATH_RAY_NODE_UNALIGNED) {
- cnodes = kernel_tex_fetch_avxf(__bvh_nodes, node_addr + 26);
- }
- else
-#endif
- {
- cnodes = kernel_tex_fetch_avxf(__bvh_nodes, node_addr + 14);
- }
-
- /* One child is hit, continue with that child. */
- int r = __bscf(child_mask);
- if (child_mask == 0) {
- node_addr = __float_as_int(cnodes[r]);
- continue;
- }
-
- /* Two children are hit, push far child, and continue with
- * closer child.
- */
- int c0 = __float_as_int(cnodes[r]);
- float d0 = ((float *)&dist)[r];
- r = __bscf(child_mask);
- int c1 = __float_as_int(cnodes[r]);
- float d1 = ((float *)&dist)[r];
- if (child_mask == 0) {
- if (d1 < d0) {
- node_addr = c1;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c0;
- traversal_stack[stack_ptr].dist = d0;
- continue;
- }
- else {
- node_addr = c0;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c1;
- traversal_stack[stack_ptr].dist = d1;
- continue;
- }
- }
-
- /* Here starts the slow path for 3 or 4 hit children. We push
- * all nodes onto the stack to sort them there.
- */
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c1;
- traversal_stack[stack_ptr].dist = d1;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c0;
- traversal_stack[stack_ptr].dist = d0;
-
- /* Three children are hit, push all onto stack and sort 3
- * stack items, continue with closest child.
- */
- r = __bscf(child_mask);
- int c2 = __float_as_int(cnodes[r]);
- float d2 = ((float *)&dist)[r];
- if (child_mask == 0) {
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c2;
- traversal_stack[stack_ptr].dist = d2;
- obvh_stack_sort(&traversal_stack[stack_ptr],
- &traversal_stack[stack_ptr - 1],
- &traversal_stack[stack_ptr - 2]);
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- continue;
- }
-
- /* Four children are hit, push all onto stack and sort 4
- * stack items, continue with closest child.
- */
- r = __bscf(child_mask);
- int c3 = __float_as_int(cnodes[r]);
- float d3 = ((float *)&dist)[r];
- if (child_mask == 0) {
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c3;
- traversal_stack[stack_ptr].dist = d3;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c2;
- traversal_stack[stack_ptr].dist = d2;
- obvh_stack_sort(&traversal_stack[stack_ptr],
- &traversal_stack[stack_ptr - 1],
- &traversal_stack[stack_ptr - 2],
- &traversal_stack[stack_ptr - 3]);
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- continue;
- }
-
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c3;
- traversal_stack[stack_ptr].dist = d3;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c2;
- traversal_stack[stack_ptr].dist = d2;
-
- /* Five children are hit, push all onto stack and sort 5
- * stack items, continue with closest child
- */
- r = __bscf(child_mask);
- int c4 = __float_as_int(cnodes[r]);
- float d4 = ((float *)&dist)[r];
- if (child_mask == 0) {
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c4;
- traversal_stack[stack_ptr].dist = d4;
- obvh_stack_sort(&traversal_stack[stack_ptr],
- &traversal_stack[stack_ptr - 1],
- &traversal_stack[stack_ptr - 2],
- &traversal_stack[stack_ptr - 3],
- &traversal_stack[stack_ptr - 4]);
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- continue;
- }
- /* Six children are hit, push all onto stack and sort 6
- * stack items, continue with closest child.
- */
- r = __bscf(child_mask);
- int c5 = __float_as_int(cnodes[r]);
- float d5 = ((float *)&dist)[r];
- if (child_mask == 0) {
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c5;
- traversal_stack[stack_ptr].dist = d5;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c4;
- traversal_stack[stack_ptr].dist = d4;
- obvh_stack_sort(&traversal_stack[stack_ptr],
- &traversal_stack[stack_ptr - 1],
- &traversal_stack[stack_ptr - 2],
- &traversal_stack[stack_ptr - 3],
- &traversal_stack[stack_ptr - 4],
- &traversal_stack[stack_ptr - 5]);
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- continue;
- }
-
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c5;
- traversal_stack[stack_ptr].dist = d5;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c4;
- traversal_stack[stack_ptr].dist = d4;
-
- /* Seven children are hit, push all onto stack and sort 7
- * stack items, continue with closest child.
- */
- r = __bscf(child_mask);
- int c6 = __float_as_int(cnodes[r]);
- float d6 = ((float *)&dist)[r];
- if (child_mask == 0) {
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c6;
- traversal_stack[stack_ptr].dist = d6;
- obvh_stack_sort(&traversal_stack[stack_ptr],
- &traversal_stack[stack_ptr - 1],
- &traversal_stack[stack_ptr - 2],
- &traversal_stack[stack_ptr - 3],
- &traversal_stack[stack_ptr - 4],
- &traversal_stack[stack_ptr - 5],
- &traversal_stack[stack_ptr - 6]);
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- continue;
- }
- /* Eight children are hit, push all onto stack and sort 8
- * stack items, continue with closest child.
- */
- r = __bscf(child_mask);
- int c7 = __float_as_int(cnodes[r]);
- float d7 = ((float *)&dist)[r];
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c7;
- traversal_stack[stack_ptr].dist = d7;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c6;
- traversal_stack[stack_ptr].dist = d6;
- obvh_stack_sort(&traversal_stack[stack_ptr],
- &traversal_stack[stack_ptr - 1],
- &traversal_stack[stack_ptr - 2],
- &traversal_stack[stack_ptr - 3],
- &traversal_stack[stack_ptr - 4],
- &traversal_stack[stack_ptr - 5],
- &traversal_stack[stack_ptr - 6],
- &traversal_stack[stack_ptr - 7]);
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- continue;
- }
-
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- }
-
- /* If node is leaf, fetch triangle list. */
- if (node_addr < 0) {
- float4 leaf = kernel_tex_fetch(__bvh_leaf_nodes, (-node_addr - 1));
- int prim_addr = __float_as_int(leaf.x);
-
- int prim_addr2 = __float_as_int(leaf.y);
- const uint type = __float_as_int(leaf.w);
-
- /* Pop. */
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
-
- /* Primitive intersection. */
- switch (type & PRIMITIVE_ALL) {
- case PRIMITIVE_TRIANGLE: {
- /* Intersect ray against primitive, */
- for (; prim_addr < prim_addr2; prim_addr++) {
- kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type);
- if (triangle_intersect_local(kg,
- local_isect,
- P,
- dir,
- object,
- local_object,
- prim_addr,
- isect_t,
- lcg_state,
- max_hits)) {
- return true;
- }
- }
- break;
- }
-#if BVH_FEATURE(BVH_MOTION)
- case PRIMITIVE_MOTION_TRIANGLE: {
- /* Intersect ray against primitive. */
- for (; prim_addr < prim_addr2; prim_addr++) {
- kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type);
- if (motion_triangle_intersect_local(kg,
- local_isect,
- P,
- dir,
- ray->time,
- object,
- local_object,
- prim_addr,
- isect_t,
- lcg_state,
- max_hits)) {
- return true;
- }
- }
- break;
- }
-#endif
- default:
- break;
- }
- }
- } while (node_addr != ENTRYPOINT_SENTINEL);
- } while (node_addr != ENTRYPOINT_SENTINEL);
- return false;
-}
-
-#undef NODE_INTERSECT
diff --git a/intern/cycles/kernel/bvh/obvh_nodes.h b/intern/cycles/kernel/bvh/obvh_nodes.h
deleted file mode 100644
index e5c935b75ed..00000000000
--- a/intern/cycles/kernel/bvh/obvh_nodes.h
+++ /dev/null
@@ -1,410 +0,0 @@
-/*
- * Copyright 2011-2014, Blender Foundation.
- *
- * 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.
- *
- * Aligned nodes intersection AVX code is adopted from Embree,
- */
-
-struct OBVHStackItem {
- int addr;
- float dist;
-};
-
-ccl_device_inline void obvh_near_far_idx_calc(const float3 &idir,
- int *ccl_restrict near_x,
- int *ccl_restrict near_y,
- int *ccl_restrict near_z,
- int *ccl_restrict far_x,
- int *ccl_restrict far_y,
- int *ccl_restrict far_z)
-
-{
-#ifdef __KERNEL_SSE__
- *near_x = 0;
- *far_x = 1;
- *near_y = 2;
- *far_y = 3;
- *near_z = 4;
- *far_z = 5;
-
- const size_t mask = movemask(ssef(idir.m128));
-
- const int mask_x = mask & 1;
- const int mask_y = (mask & 2) >> 1;
- const int mask_z = (mask & 4) >> 2;
-
- *near_x += mask_x;
- *far_x -= mask_x;
- *near_y += mask_y;
- *far_y -= mask_y;
- *near_z += mask_z;
- *far_z -= mask_z;
-#else
- if (idir.x >= 0.0f) {
- *near_x = 0;
- *far_x = 1;
- }
- else {
- *near_x = 1;
- *far_x = 0;
- }
- if (idir.y >= 0.0f) {
- *near_y = 2;
- *far_y = 3;
- }
- else {
- *near_y = 3;
- *far_y = 2;
- }
- if (idir.z >= 0.0f) {
- *near_z = 4;
- *far_z = 5;
- }
- else {
- *near_z = 5;
- *far_z = 4;
- }
-#endif
-}
-
-ccl_device_inline void obvh_item_swap(OBVHStackItem *ccl_restrict a, OBVHStackItem *ccl_restrict b)
-{
- OBVHStackItem tmp = *a;
- *a = *b;
- *b = tmp;
-}
-
-ccl_device_inline void obvh_stack_sort(OBVHStackItem *ccl_restrict s1,
- OBVHStackItem *ccl_restrict s2,
- OBVHStackItem *ccl_restrict s3)
-{
- if (s2->dist < s1->dist) {
- obvh_item_swap(s2, s1);
- }
- if (s3->dist < s2->dist) {
- obvh_item_swap(s3, s2);
- }
- if (s2->dist < s1->dist) {
- obvh_item_swap(s2, s1);
- }
-}
-
-ccl_device_inline void obvh_stack_sort(OBVHStackItem *ccl_restrict s1,
- OBVHStackItem *ccl_restrict s2,
- OBVHStackItem *ccl_restrict s3,
- OBVHStackItem *ccl_restrict s4)
-{
- if (s2->dist < s1->dist) {
- obvh_item_swap(s2, s1);
- }
- if (s4->dist < s3->dist) {
- obvh_item_swap(s4, s3);
- }
- if (s3->dist < s1->dist) {
- obvh_item_swap(s3, s1);
- }
- if (s4->dist < s2->dist) {
- obvh_item_swap(s4, s2);
- }
- if (s3->dist < s2->dist) {
- obvh_item_swap(s3, s2);
- }
-}
-
-ccl_device_inline void obvh_stack_sort(OBVHStackItem *ccl_restrict s1,
- OBVHStackItem *ccl_restrict s2,
- OBVHStackItem *ccl_restrict s3,
- OBVHStackItem *ccl_restrict s4,
- OBVHStackItem *ccl_restrict s5)
-{
- obvh_stack_sort(s1, s2, s3, s4);
- if (s5->dist < s4->dist) {
- obvh_item_swap(s4, s5);
- if (s4->dist < s3->dist) {
- obvh_item_swap(s3, s4);
- if (s3->dist < s2->dist) {
- obvh_item_swap(s2, s3);
- if (s2->dist < s1->dist) {
- obvh_item_swap(s1, s2);
- }
- }
- }
- }
-}
-
-ccl_device_inline void obvh_stack_sort(OBVHStackItem *ccl_restrict s1,
- OBVHStackItem *ccl_restrict s2,
- OBVHStackItem *ccl_restrict s3,
- OBVHStackItem *ccl_restrict s4,
- OBVHStackItem *ccl_restrict s5,
- OBVHStackItem *ccl_restrict s6)
-{
- obvh_stack_sort(s1, s2, s3, s4, s5);
- if (s6->dist < s5->dist) {
- obvh_item_swap(s5, s6);
- if (s5->dist < s4->dist) {
- obvh_item_swap(s4, s5);
- if (s4->dist < s3->dist) {
- obvh_item_swap(s3, s4);
- if (s3->dist < s2->dist) {
- obvh_item_swap(s2, s3);
- if (s2->dist < s1->dist) {
- obvh_item_swap(s1, s2);
- }
- }
- }
- }
- }
-}
-
-ccl_device_inline void obvh_stack_sort(OBVHStackItem *ccl_restrict s1,
- OBVHStackItem *ccl_restrict s2,
- OBVHStackItem *ccl_restrict s3,
- OBVHStackItem *ccl_restrict s4,
- OBVHStackItem *ccl_restrict s5,
- OBVHStackItem *ccl_restrict s6,
- OBVHStackItem *ccl_restrict s7)
-{
- obvh_stack_sort(s1, s2, s3, s4, s5, s6);
- if (s7->dist < s6->dist) {
- obvh_item_swap(s6, s7);
- if (s6->dist < s5->dist) {
- obvh_item_swap(s5, s6);
- if (s5->dist < s4->dist) {
- obvh_item_swap(s4, s5);
- if (s4->dist < s3->dist) {
- obvh_item_swap(s3, s4);
- if (s3->dist < s2->dist) {
- obvh_item_swap(s2, s3);
- if (s2->dist < s1->dist) {
- obvh_item_swap(s1, s2);
- }
- }
- }
- }
- }
- }
-}
-
-ccl_device_inline void obvh_stack_sort(OBVHStackItem *ccl_restrict s1,
- OBVHStackItem *ccl_restrict s2,
- OBVHStackItem *ccl_restrict s3,
- OBVHStackItem *ccl_restrict s4,
- OBVHStackItem *ccl_restrict s5,
- OBVHStackItem *ccl_restrict s6,
- OBVHStackItem *ccl_restrict s7,
- OBVHStackItem *ccl_restrict s8)
-{
- obvh_stack_sort(s1, s2, s3, s4, s5, s6, s7);
- if (s8->dist < s7->dist) {
- obvh_item_swap(s7, s8);
- if (s7->dist < s6->dist) {
- obvh_item_swap(s6, s7);
- if (s6->dist < s5->dist) {
- obvh_item_swap(s5, s6);
- if (s5->dist < s4->dist) {
- obvh_item_swap(s4, s5);
- if (s4->dist < s3->dist) {
- obvh_item_swap(s3, s4);
- if (s3->dist < s2->dist) {
- obvh_item_swap(s2, s3);
- if (s2->dist < s1->dist) {
- obvh_item_swap(s1, s2);
- }
- }
- }
- }
- }
- }
- }
-}
-
-/* Axis-aligned nodes intersection */
-
-ccl_device_inline int obvh_aligned_node_intersect(KernelGlobals *ccl_restrict kg,
- const avxf &isect_near,
- const avxf &isect_far,
-#ifdef __KERNEL_AVX2__
- const avx3f &org_idir,
-#else
- const avx3f &org,
-#endif
- const avx3f &idir,
- const int near_x,
- const int near_y,
- const int near_z,
- const int far_x,
- const int far_y,
- const int far_z,
- const int node_addr,
- avxf *ccl_restrict dist)
-{
- const int offset = node_addr + 2;
-#ifdef __KERNEL_AVX2__
- const avxf tnear_x = msub(
- kernel_tex_fetch_avxf(__bvh_nodes, offset + near_x * 2), idir.x, org_idir.x);
- const avxf tnear_y = msub(
- kernel_tex_fetch_avxf(__bvh_nodes, offset + near_y * 2), idir.y, org_idir.y);
- const avxf tnear_z = msub(
- kernel_tex_fetch_avxf(__bvh_nodes, offset + near_z * 2), idir.z, org_idir.z);
- const avxf tfar_x = msub(
- kernel_tex_fetch_avxf(__bvh_nodes, offset + far_x * 2), idir.x, org_idir.x);
- const avxf tfar_y = msub(
- kernel_tex_fetch_avxf(__bvh_nodes, offset + far_y * 2), idir.y, org_idir.y);
- const avxf tfar_z = msub(
- kernel_tex_fetch_avxf(__bvh_nodes, offset + far_z * 2), idir.z, org_idir.z);
-
- const avxf tnear = max4(tnear_x, tnear_y, tnear_z, isect_near);
- const avxf tfar = min4(tfar_x, tfar_y, tfar_z, isect_far);
- const avxb vmask = tnear <= tfar;
- int mask = (int)movemask(vmask);
- *dist = tnear;
- return mask;
-#else
- return 0;
-#endif
-}
-
-/* Unaligned nodes intersection */
-
-ccl_device_inline int obvh_unaligned_node_intersect(KernelGlobals *ccl_restrict kg,
- const avxf &isect_near,
- const avxf &isect_far,
-#ifdef __KERNEL_AVX2__
- const avx3f &org_idir,
-#endif
- const avx3f &org,
- const avx3f &dir,
- const avx3f &idir,
- const int near_x,
- const int near_y,
- const int near_z,
- const int far_x,
- const int far_y,
- const int far_z,
- const int node_addr,
- avxf *ccl_restrict dist)
-{
- const int offset = node_addr;
- const avxf tfm_x_x = kernel_tex_fetch_avxf(__bvh_nodes, offset + 2);
- const avxf tfm_x_y = kernel_tex_fetch_avxf(__bvh_nodes, offset + 4);
- const avxf tfm_x_z = kernel_tex_fetch_avxf(__bvh_nodes, offset + 6);
-
- const avxf tfm_y_x = kernel_tex_fetch_avxf(__bvh_nodes, offset + 8);
- const avxf tfm_y_y = kernel_tex_fetch_avxf(__bvh_nodes, offset + 10);
- const avxf tfm_y_z = kernel_tex_fetch_avxf(__bvh_nodes, offset + 12);
-
- const avxf tfm_z_x = kernel_tex_fetch_avxf(__bvh_nodes, offset + 14);
- const avxf tfm_z_y = kernel_tex_fetch_avxf(__bvh_nodes, offset + 16);
- const avxf tfm_z_z = kernel_tex_fetch_avxf(__bvh_nodes, offset + 18);
-
- const avxf tfm_t_x = kernel_tex_fetch_avxf(__bvh_nodes, offset + 20);
- const avxf tfm_t_y = kernel_tex_fetch_avxf(__bvh_nodes, offset + 22);
- const avxf tfm_t_z = kernel_tex_fetch_avxf(__bvh_nodes, offset + 24);
-
- const avxf aligned_dir_x = dir.x * tfm_x_x + dir.y * tfm_x_y + dir.z * tfm_x_z,
- aligned_dir_y = dir.x * tfm_y_x + dir.y * tfm_y_y + dir.z * tfm_y_z,
- aligned_dir_z = dir.x * tfm_z_x + dir.y * tfm_z_y + dir.z * tfm_z_z;
-
- const avxf aligned_P_x = org.x * tfm_x_x + org.y * tfm_x_y + org.z * tfm_x_z + tfm_t_x,
- aligned_P_y = org.x * tfm_y_x + org.y * tfm_y_y + org.z * tfm_y_z + tfm_t_y,
- aligned_P_z = org.x * tfm_z_x + org.y * tfm_z_y + org.z * tfm_z_z + tfm_t_z;
-
- const avxf neg_one(-1.0f);
- const avxf nrdir_x = neg_one / aligned_dir_x, nrdir_y = neg_one / aligned_dir_y,
- nrdir_z = neg_one / aligned_dir_z;
-
- const avxf tlower_x = aligned_P_x * nrdir_x, tlower_y = aligned_P_y * nrdir_y,
- tlower_z = aligned_P_z * nrdir_z;
-
- const avxf tupper_x = tlower_x - nrdir_x, tupper_y = tlower_y - nrdir_y,
- tupper_z = tlower_z - nrdir_z;
-
- const avxf tnear_x = min(tlower_x, tupper_x);
- const avxf tnear_y = min(tlower_y, tupper_y);
- const avxf tnear_z = min(tlower_z, tupper_z);
- const avxf tfar_x = max(tlower_x, tupper_x);
- const avxf tfar_y = max(tlower_y, tupper_y);
- const avxf tfar_z = max(tlower_z, tupper_z);
- const avxf tnear = max4(isect_near, tnear_x, tnear_y, tnear_z);
- const avxf tfar = min4(isect_far, tfar_x, tfar_y, tfar_z);
- const avxb vmask = tnear <= tfar;
- *dist = tnear;
- return movemask(vmask);
-}
-
-/* Intersectors wrappers.
- *
- * They'll check node type and call appropriate intersection code.
- */
-
-ccl_device_inline int obvh_node_intersect(KernelGlobals *ccl_restrict kg,
- const avxf &isect_near,
- const avxf &isect_far,
-#ifdef __KERNEL_AVX2__
- const avx3f &org_idir,
-#endif
- const avx3f &org,
- const avx3f &dir,
- const avx3f &idir,
- const int near_x,
- const int near_y,
- const int near_z,
- const int far_x,
- const int far_y,
- const int far_z,
- const int node_addr,
- avxf *ccl_restrict dist)
-{
- const int offset = node_addr;
- const float4 node = kernel_tex_fetch(__bvh_nodes, offset);
- if (__float_as_uint(node.x) & PATH_RAY_NODE_UNALIGNED) {
- return obvh_unaligned_node_intersect(kg,
- isect_near,
- isect_far,
-#ifdef __KERNEL_AVX2__
- org_idir,
-#endif
- org,
- dir,
- idir,
- near_x,
- near_y,
- near_z,
- far_x,
- far_y,
- far_z,
- node_addr,
- dist);
- }
- else {
- return obvh_aligned_node_intersect(kg,
- isect_near,
- isect_far,
-#ifdef __KERNEL_AVX2__
- org_idir,
-#else
- org,
-#endif
- idir,
- near_x,
- near_y,
- near_z,
- far_x,
- far_y,
- far_z,
- node_addr,
- dist);
- }
-}
diff --git a/intern/cycles/kernel/bvh/obvh_shadow_all.h b/intern/cycles/kernel/bvh/obvh_shadow_all.h
deleted file mode 100644
index b7ab75b723c..00000000000
--- a/intern/cycles/kernel/bvh/obvh_shadow_all.h
+++ /dev/null
@@ -1,664 +0,0 @@
-/*
- * Copyright 2011-2013 Blender Foundation
- *
- * 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.
- */
-
-/* This is a template BVH traversal function, where various features can be
- * enabled/disabled. This way we can compile optimized versions for each case
- * without new features slowing things down.
- *
- * BVH_INSTANCING: object instancing
- * BVH_HAIR: hair curve rendering
- * BVH_MOTION: motion blur rendering
- */
-
-#if BVH_FEATURE(BVH_HAIR)
-# define NODE_INTERSECT obvh_node_intersect
-#else
-# define NODE_INTERSECT obvh_aligned_node_intersect
-#endif
-
-ccl_device bool BVH_FUNCTION_FULL_NAME(OBVH)(KernelGlobals *kg,
- const Ray *ray,
- Intersection *isect_array,
- const int skip_object,
- const uint max_hits,
- uint *num_hits)
-{
- /* TODO(sergey):
- * - Test if pushing distance on the stack helps.
- * - Likely and unlikely for if() statements.
- * - Test restrict attribute for pointers.
- */
-
- /* Traversal stack in CUDA thread-local memory. */
- OBVHStackItem traversal_stack[BVH_OSTACK_SIZE];
- traversal_stack[0].addr = ENTRYPOINT_SENTINEL;
-
- /* Traversal variables in registers. */
- int stack_ptr = 0;
- int node_addr = kernel_data.bvh.root;
-
- /* Ray parameters in registers. */
- const float tmax = ray->t;
- float3 P = ray->P;
- float3 dir = bvh_clamp_direction(ray->D);
- float3 idir = bvh_inverse_direction(dir);
- int object = OBJECT_NONE;
- float isect_t = tmax;
-
-#if BVH_FEATURE(BVH_MOTION)
- Transform ob_itfm;
-#endif
-
- *num_hits = 0;
- isect_array->t = tmax;
-
-#if BVH_FEATURE(BVH_INSTANCING)
- int num_hits_in_instance = 0;
-#endif
-
- avxf tnear(0.0f), tfar(isect_t);
-#if BVH_FEATURE(BVH_HAIR)
- avx3f dir4(avxf(dir.x), avxf(dir.y), avxf(dir.z));
-#endif
- avx3f idir4(avxf(idir.x), avxf(idir.y), avxf(idir.z));
-
-#ifdef __KERNEL_AVX2__
- float3 P_idir = P * idir;
- avx3f P_idir4(P_idir.x, P_idir.y, P_idir.z);
-#endif
-#if BVH_FEATURE(BVH_HAIR) || !defined(__KERNEL_AVX2__)
- avx3f org4(avxf(P.x), avxf(P.y), avxf(P.z));
-#endif
-
- /* Offsets to select the side that becomes the lower or upper bound. */
- int near_x, near_y, near_z;
- int far_x, far_y, far_z;
- obvh_near_far_idx_calc(idir, &near_x, &near_y, &near_z, &far_x, &far_y, &far_z);
-
- /* Traversal loop. */
- do {
- do {
- /* Traverse internal nodes. */
- while (node_addr >= 0 && node_addr != ENTRYPOINT_SENTINEL) {
- float4 inodes = kernel_tex_fetch(__bvh_nodes, node_addr + 0);
- (void)inodes;
-
- if (false
-#ifdef __VISIBILITY_FLAG__
- || ((__float_as_uint(inodes.x) & PATH_RAY_SHADOW) == 0)
-#endif
-#if BVH_FEATURE(BVH_MOTION)
- || UNLIKELY(ray->time < inodes.y) || UNLIKELY(ray->time > inodes.z)
-#endif
- ) {
- /* Pop. */
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- continue;
- }
-
- avxf dist;
- int child_mask = NODE_INTERSECT(kg,
- tnear,
- tfar,
-#ifdef __KERNEL_AVX2__
- P_idir4,
-#endif
-#if BVH_FEATURE(BVH_HAIR) || !defined(__KERNEL_AVX2__)
- //#if !defined(__KERNEL_AVX2__)
- org4,
-#endif
-#if BVH_FEATURE(BVH_HAIR)
- dir4,
-#endif
- idir4,
- near_x,
- near_y,
- near_z,
- far_x,
- far_y,
- far_z,
- node_addr,
- &dist);
-
- if (child_mask != 0) {
- avxf cnodes;
-#if BVH_FEATURE(BVH_HAIR)
- if (__float_as_uint(inodes.x) & PATH_RAY_NODE_UNALIGNED) {
- cnodes = kernel_tex_fetch_avxf(__bvh_nodes, node_addr + 26);
- }
- else
-#endif
- {
- cnodes = kernel_tex_fetch_avxf(__bvh_nodes, node_addr + 14);
- }
-
- /* One child is hit, continue with that child. */
- int r = __bscf(child_mask);
- if (child_mask == 0) {
- node_addr = __float_as_int(cnodes[r]);
- continue;
- }
-
- /* Two children are hit, push far child, and continue with
- * closer child.
- */
- int c0 = __float_as_int(cnodes[r]);
- float d0 = ((float *)&dist)[r];
- r = __bscf(child_mask);
- int c1 = __float_as_int(cnodes[r]);
- float d1 = ((float *)&dist)[r];
- if (child_mask == 0) {
- if (d1 < d0) {
- node_addr = c1;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c0;
- traversal_stack[stack_ptr].dist = d0;
- continue;
- }
- else {
- node_addr = c0;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c1;
- traversal_stack[stack_ptr].dist = d1;
- continue;
- }
- }
-
- /* Here starts the slow path for 3 or 4 hit children. We push
- * all nodes onto the stack to sort them there.
- */
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c1;
- traversal_stack[stack_ptr].dist = d1;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c0;
- traversal_stack[stack_ptr].dist = d0;
-
- /* Three children are hit, push all onto stack and sort 3
- * stack items, continue with closest child.
- */
- r = __bscf(child_mask);
- int c2 = __float_as_int(cnodes[r]);
- float d2 = ((float *)&dist)[r];
- if (child_mask == 0) {
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c2;
- traversal_stack[stack_ptr].dist = d2;
- obvh_stack_sort(&traversal_stack[stack_ptr],
- &traversal_stack[stack_ptr - 1],
- &traversal_stack[stack_ptr - 2]);
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- continue;
- }
-
- /* Four children are hit, push all onto stack and sort 4
- * stack items, continue with closest child.
- */
- r = __bscf(child_mask);
- int c3 = __float_as_int(cnodes[r]);
- float d3 = ((float *)&dist)[r];
- if (child_mask == 0) {
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c3;
- traversal_stack[stack_ptr].dist = d3;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c2;
- traversal_stack[stack_ptr].dist = d2;
- obvh_stack_sort(&traversal_stack[stack_ptr],
- &traversal_stack[stack_ptr - 1],
- &traversal_stack[stack_ptr - 2],
- &traversal_stack[stack_ptr - 3]);
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- continue;
- }
-
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c3;
- traversal_stack[stack_ptr].dist = d3;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c2;
- traversal_stack[stack_ptr].dist = d2;
-
- /* Five children are hit, push all onto stack and sort 5
- * stack items, continue with closest child
- */
- r = __bscf(child_mask);
- int c4 = __float_as_int(cnodes[r]);
- float d4 = ((float *)&dist)[r];
- if (child_mask == 0) {
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c4;
- traversal_stack[stack_ptr].dist = d4;
- obvh_stack_sort(&traversal_stack[stack_ptr],
- &traversal_stack[stack_ptr - 1],
- &traversal_stack[stack_ptr - 2],
- &traversal_stack[stack_ptr - 3],
- &traversal_stack[stack_ptr - 4]);
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- continue;
- }
-
- /* Six children are hit, push all onto stack and sort 6
- * stack items, continue with closest child.
- */
- r = __bscf(child_mask);
- int c5 = __float_as_int(cnodes[r]);
- float d5 = ((float *)&dist)[r];
- if (child_mask == 0) {
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c5;
- traversal_stack[stack_ptr].dist = d5;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c4;
- traversal_stack[stack_ptr].dist = d4;
- obvh_stack_sort(&traversal_stack[stack_ptr],
- &traversal_stack[stack_ptr - 1],
- &traversal_stack[stack_ptr - 2],
- &traversal_stack[stack_ptr - 3],
- &traversal_stack[stack_ptr - 4],
- &traversal_stack[stack_ptr - 5]);
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- continue;
- }
-
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c5;
- traversal_stack[stack_ptr].dist = d5;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c4;
- traversal_stack[stack_ptr].dist = d4;
-
- /* Seven children are hit, push all onto stack and sort 7
- * stack items, continue with closest child.
- */
- r = __bscf(child_mask);
- int c6 = __float_as_int(cnodes[r]);
- float d6 = ((float *)&dist)[r];
- if (child_mask == 0) {
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c6;
- traversal_stack[stack_ptr].dist = d6;
- obvh_stack_sort(&traversal_stack[stack_ptr],
- &traversal_stack[stack_ptr - 1],
- &traversal_stack[stack_ptr - 2],
- &traversal_stack[stack_ptr - 3],
- &traversal_stack[stack_ptr - 4],
- &traversal_stack[stack_ptr - 5],
- &traversal_stack[stack_ptr - 6]);
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- continue;
- }
-
- /* Eight children are hit, push all onto stack and sort 8
- * stack items, continue with closest child.
- */
- r = __bscf(child_mask);
- int c7 = __float_as_int(cnodes[r]);
- float d7 = ((float *)&dist)[r];
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c7;
- traversal_stack[stack_ptr].dist = d7;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c6;
- traversal_stack[stack_ptr].dist = d6;
- obvh_stack_sort(&traversal_stack[stack_ptr],
- &traversal_stack[stack_ptr - 1],
- &traversal_stack[stack_ptr - 2],
- &traversal_stack[stack_ptr - 3],
- &traversal_stack[stack_ptr - 4],
- &traversal_stack[stack_ptr - 5],
- &traversal_stack[stack_ptr - 6],
- &traversal_stack[stack_ptr - 7]);
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- continue;
- }
-
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- }
-
- /* If node is leaf, fetch triangle list. */
- if (node_addr < 0) {
- float4 leaf = kernel_tex_fetch(__bvh_leaf_nodes, (-node_addr - 1));
-#ifdef __VISIBILITY_FLAG__
- if ((__float_as_uint(leaf.z) & PATH_RAY_SHADOW) == 0) {
- /* Pop. */
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- continue;
- }
-#endif
-
- int prim_addr = __float_as_int(leaf.x);
-
-#if BVH_FEATURE(BVH_INSTANCING)
- if (prim_addr >= 0) {
-#endif
- int prim_addr2 = __float_as_int(leaf.y);
- const uint type = __float_as_int(leaf.w);
- const uint p_type = type & PRIMITIVE_ALL;
-
- /* Pop. */
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
-
- /* Primitive intersection. */
- if (p_type == PRIMITIVE_TRIANGLE) {
- int prim_count = prim_addr2 - prim_addr;
- if (prim_count < 3) {
- while (prim_addr < prim_addr2) {
- kernel_assert((kernel_tex_fetch(__prim_type, prim_addr) & PRIMITIVE_ALL) ==
- p_type);
- int hit = triangle_intersect(
- kg, isect_array, P, dir, PATH_RAY_SHADOW, object, prim_addr);
- /* Shadow ray early termination. */
- if (hit) {
- /* detect if this surface has a shader with transparent shadows */
-
- /* todo: optimize so primitive visibility flag indicates if
- * the primitive has a transparent shadow shader? */
- int prim = kernel_tex_fetch(__prim_index, isect_array->prim);
- int shader = 0;
-
-#ifdef __HAIR__
- if (kernel_tex_fetch(__prim_type, isect_array->prim) & PRIMITIVE_ALL_TRIANGLE)
-#endif
- {
- shader = kernel_tex_fetch(__tri_shader, prim);
- }
-#ifdef __HAIR__
- else {
- float4 str = kernel_tex_fetch(__curves, prim);
- shader = __float_as_int(str.z);
- }
-#endif
- int flag = kernel_tex_fetch(__shaders, (shader & SHADER_MASK)).flags;
-
- /* if no transparent shadows, all light is blocked */
- if (!(flag & SD_HAS_TRANSPARENT_SHADOW)) {
- return true;
- }
- /* if maximum number of hits reached, block all light */
- else if (*num_hits == max_hits) {
- return true;
- }
-
- /* move on to next entry in intersections array */
- isect_array++;
- (*num_hits)++;
-#if BVH_FEATURE(BVH_INSTANCING)
- num_hits_in_instance++;
-#endif
-
- isect_array->t = isect_t;
- }
-
- prim_addr++;
- } // while
- }
- else {
- kernel_assert((kernel_tex_fetch(__prim_type, (prim_addr)) & PRIMITIVE_ALL) ==
- p_type);
-
-#if BVH_FEATURE(BVH_INSTANCING)
- int *nhiptr = &num_hits_in_instance;
-#else
- int nhi = 0;
- int *nhiptr = &nhi;
-#endif
-
- int result = triangle_intersect8(kg,
- &isect_array,
- P,
- dir,
- PATH_RAY_SHADOW,
- object,
- prim_addr,
- prim_count,
- num_hits,
- max_hits,
- nhiptr,
- isect_t);
- if (result == 2) {
- return true;
- }
- } // prim_count
- } // PRIMITIVE_TRIANGLE
- else {
- while (prim_addr < prim_addr2) {
- kernel_assert((kernel_tex_fetch(__prim_type, prim_addr) & PRIMITIVE_ALL) == p_type);
-
-#ifdef __SHADOW_TRICKS__
- uint tri_object = (object == OBJECT_NONE) ?
- kernel_tex_fetch(__prim_object, prim_addr) :
- object;
- if (tri_object == skip_object) {
- ++prim_addr;
- continue;
- }
-#endif
-
- bool hit;
-
- /* todo: specialized intersect functions which don't fill in
- * isect unless needed and check SD_HAS_TRANSPARENT_SHADOW?
- * might give a few % performance improvement */
-
- switch (p_type) {
-
-#if BVH_FEATURE(BVH_MOTION)
- case PRIMITIVE_MOTION_TRIANGLE: {
- hit = motion_triangle_intersect(
- kg, isect_array, P, dir, ray->time, PATH_RAY_SHADOW, object, prim_addr);
- break;
- }
-#endif
-#if BVH_FEATURE(BVH_HAIR)
- case PRIMITIVE_CURVE:
- case PRIMITIVE_MOTION_CURVE: {
- const uint curve_type = kernel_tex_fetch(__prim_type, prim_addr);
- if (kernel_data.curve.curveflags & CURVE_KN_INTERPOLATE) {
- hit = cardinal_curve_intersect(kg,
- isect_array,
- P,
- dir,
- PATH_RAY_SHADOW,
- object,
- prim_addr,
- ray->time,
- curve_type);
- }
- else {
- hit = curve_intersect(kg,
- isect_array,
- P,
- dir,
- PATH_RAY_SHADOW,
- object,
- prim_addr,
- ray->time,
- curve_type);
- }
- break;
- }
-#endif
- default: {
- hit = false;
- break;
- }
- }
-
- /* Shadow ray early termination. */
- if (hit) {
- /* detect if this surface has a shader with transparent shadows */
-
- /* todo: optimize so primitive visibility flag indicates if
- * the primitive has a transparent shadow shader? */
- int prim = kernel_tex_fetch(__prim_index, isect_array->prim);
- int shader = 0;
-
-#ifdef __HAIR__
- if (kernel_tex_fetch(__prim_type, isect_array->prim) & PRIMITIVE_ALL_TRIANGLE)
-#endif
- {
- shader = kernel_tex_fetch(__tri_shader, prim);
- }
-#ifdef __HAIR__
- else {
- float4 str = kernel_tex_fetch(__curves, prim);
- shader = __float_as_int(str.z);
- }
-#endif
- int flag = kernel_tex_fetch(__shaders, (shader & SHADER_MASK)).flags;
-
- /* if no transparent shadows, all light is blocked */
- if (!(flag & SD_HAS_TRANSPARENT_SHADOW)) {
- return true;
- }
- /* if maximum number of hits reached, block all light */
- else if (*num_hits == max_hits) {
- return true;
- }
-
- /* move on to next entry in intersections array */
- isect_array++;
- (*num_hits)++;
-#if BVH_FEATURE(BVH_INSTANCING)
- num_hits_in_instance++;
-#endif
-
- isect_array->t = isect_t;
- }
-
- prim_addr++;
- } // while prim
- }
- }
-#if BVH_FEATURE(BVH_INSTANCING)
- else {
- /* Instance push. */
- object = kernel_tex_fetch(__prim_object, -prim_addr - 1);
-
-# if BVH_FEATURE(BVH_MOTION)
- isect_t = bvh_instance_motion_push(kg, object, ray, &P, &dir, &idir, isect_t, &ob_itfm);
-# else
- isect_t = bvh_instance_push(kg, object, ray, &P, &dir, &idir, isect_t);
-# endif
-
- num_hits_in_instance = 0;
- isect_array->t = isect_t;
-
- obvh_near_far_idx_calc(idir, &near_x, &near_y, &near_z, &far_x, &far_y, &far_z);
- tfar = avxf(isect_t);
-# if BVH_FEATURE(BVH_HAIR)
- dir4 = avx3f(avxf(dir.x), avxf(dir.y), avxf(dir.z));
-# endif
- idir4 = avx3f(avxf(idir.x), avxf(idir.y), avxf(idir.z));
-# ifdef __KERNEL_AVX2__
- P_idir = P * idir;
- P_idir4 = avx3f(P_idir.x, P_idir.y, P_idir.z);
-# endif
-# if BVH_FEATURE(BVH_HAIR) || !defined(__KERNEL_AVX2__)
- org4 = avx3f(avxf(P.x), avxf(P.y), avxf(P.z));
-# endif
-
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = ENTRYPOINT_SENTINEL;
-
- node_addr = kernel_tex_fetch(__object_node, object);
- }
- }
-#endif /* FEATURE(BVH_INSTANCING) */
- } while (node_addr != ENTRYPOINT_SENTINEL);
-
-#if BVH_FEATURE(BVH_INSTANCING)
- if (stack_ptr >= 0) {
- kernel_assert(object != OBJECT_NONE);
-
- /* Instance pop. */
- if (num_hits_in_instance) {
- float t_fac;
-# if BVH_FEATURE(BVH_MOTION)
- bvh_instance_motion_pop_factor(kg, object, ray, &P, &dir, &idir, &t_fac, &ob_itfm);
-# else
- bvh_instance_pop_factor(kg, object, ray, &P, &dir, &idir, &t_fac);
-# endif
- /* Scale isect->t to adjust for instancing. */
- for (int i = 0; i < num_hits_in_instance; i++) {
- (isect_array - i - 1)->t *= t_fac;
- }
- }
- else {
-# if BVH_FEATURE(BVH_MOTION)
- bvh_instance_motion_pop(kg, object, ray, &P, &dir, &idir, FLT_MAX, &ob_itfm);
-# else
- bvh_instance_pop(kg, object, ray, &P, &dir, &idir, FLT_MAX);
-# endif
- }
-
- isect_t = tmax;
- isect_array->t = isect_t;
-
- obvh_near_far_idx_calc(idir, &near_x, &near_y, &near_z, &far_x, &far_y, &far_z);
- tfar = avxf(isect_t);
-# if BVH_FEATURE(BVH_HAIR)
- dir4 = avx3f(avxf(dir.x), avxf(dir.y), avxf(dir.z));
-# endif
- idir4 = avx3f(avxf(idir.x), avxf(idir.y), avxf(idir.z));
-# ifdef __KERNEL_AVX2__
- P_idir = P * idir;
- P_idir4 = avx3f(P_idir.x, P_idir.y, P_idir.z);
-# endif
-# if BVH_FEATURE(BVH_HAIR) || !defined(__KERNEL_AVX2__)
- org4 = avx3f(avxf(P.x), avxf(P.y), avxf(P.z));
-# endif
-
- object = OBJECT_NONE;
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- }
-#endif /* FEATURE(BVH_INSTANCING) */
- } while (node_addr != ENTRYPOINT_SENTINEL);
-
- return false;
-}
-
-#undef NODE_INTERSECT
diff --git a/intern/cycles/kernel/bvh/obvh_traversal.h b/intern/cycles/kernel/bvh/obvh_traversal.h
deleted file mode 100644
index 9095233f8b6..00000000000
--- a/intern/cycles/kernel/bvh/obvh_traversal.h
+++ /dev/null
@@ -1,557 +0,0 @@
-/*
- * Copyright 2011-2013 Blender Foundation
- *
- * 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.
- */
-
-/* This is a template BVH traversal function, where various features can be
- * enabled/disabled. This way we can compile optimized versions for each case
- * without new features slowing things down.
- *
- * BVH_INSTANCING: object instancing
- * BVH_HAIR: hair curve rendering
- * BVH_MOTION: motion blur rendering
- */
-
-#if BVH_FEATURE(BVH_HAIR)
-# define NODE_INTERSECT obvh_node_intersect
-#else
-# define NODE_INTERSECT obvh_aligned_node_intersect
-#endif
-
-ccl_device bool BVH_FUNCTION_FULL_NAME(OBVH)(KernelGlobals *kg,
- const Ray *ray,
- Intersection *isect,
- const uint visibility)
-{
- /* Traversal stack in CUDA thread-local memory. */
- OBVHStackItem traversal_stack[BVH_OSTACK_SIZE];
- traversal_stack[0].addr = ENTRYPOINT_SENTINEL;
- traversal_stack[0].dist = -FLT_MAX;
-
- /* Traversal variables in registers. */
- int stack_ptr = 0;
- int node_addr = kernel_data.bvh.root;
- float node_dist = -FLT_MAX;
-
- /* Ray parameters in registers. */
- float3 P = ray->P;
- float3 dir = bvh_clamp_direction(ray->D);
- float3 idir = bvh_inverse_direction(dir);
- int object = OBJECT_NONE;
-
-#if BVH_FEATURE(BVH_MOTION)
- Transform ob_itfm;
-#endif
-
- isect->t = ray->t;
- isect->u = 0.0f;
- isect->v = 0.0f;
- isect->prim = PRIM_NONE;
- isect->object = OBJECT_NONE;
-
- BVH_DEBUG_INIT();
- avxf tnear(0.0f), tfar(ray->t);
-#if BVH_FEATURE(BVH_HAIR)
- avx3f dir4(avxf(dir.x), avxf(dir.y), avxf(dir.z));
-#endif
- avx3f idir4(avxf(idir.x), avxf(idir.y), avxf(idir.z));
-
-#ifdef __KERNEL_AVX2__
- float3 P_idir = P * idir;
- avx3f P_idir4 = avx3f(P_idir.x, P_idir.y, P_idir.z);
-#endif
-#if BVH_FEATURE(BVH_HAIR) || !defined(__KERNEL_AVX2__)
- avx3f org4 = avx3f(avxf(P.x), avxf(P.y), avxf(P.z));
-#endif
-
- /* Offsets to select the side that becomes the lower or upper bound. */
- int near_x, near_y, near_z;
- int far_x, far_y, far_z;
- obvh_near_far_idx_calc(idir, &near_x, &near_y, &near_z, &far_x, &far_y, &far_z);
- /* Traversal loop. */
- do {
- do {
- /* Traverse internal nodes. */
- while (node_addr >= 0 && node_addr != ENTRYPOINT_SENTINEL) {
- float4 inodes = kernel_tex_fetch(__bvh_nodes, node_addr + 0);
- (void)inodes;
-
- if (UNLIKELY(node_dist > isect->t)
-#if BVH_FEATURE(BVH_MOTION)
- || UNLIKELY(ray->time < inodes.y) || UNLIKELY(ray->time > inodes.z)
-#endif
-#ifdef __VISIBILITY_FLAG__
- || (__float_as_uint(inodes.x) & visibility) == 0
-#endif
- ) {
- /* Pop. */
- node_addr = traversal_stack[stack_ptr].addr;
- node_dist = traversal_stack[stack_ptr].dist;
- --stack_ptr;
- continue;
- }
-
- int child_mask;
- avxf dist;
-
- BVH_DEBUG_NEXT_NODE();
-
- {
- child_mask = NODE_INTERSECT(kg,
- tnear,
- tfar,
-#ifdef __KERNEL_AVX2__
- P_idir4,
-#endif
-#if BVH_FEATURE(BVH_HAIR) || !defined(__KERNEL_AVX2__)
- org4,
-#endif
-#if BVH_FEATURE(BVH_HAIR)
- dir4,
-#endif
- idir4,
- near_x,
- near_y,
- near_z,
- far_x,
- far_y,
- far_z,
- node_addr,
- &dist);
- }
-
- if (child_mask != 0) {
- avxf cnodes;
- /* TODO(sergey): Investigate whether moving cnodes upwards
- * gives a speedup (will be different cache pattern but will
- * avoid extra check here).
- */
-#if BVH_FEATURE(BVH_HAIR)
- if (__float_as_uint(inodes.x) & PATH_RAY_NODE_UNALIGNED) {
- cnodes = kernel_tex_fetch_avxf(__bvh_nodes, node_addr + 26);
- }
- else
-#endif
- {
- cnodes = kernel_tex_fetch_avxf(__bvh_nodes, node_addr + 14);
- }
-
- /* One child is hit, continue with that child. */
- int r = __bscf(child_mask);
- float d0 = ((float *)&dist)[r];
- if (child_mask == 0) {
- node_addr = __float_as_int(cnodes[r]);
- node_dist = d0;
- continue;
- }
-
- /* Two children are hit, push far child, and continue with
- * closer child.
- */
- int c0 = __float_as_int(cnodes[r]);
- r = __bscf(child_mask);
- int c1 = __float_as_int(cnodes[r]);
- float d1 = ((float *)&dist)[r];
- if (child_mask == 0) {
- if (d1 < d0) {
- node_addr = c1;
- node_dist = d1;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c0;
- traversal_stack[stack_ptr].dist = d0;
- continue;
- }
- else {
- node_addr = c0;
- node_dist = d0;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c1;
- traversal_stack[stack_ptr].dist = d1;
- continue;
- }
- }
-
- /* Here starts the slow path for 3 or 4 hit children. We push
- * all nodes onto the stack to sort them there.
- */
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c1;
- traversal_stack[stack_ptr].dist = d1;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c0;
- traversal_stack[stack_ptr].dist = d0;
-
- /* Three children are hit, push all onto stack and sort 3
- * stack items, continue with closest child.
- */
- r = __bscf(child_mask);
- int c2 = __float_as_int(cnodes[r]);
- float d2 = ((float *)&dist)[r];
- if (child_mask == 0) {
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c2;
- traversal_stack[stack_ptr].dist = d2;
- obvh_stack_sort(&traversal_stack[stack_ptr],
- &traversal_stack[stack_ptr - 1],
- &traversal_stack[stack_ptr - 2]);
- node_addr = traversal_stack[stack_ptr].addr;
- node_dist = traversal_stack[stack_ptr].dist;
- --stack_ptr;
- continue;
- }
-
- /* Four children are hit, push all onto stack and sort 4
- * stack items, continue with closest child.
- */
- r = __bscf(child_mask);
- int c3 = __float_as_int(cnodes[r]);
- float d3 = ((float *)&dist)[r];
- if (child_mask == 0) {
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c3;
- traversal_stack[stack_ptr].dist = d3;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c2;
- traversal_stack[stack_ptr].dist = d2;
- obvh_stack_sort(&traversal_stack[stack_ptr],
- &traversal_stack[stack_ptr - 1],
- &traversal_stack[stack_ptr - 2],
- &traversal_stack[stack_ptr - 3]);
- node_addr = traversal_stack[stack_ptr].addr;
- node_dist = traversal_stack[stack_ptr].dist;
- --stack_ptr;
- continue;
- }
-
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c3;
- traversal_stack[stack_ptr].dist = d3;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c2;
- traversal_stack[stack_ptr].dist = d2;
-
- /* Five children are hit, push all onto stack and sort 5
- * stack items, continue with closest child.
- */
- r = __bscf(child_mask);
- int c4 = __float_as_int(cnodes[r]);
- float d4 = ((float *)&dist)[r];
- if (child_mask == 0) {
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c4;
- traversal_stack[stack_ptr].dist = d4;
- obvh_stack_sort(&traversal_stack[stack_ptr],
- &traversal_stack[stack_ptr - 1],
- &traversal_stack[stack_ptr - 2],
- &traversal_stack[stack_ptr - 3],
- &traversal_stack[stack_ptr - 4]);
- node_addr = traversal_stack[stack_ptr].addr;
- node_dist = traversal_stack[stack_ptr].dist;
- --stack_ptr;
- continue;
- }
-
- /* Six children are hit, push all onto stack and sort 6
- * stack items, continue with closest child.
- */
- r = __bscf(child_mask);
- int c5 = __float_as_int(cnodes[r]);
- float d5 = ((float *)&dist)[r];
- if (child_mask == 0) {
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c5;
- traversal_stack[stack_ptr].dist = d5;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c4;
- traversal_stack[stack_ptr].dist = d4;
- obvh_stack_sort(&traversal_stack[stack_ptr],
- &traversal_stack[stack_ptr - 1],
- &traversal_stack[stack_ptr - 2],
- &traversal_stack[stack_ptr - 3],
- &traversal_stack[stack_ptr - 4],
- &traversal_stack[stack_ptr - 5]);
- node_addr = traversal_stack[stack_ptr].addr;
- node_dist = traversal_stack[stack_ptr].dist;
- --stack_ptr;
- continue;
- }
-
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c5;
- traversal_stack[stack_ptr].dist = d5;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c4;
- traversal_stack[stack_ptr].dist = d4;
-
- /* Seven children are hit, push all onto stack and sort 7
- * stack items, continue with closest child.
- */
- r = __bscf(child_mask);
- int c6 = __float_as_int(cnodes[r]);
- float d6 = ((float *)&dist)[r];
- if (child_mask == 0) {
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c6;
- traversal_stack[stack_ptr].dist = d6;
- obvh_stack_sort(&traversal_stack[stack_ptr],
- &traversal_stack[stack_ptr - 1],
- &traversal_stack[stack_ptr - 2],
- &traversal_stack[stack_ptr - 3],
- &traversal_stack[stack_ptr - 4],
- &traversal_stack[stack_ptr - 5],
- &traversal_stack[stack_ptr - 6]);
- node_addr = traversal_stack[stack_ptr].addr;
- node_dist = traversal_stack[stack_ptr].dist;
- --stack_ptr;
- continue;
- }
-
- /* Eight children are hit, push all onto stack and sort 8
- * stack items, continue with closest child.
- */
- r = __bscf(child_mask);
- int c7 = __float_as_int(cnodes[r]);
- float d7 = ((float *)&dist)[r];
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c7;
- traversal_stack[stack_ptr].dist = d7;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c6;
- traversal_stack[stack_ptr].dist = d6;
- obvh_stack_sort(&traversal_stack[stack_ptr],
- &traversal_stack[stack_ptr - 1],
- &traversal_stack[stack_ptr - 2],
- &traversal_stack[stack_ptr - 3],
- &traversal_stack[stack_ptr - 4],
- &traversal_stack[stack_ptr - 5],
- &traversal_stack[stack_ptr - 6],
- &traversal_stack[stack_ptr - 7]);
- node_addr = traversal_stack[stack_ptr].addr;
- node_dist = traversal_stack[stack_ptr].dist;
- --stack_ptr;
- continue;
- }
-
- node_addr = traversal_stack[stack_ptr].addr;
- node_dist = traversal_stack[stack_ptr].dist;
- --stack_ptr;
- }
-
- /* If node is leaf, fetch triangle list. */
- if (node_addr < 0) {
- float4 leaf = kernel_tex_fetch(__bvh_leaf_nodes, (-node_addr - 1));
-
-#ifdef __VISIBILITY_FLAG__
- if (UNLIKELY((node_dist > isect->t) || ((__float_as_uint(leaf.z) & visibility) == 0)))
-#else
- if (UNLIKELY((node_dist > isect->t)))
-#endif
- {
- /* Pop. */
- node_addr = traversal_stack[stack_ptr].addr;
- node_dist = traversal_stack[stack_ptr].dist;
- --stack_ptr;
- continue;
- }
- int prim_addr = __float_as_int(leaf.x);
-
-#if BVH_FEATURE(BVH_INSTANCING)
- if (prim_addr >= 0) {
-#endif
- int prim_addr2 = __float_as_int(leaf.y);
- const uint type = __float_as_int(leaf.w);
-
- /* Pop. */
- node_addr = traversal_stack[stack_ptr].addr;
- node_dist = traversal_stack[stack_ptr].dist;
- --stack_ptr;
-
- /* Primitive intersection. */
- switch (type & PRIMITIVE_ALL) {
- case PRIMITIVE_TRIANGLE: {
- int prim_count = prim_addr2 - prim_addr;
- if (prim_count < 3) {
- for (; prim_addr < prim_addr2; prim_addr++) {
- BVH_DEBUG_NEXT_INTERSECTION();
- kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type);
- if (triangle_intersect(kg, isect, P, dir, visibility, object, prim_addr)) {
- tfar = avxf(isect->t);
- /* Shadow ray early termination. */
- if (visibility == PATH_RAY_SHADOW_OPAQUE) {
- return true;
- }
- }
- } // for
- }
- else {
- kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type);
- if (triangle_intersect8(kg,
- &isect,
- P,
- dir,
- visibility,
- object,
- prim_addr,
- prim_count,
- 0,
- 0,
- NULL,
- 0.0f)) {
- tfar = avxf(isect->t);
- if (visibility == PATH_RAY_SHADOW_OPAQUE) {
- return true;
- }
- }
- } // prim count
- break;
- }
-#if BVH_FEATURE(BVH_MOTION)
- case PRIMITIVE_MOTION_TRIANGLE: {
- for (; prim_addr < prim_addr2; prim_addr++) {
- BVH_DEBUG_NEXT_INTERSECTION();
- kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type);
- if (motion_triangle_intersect(
- kg, isect, P, dir, ray->time, visibility, object, prim_addr)) {
- tfar = avxf(isect->t);
- /* Shadow ray early termination. */
- if (visibility == PATH_RAY_SHADOW_OPAQUE) {
- return true;
- }
- }
- }
- break;
- }
-#endif /* BVH_FEATURE(BVH_MOTION) */
-#if BVH_FEATURE(BVH_HAIR)
- case PRIMITIVE_CURVE:
- case PRIMITIVE_MOTION_CURVE: {
- for (; prim_addr < prim_addr2; prim_addr++) {
- BVH_DEBUG_NEXT_INTERSECTION();
- const uint curve_type = kernel_tex_fetch(__prim_type, prim_addr);
- kernel_assert((curve_type & PRIMITIVE_ALL) == (type & PRIMITIVE_ALL));
- bool hit;
- if (kernel_data.curve.curveflags & CURVE_KN_INTERPOLATE) {
- hit = cardinal_curve_intersect(
- kg, isect, P, dir, visibility, object, prim_addr, ray->time, curve_type);
- }
- else {
- hit = curve_intersect(
- kg, isect, P, dir, visibility, object, prim_addr, ray->time, curve_type);
- }
- if (hit) {
- tfar = avxf(isect->t);
- /* Shadow ray early termination. */
- if (visibility == PATH_RAY_SHADOW_OPAQUE) {
- return true;
- }
- }
- }
- break;
- }
-#endif /* BVH_FEATURE(BVH_HAIR) */
- }
- }
-#if BVH_FEATURE(BVH_INSTANCING)
- else {
- /* Instance push. */
- object = kernel_tex_fetch(__prim_object, -prim_addr - 1);
-
-# if BVH_FEATURE(BVH_MOTION)
- qbvh_instance_motion_push(
- kg, object, ray, &P, &dir, &idir, &isect->t, &node_dist, &ob_itfm);
-# else
- qbvh_instance_push(kg, object, ray, &P, &dir, &idir, &isect->t, &node_dist);
-# endif
-
- obvh_near_far_idx_calc(idir, &near_x, &near_y, &near_z, &far_x, &far_y, &far_z);
- tfar = avxf(isect->t);
-# if BVH_FEATURE(BVH_HAIR)
- dir4 = avx3f(avxf(dir.x), avxf(dir.y), avxf(dir.z));
-# endif
- idir4 = avx3f(avxf(idir.x), avxf(idir.y), avxf(idir.z));
-# ifdef __KERNEL_AVX2__
- P_idir = P * idir;
- P_idir4 = avx3f(P_idir.x, P_idir.y, P_idir.z);
-# endif
-# if BVH_FEATURE(BVH_HAIR) || !defined(__KERNEL_AVX2__)
- org4 = avx3f(avxf(P.x), avxf(P.y), avxf(P.z));
-# endif
-
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = ENTRYPOINT_SENTINEL;
- traversal_stack[stack_ptr].dist = -FLT_MAX;
-
- node_addr = kernel_tex_fetch(__object_node, object);
-
- BVH_DEBUG_NEXT_INSTANCE();
- }
- }
-#endif /* FEATURE(BVH_INSTANCING) */
- } while (node_addr != ENTRYPOINT_SENTINEL);
-
-#if BVH_FEATURE(BVH_INSTANCING)
- if (stack_ptr >= 0) {
- kernel_assert(object != OBJECT_NONE);
-
- /* Instance pop. */
-# if BVH_FEATURE(BVH_MOTION)
- isect->t = bvh_instance_motion_pop(kg, object, ray, &P, &dir, &idir, isect->t, &ob_itfm);
-# else
- isect->t = bvh_instance_pop(kg, object, ray, &P, &dir, &idir, isect->t);
-# endif
-
- obvh_near_far_idx_calc(idir, &near_x, &near_y, &near_z, &far_x, &far_y, &far_z);
- tfar = avxf(isect->t);
-# if BVH_FEATURE(BVH_HAIR)
- dir4 = avx3f(avxf(dir.x), avxf(dir.y), avxf(dir.z));
-# endif
- idir4 = avx3f(avxf(idir.x), avxf(idir.y), avxf(idir.z));
-# ifdef __KERNEL_AVX2__
- P_idir = P * idir;
- P_idir4 = avx3f(P_idir.x, P_idir.y, P_idir.z);
-# endif
-# if BVH_FEATURE(BVH_HAIR) || !defined(__KERNEL_AVX2__)
- org4 = avx3f(avxf(P.x), avxf(P.y), avxf(P.z));
-# endif
-
- object = OBJECT_NONE;
- node_addr = traversal_stack[stack_ptr].addr;
- node_dist = traversal_stack[stack_ptr].dist;
- --stack_ptr;
- }
-#endif /* FEATURE(BVH_INSTANCING) */
- } while (node_addr != ENTRYPOINT_SENTINEL);
-
- return (isect->prim != PRIM_NONE);
-}
-
-#undef NODE_INTERSECT
diff --git a/intern/cycles/kernel/bvh/obvh_volume.h b/intern/cycles/kernel/bvh/obvh_volume.h
deleted file mode 100644
index fb41ae783ab..00000000000
--- a/intern/cycles/kernel/bvh/obvh_volume.h
+++ /dev/null
@@ -1,480 +0,0 @@
-/*
- * Copyright 2011-2013 Blender Foundation
- *
- * 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.
- */
-
-/* This is a template BVH traversal function for volumes, where
- * various features can be enabled/disabled. This way we can compile optimized
- * versions for each case without new features slowing things down.
- *
- * BVH_INSTANCING: object instancing
- * BVH_MOTION: motion blur rendering
- */
-
-#if BVH_FEATURE(BVH_HAIR)
-# define NODE_INTERSECT obvh_node_intersect
-#else
-# define NODE_INTERSECT obvh_aligned_node_intersect
-#endif
-
-ccl_device bool BVH_FUNCTION_FULL_NAME(OBVH)(KernelGlobals *kg,
- const Ray *ray,
- Intersection *isect,
- const uint visibility)
-{
- /* Traversal stack in CUDA thread-local memory. */
- OBVHStackItem traversal_stack[BVH_OSTACK_SIZE];
- traversal_stack[0].addr = ENTRYPOINT_SENTINEL;
-
- /* Traversal variables in registers. */
- int stack_ptr = 0;
- int node_addr = kernel_data.bvh.root;
-
- /* Ray parameters in registers. */
- float3 P = ray->P;
- float3 dir = bvh_clamp_direction(ray->D);
- float3 idir = bvh_inverse_direction(dir);
- int object = OBJECT_NONE;
-
-#if BVH_FEATURE(BVH_MOTION)
- Transform ob_itfm;
-#endif
-
- isect->t = ray->t;
- isect->u = 0.0f;
- isect->v = 0.0f;
- isect->prim = PRIM_NONE;
- isect->object = OBJECT_NONE;
-
- avxf tnear(0.0f), tfar(ray->t);
-#if BVH_FEATURE(BVH_HAIR)
- avx3f dir4(avxf(dir.x), avxf(dir.y), avxf(dir.z));
-#endif
- avx3f idir4(avxf(idir.x), avxf(idir.y), avxf(idir.z));
-
-#ifdef __KERNEL_AVX2__
- float3 P_idir = P * idir;
- avx3f P_idir4(P_idir.x, P_idir.y, P_idir.z);
-#endif
-#if BVH_FEATURE(BVH_HAIR) || !defined(__KERNEL_AVX2__)
- avx3f org4(avxf(P.x), avxf(P.y), avxf(P.z));
-#endif
-
- /* Offsets to select the side that becomes the lower or upper bound. */
- int near_x, near_y, near_z;
- int far_x, far_y, far_z;
- obvh_near_far_idx_calc(idir, &near_x, &near_y, &near_z, &far_x, &far_y, &far_z);
-
- /* Traversal loop. */
- do {
- do {
- /* Traverse internal nodes. */
- while (node_addr >= 0 && node_addr != ENTRYPOINT_SENTINEL) {
- float4 inodes = kernel_tex_fetch(__bvh_nodes, node_addr + 0);
-
-#ifdef __VISIBILITY_FLAG__
- if ((__float_as_uint(inodes.x) & visibility) == 0) {
- /* Pop. */
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- continue;
- }
-#endif
-
- avxf dist;
- int child_mask = NODE_INTERSECT(kg,
- tnear,
- tfar,
-#ifdef __KERNEL_AVX2__
- P_idir4,
-#endif
-#if BVH_FEATURE(BVH_HAIR) || !defined(__KERNEL_AVX2__)
- org4,
-#endif
-#if BVH_FEATURE(BVH_HAIR)
- dir4,
-#endif
- idir4,
- near_x,
- near_y,
- near_z,
- far_x,
- far_y,
- far_z,
- node_addr,
- &dist);
-
- if (child_mask != 0) {
- avxf cnodes;
-#if BVH_FEATURE(BVH_HAIR)
- if (__float_as_uint(inodes.x) & PATH_RAY_NODE_UNALIGNED) {
- cnodes = kernel_tex_fetch_avxf(__bvh_nodes, node_addr + 26);
- }
- else
-#endif
- {
- cnodes = kernel_tex_fetch_avxf(__bvh_nodes, node_addr + 14);
- }
-
- /* One child is hit, continue with that child. */
- int r = __bscf(child_mask);
- if (child_mask == 0) {
- node_addr = __float_as_int(cnodes[r]);
- continue;
- }
-
- /* Two children are hit, push far child, and continue with
- * closer child.
- */
- int c0 = __float_as_int(cnodes[r]);
- float d0 = ((float *)&dist)[r];
- r = __bscf(child_mask);
- int c1 = __float_as_int(cnodes[r]);
- float d1 = ((float *)&dist)[r];
- if (child_mask == 0) {
- if (d1 < d0) {
- node_addr = c1;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c0;
- traversal_stack[stack_ptr].dist = d0;
- continue;
- }
- else {
- node_addr = c0;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c1;
- traversal_stack[stack_ptr].dist = d1;
- continue;
- }
- }
-
- /* Here starts the slow path for 3 or 4 hit children. We push
- * all nodes onto the stack to sort them there.
- */
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c1;
- traversal_stack[stack_ptr].dist = d1;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c0;
- traversal_stack[stack_ptr].dist = d0;
-
- /* Three children are hit, push all onto stack and sort 3
- * stack items, continue with closest child.
- */
- r = __bscf(child_mask);
- int c2 = __float_as_int(cnodes[r]);
- float d2 = ((float *)&dist)[r];
- if (child_mask == 0) {
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c2;
- traversal_stack[stack_ptr].dist = d2;
- obvh_stack_sort(&traversal_stack[stack_ptr],
- &traversal_stack[stack_ptr - 1],
- &traversal_stack[stack_ptr - 2]);
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- continue;
- }
-
- /* Four children are hit, push all onto stack and sort 4
- * stack items, continue with closest child.
- */
- r = __bscf(child_mask);
- int c3 = __float_as_int(cnodes[r]);
- float d3 = ((float *)&dist)[r];
- if (child_mask == 0) {
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c3;
- traversal_stack[stack_ptr].dist = d3;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c2;
- traversal_stack[stack_ptr].dist = d2;
- obvh_stack_sort(&traversal_stack[stack_ptr],
- &traversal_stack[stack_ptr - 1],
- &traversal_stack[stack_ptr - 2],
- &traversal_stack[stack_ptr - 3]);
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- continue;
- }
-
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c3;
- traversal_stack[stack_ptr].dist = d3;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c2;
- traversal_stack[stack_ptr].dist = d2;
-
- /* Five children are hit, push all onto stack and sort 5
- * stack items, continue with closest child
- */
- r = __bscf(child_mask);
- int c4 = __float_as_int(cnodes[r]);
- float d4 = ((float *)&dist)[r];
- if (child_mask == 0) {
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c4;
- traversal_stack[stack_ptr].dist = d4;
- obvh_stack_sort(&traversal_stack[stack_ptr],
- &traversal_stack[stack_ptr - 1],
- &traversal_stack[stack_ptr - 2],
- &traversal_stack[stack_ptr - 3],
- &traversal_stack[stack_ptr - 4]);
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- continue;
- }
-
- /* Six children are hit, push all onto stack and sort 6
- * stack items, continue with closest child.
- */
- r = __bscf(child_mask);
- int c5 = __float_as_int(cnodes[r]);
- float d5 = ((float *)&dist)[r];
- if (child_mask == 0) {
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c5;
- traversal_stack[stack_ptr].dist = d5;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c4;
- traversal_stack[stack_ptr].dist = d4;
- obvh_stack_sort(&traversal_stack[stack_ptr],
- &traversal_stack[stack_ptr - 1],
- &traversal_stack[stack_ptr - 2],
- &traversal_stack[stack_ptr - 3],
- &traversal_stack[stack_ptr - 4],
- &traversal_stack[stack_ptr - 5]);
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- continue;
- }
-
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c5;
- traversal_stack[stack_ptr].dist = d5;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c4;
- traversal_stack[stack_ptr].dist = d4;
-
- /* Seven children are hit, push all onto stack and sort 7
- * stack items, continue with closest child.
- */
- r = __bscf(child_mask);
- int c6 = __float_as_int(cnodes[r]);
- float d6 = ((float *)&dist)[r];
- if (child_mask == 0) {
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c6;
- traversal_stack[stack_ptr].dist = d6;
- obvh_stack_sort(&traversal_stack[stack_ptr],
- &traversal_stack[stack_ptr - 1],
- &traversal_stack[stack_ptr - 2],
- &traversal_stack[stack_ptr - 3],
- &traversal_stack[stack_ptr - 4],
- &traversal_stack[stack_ptr - 5],
- &traversal_stack[stack_ptr - 6]);
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- continue;
- }
-
- /* Eight children are hit, push all onto stack and sort 8
- * stack items, continue with closest child.
- */
- r = __bscf(child_mask);
- int c7 = __float_as_int(cnodes[r]);
- float d7 = ((float *)&dist)[r];
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c7;
- traversal_stack[stack_ptr].dist = d7;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c6;
- traversal_stack[stack_ptr].dist = d6;
- obvh_stack_sort(&traversal_stack[stack_ptr],
- &traversal_stack[stack_ptr - 1],
- &traversal_stack[stack_ptr - 2],
- &traversal_stack[stack_ptr - 3],
- &traversal_stack[stack_ptr - 4],
- &traversal_stack[stack_ptr - 5],
- &traversal_stack[stack_ptr - 6],
- &traversal_stack[stack_ptr - 7]);
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- continue;
- }
-
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- }
-
- /* If node is leaf, fetch triangle list. */
- if (node_addr < 0) {
- float4 leaf = kernel_tex_fetch(__bvh_leaf_nodes, (-node_addr - 1));
-
- if ((__float_as_uint(leaf.z) & visibility) == 0) {
- /* Pop. */
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- continue;
- }
-
- int prim_addr = __float_as_int(leaf.x);
-
-#if BVH_FEATURE(BVH_INSTANCING)
- if (prim_addr >= 0) {
-#endif
- int prim_addr2 = __float_as_int(leaf.y);
- const uint type = __float_as_int(leaf.w);
- const uint p_type = type & PRIMITIVE_ALL;
-
- /* Pop. */
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
-
- /* Primitive intersection. */
- switch (p_type) {
- case PRIMITIVE_TRIANGLE: {
- for (; prim_addr < prim_addr2; prim_addr++) {
- kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type);
- /* Only primitives from volume object. */
- uint tri_object = (object == OBJECT_NONE) ?
- kernel_tex_fetch(__prim_object, prim_addr) :
- object;
- int object_flag = kernel_tex_fetch(__object_flag, tri_object);
- if ((object_flag & SD_OBJECT_HAS_VOLUME) == 0) {
- continue;
- }
- /* Intersect ray against primitive. */
- triangle_intersect(kg, isect, P, dir, visibility, object, prim_addr);
- }
- break;
- }
-#if BVH_FEATURE(BVH_MOTION)
- case PRIMITIVE_MOTION_TRIANGLE: {
- for (; prim_addr < prim_addr2; prim_addr++) {
- kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type);
- /* Only primitives from volume object. */
- uint tri_object = (object == OBJECT_NONE) ?
- kernel_tex_fetch(__prim_object, prim_addr) :
- object;
- int object_flag = kernel_tex_fetch(__object_flag, tri_object);
- if ((object_flag & SD_OBJECT_HAS_VOLUME) == 0) {
- continue;
- }
- /* Intersect ray against primitive. */
- motion_triangle_intersect(
- kg, isect, P, dir, ray->time, visibility, object, prim_addr);
- }
- break;
- }
-#endif
- }
- }
-#if BVH_FEATURE(BVH_INSTANCING)
- else {
- /* Instance push. */
- object = kernel_tex_fetch(__prim_object, -prim_addr - 1);
- int object_flag = kernel_tex_fetch(__object_flag, object);
- if (object_flag & SD_OBJECT_HAS_VOLUME) {
-# if BVH_FEATURE(BVH_MOTION)
- isect->t = bvh_instance_motion_push(
- kg, object, ray, &P, &dir, &idir, isect->t, &ob_itfm);
-# else
- isect->t = bvh_instance_push(kg, object, ray, &P, &dir, &idir, isect->t);
-# endif
-
- obvh_near_far_idx_calc(idir, &near_x, &near_y, &near_z, &far_x, &far_y, &far_z);
- tfar = avxf(isect->t);
-# if BVH_FEATURE(BVH_HAIR)
- dir4 = avx3f(avxf(dir.x), avxf(dir.y), avxf(dir.z));
-# endif
- idir4 = avx3f(avxf(idir.x), avxf(idir.y), avxf(idir.z));
-# ifdef __KERNEL_AVX2__
- P_idir = P * idir;
- P_idir4 = avx3f(P_idir.x, P_idir.y, P_idir.z);
-# endif
-# if BVH_FEATURE(BVH_HAIR) || !defined(__KERNEL_AVX2__)
- org4 = avx3f(avxf(P.x), avxf(P.y), avxf(P.z));
-# endif
-
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = ENTRYPOINT_SENTINEL;
-
- node_addr = kernel_tex_fetch(__object_node, object);
- }
- else {
- /* Pop. */
- object = OBJECT_NONE;
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- }
- }
- }
-#endif /* FEATURE(BVH_INSTANCING) */
- } while (node_addr != ENTRYPOINT_SENTINEL);
-
-#if BVH_FEATURE(BVH_INSTANCING)
- if (stack_ptr >= 0) {
- kernel_assert(object != OBJECT_NONE);
-
- /* Instance pop. */
-# if BVH_FEATURE(BVH_MOTION)
- isect->t = bvh_instance_motion_pop(kg, object, ray, &P, &dir, &idir, isect->t, &ob_itfm);
-# else
- isect->t = bvh_instance_pop(kg, object, ray, &P, &dir, &idir, isect->t);
-# endif
-
- obvh_near_far_idx_calc(idir, &near_x, &near_y, &near_z, &far_x, &far_y, &far_z);
- tfar = avxf(isect->t);
-# if BVH_FEATURE(BVH_HAIR)
- dir4 = avx3f(avxf(dir.x), avxf(dir.y), avxf(dir.z));
-# endif
- idir4 = avx3f(avxf(idir.x), avxf(idir.y), avxf(idir.z));
-# ifdef __KERNEL_AVX2__
- P_idir = P * idir;
- P_idir4 = avx3f(P_idir.x, P_idir.y, P_idir.z);
-# endif
-# if BVH_FEATURE(BVH_HAIR) || !defined(__KERNEL_AVX2__)
- org4 = avx3f(avxf(P.x), avxf(P.y), avxf(P.z));
-# endif
-
- object = OBJECT_NONE;
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- }
-#endif /* FEATURE(BVH_INSTANCING) */
- } while (node_addr != ENTRYPOINT_SENTINEL);
-
- return (isect->prim != PRIM_NONE);
-}
-
-#undef NODE_INTERSECT
diff --git a/intern/cycles/kernel/bvh/obvh_volume_all.h b/intern/cycles/kernel/bvh/obvh_volume_all.h
deleted file mode 100644
index 56e2afd4a11..00000000000
--- a/intern/cycles/kernel/bvh/obvh_volume_all.h
+++ /dev/null
@@ -1,551 +0,0 @@
-/*
- * Copyright 2011-2013 Blender Foundation
- *
- * 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.
- */
-
-/* This is a template BVH traversal function for volumes, where
- * various features can be enabled/disabled. This way we can compile optimized
- * versions for each case without new features slowing things down.
- *
- * BVH_INSTANCING: object instancing
- * BVH_MOTION: motion blur rendering
- */
-
-#if BVH_FEATURE(BVH_HAIR)
-# define NODE_INTERSECT obvh_node_intersect
-#else
-# define NODE_INTERSECT obvh_aligned_node_intersect
-#endif
-
-ccl_device uint BVH_FUNCTION_FULL_NAME(OBVH)(KernelGlobals *kg,
- const Ray *ray,
- Intersection *isect_array,
- const uint max_hits,
- const uint visibility)
-{
- /* Traversal stack in CUDA thread-local memory. */
- OBVHStackItem traversal_stack[BVH_OSTACK_SIZE];
- traversal_stack[0].addr = ENTRYPOINT_SENTINEL;
-
- /* Traversal variables in registers. */
- int stack_ptr = 0;
- int node_addr = kernel_data.bvh.root;
-
- /* Ray parameters in registers. */
- const float tmax = ray->t;
- float3 P = ray->P;
- float3 dir = bvh_clamp_direction(ray->D);
- float3 idir = bvh_inverse_direction(dir);
- int object = OBJECT_NONE;
- float isect_t = tmax;
-
-#if BVH_FEATURE(BVH_MOTION)
- Transform ob_itfm;
-#endif
-
- uint num_hits = 0;
- isect_array->t = tmax;
-
-#if BVH_FEATURE(BVH_INSTANCING)
- int num_hits_in_instance = 0;
-#endif
-
- avxf tnear(0.0f), tfar(isect_t);
-#if BVH_FEATURE(BVH_HAIR)
- avx3f dir4(avxf(dir.x), avxf(dir.y), avxf(dir.z));
-#endif
- avx3f idir4(avxf(idir.x), avxf(idir.y), avxf(idir.z));
-
-#ifdef __KERNEL_AVX2__
- float3 P_idir = P * idir;
- avx3f P_idir4(P_idir.x, P_idir.y, P_idir.z);
-#endif
-#if BVH_FEATURE(BVH_HAIR) || !defined(__KERNEL_AVX2__)
- avx3f org4(avxf(P.x), avxf(P.y), avxf(P.z));
-#endif
-
- /* Offsets to select the side that becomes the lower or upper bound. */
- int near_x, near_y, near_z;
- int far_x, far_y, far_z;
- obvh_near_far_idx_calc(idir, &near_x, &near_y, &near_z, &far_x, &far_y, &far_z);
-
- /* Traversal loop. */
- do {
- do {
- /* Traverse internal nodes. */
- while (node_addr >= 0 && node_addr != ENTRYPOINT_SENTINEL) {
- float4 inodes = kernel_tex_fetch(__bvh_nodes, node_addr + 0);
-
-#ifdef __VISIBILITY_FLAG__
- if ((__float_as_uint(inodes.x) & visibility) == 0) {
- /* Pop. */
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- continue;
- }
-#endif
-
- avxf dist;
- int child_mask = NODE_INTERSECT(kg,
- tnear,
- tfar,
-#ifdef __KERNEL_AVX2__
- P_idir4,
-#endif
-#if BVH_FEATURE(BVH_HAIR) || !defined(__KERNEL_AVX2__)
- org4,
-#endif
-#if BVH_FEATURE(BVH_HAIR)
- dir4,
-#endif
- idir4,
- near_x,
- near_y,
- near_z,
- far_x,
- far_y,
- far_z,
- node_addr,
- &dist);
-
- if (child_mask != 0) {
- avxf cnodes;
-#if BVH_FEATURE(BVH_HAIR)
- if (__float_as_uint(inodes.x) & PATH_RAY_NODE_UNALIGNED) {
- cnodes = kernel_tex_fetch_avxf(__bvh_nodes, node_addr + 26);
- }
- else
-#endif
- {
- cnodes = kernel_tex_fetch_avxf(__bvh_nodes, node_addr + 14);
- }
-
- /* One child is hit, continue with that child. */
- int r = __bscf(child_mask);
- if (child_mask == 0) {
- node_addr = __float_as_int(cnodes[r]);
- continue;
- }
-
- /* Two children are hit, push far child, and continue with
- * closer child.
- */
- int c0 = __float_as_int(cnodes[r]);
- float d0 = ((float *)&dist)[r];
- r = __bscf(child_mask);
- int c1 = __float_as_int(cnodes[r]);
- float d1 = ((float *)&dist)[r];
- if (child_mask == 0) {
- if (d1 < d0) {
- node_addr = c1;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c0;
- traversal_stack[stack_ptr].dist = d0;
- continue;
- }
- else {
- node_addr = c0;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c1;
- traversal_stack[stack_ptr].dist = d1;
- continue;
- }
- }
-
- /* Here starts the slow path for 3 or 4 hit children. We push
- * all nodes onto the stack to sort them there.
- */
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c1;
- traversal_stack[stack_ptr].dist = d1;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c0;
- traversal_stack[stack_ptr].dist = d0;
-
- /* Three children are hit, push all onto stack and sort 3
- * stack items, continue with closest child.
- */
- r = __bscf(child_mask);
- int c2 = __float_as_int(cnodes[r]);
- float d2 = ((float *)&dist)[r];
- if (child_mask == 0) {
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c2;
- traversal_stack[stack_ptr].dist = d2;
- obvh_stack_sort(&traversal_stack[stack_ptr],
- &traversal_stack[stack_ptr - 1],
- &traversal_stack[stack_ptr - 2]);
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- continue;
- }
-
- /* Four children are hit, push all onto stack and sort 4
- * stack items, continue with closest child.
- */
- r = __bscf(child_mask);
- int c3 = __float_as_int(cnodes[r]);
- float d3 = ((float *)&dist)[r];
- if (child_mask == 0) {
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c3;
- traversal_stack[stack_ptr].dist = d3;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c2;
- traversal_stack[stack_ptr].dist = d2;
- obvh_stack_sort(&traversal_stack[stack_ptr],
- &traversal_stack[stack_ptr - 1],
- &traversal_stack[stack_ptr - 2],
- &traversal_stack[stack_ptr - 3]);
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- continue;
- }
-
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c3;
- traversal_stack[stack_ptr].dist = d3;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c2;
- traversal_stack[stack_ptr].dist = d2;
-
- /* Five children are hit, push all onto stack and sort 5
- * stack items, continue with closest child.
- */
- r = __bscf(child_mask);
- int c4 = __float_as_int(cnodes[r]);
- float d4 = ((float *)&dist)[r];
- if (child_mask == 0) {
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c4;
- traversal_stack[stack_ptr].dist = d4;
- obvh_stack_sort(&traversal_stack[stack_ptr],
- &traversal_stack[stack_ptr - 1],
- &traversal_stack[stack_ptr - 2],
- &traversal_stack[stack_ptr - 3],
- &traversal_stack[stack_ptr - 4]);
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- continue;
- }
-
- /* Six children are hit, push all onto stack and sort 6
- * stack items, continue with closest child.
- */
- r = __bscf(child_mask);
- int c5 = __float_as_int(cnodes[r]);
- float d5 = ((float *)&dist)[r];
- if (child_mask == 0) {
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c5;
- traversal_stack[stack_ptr].dist = d5;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c4;
- traversal_stack[stack_ptr].dist = d4;
- obvh_stack_sort(&traversal_stack[stack_ptr],
- &traversal_stack[stack_ptr - 1],
- &traversal_stack[stack_ptr - 2],
- &traversal_stack[stack_ptr - 3],
- &traversal_stack[stack_ptr - 4],
- &traversal_stack[stack_ptr - 5]);
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- continue;
- }
-
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c5;
- traversal_stack[stack_ptr].dist = d5;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c4;
- traversal_stack[stack_ptr].dist = d4;
-
- /* Seven children are hit, push all onto stack and sort 7
- * stack items, continue with closest child.
- */
- r = __bscf(child_mask);
- int c6 = __float_as_int(cnodes[r]);
- float d6 = ((float *)&dist)[r];
- if (child_mask == 0) {
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c6;
- traversal_stack[stack_ptr].dist = d6;
- obvh_stack_sort(&traversal_stack[stack_ptr],
- &traversal_stack[stack_ptr - 1],
- &traversal_stack[stack_ptr - 2],
- &traversal_stack[stack_ptr - 3],
- &traversal_stack[stack_ptr - 4],
- &traversal_stack[stack_ptr - 5],
- &traversal_stack[stack_ptr - 6]);
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- continue;
- }
-
- /* Eight children are hit, push all onto stack and sort 8
- * stack items, continue with closest child.
- */
- r = __bscf(child_mask);
- int c7 = __float_as_int(cnodes[r]);
- float d7 = ((float *)&dist)[r];
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c7;
- traversal_stack[stack_ptr].dist = d7;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c6;
- traversal_stack[stack_ptr].dist = d6;
- obvh_stack_sort(&traversal_stack[stack_ptr],
- &traversal_stack[stack_ptr - 1],
- &traversal_stack[stack_ptr - 2],
- &traversal_stack[stack_ptr - 3],
- &traversal_stack[stack_ptr - 4],
- &traversal_stack[stack_ptr - 5],
- &traversal_stack[stack_ptr - 6],
- &traversal_stack[stack_ptr - 7]);
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- continue;
- }
-
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- }
-
- /* If node is leaf, fetch triangle list. */
- if (node_addr < 0) {
- float4 leaf = kernel_tex_fetch(__bvh_leaf_nodes, (-node_addr - 1));
-
- if ((__float_as_uint(leaf.z) & visibility) == 0) {
- /* Pop. */
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- continue;
- }
-
- int prim_addr = __float_as_int(leaf.x);
-
-#if BVH_FEATURE(BVH_INSTANCING)
- if (prim_addr >= 0) {
-#endif
- int prim_addr2 = __float_as_int(leaf.y);
- const uint type = __float_as_int(leaf.w);
- const uint p_type = type & PRIMITIVE_ALL;
- bool hit;
-
- /* Pop. */
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
-
- /* Primitive intersection. */
- switch (p_type) {
- case PRIMITIVE_TRIANGLE: {
- for (; prim_addr < prim_addr2; prim_addr++) {
- kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type);
- /* Only primitives from volume object. */
- uint tri_object = (object == OBJECT_NONE) ?
- kernel_tex_fetch(__prim_object, prim_addr) :
- object;
- int object_flag = kernel_tex_fetch(__object_flag, tri_object);
- if ((object_flag & SD_OBJECT_HAS_VOLUME) == 0) {
- continue;
- }
- /* Intersect ray against primitive. */
- hit = triangle_intersect(kg, isect_array, P, dir, visibility, object, prim_addr);
- if (hit) {
- /* Move on to next entry in intersections array. */
- isect_array++;
- num_hits++;
-#if BVH_FEATURE(BVH_INSTANCING)
- num_hits_in_instance++;
-#endif
- isect_array->t = isect_t;
- if (num_hits == max_hits) {
-#if BVH_FEATURE(BVH_INSTANCING)
-# if BVH_FEATURE(BVH_MOTION)
- float t_fac = 1.0f / len(transform_direction(&ob_itfm, dir));
-# else
- Transform itfm = object_fetch_transform(kg, object, OBJECT_INVERSE_TRANSFORM);
- float t_fac = 1.0f / len(transform_direction(&itfm, dir));
-# endif
- for (int i = 0; i < num_hits_in_instance; i++) {
- (isect_array - i - 1)->t *= t_fac;
- }
-#endif /* BVH_FEATURE(BVH_INSTANCING) */
- return num_hits;
- }
- }
- }
- break;
- }
-#if BVH_FEATURE(BVH_MOTION)
- case PRIMITIVE_MOTION_TRIANGLE: {
- for (; prim_addr < prim_addr2; prim_addr++) {
- kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type);
- /* Only primitives from volume object. */
- uint tri_object = (object == OBJECT_NONE) ?
- kernel_tex_fetch(__prim_object, prim_addr) :
- object;
- int object_flag = kernel_tex_fetch(__object_flag, tri_object);
- if ((object_flag & SD_OBJECT_HAS_VOLUME) == 0) {
- continue;
- }
- /* Intersect ray against primitive. */
- hit = motion_triangle_intersect(
- kg, isect_array, P, dir, ray->time, visibility, object, prim_addr);
- if (hit) {
- /* Move on to next entry in intersections array. */
- isect_array++;
- num_hits++;
-# if BVH_FEATURE(BVH_INSTANCING)
- num_hits_in_instance++;
-# endif
- isect_array->t = isect_t;
- if (num_hits == max_hits) {
-# if BVH_FEATURE(BVH_INSTANCING)
-# if BVH_FEATURE(BVH_MOTION)
- float t_fac = 1.0f / len(transform_direction(&ob_itfm, dir));
-# else
- Transform itfm = object_fetch_transform(kg, object, OBJECT_INVERSE_TRANSFORM);
- float t_fac = 1.0f / len(transform_direction(&itfm, dir));
-# endif
- for (int i = 0; i < num_hits_in_instance; i++) {
- (isect_array - i - 1)->t *= t_fac;
- }
-# endif /* BVH_FEATURE(BVH_INSTANCING) */
- return num_hits;
- }
- }
- }
- break;
- }
-#endif
- }
- }
-#if BVH_FEATURE(BVH_INSTANCING)
- else {
- /* Instance push. */
- object = kernel_tex_fetch(__prim_object, -prim_addr - 1);
- int object_flag = kernel_tex_fetch(__object_flag, object);
- if (object_flag & SD_OBJECT_HAS_VOLUME) {
-# if BVH_FEATURE(BVH_MOTION)
- isect_t = bvh_instance_motion_push(
- kg, object, ray, &P, &dir, &idir, isect_t, &ob_itfm);
-# else
- isect_t = bvh_instance_push(kg, object, ray, &P, &dir, &idir, isect_t);
-# endif
-
- obvh_near_far_idx_calc(idir, &near_x, &near_y, &near_z, &far_x, &far_y, &far_z);
- tfar = avxf(isect_t);
- idir4 = avx3f(avxf(idir.x), avxf(idir.y), avxf(idir.z));
-# if BVH_FEATURE(BVH_HAIR)
- dir4 = avx3f(avxf(dir.x), avxf(dir.y), avxf(dir.z));
-# endif
-# ifdef __KERNEL_AVX2__
- P_idir = P * idir;
- P_idir4 = avx3f(P_idir.x, P_idir.y, P_idir.z);
-# endif
-# if BVH_FEATURE(BVH_HAIR) || !defined(__KERNEL_AVX2__)
- org4 = avx3f(avxf(P.x), avxf(P.y), avxf(P.z));
-# endif
-
- num_hits_in_instance = 0;
- isect_array->t = isect_t;
-
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_OSTACK_SIZE);
- traversal_stack[stack_ptr].addr = ENTRYPOINT_SENTINEL;
-
- node_addr = kernel_tex_fetch(__object_node, object);
- }
- else {
- /* Pop. */
- object = OBJECT_NONE;
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- }
- }
- }
-#endif /* FEATURE(BVH_INSTANCING) */
- } while (node_addr != ENTRYPOINT_SENTINEL);
-
-#if BVH_FEATURE(BVH_INSTANCING)
- if (stack_ptr >= 0) {
- kernel_assert(object != OBJECT_NONE);
-
- /* Instance pop. */
- if (num_hits_in_instance) {
- float t_fac;
-# if BVH_FEATURE(BVH_MOTION)
- bvh_instance_motion_pop_factor(kg, object, ray, &P, &dir, &idir, &t_fac, &ob_itfm);
-# else
- bvh_instance_pop_factor(kg, object, ray, &P, &dir, &idir, &t_fac);
-# endif
- /* Scale isect->t to adjust for instancing. */
- for (int i = 0; i < num_hits_in_instance; i++) {
- (isect_array - i - 1)->t *= t_fac;
- }
- }
- else {
-# if BVH_FEATURE(BVH_MOTION)
- bvh_instance_motion_pop(kg, object, ray, &P, &dir, &idir, FLT_MAX, &ob_itfm);
-# else
- bvh_instance_pop(kg, object, ray, &P, &dir, &idir, FLT_MAX);
-# endif
- }
-
- isect_t = tmax;
- isect_array->t = isect_t;
-
- obvh_near_far_idx_calc(idir, &near_x, &near_y, &near_z, &far_x, &far_y, &far_z);
- tfar = avxf(isect_t);
-# if BVH_FEATURE(BVH_HAIR)
- dir4 = avx3f(avxf(dir.x), avxf(dir.y), avxf(dir.z));
-# endif
- idir4 = avx3f(avxf(idir.x), avxf(idir.y), avxf(idir.z));
-# ifdef __KERNEL_AVX2__
- P_idir = P * idir;
- P_idir4 = avx3f(P_idir.x, P_idir.y, P_idir.z);
-# endif
-# if BVH_FEATURE(BVH_HAIR) || !defined(__KERNEL_AVX2__)
- org4 = avx3f(avxf(P.x), avxf(P.y), avxf(P.z));
-# endif
-
- object = OBJECT_NONE;
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- }
-#endif /* FEATURE(BVH_INSTANCING) */
- } while (node_addr != ENTRYPOINT_SENTINEL);
-
- return num_hits;
-}
-
-#undef NODE_INTERSECT
diff --git a/intern/cycles/kernel/bvh/qbvh_local.h b/intern/cycles/kernel/bvh/qbvh_local.h
deleted file mode 100644
index b21f79bd3a0..00000000000
--- a/intern/cycles/kernel/bvh/qbvh_local.h
+++ /dev/null
@@ -1,291 +0,0 @@
-/*
- * Copyright 2011-2013 Blender Foundation
- *
- * 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.
- */
-
-/* This is a template BVH traversal function for finding local intersections
- * around the shading point, for subsurface scattering and bevel. We disable
- * various features for performance, and for instanced objects avoid traversing
- * other parts of the scene.
- *
- * BVH_MOTION: motion blur rendering
- */
-
-#if BVH_FEATURE(BVH_HAIR)
-# define NODE_INTERSECT qbvh_node_intersect
-#else
-# define NODE_INTERSECT qbvh_aligned_node_intersect
-#endif
-
-ccl_device bool BVH_FUNCTION_FULL_NAME(QBVH)(KernelGlobals *kg,
- const Ray *ray,
- LocalIntersection *local_isect,
- int local_object,
- uint *lcg_state,
- int max_hits)
-{
- /* TODO(sergey):
- * - Test if pushing distance on the stack helps (for non shadow rays).
- * - Separate version for shadow rays.
- * - Likely and unlikely for if() statements.
- * - SSE for hair.
- * - Test restrict attribute for pointers.
- */
-
- /* Traversal stack in CUDA thread-local memory. */
- QBVHStackItem traversal_stack[BVH_QSTACK_SIZE];
- traversal_stack[0].addr = ENTRYPOINT_SENTINEL;
-
- /* Traversal variables in registers. */
- int stack_ptr = 0;
- int node_addr = kernel_tex_fetch(__object_node, local_object);
-
- /* Ray parameters in registers. */
- float3 P = ray->P;
- float3 dir = bvh_clamp_direction(ray->D);
- float3 idir = bvh_inverse_direction(dir);
- int object = OBJECT_NONE;
- float isect_t = ray->t;
-
- if (local_isect != NULL) {
- local_isect->num_hits = 0;
- }
- kernel_assert((local_isect == NULL) == (max_hits == 0));
-
- const int object_flag = kernel_tex_fetch(__object_flag, local_object);
- if (!(object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
-#if BVH_FEATURE(BVH_MOTION)
- Transform ob_itfm;
- isect_t = bvh_instance_motion_push(kg, local_object, ray, &P, &dir, &idir, isect_t, &ob_itfm);
-#else
- isect_t = bvh_instance_push(kg, local_object, ray, &P, &dir, &idir, isect_t);
-#endif
- object = local_object;
- }
-
- ssef tnear(0.0f), tfar(isect_t);
-#if BVH_FEATURE(BVH_HAIR)
- sse3f dir4(ssef(dir.x), ssef(dir.y), ssef(dir.z));
-#endif
- sse3f idir4(ssef(idir.x), ssef(idir.y), ssef(idir.z));
-
-#ifdef __KERNEL_AVX2__
- float3 P_idir = P * idir;
- sse3f P_idir4(P_idir.x, P_idir.y, P_idir.z);
-#endif
-#if BVH_FEATURE(BVH_HAIR) || !defined(__KERNEL_AVX2__)
- sse3f org4(ssef(P.x), ssef(P.y), ssef(P.z));
-#endif
-
- /* Offsets to select the side that becomes the lower or upper bound. */
- int near_x, near_y, near_z;
- int far_x, far_y, far_z;
- qbvh_near_far_idx_calc(idir, &near_x, &near_y, &near_z, &far_x, &far_y, &far_z);
-
- /* Traversal loop. */
- do {
- do {
- /* Traverse internal nodes. */
- while (node_addr >= 0 && node_addr != ENTRYPOINT_SENTINEL) {
- ssef dist;
- int child_mask = NODE_INTERSECT(kg,
- tnear,
- tfar,
-#ifdef __KERNEL_AVX2__
- P_idir4,
-#endif
-#if BVH_FEATURE(BVH_HAIR) || !defined(__KERNEL_AVX2__)
- org4,
-#endif
-#if BVH_FEATURE(BVH_HAIR)
- dir4,
-#endif
- idir4,
- near_x,
- near_y,
- near_z,
- far_x,
- far_y,
- far_z,
- node_addr,
- &dist);
-
- if (child_mask != 0) {
- float4 inodes = kernel_tex_fetch(__bvh_nodes, node_addr + 0);
- float4 cnodes;
-#if BVH_FEATURE(BVH_HAIR)
- if (__float_as_uint(inodes.x) & PATH_RAY_NODE_UNALIGNED) {
- cnodes = kernel_tex_fetch(__bvh_nodes, node_addr + 13);
- }
- else
-#endif
- {
- cnodes = kernel_tex_fetch(__bvh_nodes, node_addr + 7);
- }
-
- /* One child is hit, continue with that child. */
- int r = __bscf(child_mask);
- if (child_mask == 0) {
- node_addr = __float_as_int(cnodes[r]);
- continue;
- }
-
- /* Two children are hit, push far child, and continue with
- * closer child.
- */
- int c0 = __float_as_int(cnodes[r]);
- float d0 = ((float *)&dist)[r];
- r = __bscf(child_mask);
- int c1 = __float_as_int(cnodes[r]);
- float d1 = ((float *)&dist)[r];
- if (child_mask == 0) {
- if (d1 < d0) {
- node_addr = c1;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_QSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c0;
- traversal_stack[stack_ptr].dist = d0;
- continue;
- }
- else {
- node_addr = c0;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_QSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c1;
- traversal_stack[stack_ptr].dist = d1;
- continue;
- }
- }
-
- /* Here starts the slow path for 3 or 4 hit children. We push
- * all nodes onto the stack to sort them there.
- */
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_QSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c1;
- traversal_stack[stack_ptr].dist = d1;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_QSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c0;
- traversal_stack[stack_ptr].dist = d0;
-
- /* Three children are hit, push all onto stack and sort 3
- * stack items, continue with closest child.
- */
- r = __bscf(child_mask);
- int c2 = __float_as_int(cnodes[r]);
- float d2 = ((float *)&dist)[r];
- if (child_mask == 0) {
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_QSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c2;
- traversal_stack[stack_ptr].dist = d2;
- qbvh_stack_sort(&traversal_stack[stack_ptr],
- &traversal_stack[stack_ptr - 1],
- &traversal_stack[stack_ptr - 2]);
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- continue;
- }
-
- /* Four children are hit, push all onto stack and sort 4
- * stack items, continue with closest child.
- */
- r = __bscf(child_mask);
- int c3 = __float_as_int(cnodes[r]);
- float d3 = ((float *)&dist)[r];
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_QSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c3;
- traversal_stack[stack_ptr].dist = d3;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_QSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c2;
- traversal_stack[stack_ptr].dist = d2;
- qbvh_stack_sort(&traversal_stack[stack_ptr],
- &traversal_stack[stack_ptr - 1],
- &traversal_stack[stack_ptr - 2],
- &traversal_stack[stack_ptr - 3]);
- }
-
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- }
-
- /* If node is leaf, fetch triangle list. */
- if (node_addr < 0) {
- float4 leaf = kernel_tex_fetch(__bvh_leaf_nodes, (-node_addr - 1));
- int prim_addr = __float_as_int(leaf.x);
-
- int prim_addr2 = __float_as_int(leaf.y);
- const uint type = __float_as_int(leaf.w);
-
- /* Pop. */
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
-
- /* Primitive intersection. */
- switch (type & PRIMITIVE_ALL) {
- case PRIMITIVE_TRIANGLE: {
- /* Intersect ray against primitive, */
- for (; prim_addr < prim_addr2; prim_addr++) {
- kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type);
- if (triangle_intersect_local(kg,
- local_isect,
- P,
- dir,
- object,
- local_object,
- prim_addr,
- isect_t,
- lcg_state,
- max_hits)) {
- return true;
- }
- }
- break;
- }
-#if BVH_FEATURE(BVH_MOTION)
- case PRIMITIVE_MOTION_TRIANGLE: {
- /* Intersect ray against primitive. */
- for (; prim_addr < prim_addr2; prim_addr++) {
- kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type);
- if (motion_triangle_intersect_local(kg,
- local_isect,
- P,
- dir,
- ray->time,
- object,
- local_object,
- prim_addr,
- isect_t,
- lcg_state,
- max_hits)) {
- return true;
- }
- }
- break;
- }
-#endif
- default:
- break;
- }
- }
- } while (node_addr != ENTRYPOINT_SENTINEL);
- } while (node_addr != ENTRYPOINT_SENTINEL);
-
- return false;
-}
-
-#undef NODE_INTERSECT
diff --git a/intern/cycles/kernel/bvh/qbvh_nodes.h b/intern/cycles/kernel/bvh/qbvh_nodes.h
deleted file mode 100644
index 070406fb18a..00000000000
--- a/intern/cycles/kernel/bvh/qbvh_nodes.h
+++ /dev/null
@@ -1,329 +0,0 @@
-/*
- * Copyright 2011-2014, Blender Foundation.
- *
- * 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.
- *
- * Aligned nodes intersection SSE code is adopted from Embree,
- */
-
-struct QBVHStackItem {
- int addr;
- float dist;
-};
-
-ccl_device_inline void qbvh_near_far_idx_calc(const float3 &idir,
- int *ccl_restrict near_x,
- int *ccl_restrict near_y,
- int *ccl_restrict near_z,
- int *ccl_restrict far_x,
- int *ccl_restrict far_y,
- int *ccl_restrict far_z)
-
-{
-#ifdef __KERNEL_SSE__
- *near_x = 0;
- *far_x = 1;
- *near_y = 2;
- *far_y = 3;
- *near_z = 4;
- *far_z = 5;
-
- const size_t mask = movemask(ssef(idir.m128));
-
- const int mask_x = mask & 1;
- const int mask_y = (mask & 2) >> 1;
- const int mask_z = (mask & 4) >> 2;
-
- *near_x += mask_x;
- *far_x -= mask_x;
- *near_y += mask_y;
- *far_y -= mask_y;
- *near_z += mask_z;
- *far_z -= mask_z;
-#else
- if (idir.x >= 0.0f) {
- *near_x = 0;
- *far_x = 1;
- }
- else {
- *near_x = 1;
- *far_x = 0;
- }
- if (idir.y >= 0.0f) {
- *near_y = 2;
- *far_y = 3;
- }
- else {
- *near_y = 3;
- *far_y = 2;
- }
- if (idir.z >= 0.0f) {
- *near_z = 4;
- *far_z = 5;
- }
- else {
- *near_z = 5;
- *far_z = 4;
- }
-#endif
-}
-
-/* TOOD(sergey): Investigate if using intrinsics helps for both
- * stack item swap and float comparison.
- */
-ccl_device_inline void qbvh_item_swap(QBVHStackItem *ccl_restrict a, QBVHStackItem *ccl_restrict b)
-{
- QBVHStackItem tmp = *a;
- *a = *b;
- *b = tmp;
-}
-
-ccl_device_inline void qbvh_stack_sort(QBVHStackItem *ccl_restrict s1,
- QBVHStackItem *ccl_restrict s2,
- QBVHStackItem *ccl_restrict s3)
-{
- if (s2->dist < s1->dist) {
- qbvh_item_swap(s2, s1);
- }
- if (s3->dist < s2->dist) {
- qbvh_item_swap(s3, s2);
- }
- if (s2->dist < s1->dist) {
- qbvh_item_swap(s2, s1);
- }
-}
-
-ccl_device_inline void qbvh_stack_sort(QBVHStackItem *ccl_restrict s1,
- QBVHStackItem *ccl_restrict s2,
- QBVHStackItem *ccl_restrict s3,
- QBVHStackItem *ccl_restrict s4)
-{
- if (s2->dist < s1->dist) {
- qbvh_item_swap(s2, s1);
- }
- if (s4->dist < s3->dist) {
- qbvh_item_swap(s4, s3);
- }
- if (s3->dist < s1->dist) {
- qbvh_item_swap(s3, s1);
- }
- if (s4->dist < s2->dist) {
- qbvh_item_swap(s4, s2);
- }
- if (s3->dist < s2->dist) {
- qbvh_item_swap(s3, s2);
- }
-}
-
-/* Axis-aligned nodes intersection */
-
-// ccl_device_inline int qbvh_aligned_node_intersect(KernelGlobals *ccl_restrict kg,
-static int qbvh_aligned_node_intersect(KernelGlobals *ccl_restrict kg,
- const ssef &isect_near,
- const ssef &isect_far,
-#ifdef __KERNEL_AVX2__
- const sse3f &org_idir,
-#else
- const sse3f &org,
-#endif
- const sse3f &idir,
- const int near_x,
- const int near_y,
- const int near_z,
- const int far_x,
- const int far_y,
- const int far_z,
- const int node_addr,
- ssef *ccl_restrict dist)
-{
- const int offset = node_addr + 1;
-#ifdef __KERNEL_AVX2__
- const ssef tnear_x = msub(
- kernel_tex_fetch_ssef(__bvh_nodes, offset + near_x), idir.x, org_idir.x);
- const ssef tnear_y = msub(
- kernel_tex_fetch_ssef(__bvh_nodes, offset + near_y), idir.y, org_idir.y);
- const ssef tnear_z = msub(
- kernel_tex_fetch_ssef(__bvh_nodes, offset + near_z), idir.z, org_idir.z);
- const ssef tfar_x = msub(kernel_tex_fetch_ssef(__bvh_nodes, offset + far_x), idir.x, org_idir.x);
- const ssef tfar_y = msub(kernel_tex_fetch_ssef(__bvh_nodes, offset + far_y), idir.y, org_idir.y);
- const ssef tfar_z = msub(kernel_tex_fetch_ssef(__bvh_nodes, offset + far_z), idir.z, org_idir.z);
-#else
- const ssef tnear_x = (kernel_tex_fetch_ssef(__bvh_nodes, offset + near_x) - org.x) * idir.x;
- const ssef tnear_y = (kernel_tex_fetch_ssef(__bvh_nodes, offset + near_y) - org.y) * idir.y;
- const ssef tnear_z = (kernel_tex_fetch_ssef(__bvh_nodes, offset + near_z) - org.z) * idir.z;
- const ssef tfar_x = (kernel_tex_fetch_ssef(__bvh_nodes, offset + far_x) - org.x) * idir.x;
- const ssef tfar_y = (kernel_tex_fetch_ssef(__bvh_nodes, offset + far_y) - org.y) * idir.y;
- const ssef tfar_z = (kernel_tex_fetch_ssef(__bvh_nodes, offset + far_z) - org.z) * idir.z;
-#endif
-
-#ifdef __KERNEL_SSE41__
- const ssef tnear = maxi(maxi(tnear_x, tnear_y), maxi(tnear_z, isect_near));
- const ssef tfar = mini(mini(tfar_x, tfar_y), mini(tfar_z, isect_far));
- const sseb vmask = cast(tnear) > cast(tfar);
- int mask = (int)movemask(vmask) ^ 0xf;
-#else
- const ssef tnear = max4(isect_near, tnear_x, tnear_y, tnear_z);
- const ssef tfar = min4(isect_far, tfar_x, tfar_y, tfar_z);
- const sseb vmask = tnear <= tfar;
- int mask = (int)movemask(vmask);
-#endif
- *dist = tnear;
- return mask;
-}
-
-/* Unaligned nodes intersection */
-
-ccl_device_inline int qbvh_unaligned_node_intersect(KernelGlobals *ccl_restrict kg,
- const ssef &isect_near,
- const ssef &isect_far,
-#ifdef __KERNEL_AVX2__
- const sse3f &org_idir,
-#endif
- const sse3f &org,
- const sse3f &dir,
- const sse3f &idir,
- const int near_x,
- const int near_y,
- const int near_z,
- const int far_x,
- const int far_y,
- const int far_z,
- const int node_addr,
- ssef *ccl_restrict dist)
-{
- const int offset = node_addr;
- const ssef tfm_x_x = kernel_tex_fetch_ssef(__bvh_nodes, offset + 1);
- const ssef tfm_x_y = kernel_tex_fetch_ssef(__bvh_nodes, offset + 2);
- const ssef tfm_x_z = kernel_tex_fetch_ssef(__bvh_nodes, offset + 3);
-
- const ssef tfm_y_x = kernel_tex_fetch_ssef(__bvh_nodes, offset + 4);
- const ssef tfm_y_y = kernel_tex_fetch_ssef(__bvh_nodes, offset + 5);
- const ssef tfm_y_z = kernel_tex_fetch_ssef(__bvh_nodes, offset + 6);
-
- const ssef tfm_z_x = kernel_tex_fetch_ssef(__bvh_nodes, offset + 7);
- const ssef tfm_z_y = kernel_tex_fetch_ssef(__bvh_nodes, offset + 8);
- const ssef tfm_z_z = kernel_tex_fetch_ssef(__bvh_nodes, offset + 9);
-
- const ssef tfm_t_x = kernel_tex_fetch_ssef(__bvh_nodes, offset + 10);
- const ssef tfm_t_y = kernel_tex_fetch_ssef(__bvh_nodes, offset + 11);
- const ssef tfm_t_z = kernel_tex_fetch_ssef(__bvh_nodes, offset + 12);
-
- const ssef aligned_dir_x = dir.x * tfm_x_x + dir.y * tfm_x_y + dir.z * tfm_x_z,
- aligned_dir_y = dir.x * tfm_y_x + dir.y * tfm_y_y + dir.z * tfm_y_z,
- aligned_dir_z = dir.x * tfm_z_x + dir.y * tfm_z_y + dir.z * tfm_z_z;
-
- const ssef aligned_P_x = org.x * tfm_x_x + org.y * tfm_x_y + org.z * tfm_x_z + tfm_t_x,
- aligned_P_y = org.x * tfm_y_x + org.y * tfm_y_y + org.z * tfm_y_z + tfm_t_y,
- aligned_P_z = org.x * tfm_z_x + org.y * tfm_z_y + org.z * tfm_z_z + tfm_t_z;
-
- const ssef neg_one(-1.0f, -1.0f, -1.0f, -1.0f);
- const ssef nrdir_x = neg_one / aligned_dir_x, nrdir_y = neg_one / aligned_dir_y,
- nrdir_z = neg_one / aligned_dir_z;
-
- const ssef tlower_x = aligned_P_x * nrdir_x, tlower_y = aligned_P_y * nrdir_y,
- tlower_z = aligned_P_z * nrdir_z;
-
- const ssef tupper_x = tlower_x - nrdir_x, tupper_y = tlower_y - nrdir_y,
- tupper_z = tlower_z - nrdir_z;
-
-#ifdef __KERNEL_SSE41__
- const ssef tnear_x = mini(tlower_x, tupper_x);
- const ssef tnear_y = mini(tlower_y, tupper_y);
- const ssef tnear_z = mini(tlower_z, tupper_z);
- const ssef tfar_x = maxi(tlower_x, tupper_x);
- const ssef tfar_y = maxi(tlower_y, tupper_y);
- const ssef tfar_z = maxi(tlower_z, tupper_z);
- const ssef tnear = max4(isect_near, tnear_x, tnear_y, tnear_z);
- const ssef tfar = min4(isect_far, tfar_x, tfar_y, tfar_z);
- const sseb vmask = tnear <= tfar;
- *dist = tnear;
- return movemask(vmask);
-#else
- const ssef tnear_x = min(tlower_x, tupper_x);
- const ssef tnear_y = min(tlower_y, tupper_y);
- const ssef tnear_z = min(tlower_z, tupper_z);
- const ssef tfar_x = max(tlower_x, tupper_x);
- const ssef tfar_y = max(tlower_y, tupper_y);
- const ssef tfar_z = max(tlower_z, tupper_z);
- const ssef tnear = max4(isect_near, tnear_x, tnear_y, tnear_z);
- const ssef tfar = min4(isect_far, tfar_x, tfar_y, tfar_z);
- const sseb vmask = tnear <= tfar;
- *dist = tnear;
- return movemask(vmask);
-#endif
-}
-
-/* Intersectors wrappers.
- *
- * They'll check node type and call appropriate intersection code.
- */
-
-ccl_device_inline int qbvh_node_intersect(KernelGlobals *ccl_restrict kg,
- const ssef &isect_near,
- const ssef &isect_far,
-#ifdef __KERNEL_AVX2__
- const sse3f &org_idir,
-#endif
- const sse3f &org,
- const sse3f &dir,
- const sse3f &idir,
- const int near_x,
- const int near_y,
- const int near_z,
- const int far_x,
- const int far_y,
- const int far_z,
- const int node_addr,
- ssef *ccl_restrict dist)
-{
- const int offset = node_addr;
- const float4 node = kernel_tex_fetch(__bvh_nodes, offset);
- if (__float_as_uint(node.x) & PATH_RAY_NODE_UNALIGNED) {
- return qbvh_unaligned_node_intersect(kg,
- isect_near,
- isect_far,
-#ifdef __KERNEL_AVX2__
- org_idir,
-#endif
- org,
- dir,
- idir,
- near_x,
- near_y,
- near_z,
- far_x,
- far_y,
- far_z,
- node_addr,
- dist);
- }
- else {
- return qbvh_aligned_node_intersect(kg,
- isect_near,
- isect_far,
-#ifdef __KERNEL_AVX2__
- org_idir,
-#else
- org,
-#endif
- idir,
- near_x,
- near_y,
- near_z,
- far_x,
- far_y,
- far_z,
- node_addr,
- dist);
- }
-}
diff --git a/intern/cycles/kernel/bvh/qbvh_shadow_all.h b/intern/cycles/kernel/bvh/qbvh_shadow_all.h
deleted file mode 100644
index 682251bf25b..00000000000
--- a/intern/cycles/kernel/bvh/qbvh_shadow_all.h
+++ /dev/null
@@ -1,453 +0,0 @@
-/*
- * Copyright 2011-2013 Blender Foundation
- *
- * 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.
- */
-
-/* This is a template BVH traversal function, where various features can be
- * enabled/disabled. This way we can compile optimized versions for each case
- * without new features slowing things down.
- *
- * BVH_INSTANCING: object instancing
- * BVH_HAIR: hair curve rendering
- * BVH_MOTION: motion blur rendering
- */
-
-#if BVH_FEATURE(BVH_HAIR)
-# define NODE_INTERSECT qbvh_node_intersect
-#else
-# define NODE_INTERSECT qbvh_aligned_node_intersect
-#endif
-
-ccl_device bool BVH_FUNCTION_FULL_NAME(QBVH)(KernelGlobals *kg,
- const Ray *ray,
- Intersection *isect_array,
- const uint visibility,
- const uint max_hits,
- uint *num_hits)
-{
- /* TODO(sergey):
- * - Test if pushing distance on the stack helps.
- * - Likely and unlikely for if() statements.
- * - Test restrict attribute for pointers.
- */
-
- /* Traversal stack in CUDA thread-local memory. */
- QBVHStackItem traversal_stack[BVH_QSTACK_SIZE];
- traversal_stack[0].addr = ENTRYPOINT_SENTINEL;
-
- /* Traversal variables in registers. */
- int stack_ptr = 0;
- int node_addr = kernel_data.bvh.root;
-
- /* Ray parameters in registers. */
- const float tmax = ray->t;
- float3 P = ray->P;
- float3 dir = bvh_clamp_direction(ray->D);
- float3 idir = bvh_inverse_direction(dir);
- int object = OBJECT_NONE;
- float isect_t = tmax;
-
-#if BVH_FEATURE(BVH_MOTION)
- Transform ob_itfm;
-#endif
-
- *num_hits = 0;
- isect_array->t = tmax;
-
-#if BVH_FEATURE(BVH_INSTANCING)
- int num_hits_in_instance = 0;
-#endif
-
- ssef tnear(0.0f), tfar(isect_t);
-#if BVH_FEATURE(BVH_HAIR)
- sse3f dir4(ssef(dir.x), ssef(dir.y), ssef(dir.z));
-#endif
- sse3f idir4(ssef(idir.x), ssef(idir.y), ssef(idir.z));
-
-#ifdef __KERNEL_AVX2__
- float3 P_idir = P * idir;
- sse3f P_idir4(P_idir.x, P_idir.y, P_idir.z);
-#endif
-#if BVH_FEATURE(BVH_HAIR) || !defined(__KERNEL_AVX2__)
- sse3f org4(ssef(P.x), ssef(P.y), ssef(P.z));
-#endif
-
- /* Offsets to select the side that becomes the lower or upper bound. */
- int near_x, near_y, near_z;
- int far_x, far_y, far_z;
- qbvh_near_far_idx_calc(idir, &near_x, &near_y, &near_z, &far_x, &far_y, &far_z);
-
- /* Traversal loop. */
- do {
- do {
- /* Traverse internal nodes. */
- while (node_addr >= 0 && node_addr != ENTRYPOINT_SENTINEL) {
- float4 inodes = kernel_tex_fetch(__bvh_nodes, node_addr + 0);
- (void)inodes;
-
- if (false
-#ifdef __VISIBILITY_FLAG__
- || ((__float_as_uint(inodes.x) & visibility) == 0)
-#endif
-#if BVH_FEATURE(BVH_MOTION)
- || UNLIKELY(ray->time < inodes.y) || UNLIKELY(ray->time > inodes.z)
-#endif
- ) {
- /* Pop. */
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- continue;
- }
-
- ssef dist;
- int child_mask = NODE_INTERSECT(kg,
- tnear,
- tfar,
-#ifdef __KERNEL_AVX2__
- P_idir4,
-#endif
-#if BVH_FEATURE(BVH_HAIR) || !defined(__KERNEL_AVX2__)
- org4,
-#endif
-#if BVH_FEATURE(BVH_HAIR)
- dir4,
-#endif
- idir4,
- near_x,
- near_y,
- near_z,
- far_x,
- far_y,
- far_z,
- node_addr,
- &dist);
-
- if (child_mask != 0) {
- float4 cnodes;
-#if BVH_FEATURE(BVH_HAIR)
- if (__float_as_uint(inodes.x) & PATH_RAY_NODE_UNALIGNED) {
- cnodes = kernel_tex_fetch(__bvh_nodes, node_addr + 13);
- }
- else
-#endif
- {
- cnodes = kernel_tex_fetch(__bvh_nodes, node_addr + 7);
- }
-
- /* One child is hit, continue with that child. */
- int r = __bscf(child_mask);
- if (child_mask == 0) {
- node_addr = __float_as_int(cnodes[r]);
- continue;
- }
-
- /* Two children are hit, push far child, and continue with
- * closer child.
- */
- int c0 = __float_as_int(cnodes[r]);
- float d0 = ((float *)&dist)[r];
- r = __bscf(child_mask);
- int c1 = __float_as_int(cnodes[r]);
- float d1 = ((float *)&dist)[r];
- if (child_mask == 0) {
- if (d1 < d0) {
- node_addr = c1;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_QSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c0;
- traversal_stack[stack_ptr].dist = d0;
- continue;
- }
- else {
- node_addr = c0;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_QSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c1;
- traversal_stack[stack_ptr].dist = d1;
- continue;
- }
- }
-
- /* Here starts the slow path for 3 or 4 hit children. We push
- * all nodes onto the stack to sort them there.
- */
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_QSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c1;
- traversal_stack[stack_ptr].dist = d1;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_QSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c0;
- traversal_stack[stack_ptr].dist = d0;
-
- /* Three children are hit, push all onto stack and sort 3
- * stack items, continue with closest child.
- */
- r = __bscf(child_mask);
- int c2 = __float_as_int(cnodes[r]);
- float d2 = ((float *)&dist)[r];
- if (child_mask == 0) {
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_QSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c2;
- traversal_stack[stack_ptr].dist = d2;
- qbvh_stack_sort(&traversal_stack[stack_ptr],
- &traversal_stack[stack_ptr - 1],
- &traversal_stack[stack_ptr - 2]);
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- continue;
- }
-
- /* Four children are hit, push all onto stack and sort 4
- * stack items, continue with closest child.
- */
- r = __bscf(child_mask);
- int c3 = __float_as_int(cnodes[r]);
- float d3 = ((float *)&dist)[r];
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_QSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c3;
- traversal_stack[stack_ptr].dist = d3;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_QSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c2;
- traversal_stack[stack_ptr].dist = d2;
- qbvh_stack_sort(&traversal_stack[stack_ptr],
- &traversal_stack[stack_ptr - 1],
- &traversal_stack[stack_ptr - 2],
- &traversal_stack[stack_ptr - 3]);
- }
-
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- }
-
- /* If node is leaf, fetch triangle list. */
- if (node_addr < 0) {
- float4 leaf = kernel_tex_fetch(__bvh_leaf_nodes, (-node_addr - 1));
-#ifdef __VISIBILITY_FLAG__
- if ((__float_as_uint(leaf.z) & visibility) == 0) {
- /* Pop. */
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- continue;
- }
-#endif
-
- int prim_addr = __float_as_int(leaf.x);
-
-#if BVH_FEATURE(BVH_INSTANCING)
- if (prim_addr >= 0) {
-#endif
- int prim_addr2 = __float_as_int(leaf.y);
- const uint type = __float_as_int(leaf.w);
- const uint p_type = type & PRIMITIVE_ALL;
-
- /* Pop. */
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
-
- /* Primitive intersection. */
- while (prim_addr < prim_addr2) {
- kernel_assert((kernel_tex_fetch(__prim_type, prim_addr) & PRIMITIVE_ALL) == p_type);
- bool hit;
-
- /* todo: specialized intersect functions which don't fill in
- * isect unless needed and check SD_HAS_TRANSPARENT_SHADOW?
- * might give a few % performance improvement */
-
- switch (p_type) {
- case PRIMITIVE_TRIANGLE: {
- hit = triangle_intersect(kg, isect_array, P, dir, visibility, object, prim_addr);
- break;
- }
-#if BVH_FEATURE(BVH_MOTION)
- case PRIMITIVE_MOTION_TRIANGLE: {
- hit = motion_triangle_intersect(
- kg, isect_array, P, dir, ray->time, visibility, object, prim_addr);
- break;
- }
-#endif
-#if BVH_FEATURE(BVH_HAIR)
- case PRIMITIVE_CURVE:
- case PRIMITIVE_MOTION_CURVE: {
- const uint curve_type = kernel_tex_fetch(__prim_type, prim_addr);
- if (kernel_data.curve.curveflags & CURVE_KN_INTERPOLATE) {
- hit = cardinal_curve_intersect(kg,
- isect_array,
- P,
- dir,
- visibility,
- object,
- prim_addr,
- ray->time,
- curve_type);
- }
- else {
- hit = curve_intersect(kg,
- isect_array,
- P,
- dir,
- visibility,
- object,
- prim_addr,
- ray->time,
- curve_type);
- }
- break;
- }
-#endif
- default: {
- hit = false;
- break;
- }
- }
-
- /* Shadow ray early termination. */
- if (hit) {
- /* detect if this surface has a shader with transparent shadows */
-
- /* todo: optimize so primitive visibility flag indicates if
- * the primitive has a transparent shadow shader? */
- int prim = kernel_tex_fetch(__prim_index, isect_array->prim);
- int shader = 0;
-
-#ifdef __HAIR__
- if (kernel_tex_fetch(__prim_type, isect_array->prim) & PRIMITIVE_ALL_TRIANGLE)
-#endif
- {
- shader = kernel_tex_fetch(__tri_shader, prim);
- }
-#ifdef __HAIR__
- else {
- float4 str = kernel_tex_fetch(__curves, prim);
- shader = __float_as_int(str.z);
- }
-#endif
- int flag = kernel_tex_fetch(__shaders, (shader & SHADER_MASK)).flags;
-
- /* if no transparent shadows, all light is blocked */
- if (!(flag & SD_HAS_TRANSPARENT_SHADOW)) {
- return true;
- }
- /* if maximum number of hits reached, block all light */
- else if (*num_hits == max_hits) {
- return true;
- }
-
- /* move on to next entry in intersections array */
- isect_array++;
- (*num_hits)++;
-#if BVH_FEATURE(BVH_INSTANCING)
- num_hits_in_instance++;
-#endif
-
- isect_array->t = isect_t;
- }
-
- prim_addr++;
- }
- }
-#if BVH_FEATURE(BVH_INSTANCING)
- else {
- /* Instance push. */
- object = kernel_tex_fetch(__prim_object, -prim_addr - 1);
-
-# if BVH_FEATURE(BVH_MOTION)
- isect_t = bvh_instance_motion_push(kg, object, ray, &P, &dir, &idir, isect_t, &ob_itfm);
-# else
- isect_t = bvh_instance_push(kg, object, ray, &P, &dir, &idir, isect_t);
-# endif
-
- num_hits_in_instance = 0;
- isect_array->t = isect_t;
-
- qbvh_near_far_idx_calc(idir, &near_x, &near_y, &near_z, &far_x, &far_y, &far_z);
- tfar = ssef(isect_t);
-# if BVH_FEATURE(BVH_HAIR)
- dir4 = sse3f(ssef(dir.x), ssef(dir.y), ssef(dir.z));
-# endif
- idir4 = sse3f(ssef(idir.x), ssef(idir.y), ssef(idir.z));
-# ifdef __KERNEL_AVX2__
- P_idir = P * idir;
- P_idir4 = sse3f(P_idir.x, P_idir.y, P_idir.z);
-# endif
-# if BVH_FEATURE(BVH_HAIR) || !defined(__KERNEL_AVX2__)
- org4 = sse3f(ssef(P.x), ssef(P.y), ssef(P.z));
-# endif
-
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_QSTACK_SIZE);
- traversal_stack[stack_ptr].addr = ENTRYPOINT_SENTINEL;
-
- node_addr = kernel_tex_fetch(__object_node, object);
- }
- }
-#endif /* FEATURE(BVH_INSTANCING) */
- } while (node_addr != ENTRYPOINT_SENTINEL);
-
-#if BVH_FEATURE(BVH_INSTANCING)
- if (stack_ptr >= 0) {
- kernel_assert(object != OBJECT_NONE);
-
- /* Instance pop. */
- if (num_hits_in_instance) {
- float t_fac;
-# if BVH_FEATURE(BVH_MOTION)
- bvh_instance_motion_pop_factor(kg, object, ray, &P, &dir, &idir, &t_fac, &ob_itfm);
-# else
- bvh_instance_pop_factor(kg, object, ray, &P, &dir, &idir, &t_fac);
-# endif
- /* Scale isect->t to adjust for instancing. */
- for (int i = 0; i < num_hits_in_instance; i++) {
- (isect_array - i - 1)->t *= t_fac;
- }
- }
- else {
-# if BVH_FEATURE(BVH_MOTION)
- bvh_instance_motion_pop(kg, object, ray, &P, &dir, &idir, FLT_MAX, &ob_itfm);
-# else
- bvh_instance_pop(kg, object, ray, &P, &dir, &idir, FLT_MAX);
-# endif
- }
-
- isect_t = tmax;
- isect_array->t = isect_t;
-
- qbvh_near_far_idx_calc(idir, &near_x, &near_y, &near_z, &far_x, &far_y, &far_z);
- tfar = ssef(isect_t);
-# if BVH_FEATURE(BVH_HAIR)
- dir4 = sse3f(ssef(dir.x), ssef(dir.y), ssef(dir.z));
-# endif
- idir4 = sse3f(ssef(idir.x), ssef(idir.y), ssef(idir.z));
-# ifdef __KERNEL_AVX2__
- P_idir = P * idir;
- P_idir4 = sse3f(P_idir.x, P_idir.y, P_idir.z);
-# endif
-# if BVH_FEATURE(BVH_HAIR) || !defined(__KERNEL_AVX2__)
- org4 = sse3f(ssef(P.x), ssef(P.y), ssef(P.z));
-# endif
-
- object = OBJECT_NONE;
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- }
-#endif /* FEATURE(BVH_INSTANCING) */
- } while (node_addr != ENTRYPOINT_SENTINEL);
-
- return false;
-}
-
-#undef NODE_INTERSECT
diff --git a/intern/cycles/kernel/bvh/qbvh_traversal.h b/intern/cycles/kernel/bvh/qbvh_traversal.h
deleted file mode 100644
index f43e84bf368..00000000000
--- a/intern/cycles/kernel/bvh/qbvh_traversal.h
+++ /dev/null
@@ -1,420 +0,0 @@
-/*
- * Copyright 2011-2013 Blender Foundation
- *
- * 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.
- */
-
-/* This is a template BVH traversal function, where various features can be
- * enabled/disabled. This way we can compile optimized versions for each case
- * without new features slowing things down.
- *
- * BVH_INSTANCING: object instancing
- * BVH_HAIR: hair curve rendering
- * BVH_MOTION: motion blur rendering
- */
-
-#if BVH_FEATURE(BVH_HAIR)
-# define NODE_INTERSECT qbvh_node_intersect
-#else
-# define NODE_INTERSECT qbvh_aligned_node_intersect
-#endif
-
-ccl_device bool BVH_FUNCTION_FULL_NAME(QBVH)(KernelGlobals *kg,
- const Ray *ray,
- Intersection *isect,
- const uint visibility)
-{
- /* TODO(sergey):
- * - Test if pushing distance on the stack helps (for non shadow rays).
- * - Separate version for shadow rays.
- * - Likely and unlikely for if() statements.
- * - Test restrict attribute for pointers.
- */
-
- /* Traversal stack in CUDA thread-local memory. */
- QBVHStackItem traversal_stack[BVH_QSTACK_SIZE];
- traversal_stack[0].addr = ENTRYPOINT_SENTINEL;
- traversal_stack[0].dist = -FLT_MAX;
-
- /* Traversal variables in registers. */
- int stack_ptr = 0;
- int node_addr = kernel_data.bvh.root;
- float node_dist = -FLT_MAX;
-
- /* Ray parameters in registers. */
- float3 P = ray->P;
- float3 dir = bvh_clamp_direction(ray->D);
- float3 idir = bvh_inverse_direction(dir);
- int object = OBJECT_NONE;
-
-#if BVH_FEATURE(BVH_MOTION)
- Transform ob_itfm;
-#endif
-
- isect->t = ray->t;
- isect->u = 0.0f;
- isect->v = 0.0f;
- isect->prim = PRIM_NONE;
- isect->object = OBJECT_NONE;
-
- BVH_DEBUG_INIT();
-
- ssef tnear(0.0f), tfar(ray->t);
-#if BVH_FEATURE(BVH_HAIR)
- sse3f dir4(ssef(dir.x), ssef(dir.y), ssef(dir.z));
-#endif
- sse3f idir4(ssef(idir.x), ssef(idir.y), ssef(idir.z));
-
-#ifdef __KERNEL_AVX2__
- float3 P_idir = P * idir;
- sse3f P_idir4 = sse3f(P_idir.x, P_idir.y, P_idir.z);
-#endif
-#if BVH_FEATURE(BVH_HAIR) || !defined(__KERNEL_AVX2__)
- sse3f org4 = sse3f(ssef(P.x), ssef(P.y), ssef(P.z));
-#endif
-
- /* Offsets to select the side that becomes the lower or upper bound. */
- int near_x, near_y, near_z;
- int far_x, far_y, far_z;
- qbvh_near_far_idx_calc(idir, &near_x, &near_y, &near_z, &far_x, &far_y, &far_z);
-
- /* Traversal loop. */
- do {
- do {
- /* Traverse internal nodes. */
- while (node_addr >= 0 && node_addr != ENTRYPOINT_SENTINEL) {
- float4 inodes = kernel_tex_fetch(__bvh_nodes, node_addr + 0);
- (void)inodes;
-
- if (UNLIKELY(node_dist > isect->t)
-#if BVH_FEATURE(BVH_MOTION)
- || UNLIKELY(ray->time < inodes.y) || UNLIKELY(ray->time > inodes.z)
-#endif
-#ifdef __VISIBILITY_FLAG__
- || (__float_as_uint(inodes.x) & visibility) == 0
-#endif
- ) {
- /* Pop. */
- node_addr = traversal_stack[stack_ptr].addr;
- node_dist = traversal_stack[stack_ptr].dist;
- --stack_ptr;
- continue;
- }
-
- int child_mask;
- ssef dist;
-
- BVH_DEBUG_NEXT_NODE();
-
- {
- child_mask = NODE_INTERSECT(kg,
- tnear,
- tfar,
-#ifdef __KERNEL_AVX2__
- P_idir4,
-#endif
-#if BVH_FEATURE(BVH_HAIR) || !defined(__KERNEL_AVX2__)
- org4,
-#endif
-#if BVH_FEATURE(BVH_HAIR)
- dir4,
-#endif
- idir4,
- near_x,
- near_y,
- near_z,
- far_x,
- far_y,
- far_z,
- node_addr,
- &dist);
- }
-
- if (child_mask != 0) {
- float4 cnodes;
- /* TODO(sergey): Investigate whether moving cnodes upwards
- * gives a speedup (will be different cache pattern but will
- * avoid extra check here).
- */
-#if BVH_FEATURE(BVH_HAIR)
- if (__float_as_uint(inodes.x) & PATH_RAY_NODE_UNALIGNED) {
- cnodes = kernel_tex_fetch(__bvh_nodes, node_addr + 13);
- }
- else
-#endif
- {
- cnodes = kernel_tex_fetch(__bvh_nodes, node_addr + 7);
- }
-
- /* One child is hit, continue with that child. */
- int r = __bscf(child_mask);
- float d0 = ((float *)&dist)[r];
- if (child_mask == 0) {
- node_addr = __float_as_int(cnodes[r]);
- node_dist = d0;
- continue;
- }
-
- /* Two children are hit, push far child, and continue with
- * closer child.
- */
- int c0 = __float_as_int(cnodes[r]);
- r = __bscf(child_mask);
- int c1 = __float_as_int(cnodes[r]);
- float d1 = ((float *)&dist)[r];
- if (child_mask == 0) {
- if (d1 < d0) {
- node_addr = c1;
- node_dist = d1;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_QSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c0;
- traversal_stack[stack_ptr].dist = d0;
- continue;
- }
- else {
- node_addr = c0;
- node_dist = d0;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_QSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c1;
- traversal_stack[stack_ptr].dist = d1;
- continue;
- }
- }
-
- /* Here starts the slow path for 3 or 4 hit children. We push
- * all nodes onto the stack to sort them there.
- */
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_QSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c1;
- traversal_stack[stack_ptr].dist = d1;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_QSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c0;
- traversal_stack[stack_ptr].dist = d0;
-
- /* Three children are hit, push all onto stack and sort 3
- * stack items, continue with closest child.
- */
- r = __bscf(child_mask);
- int c2 = __float_as_int(cnodes[r]);
- float d2 = ((float *)&dist)[r];
- if (child_mask == 0) {
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_QSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c2;
- traversal_stack[stack_ptr].dist = d2;
- qbvh_stack_sort(&traversal_stack[stack_ptr],
- &traversal_stack[stack_ptr - 1],
- &traversal_stack[stack_ptr - 2]);
- node_addr = traversal_stack[stack_ptr].addr;
- node_dist = traversal_stack[stack_ptr].dist;
- --stack_ptr;
- continue;
- }
-
- /* Four children are hit, push all onto stack and sort 4
- * stack items, continue with closest child.
- */
- r = __bscf(child_mask);
- int c3 = __float_as_int(cnodes[r]);
- float d3 = ((float *)&dist)[r];
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_QSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c3;
- traversal_stack[stack_ptr].dist = d3;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_QSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c2;
- traversal_stack[stack_ptr].dist = d2;
- qbvh_stack_sort(&traversal_stack[stack_ptr],
- &traversal_stack[stack_ptr - 1],
- &traversal_stack[stack_ptr - 2],
- &traversal_stack[stack_ptr - 3]);
- }
-
- node_addr = traversal_stack[stack_ptr].addr;
- node_dist = traversal_stack[stack_ptr].dist;
- --stack_ptr;
- }
-
- /* If node is leaf, fetch triangle list. */
- if (node_addr < 0) {
- float4 leaf = kernel_tex_fetch(__bvh_leaf_nodes, (-node_addr - 1));
-
-#ifdef __VISIBILITY_FLAG__
- if (UNLIKELY((node_dist > isect->t) || ((__float_as_uint(leaf.z) & visibility) == 0)))
-#else
- if (UNLIKELY((node_dist > isect->t)))
-#endif
- {
- /* Pop. */
- node_addr = traversal_stack[stack_ptr].addr;
- node_dist = traversal_stack[stack_ptr].dist;
- --stack_ptr;
- continue;
- }
-
- int prim_addr = __float_as_int(leaf.x);
-
-#if BVH_FEATURE(BVH_INSTANCING)
- if (prim_addr >= 0) {
-#endif
- int prim_addr2 = __float_as_int(leaf.y);
- const uint type = __float_as_int(leaf.w);
-
- /* Pop. */
- node_addr = traversal_stack[stack_ptr].addr;
- node_dist = traversal_stack[stack_ptr].dist;
- --stack_ptr;
-
- /* Primitive intersection. */
- switch (type & PRIMITIVE_ALL) {
- case PRIMITIVE_TRIANGLE: {
- for (; prim_addr < prim_addr2; prim_addr++) {
- BVH_DEBUG_NEXT_INTERSECTION();
- kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type);
- if (triangle_intersect(kg, isect, P, dir, visibility, object, prim_addr)) {
- tfar = ssef(isect->t);
- /* Shadow ray early termination. */
- if (visibility & PATH_RAY_SHADOW_OPAQUE) {
- return true;
- }
- }
- }
- break;
- }
-#if BVH_FEATURE(BVH_MOTION)
- case PRIMITIVE_MOTION_TRIANGLE: {
- for (; prim_addr < prim_addr2; prim_addr++) {
- BVH_DEBUG_NEXT_INTERSECTION();
- kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type);
- if (motion_triangle_intersect(
- kg, isect, P, dir, ray->time, visibility, object, prim_addr)) {
- tfar = ssef(isect->t);
- /* Shadow ray early termination. */
- if (visibility & PATH_RAY_SHADOW_OPAQUE) {
- return true;
- }
- }
- }
- break;
- }
-#endif /* BVH_FEATURE(BVH_MOTION) */
-#if BVH_FEATURE(BVH_HAIR)
- case PRIMITIVE_CURVE:
- case PRIMITIVE_MOTION_CURVE: {
- for (; prim_addr < prim_addr2; prim_addr++) {
- BVH_DEBUG_NEXT_INTERSECTION();
- const uint curve_type = kernel_tex_fetch(__prim_type, prim_addr);
- kernel_assert((curve_type & PRIMITIVE_ALL) == (type & PRIMITIVE_ALL));
- bool hit;
- if (kernel_data.curve.curveflags & CURVE_KN_INTERPOLATE) {
- hit = cardinal_curve_intersect(
- kg, isect, P, dir, visibility, object, prim_addr, ray->time, curve_type);
- }
- else {
- hit = curve_intersect(
- kg, isect, P, dir, visibility, object, prim_addr, ray->time, curve_type);
- }
- if (hit) {
- tfar = ssef(isect->t);
- /* Shadow ray early termination. */
- if (visibility & PATH_RAY_SHADOW_OPAQUE) {
- return true;
- }
- }
- }
- break;
- }
-#endif /* BVH_FEATURE(BVH_HAIR) */
- }
- }
-#if BVH_FEATURE(BVH_INSTANCING)
- else {
- /* Instance push. */
- object = kernel_tex_fetch(__prim_object, -prim_addr - 1);
-
-# if BVH_FEATURE(BVH_MOTION)
- qbvh_instance_motion_push(
- kg, object, ray, &P, &dir, &idir, &isect->t, &node_dist, &ob_itfm);
-# else
- qbvh_instance_push(kg, object, ray, &P, &dir, &idir, &isect->t, &node_dist);
-# endif
-
- qbvh_near_far_idx_calc(idir, &near_x, &near_y, &near_z, &far_x, &far_y, &far_z);
- tfar = ssef(isect->t);
-# if BVH_FEATURE(BVH_HAIR)
- dir4 = sse3f(ssef(dir.x), ssef(dir.y), ssef(dir.z));
-# endif
- idir4 = sse3f(ssef(idir.x), ssef(idir.y), ssef(idir.z));
-# ifdef __KERNEL_AVX2__
- P_idir = P * idir;
- P_idir4 = sse3f(P_idir.x, P_idir.y, P_idir.z);
-# endif
-# if BVH_FEATURE(BVH_HAIR) || !defined(__KERNEL_AVX2__)
- org4 = sse3f(ssef(P.x), ssef(P.y), ssef(P.z));
-# endif
-
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_QSTACK_SIZE);
- traversal_stack[stack_ptr].addr = ENTRYPOINT_SENTINEL;
- traversal_stack[stack_ptr].dist = -FLT_MAX;
-
- node_addr = kernel_tex_fetch(__object_node, object);
-
- BVH_DEBUG_NEXT_INSTANCE();
- }
- }
-#endif /* FEATURE(BVH_INSTANCING) */
- } while (node_addr != ENTRYPOINT_SENTINEL);
-
-#if BVH_FEATURE(BVH_INSTANCING)
- if (stack_ptr >= 0) {
- kernel_assert(object != OBJECT_NONE);
-
- /* Instance pop. */
-# if BVH_FEATURE(BVH_MOTION)
- isect->t = bvh_instance_motion_pop(kg, object, ray, &P, &dir, &idir, isect->t, &ob_itfm);
-# else
- isect->t = bvh_instance_pop(kg, object, ray, &P, &dir, &idir, isect->t);
-# endif
-
- qbvh_near_far_idx_calc(idir, &near_x, &near_y, &near_z, &far_x, &far_y, &far_z);
- tfar = ssef(isect->t);
-# if BVH_FEATURE(BVH_HAIR)
- dir4 = sse3f(ssef(dir.x), ssef(dir.y), ssef(dir.z));
-# endif
- idir4 = sse3f(ssef(idir.x), ssef(idir.y), ssef(idir.z));
-# ifdef __KERNEL_AVX2__
- P_idir = P * idir;
- P_idir4 = sse3f(P_idir.x, P_idir.y, P_idir.z);
-# endif
-# if BVH_FEATURE(BVH_HAIR) || !defined(__KERNEL_AVX2__)
- org4 = sse3f(ssef(P.x), ssef(P.y), ssef(P.z));
-# endif
-
- object = OBJECT_NONE;
- node_addr = traversal_stack[stack_ptr].addr;
- node_dist = traversal_stack[stack_ptr].dist;
- --stack_ptr;
- }
-#endif /* FEATURE(BVH_INSTANCING) */
- } while (node_addr != ENTRYPOINT_SENTINEL);
-
- return (isect->prim != PRIM_NONE);
-}
-
-#undef NODE_INTERSECT
diff --git a/intern/cycles/kernel/bvh/qbvh_volume.h b/intern/cycles/kernel/bvh/qbvh_volume.h
deleted file mode 100644
index e4eaed04467..00000000000
--- a/intern/cycles/kernel/bvh/qbvh_volume.h
+++ /dev/null
@@ -1,367 +0,0 @@
-/*
- * Copyright 2011-2013 Blender Foundation
- *
- * 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.
- */
-
-/* This is a template BVH traversal function for volumes, where
- * various features can be enabled/disabled. This way we can compile optimized
- * versions for each case without new features slowing things down.
- *
- * BVH_INSTANCING: object instancing
- * BVH_MOTION: motion blur rendering
- */
-
-#if BVH_FEATURE(BVH_HAIR)
-# define NODE_INTERSECT qbvh_node_intersect
-#else
-# define NODE_INTERSECT qbvh_aligned_node_intersect
-#endif
-
-ccl_device bool BVH_FUNCTION_FULL_NAME(QBVH)(KernelGlobals *kg,
- const Ray *ray,
- Intersection *isect,
- const uint visibility)
-{
- /* TODO(sergey):
- * - Test if pushing distance on the stack helps.
- * - Likely and unlikely for if() statements.
- * - Test restrict attribute for pointers.
- */
-
- /* Traversal stack in CUDA thread-local memory. */
- QBVHStackItem traversal_stack[BVH_QSTACK_SIZE];
- traversal_stack[0].addr = ENTRYPOINT_SENTINEL;
-
- /* Traversal variables in registers. */
- int stack_ptr = 0;
- int node_addr = kernel_data.bvh.root;
-
- /* Ray parameters in registers. */
- float3 P = ray->P;
- float3 dir = bvh_clamp_direction(ray->D);
- float3 idir = bvh_inverse_direction(dir);
- int object = OBJECT_NONE;
-
-#if BVH_FEATURE(BVH_MOTION)
- Transform ob_itfm;
-#endif
-
- isect->t = ray->t;
- isect->u = 0.0f;
- isect->v = 0.0f;
- isect->prim = PRIM_NONE;
- isect->object = OBJECT_NONE;
-
- ssef tnear(0.0f), tfar(ray->t);
-#if BVH_FEATURE(BVH_HAIR)
- sse3f dir4(ssef(dir.x), ssef(dir.y), ssef(dir.z));
-#endif
- sse3f idir4(ssef(idir.x), ssef(idir.y), ssef(idir.z));
-
-#ifdef __KERNEL_AVX2__
- float3 P_idir = P * idir;
- sse3f P_idir4(P_idir.x, P_idir.y, P_idir.z);
-#endif
-#if BVH_FEATURE(BVH_HAIR) || !defined(__KERNEL_AVX2__)
- sse3f org4(ssef(P.x), ssef(P.y), ssef(P.z));
-#endif
-
- /* Offsets to select the side that becomes the lower or upper bound. */
- int near_x, near_y, near_z;
- int far_x, far_y, far_z;
- qbvh_near_far_idx_calc(idir, &near_x, &near_y, &near_z, &far_x, &far_y, &far_z);
-
- /* Traversal loop. */
- do {
- do {
- /* Traverse internal nodes. */
- while (node_addr >= 0 && node_addr != ENTRYPOINT_SENTINEL) {
- float4 inodes = kernel_tex_fetch(__bvh_nodes, node_addr + 0);
-
-#ifdef __VISIBILITY_FLAG__
- if ((__float_as_uint(inodes.x) & visibility) == 0) {
- /* Pop. */
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- continue;
- }
-#endif
-
- ssef dist;
- int child_mask = NODE_INTERSECT(kg,
- tnear,
- tfar,
-#ifdef __KERNEL_AVX2__
- P_idir4,
-#endif
-#if BVH_FEATURE(BVH_HAIR) || !defined(__KERNEL_AVX2__)
- org4,
-#endif
-#if BVH_FEATURE(BVH_HAIR)
- dir4,
-#endif
- idir4,
- near_x,
- near_y,
- near_z,
- far_x,
- far_y,
- far_z,
- node_addr,
- &dist);
-
- if (child_mask != 0) {
- float4 cnodes;
-#if BVH_FEATURE(BVH_HAIR)
- if (__float_as_uint(inodes.x) & PATH_RAY_NODE_UNALIGNED) {
- cnodes = kernel_tex_fetch(__bvh_nodes, node_addr + 13);
- }
- else
-#endif
- {
- cnodes = kernel_tex_fetch(__bvh_nodes, node_addr + 7);
- }
-
- /* One child is hit, continue with that child. */
- int r = __bscf(child_mask);
- if (child_mask == 0) {
- node_addr = __float_as_int(cnodes[r]);
- continue;
- }
-
- /* Two children are hit, push far child, and continue with
- * closer child.
- */
- int c0 = __float_as_int(cnodes[r]);
- float d0 = ((float *)&dist)[r];
- r = __bscf(child_mask);
- int c1 = __float_as_int(cnodes[r]);
- float d1 = ((float *)&dist)[r];
- if (child_mask == 0) {
- if (d1 < d0) {
- node_addr = c1;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_QSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c0;
- traversal_stack[stack_ptr].dist = d0;
- continue;
- }
- else {
- node_addr = c0;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_QSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c1;
- traversal_stack[stack_ptr].dist = d1;
- continue;
- }
- }
-
- /* Here starts the slow path for 3 or 4 hit children. We push
- * all nodes onto the stack to sort them there.
- */
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_QSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c1;
- traversal_stack[stack_ptr].dist = d1;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_QSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c0;
- traversal_stack[stack_ptr].dist = d0;
-
- /* Three children are hit, push all onto stack and sort 3
- * stack items, continue with closest child.
- */
- r = __bscf(child_mask);
- int c2 = __float_as_int(cnodes[r]);
- float d2 = ((float *)&dist)[r];
- if (child_mask == 0) {
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_QSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c2;
- traversal_stack[stack_ptr].dist = d2;
- qbvh_stack_sort(&traversal_stack[stack_ptr],
- &traversal_stack[stack_ptr - 1],
- &traversal_stack[stack_ptr - 2]);
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- continue;
- }
-
- /* Four children are hit, push all onto stack and sort 4
- * stack items, continue with closest child.
- */
- r = __bscf(child_mask);
- int c3 = __float_as_int(cnodes[r]);
- float d3 = ((float *)&dist)[r];
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_QSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c3;
- traversal_stack[stack_ptr].dist = d3;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_QSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c2;
- traversal_stack[stack_ptr].dist = d2;
- qbvh_stack_sort(&traversal_stack[stack_ptr],
- &traversal_stack[stack_ptr - 1],
- &traversal_stack[stack_ptr - 2],
- &traversal_stack[stack_ptr - 3]);
- }
-
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- }
-
- /* If node is leaf, fetch triangle list. */
- if (node_addr < 0) {
- float4 leaf = kernel_tex_fetch(__bvh_leaf_nodes, (-node_addr - 1));
-
- if ((__float_as_uint(leaf.z) & visibility) == 0) {
- /* Pop. */
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- continue;
- }
-
- int prim_addr = __float_as_int(leaf.x);
-
-#if BVH_FEATURE(BVH_INSTANCING)
- if (prim_addr >= 0) {
-#endif
- int prim_addr2 = __float_as_int(leaf.y);
- const uint type = __float_as_int(leaf.w);
- const uint p_type = type & PRIMITIVE_ALL;
-
- /* Pop. */
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
-
- /* Primitive intersection. */
- switch (p_type) {
- case PRIMITIVE_TRIANGLE: {
- for (; prim_addr < prim_addr2; prim_addr++) {
- kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type);
- /* Only primitives from volume object. */
- uint tri_object = (object == OBJECT_NONE) ?
- kernel_tex_fetch(__prim_object, prim_addr) :
- object;
- int object_flag = kernel_tex_fetch(__object_flag, tri_object);
- if ((object_flag & SD_OBJECT_HAS_VOLUME) == 0) {
- continue;
- }
- /* Intersect ray against primitive. */
- triangle_intersect(kg, isect, P, dir, visibility, object, prim_addr);
- }
- break;
- }
-#if BVH_FEATURE(BVH_MOTION)
- case PRIMITIVE_MOTION_TRIANGLE: {
- for (; prim_addr < prim_addr2; prim_addr++) {
- kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type);
- /* Only primitives from volume object. */
- uint tri_object = (object == OBJECT_NONE) ?
- kernel_tex_fetch(__prim_object, prim_addr) :
- object;
- int object_flag = kernel_tex_fetch(__object_flag, tri_object);
- if ((object_flag & SD_OBJECT_HAS_VOLUME) == 0) {
- continue;
- }
- /* Intersect ray against primitive. */
- motion_triangle_intersect(
- kg, isect, P, dir, ray->time, visibility, object, prim_addr);
- }
- break;
- }
-#endif
- }
- }
-#if BVH_FEATURE(BVH_INSTANCING)
- else {
- /* Instance push. */
- object = kernel_tex_fetch(__prim_object, -prim_addr - 1);
- int object_flag = kernel_tex_fetch(__object_flag, object);
- if (object_flag & SD_OBJECT_HAS_VOLUME) {
-# if BVH_FEATURE(BVH_MOTION)
- isect->t = bvh_instance_motion_push(
- kg, object, ray, &P, &dir, &idir, isect->t, &ob_itfm);
-# else
- isect->t = bvh_instance_push(kg, object, ray, &P, &dir, &idir, isect->t);
-# endif
-
- qbvh_near_far_idx_calc(idir, &near_x, &near_y, &near_z, &far_x, &far_y, &far_z);
- tfar = ssef(isect->t);
-# if BVH_FEATURE(BVH_HAIR)
- dir4 = sse3f(ssef(dir.x), ssef(dir.y), ssef(dir.z));
-# endif
- idir4 = sse3f(ssef(idir.x), ssef(idir.y), ssef(idir.z));
-# ifdef __KERNEL_AVX2__
- P_idir = P * idir;
- P_idir4 = sse3f(P_idir.x, P_idir.y, P_idir.z);
-# endif
-# if BVH_FEATURE(BVH_HAIR) || !defined(__KERNEL_AVX2__)
- org4 = sse3f(ssef(P.x), ssef(P.y), ssef(P.z));
-# endif
-
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_QSTACK_SIZE);
- traversal_stack[stack_ptr].addr = ENTRYPOINT_SENTINEL;
-
- node_addr = kernel_tex_fetch(__object_node, object);
- }
- else {
- /* Pop. */
- object = OBJECT_NONE;
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- }
- }
- }
-#endif /* FEATURE(BVH_INSTANCING) */
- } while (node_addr != ENTRYPOINT_SENTINEL);
-
-#if BVH_FEATURE(BVH_INSTANCING)
- if (stack_ptr >= 0) {
- kernel_assert(object != OBJECT_NONE);
-
- /* Instance pop. */
-# if BVH_FEATURE(BVH_MOTION)
- isect->t = bvh_instance_motion_pop(kg, object, ray, &P, &dir, &idir, isect->t, &ob_itfm);
-# else
- isect->t = bvh_instance_pop(kg, object, ray, &P, &dir, &idir, isect->t);
-# endif
-
- qbvh_near_far_idx_calc(idir, &near_x, &near_y, &near_z, &far_x, &far_y, &far_z);
- tfar = ssef(isect->t);
-# if BVH_FEATURE(BVH_HAIR)
- dir4 = sse3f(ssef(dir.x), ssef(dir.y), ssef(dir.z));
-# endif
- idir4 = sse3f(ssef(idir.x), ssef(idir.y), ssef(idir.z));
-# ifdef __KERNEL_AVX2__
- P_idir = P * idir;
- P_idir4 = sse3f(P_idir.x, P_idir.y, P_idir.z);
-# endif
-# if BVH_FEATURE(BVH_HAIR) || !defined(__KERNEL_AVX2__)
- org4 = sse3f(ssef(P.x), ssef(P.y), ssef(P.z));
-# endif
-
- object = OBJECT_NONE;
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- }
-#endif /* FEATURE(BVH_INSTANCING) */
- } while (node_addr != ENTRYPOINT_SENTINEL);
-
- return (isect->prim != PRIM_NONE);
-}
-
-#undef NODE_INTERSECT
diff --git a/intern/cycles/kernel/bvh/qbvh_volume_all.h b/intern/cycles/kernel/bvh/qbvh_volume_all.h
deleted file mode 100644
index eddc48c487e..00000000000
--- a/intern/cycles/kernel/bvh/qbvh_volume_all.h
+++ /dev/null
@@ -1,444 +0,0 @@
-/*
- * Copyright 2011-2013 Blender Foundation
- *
- * 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.
- */
-
-/* This is a template BVH traversal function for volumes, where
- * various features can be enabled/disabled. This way we can compile optimized
- * versions for each case without new features slowing things down.
- *
- * BVH_INSTANCING: object instancing
- * BVH_MOTION: motion blur rendering
- */
-
-#if BVH_FEATURE(BVH_HAIR)
-# define NODE_INTERSECT qbvh_node_intersect
-#else
-# define NODE_INTERSECT qbvh_aligned_node_intersect
-#endif
-
-ccl_device uint BVH_FUNCTION_FULL_NAME(QBVH)(KernelGlobals *kg,
- const Ray *ray,
- Intersection *isect_array,
- const uint max_hits,
- const uint visibility)
-{
- /* TODO(sergey):
- * - Test if pushing distance on the stack helps.
- * - Likely and unlikely for if() statements.
- * - Test restrict attribute for pointers.
- */
-
- /* Traversal stack in CUDA thread-local memory. */
- QBVHStackItem traversal_stack[BVH_QSTACK_SIZE];
- traversal_stack[0].addr = ENTRYPOINT_SENTINEL;
-
- /* Traversal variables in registers. */
- int stack_ptr = 0;
- int node_addr = kernel_data.bvh.root;
-
- /* Ray parameters in registers. */
- const float tmax = ray->t;
- float3 P = ray->P;
- float3 dir = bvh_clamp_direction(ray->D);
- float3 idir = bvh_inverse_direction(dir);
- int object = OBJECT_NONE;
- float isect_t = tmax;
-
-#if BVH_FEATURE(BVH_MOTION)
- Transform ob_itfm;
-#endif
-
- uint num_hits = 0;
- isect_array->t = tmax;
-
-#if BVH_FEATURE(BVH_INSTANCING)
- int num_hits_in_instance = 0;
-#endif
-
- ssef tnear(0.0f), tfar(isect_t);
-#if BVH_FEATURE(BVH_HAIR)
- sse3f dir4(ssef(dir.x), ssef(dir.y), ssef(dir.z));
-#endif
- sse3f idir4(ssef(idir.x), ssef(idir.y), ssef(idir.z));
-
-#ifdef __KERNEL_AVX2__
- float3 P_idir = P * idir;
- sse3f P_idir4(P_idir.x, P_idir.y, P_idir.z);
-#endif
-#if BVH_FEATURE(BVH_HAIR) || !defined(__KERNEL_AVX2__)
- sse3f org4(ssef(P.x), ssef(P.y), ssef(P.z));
-#endif
-
- /* Offsets to select the side that becomes the lower or upper bound. */
- int near_x, near_y, near_z;
- int far_x, far_y, far_z;
- qbvh_near_far_idx_calc(idir, &near_x, &near_y, &near_z, &far_x, &far_y, &far_z);
-
- /* Traversal loop. */
- do {
- do {
- /* Traverse internal nodes. */
- while (node_addr >= 0 && node_addr != ENTRYPOINT_SENTINEL) {
- float4 inodes = kernel_tex_fetch(__bvh_nodes, node_addr + 0);
-
-#ifdef __VISIBILITY_FLAG__
- if ((__float_as_uint(inodes.x) & visibility) == 0) {
- /* Pop. */
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- continue;
- }
-#endif
-
- ssef dist;
- int child_mask = NODE_INTERSECT(kg,
- tnear,
- tfar,
-#ifdef __KERNEL_AVX2__
- P_idir4,
-#endif
-#if BVH_FEATURE(BVH_HAIR) || !defined(__KERNEL_AVX2__)
- org4,
-#endif
-#if BVH_FEATURE(BVH_HAIR)
- dir4,
-#endif
- idir4,
- near_x,
- near_y,
- near_z,
- far_x,
- far_y,
- far_z,
- node_addr,
- &dist);
-
- if (child_mask != 0) {
- float4 cnodes;
-#if BVH_FEATURE(BVH_HAIR)
- if (__float_as_uint(inodes.x) & PATH_RAY_NODE_UNALIGNED) {
- cnodes = kernel_tex_fetch(__bvh_nodes, node_addr + 13);
- }
- else
-#endif
- {
- cnodes = kernel_tex_fetch(__bvh_nodes, node_addr + 7);
- }
-
- /* One child is hit, continue with that child. */
- int r = __bscf(child_mask);
- if (child_mask == 0) {
- node_addr = __float_as_int(cnodes[r]);
- continue;
- }
-
- /* Two children are hit, push far child, and continue with
- * closer child.
- */
- int c0 = __float_as_int(cnodes[r]);
- float d0 = ((float *)&dist)[r];
- r = __bscf(child_mask);
- int c1 = __float_as_int(cnodes[r]);
- float d1 = ((float *)&dist)[r];
- if (child_mask == 0) {
- if (d1 < d0) {
- node_addr = c1;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_QSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c0;
- traversal_stack[stack_ptr].dist = d0;
- continue;
- }
- else {
- node_addr = c0;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_QSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c1;
- traversal_stack[stack_ptr].dist = d1;
- continue;
- }
- }
-
- /* Here starts the slow path for 3 or 4 hit children. We push
- * all nodes onto the stack to sort them there.
- */
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_QSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c1;
- traversal_stack[stack_ptr].dist = d1;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_QSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c0;
- traversal_stack[stack_ptr].dist = d0;
-
- /* Three children are hit, push all onto stack and sort 3
- * stack items, continue with closest child.
- */
- r = __bscf(child_mask);
- int c2 = __float_as_int(cnodes[r]);
- float d2 = ((float *)&dist)[r];
- if (child_mask == 0) {
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_QSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c2;
- traversal_stack[stack_ptr].dist = d2;
- qbvh_stack_sort(&traversal_stack[stack_ptr],
- &traversal_stack[stack_ptr - 1],
- &traversal_stack[stack_ptr - 2]);
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- continue;
- }
-
- /* Four children are hit, push all onto stack and sort 4
- * stack items, continue with closest child.
- */
- r = __bscf(child_mask);
- int c3 = __float_as_int(cnodes[r]);
- float d3 = ((float *)&dist)[r];
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_QSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c3;
- traversal_stack[stack_ptr].dist = d3;
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_QSTACK_SIZE);
- traversal_stack[stack_ptr].addr = c2;
- traversal_stack[stack_ptr].dist = d2;
- qbvh_stack_sort(&traversal_stack[stack_ptr],
- &traversal_stack[stack_ptr - 1],
- &traversal_stack[stack_ptr - 2],
- &traversal_stack[stack_ptr - 3]);
- }
-
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- }
-
- /* If node is leaf, fetch triangle list. */
- if (node_addr < 0) {
- float4 leaf = kernel_tex_fetch(__bvh_leaf_nodes, (-node_addr - 1));
-
- if ((__float_as_uint(leaf.z) & visibility) == 0) {
- /* Pop. */
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- continue;
- }
-
- int prim_addr = __float_as_int(leaf.x);
-
-#if BVH_FEATURE(BVH_INSTANCING)
- if (prim_addr >= 0) {
-#endif
- int prim_addr2 = __float_as_int(leaf.y);
- const uint type = __float_as_int(leaf.w);
- const uint p_type = type & PRIMITIVE_ALL;
- bool hit;
-
- /* Pop. */
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
-
- /* Primitive intersection. */
- switch (p_type) {
- case PRIMITIVE_TRIANGLE: {
- for (; prim_addr < prim_addr2; prim_addr++) {
- kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type);
- /* Only primitives from volume object. */
- uint tri_object = (object == OBJECT_NONE) ?
- kernel_tex_fetch(__prim_object, prim_addr) :
- object;
- int object_flag = kernel_tex_fetch(__object_flag, tri_object);
- if ((object_flag & SD_OBJECT_HAS_VOLUME) == 0) {
- continue;
- }
- /* Intersect ray against primitive. */
- hit = triangle_intersect(kg, isect_array, P, dir, visibility, object, prim_addr);
- if (hit) {
- /* Move on to next entry in intersections array. */
- isect_array++;
- num_hits++;
-#if BVH_FEATURE(BVH_INSTANCING)
- num_hits_in_instance++;
-#endif
- isect_array->t = isect_t;
- if (num_hits == max_hits) {
-#if BVH_FEATURE(BVH_INSTANCING)
- if (object != OBJECT_NONE) {
-# if BVH_FEATURE(BVH_MOTION)
- float t_fac = 1.0f / len(transform_direction(&ob_itfm, dir));
-# else
- Transform itfm = object_fetch_transform(
- kg, object, OBJECT_INVERSE_TRANSFORM);
- float t_fac = 1.0f / len(transform_direction(&itfm, dir));
-# endif
- for (int i = 0; i < num_hits_in_instance; i++) {
- (isect_array - i - 1)->t *= t_fac;
- }
- }
-#endif /* BVH_FEATURE(BVH_INSTANCING) */
- return num_hits;
- }
- }
- }
- break;
- }
-#if BVH_FEATURE(BVH_MOTION)
- case PRIMITIVE_MOTION_TRIANGLE: {
- for (; prim_addr < prim_addr2; prim_addr++) {
- kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type);
- /* Only primitives from volume object. */
- uint tri_object = (object == OBJECT_NONE) ?
- kernel_tex_fetch(__prim_object, prim_addr) :
- object;
- int object_flag = kernel_tex_fetch(__object_flag, tri_object);
- if ((object_flag & SD_OBJECT_HAS_VOLUME) == 0) {
- continue;
- }
- /* Intersect ray against primitive. */
- hit = motion_triangle_intersect(
- kg, isect_array, P, dir, ray->time, visibility, object, prim_addr);
- if (hit) {
- /* Move on to next entry in intersections array. */
- isect_array++;
- num_hits++;
-# if BVH_FEATURE(BVH_INSTANCING)
- num_hits_in_instance++;
-# endif
- isect_array->t = isect_t;
- if (num_hits == max_hits) {
-# if BVH_FEATURE(BVH_INSTANCING)
- if (object != OBJECT_NONE) {
-# if BVH_FEATURE(BVH_MOTION)
- float t_fac = 1.0f / len(transform_direction(&ob_itfm, dir));
-# else
- Transform itfm = object_fetch_transform(
- kg, object, OBJECT_INVERSE_TRANSFORM);
- float t_fac = 1.0f / len(transform_direction(&itfm, dir));
-# endif
- for (int i = 0; i < num_hits_in_instance; i++) {
- (isect_array - i - 1)->t *= t_fac;
- }
- }
-# endif /* BVH_FEATURE(BVH_INSTANCING) */
- return num_hits;
- }
- }
- }
- break;
- }
-#endif
- }
- }
-#if BVH_FEATURE(BVH_INSTANCING)
- else {
- /* Instance push. */
- object = kernel_tex_fetch(__prim_object, -prim_addr - 1);
- int object_flag = kernel_tex_fetch(__object_flag, object);
- if (object_flag & SD_OBJECT_HAS_VOLUME) {
-# if BVH_FEATURE(BVH_MOTION)
- isect_t = bvh_instance_motion_push(
- kg, object, ray, &P, &dir, &idir, isect_t, &ob_itfm);
-# else
- isect_t = bvh_instance_push(kg, object, ray, &P, &dir, &idir, isect_t);
-# endif
-
- qbvh_near_far_idx_calc(idir, &near_x, &near_y, &near_z, &far_x, &far_y, &far_z);
- tfar = ssef(isect_t);
- idir4 = sse3f(ssef(idir.x), ssef(idir.y), ssef(idir.z));
-# if BVH_FEATURE(BVH_HAIR)
- dir4 = sse3f(ssef(dir.x), ssef(dir.y), ssef(dir.z));
-# endif
-# ifdef __KERNEL_AVX2__
- P_idir = P * idir;
- P_idir4 = sse3f(P_idir.x, P_idir.y, P_idir.z);
-# endif
-# if BVH_FEATURE(BVH_HAIR) || !defined(__KERNEL_AVX2__)
- org4 = sse3f(ssef(P.x), ssef(P.y), ssef(P.z));
-# endif
-
- num_hits_in_instance = 0;
- isect_array->t = isect_t;
-
- ++stack_ptr;
- kernel_assert(stack_ptr < BVH_QSTACK_SIZE);
- traversal_stack[stack_ptr].addr = ENTRYPOINT_SENTINEL;
-
- node_addr = kernel_tex_fetch(__object_node, object);
- }
- else {
- /* Pop. */
- object = OBJECT_NONE;
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- }
- }
- }
-#endif /* FEATURE(BVH_INSTANCING) */
- } while (node_addr != ENTRYPOINT_SENTINEL);
-
-#if BVH_FEATURE(BVH_INSTANCING)
- if (stack_ptr >= 0) {
- kernel_assert(object != OBJECT_NONE);
-
- /* Instance pop. */
- if (num_hits_in_instance) {
- float t_fac;
-# if BVH_FEATURE(BVH_MOTION)
- bvh_instance_motion_pop_factor(kg, object, ray, &P, &dir, &idir, &t_fac, &ob_itfm);
-# else
- bvh_instance_pop_factor(kg, object, ray, &P, &dir, &idir, &t_fac);
-# endif
- /* Scale isect->t to adjust for instancing. */
- for (int i = 0; i < num_hits_in_instance; i++) {
- (isect_array - i - 1)->t *= t_fac;
- }
- }
- else {
-# if BVH_FEATURE(BVH_MOTION)
- bvh_instance_motion_pop(kg, object, ray, &P, &dir, &idir, FLT_MAX, &ob_itfm);
-# else
- bvh_instance_pop(kg, object, ray, &P, &dir, &idir, FLT_MAX);
-# endif
- }
-
- isect_t = tmax;
- isect_array->t = isect_t;
-
- qbvh_near_far_idx_calc(idir, &near_x, &near_y, &near_z, &far_x, &far_y, &far_z);
- tfar = ssef(isect_t);
-# if BVH_FEATURE(BVH_HAIR)
- dir4 = sse3f(ssef(dir.x), ssef(dir.y), ssef(dir.z));
-# endif
- idir4 = sse3f(ssef(idir.x), ssef(idir.y), ssef(idir.z));
-# ifdef __KERNEL_AVX2__
- P_idir = P * idir;
- P_idir4 = sse3f(P_idir.x, P_idir.y, P_idir.z);
-# endif
-# if BVH_FEATURE(BVH_HAIR) || !defined(__KERNEL_AVX2__)
- org4 = sse3f(ssef(P.x), ssef(P.y), ssef(P.z));
-# endif
-
- object = OBJECT_NONE;
- node_addr = traversal_stack[stack_ptr].addr;
- --stack_ptr;
- }
-#endif /* FEATURE(BVH_INSTANCING) */
- } while (node_addr != ENTRYPOINT_SENTINEL);
-
- return num_hits;
-}
-
-#undef NODE_INTERSECT
diff --git a/intern/cycles/kernel/closure/bsdf.h b/intern/cycles/kernel/closure/bsdf.h
index 4cc61e8ee71..6070fd983f5 100644
--- a/intern/cycles/kernel/closure/bsdf.h
+++ b/intern/cycles/kernel/closure/bsdf.h
@@ -119,13 +119,16 @@ ccl_device_inline int bsdf_sample(KernelGlobals *kg,
differential3 *domega_in,
float *pdf)
{
+ /* For curves use the smooth normal, particularly for ribbons the geometric
+ * normal gives too much darkening otherwise. */
int label;
+ const float3 Ng = (sd->type & PRIMITIVE_ALL_CURVE) ? sc->N : sd->Ng;
switch (sc->type) {
case CLOSURE_BSDF_DIFFUSE_ID:
case CLOSURE_BSDF_BSSRDF_ID:
label = bsdf_diffuse_sample(sc,
- sd->Ng,
+ Ng,
sd->I,
sd->dI.dx,
sd->dI.dy,
@@ -140,7 +143,7 @@ ccl_device_inline int bsdf_sample(KernelGlobals *kg,
#ifdef __SVM__
case CLOSURE_BSDF_OREN_NAYAR_ID:
label = bsdf_oren_nayar_sample(sc,
- sd->Ng,
+ Ng,
sd->I,
sd->dI.dx,
sd->dI.dy,
@@ -155,7 +158,7 @@ ccl_device_inline int bsdf_sample(KernelGlobals *kg,
# ifdef __OSL__
case CLOSURE_BSDF_PHONG_RAMP_ID:
label = bsdf_phong_ramp_sample(sc,
- sd->Ng,
+ Ng,
sd->I,
sd->dI.dx,
sd->dI.dy,
@@ -169,7 +172,7 @@ ccl_device_inline int bsdf_sample(KernelGlobals *kg,
break;
case CLOSURE_BSDF_DIFFUSE_RAMP_ID:
label = bsdf_diffuse_ramp_sample(sc,
- sd->Ng,
+ Ng,
sd->I,
sd->dI.dx,
sd->dI.dy,
@@ -184,7 +187,7 @@ ccl_device_inline int bsdf_sample(KernelGlobals *kg,
# endif
case CLOSURE_BSDF_TRANSLUCENT_ID:
label = bsdf_translucent_sample(sc,
- sd->Ng,
+ Ng,
sd->I,
sd->dI.dx,
sd->dI.dy,
@@ -198,7 +201,7 @@ ccl_device_inline int bsdf_sample(KernelGlobals *kg,
break;
case CLOSURE_BSDF_REFLECTION_ID:
label = bsdf_reflection_sample(sc,
- sd->Ng,
+ Ng,
sd->I,
sd->dI.dx,
sd->dI.dy,
@@ -212,7 +215,7 @@ ccl_device_inline int bsdf_sample(KernelGlobals *kg,
break;
case CLOSURE_BSDF_REFRACTION_ID:
label = bsdf_refraction_sample(sc,
- sd->Ng,
+ Ng,
sd->I,
sd->dI.dx,
sd->dI.dy,
@@ -226,7 +229,7 @@ ccl_device_inline int bsdf_sample(KernelGlobals *kg,
break;
case CLOSURE_BSDF_TRANSPARENT_ID:
label = bsdf_transparent_sample(sc,
- sd->Ng,
+ Ng,
sd->I,
sd->dI.dx,
sd->dI.dy,
@@ -244,7 +247,7 @@ ccl_device_inline int bsdf_sample(KernelGlobals *kg,
case CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID:
label = bsdf_microfacet_ggx_sample(kg,
sc,
- sd->Ng,
+ Ng,
sd->I,
sd->dI.dx,
sd->dI.dy,
@@ -260,7 +263,7 @@ ccl_device_inline int bsdf_sample(KernelGlobals *kg,
case CLOSURE_BSDF_MICROFACET_MULTI_GGX_FRESNEL_ID:
label = bsdf_microfacet_multi_ggx_sample(kg,
sc,
- sd->Ng,
+ Ng,
sd->I,
sd->dI.dx,
sd->dI.dy,
@@ -277,7 +280,7 @@ ccl_device_inline int bsdf_sample(KernelGlobals *kg,
case CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_FRESNEL_ID:
label = bsdf_microfacet_multi_ggx_glass_sample(kg,
sc,
- sd->Ng,
+ Ng,
sd->I,
sd->dI.dx,
sd->dI.dy,
@@ -294,7 +297,7 @@ ccl_device_inline int bsdf_sample(KernelGlobals *kg,
case CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID:
label = bsdf_microfacet_beckmann_sample(kg,
sc,
- sd->Ng,
+ Ng,
sd->I,
sd->dI.dx,
sd->dI.dy,
@@ -308,7 +311,7 @@ ccl_device_inline int bsdf_sample(KernelGlobals *kg,
break;
case CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID:
label = bsdf_ashikhmin_shirley_sample(sc,
- sd->Ng,
+ Ng,
sd->I,
sd->dI.dx,
sd->dI.dy,
@@ -322,7 +325,7 @@ ccl_device_inline int bsdf_sample(KernelGlobals *kg,
break;
case CLOSURE_BSDF_ASHIKHMIN_VELVET_ID:
label = bsdf_ashikhmin_velvet_sample(sc,
- sd->Ng,
+ Ng,
sd->I,
sd->dI.dx,
sd->dI.dy,
@@ -336,7 +339,7 @@ ccl_device_inline int bsdf_sample(KernelGlobals *kg,
break;
case CLOSURE_BSDF_DIFFUSE_TOON_ID:
label = bsdf_diffuse_toon_sample(sc,
- sd->Ng,
+ Ng,
sd->I,
sd->dI.dx,
sd->dI.dy,
@@ -350,7 +353,7 @@ ccl_device_inline int bsdf_sample(KernelGlobals *kg,
break;
case CLOSURE_BSDF_GLOSSY_TOON_ID:
label = bsdf_glossy_toon_sample(sc,
- sd->Ng,
+ Ng,
sd->I,
sd->dI.dx,
sd->dI.dy,
@@ -364,7 +367,7 @@ ccl_device_inline int bsdf_sample(KernelGlobals *kg,
break;
case CLOSURE_BSDF_HAIR_REFLECTION_ID:
label = bsdf_hair_reflection_sample(sc,
- sd->Ng,
+ Ng,
sd->I,
sd->dI.dx,
sd->dI.dy,
@@ -378,7 +381,7 @@ ccl_device_inline int bsdf_sample(KernelGlobals *kg,
break;
case CLOSURE_BSDF_HAIR_TRANSMISSION_ID:
label = bsdf_hair_transmission_sample(sc,
- sd->Ng,
+ Ng,
sd->I,
sd->dI.dx,
sd->dI.dy,
@@ -398,7 +401,7 @@ ccl_device_inline int bsdf_sample(KernelGlobals *kg,
case CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID:
case CLOSURE_BSDF_BSSRDF_PRINCIPLED_ID:
label = bsdf_principled_diffuse_sample(sc,
- sd->Ng,
+ Ng,
sd->I,
sd->dI.dx,
sd->dI.dy,
@@ -412,7 +415,7 @@ ccl_device_inline int bsdf_sample(KernelGlobals *kg,
break;
case CLOSURE_BSDF_PRINCIPLED_SHEEN_ID:
label = bsdf_principled_sheen_sample(sc,
- sd->Ng,
+ Ng,
sd->I,
sd->dI.dx,
sd->dI.dy,
@@ -485,9 +488,12 @@ ccl_device_inline
const float3 omega_in,
float *pdf)
{
+ /* For curves use the smooth normal, particularly for ribbons the geometric
+ * normal gives too much darkening otherwise. */
+ const float3 Ng = (sd->type & PRIMITIVE_ALL_CURVE) ? sd->N : sd->Ng;
float3 eval;
- if (dot(sd->Ng, omega_in) >= 0.0f) {
+ if (dot(Ng, omega_in) >= 0.0f) {
switch (sc->type) {
case CLOSURE_BSDF_DIFFUSE_ID:
case CLOSURE_BSDF_BSSRDF_ID:
diff --git a/intern/cycles/kernel/closure/bsdf_hair_principled.h b/intern/cycles/kernel/closure/bsdf_hair_principled.h
index f78bbeb5d9d..389bd62ba68 100644
--- a/intern/cycles/kernel/closure/bsdf_hair_principled.h
+++ b/intern/cycles/kernel/closure/bsdf_hair_principled.h
@@ -206,9 +206,6 @@ ccl_device int bsdf_principled_hair_setup(ShaderData *sd, PrincipledHairBSDF *bs
float3 X = safe_normalize(sd->dPdu);
float3 Y = safe_normalize(cross(X, sd->I));
float3 Z = safe_normalize(cross(X, Y));
- /* TODO: the solution below works where sd->Ng is the normal
- * pointing from the center of the curve to the shading point.
- * It doesn't work for triangles, see https://developer.blender.org/T43625 */
/* h -1..0..1 means the rays goes from grazing the hair, to hitting it at
* the center, to grazing the other edge. This is the sine of the angle
@@ -216,7 +213,9 @@ ccl_device int bsdf_principled_hair_setup(ShaderData *sd, PrincipledHairBSDF *bs
/* TODO: we convert this value to a cosine later and discard the sign, so
* we could probably save some operations. */
- float h = dot(cross(sd->Ng, X), Z);
+ float h = (sd->type & (PRIMITIVE_CURVE_RIBBON | PRIMITIVE_MOTION_CURVE_RIBBON)) ?
+ -sd->v :
+ dot(cross(sd->Ng, X), Z);
kernel_assert(fabsf(h) < 1.0f + 1e-4f);
kernel_assert(isfinite3_safe(Y));
diff --git a/intern/cycles/kernel/geom/geom_curve.h b/intern/cycles/kernel/geom/geom_curve.h
index 928cad58452..6ff0c7f2044 100644
--- a/intern/cycles/kernel/geom/geom_curve.h
+++ b/intern/cycles/kernel/geom/geom_curve.h
@@ -23,33 +23,6 @@ CCL_NAMESPACE_BEGIN
#ifdef __HAIR__
-/* Interpolation of curve geometry */
-
-ccl_device_inline float3 curvetangent(float t, float3 p0, float3 p1, float3 p2, float3 p3)
-{
- float fc = 0.71f;
- float data[4];
- float t2 = t * t;
- data[0] = -3.0f * fc * t2 + 4.0f * fc * t - fc;
- data[1] = 3.0f * (2.0f - fc) * t2 + 2.0f * (fc - 3.0f) * t;
- data[2] = 3.0f * (fc - 2.0f) * t2 + 2.0f * (3.0f - 2.0f * fc) * t + fc;
- data[3] = 3.0f * fc * t2 - 2.0f * fc * t;
- return data[0] * p0 + data[1] * p1 + data[2] * p2 + data[3] * p3;
-}
-
-ccl_device_inline float3 curvepoint(float t, float3 p0, float3 p1, float3 p2, float3 p3)
-{
- float data[4];
- float fc = 0.71f;
- float t2 = t * t;
- float t3 = t2 * t;
- data[0] = -fc * t3 + 2.0f * fc * t2 - fc * t;
- data[1] = (2.0f - fc) * t3 + (fc - 3.0f) * t2 + 1.0f;
- data[2] = (fc - 2.0f) * t3 + (3.0f - 2.0f * fc) * t2 + fc * t;
- data[3] = fc * t3 - fc * t2;
- return data[0] * p0 + data[1] * p1 + data[2] * p2 + data[3] * p3;
-}
-
/* Reading attributes on various curve elements */
ccl_device float curve_attribute_float(
@@ -225,6 +198,66 @@ ccl_device float3 curve_attribute_float3(KernelGlobals *kg,
}
}
+ccl_device float4 curve_attribute_float4(KernelGlobals *kg,
+ const ShaderData *sd,
+ const AttributeDescriptor desc,
+ float4 *dx,
+ float4 *dy)
+{
+ if (desc.element == ATTR_ELEMENT_CURVE) {
+ /* idea: we can't derive any useful differentials here, but for tiled
+ * mipmap image caching it would be useful to avoid reading the highest
+ * detail level always. maybe a derivative based on the hair density
+ * could be computed somehow? */
+# ifdef __RAY_DIFFERENTIALS__
+ if (dx)
+ *dx = make_float4(0.0f, 0.0f, 0.0f, 0.0f);
+ if (dy)
+ *dy = make_float4(0.0f, 0.0f, 0.0f, 0.0f);
+# endif
+
+ return kernel_tex_fetch(__attributes_float3, desc.offset + sd->prim);
+ }
+ else if (desc.element == ATTR_ELEMENT_CURVE_KEY ||
+ desc.element == ATTR_ELEMENT_CURVE_KEY_MOTION) {
+ float4 curvedata = kernel_tex_fetch(__curves, sd->prim);
+ int k0 = __float_as_int(curvedata.x) + PRIMITIVE_UNPACK_SEGMENT(sd->type);
+ int k1 = k0 + 1;
+
+ float4 f0 = kernel_tex_fetch(__attributes_float3, desc.offset + k0);
+ float4 f1 = kernel_tex_fetch(__attributes_float3, desc.offset + k1);
+
+# ifdef __RAY_DIFFERENTIALS__
+ if (dx)
+ *dx = sd->du.dx * (f1 - f0);
+ if (dy)
+ *dy = make_float4(0.0f, 0.0f, 0.0f, 0.0f);
+# endif
+
+ return (1.0f - sd->u) * f0 + sd->u * f1;
+ }
+ else if (desc.element == ATTR_ELEMENT_OBJECT || desc.element == ATTR_ELEMENT_MESH) {
+# ifdef __RAY_DIFFERENTIALS__
+ if (dx)
+ *dx = make_float4(0.0f, 0.0f, 0.0f, 0.0f);
+ if (dy)
+ *dy = make_float4(0.0f, 0.0f, 0.0f, 0.0f);
+# endif
+
+ return kernel_tex_fetch(__attributes_float3, desc.offset);
+ }
+ else {
+# ifdef __RAY_DIFFERENTIALS__
+ if (dx)
+ *dx = make_float4(0.0f, 0.0f, 0.0f, 0.0f);
+ if (dy)
+ *dy = make_float4(0.0f, 0.0f, 0.0f, 0.0f);
+# endif
+
+ return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
+ }
+}
+
/* Curve thickness */
ccl_device float curve_thickness(KernelGlobals *kg, ShaderData *sd)
@@ -238,12 +271,12 @@ ccl_device float curve_thickness(KernelGlobals *kg, ShaderData *sd)
float4 P_curve[2];
- if (sd->type & PRIMITIVE_CURVE) {
+ if (!(sd->type & PRIMITIVE_ALL_MOTION)) {
P_curve[0] = kernel_tex_fetch(__curve_keys, k0);
P_curve[1] = kernel_tex_fetch(__curve_keys, k1);
}
else {
- motion_curve_keys(kg, sd->object, sd->prim, sd->time, k0, k1, P_curve);
+ motion_curve_keys_linear(kg, sd->object, sd->prim, sd->time, k0, k1, P_curve);
}
r = (P_curve[1].w - P_curve[0].w) * sd->u + P_curve[0].w;
diff --git a/intern/cycles/kernel/geom/geom_curve_intersect.h b/intern/cycles/kernel/geom/geom_curve_intersect.h
index 7a770470150..c04dbee52cc 100644
--- a/intern/cycles/kernel/geom/geom_curve_intersect.h
+++ b/intern/cycles/kernel/geom/geom_curve_intersect.h
@@ -1,4 +1,7 @@
/*
+ * Copyright 2009-2020 Intel Corporation. Adapted from Embree with
+ * with modifications.
+ *
* 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
@@ -14,501 +17,620 @@
CCL_NAMESPACE_BEGIN
-/* Curve primitive intersection functions. */
+/* Curve primitive intersection functions.
+ *
+ * The code here was adapted from curve_intersector_sweep.h in Embree, to get
+ * an exact match between Embree CPU ray-tracing and our GPU ray-tracing. */
+
+#define CURVE_NUM_BEZIER_SUBDIVISIONS 3
+#define CURVE_NUM_BEZIER_SUBDIVISIONS_UNSTABLE (CURVE_NUM_BEZIER_SUBDIVISIONS + 1)
+#define CURVE_NUM_BEZIER_STEPS 2
+#define CURVE_NUM_JACOBIAN_ITERATIONS 5
#ifdef __HAIR__
-# ifdef __KERNEL_SSE2__
-ccl_device_inline ssef transform_point_T3(const ssef t[3], const ssef &a)
+/* Catmull-rom curve evaluation. */
+
+ccl_device_inline float4 catmull_rom_basis_eval(const float4 curve[4], float u)
{
- return madd(shuffle<0>(a), t[0], madd(shuffle<1>(a), t[1], shuffle<2>(a) * t[2]));
+ const float t = u;
+ const float s = 1.0f - u;
+ const float n0 = -t * s * s;
+ const float n1 = 2.0f + t * t * (3.0f * t - 5.0f);
+ const float n2 = 2.0f + s * s * (3.0f * s - 5.0f);
+ const float n3 = -s * t * t;
+ return 0.5f * (curve[0] * n0 + curve[1] * n1 + curve[2] * n2 + curve[3] * n3);
}
-# endif
-/* On CPU pass P and dir by reference to aligned vector. */
-ccl_device_forceinline bool cardinal_curve_intersect(KernelGlobals *kg,
- Intersection *isect,
- const float3 ccl_ref P,
- const float3 ccl_ref dir,
- uint visibility,
- int object,
- int curveAddr,
- float time,
- int type)
+ccl_device_inline float4 catmull_rom_basis_derivative(const float4 curve[4], float u)
{
- const bool is_curve_primitive = (type & PRIMITIVE_CURVE);
+ const float t = u;
+ const float s = 1.0f - u;
+ const float n0 = -s * s + 2.0f * s * t;
+ const float n1 = 2.0f * t * (3.0f * t - 5.0f) + 3.0f * t * t;
+ const float n2 = 2.0f * s * (3.0f * t + 2.0f) - 3.0f * s * s;
+ const float n3 = -2.0f * s * t + t * t;
+ return 0.5f * (curve[0] * n0 + curve[1] * n1 + curve[2] * n2 + curve[3] * n3);
+}
-# ifndef __KERNEL_OPTIX__ /* see OptiX motion flag OPTIX_MOTION_FLAG_[START|END]_VANISH */
- if (!is_curve_primitive && kernel_data.bvh.use_bvh_steps) {
- const float2 prim_time = kernel_tex_fetch(__prim_time, curveAddr);
- if (time < prim_time.x || time > prim_time.y) {
- return false;
- }
- }
-# endif
+ccl_device_inline float4 catmull_rom_basis_derivative2(const float4 curve[4], float u)
+{
- int segment = PRIMITIVE_UNPACK_SEGMENT(type);
- float epsilon = 0.0f;
- float r_st, r_en;
+ const float t = u;
+ const float n0 = -3.0f * t + 2.0f;
+ const float n1 = 9.0f * t - 5.0f;
+ const float n2 = -9.0f * t + 4.0f;
+ const float n3 = 3.0f * t - 1.0f;
+ return (curve[0] * n0 + curve[1] * n1 + curve[2] * n2 + curve[3] * n3);
+}
- int depth = kernel_data.curve.subdivisions;
- int flags = kernel_data.curve.curveflags;
- int prim = kernel_tex_fetch(__prim_index, curveAddr);
+/* Thick Curve */
-# ifdef __KERNEL_SSE2__
- ssef vdir = load4f(dir);
- ssef vcurve_coef[4];
- const float3 *curve_coef = (float3 *)vcurve_coef;
+ccl_device_inline float3 dnormalize(const float3 p, const float3 dp)
+{
+ const float pp = dot(p, p);
+ const float pdp = dot(p, dp);
+ return (pp * dp - pdp * p) / (pp * sqrtf(pp));
+}
- {
- ssef dtmp = vdir * vdir;
- ssef d_ss = mm_sqrt(dtmp + shuffle<2>(dtmp));
- ssef rd_ss = load1f_first(1.0f) / d_ss;
-
- ssei v00vec = load4i((ssei *)&kg->__curves.data[prim]);
- int2 &v00 = (int2 &)v00vec;
-
- int k0 = v00.x + segment;
- int k1 = k0 + 1;
- int ka = max(k0 - 1, v00.x);
- int kb = min(k1 + 1, v00.x + v00.y - 1);
-
-# if defined(__KERNEL_AVX2__) && defined(__KERNEL_SSE__) && \
- (!defined(_MSC_VER) || _MSC_VER > 1800)
- avxf P_curve_0_1, P_curve_2_3;
- if (is_curve_primitive) {
- P_curve_0_1 = _mm256_loadu2_m128(&kg->__curve_keys.data[k0].x, &kg->__curve_keys.data[ka].x);
- P_curve_2_3 = _mm256_loadu2_m128(&kg->__curve_keys.data[kb].x, &kg->__curve_keys.data[k1].x);
- }
- else {
- int fobject = (object == OBJECT_NONE) ? kernel_tex_fetch(__prim_object, curveAddr) : object;
- motion_cardinal_curve_keys_avx(
- kg, fobject, prim, time, ka, k0, k1, kb, &P_curve_0_1, &P_curve_2_3);
- }
-# else /* __KERNEL_AVX2__ */
- ssef P_curve[4];
-
- if (is_curve_primitive) {
- P_curve[0] = load4f(&kg->__curve_keys.data[ka].x);
- P_curve[1] = load4f(&kg->__curve_keys.data[k0].x);
- P_curve[2] = load4f(&kg->__curve_keys.data[k1].x);
- P_curve[3] = load4f(&kg->__curve_keys.data[kb].x);
+ccl_device_inline float sqr_point_to_line_distance(const float3 PmQ0, const float3 Q1mQ0)
+{
+ const float3 N = cross(PmQ0, Q1mQ0);
+ const float3 D = Q1mQ0;
+ return dot(N, N) / dot(D, D);
+}
+
+ccl_device_inline bool cylinder_intersect(const float3 cylinder_start,
+ const float3 cylinder_end,
+ const float cylinder_radius,
+ const float3 ray_dir,
+ float2 *t_o,
+ float *u0_o,
+ float3 *Ng0_o,
+ float *u1_o,
+ float3 *Ng1_o)
+{
+ /* Calculate quadratic equation to solve. */
+ const float rl = 1.0f / len(cylinder_end - cylinder_start);
+ const float3 P0 = cylinder_start, dP = (cylinder_end - cylinder_start) * rl;
+ const float3 O = -P0, dO = ray_dir;
+
+ const float dOdO = dot(dO, dO);
+ const float OdO = dot(dO, O);
+ const float OO = dot(O, O);
+ const float dOz = dot(dP, dO);
+ const float Oz = dot(dP, O);
+
+ const float A = dOdO - sqr(dOz);
+ const float B = 2.0f * (OdO - dOz * Oz);
+ const float C = OO - sqr(Oz) - sqr(cylinder_radius);
+
+ /* We miss the cylinder if determinant is smaller than zero. */
+ const float D = B * B - 4.0f * A * C;
+ if (!(D >= 0.0f)) {
+ *t_o = make_float2(FLT_MAX, -FLT_MAX);
+ return false;
+ }
+
+ /* Special case for rays that are parallel to the cylinder. */
+ const float eps = 16.0f * FLT_EPSILON * max(fabsf(dOdO), fabsf(sqr(dOz)));
+ if (fabsf(A) < eps) {
+ if (C <= 0.0f) {
+ *t_o = make_float2(-FLT_MAX, FLT_MAX);
+ return true;
}
else {
- int fobject = (object == OBJECT_NONE) ? kernel_tex_fetch(__prim_object, curveAddr) : object;
- motion_cardinal_curve_keys(kg, fobject, prim, time, ka, k0, k1, kb, (float4 *)&P_curve);
+ *t_o = make_float2(-FLT_MAX, FLT_MAX);
+ return false;
}
-# endif /* __KERNEL_AVX2__ */
-
- ssef rd_sgn = set_sign_bit<0, 1, 1, 1>(shuffle<0>(rd_ss));
- ssef mul_zxxy = shuffle<2, 0, 0, 1>(vdir) * rd_sgn;
- ssef mul_yz = shuffle<1, 2, 1, 2>(vdir) * mul_zxxy;
- ssef mul_shuf = shuffle<0, 1, 2, 3>(mul_zxxy, mul_yz);
- ssef vdir0 = vdir & cast(ssei(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0));
-
- ssef htfm0 = shuffle<0, 2, 0, 3>(mul_shuf, vdir0);
- ssef htfm1 = shuffle<1, 0, 1, 3>(load1f_first(extract<0>(d_ss)), vdir0);
- ssef htfm2 = shuffle<1, 3, 2, 3>(mul_shuf, vdir0);
-
-# if defined(__KERNEL_AVX2__) && defined(__KERNEL_SSE__) && \
- (!defined(_MSC_VER) || _MSC_VER > 1800)
- const avxf vPP = _mm256_broadcast_ps(&P.m128);
- const avxf htfm00 = avxf(htfm0.m128, htfm0.m128);
- const avxf htfm11 = avxf(htfm1.m128, htfm1.m128);
- const avxf htfm22 = avxf(htfm2.m128, htfm2.m128);
-
- const avxf p01 = madd(
- shuffle<0>(P_curve_0_1 - vPP),
- htfm00,
- madd(shuffle<1>(P_curve_0_1 - vPP), htfm11, shuffle<2>(P_curve_0_1 - vPP) * htfm22));
- const avxf p23 = madd(
- shuffle<0>(P_curve_2_3 - vPP),
- htfm00,
- madd(shuffle<1>(P_curve_2_3 - vPP), htfm11, shuffle<2>(P_curve_2_3 - vPP) * htfm22));
-
- const ssef p0 = _mm256_castps256_ps128(p01);
- const ssef p1 = _mm256_extractf128_ps(p01, 1);
- const ssef p2 = _mm256_castps256_ps128(p23);
- const ssef p3 = _mm256_extractf128_ps(p23, 1);
-
- const ssef P_curve_1 = _mm256_extractf128_ps(P_curve_0_1, 1);
- r_st = ((float4 &)P_curve_1).w;
- const ssef P_curve_2 = _mm256_castps256_ps128(P_curve_2_3);
- r_en = ((float4 &)P_curve_2).w;
-# else /* __KERNEL_AVX2__ */
- ssef htfm[] = {htfm0, htfm1, htfm2};
- ssef vP = load4f(P);
- ssef p0 = transform_point_T3(htfm, P_curve[0] - vP);
- ssef p1 = transform_point_T3(htfm, P_curve[1] - vP);
- ssef p2 = transform_point_T3(htfm, P_curve[2] - vP);
- ssef p3 = transform_point_T3(htfm, P_curve[3] - vP);
-
- r_st = ((float4 &)P_curve[1]).w;
- r_en = ((float4 &)P_curve[2]).w;
-# endif /* __KERNEL_AVX2__ */
-
- float fc = 0.71f;
- ssef vfc = ssef(fc);
- ssef vfcxp3 = vfc * p3;
-
- vcurve_coef[0] = p1;
- vcurve_coef[1] = vfc * (p2 - p0);
- vcurve_coef[2] = madd(
- ssef(fc * 2.0f), p0, madd(ssef(fc - 3.0f), p1, msub(ssef(3.0f - 2.0f * fc), p2, vfcxp3)));
- vcurve_coef[3] = msub(ssef(fc - 2.0f), p2 - p1, msub(vfc, p0, vfcxp3));
}
-# else
- float3 curve_coef[4];
- /* curve Intersection check */
- /* obtain curve parameters */
+ /* Standard case for rays that are not parallel to the cylinder. */
+ const float Q = sqrtf(D);
+ const float rcp_2A = 1.0f / (2.0f * A);
+ const float t0 = (-B - Q) * rcp_2A;
+ const float t1 = (-B + Q) * rcp_2A;
+
+ /* Calculates u and Ng for near hit. */
{
- /* ray transform created - this should be created at beginning of intersection loop */
- Transform htfm;
- float d = sqrtf(dir.x * dir.x + dir.z * dir.z);
- htfm = make_transform(dir.z / d,
- 0,
- -dir.x / d,
- 0,
- -dir.x * dir.y / d,
- d,
- -dir.y * dir.z / d,
- 0,
- dir.x,
- dir.y,
- dir.z,
- 0);
-
- float4 v00 = kernel_tex_fetch(__curves, prim);
-
- int k0 = __float_as_int(v00.x) + segment;
- int k1 = k0 + 1;
-
- int ka = max(k0 - 1, __float_as_int(v00.x));
- int kb = min(k1 + 1, __float_as_int(v00.x) + __float_as_int(v00.y) - 1);
-
- float4 P_curve[4];
-
- if (is_curve_primitive) {
- P_curve[0] = kernel_tex_fetch(__curve_keys, ka);
- P_curve[1] = kernel_tex_fetch(__curve_keys, k0);
- P_curve[2] = kernel_tex_fetch(__curve_keys, k1);
- P_curve[3] = kernel_tex_fetch(__curve_keys, kb);
- }
- else {
- int fobject = (object == OBJECT_NONE) ? kernel_tex_fetch(__prim_object, curveAddr) : object;
- motion_cardinal_curve_keys(kg, fobject, prim, time, ka, k0, k1, kb, P_curve);
- }
+ *u0_o = (t0 * dOz + Oz) * rl;
+ const float3 Pr = t0 * ray_dir;
+ const float3 Pl = (*u0_o) * (cylinder_end - cylinder_start) + cylinder_start;
+ *Ng0_o = Pr - Pl;
+ }
- float3 p0 = transform_point(&htfm, float4_to_float3(P_curve[0]) - P);
- float3 p1 = transform_point(&htfm, float4_to_float3(P_curve[1]) - P);
- float3 p2 = transform_point(&htfm, float4_to_float3(P_curve[2]) - P);
- float3 p3 = transform_point(&htfm, float4_to_float3(P_curve[3]) - P);
-
- float fc = 0.71f;
- curve_coef[0] = p1;
- curve_coef[1] = -fc * p0 + fc * p2;
- curve_coef[2] = 2.0f * fc * p0 + (fc - 3.0f) * p1 + (3.0f - 2.0f * fc) * p2 - fc * p3;
- curve_coef[3] = -fc * p0 + (2.0f - fc) * p1 + (fc - 2.0f) * p2 + fc * p3;
- r_st = P_curve[1].w;
- r_en = P_curve[2].w;
+ /* Calculates u and Ng for far hit. */
+ {
+ *u1_o = (t1 * dOz + Oz) * rl;
+ const float3 Pr = t1 * ray_dir;
+ const float3 Pl = (*u1_o) * (cylinder_end - cylinder_start) + cylinder_start;
+ *Ng1_o = Pr - Pl;
}
-# endif
- float r_curr = max(r_st, r_en);
-
- if ((flags & CURVE_KN_RIBBONS) || !(flags & CURVE_KN_BACKFACING))
- epsilon = 2 * r_curr;
-
- /* find bounds - this is slow for cubic curves */
- float upper, lower;
-
- float zextrem[4];
- curvebounds(&lower,
- &upper,
- &zextrem[0],
- &zextrem[1],
- &zextrem[2],
- &zextrem[3],
- curve_coef[0].z,
- curve_coef[1].z,
- curve_coef[2].z,
- curve_coef[3].z);
- if (lower - r_curr > isect->t || upper + r_curr < epsilon)
- return false;
+ *t_o = make_float2(t0, t1);
- /* minimum width extension */
- float xextrem[4];
- curvebounds(&lower,
- &upper,
- &xextrem[0],
- &xextrem[1],
- &xextrem[2],
- &xextrem[3],
- curve_coef[0].x,
- curve_coef[1].x,
- curve_coef[2].x,
- curve_coef[3].x);
- if (lower > r_curr || upper < -r_curr)
- return false;
+ return true;
+}
- float yextrem[4];
- curvebounds(&lower,
- &upper,
- &yextrem[0],
- &yextrem[1],
- &yextrem[2],
- &yextrem[3],
- curve_coef[0].y,
- curve_coef[1].y,
- curve_coef[2].y,
- curve_coef[3].y);
- if (lower > r_curr || upper < -r_curr)
- return false;
+ccl_device_inline float2 half_plane_intersect(const float3 P, const float3 N, const float3 ray_dir)
+{
+ const float3 O = -P;
+ const float3 D = ray_dir;
+ const float ON = dot(O, N);
+ const float DN = dot(D, N);
+ const float min_rcp_input = 1e-18f;
+ const bool eps = fabsf(DN) < min_rcp_input;
+ const float t = -ON / DN;
+ const float lower = (eps || DN < 0.0f) ? -FLT_MAX : t;
+ const float upper = (eps || DN > 0.0f) ? FLT_MAX : t;
+ return make_float2(lower, upper);
+}
- /* setup recurrent loop */
- int level = 1 << depth;
- int tree = 0;
- float resol = 1.0f / (float)level;
- bool hit = false;
-
- /* begin loop */
- while (!(tree >> (depth))) {
- const float i_st = tree * resol;
- const float i_en = i_st + (level * resol);
-
-# ifdef __KERNEL_SSE2__
- ssef vi_st = ssef(i_st), vi_en = ssef(i_en);
- ssef vp_st = madd(madd(madd(vcurve_coef[3], vi_st, vcurve_coef[2]), vi_st, vcurve_coef[1]),
- vi_st,
- vcurve_coef[0]);
- ssef vp_en = madd(madd(madd(vcurve_coef[3], vi_en, vcurve_coef[2]), vi_en, vcurve_coef[1]),
- vi_en,
- vcurve_coef[0]);
-
- ssef vbmin = min(vp_st, vp_en);
- ssef vbmax = max(vp_st, vp_en);
-
- float3 &bmin = (float3 &)vbmin, &bmax = (float3 &)vbmax;
- float &bminx = bmin.x, &bminy = bmin.y, &bminz = bmin.z;
- float &bmaxx = bmax.x, &bmaxy = bmax.y, &bmaxz = bmax.z;
- float3 &p_st = (float3 &)vp_st, &p_en = (float3 &)vp_en;
-# else
- float3 p_st = ((curve_coef[3] * i_st + curve_coef[2]) * i_st + curve_coef[1]) * i_st +
- curve_coef[0];
- float3 p_en = ((curve_coef[3] * i_en + curve_coef[2]) * i_en + curve_coef[1]) * i_en +
- curve_coef[0];
-
- float bminx = min(p_st.x, p_en.x);
- float bmaxx = max(p_st.x, p_en.x);
- float bminy = min(p_st.y, p_en.y);
- float bmaxy = max(p_st.y, p_en.y);
- float bminz = min(p_st.z, p_en.z);
- float bmaxz = max(p_st.z, p_en.z);
-# endif
+ccl_device bool curve_intersect_iterative(const float3 ray_dir,
+ const float dt,
+ const float4 curve[4],
+ float u,
+ float t,
+ const bool use_backfacing,
+ Intersection *isect)
+{
+ const float length_ray_dir = len(ray_dir);
+
+ /* Error of curve evaluations is proportional to largest coordinate. */
+ const float4 box_min = min(min(curve[0], curve[1]), min(curve[2], curve[3]));
+ const float4 box_max = max(min(curve[0], curve[1]), max(curve[2], curve[3]));
+ const float4 box_abs = max(fabs(box_min), fabs(box_max));
+ const float P_err = 16.0f * FLT_EPSILON *
+ max(box_abs.x, max(box_abs.y, max(box_abs.z, box_abs.w)));
+ const float radius_max = box_max.w;
+
+ for (int i = 0; i < CURVE_NUM_JACOBIAN_ITERATIONS; i++) {
+ const float3 Q = ray_dir * t;
+ const float3 dQdt = ray_dir;
+ const float Q_err = 16.0f * FLT_EPSILON * length_ray_dir * t;
+
+ const float4 P4 = catmull_rom_basis_eval(curve, u);
+ const float4 dPdu4 = catmull_rom_basis_derivative(curve, u);
+
+ const float3 P = float4_to_float3(P4);
+ const float3 dPdu = float4_to_float3(dPdu4);
+ const float radius = P4.w;
+ const float dradiusdu = dPdu4.w;
+
+ const float3 ddPdu = float4_to_float3(catmull_rom_basis_derivative2(curve, u));
+
+ const float3 R = Q - P;
+ const float len_R = len(R);
+ const float R_err = max(Q_err, P_err);
+ const float3 dRdu = -dPdu;
+ const float3 dRdt = dQdt;
+
+ const float3 T = normalize(dPdu);
+ const float3 dTdu = dnormalize(dPdu, ddPdu);
+ const float cos_err = P_err / len(dPdu);
+
+ const float f = dot(R, T);
+ const float f_err = len_R * P_err + R_err + cos_err * (1.0f + len_R);
+ const float dfdu = dot(dRdu, T) + dot(R, dTdu);
+ const float dfdt = dot(dRdt, T);
+
+ const float K = dot(R, R) - sqr(f);
+ const float dKdu = (dot(R, dRdu) - f * dfdu);
+ const float dKdt = (dot(R, dRdt) - f * dfdt);
+ const float rsqrt_K = inversesqrtf(K);
+
+ const float g = sqrtf(K) - radius;
+ const float g_err = R_err + f_err + 16.0f * FLT_EPSILON * radius_max;
+ const float dgdu = dKdu * rsqrt_K - dradiusdu;
+ const float dgdt = dKdt * rsqrt_K;
+
+ const float invdet = 1.0f / (dfdu * dgdt - dgdu * dfdt);
+ u -= (dgdt * f - dfdt * g) * invdet;
+ t -= (-dgdu * f + dfdu * g) * invdet;
+
+ if (fabsf(f) < f_err && fabsf(g) < g_err) {
+ t += dt;
+ if (!(0.0f <= t && t <= isect->t)) {
+ return false; /* Rejects NaNs */
+ }
+ if (!(u >= 0.0f && u <= 1.0f)) {
+ return false; /* Rejects NaNs */
+ }
- if (xextrem[0] >= i_st && xextrem[0] <= i_en) {
- bminx = min(bminx, xextrem[1]);
- bmaxx = max(bmaxx, xextrem[1]);
- }
- if (xextrem[2] >= i_st && xextrem[2] <= i_en) {
- bminx = min(bminx, xextrem[3]);
- bmaxx = max(bmaxx, xextrem[3]);
- }
- if (yextrem[0] >= i_st && yextrem[0] <= i_en) {
- bminy = min(bminy, yextrem[1]);
- bmaxy = max(bmaxy, yextrem[1]);
- }
- if (yextrem[2] >= i_st && yextrem[2] <= i_en) {
- bminy = min(bminy, yextrem[3]);
- bmaxy = max(bmaxy, yextrem[3]);
- }
- if (zextrem[0] >= i_st && zextrem[0] <= i_en) {
- bminz = min(bminz, zextrem[1]);
- bmaxz = max(bmaxz, zextrem[1]);
- }
- if (zextrem[2] >= i_st && zextrem[2] <= i_en) {
- bminz = min(bminz, zextrem[3]);
- bmaxz = max(bmaxz, zextrem[3]);
- }
+ /* Backface culling. */
+ const float3 R = normalize(Q - P);
+ const float3 U = dradiusdu * R + dPdu;
+ const float3 V = cross(dPdu, R);
+ const float3 Ng = cross(V, U);
+ if (!use_backfacing && dot(ray_dir, Ng) > 0.0f) {
+ return false;
+ }
- float r1 = r_st + (r_en - r_st) * i_st;
- float r2 = r_st + (r_en - r_st) * i_en;
- r_curr = max(r1, r2);
+ /* Record intersection. */
+ isect->t = t;
+ isect->u = u;
+ isect->v = 0.0f;
- if (bminz - r_curr > isect->t || bmaxz + r_curr < epsilon || bminx > r_curr ||
- bmaxx < -r_curr || bminy > r_curr || bmaxy < -r_curr) {
- /* the bounding box does not overlap the square centered at O */
- tree += level;
- level = tree & -tree;
+ return true;
}
- else if (level == 1) {
-
- /* the maximum recursion depth is reached.
- * check if dP0.(Q-P0)>=0 and dPn.(Pn-Q)>=0.
- * dP* is reversed if necessary.*/
- float t = isect->t;
- float u = 0.0f;
- float gd = 0.0f;
-
- if (flags & CURVE_KN_RIBBONS) {
- float3 tg = (p_en - p_st);
-# ifdef __KERNEL_SSE__
- const float3 tg_sq = tg * tg;
- float w = tg_sq.x + tg_sq.y;
-# else
- float w = tg.x * tg.x + tg.y * tg.y;
-# endif
- if (w == 0) {
- tree++;
- level = tree & -tree;
- continue;
- }
-# ifdef __KERNEL_SSE__
- const float3 p_sttg = p_st * tg;
- w = -(p_sttg.x + p_sttg.y) / w;
+ }
+ return false;
+}
+
+ccl_device bool curve_intersect_recursive(const float3 ray_orig,
+ const float3 ray_dir,
+ float4 curve[4],
+ Intersection *isect)
+{
+ /* Move ray closer to make intersection stable. */
+ const float3 center = float4_to_float3(0.25f * (curve[0] + curve[1] + curve[2] + curve[3]));
+ const float dt = dot(center - ray_orig, ray_dir) / dot(ray_dir, ray_dir);
+ const float3 ref = ray_orig + ray_dir * dt;
+ const float4 ref4 = make_float4(ref.x, ref.y, ref.z, 0.0f);
+ curve[0] -= ref4;
+ curve[1] -= ref4;
+ curve[2] -= ref4;
+ curve[3] -= ref4;
+
+ const bool use_backfacing = false;
+ const float step_size = 1.0f / (float)(CURVE_NUM_BEZIER_STEPS);
+
+ int depth = 0;
+
+ /* todo: optimize stack for GPU somehow? Possibly some bitflags are enough, and
+ * u0/u1 can be derived from the depth. */
+ struct {
+ float u0, u1;
+ int i;
+ } stack[CURVE_NUM_BEZIER_SUBDIVISIONS_UNSTABLE];
+
+ bool found = false;
+
+ float u0 = 0.0f;
+ float u1 = 1.0f;
+ int i = 0;
+
+ while (1) {
+ for (; i < CURVE_NUM_BEZIER_STEPS; i++) {
+ const float step = i * step_size;
+
+ /* Subdivide curve. */
+ const float dscale = (u1 - u0) * (1.0f / 3.0f) * step_size;
+ const float vu0 = mix(u0, u1, step);
+ const float vu1 = mix(u0, u1, step + step_size);
+
+ const float4 P0 = catmull_rom_basis_eval(curve, vu0);
+ const float4 dP0du = dscale * catmull_rom_basis_derivative(curve, vu0);
+ const float4 P3 = catmull_rom_basis_eval(curve, vu1);
+ const float4 dP3du = dscale * catmull_rom_basis_derivative(curve, vu1);
+
+ const float4 P1 = P0 + dP0du;
+ const float4 P2 = P3 - dP3du;
+
+ /* Calculate bounding cylinders. */
+ const float rr1 = sqr_point_to_line_distance(float4_to_float3(dP0du),
+ float4_to_float3(P3 - P0));
+ const float rr2 = sqr_point_to_line_distance(float4_to_float3(dP3du),
+ float4_to_float3(P3 - P0));
+ const float maxr12 = sqrtf(max(rr1, rr2));
+ const float one_plus_ulp = 1.0f + 2.0f * FLT_EPSILON;
+ const float one_minus_ulp = 1.0f - 2.0f * FLT_EPSILON;
+ float r_outer = max(max(P0.w, P1.w), max(P2.w, P3.w)) + maxr12;
+ float r_inner = min(min(P0.w, P1.w), min(P2.w, P3.w)) - maxr12;
+ r_outer = one_plus_ulp * r_outer;
+ r_inner = max(0.0f, one_minus_ulp * r_inner);
+ bool valid = true;
+
+ /* Intersect with outer cylinder. */
+ float2 tc_outer;
+ float u_outer0, u_outer1;
+ float3 Ng_outer0, Ng_outer1;
+ valid = cylinder_intersect(float4_to_float3(P0),
+ float4_to_float3(P3),
+ r_outer,
+ ray_dir,
+ &tc_outer,
+ &u_outer0,
+ &Ng_outer0,
+ &u_outer1,
+ &Ng_outer1);
+ if (!valid) {
+ continue;
+ }
+
+ /* Intersect with cap-planes. */
+ float2 tp = make_float2(-dt, isect->t - dt);
+ tp = make_float2(max(tp.x, tc_outer.x), min(tp.y, tc_outer.y));
+ const float2 h0 = half_plane_intersect(
+ float4_to_float3(P0), float4_to_float3(dP0du), ray_dir);
+ tp = make_float2(max(tp.x, h0.x), min(tp.y, h0.y));
+ const float2 h1 = half_plane_intersect(
+ float4_to_float3(P3), -float4_to_float3(dP3du), ray_dir);
+ tp = make_float2(max(tp.x, h1.x), min(tp.y, h1.y));
+ valid = tp.x <= tp.y;
+ if (!valid) {
+ continue;
+ }
+
+ /* Clamp and correct u parameter. */
+ u_outer0 = clamp(u_outer0, 0.0f, 1.0f);
+ u_outer1 = clamp(u_outer1, 0.0f, 1.0f);
+ u_outer0 = mix(u0, u1, (step + u_outer0) * (1.0f / (float)(CURVE_NUM_BEZIER_STEPS + 1)));
+ u_outer1 = mix(u0, u1, (step + u_outer1) * (1.0f / (float)(CURVE_NUM_BEZIER_STEPS + 1)));
+
+ /* Intersect with inner cylinder. */
+ float2 tc_inner;
+ float u_inner0, u_inner1;
+ float3 Ng_inner0, Ng_inner1;
+ const bool valid_inner = cylinder_intersect(float4_to_float3(P0),
+ float4_to_float3(P3),
+ r_inner,
+ ray_dir,
+ &tc_inner,
+ &u_inner0,
+ &Ng_inner0,
+ &u_inner1,
+ &Ng_inner1);
+
+ /* At the unstable area we subdivide deeper. */
+# if 0
+ const bool unstable0 = (!valid_inner) |
+ (fabsf(dot(normalize(ray_dir), normalize(Ng_inner0))) < 0.3f);
+ const bool unstable1 = (!valid_inner) |
+ (fabsf(dot(normalize(ray_dir), normalize(Ng_inner1))) < 0.3f);
# else
- w = -(p_st.x * tg.x + p_st.y * tg.y) / w;
-# endif
- w = saturate(w);
-
- /* compute u on the curve segment */
- u = i_st * (1 - w) + i_en * w;
- r_curr = r_st + (r_en - r_st) * u;
- /* compare x-y distances */
- float3 p_curr = ((curve_coef[3] * u + curve_coef[2]) * u + curve_coef[1]) * u +
- curve_coef[0];
-
- float3 dp_st = (3 * curve_coef[3] * i_st + 2 * curve_coef[2]) * i_st + curve_coef[1];
- if (dot(tg, dp_st) < 0)
- dp_st *= -1;
- if (dot(dp_st, -p_st) + p_curr.z * dp_st.z < 0) {
- tree++;
- level = tree & -tree;
- continue;
- }
- float3 dp_en = (3 * curve_coef[3] * i_en + 2 * curve_coef[2]) * i_en + curve_coef[1];
- if (dot(tg, dp_en) < 0)
- dp_en *= -1;
- if (dot(dp_en, p_en) - p_curr.z * dp_en.z < 0) {
- tree++;
- level = tree & -tree;
- continue;
- }
+ /* On the GPU appears to be a little faster if always enabled. */
+ (void)valid_inner;
- if (p_curr.x * p_curr.x + p_curr.y * p_curr.y >= r_curr * r_curr || p_curr.z <= epsilon ||
- isect->t < p_curr.z) {
- tree++;
- level = tree & -tree;
- continue;
- }
+ const bool unstable0 = true;
+ const bool unstable1 = true;
+# endif
- t = p_curr.z;
+ /* Subtract the inner interval from the current hit interval. */
+ float2 tp0 = make_float2(tp.x, min(tp.y, tc_inner.x));
+ float2 tp1 = make_float2(max(tp.x, tc_inner.y), tp.y);
+ bool valid0 = valid && (tp0.x <= tp0.y);
+ bool valid1 = valid && (tp1.x <= tp1.y);
+ if (!(valid0 || valid1)) {
+ continue;
}
- else {
- float l = len(p_en - p_st);
- float invl = 1.0f / l;
- float3 tg = (p_en - p_st) * invl;
- gd = (r2 - r1) * invl;
- float difz = -dot(p_st, tg);
- float cyla = 1.0f - (tg.z * tg.z * (1 + gd * gd));
- float invcyla = 1.0f / cyla;
- float halfb = (-p_st.z - tg.z * (difz + gd * (difz * gd + r1)));
- float tcentre = -halfb * invcyla;
- float zcentre = difz + (tg.z * tcentre);
- float3 tdif = -p_st;
- tdif.z += tcentre;
- float tdifz = dot(tdif, tg);
- float tb = 2 * (tdif.z - tg.z * (tdifz + gd * (tdifz * gd + r1)));
- float tc = dot(tdif, tdif) - tdifz * tdifz * (1 + gd * gd) - r1 * r1 - 2 * r1 * tdifz * gd;
- float td = tb * tb - 4 * cyla * tc;
- if (td < 0.0f) {
- tree++;
- level = tree & -tree;
- continue;
- }
- float rootd = sqrtf(td);
- float correction = (-tb - rootd) * 0.5f * invcyla;
- t = tcentre + correction;
-
- float3 dp_st = (3 * curve_coef[3] * i_st + 2 * curve_coef[2]) * i_st + curve_coef[1];
- if (dot(tg, dp_st) < 0)
- dp_st *= -1;
- float3 dp_en = (3 * curve_coef[3] * i_en + 2 * curve_coef[2]) * i_en + curve_coef[1];
- if (dot(tg, dp_en) < 0)
- dp_en *= -1;
-
- if (flags & CURVE_KN_BACKFACING &&
- (dot(dp_st, -p_st) + t * dp_st.z < 0 || dot(dp_en, p_en) - t * dp_en.z < 0 ||
- isect->t < t || t <= 0.0f)) {
- correction = (-tb + rootd) * 0.5f * invcyla;
- t = tcentre + correction;
+ /* Process one or two hits. */
+ bool recurse = false;
+ if (valid0) {
+ const int termDepth = unstable0 ? CURVE_NUM_BEZIER_SUBDIVISIONS_UNSTABLE :
+ CURVE_NUM_BEZIER_SUBDIVISIONS;
+ if (depth >= termDepth) {
+ found |= curve_intersect_iterative(
+ ray_dir, dt, curve, u_outer0, tp0.x, use_backfacing, isect);
}
-
- if (dot(dp_st, -p_st) + t * dp_st.z < 0 || dot(dp_en, p_en) - t * dp_en.z < 0 ||
- isect->t < t || t <= 0.0f) {
- tree++;
- level = tree & -tree;
- continue;
+ else {
+ recurse = true;
}
+ }
- float w = (zcentre + (tg.z * correction)) * invl;
- w = saturate(w);
- /* compute u on the curve segment */
- u = i_st * (1 - w) + i_en * w;
+ if (valid1 && (tp1.x + dt <= isect->t)) {
+ const int termDepth = unstable1 ? CURVE_NUM_BEZIER_SUBDIVISIONS_UNSTABLE :
+ CURVE_NUM_BEZIER_SUBDIVISIONS;
+ if (depth >= termDepth) {
+ found |= curve_intersect_iterative(
+ ray_dir, dt, curve, u_outer1, tp1.y, use_backfacing, isect);
+ }
+ else {
+ recurse = true;
+ }
}
- /* we found a new intersection */
-# ifdef __VISIBILITY_FLAG__
- /* visibility flag test. we do it here under the assumption
- * that most triangles are culled by node flags */
- if (kernel_tex_fetch(__prim_visibility, curveAddr) & visibility)
-# endif
- {
- /* record intersection */
- isect->t = t;
- isect->u = u;
- isect->v = gd;
- isect->prim = curveAddr;
- isect->object = object;
- isect->type = type;
- hit = true;
+ if (recurse) {
+ stack[depth].u0 = u0;
+ stack[depth].u1 = u1;
+ stack[depth].i = i + 1;
+ depth++;
+
+ u0 = vu0;
+ u1 = vu1;
+ i = -1;
}
+ }
- tree++;
- level = tree & -tree;
+ if (depth > 0) {
+ depth--;
+ u0 = stack[depth].u0;
+ u1 = stack[depth].u1;
+ i = stack[depth].i;
}
else {
- /* split the curve into two curves and process */
- level = level >> 1;
+ break;
}
}
- return hit;
+ return found;
+}
+
+/* Ribbons */
+
+ccl_device_inline bool cylinder_culling_test(const float2 p1, const float2 p2, const float r)
+{
+ /* Performs culling against a cylinder. */
+ const float2 dp = p2 - p1;
+ const float num = dp.x * p1.y - dp.y * p1.x;
+ const float den2 = dot(p2 - p1, p2 - p1);
+ return num * num <= r * r * den2;
+}
+
+/*! Intersects a ray with a quad with backface culling
+ * enabled. The quad v0,v1,v2,v3 is split into two triangles
+ * v0,v1,v3 and v2,v3,v1. The edge v1,v2 decides which of the two
+ * triangles gets intersected. */
+ccl_device_inline bool ribbon_intersect_quad(const float ray_tfar,
+ const float3 quad_v0,
+ const float3 quad_v1,
+ const float3 quad_v2,
+ const float3 quad_v3,
+ float *u_o,
+ float *v_o,
+ float *t_o)
+{
+ /* Calculate vertices relative to ray origin? */
+ const float3 O = make_float3(0.0f, 0.0f, 0.0f);
+ const float3 D = make_float3(0.0f, 0.0f, 1.0f);
+ const float3 va = quad_v0 - O;
+ const float3 vb = quad_v1 - O;
+ const float3 vc = quad_v2 - O;
+ const float3 vd = quad_v3 - O;
+
+ const float3 edb = vb - vd;
+ const float WW = dot(cross(vd, edb), D);
+ const float3 v0 = (WW <= 0.0f) ? va : vc;
+ const float3 v1 = (WW <= 0.0f) ? vb : vd;
+ const float3 v2 = (WW <= 0.0f) ? vd : vb;
+
+ /* Calculate edges? */
+ const float3 e0 = v2 - v0;
+ const float3 e1 = v0 - v1;
+
+ /* perform edge tests */
+ const float U = dot(cross(v0, e0), D);
+ const float V = dot(cross(v1, e1), D);
+ if (!(max(U, V) <= 0.0f)) {
+ return false;
+ }
+
+ /* Calculate geometry normal and denominator? */
+ const float3 Ng = cross(e1, e0);
+ const float den = dot(Ng, D);
+ const float rcpDen = 1.0f / den;
+
+ /* Perform depth test? */
+ const float t = rcpDen * dot(v0, Ng);
+ if (!(0.0f <= t && t <= ray_tfar)) {
+ return false;
+ }
+
+ /* Avoid division by 0? */
+ if (!(den != 0.0f)) {
+ return false;
+ }
+
+ /* Update hit information? */
+ *t_o = t;
+ *u_o = U * rcpDen;
+ *v_o = V * rcpDen;
+ *u_o = (WW <= 0.0f) ? *u_o : 1.0f - *u_o;
+ *v_o = (WW <= 0.0f) ? *v_o : 1.0f - *v_o;
+ return true;
+}
+
+ccl_device_inline void ribbon_ray_space(const float3 ray_dir, float3 ray_space[3])
+{
+ const float3 dx0 = make_float3(0, ray_dir.z, -ray_dir.y);
+ const float3 dx1 = make_float3(-ray_dir.z, 0, ray_dir.x);
+ ray_space[0] = normalize(dot(dx0, dx0) > dot(dx1, dx1) ? dx0 : dx1);
+ ray_space[1] = normalize(cross(ray_dir, ray_space[0]));
+ ray_space[2] = ray_dir;
+}
+
+ccl_device_inline float4 ribbon_to_ray_space(const float3 ray_space[3],
+ const float3 ray_org,
+ const float4 P4)
+{
+ float3 P = float4_to_float3(P4) - ray_org;
+ return make_float4(dot(ray_space[0], P), dot(ray_space[1], P), dot(ray_space[2], P), P4.w);
+}
+
+ccl_device_inline bool ribbon_intersect(const float3 ray_org,
+ const float3 ray_dir,
+ const float ray_tfar,
+ const int N,
+ float4 curve[4],
+ Intersection *isect)
+{
+ /* Transform control points into ray space. */
+ float3 ray_space[3];
+ ribbon_ray_space(ray_dir, ray_space);
+
+ curve[0] = ribbon_to_ray_space(ray_space, ray_org, curve[0]);
+ curve[1] = ribbon_to_ray_space(ray_space, ray_org, curve[1]);
+ curve[2] = ribbon_to_ray_space(ray_space, ray_org, curve[2]);
+ curve[3] = ribbon_to_ray_space(ray_space, ray_org, curve[3]);
+
+ const float4 mx = max(max(fabs(curve[0]), fabs(curve[1])), max(fabs(curve[2]), fabs(curve[3])));
+ const float eps = 4.0f * FLT_EPSILON * max(max(mx.x, mx.y), max(mx.z, mx.w));
+ const float step_size = 1.0f / (float)N;
+
+ /* Evaluate first point and radius scaled normal direction. */
+ float4 p0 = catmull_rom_basis_eval(curve, 0.0f);
+ float3 dp0dt = float4_to_float3(catmull_rom_basis_derivative(curve, 0.0f));
+ if (max3(fabs(dp0dt)) < eps) {
+ const float4 p1 = catmull_rom_basis_eval(curve, step_size);
+ dp0dt = float4_to_float3(p1 - p0);
+ }
+ float3 wn0 = normalize(make_float3(dp0dt.y, -dp0dt.x, 0.0f)) * p0.w;
+
+ /* Evaluate the bezier curve. */
+ for (int i = 0; i < N; i++) {
+ const float u = i * step_size;
+ const float4 p1 = catmull_rom_basis_eval(curve, u + step_size);
+ bool valid = cylinder_culling_test(
+ make_float2(p0.x, p0.y), make_float2(p1.x, p1.y), max(p0.w, p1.w));
+ if (!valid) {
+ continue;
+ }
+
+ /* Evaluate next point. */
+ float3 dp1dt = float4_to_float3(catmull_rom_basis_derivative(curve, u + step_size));
+ dp1dt = (max3(fabs(dp1dt)) < eps) ? float4_to_float3(p1 - p0) : dp1dt;
+ const float3 wn1 = normalize(make_float3(dp1dt.y, -dp1dt.x, 0.0f)) * p1.w;
+
+ /* Construct quad coordinates. */
+ const float3 lp0 = float4_to_float3(p0) + wn0;
+ const float3 lp1 = float4_to_float3(p1) + wn1;
+ const float3 up0 = float4_to_float3(p0) - wn0;
+ const float3 up1 = float4_to_float3(p1) - wn1;
+
+ /* Intersect quad. */
+ float vu, vv, vt;
+ bool valid0 = ribbon_intersect_quad(isect->t, lp0, lp1, up1, up0, &vu, &vv, &vt);
+
+ if (valid0) {
+ /* ignore self intersections */
+ const float avoidance_factor = 2.0f;
+ if (avoidance_factor != 0.0f) {
+ float r = mix(p0.w, p1.w, vu);
+ valid0 = vt > avoidance_factor * r;
+ }
+
+ if (valid0) {
+ vv = 2.0f * vv - 1.0f;
+
+ /* Record intersection. */
+ isect->t = vt;
+ isect->u = u + vu * step_size;
+ isect->v = vv;
+ return true;
+ }
+ }
+
+ p0 = p1;
+ wn0 = wn1;
+ }
+ return false;
}
ccl_device_forceinline bool curve_intersect(KernelGlobals *kg,
Intersection *isect,
- float3 P,
- float3 direction,
+ const float3 P,
+ const float3 dir,
uint visibility,
int object,
int curveAddr,
float time,
int type)
{
- /* define few macros to minimize code duplication for SSE */
-# ifndef __KERNEL_SSE2__
-# define len3_squared(x) len_squared(x)
-# define len3(x) len(x)
-# define dot3(x, y) dot(x, y)
-# endif
-
- const bool is_curve_primitive = (type & PRIMITIVE_CURVE);
+ const bool is_motion = (type & PRIMITIVE_ALL_MOTION);
-# ifndef __KERNEL_OPTIX__ /* see OptiX motion flag OPTIX_MOTION_FLAG_[START|END]_VANISH */
- if (!is_curve_primitive && kernel_data.bvh.use_bvh_steps) {
+# ifndef __KERNEL_OPTIX__ /* See OptiX motion flag OPTIX_MOTION_FLAG_[START|END]_VANISH */
+ if (is_motion && kernel_data.bvh.use_bvh_steps) {
const float2 prim_time = kernel_tex_fetch(__prim_time, curveAddr);
if (time < prim_time.x || time > prim_time.y) {
return false;
@@ -517,210 +639,63 @@ ccl_device_forceinline bool curve_intersect(KernelGlobals *kg,
# endif
int segment = PRIMITIVE_UNPACK_SEGMENT(type);
- /* curve Intersection check */
- int flags = kernel_data.curve.curveflags;
-
int prim = kernel_tex_fetch(__prim_index, curveAddr);
+
float4 v00 = kernel_tex_fetch(__curves, prim);
- int cnum = __float_as_int(v00.x);
- int k0 = cnum + segment;
+ int k0 = __float_as_int(v00.x) + segment;
int k1 = k0 + 1;
-# ifndef __KERNEL_SSE2__
- float4 P_curve[2];
+ int ka = max(k0 - 1, __float_as_int(v00.x));
+ int kb = min(k1 + 1, __float_as_int(v00.x) + __float_as_int(v00.y) - 1);
- if (is_curve_primitive) {
- P_curve[0] = kernel_tex_fetch(__curve_keys, k0);
- P_curve[1] = kernel_tex_fetch(__curve_keys, k1);
+ float4 curve[4];
+ if (!is_motion) {
+ curve[0] = kernel_tex_fetch(__curve_keys, ka);
+ curve[1] = kernel_tex_fetch(__curve_keys, k0);
+ curve[2] = kernel_tex_fetch(__curve_keys, k1);
+ curve[3] = kernel_tex_fetch(__curve_keys, kb);
}
else {
int fobject = (object == OBJECT_NONE) ? kernel_tex_fetch(__prim_object, curveAddr) : object;
- motion_curve_keys(kg, fobject, prim, time, k0, k1, P_curve);
- }
-
- float r1 = P_curve[0].w;
- float r2 = P_curve[1].w;
- float3 p1 = float4_to_float3(P_curve[0]);
- float3 p2 = float4_to_float3(P_curve[1]);
-
- /* minimum width extension */
- float3 dif = P - p1;
- float3 dif_second = P - p2;
-
- float3 p21_diff = p2 - p1;
- float3 sphere_dif1 = (dif + dif_second) * 0.5f;
- float3 dir = direction;
- float sphere_b_tmp = dot3(dir, sphere_dif1);
- float3 sphere_dif2 = sphere_dif1 - sphere_b_tmp * dir;
-# else
- ssef P_curve[2];
-
- if (is_curve_primitive) {
- P_curve[0] = load4f(&kg->__curve_keys.data[k0].x);
- P_curve[1] = load4f(&kg->__curve_keys.data[k1].x);
+ motion_curve_keys(kg, fobject, prim, time, ka, k0, k1, kb, curve);
}
- else {
- int fobject = (object == OBJECT_NONE) ? kernel_tex_fetch(__prim_object, curveAddr) : object;
- motion_curve_keys(kg, fobject, prim, time, k0, k1, (float4 *)&P_curve);
- }
-
- ssef r12 = shuffle<3, 3, 3, 3>(P_curve[0], P_curve[1]);
- const ssef vP = load4f(P);
- const ssef dif = vP - P_curve[0];
- const ssef dif_second = vP - P_curve[1];
- float r1 = extract<0>(r12), r2 = extract<0>(shuffle<2>(r12));
-
- const ssef p21_diff = P_curve[1] - P_curve[0];
- const ssef sphere_dif1 = (dif + dif_second) * 0.5f;
- const ssef dir = load4f(direction);
- const ssef sphere_b_tmp = dot3_splat(dir, sphere_dif1);
- const ssef sphere_dif2 = nmadd(sphere_b_tmp, dir, sphere_dif1);
-# endif
-
- float mr = max(r1, r2);
- float l = len3(p21_diff);
- float invl = 1.0f / l;
- float sp_r = mr + 0.5f * l;
- float sphere_b = dot3(dir, sphere_dif2);
- float sdisc = sphere_b * sphere_b - len3_squared(sphere_dif2) + sp_r * sp_r;
-
- if (sdisc < 0.0f)
- return false;
-
- /* obtain parameters and test midpoint distance for suitable modes */
-# ifndef __KERNEL_SSE2__
- float3 tg = p21_diff * invl;
-# else
- const ssef tg = p21_diff * invl;
-# endif
- float gd = (r2 - r1) * invl;
-
- float dirz = dot3(dir, tg);
- float difz = dot3(dif, tg);
-
- float a = 1.0f - (dirz * dirz * (1 + gd * gd));
-
- float halfb = dot3(dir, dif) - dirz * (difz + gd * (difz * gd + r1));
-
- float tcentre = -halfb / a;
- float zcentre = difz + (dirz * tcentre);
-
- if ((tcentre > isect->t) && !(flags & CURVE_KN_ACCURATE))
- return false;
- if ((zcentre < 0 || zcentre > l) && !(flags & CURVE_KN_ACCURATE) &&
- !(flags & CURVE_KN_INTERSECTCORRECTION))
+# ifdef __VISIBILITY_FLAG__
+ if (!(kernel_tex_fetch(__prim_visibility, curveAddr) & visibility)) {
return false;
-
- /* test minimum separation */
-# ifndef __KERNEL_SSE2__
- float3 cprod = cross(tg, dir);
- float cprod2sq = len3_squared(cross(tg, dif));
-# else
- const ssef cprod = cross(tg, dir);
- float cprod2sq = len3_squared(cross_zxy(tg, dif));
+ }
# endif
- float cprodsq = len3_squared(cprod);
- float distscaled = dot3(cprod, dif);
-
- if (cprodsq == 0)
- distscaled = cprod2sq;
- else
- distscaled = (distscaled * distscaled) / cprodsq;
-
- if (distscaled > mr * mr)
- return false;
- /* calculate true intersection */
-# ifndef __KERNEL_SSE2__
- float3 tdif = dif + tcentre * dir;
-# else
- const ssef tdif = madd(ssef(tcentre), dir, dif);
-# endif
- float tdifz = dot3(tdif, tg);
- float tdifma = tdifz * gd + r1;
- float tb = 2 * (dot3(dir, tdif) - dirz * (tdifz + gd * tdifma));
- float tc = dot3(tdif, tdif) - tdifz * tdifz - tdifma * tdifma;
- float td = tb * tb - 4 * a * tc;
+ if (type & (PRIMITIVE_CURVE_RIBBON | PRIMITIVE_MOTION_CURVE_RIBBON)) {
+ /* todo: adaptive number of subdivisions could help performance here. */
+ const int subdivisions = kernel_data.bvh.curve_subdivisions;
+ if (ribbon_intersect(P, dir, isect->t, subdivisions, curve, isect)) {
+ isect->prim = curveAddr;
+ isect->object = object;
+ isect->type = type;
+ return true;
+ }
- if (td < 0.0f)
return false;
-
- float rootd = 0.0f;
- float correction = 0.0f;
- if (flags & CURVE_KN_ACCURATE) {
- rootd = sqrtf(td);
- correction = ((-tb - rootd) / (2 * a));
}
-
- float t = tcentre + correction;
-
- if (t < isect->t) {
-
- if (flags & CURVE_KN_INTERSECTCORRECTION) {
- rootd = sqrtf(td);
- correction = ((-tb - rootd) / (2 * a));
- t = tcentre + correction;
- }
-
- float z = zcentre + (dirz * correction);
- // bool backface = false;
-
- if (flags & CURVE_KN_BACKFACING && (t < 0.0f || z < 0 || z > l)) {
- // backface = true;
- correction = ((-tb + rootd) / (2 * a));
- t = tcentre + correction;
- z = zcentre + (dirz * correction);
+ else {
+ if (curve_intersect_recursive(P, dir, curve, isect)) {
+ isect->prim = curveAddr;
+ isect->object = object;
+ isect->type = type;
+ return true;
}
- if (t > 0.0f && t < isect->t && z >= 0 && z <= l) {
-
- if (flags & CURVE_KN_ENCLOSEFILTER) {
- float enc_ratio = 1.01f;
- if ((difz > -r1 * enc_ratio) && (dot3(dif_second, tg) < r2 * enc_ratio)) {
- float a2 = 1.0f - (dirz * dirz * (1 + gd * gd * enc_ratio * enc_ratio));
- float c2 = dot3(dif, dif) - difz * difz * (1 + gd * gd * enc_ratio * enc_ratio) -
- r1 * r1 * enc_ratio * enc_ratio - 2 * r1 * difz * gd * enc_ratio;
- if (a2 * c2 < 0.0f)
- return false;
- }
- }
-
-# ifdef __VISIBILITY_FLAG__
- /* visibility flag test. we do it here under the assumption
- * that most triangles are culled by node flags */
- if (kernel_tex_fetch(__prim_visibility, curveAddr) & visibility)
-# endif
- {
- /* record intersection */
- isect->t = t;
- isect->u = z * invl;
- isect->v = gd;
- isect->prim = curveAddr;
- isect->object = object;
- isect->type = type;
-
- return true;
- }
- }
+ return false;
}
-
- return false;
-
-# ifndef __KERNEL_SSE2__
-# undef len3_squared
-# undef len3
-# undef dot3
-# endif
}
-ccl_device_inline float3 curve_refine(KernelGlobals *kg,
- ShaderData *sd,
- const Intersection *isect,
- const Ray *ray)
+ccl_device_inline void curve_shader_setup(KernelGlobals *kg,
+ ShaderData *sd,
+ const Intersection *isect,
+ const Ray *ray)
{
- int flag = kernel_data.curve.curveflags;
float t = isect->t;
float3 P = ray->P;
float3 D = ray->D;
@@ -743,118 +718,60 @@ ccl_device_inline float3 curve_refine(KernelGlobals *kg,
int k0 = __float_as_int(v00.x) + PRIMITIVE_UNPACK_SEGMENT(sd->type);
int k1 = k0 + 1;
- float3 tg;
+ int ka = max(k0 - 1, __float_as_int(v00.x));
+ int kb = min(k1 + 1, __float_as_int(v00.x) + __float_as_int(v00.y) - 1);
- if (flag & CURVE_KN_INTERPOLATE) {
- int ka = max(k0 - 1, __float_as_int(v00.x));
- int kb = min(k1 + 1, __float_as_int(v00.x) + __float_as_int(v00.y) - 1);
+ float4 P_curve[4];
- float4 P_curve[4];
+ if (!(sd->type & PRIMITIVE_ALL_MOTION)) {
+ P_curve[0] = kernel_tex_fetch(__curve_keys, ka);
+ P_curve[1] = kernel_tex_fetch(__curve_keys, k0);
+ P_curve[2] = kernel_tex_fetch(__curve_keys, k1);
+ P_curve[3] = kernel_tex_fetch(__curve_keys, kb);
+ }
+ else {
+ motion_curve_keys(kg, sd->object, sd->prim, sd->time, ka, k0, k1, kb, P_curve);
+ }
- if (sd->type & PRIMITIVE_CURVE) {
- P_curve[0] = kernel_tex_fetch(__curve_keys, ka);
- P_curve[1] = kernel_tex_fetch(__curve_keys, k0);
- P_curve[2] = kernel_tex_fetch(__curve_keys, k1);
- P_curve[3] = kernel_tex_fetch(__curve_keys, kb);
- }
- else {
- motion_cardinal_curve_keys(kg, sd->object, sd->prim, sd->time, ka, k0, k1, kb, P_curve);
- }
+ sd->u = isect->u;
+ sd->v = isect->v;
- float3 p[4];
- p[0] = float4_to_float3(P_curve[0]);
- p[1] = float4_to_float3(P_curve[1]);
- p[2] = float4_to_float3(P_curve[2]);
- p[3] = float4_to_float3(P_curve[3]);
+ P = P + D * t;
- P = P + D * t;
+ const float4 dPdu4 = catmull_rom_basis_derivative(P_curve, isect->u);
+ const float3 dPdu = float4_to_float3(dPdu4);
-# ifdef __UV__
- sd->u = isect->u;
- sd->v = 0.0f;
-# endif
+ if (sd->type & (PRIMITIVE_CURVE_RIBBON | PRIMITIVE_MOTION_CURVE_RIBBON)) {
+ /* Rounded smooth normals for ribbons, to approximate thick curve shape. */
+ const float3 tangent = normalize(dPdu);
+ const float3 bitangent = normalize(cross(tangent, -D));
+ const float sine = isect->v;
+ const float cosine = safe_sqrtf(1.0f - sine * sine);
- tg = normalize(curvetangent(isect->u, p[0], p[1], p[2], p[3]));
+ sd->N = normalize(sine * bitangent - cosine * normalize(cross(tangent, bitangent)));
+ sd->Ng = -D;
- if (kernel_data.curve.curveflags & CURVE_KN_RIBBONS) {
- sd->Ng = normalize(-(D - tg * (dot(tg, D))));
- }
- else {
-# ifdef __EMBREE__
- if (kernel_data.bvh.scene) {
- sd->Ng = normalize(isect->Ng);
- }
- else
+# if 0
+ /* This approximates the position and geometric normal of a thick curve too,
+ * but gives too many issues with wrong self intersections. */
+ const float dPdu_radius = dPdu4.w;
+ sd->Ng = sd->N;
+ P += sd->N * dPdu_radius;
# endif
- {
- /* direction from inside to surface of curve */
- float3 p_curr = curvepoint(isect->u, p[0], p[1], p[2], p[3]);
- sd->Ng = normalize(P - p_curr);
-
- /* adjustment for changing radius */
- float gd = isect->v;
-
- if (gd != 0.0f) {
- sd->Ng = sd->Ng - gd * tg;
- sd->Ng = normalize(sd->Ng);
- }
- }
- }
-
- /* todo: sometimes the normal is still so that this is detected as
- * backfacing even if cull backfaces is enabled */
-
- sd->N = sd->Ng;
}
else {
- float4 P_curve[2];
-
- if (sd->type & PRIMITIVE_CURVE) {
- P_curve[0] = kernel_tex_fetch(__curve_keys, k0);
- P_curve[1] = kernel_tex_fetch(__curve_keys, k1);
- }
- else {
- motion_curve_keys(kg, sd->object, sd->prim, sd->time, k0, k1, P_curve);
- }
-
- float l = 1.0f;
- tg = normalize_len(float4_to_float3(P_curve[1] - P_curve[0]), &l);
-
- P = P + D * t;
-
- float3 dif = P - float4_to_float3(P_curve[0]);
-
-# ifdef __UV__
- sd->u = dot(dif, tg) / l;
- sd->v = 0.0f;
-# endif
-
- if (flag & CURVE_KN_TRUETANGENTGNORMAL) {
- sd->Ng = -(D - tg * dot(tg, D));
- sd->Ng = normalize(sd->Ng);
- }
- else {
- float gd = isect->v;
-
- /* direction from inside to surface of curve */
- float denom = fmaxf(P_curve[0].w + sd->u * l * gd, 1e-8f);
- sd->Ng = (dif - tg * sd->u * l) / denom;
-
- /* adjustment for changing radius */
- if (gd != 0.0f) {
- sd->Ng = sd->Ng - gd * tg;
- }
-
- sd->Ng = normalize(sd->Ng);
- }
-
+ /* Thick curves, compute normal using direction from inside the curve.
+ * This could be optimized by recording the normal in the intersection,
+ * however for Optix this would go beyond the size of the payload. */
+ const float3 P_inside = float4_to_float3(catmull_rom_basis_eval(P_curve, isect->u));
+ sd->Ng = normalize(P - P_inside);
sd->N = sd->Ng;
}
# ifdef __DPDU__
/* dPdu/dPdv */
- sd->dPdu = tg;
- sd->dPdv = cross(tg, sd->Ng);
+ sd->dPdu = dPdu;
+ sd->dPdv = cross(dPdu, sd->Ng);
# endif
if (isect->object != OBJECT_NONE) {
@@ -867,7 +784,10 @@ ccl_device_inline float3 curve_refine(KernelGlobals *kg,
P = transform_point(&tfm, P);
}
- return P;
+ sd->P = P;
+
+ float4 curvedata = kernel_tex_fetch(__curves, sd->prim);
+ sd->shader = __float_as_int(curvedata.z);
}
#endif
diff --git a/intern/cycles/kernel/geom/geom_motion_curve.h b/intern/cycles/kernel/geom/geom_motion_curve.h
index 0e2a00e9d2e..0f66f4af755 100644
--- a/intern/cycles/kernel/geom/geom_motion_curve.h
+++ b/intern/cycles/kernel/geom/geom_motion_curve.h
@@ -50,14 +50,14 @@ ccl_device_inline int find_attribute_curve_motion(KernelGlobals *kg,
return (attr_map.y == ATTR_ELEMENT_NONE) ? (int)ATTR_STD_NOT_FOUND : (int)attr_map.z;
}
-ccl_device_inline void motion_curve_keys_for_step(KernelGlobals *kg,
- int offset,
- int numkeys,
- int numsteps,
- int step,
- int k0,
- int k1,
- float4 keys[2])
+ccl_device_inline void motion_curve_keys_for_step_linear(KernelGlobals *kg,
+ int offset,
+ int numkeys,
+ int numsteps,
+ int step,
+ int k0,
+ int k1,
+ float4 keys[2])
{
if (step == numsteps) {
/* center step: regular key location */
@@ -77,7 +77,7 @@ ccl_device_inline void motion_curve_keys_for_step(KernelGlobals *kg,
}
/* return 2 curve key locations */
-ccl_device_inline void motion_curve_keys(
+ccl_device_inline void motion_curve_keys_linear(
KernelGlobals *kg, int object, int prim, float time, int k0, int k1, float4 keys[2])
{
/* get motion info */
@@ -97,24 +97,24 @@ ccl_device_inline void motion_curve_keys(
/* fetch key coordinates */
float4 next_keys[2];
- motion_curve_keys_for_step(kg, offset, numkeys, numsteps, step, k0, k1, keys);
- motion_curve_keys_for_step(kg, offset, numkeys, numsteps, step + 1, k0, k1, next_keys);
+ motion_curve_keys_for_step_linear(kg, offset, numkeys, numsteps, step, k0, k1, keys);
+ motion_curve_keys_for_step_linear(kg, offset, numkeys, numsteps, step + 1, k0, k1, next_keys);
/* interpolate between steps */
keys[0] = (1.0f - t) * keys[0] + t * next_keys[0];
keys[1] = (1.0f - t) * keys[1] + t * next_keys[1];
}
-ccl_device_inline void motion_cardinal_curve_keys_for_step(KernelGlobals *kg,
- int offset,
- int numkeys,
- int numsteps,
- int step,
- int k0,
- int k1,
- int k2,
- int k3,
- float4 keys[4])
+ccl_device_inline void motion_curve_keys_for_step(KernelGlobals *kg,
+ int offset,
+ int numkeys,
+ int numsteps,
+ int step,
+ int k0,
+ int k1,
+ int k2,
+ int k3,
+ float4 keys[4])
{
if (step == numsteps) {
/* center step: regular key location */
@@ -138,15 +138,15 @@ ccl_device_inline void motion_cardinal_curve_keys_for_step(KernelGlobals *kg,
}
/* return 2 curve key locations */
-ccl_device_inline void motion_cardinal_curve_keys(KernelGlobals *kg,
- int object,
- int prim,
- float time,
- int k0,
- int k1,
- int k2,
- int k3,
- float4 keys[4])
+ccl_device_inline void motion_curve_keys(KernelGlobals *kg,
+ int object,
+ int prim,
+ float time,
+ int k0,
+ int k1,
+ int k2,
+ int k3,
+ float4 keys[4])
{
/* get motion info */
int numsteps, numkeys;
@@ -165,9 +165,8 @@ ccl_device_inline void motion_cardinal_curve_keys(KernelGlobals *kg,
/* fetch key coordinates */
float4 next_keys[4];
- motion_cardinal_curve_keys_for_step(kg, offset, numkeys, numsteps, step, k0, k1, k2, k3, keys);
- motion_cardinal_curve_keys_for_step(
- kg, offset, numkeys, numsteps, step + 1, k0, k1, k2, k3, next_keys);
+ motion_curve_keys_for_step(kg, offset, numkeys, numsteps, step, k0, k1, k2, k3, keys);
+ motion_curve_keys_for_step(kg, offset, numkeys, numsteps, step + 1, k0, k1, k2, k3, next_keys);
/* interpolate between steps */
keys[0] = (1.0f - t) * keys[0] + t * next_keys[0];
@@ -176,53 +175,6 @@ ccl_device_inline void motion_cardinal_curve_keys(KernelGlobals *kg,
keys[3] = (1.0f - t) * keys[3] + t * next_keys[3];
}
-# if defined(__KERNEL_AVX2__) && defined(__KERNEL_SSE__)
-/* Similar to above, but returns keys as pair of two AVX registers with each
- * holding two float4.
- */
-ccl_device_inline void motion_cardinal_curve_keys_avx(KernelGlobals *kg,
- int object,
- int prim,
- float time,
- int k0,
- int k1,
- int k2,
- int k3,
- avxf *out_keys_0_1,
- avxf *out_keys_2_3)
-{
- /* Get motion info. */
- int numsteps, numkeys;
- object_motion_info(kg, object, &numsteps, NULL, &numkeys);
-
- /* Figure out which steps we need to fetch and their interpolation factor. */
- int maxstep = numsteps * 2;
- int step = min((int)(time * maxstep), maxstep - 1);
- float t = time * maxstep - step;
-
- /* Find attribute. */
- AttributeElement elem;
- int offset = find_attribute_curve_motion(kg, object, ATTR_STD_MOTION_VERTEX_POSITION, &elem);
- kernel_assert(offset != ATTR_STD_NOT_FOUND);
-
- /* Fetch key coordinates. */
- float4 next_keys[4];
- float4 keys[4];
- motion_cardinal_curve_keys_for_step(kg, offset, numkeys, numsteps, step, k0, k1, k2, k3, keys);
- motion_cardinal_curve_keys_for_step(
- kg, offset, numkeys, numsteps, step + 1, k0, k1, k2, k3, next_keys);
-
- const avxf keys_0_1 = avxf(keys[0].m128, keys[1].m128);
- const avxf keys_2_3 = avxf(keys[2].m128, keys[3].m128);
- const avxf next_keys_0_1 = avxf(next_keys[0].m128, next_keys[1].m128);
- const avxf next_keys_2_3 = avxf(next_keys[2].m128, next_keys[3].m128);
-
- /* Interpolate between steps. */
- *out_keys_0_1 = (1.0f - t) * keys_0_1 + t * next_keys_0_1;
- *out_keys_2_3 = (1.0f - t) * keys_2_3 + t * next_keys_2_3;
-}
-# endif
-
#endif
CCL_NAMESPACE_END
diff --git a/intern/cycles/kernel/geom/geom_object.h b/intern/cycles/kernel/geom/geom_object.h
index 3aa68e1f84e..614e2e3b92b 100644
--- a/intern/cycles/kernel/geom/geom_object.h
+++ b/intern/cycles/kernel/geom/geom_object.h
@@ -411,25 +411,10 @@ ccl_device float3 particle_angular_velocity(KernelGlobals *kg, int particle)
ccl_device_inline float3 bvh_clamp_direction(float3 dir)
{
- /* clamp absolute values by exp2f(-80.0f) to avoid division by zero when calculating inverse
- * direction */
-#if defined(__KERNEL_SSE__) && defined(__KERNEL_SSE2__)
- const ssef oopes(8.271806E-25f, 8.271806E-25f, 8.271806E-25f, 0.0f);
- const ssef mask = _mm_cmpgt_ps(fabs(dir), oopes);
- const ssef signdir = signmsk(dir.m128) | oopes;
-# ifndef __KERNEL_AVX__
- ssef res = mask & ssef(dir);
- res = _mm_or_ps(res, _mm_andnot_ps(mask, signdir));
-# else
- ssef res = _mm_blendv_ps(signdir, dir, mask);
-# endif
- return float3(res);
-#else /* __KERNEL_SSE__ && __KERNEL_SSE2__ */
const float ooeps = 8.271806E-25f;
return make_float3((fabsf(dir.x) > ooeps) ? dir.x : copysignf(ooeps, dir.x),
(fabsf(dir.y) > ooeps) ? dir.y : copysignf(ooeps, dir.y),
(fabsf(dir.z) > ooeps) ? dir.z : copysignf(ooeps, dir.z));
-#endif /* __KERNEL_SSE__ && __KERNEL_SSE2__ */
}
ccl_device_inline float3 bvh_inverse_direction(float3 dir)
@@ -457,38 +442,6 @@ ccl_device_inline float bvh_instance_push(
return t;
}
-#ifdef __QBVH__
-/* Same as above, but optimized for QBVH scene intersection,
- * which needs to modify two max distances.
- *
- * TODO(sergey): Investigate if passing NULL instead of t1 gets optimized
- * so we can avoid having this duplication.
- */
-ccl_device_inline void qbvh_instance_push(KernelGlobals *kg,
- int object,
- const Ray *ray,
- float3 *P,
- float3 *dir,
- float3 *idir,
- float *t,
- float *t1)
-{
- Transform tfm = object_fetch_transform(kg, object, OBJECT_INVERSE_TRANSFORM);
-
- *P = transform_point(&tfm, ray->P);
-
- float len;
- *dir = bvh_clamp_direction(normalize_len(transform_direction(&tfm, ray->D), &len));
- *idir = bvh_inverse_direction(*dir);
-
- if (*t != FLT_MAX)
- *t *= len;
-
- if (*t1 != -FLT_MAX)
- *t1 *= len;
-}
-#endif
-
/* Transorm ray to exit static object in BVH */
ccl_device_inline float bvh_instance_pop(
@@ -551,39 +504,6 @@ ccl_device_inline float bvh_instance_motion_push(KernelGlobals *kg,
return t;
}
-# ifdef __QBVH__
-/* Same as above, but optimized for QBVH scene intersection,
- * which needs to modify two max distances.
- *
- * TODO(sergey): Investigate if passing NULL instead of t1 gets optimized
- * so we can avoid having this duplication.
- */
-ccl_device_inline void qbvh_instance_motion_push(KernelGlobals *kg,
- int object,
- const Ray *ray,
- float3 *P,
- float3 *dir,
- float3 *idir,
- float *t,
- float *t1,
- Transform *itfm)
-{
- object_fetch_transform_motion_test(kg, object, ray->time, itfm);
-
- *P = transform_point(itfm, ray->P);
-
- float len;
- *dir = bvh_clamp_direction(normalize_len(transform_direction(itfm, ray->D), &len));
- *idir = bvh_inverse_direction(*dir);
-
- if (*t != FLT_MAX)
- *t *= len;
-
- if (*t1 != -FLT_MAX)
- *t1 *= len;
-}
-# endif
-
/* Transorm ray to exit motion blurred object in BVH */
ccl_device_inline float bvh_instance_motion_pop(KernelGlobals *kg,
diff --git a/intern/cycles/kernel/geom/geom_primitive.h b/intern/cycles/kernel/geom/geom_primitive.h
index 9a91da79f58..997abf438d0 100644
--- a/intern/cycles/kernel/geom/geom_primitive.h
+++ b/intern/cycles/kernel/geom/geom_primitive.h
@@ -174,6 +174,11 @@ ccl_device_inline float4 primitive_attribute_float4(KernelGlobals *kg,
else
return subd_triangle_attribute_float4(kg, sd, desc, dx, dy);
}
+#ifdef __HAIR__
+ else if (sd->type & PRIMITIVE_ALL_CURVE) {
+ return curve_attribute_float4(kg, sd, desc, dx, dy);
+ }
+#endif
else {
if (dx)
*dx = make_float4(0.0f, 0.0f, 0.0f, 0.0f);
diff --git a/intern/cycles/kernel/geom/geom_triangle.h b/intern/cycles/kernel/geom/geom_triangle.h
index a2731bf2bd0..0278f3ade8e 100644
--- a/intern/cycles/kernel/geom/geom_triangle.h
+++ b/intern/cycles/kernel/geom/geom_triangle.h
@@ -312,12 +312,21 @@ ccl_device float4 triangle_attribute_float4(KernelGlobals *kg,
float4 *dx,
float4 *dy)
{
- if (desc.element == ATTR_ELEMENT_CORNER_BYTE) {
- int tri = desc.offset + sd->prim * 3;
-
- float4 f0 = color_uchar4_to_float4(kernel_tex_fetch(__attributes_uchar4, tri + 0));
- float4 f1 = color_uchar4_to_float4(kernel_tex_fetch(__attributes_uchar4, tri + 1));
- float4 f2 = color_uchar4_to_float4(kernel_tex_fetch(__attributes_uchar4, tri + 2));
+ if (desc.element == ATTR_ELEMENT_CORNER_BYTE || desc.element == ATTR_ELEMENT_VERTEX) {
+ float4 f0, f1, f2;
+
+ if (desc.element == ATTR_ELEMENT_CORNER_BYTE) {
+ int tri = desc.offset + sd->prim * 3;
+ f0 = color_uchar4_to_float4(kernel_tex_fetch(__attributes_uchar4, tri + 0));
+ f1 = color_uchar4_to_float4(kernel_tex_fetch(__attributes_uchar4, tri + 1));
+ f2 = color_uchar4_to_float4(kernel_tex_fetch(__attributes_uchar4, tri + 2));
+ }
+ else {
+ uint4 tri_vindex = kernel_tex_fetch(__tri_vindex, sd->prim);
+ f0 = kernel_tex_fetch(__attributes_float3, desc.offset + tri_vindex.x);
+ f1 = kernel_tex_fetch(__attributes_float3, desc.offset + tri_vindex.y);
+ f2 = kernel_tex_fetch(__attributes_float3, desc.offset + tri_vindex.z);
+ }
#ifdef __RAY_DIFFERENTIALS__
if (dx)
diff --git a/intern/cycles/kernel/geom/geom_triangle_intersect.h b/intern/cycles/kernel/geom/geom_triangle_intersect.h
index 6604806f73b..b0cce274b94 100644
--- a/intern/cycles/kernel/geom/geom_triangle_intersect.h
+++ b/intern/cycles/kernel/geom/geom_triangle_intersect.h
@@ -71,433 +71,6 @@ ccl_device_inline bool triangle_intersect(KernelGlobals *kg,
return false;
}
-#ifdef __KERNEL_AVX2__
-# define cross256(A, B, C, D) _mm256_fmsub_ps(A, B, _mm256_mul_ps(C, D))
-ccl_device_inline int ray_triangle_intersect8(KernelGlobals *kg,
- float3 ray_P,
- float3 ray_dir,
- Intersection **isect,
- uint visibility,
- int object,
- __m256 *triA,
- __m256 *triB,
- __m256 *triC,
- int prim_addr,
- int prim_num,
- uint *num_hits,
- uint max_hits,
- int *num_hits_in_instance,
- float isect_t)
-{
-
- const unsigned char prim_num_mask = (1 << prim_num) - 1;
-
- const __m256i zero256 = _mm256_setzero_si256();
-
- const __m256 Px256 = _mm256_set1_ps(ray_P.x);
- const __m256 Py256 = _mm256_set1_ps(ray_P.y);
- const __m256 Pz256 = _mm256_set1_ps(ray_P.z);
-
- const __m256 dirx256 = _mm256_set1_ps(ray_dir.x);
- const __m256 diry256 = _mm256_set1_ps(ray_dir.y);
- const __m256 dirz256 = _mm256_set1_ps(ray_dir.z);
-
- /* Calculate vertices relative to ray origin. */
- __m256 v0_x_256 = _mm256_sub_ps(triC[0], Px256);
- __m256 v0_y_256 = _mm256_sub_ps(triC[1], Py256);
- __m256 v0_z_256 = _mm256_sub_ps(triC[2], Pz256);
-
- __m256 v1_x_256 = _mm256_sub_ps(triA[0], Px256);
- __m256 v1_y_256 = _mm256_sub_ps(triA[1], Py256);
- __m256 v1_z_256 = _mm256_sub_ps(triA[2], Pz256);
-
- __m256 v2_x_256 = _mm256_sub_ps(triB[0], Px256);
- __m256 v2_y_256 = _mm256_sub_ps(triB[1], Py256);
- __m256 v2_z_256 = _mm256_sub_ps(triB[2], Pz256);
-
- __m256 v0_v1_x_256 = _mm256_add_ps(v0_x_256, v1_x_256);
- __m256 v0_v1_y_256 = _mm256_add_ps(v0_y_256, v1_y_256);
- __m256 v0_v1_z_256 = _mm256_add_ps(v0_z_256, v1_z_256);
-
- __m256 v0_v2_x_256 = _mm256_add_ps(v0_x_256, v2_x_256);
- __m256 v0_v2_y_256 = _mm256_add_ps(v0_y_256, v2_y_256);
- __m256 v0_v2_z_256 = _mm256_add_ps(v0_z_256, v2_z_256);
-
- __m256 v1_v2_x_256 = _mm256_add_ps(v1_x_256, v2_x_256);
- __m256 v1_v2_y_256 = _mm256_add_ps(v1_y_256, v2_y_256);
- __m256 v1_v2_z_256 = _mm256_add_ps(v1_z_256, v2_z_256);
-
- /* Calculate triangle edges. */
- __m256 e0_x_256 = _mm256_sub_ps(v2_x_256, v0_x_256);
- __m256 e0_y_256 = _mm256_sub_ps(v2_y_256, v0_y_256);
- __m256 e0_z_256 = _mm256_sub_ps(v2_z_256, v0_z_256);
-
- __m256 e1_x_256 = _mm256_sub_ps(v0_x_256, v1_x_256);
- __m256 e1_y_256 = _mm256_sub_ps(v0_y_256, v1_y_256);
- __m256 e1_z_256 = _mm256_sub_ps(v0_z_256, v1_z_256);
-
- __m256 e2_x_256 = _mm256_sub_ps(v1_x_256, v2_x_256);
- __m256 e2_y_256 = _mm256_sub_ps(v1_y_256, v2_y_256);
- __m256 e2_z_256 = _mm256_sub_ps(v1_z_256, v2_z_256);
-
- /* Perform edge tests. */
- /* cross (AyBz - AzBy, AzBx -AxBz, AxBy - AyBx) */
- __m256 U_x_256 = cross256(v0_v2_y_256, e0_z_256, v0_v2_z_256, e0_y_256);
- __m256 U_y_256 = cross256(v0_v2_z_256, e0_x_256, v0_v2_x_256, e0_z_256);
- __m256 U_z_256 = cross256(v0_v2_x_256, e0_y_256, v0_v2_y_256, e0_x_256);
- /* vertical dot */
- __m256 U_256 = _mm256_mul_ps(U_x_256, dirx256);
- U_256 = _mm256_fmadd_ps(U_y_256, diry256, U_256);
- U_256 = _mm256_fmadd_ps(U_z_256, dirz256, U_256);
-
- __m256 V_x_256 = cross256(v0_v1_y_256, e1_z_256, v0_v1_z_256, e1_y_256);
- __m256 V_y_256 = cross256(v0_v1_z_256, e1_x_256, v0_v1_x_256, e1_z_256);
- __m256 V_z_256 = cross256(v0_v1_x_256, e1_y_256, v0_v1_y_256, e1_x_256);
- /* vertical dot */
- __m256 V_256 = _mm256_mul_ps(V_x_256, dirx256);
- V_256 = _mm256_fmadd_ps(V_y_256, diry256, V_256);
- V_256 = _mm256_fmadd_ps(V_z_256, dirz256, V_256);
-
- __m256 W_x_256 = cross256(v1_v2_y_256, e2_z_256, v1_v2_z_256, e2_y_256);
- __m256 W_y_256 = cross256(v1_v2_z_256, e2_x_256, v1_v2_x_256, e2_z_256);
- __m256 W_z_256 = cross256(v1_v2_x_256, e2_y_256, v1_v2_y_256, e2_x_256);
- /* vertical dot */
- __m256 W_256 = _mm256_mul_ps(W_x_256, dirx256);
- W_256 = _mm256_fmadd_ps(W_y_256, diry256, W_256);
- W_256 = _mm256_fmadd_ps(W_z_256, dirz256, W_256);
-
- __m256i U_256_1 = _mm256_srli_epi32(_mm256_castps_si256(U_256), 31);
- __m256i V_256_1 = _mm256_srli_epi32(_mm256_castps_si256(V_256), 31);
- __m256i W_256_1 = _mm256_srli_epi32(_mm256_castps_si256(W_256), 31);
- __m256i UVW_256_1 = _mm256_add_epi32(_mm256_add_epi32(U_256_1, V_256_1), W_256_1);
-
- const __m256i one256 = _mm256_set1_epi32(1);
- const __m256i two256 = _mm256_set1_epi32(2);
-
- __m256i mask_minmaxUVW_256 = _mm256_or_si256(_mm256_cmpeq_epi32(one256, UVW_256_1),
- _mm256_cmpeq_epi32(two256, UVW_256_1));
-
- unsigned char mask_minmaxUVW_pos = _mm256_movemask_ps(_mm256_castsi256_ps(mask_minmaxUVW_256));
- if ((mask_minmaxUVW_pos & prim_num_mask) == prim_num_mask) { // all bits set
- return false;
- }
-
- /* Calculate geometry normal and denominator. */
- __m256 Ng1_x_256 = cross256(e1_y_256, e0_z_256, e1_z_256, e0_y_256);
- __m256 Ng1_y_256 = cross256(e1_z_256, e0_x_256, e1_x_256, e0_z_256);
- __m256 Ng1_z_256 = cross256(e1_x_256, e0_y_256, e1_y_256, e0_x_256);
-
- Ng1_x_256 = _mm256_add_ps(Ng1_x_256, Ng1_x_256);
- Ng1_y_256 = _mm256_add_ps(Ng1_y_256, Ng1_y_256);
- Ng1_z_256 = _mm256_add_ps(Ng1_z_256, Ng1_z_256);
-
- /* vertical dot */
- __m256 den_256 = _mm256_mul_ps(Ng1_x_256, dirx256);
- den_256 = _mm256_fmadd_ps(Ng1_y_256, diry256, den_256);
- den_256 = _mm256_fmadd_ps(Ng1_z_256, dirz256, den_256);
-
- /* Perform depth test. */
- __m256 T_256 = _mm256_mul_ps(Ng1_x_256, v0_x_256);
- T_256 = _mm256_fmadd_ps(Ng1_y_256, v0_y_256, T_256);
- T_256 = _mm256_fmadd_ps(Ng1_z_256, v0_z_256, T_256);
-
- const __m256i c0x80000000 = _mm256_set1_epi32(0x80000000);
- __m256i sign_den_256 = _mm256_and_si256(_mm256_castps_si256(den_256), c0x80000000);
-
- __m256 sign_T_256 = _mm256_castsi256_ps(
- _mm256_xor_si256(_mm256_castps_si256(T_256), sign_den_256));
-
- unsigned char mask_sign_T = _mm256_movemask_ps(sign_T_256);
- if (((mask_minmaxUVW_pos | mask_sign_T) & prim_num_mask) == prim_num_mask) {
- return false;
- }
-
- __m256 xor_signmask_256 = _mm256_castsi256_ps(
- _mm256_xor_si256(_mm256_castps_si256(den_256), sign_den_256));
-
- ccl_align(32) float den8[8], U8[8], V8[8], T8[8], sign_T8[8], xor_signmask8[8];
- ccl_align(32) unsigned int mask_minmaxUVW8[8];
-
- if (visibility == PATH_RAY_SHADOW_OPAQUE) {
- __m256i mask_final_256 = _mm256_cmpeq_epi32(mask_minmaxUVW_256, zero256);
- __m256i maskden256 = _mm256_cmpeq_epi32(_mm256_castps_si256(den_256), zero256);
- __m256i mask0 = _mm256_cmpgt_epi32(zero256, _mm256_castps_si256(sign_T_256));
- __m256 rayt_256 = _mm256_set1_ps((*isect)->t);
- __m256i mask1 = _mm256_cmpgt_epi32(
- _mm256_castps_si256(sign_T_256),
- _mm256_castps_si256(_mm256_mul_ps(
- _mm256_castsi256_ps(_mm256_xor_si256(_mm256_castps_si256(den_256), sign_den_256)),
- rayt_256)));
- mask0 = _mm256_or_si256(mask1, mask0);
- mask_final_256 = _mm256_andnot_si256(mask0, mask_final_256); //(~mask_minmaxUVW_pos) &(~mask)
- mask_final_256 = _mm256_andnot_si256(
- maskden256, mask_final_256); //(~mask_minmaxUVW_pos) &(~mask) & (~maskden)
- int mask_final = _mm256_movemask_ps(_mm256_castsi256_ps(mask_final_256));
- if ((mask_final & prim_num_mask) == 0) {
- return false;
- }
- while (mask_final != 0) {
- const int i = __bscf(mask_final);
- if (i >= prim_num) {
- return false;
- }
-# ifdef __VISIBILITY_FLAG__
- if ((kernel_tex_fetch(__prim_visibility, (prim_addr + i)) & visibility) == 0) {
- continue;
- }
-# endif
- __m256 inv_den_256 = _mm256_rcp_ps(den_256);
- U_256 = _mm256_mul_ps(U_256, inv_den_256);
- V_256 = _mm256_mul_ps(V_256, inv_den_256);
- T_256 = _mm256_mul_ps(T_256, inv_den_256);
- _mm256_store_ps(U8, U_256);
- _mm256_store_ps(V8, V_256);
- _mm256_store_ps(T8, T_256);
- (*isect)->u = U8[i];
- (*isect)->v = V8[i];
- (*isect)->t = T8[i];
- (*isect)->prim = (prim_addr + i);
- (*isect)->object = object;
- (*isect)->type = PRIMITIVE_TRIANGLE;
- return true;
- }
- return false;
- }
- else {
- _mm256_store_ps(den8, den_256);
- _mm256_store_ps(U8, U_256);
- _mm256_store_ps(V8, V_256);
- _mm256_store_ps(T8, T_256);
-
- _mm256_store_ps(sign_T8, sign_T_256);
- _mm256_store_ps(xor_signmask8, xor_signmask_256);
- _mm256_store_si256((__m256i *)mask_minmaxUVW8, mask_minmaxUVW_256);
-
- int ret = false;
-
- if (visibility == PATH_RAY_SHADOW) {
- for (int i = 0; i < prim_num; i++) {
- if (mask_minmaxUVW8[i]) {
- continue;
- }
-# ifdef __VISIBILITY_FLAG__
- if ((kernel_tex_fetch(__prim_visibility, (prim_addr + i)) & visibility) == 0) {
- continue;
- }
-# endif
- if ((sign_T8[i] < 0.0f) || (sign_T8[i] > (*isect)->t * xor_signmask8[i])) {
- continue;
- }
- if (!den8[i]) {
- continue;
- }
- const float inv_den = 1.0f / den8[i];
- (*isect)->u = U8[i] * inv_den;
- (*isect)->v = V8[i] * inv_den;
- (*isect)->t = T8[i] * inv_den;
- (*isect)->prim = (prim_addr + i);
- (*isect)->object = object;
- (*isect)->type = PRIMITIVE_TRIANGLE;
- const int prim = kernel_tex_fetch(__prim_index, (*isect)->prim);
- int shader = 0;
-# ifdef __HAIR__
- if (kernel_tex_fetch(__prim_type, (*isect)->prim) & PRIMITIVE_ALL_TRIANGLE)
-# endif
- {
- shader = kernel_tex_fetch(__tri_shader, prim);
- }
-# ifdef __HAIR__
- else {
- float4 str = kernel_tex_fetch(__curves, prim);
- shader = __float_as_int(str.z);
- }
-# endif
- const int flag = kernel_tex_fetch(__shaders, (shader & SHADER_MASK)).flags;
- /* If no transparent shadows, all light is blocked. */
- if (!(flag & SD_HAS_TRANSPARENT_SHADOW)) {
- return 2;
- }
- /* If maximum number of hits reached, block all light. */
- else if (num_hits == NULL || *num_hits == max_hits) {
- return 2;
- }
- /* Move on to next entry in intersections array. */
- ret = true;
- (*isect)++;
- (*num_hits)++;
- (*num_hits_in_instance)++;
- (*isect)->t = isect_t;
- }
- }
- else {
- for (int i = 0; i < prim_num; i++) {
- if (mask_minmaxUVW8[i]) {
- continue;
- }
-# ifdef __VISIBILITY_FLAG__
- if ((kernel_tex_fetch(__prim_visibility, (prim_addr + i)) & visibility) == 0) {
- continue;
- }
-# endif
- if ((sign_T8[i] < 0.0f) || (sign_T8[i] > (*isect)->t * xor_signmask8[i])) {
- continue;
- }
- if (!den8[i]) {
- continue;
- }
- const float inv_den = 1.0f / den8[i];
- (*isect)->u = U8[i] * inv_den;
- (*isect)->v = V8[i] * inv_den;
- (*isect)->t = T8[i] * inv_den;
- (*isect)->prim = (prim_addr + i);
- (*isect)->object = object;
- (*isect)->type = PRIMITIVE_TRIANGLE;
- ret = true;
- }
- }
- return ret;
- }
-}
-
-ccl_device_inline int triangle_intersect8(KernelGlobals *kg,
- Intersection **isect,
- float3 P,
- float3 dir,
- uint visibility,
- int object,
- int prim_addr,
- int prim_num,
- uint *num_hits,
- uint max_hits,
- int *num_hits_in_instance,
- float isect_t)
-{
- __m128 tri_a[8], tri_b[8], tri_c[8];
- __m256 tritmp[12], tri[12];
- __m256 triA[3], triB[3], triC[3];
-
- int i, r;
-
- uint tri_vindex = kernel_tex_fetch(__prim_tri_index, prim_addr);
- for (i = 0; i < prim_num; i++) {
- tri_a[i] = *(__m128 *)&kg->__prim_tri_verts.data[tri_vindex++];
- tri_b[i] = *(__m128 *)&kg->__prim_tri_verts.data[tri_vindex++];
- tri_c[i] = *(__m128 *)&kg->__prim_tri_verts.data[tri_vindex++];
- }
- // create 9 or 12 placeholders
- tri[0] = _mm256_castps128_ps256(tri_a[0]); //_mm256_zextps128_ps256
- tri[1] = _mm256_castps128_ps256(tri_b[0]); //_mm256_zextps128_ps256
- tri[2] = _mm256_castps128_ps256(tri_c[0]); //_mm256_zextps128_ps256
-
- tri[3] = _mm256_castps128_ps256(tri_a[1]); //_mm256_zextps128_ps256
- tri[4] = _mm256_castps128_ps256(tri_b[1]); //_mm256_zextps128_ps256
- tri[5] = _mm256_castps128_ps256(tri_c[1]); //_mm256_zextps128_ps256
-
- tri[6] = _mm256_castps128_ps256(tri_a[2]); //_mm256_zextps128_ps256
- tri[7] = _mm256_castps128_ps256(tri_b[2]); //_mm256_zextps128_ps256
- tri[8] = _mm256_castps128_ps256(tri_c[2]); //_mm256_zextps128_ps256
-
- if (prim_num > 3) {
- tri[9] = _mm256_castps128_ps256(tri_a[3]); //_mm256_zextps128_ps256
- tri[10] = _mm256_castps128_ps256(tri_b[3]); //_mm256_zextps128_ps256
- tri[11] = _mm256_castps128_ps256(tri_c[3]); //_mm256_zextps128_ps256
- }
-
- for (i = 4, r = 0; i < prim_num; i++, r += 3) {
- tri[r] = _mm256_insertf128_ps(tri[r], tri_a[i], 1);
- tri[r + 1] = _mm256_insertf128_ps(tri[r + 1], tri_b[i], 1);
- tri[r + 2] = _mm256_insertf128_ps(tri[r + 2], tri_c[i], 1);
- }
-
- //------------------------------------------------
- // 0! Xa0 Ya0 Za0 1 Xa4 Ya4 Za4 1
- // 1! Xb0 Yb0 Zb0 1 Xb4 Yb4 Zb4 1
- // 2! Xc0 Yc0 Zc0 1 Xc4 Yc4 Zc4 1
-
- // 3! Xa1 Ya1 Za1 1 Xa5 Ya5 Za5 1
- // 4! Xb1 Yb1 Zb1 1 Xb5 Yb5 Zb5 1
- // 5! Xc1 Yc1 Zc1 1 Xc5 Yc5 Zc5 1
-
- // 6! Xa2 Ya2 Za2 1 Xa6 Ya6 Za6 1
- // 7! Xb2 Yb2 Zb2 1 Xb6 Yb6 Zb6 1
- // 8! Xc2 Yc2 Zc2 1 Xc6 Yc6 Zc6 1
-
- // 9! Xa3 Ya3 Za3 1 Xa7 Ya7 Za7 1
- // 10! Xb3 Yb3 Zb3 1 Xb7 Yb7 Zb7 1
- // 11! Xc3 Yc3 Zc3 1 Xc7 Yc7 Zc7 1
-
- //"transpose"
- tritmp[0] = _mm256_unpacklo_ps(tri[0], tri[3]); // 0! Xa0 Xa1 Ya0 Ya1 Xa4 Xa5 Ya4 Ya5
- tritmp[1] = _mm256_unpackhi_ps(tri[0], tri[3]); // 1! Za0 Za1 1 1 Za4 Za5 1 1
-
- tritmp[2] = _mm256_unpacklo_ps(tri[6], tri[9]); // 2! Xa2 Xa3 Ya2 Ya3 Xa6 Xa7 Ya6 Ya7
- tritmp[3] = _mm256_unpackhi_ps(tri[6], tri[9]); // 3! Za2 Za3 1 1 Za6 Za7 1 1
-
- tritmp[4] = _mm256_unpacklo_ps(tri[1], tri[4]); // 4! Xb0 Xb1 Yb0 Yb1 Xb4 Xb5 Yb4 Yb5
- tritmp[5] = _mm256_unpackhi_ps(tri[1], tri[4]); // 5! Zb0 Zb1 1 1 Zb4 Zb5 1 1
-
- tritmp[6] = _mm256_unpacklo_ps(tri[7], tri[10]); // 6! Xb2 Xb3 Yb2 Yb3 Xb6 Xb7 Yb6 Yb7
- tritmp[7] = _mm256_unpackhi_ps(tri[7], tri[10]); // 7! Zb2 Zb3 1 1 Zb6 Zb7 1 1
-
- tritmp[8] = _mm256_unpacklo_ps(tri[2], tri[5]); // 8! Xc0 Xc1 Yc0 Yc1 Xc4 Xc5 Yc4 Yc5
- tritmp[9] = _mm256_unpackhi_ps(tri[2], tri[5]); // 9! Zc0 Zc1 1 1 Zc4 Zc5 1 1
-
- tritmp[10] = _mm256_unpacklo_ps(tri[8], tri[11]); // 10! Xc2 Xc3 Yc2 Yc3 Xc6 Xc7 Yc6 Yc7
- tritmp[11] = _mm256_unpackhi_ps(tri[8], tri[11]); // 11! Zc2 Zc3 1 1 Zc6 Zc7 1 1
-
- /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
- triA[0] = _mm256_castpd_ps(
- _mm256_unpacklo_pd(_mm256_castps_pd(tritmp[0]),
- _mm256_castps_pd(tritmp[2]))); // Xa0 Xa1 Xa2 Xa3 Xa4 Xa5 Xa6 Xa7
- triA[1] = _mm256_castpd_ps(
- _mm256_unpackhi_pd(_mm256_castps_pd(tritmp[0]),
- _mm256_castps_pd(tritmp[2]))); // Ya0 Ya1 Ya2 Ya3 Ya4 Ya5 Ya6 Ya7
- triA[2] = _mm256_castpd_ps(
- _mm256_unpacklo_pd(_mm256_castps_pd(tritmp[1]),
- _mm256_castps_pd(tritmp[3]))); // Za0 Za1 Za2 Za3 Za4 Za5 Za6 Za7
-
- triB[0] = _mm256_castpd_ps(
- _mm256_unpacklo_pd(_mm256_castps_pd(tritmp[4]),
- _mm256_castps_pd(tritmp[6]))); // Xb0 Xb1 Xb2 Xb3 Xb4 Xb5 Xb5 Xb7
- triB[1] = _mm256_castpd_ps(
- _mm256_unpackhi_pd(_mm256_castps_pd(tritmp[4]),
- _mm256_castps_pd(tritmp[6]))); // Yb0 Yb1 Yb2 Yb3 Yb4 Yb5 Yb5 Yb7
- triB[2] = _mm256_castpd_ps(
- _mm256_unpacklo_pd(_mm256_castps_pd(tritmp[5]),
- _mm256_castps_pd(tritmp[7]))); // Zb0 Zb1 Zb2 Zb3 Zb4 Zb5 Zb5 Zb7
-
- triC[0] = _mm256_castpd_ps(
- _mm256_unpacklo_pd(_mm256_castps_pd(tritmp[8]),
- _mm256_castps_pd(tritmp[10]))); // Xc0 Xc1 Xc2 Xc3 Xc4 Xc5 Xc6 Xc7
- triC[1] = _mm256_castpd_ps(
- _mm256_unpackhi_pd(_mm256_castps_pd(tritmp[8]),
- _mm256_castps_pd(tritmp[10]))); // Yc0 Yc1 Yc2 Yc3 Yc4 Yc5 Yc6 Yc7
- triC[2] = _mm256_castpd_ps(
- _mm256_unpacklo_pd(_mm256_castps_pd(tritmp[9]),
- _mm256_castps_pd(tritmp[11]))); // Zc0 Zc1 Zc2 Zc3 Zc4 Zc5 Zc6 Zc7
-
- /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
-
- int result = ray_triangle_intersect8(kg,
- P,
- dir,
- isect,
- visibility,
- object,
- triA,
- triB,
- triC,
- prim_addr,
- prim_num,
- num_hits,
- max_hits,
- num_hits_in_instance,
- isect_t);
- return result;
-}
-
-#endif /* __KERNEL_AVX2__ */
-
/* Special ray intersection routines for subsurface scattering. In that case we
* only want to intersect with primitives in the same object, and if case of
* multiple hits we pick a single random primitive as the intersection point.
diff --git a/intern/cycles/kernel/kernel_emission.h b/intern/cycles/kernel/kernel_emission.h
index 71b176a0a8f..4ac07d86dda 100644
--- a/intern/cycles/kernel/kernel_emission.h
+++ b/intern/cycles/kernel/kernel_emission.h
@@ -326,9 +326,7 @@ ccl_device_noinline_cpu float3 indirect_background(KernelGlobals *kg,
/* Background MIS weights. */
# ifdef __BACKGROUND_MIS__
/* Check if background light exists or if we should skip pdf. */
- int res_x = kernel_data.integrator.pdf_background_res_x;
-
- if (!(state->flag & PATH_RAY_MIS_SKIP) && res_x) {
+ if (!(state->flag & PATH_RAY_MIS_SKIP) && kernel_data.background.use_mis) {
/* multiple importance sampling, get background light pdf for ray
* direction, and compute weight with respect to BSDF pdf */
float pdf = background_light_pdf(kg, ray->P, ray->D);
diff --git a/intern/cycles/kernel/kernel_light.h b/intern/cycles/kernel/kernel_light.h
index 04472212d0c..138b90373a6 100644
--- a/intern/cycles/kernel/kernel_light.h
+++ b/intern/cycles/kernel/kernel_light.h
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "kernel_light_background.h"
+
CCL_NAMESPACE_BEGIN
/* Light Sample result */
@@ -33,500 +35,6 @@ typedef struct LightSample {
LightType type; /* type of light */
} LightSample;
-/* Area light sampling */
-
-/* Uses the following paper:
- *
- * Carlos Urena et al.
- * An Area-Preserving Parametrization for Spherical Rectangles.
- *
- * https://www.solidangle.com/research/egsr2013_spherical_rectangle.pdf
- *
- * Note: light_p is modified when sample_coord is true.
- */
-ccl_device_inline float rect_light_sample(float3 P,
- float3 *light_p,
- float3 axisu,
- float3 axisv,
- float randu,
- float randv,
- bool sample_coord)
-{
- /* In our name system we're using P for the center,
- * which is o in the paper.
- */
-
- float3 corner = *light_p - axisu * 0.5f - axisv * 0.5f;
- float axisu_len, axisv_len;
- /* Compute local reference system R. */
- float3 x = normalize_len(axisu, &axisu_len);
- float3 y = normalize_len(axisv, &axisv_len);
- float3 z = cross(x, y);
- /* Compute rectangle coords in local reference system. */
- float3 dir = corner - P;
- float z0 = dot(dir, z);
- /* Flip 'z' to make it point against Q. */
- if (z0 > 0.0f) {
- z *= -1.0f;
- z0 *= -1.0f;
- }
- float x0 = dot(dir, x);
- float y0 = dot(dir, y);
- float x1 = x0 + axisu_len;
- float y1 = y0 + axisv_len;
- /* Compute internal angles (gamma_i). */
- float4 diff = make_float4(x0, y1, x1, y0) - make_float4(x1, y0, x0, y1);
- float4 nz = make_float4(y0, x1, y1, x0) * diff;
- nz = nz / sqrt(z0 * z0 * diff * diff + nz * nz);
- float g0 = safe_acosf(-nz.x * nz.y);
- float g1 = safe_acosf(-nz.y * nz.z);
- float g2 = safe_acosf(-nz.z * nz.w);
- float g3 = safe_acosf(-nz.w * nz.x);
- /* Compute predefined constants. */
- float b0 = nz.x;
- float b1 = nz.z;
- float b0sq = b0 * b0;
- float k = M_2PI_F - g2 - g3;
- /* Compute solid angle from internal angles. */
- float S = g0 + g1 - k;
-
- if (sample_coord) {
- /* Compute cu. */
- float au = randu * S + k;
- float fu = (cosf(au) * b0 - b1) / sinf(au);
- float cu = 1.0f / sqrtf(fu * fu + b0sq) * (fu > 0.0f ? 1.0f : -1.0f);
- cu = clamp(cu, -1.0f, 1.0f);
- /* Compute xu. */
- float xu = -(cu * z0) / max(sqrtf(1.0f - cu * cu), 1e-7f);
- xu = clamp(xu, x0, x1);
- /* Compute yv. */
- float z0sq = z0 * z0;
- float y0sq = y0 * y0;
- float y1sq = y1 * y1;
- float d = sqrtf(xu * xu + z0sq);
- float h0 = y0 / sqrtf(d * d + y0sq);
- float h1 = y1 / sqrtf(d * d + y1sq);
- float hv = h0 + randv * (h1 - h0), hv2 = hv * hv;
- float yv = (hv2 < 1.0f - 1e-6f) ? (hv * d) / sqrtf(1.0f - hv2) : y1;
-
- /* Transform (xu, yv, z0) to world coords. */
- *light_p = P + xu * x + yv * y + z0 * z;
- }
-
- /* return pdf */
- if (S != 0.0f)
- return 1.0f / S;
- else
- return 0.0f;
-}
-
-ccl_device_inline float3 ellipse_sample(float3 ru, float3 rv, float randu, float randv)
-{
- to_unit_disk(&randu, &randv);
- return ru * randu + rv * randv;
-}
-
-ccl_device float3 disk_light_sample(float3 v, float randu, float randv)
-{
- float3 ru, rv;
-
- make_orthonormals(v, &ru, &rv);
-
- return ellipse_sample(ru, rv, randu, randv);
-}
-
-ccl_device float3 distant_light_sample(float3 D, float radius, float randu, float randv)
-{
- return normalize(D + disk_light_sample(D, randu, randv) * radius);
-}
-
-ccl_device float3
-sphere_light_sample(float3 P, float3 center, float radius, float randu, float randv)
-{
- return disk_light_sample(normalize(P - center), randu, randv) * radius;
-}
-
-ccl_device float spot_light_attenuation(float3 dir,
- float spot_angle,
- float spot_smooth,
- LightSample *ls)
-{
- float3 I = ls->Ng;
-
- float attenuation = dot(dir, I);
-
- if (attenuation <= spot_angle) {
- attenuation = 0.0f;
- }
- else {
- float t = attenuation - spot_angle;
-
- if (t < spot_smooth && spot_smooth != 0.0f)
- attenuation *= smoothstepf(t / spot_smooth);
- }
-
- return attenuation;
-}
-
-ccl_device float lamp_light_pdf(KernelGlobals *kg, const float3 Ng, const float3 I, float t)
-{
- float cos_pi = dot(Ng, I);
-
- if (cos_pi <= 0.0f)
- return 0.0f;
-
- return t * t / cos_pi;
-}
-
-/* Background Light */
-
-#ifdef __BACKGROUND_MIS__
-
-ccl_device float3 background_map_sample(KernelGlobals *kg, float randu, float randv, float *pdf)
-{
- /* for the following, the CDF values are actually a pair of floats, with the
- * function value as X and the actual CDF as Y. The last entry's function
- * value is the CDF total. */
- int res_x = kernel_data.integrator.pdf_background_res_x;
- int res_y = kernel_data.integrator.pdf_background_res_y;
- int cdf_width = res_x + 1;
-
- /* this is basically std::lower_bound as used by pbrt */
- int first = 0;
- int count = res_y;
-
- while (count > 0) {
- int step = count >> 1;
- int middle = first + step;
-
- if (kernel_tex_fetch(__light_background_marginal_cdf, middle).y < randv) {
- first = middle + 1;
- count -= step + 1;
- }
- else
- count = step;
- }
-
- int index_v = max(0, first - 1);
- kernel_assert(index_v >= 0 && index_v < res_y);
-
- float2 cdf_v = kernel_tex_fetch(__light_background_marginal_cdf, index_v);
- float2 cdf_next_v = kernel_tex_fetch(__light_background_marginal_cdf, index_v + 1);
- float2 cdf_last_v = kernel_tex_fetch(__light_background_marginal_cdf, res_y);
-
- /* importance-sampled V direction */
- float dv = inverse_lerp(cdf_v.y, cdf_next_v.y, randv);
- float v = (index_v + dv) / res_y;
-
- /* this is basically std::lower_bound as used by pbrt */
- first = 0;
- count = res_x;
- while (count > 0) {
- int step = count >> 1;
- int middle = first + step;
-
- if (kernel_tex_fetch(__light_background_conditional_cdf, index_v * cdf_width + middle).y <
- randu) {
- first = middle + 1;
- count -= step + 1;
- }
- else
- count = step;
- }
-
- int index_u = max(0, first - 1);
- kernel_assert(index_u >= 0 && index_u < res_x);
-
- float2 cdf_u = kernel_tex_fetch(__light_background_conditional_cdf,
- index_v * cdf_width + index_u);
- float2 cdf_next_u = kernel_tex_fetch(__light_background_conditional_cdf,
- index_v * cdf_width + index_u + 1);
- float2 cdf_last_u = kernel_tex_fetch(__light_background_conditional_cdf,
- index_v * cdf_width + res_x);
-
- /* importance-sampled U direction */
- float du = inverse_lerp(cdf_u.y, cdf_next_u.y, randu);
- float u = (index_u + du) / res_x;
-
- /* compute pdf */
- float sin_theta = sinf(M_PI_F * v);
- float denom = (M_2PI_F * M_PI_F * sin_theta) * cdf_last_u.x * cdf_last_v.x;
-
- if (sin_theta == 0.0f || denom == 0.0f)
- *pdf = 0.0f;
- else
- *pdf = (cdf_u.x * cdf_v.x) / denom;
-
- /* compute direction */
- return equirectangular_to_direction(u, v);
-}
-
-/* TODO(sergey): Same as above, after the release we should consider using
- * 'noinline' for all devices.
- */
-ccl_device float background_map_pdf(KernelGlobals *kg, float3 direction)
-{
- float2 uv = direction_to_equirectangular(direction);
- int res_x = kernel_data.integrator.pdf_background_res_x;
- int res_y = kernel_data.integrator.pdf_background_res_y;
- int cdf_width = res_x + 1;
-
- float sin_theta = sinf(uv.y * M_PI_F);
-
- if (sin_theta == 0.0f)
- return 0.0f;
-
- int index_u = clamp(float_to_int(uv.x * res_x), 0, res_x - 1);
- int index_v = clamp(float_to_int(uv.y * res_y), 0, res_y - 1);
-
- /* pdfs in V direction */
- float2 cdf_last_u = kernel_tex_fetch(__light_background_conditional_cdf,
- index_v * cdf_width + res_x);
- float2 cdf_last_v = kernel_tex_fetch(__light_background_marginal_cdf, res_y);
-
- float denom = (M_2PI_F * M_PI_F * sin_theta) * cdf_last_u.x * cdf_last_v.x;
-
- if (denom == 0.0f)
- return 0.0f;
-
- /* pdfs in U direction */
- float2 cdf_u = kernel_tex_fetch(__light_background_conditional_cdf,
- index_v * cdf_width + index_u);
- float2 cdf_v = kernel_tex_fetch(__light_background_marginal_cdf, index_v);
-
- return (cdf_u.x * cdf_v.x) / denom;
-}
-
-ccl_device_inline bool background_portal_data_fetch_and_check_side(
- KernelGlobals *kg, float3 P, int index, float3 *lightpos, float3 *dir)
-{
- int portal = kernel_data.integrator.portal_offset + index;
- const ccl_global KernelLight *klight = &kernel_tex_fetch(__lights, portal);
-
- *lightpos = make_float3(klight->co[0], klight->co[1], klight->co[2]);
- *dir = make_float3(klight->area.dir[0], klight->area.dir[1], klight->area.dir[2]);
-
- /* Check whether portal is on the right side. */
- if (dot(*dir, P - *lightpos) > 1e-4f)
- return true;
-
- return false;
-}
-
-ccl_device_inline float background_portal_pdf(
- KernelGlobals *kg, float3 P, float3 direction, int ignore_portal, bool *is_possible)
-{
- float portal_pdf = 0.0f;
-
- int num_possible = 0;
- for (int p = 0; p < kernel_data.integrator.num_portals; p++) {
- if (p == ignore_portal)
- continue;
-
- float3 lightpos, dir;
- if (!background_portal_data_fetch_and_check_side(kg, P, p, &lightpos, &dir))
- continue;
-
- /* There's a portal that could be sampled from this position. */
- if (is_possible) {
- *is_possible = true;
- }
- num_possible++;
-
- int portal = kernel_data.integrator.portal_offset + p;
- const ccl_global KernelLight *klight = &kernel_tex_fetch(__lights, portal);
- float3 axisu = make_float3(
- klight->area.axisu[0], klight->area.axisu[1], klight->area.axisu[2]);
- float3 axisv = make_float3(
- klight->area.axisv[0], klight->area.axisv[1], klight->area.axisv[2]);
- bool is_round = (klight->area.invarea < 0.0f);
-
- if (!ray_quad_intersect(P,
- direction,
- 1e-4f,
- FLT_MAX,
- lightpos,
- axisu,
- axisv,
- dir,
- NULL,
- NULL,
- NULL,
- NULL,
- is_round))
- continue;
-
- if (is_round) {
- float t;
- float3 D = normalize_len(lightpos - P, &t);
- portal_pdf += fabsf(klight->area.invarea) * lamp_light_pdf(kg, dir, -D, t);
- }
- else {
- portal_pdf += rect_light_sample(P, &lightpos, axisu, axisv, 0.0f, 0.0f, false);
- }
- }
-
- if (ignore_portal >= 0) {
- /* We have skipped a portal that could be sampled as well. */
- num_possible++;
- }
-
- return (num_possible > 0) ? portal_pdf / num_possible : 0.0f;
-}
-
-ccl_device int background_num_possible_portals(KernelGlobals *kg, float3 P)
-{
- int num_possible_portals = 0;
- for (int p = 0; p < kernel_data.integrator.num_portals; p++) {
- float3 lightpos, dir;
- if (background_portal_data_fetch_and_check_side(kg, P, p, &lightpos, &dir))
- num_possible_portals++;
- }
- return num_possible_portals;
-}
-
-ccl_device float3 background_portal_sample(KernelGlobals *kg,
- float3 P,
- float randu,
- float randv,
- int num_possible,
- int *sampled_portal,
- float *pdf)
-{
- /* Pick a portal, then re-normalize randv. */
- randv *= num_possible;
- int portal = (int)randv;
- randv -= portal;
-
- /* TODO(sergey): Some smarter way of finding portal to sample
- * is welcome.
- */
- for (int p = 0; p < kernel_data.integrator.num_portals; p++) {
- /* Search for the sampled portal. */
- float3 lightpos, dir;
- if (!background_portal_data_fetch_and_check_side(kg, P, p, &lightpos, &dir))
- continue;
-
- if (portal == 0) {
- /* p is the portal to be sampled. */
- int portal = kernel_data.integrator.portal_offset + p;
- const ccl_global KernelLight *klight = &kernel_tex_fetch(__lights, portal);
- float3 axisu = make_float3(
- klight->area.axisu[0], klight->area.axisu[1], klight->area.axisu[2]);
- float3 axisv = make_float3(
- klight->area.axisv[0], klight->area.axisv[1], klight->area.axisv[2]);
- bool is_round = (klight->area.invarea < 0.0f);
-
- float3 D;
- if (is_round) {
- lightpos += ellipse_sample(axisu * 0.5f, axisv * 0.5f, randu, randv);
- float t;
- D = normalize_len(lightpos - P, &t);
- *pdf = fabsf(klight->area.invarea) * lamp_light_pdf(kg, dir, -D, t);
- }
- else {
- *pdf = rect_light_sample(P, &lightpos, axisu, axisv, randu, randv, true);
- D = normalize(lightpos - P);
- }
-
- *pdf /= num_possible;
- *sampled_portal = p;
- return D;
- }
-
- portal--;
- }
-
- return make_float3(0.0f, 0.0f, 0.0f);
-}
-
-ccl_device_inline float3
-background_light_sample(KernelGlobals *kg, float3 P, float randu, float randv, float *pdf)
-{
- /* Probability of sampling portals instead of the map. */
- float portal_sampling_pdf = kernel_data.integrator.portal_pdf;
-
- /* Check if there are portals in the scene which we can sample. */
- if (portal_sampling_pdf > 0.0f) {
- int num_portals = background_num_possible_portals(kg, P);
- if (num_portals > 0) {
- if (portal_sampling_pdf == 1.0f || randu < portal_sampling_pdf) {
- if (portal_sampling_pdf < 1.0f) {
- randu /= portal_sampling_pdf;
- }
- int portal;
- float3 D = background_portal_sample(kg, P, randu, randv, num_portals, &portal, pdf);
- if (num_portals > 1) {
- /* Ignore the chosen portal, its pdf is already included. */
- *pdf += background_portal_pdf(kg, P, D, portal, NULL);
- }
- /* We could also have sampled the map, so combine with MIS. */
- if (portal_sampling_pdf < 1.0f) {
- float cdf_pdf = background_map_pdf(kg, D);
- *pdf = (portal_sampling_pdf * (*pdf) + (1.0f - portal_sampling_pdf) * cdf_pdf);
- }
- return D;
- }
- else {
- /* Sample map, but with nonzero portal_sampling_pdf for MIS. */
- randu = (randu - portal_sampling_pdf) / (1.0f - portal_sampling_pdf);
- }
- }
- else {
- /* We can't sample a portal.
- * Check if we can sample the map instead.
- */
- if (portal_sampling_pdf == 1.0f) {
- /* Use uniform as a fallback if we can't sample the map. */
- *pdf = 1.0f / M_4PI_F;
- return sample_uniform_sphere(randu, randv);
- }
- else {
- portal_sampling_pdf = 0.0f;
- }
- }
- }
-
- float3 D = background_map_sample(kg, randu, randv, pdf);
- /* Use MIS if portals could be sampled as well. */
- if (portal_sampling_pdf > 0.0f) {
- float portal_pdf = background_portal_pdf(kg, P, D, -1, NULL);
- *pdf = (portal_sampling_pdf * portal_pdf + (1.0f - portal_sampling_pdf) * (*pdf));
- }
- return D;
-}
-
-ccl_device float background_light_pdf(KernelGlobals *kg, float3 P, float3 direction)
-{
- /* Probability of sampling portals instead of the map. */
- float portal_sampling_pdf = kernel_data.integrator.portal_pdf;
-
- float portal_pdf = 0.0f, map_pdf = 0.0f;
- if (portal_sampling_pdf > 0.0f) {
- /* Evaluate PDF of sampling this direction by portal sampling. */
- bool is_possible = false;
- portal_pdf = background_portal_pdf(kg, P, direction, -1, &is_possible) * portal_sampling_pdf;
- if (!is_possible) {
- /* Portal sampling is not possible here because all portals point to the wrong side.
- * If map sampling is possible, it would be used instead,
- * otherwise fallback sampling is used. */
- if (portal_sampling_pdf == 1.0f) {
- return kernel_data.integrator.pdf_lights / M_4PI_F;
- }
- else {
- /* Force map sampling. */
- portal_sampling_pdf = 0.0f;
- }
- }
- }
- if (portal_sampling_pdf < 1.0f) {
- /* Evaluate PDF of sampling this direction by map sampling. */
- map_pdf = background_map_pdf(kg, direction) * (1.0f - portal_sampling_pdf);
- }
- return (portal_pdf + map_pdf) * kernel_data.integrator.pdf_lights;
-}
-#endif
-
/* Regular Light */
ccl_device_inline bool lamp_light_sample(
@@ -594,7 +102,7 @@ ccl_device_inline bool lamp_light_sample(
/* spot light attenuation */
float3 dir = make_float3(klight->spot.dir[0], klight->spot.dir[1], klight->spot.dir[2]);
ls->eval_fac *= spot_light_attenuation(
- dir, klight->spot.spot_angle, klight->spot.spot_smooth, ls);
+ dir, klight->spot.spot_angle, klight->spot.spot_smooth, ls->Ng);
if (ls->eval_fac == 0.0f) {
return false;
}
@@ -732,7 +240,7 @@ ccl_device bool lamp_light_eval(
/* spot light attenuation */
float3 dir = make_float3(klight->spot.dir[0], klight->spot.dir[1], klight->spot.dir[2]);
ls->eval_fac *= spot_light_attenuation(
- dir, klight->spot.spot_angle, klight->spot.spot_smooth, ls);
+ dir, klight->spot.spot_angle, klight->spot.spot_smooth, ls->Ng);
if (ls->eval_fac == 0.0f)
return false;
@@ -805,20 +313,18 @@ ccl_device_inline bool triangle_world_space_vertices(
triangle_vertices(kg, prim, V);
}
-#ifdef __INSTANCING__
if (!(object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
-# ifdef __OBJECT_MOTION__
+#ifdef __OBJECT_MOTION__
float object_time = (time >= 0.0f) ? time : 0.5f;
Transform tfm = object_fetch_transform_motion_test(kg, object, object_time, NULL);
-# else
+#else
Transform tfm = object_fetch_transform(kg, object, OBJECT_TRANSFORM);
-# endif
+#endif
V[0] = transform_point(&tfm, V[0]);
V[1] = transform_point(&tfm, V[1]);
V[2] = transform_point(&tfm, V[2]);
has_motion = true;
}
-#endif
return has_motion;
}
diff --git a/intern/cycles/kernel/kernel_light_background.h b/intern/cycles/kernel/kernel_light_background.h
new file mode 100644
index 00000000000..30e336f0f80
--- /dev/null
+++ b/intern/cycles/kernel/kernel_light_background.h
@@ -0,0 +1,448 @@
+/*
+ * Copyright 2011-2020 Blender Foundation
+ *
+ * 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.
+ */
+
+#include "kernel_light_common.h"
+
+CCL_NAMESPACE_BEGIN
+
+/* Background Light */
+
+#ifdef __BACKGROUND_MIS__
+
+ccl_device float3 background_map_sample(KernelGlobals *kg, float randu, float randv, float *pdf)
+{
+ /* for the following, the CDF values are actually a pair of floats, with the
+ * function value as X and the actual CDF as Y. The last entry's function
+ * value is the CDF total. */
+ int res_x = kernel_data.background.map_res_x;
+ int res_y = kernel_data.background.map_res_y;
+ int cdf_width = res_x + 1;
+
+ /* this is basically std::lower_bound as used by pbrt */
+ int first = 0;
+ int count = res_y;
+
+ while (count > 0) {
+ int step = count >> 1;
+ int middle = first + step;
+
+ if (kernel_tex_fetch(__light_background_marginal_cdf, middle).y < randv) {
+ first = middle + 1;
+ count -= step + 1;
+ }
+ else
+ count = step;
+ }
+
+ int index_v = max(0, first - 1);
+ kernel_assert(index_v >= 0 && index_v < res_y);
+
+ float2 cdf_v = kernel_tex_fetch(__light_background_marginal_cdf, index_v);
+ float2 cdf_next_v = kernel_tex_fetch(__light_background_marginal_cdf, index_v + 1);
+ float2 cdf_last_v = kernel_tex_fetch(__light_background_marginal_cdf, res_y);
+
+ /* importance-sampled V direction */
+ float dv = inverse_lerp(cdf_v.y, cdf_next_v.y, randv);
+ float v = (index_v + dv) / res_y;
+
+ /* this is basically std::lower_bound as used by pbrt */
+ first = 0;
+ count = res_x;
+ while (count > 0) {
+ int step = count >> 1;
+ int middle = first + step;
+
+ if (kernel_tex_fetch(__light_background_conditional_cdf, index_v * cdf_width + middle).y <
+ randu) {
+ first = middle + 1;
+ count -= step + 1;
+ }
+ else
+ count = step;
+ }
+
+ int index_u = max(0, first - 1);
+ kernel_assert(index_u >= 0 && index_u < res_x);
+
+ float2 cdf_u = kernel_tex_fetch(__light_background_conditional_cdf,
+ index_v * cdf_width + index_u);
+ float2 cdf_next_u = kernel_tex_fetch(__light_background_conditional_cdf,
+ index_v * cdf_width + index_u + 1);
+ float2 cdf_last_u = kernel_tex_fetch(__light_background_conditional_cdf,
+ index_v * cdf_width + res_x);
+
+ /* importance-sampled U direction */
+ float du = inverse_lerp(cdf_u.y, cdf_next_u.y, randu);
+ float u = (index_u + du) / res_x;
+
+ /* compute pdf */
+ float sin_theta = sinf(M_PI_F * v);
+ float denom = (M_2PI_F * M_PI_F * sin_theta) * cdf_last_u.x * cdf_last_v.x;
+
+ if (sin_theta == 0.0f || denom == 0.0f)
+ *pdf = 0.0f;
+ else
+ *pdf = (cdf_u.x * cdf_v.x) / denom;
+
+ /* compute direction */
+ return equirectangular_to_direction(u, v);
+}
+
+/* TODO(sergey): Same as above, after the release we should consider using
+ * 'noinline' for all devices.
+ */
+ccl_device float background_map_pdf(KernelGlobals *kg, float3 direction)
+{
+ float2 uv = direction_to_equirectangular(direction);
+ int res_x = kernel_data.background.map_res_x;
+ int res_y = kernel_data.background.map_res_y;
+ int cdf_width = res_x + 1;
+
+ float sin_theta = sinf(uv.y * M_PI_F);
+
+ if (sin_theta == 0.0f)
+ return 0.0f;
+
+ int index_u = clamp(float_to_int(uv.x * res_x), 0, res_x - 1);
+ int index_v = clamp(float_to_int(uv.y * res_y), 0, res_y - 1);
+
+ /* pdfs in V direction */
+ float2 cdf_last_u = kernel_tex_fetch(__light_background_conditional_cdf,
+ index_v * cdf_width + res_x);
+ float2 cdf_last_v = kernel_tex_fetch(__light_background_marginal_cdf, res_y);
+
+ float denom = (M_2PI_F * M_PI_F * sin_theta) * cdf_last_u.x * cdf_last_v.x;
+
+ if (denom == 0.0f)
+ return 0.0f;
+
+ /* pdfs in U direction */
+ float2 cdf_u = kernel_tex_fetch(__light_background_conditional_cdf,
+ index_v * cdf_width + index_u);
+ float2 cdf_v = kernel_tex_fetch(__light_background_marginal_cdf, index_v);
+
+ return (cdf_u.x * cdf_v.x) / denom;
+}
+
+ccl_device_inline bool background_portal_data_fetch_and_check_side(
+ KernelGlobals *kg, float3 P, int index, float3 *lightpos, float3 *dir)
+{
+ int portal = kernel_data.background.portal_offset + index;
+ const ccl_global KernelLight *klight = &kernel_tex_fetch(__lights, portal);
+
+ *lightpos = make_float3(klight->co[0], klight->co[1], klight->co[2]);
+ *dir = make_float3(klight->area.dir[0], klight->area.dir[1], klight->area.dir[2]);
+
+ /* Check whether portal is on the right side. */
+ if (dot(*dir, P - *lightpos) > 1e-4f)
+ return true;
+
+ return false;
+}
+
+ccl_device_inline float background_portal_pdf(
+ KernelGlobals *kg, float3 P, float3 direction, int ignore_portal, bool *is_possible)
+{
+ float portal_pdf = 0.0f;
+
+ int num_possible = 0;
+ for (int p = 0; p < kernel_data.background.num_portals; p++) {
+ if (p == ignore_portal)
+ continue;
+
+ float3 lightpos, dir;
+ if (!background_portal_data_fetch_and_check_side(kg, P, p, &lightpos, &dir))
+ continue;
+
+ /* There's a portal that could be sampled from this position. */
+ if (is_possible) {
+ *is_possible = true;
+ }
+ num_possible++;
+
+ int portal = kernel_data.background.portal_offset + p;
+ const ccl_global KernelLight *klight = &kernel_tex_fetch(__lights, portal);
+ float3 axisu = make_float3(
+ klight->area.axisu[0], klight->area.axisu[1], klight->area.axisu[2]);
+ float3 axisv = make_float3(
+ klight->area.axisv[0], klight->area.axisv[1], klight->area.axisv[2]);
+ bool is_round = (klight->area.invarea < 0.0f);
+
+ if (!ray_quad_intersect(P,
+ direction,
+ 1e-4f,
+ FLT_MAX,
+ lightpos,
+ axisu,
+ axisv,
+ dir,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ is_round))
+ continue;
+
+ if (is_round) {
+ float t;
+ float3 D = normalize_len(lightpos - P, &t);
+ portal_pdf += fabsf(klight->area.invarea) * lamp_light_pdf(kg, dir, -D, t);
+ }
+ else {
+ portal_pdf += rect_light_sample(P, &lightpos, axisu, axisv, 0.0f, 0.0f, false);
+ }
+ }
+
+ if (ignore_portal >= 0) {
+ /* We have skipped a portal that could be sampled as well. */
+ num_possible++;
+ }
+
+ return (num_possible > 0) ? portal_pdf / num_possible : 0.0f;
+}
+
+ccl_device int background_num_possible_portals(KernelGlobals *kg, float3 P)
+{
+ int num_possible_portals = 0;
+ for (int p = 0; p < kernel_data.background.num_portals; p++) {
+ float3 lightpos, dir;
+ if (background_portal_data_fetch_and_check_side(kg, P, p, &lightpos, &dir))
+ num_possible_portals++;
+ }
+ return num_possible_portals;
+}
+
+ccl_device float3 background_portal_sample(KernelGlobals *kg,
+ float3 P,
+ float randu,
+ float randv,
+ int num_possible,
+ int *sampled_portal,
+ float *pdf)
+{
+ /* Pick a portal, then re-normalize randv. */
+ randv *= num_possible;
+ int portal = (int)randv;
+ randv -= portal;
+
+ /* TODO(sergey): Some smarter way of finding portal to sample
+ * is welcome.
+ */
+ for (int p = 0; p < kernel_data.background.num_portals; p++) {
+ /* Search for the sampled portal. */
+ float3 lightpos, dir;
+ if (!background_portal_data_fetch_and_check_side(kg, P, p, &lightpos, &dir))
+ continue;
+
+ if (portal == 0) {
+ /* p is the portal to be sampled. */
+ int portal = kernel_data.background.portal_offset + p;
+ const ccl_global KernelLight *klight = &kernel_tex_fetch(__lights, portal);
+ float3 axisu = make_float3(
+ klight->area.axisu[0], klight->area.axisu[1], klight->area.axisu[2]);
+ float3 axisv = make_float3(
+ klight->area.axisv[0], klight->area.axisv[1], klight->area.axisv[2]);
+ bool is_round = (klight->area.invarea < 0.0f);
+
+ float3 D;
+ if (is_round) {
+ lightpos += ellipse_sample(axisu * 0.5f, axisv * 0.5f, randu, randv);
+ float t;
+ D = normalize_len(lightpos - P, &t);
+ *pdf = fabsf(klight->area.invarea) * lamp_light_pdf(kg, dir, -D, t);
+ }
+ else {
+ *pdf = rect_light_sample(P, &lightpos, axisu, axisv, randu, randv, true);
+ D = normalize(lightpos - P);
+ }
+
+ *pdf /= num_possible;
+ *sampled_portal = p;
+ return D;
+ }
+
+ portal--;
+ }
+
+ return make_float3(0.0f, 0.0f, 0.0f);
+}
+
+ccl_device_inline float3 background_sun_sample(KernelGlobals *kg,
+ float randu,
+ float randv,
+ float *pdf)
+{
+ float3 D;
+ const float3 N = float4_to_float3(kernel_data.background.sun);
+ const float angle = kernel_data.background.sun.w;
+ sample_uniform_cone(N, angle, randu, randv, &D, pdf);
+ return D;
+}
+
+ccl_device_inline float background_sun_pdf(KernelGlobals *kg, float3 D)
+{
+ const float3 N = float4_to_float3(kernel_data.background.sun);
+ const float angle = kernel_data.background.sun.w;
+ return pdf_uniform_cone(N, D, angle);
+}
+
+ccl_device_inline float3
+background_light_sample(KernelGlobals *kg, float3 P, float randu, float randv, float *pdf)
+{
+ float portal_method_pdf = kernel_data.background.portal_weight;
+ float sun_method_pdf = kernel_data.background.sun_weight;
+ float map_method_pdf = kernel_data.background.map_weight;
+
+ int num_portals = 0;
+ if (portal_method_pdf > 0.0f) {
+ /* Check if there are portals in the scene which we can sample. */
+ num_portals = background_num_possible_portals(kg, P);
+ if (num_portals == 0) {
+ portal_method_pdf = 0.0f;
+ }
+ }
+
+ float pdf_fac = (portal_method_pdf + sun_method_pdf + map_method_pdf);
+ if (pdf_fac == 0.0f) {
+ /* Use uniform as a fallback if we can't use any strategy. */
+ *pdf = 1.0f / M_4PI_F;
+ return sample_uniform_sphere(randu, randv);
+ }
+
+ pdf_fac = 1.0f / pdf_fac;
+ portal_method_pdf *= pdf_fac;
+ sun_method_pdf *= pdf_fac;
+ map_method_pdf *= pdf_fac;
+
+ /* We have 100% in total and split it between the three categories.
+ * Therefore, we pick portals if randu is between 0 and portal_method_pdf,
+ * sun if randu is between portal_method_pdf and (portal_method_pdf + sun_method_pdf)
+ * and map if randu is between (portal_method_pdf + sun_method_pdf) and 1. */
+ float sun_method_cdf = portal_method_pdf + sun_method_pdf;
+
+ int method = 0;
+ float3 D;
+ if (randu < portal_method_pdf) {
+ method = 0;
+ /* Rescale randu. */
+ if (portal_method_pdf != 1.0f) {
+ randu /= portal_method_pdf;
+ }
+
+ /* Sample a portal. */
+ int portal;
+ D = background_portal_sample(kg, P, randu, randv, num_portals, &portal, pdf);
+ if (num_portals > 1) {
+ /* Ignore the chosen portal, its pdf is already included. */
+ *pdf += background_portal_pdf(kg, P, D, portal, NULL);
+ }
+
+ /* Skip MIS if this is the only method. */
+ if (portal_method_pdf == 1.0f) {
+ return D;
+ }
+ *pdf *= portal_method_pdf;
+ }
+ else if (randu < sun_method_cdf) {
+ method = 1;
+ /* Rescale randu. */
+ if (sun_method_pdf != 1.0f) {
+ randu = (randu - portal_method_pdf) / sun_method_pdf;
+ }
+
+ D = background_sun_sample(kg, randu, randv, pdf);
+
+ /* Skip MIS if this is the only method. */
+ if (sun_method_pdf == 1.0f) {
+ return D;
+ }
+ *pdf *= sun_method_pdf;
+ }
+ else {
+ method = 2;
+ /* Rescale randu. */
+ if (map_method_pdf != 1.0f) {
+ randu = (randu - sun_method_cdf) / map_method_pdf;
+ }
+
+ D = background_map_sample(kg, randu, randv, pdf);
+
+ /* Skip MIS if this is the only method. */
+ if (map_method_pdf == 1.0f) {
+ return D;
+ }
+ *pdf *= map_method_pdf;
+ }
+
+ /* MIS weighting. */
+ if (method != 0 && portal_method_pdf != 0.0f) {
+ *pdf += portal_method_pdf * background_portal_pdf(kg, P, D, -1, NULL);
+ }
+ if (method != 1 && sun_method_pdf != 0.0f) {
+ *pdf += sun_method_pdf * background_sun_pdf(kg, D);
+ }
+ if (method != 2 && map_method_pdf != 0.0f) {
+ *pdf += map_method_pdf * background_map_pdf(kg, D);
+ }
+ return D;
+}
+
+ccl_device float background_light_pdf(KernelGlobals *kg, float3 P, float3 direction)
+{
+ float portal_method_pdf = kernel_data.background.portal_weight;
+ float sun_method_pdf = kernel_data.background.sun_weight;
+ float map_method_pdf = kernel_data.background.map_weight;
+
+ float portal_pdf = 0.0f;
+ /* Portals are a special case here since we need to compute their pdf in order
+ * to find out if we can sample them. */
+ if (portal_method_pdf > 0.0f) {
+ /* Evaluate PDF of sampling this direction by portal sampling. */
+ bool is_possible = false;
+ portal_pdf = background_portal_pdf(kg, P, direction, -1, &is_possible);
+ if (!is_possible) {
+ /* Portal sampling is not possible here because all portals point to the wrong side.
+ * If other methods can be used instead, do so, otherwise uniform sampling is used as a
+ * fallback. */
+ portal_method_pdf = 0.0f;
+ }
+ }
+
+ float pdf_fac = (portal_method_pdf + sun_method_pdf + map_method_pdf);
+ if (pdf_fac == 0.0f) {
+ /* Use uniform as a fallback if we can't use any strategy. */
+ return kernel_data.integrator.pdf_lights / M_4PI_F;
+ }
+
+ pdf_fac = 1.0f / pdf_fac;
+ portal_method_pdf *= pdf_fac;
+ sun_method_pdf *= pdf_fac;
+ map_method_pdf *= pdf_fac;
+
+ float pdf = portal_pdf * portal_method_pdf;
+ if (sun_method_pdf != 0.0f) {
+ pdf += background_sun_pdf(kg, direction) * sun_method_pdf;
+ }
+ if (map_method_pdf != 0.0f) {
+ pdf += background_map_pdf(kg, direction) * map_method_pdf;
+ }
+
+ return pdf * kernel_data.integrator.pdf_lights;
+}
+
+#endif
+
+CCL_NAMESPACE_END \ No newline at end of file
diff --git a/intern/cycles/kernel/kernel_light_common.h b/intern/cycles/kernel/kernel_light_common.h
new file mode 100644
index 00000000000..39503a4b479
--- /dev/null
+++ b/intern/cycles/kernel/kernel_light_common.h
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2011-2020 Blender Foundation
+ *
+ * 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.
+ */
+
+CCL_NAMESPACE_BEGIN
+
+/* Area light sampling */
+
+/* Uses the following paper:
+ *
+ * Carlos Urena et al.
+ * An Area-Preserving Parametrization for Spherical Rectangles.
+ *
+ * https://www.solidangle.com/research/egsr2013_spherical_rectangle.pdf
+ *
+ * Note: light_p is modified when sample_coord is true.
+ */
+ccl_device_inline float rect_light_sample(float3 P,
+ float3 *light_p,
+ float3 axisu,
+ float3 axisv,
+ float randu,
+ float randv,
+ bool sample_coord)
+{
+ /* In our name system we're using P for the center,
+ * which is o in the paper.
+ */
+
+ float3 corner = *light_p - axisu * 0.5f - axisv * 0.5f;
+ float axisu_len, axisv_len;
+ /* Compute local reference system R. */
+ float3 x = normalize_len(axisu, &axisu_len);
+ float3 y = normalize_len(axisv, &axisv_len);
+ float3 z = cross(x, y);
+ /* Compute rectangle coords in local reference system. */
+ float3 dir = corner - P;
+ float z0 = dot(dir, z);
+ /* Flip 'z' to make it point against Q. */
+ if (z0 > 0.0f) {
+ z *= -1.0f;
+ z0 *= -1.0f;
+ }
+ float x0 = dot(dir, x);
+ float y0 = dot(dir, y);
+ float x1 = x0 + axisu_len;
+ float y1 = y0 + axisv_len;
+ /* Compute internal angles (gamma_i). */
+ float4 diff = make_float4(x0, y1, x1, y0) - make_float4(x1, y0, x0, y1);
+ float4 nz = make_float4(y0, x1, y1, x0) * diff;
+ nz = nz / sqrt(z0 * z0 * diff * diff + nz * nz);
+ float g0 = safe_acosf(-nz.x * nz.y);
+ float g1 = safe_acosf(-nz.y * nz.z);
+ float g2 = safe_acosf(-nz.z * nz.w);
+ float g3 = safe_acosf(-nz.w * nz.x);
+ /* Compute predefined constants. */
+ float b0 = nz.x;
+ float b1 = nz.z;
+ float b0sq = b0 * b0;
+ float k = M_2PI_F - g2 - g3;
+ /* Compute solid angle from internal angles. */
+ float S = g0 + g1 - k;
+
+ if (sample_coord) {
+ /* Compute cu. */
+ float au = randu * S + k;
+ float fu = (cosf(au) * b0 - b1) / sinf(au);
+ float cu = 1.0f / sqrtf(fu * fu + b0sq) * (fu > 0.0f ? 1.0f : -1.0f);
+ cu = clamp(cu, -1.0f, 1.0f);
+ /* Compute xu. */
+ float xu = -(cu * z0) / max(sqrtf(1.0f - cu * cu), 1e-7f);
+ xu = clamp(xu, x0, x1);
+ /* Compute yv. */
+ float z0sq = z0 * z0;
+ float y0sq = y0 * y0;
+ float y1sq = y1 * y1;
+ float d = sqrtf(xu * xu + z0sq);
+ float h0 = y0 / sqrtf(d * d + y0sq);
+ float h1 = y1 / sqrtf(d * d + y1sq);
+ float hv = h0 + randv * (h1 - h0), hv2 = hv * hv;
+ float yv = (hv2 < 1.0f - 1e-6f) ? (hv * d) / sqrtf(1.0f - hv2) : y1;
+
+ /* Transform (xu, yv, z0) to world coords. */
+ *light_p = P + xu * x + yv * y + z0 * z;
+ }
+
+ /* return pdf */
+ if (S != 0.0f)
+ return 1.0f / S;
+ else
+ return 0.0f;
+}
+
+ccl_device_inline float3 ellipse_sample(float3 ru, float3 rv, float randu, float randv)
+{
+ to_unit_disk(&randu, &randv);
+ return ru * randu + rv * randv;
+}
+
+ccl_device float3 disk_light_sample(float3 v, float randu, float randv)
+{
+ float3 ru, rv;
+
+ make_orthonormals(v, &ru, &rv);
+
+ return ellipse_sample(ru, rv, randu, randv);
+}
+
+ccl_device float3 distant_light_sample(float3 D, float radius, float randu, float randv)
+{
+ return normalize(D + disk_light_sample(D, randu, randv) * radius);
+}
+
+ccl_device float3
+sphere_light_sample(float3 P, float3 center, float radius, float randu, float randv)
+{
+ return disk_light_sample(normalize(P - center), randu, randv) * radius;
+}
+
+ccl_device float spot_light_attenuation(float3 dir, float spot_angle, float spot_smooth, float3 N)
+{
+ float attenuation = dot(dir, N);
+
+ if (attenuation <= spot_angle) {
+ attenuation = 0.0f;
+ }
+ else {
+ float t = attenuation - spot_angle;
+
+ if (t < spot_smooth && spot_smooth != 0.0f)
+ attenuation *= smoothstepf(t / spot_smooth);
+ }
+
+ return attenuation;
+}
+
+ccl_device float lamp_light_pdf(KernelGlobals *kg, const float3 Ng, const float3 I, float t)
+{
+ float cos_pi = dot(Ng, I);
+
+ if (cos_pi <= 0.0f)
+ return 0.0f;
+
+ return t * t / cos_pi;
+}
+
+CCL_NAMESPACE_END
diff --git a/intern/cycles/kernel/kernel_montecarlo.h b/intern/cycles/kernel/kernel_montecarlo.h
index 5c776e06547..0edcc1a5a14 100644
--- a/intern/cycles/kernel/kernel_montecarlo.h
+++ b/intern/cycles/kernel/kernel_montecarlo.h
@@ -98,6 +98,16 @@ ccl_device_inline void sample_uniform_cone(
*pdf = M_1_2PI_F / (1.0f - zMin);
}
+ccl_device_inline float pdf_uniform_cone(const float3 N, float3 D, float angle)
+{
+ float zMin = cosf(angle);
+ float z = dot(N, D);
+ if (z > zMin) {
+ return M_1_2PI_F / (1.0f - zMin);
+ }
+ return 0.0f;
+}
+
/* sample uniform point on the surface of a sphere */
ccl_device float3 sample_uniform_sphere(float u1, float u2)
{
diff --git a/intern/cycles/kernel/kernel_shader.h b/intern/cycles/kernel/kernel_shader.h
index 9700aaba80f..3d9f787f267 100644
--- a/intern/cycles/kernel/kernel_shader.h
+++ b/intern/cycles/kernel/kernel_shader.h
@@ -63,10 +63,8 @@ ccl_device_noinline
{
PROFILING_INIT(kg, PROFILING_SHADER_SETUP);
-#ifdef __INSTANCING__
sd->object = (isect->object == OBJECT_NONE) ? kernel_tex_fetch(__prim_object, isect->prim) :
isect->object;
-#endif
sd->lamp = LAMP_NONE;
sd->type = isect->type;
@@ -82,18 +80,13 @@ ccl_device_noinline
sd->prim = kernel_tex_fetch(__prim_index, isect->prim);
sd->ray_length = isect->t;
-#ifdef __UV__
sd->u = isect->u;
sd->v = isect->v;
-#endif
#ifdef __HAIR__
if (sd->type & PRIMITIVE_ALL_CURVE) {
/* curve */
- float4 curvedata = kernel_tex_fetch(__curves, sd->prim);
-
- sd->shader = __float_as_int(curvedata.z);
- sd->P = curve_refine(kg, sd, isect, ray);
+ curve_shader_setup(kg, sd, isect, ray);
}
else
#endif
@@ -125,17 +118,15 @@ ccl_device_noinline
sd->flag |= kernel_tex_fetch(__shaders, (sd->shader & SHADER_MASK)).flags;
-#ifdef __INSTANCING__
if (isect->object != OBJECT_NONE) {
/* instance transform */
object_normal_transform_auto(kg, sd, &sd->N);
object_normal_transform_auto(kg, sd, &sd->Ng);
-# ifdef __DPDU__
+#ifdef __DPDU__
object_dir_transform_auto(kg, sd, &sd->dPdu);
object_dir_transform_auto(kg, sd, &sd->dPdv);
-# endif
- }
#endif
+ }
/* backfacing test */
bool backfacing = (dot(sd->Ng, sd->I) < 0.0f);
@@ -185,10 +176,8 @@ ccl_device_inline
sd->prim = kernel_tex_fetch(__prim_index, isect->prim);
sd->type = isect->type;
-# ifdef __UV__
sd->u = isect->u;
sd->v = isect->v;
-# endif
/* fetch triangle data */
if (sd->type == PRIMITIVE_TRIANGLE) {
@@ -215,17 +204,15 @@ ccl_device_inline
sd->flag |= kernel_tex_fetch(__shaders, (sd->shader & SHADER_MASK)).flags;
-# ifdef __INSTANCING__
if (isect->object != OBJECT_NONE) {
/* instance transform */
object_normal_transform_auto(kg, sd, &sd->N);
object_normal_transform_auto(kg, sd, &sd->Ng);
-# ifdef __DPDU__
+# ifdef __DPDU__
object_dir_transform_auto(kg, sd, &sd->dPdu);
object_dir_transform_auto(kg, sd, &sd->dPdv);
-# endif
- }
# endif
+ }
/* backfacing test */
if (backfacing) {
@@ -284,17 +271,13 @@ ccl_device_inline void shader_setup_from_sample(KernelGlobals *kg,
else
sd->type = PRIMITIVE_NONE;
- /* primitive */
-#ifdef __INSTANCING__
+ /* primitive */
sd->object = object;
-#endif
sd->lamp = LAMP_NONE;
/* currently no access to bvh prim index for strand sd->prim*/
sd->prim = prim;
-#ifdef __UV__
sd->u = u;
sd->v = v;
-#endif
sd->time = time;
sd->ray_length = t;
@@ -330,23 +313,19 @@ ccl_device_inline void shader_setup_from_sample(KernelGlobals *kg,
if (sd->shader & SHADER_SMOOTH_NORMAL) {
sd->N = triangle_smooth_normal(kg, Ng, sd->prim, sd->u, sd->v);
-#ifdef __INSTANCING__
if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
object_normal_transform_auto(kg, sd, &sd->N);
}
-#endif
}
/* dPdu/dPdv */
#ifdef __DPDU__
triangle_dPdudv(kg, sd->prim, &sd->dPdu, &sd->dPdv);
-# ifdef __INSTANCING__
if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
object_dir_transform_auto(kg, sd, &sd->dPdu);
object_dir_transform_auto(kg, sd, &sd->dPdv);
}
-# endif
#endif
}
else {
@@ -432,15 +411,11 @@ ccl_device_inline void shader_setup_from_background(KernelGlobals *kg,
sd->time = ray->time;
sd->ray_length = 0.0f;
-#ifdef __INSTANCING__
sd->object = OBJECT_NONE;
-#endif
sd->lamp = LAMP_NONE;
sd->prim = PRIM_NONE;
-#ifdef __UV__
sd->u = 0.0f;
sd->v = 0.0f;
-#endif
#ifdef __DPDU__
/* dPdu/dPdv */
@@ -481,17 +456,13 @@ ccl_device_inline void shader_setup_from_volume(KernelGlobals *kg, ShaderData *s
sd->time = ray->time;
sd->ray_length = 0.0f; /* todo: can we set this to some useful value? */
-# ifdef __INSTANCING__
sd->object = OBJECT_NONE; /* todo: fill this for texture coordinates */
-# endif
sd->lamp = LAMP_NONE;
sd->prim = PRIM_NONE;
sd->type = PRIMITIVE_NONE;
-# ifdef __UV__
sd->u = 0.0f;
sd->v = 0.0f;
-# endif
# ifdef __DPDU__
/* dPdu/dPdv */
diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h
index 0a0cf1bd6c0..fc9cc73a704 100644
--- a/intern/cycles/kernel/kernel_types.h
+++ b/intern/cycles/kernel/kernel_types.h
@@ -84,9 +84,7 @@ CCL_NAMESPACE_BEGIN
/* Kernel features */
#define __SOBOL__
-#define __INSTANCING__
#define __DPDU__
-#define __UV__
#define __BACKGROUND__
#define __CAUSTICS_TRICKS__
#define __VISIBILITY_FLAG__
@@ -125,9 +123,6 @@ CCL_NAMESPACE_BEGIN
/* Device specific features */
#ifdef __KERNEL_CPU__
-# ifdef __KERNEL_SSE2__
-# define __QBVH__
-# endif
# ifdef WITH_OSL
# define __OSL__
# endif
@@ -696,27 +691,38 @@ typedef enum PrimitiveType {
PRIMITIVE_NONE = 0,
PRIMITIVE_TRIANGLE = (1 << 0),
PRIMITIVE_MOTION_TRIANGLE = (1 << 1),
- PRIMITIVE_CURVE = (1 << 2),
- PRIMITIVE_MOTION_CURVE = (1 << 3),
+ PRIMITIVE_CURVE_THICK = (1 << 2),
+ PRIMITIVE_MOTION_CURVE_THICK = (1 << 3),
+ PRIMITIVE_CURVE_RIBBON = (1 << 4),
+ PRIMITIVE_MOTION_CURVE_RIBBON = (1 << 5),
/* Lamp primitive is not included below on purpose,
* since it is no real traceable primitive.
*/
- PRIMITIVE_LAMP = (1 << 4),
+ PRIMITIVE_LAMP = (1 << 6),
PRIMITIVE_ALL_TRIANGLE = (PRIMITIVE_TRIANGLE | PRIMITIVE_MOTION_TRIANGLE),
- PRIMITIVE_ALL_CURVE = (PRIMITIVE_CURVE | PRIMITIVE_MOTION_CURVE),
- PRIMITIVE_ALL_MOTION = (PRIMITIVE_MOTION_TRIANGLE | PRIMITIVE_MOTION_CURVE),
+ PRIMITIVE_ALL_CURVE = (PRIMITIVE_CURVE_THICK | PRIMITIVE_MOTION_CURVE_THICK |
+ PRIMITIVE_CURVE_RIBBON | PRIMITIVE_MOTION_CURVE_RIBBON),
+ PRIMITIVE_ALL_MOTION = (PRIMITIVE_MOTION_TRIANGLE | PRIMITIVE_MOTION_CURVE_THICK |
+ PRIMITIVE_MOTION_CURVE_RIBBON),
PRIMITIVE_ALL = (PRIMITIVE_ALL_TRIANGLE | PRIMITIVE_ALL_CURVE),
/* Total number of different traceable primitives.
* NOTE: This is an actual value, not a bitflag.
*/
- PRIMITIVE_NUM_TOTAL = 4,
+ PRIMITIVE_NUM_TOTAL = 6,
} PrimitiveType;
#define PRIMITIVE_PACK_SEGMENT(type, segment) ((segment << PRIMITIVE_NUM_TOTAL) | (type))
#define PRIMITIVE_UNPACK_SEGMENT(type) (type >> PRIMITIVE_NUM_TOTAL)
+typedef enum CurveShapeType {
+ CURVE_RIBBON = 0,
+ CURVE_THICK = 1,
+
+ CURVE_NUM_SHAPE_TYPES,
+} CurveShapeType;
+
/* Attributes */
typedef enum AttributePrimitive {
@@ -1291,6 +1297,24 @@ typedef struct KernelBackground {
float ao_factor;
float ao_distance;
float ao_bounces_factor;
+
+ /* portal sampling */
+ float portal_weight;
+ int num_portals;
+ int portal_offset;
+
+ /* sun sampling */
+ float sun_weight;
+ /* xyz store direction, w the angle. float4 instead of float3 is used
+ * to ensure consistent padding/alignment across devices. */
+ float4 sun;
+
+ /* map sampling */
+ float map_weight;
+ int map_res_x;
+ int map_res_y;
+
+ int use_mis;
} KernelBackground;
static_assert_align(KernelBackground, 16);
@@ -1302,15 +1326,8 @@ typedef struct KernelIntegrator {
int num_all_lights;
float pdf_triangles;
float pdf_lights;
- int pdf_background_res_x;
- int pdf_background_res_y;
float light_inv_rr_threshold;
- /* light portals */
- float portal_pdf;
- int num_portals;
- int portal_offset;
-
/* bounces */
int min_bounce;
int max_bounce;
@@ -1372,7 +1389,7 @@ typedef struct KernelIntegrator {
int max_closures;
- int pad1;
+ int pad1, pad2;
} KernelIntegrator;
static_assert_align(KernelIntegrator, 16);
@@ -1380,13 +1397,11 @@ typedef enum KernelBVHLayout {
BVH_LAYOUT_NONE = 0,
BVH_LAYOUT_BVH2 = (1 << 0),
- BVH_LAYOUT_BVH4 = (1 << 1),
- BVH_LAYOUT_BVH8 = (1 << 2),
+ BVH_LAYOUT_EMBREE = (1 << 1),
+ BVH_LAYOUT_OPTIX = (1 << 2),
- BVH_LAYOUT_EMBREE = (1 << 3),
- BVH_LAYOUT_OPTIX = (1 << 4),
-
- BVH_LAYOUT_DEFAULT = BVH_LAYOUT_BVH8,
+ /* Default BVH layout to use for CPU. */
+ BVH_LAYOUT_AUTO = BVH_LAYOUT_EMBREE,
BVH_LAYOUT_ALL = (unsigned int)(~0u),
} KernelBVHLayout;
@@ -1395,9 +1410,9 @@ typedef struct KernelBVH {
int root;
int have_motion;
int have_curves;
- int have_instancing;
int bvh_layout;
int use_bvh_steps;
+ int curve_subdivisions;
/* Custom BVH */
#ifdef __KERNEL_OPTIX__
@@ -1415,25 +1430,6 @@ typedef struct KernelBVH {
} KernelBVH;
static_assert_align(KernelBVH, 16);
-typedef enum CurveFlag {
- /* runtime flags */
- CURVE_KN_BACKFACING = 1, /* backside of cylinder? */
- CURVE_KN_ENCLOSEFILTER = 2, /* don't consider strands surrounding start point? */
- CURVE_KN_INTERPOLATE = 4, /* render as a curve? */
- CURVE_KN_ACCURATE = 8, /* use accurate intersections test? */
- CURVE_KN_INTERSECTCORRECTION = 16, /* correct for width after determing closest midpoint? */
- CURVE_KN_TRUETANGENTGNORMAL = 32, /* use tangent normal for geometry? */
- CURVE_KN_RIBBONS = 64, /* use flat curve ribbons */
-} CurveFlag;
-
-typedef struct KernelCurves {
- int curveflags;
- int subdivisions;
-
- int pad1, pad2;
-} KernelCurves;
-static_assert_align(KernelCurves, 16);
-
typedef struct KernelTables {
int beckmann_offset;
int pad1, pad2, pad3;
@@ -1454,7 +1450,6 @@ typedef struct KernelData {
KernelBackground background;
KernelIntegrator integrator;
KernelBVH bvh;
- KernelCurves curve;
KernelTables tables;
KernelBake bake;
} KernelData;
diff --git a/intern/cycles/kernel/kernels/cpu/kernel.cpp b/intern/cycles/kernel/kernels/cpu/kernel.cpp
index 8829a14ead5..8040bfb7b33 100644
--- a/intern/cycles/kernel/kernels/cpu/kernel.cpp
+++ b/intern/cycles/kernel/kernels/cpu/kernel.cpp
@@ -64,12 +64,14 @@ CCL_NAMESPACE_BEGIN
/* Memory Copy */
-void kernel_const_copy(KernelGlobals *kg, const char *name, void *host, size_t size)
+void kernel_const_copy(KernelGlobals *kg, const char *name, void *host, size_t)
{
- if (strcmp(name, "__data") == 0)
- memcpy(&kg->__data, host, size);
- else
+ if (strcmp(name, "__data") == 0) {
+ kg->__data = *(KernelData *)host;
+ }
+ else {
assert(0);
+ }
}
void kernel_global_memory_copy(KernelGlobals *kg, const char *name, void *mem, size_t size)
diff --git a/intern/cycles/kernel/kernels/optix/kernel_optix.cu b/intern/cycles/kernel/kernels/optix/kernel_optix.cu
index e03504316ad..c730d952ed4 100644
--- a/intern/cycles/kernel/kernels/optix/kernel_optix.cu
+++ b/intern/cycles/kernel/kernels/optix/kernel_optix.cu
@@ -256,11 +256,9 @@ extern "C" __global__ void __closesthit__kernel_optix_hit()
}
#ifdef __HAIR__
-extern "C" __global__ void __intersection__curve()
+ccl_device_inline void optix_intersection_curve(const uint prim, const uint type)
{
- const uint prim = optixGetPrimitiveIndex();
const uint object = get_object_id<true>();
- const uint type = kernel_tex_fetch(__prim_type, prim);
const uint visibility = optixGetPayload_4();
float3 P = optixGetObjectRayOrigin();
@@ -282,14 +280,30 @@ extern "C" __global__ void __intersection__curve()
if (isect.t != FLT_MAX)
isect.t *= len;
- if (!(kernel_data.curve.curveflags & CURVE_KN_INTERPOLATE) ?
- curve_intersect(NULL, &isect, P, dir, visibility, object, prim, time, type) :
- cardinal_curve_intersect(NULL, &isect, P, dir, visibility, object, prim, time, type)) {
+ if (curve_intersect(NULL, &isect, P, dir, visibility, object, prim, time, type)) {
optixReportIntersection(isect.t / len,
type & PRIMITIVE_ALL,
__float_as_int(isect.u), // Attribute_0
__float_as_int(isect.v)); // Attribute_1
}
+
+}
+
+extern "C" __global__ void __intersection__curve_ribbon()
+{
+ const uint prim = optixGetPrimitiveIndex();
+ const uint type = kernel_tex_fetch(__prim_type, prim);
+
+ if (type & (PRIMITIVE_CURVE_RIBBON | PRIMITIVE_MOTION_CURVE_RIBBON)) {
+ optix_intersection_curve(prim, type);
+ }
+}
+
+extern "C" __global__ void __intersection__curve_all()
+{
+ const uint prim = optixGetPrimitiveIndex();
+ const uint type = kernel_tex_fetch(__prim_type, prim);
+ optix_intersection_curve(prim, type);
}
#endif
diff --git a/intern/cycles/kernel/osl/CMakeLists.txt b/intern/cycles/kernel/osl/CMakeLists.txt
index fc0c845fd4f..d7ab778181e 100644
--- a/intern/cycles/kernel/osl/CMakeLists.txt
+++ b/intern/cycles/kernel/osl/CMakeLists.txt
@@ -36,6 +36,15 @@ set(LIB
# OSL and LLVM are built without RTTI
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${RTTI_DISABLE_FLAGS}")
+if(APPLE)
+ # Disable allocation warning on macOS prior to 10.14: the OSLRenderServices
+ # contains member which is 64 bytes aligned (cache inside of OIIO's
+ # unordered_map_concurrent). This is not something what the SDK supportsm, but
+ # since we take care of allocations ourselves is is OK to ignore the
+ # diagnostic message.
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -faligned-allocation")
+endif()
+
include_directories(${INC})
include_directories(SYSTEM ${INC_SYS})
diff --git a/intern/cycles/kernel/osl/osl_closures.cpp b/intern/cycles/kernel/osl/osl_closures.cpp
index 872a55143cc..7ee467a46dd 100644
--- a/intern/cycles/kernel/osl/osl_closures.cpp
+++ b/intern/cycles/kernel/osl/osl_closures.cpp
@@ -362,6 +362,9 @@ void OSLShader::register_closures(OSLShadingSystem *ss_)
id++,
closure_bsdf_transparent_params(),
closure_bsdf_transparent_prepare);
+
+ register_closure(
+ ss, "microfacet", id++, closure_bsdf_microfacet_params(), closure_bsdf_microfacet_prepare);
register_closure(ss,
"microfacet_ggx",
id++,
@@ -508,6 +511,82 @@ bool CBSDFClosure::skip(const ShaderData *sd, int path_flag, int scattering)
return false;
}
+/* Standard Microfacet Closure */
+
+class MicrofacetClosure : public CBSDFClosure {
+ public:
+ MicrofacetBsdf params;
+ ustring distribution;
+ int refract;
+
+ void setup(ShaderData *sd, int path_flag, float3 weight)
+ {
+ static ustring u_ggx("ggx");
+ static ustring u_default("default");
+
+ const int label = (refract) ? LABEL_TRANSMIT : LABEL_REFLECT;
+ if (skip(sd, path_flag, LABEL_GLOSSY | label)) {
+ return;
+ }
+
+ MicrofacetBsdf *bsdf = (MicrofacetBsdf *)bsdf_alloc_osl(
+ sd, sizeof(MicrofacetBsdf), weight, &params);
+
+ if (!bsdf) {
+ return;
+ }
+
+ /* GGX */
+ if (distribution == u_ggx || distribution == u_default) {
+ if (!refract) {
+ if (params.alpha_x == params.alpha_y) {
+ /* Isotropic */
+ sd->flag |= bsdf_microfacet_ggx_isotropic_setup(bsdf);
+ }
+ else {
+ /* Anisotropic */
+ sd->flag |= bsdf_microfacet_ggx_setup(bsdf);
+ }
+ }
+ else {
+ sd->flag |= bsdf_microfacet_ggx_refraction_setup(bsdf);
+ }
+ }
+ /* Beckmann */
+ else {
+ if (!refract) {
+ if (params.alpha_x == params.alpha_y) {
+ /* Isotropic */
+ sd->flag |= bsdf_microfacet_beckmann_isotropic_setup(bsdf);
+ }
+ else {
+ /* Anisotropic */
+ sd->flag |= bsdf_microfacet_beckmann_setup(bsdf);
+ }
+ }
+ else {
+ sd->flag |= bsdf_microfacet_beckmann_refraction_setup(bsdf);
+ }
+ }
+ }
+};
+
+ClosureParam *closure_bsdf_microfacet_params()
+{
+ static ClosureParam params[] = {CLOSURE_STRING_PARAM(MicrofacetClosure, distribution),
+ CLOSURE_FLOAT3_PARAM(MicrofacetClosure, params.N),
+ CLOSURE_FLOAT3_PARAM(MicrofacetClosure, params.T),
+ CLOSURE_FLOAT_PARAM(MicrofacetClosure, params.alpha_x),
+ CLOSURE_FLOAT_PARAM(MicrofacetClosure, params.alpha_y),
+ CLOSURE_FLOAT_PARAM(MicrofacetClosure, params.ior),
+ CLOSURE_INT_PARAM(MicrofacetClosure, refract),
+ CLOSURE_STRING_KEYPARAM(MicrofacetClosure, label, "label"),
+ CLOSURE_FINISH_PARAM(MicrofacetClosure)};
+
+ return params;
+}
+CCLOSURE_PREPARE(closure_bsdf_microfacet_prepare, MicrofacetClosure)
+
/* GGX closures with Fresnel */
class MicrofacetFresnelClosure : public CBSDFClosure {
diff --git a/intern/cycles/kernel/osl/osl_closures.h b/intern/cycles/kernel/osl/osl_closures.h
index d12afdb80dd..e4058e3a746 100644
--- a/intern/cycles/kernel/osl/osl_closures.h
+++ b/intern/cycles/kernel/osl/osl_closures.h
@@ -51,6 +51,7 @@ OSL::ClosureParam *closure_bsdf_transparent_params();
OSL::ClosureParam *closure_bssrdf_params();
OSL::ClosureParam *closure_absorption_params();
OSL::ClosureParam *closure_henyey_greenstein_params();
+OSL::ClosureParam *closure_bsdf_microfacet_params();
OSL::ClosureParam *closure_bsdf_microfacet_multi_ggx_params();
OSL::ClosureParam *closure_bsdf_microfacet_multi_ggx_glass_params();
OSL::ClosureParam *closure_bsdf_microfacet_multi_ggx_aniso_params();
@@ -70,6 +71,7 @@ void closure_bsdf_transparent_prepare(OSL::RendererServices *, int id, void *dat
void closure_bssrdf_prepare(OSL::RendererServices *, int id, void *data);
void closure_absorption_prepare(OSL::RendererServices *, int id, void *data);
void closure_henyey_greenstein_prepare(OSL::RendererServices *, int id, void *data);
+void closure_bsdf_microfacet_prepare(OSL::RendererServices *, int id, void *data);
void closure_bsdf_microfacet_multi_ggx_prepare(OSL::RendererServices *, int id, void *data);
void closure_bsdf_microfacet_multi_ggx_glass_prepare(OSL::RendererServices *, int id, void *data);
void closure_bsdf_microfacet_multi_ggx_aniso_prepare(OSL::RendererServices *, int id, void *data);
diff --git a/intern/cycles/kernel/shaders/node_sky_texture.osl b/intern/cycles/kernel/shaders/node_sky_texture.osl
index 4def237a2e0..08bc8f85120 100644
--- a/intern/cycles/kernel/shaders/node_sky_texture.osl
+++ b/intern/cycles/kernel/shaders/node_sky_texture.osl
@@ -44,13 +44,13 @@ float sky_perez_function(float lam[9], float theta, float gamma)
(1.0 + lam[2] * exp(lam[3] * gamma) + lam[4] * cgamma * cgamma);
}
-color sky_radiance_old(normal dir,
- float sunphi,
- float suntheta,
- color radiance,
- float config_x[9],
- float config_y[9],
- float config_z[9])
+color sky_radiance_preetham(normal dir,
+ float sunphi,
+ float suntheta,
+ color radiance,
+ float config_x[9],
+ float config_y[9],
+ float config_z[9])
{
/* convert vector to spherical coordinates */
vector spherical = sky_spherical_coordinates(dir);
@@ -88,13 +88,13 @@ float sky_radiance_internal(float config[9], float theta, float gamma)
(config[2] + config[3] * expM + config[5] * rayM + config[6] * mieM + config[7] * zenith);
}
-color sky_radiance_new(normal dir,
- float sunphi,
- float suntheta,
- color radiance,
- float config_x[9],
- float config_y[9],
- float config_z[9])
+color sky_radiance_hosek(normal dir,
+ float sunphi,
+ float suntheta,
+ color radiance,
+ float config_x[9],
+ float config_y[9],
+ float config_z[9])
{
/* convert vector to spherical coordinates */
vector spherical = sky_spherical_coordinates(dir);
@@ -116,16 +116,103 @@ color sky_radiance_new(normal dir,
return xyz_to_rgb(x, y, z) * (M_2PI / 683);
}
+/* Nishita improved */
+vector geographical_to_direction(float lat, float lon)
+{
+ return vector(cos(lat) * cos(lon), cos(lat) * sin(lon), sin(lat));
+}
+
+color sky_radiance_nishita(vector dir, float nishita_data[9], string filename)
+{
+ /* definitions */
+ float sun_elevation = nishita_data[6];
+ float sun_rotation = nishita_data[7];
+ float angular_diameter = nishita_data[8];
+ int sun_disc = angular_diameter > 0;
+ float alpha = 1.0;
+ color xyz;
+ /* convert dir to spherical coordinates */
+ vector direction = sky_spherical_coordinates(dir);
+
+ /* render above the horizon */
+ if (dir[2] >= 0.0) {
+ /* definitions */
+ vector sun_dir = geographical_to_direction(sun_elevation, sun_rotation + M_PI_2);
+ float sun_dir_angle = acos(dot(dir, sun_dir));
+ float half_angular = angular_diameter / 2.0;
+ float dir_elevation = M_PI_2 - direction[0];
+
+ /* if ray inside sun disc render it, otherwise render sky */
+ if (sun_dir_angle < half_angular && sun_disc == 1) {
+ /* get 3 pixels data */
+ color pixel_bottom = color(nishita_data[0], nishita_data[1], nishita_data[2]);
+ color pixel_top = color(nishita_data[3], nishita_data[4], nishita_data[5]);
+ float y;
+
+ /* sun interpolation */
+ if (sun_elevation - half_angular > 0.0) {
+ if ((sun_elevation + half_angular) > 0.0) {
+ y = ((dir_elevation - sun_elevation) / angular_diameter) + 0.5;
+ xyz = mix(pixel_bottom, pixel_top, y);
+ }
+ }
+ else {
+ if (sun_elevation + half_angular > 0.0) {
+ y = dir_elevation / (sun_elevation + half_angular);
+ xyz = mix(pixel_bottom, pixel_top, y);
+ }
+ }
+ /* limb darkening, coefficient is 0.6f */
+ float angle_fraction = sun_dir_angle / half_angular;
+ float limb_darkening = (1.0 - 0.6 * (1.0 - sqrt(1.0 - angle_fraction * angle_fraction)));
+ xyz *= limb_darkening;
+ }
+ /* sky */
+ else {
+ /* sky interpolation */
+ float x = (direction[1] + M_PI + sun_rotation) / M_2PI;
+ float y = 1.0 - (dir_elevation / M_PI_2);
+ if (x > 1.0) {
+ x = x - 1.0;
+ }
+ xyz = (color)texture(filename, x, y, "wrap", "clamp", "interp", "linear", "alpha", alpha);
+ }
+ }
+ /* ground */
+ else {
+ if (dir[2] < -0.4) {
+ xyz = color(0, 0, 0);
+ }
+ else {
+ /* black ground fade */
+ float mul = pow(1.0 + dir[2] * 2.5, 3.0);
+ /* interpolation */
+ float x = (direction[1] + M_PI + sun_rotation) / M_2PI;
+ float y = 1.5;
+ if (x > 1.0) {
+ x = x - 1.0;
+ }
+ xyz = (color)texture(
+ filename, x, y, "wrap", "periodic", "interp", "linear", "alpha", alpha) *
+ mul;
+ }
+ }
+ /* convert to RGB and adjust strength */
+ return xyz_to_rgb(xyz[0], xyz[1], xyz[2]) * 120000.0;
+}
+
shader node_sky_texture(int use_mapping = 0,
matrix mapping = matrix(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
vector Vector = P,
string type = "hosek_wilkie",
float theta = 0.0,
float phi = 0.0,
+ string filename = "",
color radiance = color(0.0, 0.0, 0.0),
float config_x[9] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
float config_y[9] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
float config_z[9] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
+ float nishita_data[9] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
output color Color = color(0.0, 0.0, 0.0))
{
vector p = Vector;
@@ -133,8 +220,10 @@ shader node_sky_texture(int use_mapping = 0,
if (use_mapping)
p = transform(mapping, p);
+ if (type == "nishita_improved")
+ Color = sky_radiance_nishita(p, nishita_data, filename);
if (type == "hosek_wilkie")
- Color = sky_radiance_new(p, phi, theta, radiance, config_x, config_y, config_z);
- else
- Color = sky_radiance_old(p, phi, theta, radiance, config_x, config_y, config_z);
+ Color = sky_radiance_hosek(p, phi, theta, radiance, config_x, config_y, config_z);
+ if (type == "preetham")
+ Color = sky_radiance_preetham(p, phi, theta, radiance, config_x, config_y, config_z);
}
diff --git a/intern/cycles/kernel/svm/svm_closure.h b/intern/cycles/kernel/svm/svm_closure.h
index 2c57a142692..1ae94f1d766 100644
--- a/intern/cycles/kernel/svm/svm_closure.h
+++ b/intern/cycles/kernel/svm/svm_closure.h
@@ -847,39 +847,29 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg,
case CLOSURE_BSDF_HAIR_TRANSMISSION_ID: {
float3 weight = sd->svm_closure_weight * mix_weight;
- if (sd->flag & SD_BACKFACING && sd->type & PRIMITIVE_ALL_CURVE) {
- /* todo: giving a fixed weight here will cause issues when
- * mixing multiple BSDFS. energy will not be conserved and
- * the throughput can blow up after multiple bounces. we
- * better figure out a way to skip backfaces from rays
- * spawned by transmission from the front */
- bsdf_transparent_setup(sd, make_float3(1.0f, 1.0f, 1.0f), path_flag);
- }
- else {
- HairBsdf *bsdf = (HairBsdf *)bsdf_alloc(sd, sizeof(HairBsdf), weight);
+ HairBsdf *bsdf = (HairBsdf *)bsdf_alloc(sd, sizeof(HairBsdf), weight);
- if (bsdf) {
- bsdf->N = N;
- bsdf->roughness1 = param1;
- bsdf->roughness2 = param2;
- bsdf->offset = -stack_load_float(stack, data_node.z);
+ if (bsdf) {
+ bsdf->N = N;
+ bsdf->roughness1 = param1;
+ bsdf->roughness2 = param2;
+ bsdf->offset = -stack_load_float(stack, data_node.z);
- if (stack_valid(data_node.y)) {
- bsdf->T = normalize(stack_load_float3(stack, data_node.y));
- }
- else if (!(sd->type & PRIMITIVE_ALL_CURVE)) {
- bsdf->T = normalize(sd->dPdv);
- bsdf->offset = 0.0f;
- }
- else
- bsdf->T = normalize(sd->dPdu);
+ if (stack_valid(data_node.y)) {
+ bsdf->T = normalize(stack_load_float3(stack, data_node.y));
+ }
+ else if (!(sd->type & PRIMITIVE_ALL_CURVE)) {
+ bsdf->T = normalize(sd->dPdv);
+ bsdf->offset = 0.0f;
+ }
+ else
+ bsdf->T = normalize(sd->dPdu);
- if (type == CLOSURE_BSDF_HAIR_REFLECTION_ID) {
- sd->flag |= bsdf_hair_reflection_setup(bsdf);
- }
- else {
- sd->flag |= bsdf_hair_transmission_setup(bsdf);
- }
+ if (type == CLOSURE_BSDF_HAIR_REFLECTION_ID) {
+ sd->flag |= bsdf_hair_reflection_setup(bsdf);
+ }
+ else {
+ sd->flag |= bsdf_hair_transmission_setup(bsdf);
}
}
diff --git a/intern/cycles/kernel/svm/svm_geometry.h b/intern/cycles/kernel/svm/svm_geometry.h
index 019c6294082..77df19b2298 100644
--- a/intern/cycles/kernel/svm/svm_geometry.h
+++ b/intern/cycles/kernel/svm/svm_geometry.h
@@ -41,11 +41,9 @@ ccl_device_inline void svm_node_geometry(
case NODE_GEOM_Ng:
data = sd->Ng;
break;
-#ifdef __UV__
case NODE_GEOM_uv:
data = make_float3(sd->u, sd->v, 0.0f);
break;
-#endif
default:
data = make_float3(0.0f, 0.0f, 0.0f);
}
diff --git a/intern/cycles/kernel/svm/svm_noise.h b/intern/cycles/kernel/svm/svm_noise.h
index 914ef2089a9..7db8ffcc6e1 100644
--- a/intern/cycles/kernel/svm/svm_noise.h
+++ b/intern/cycles/kernel/svm/svm_noise.h
@@ -573,8 +573,8 @@ ccl_device_inline ssef quad_mix(avxf p, avxf q, ssef f)
*
* Point Offset from v0
* v0 (0, 0, 0)
- * v1 (0, 0, 1) The full avx type is computed by inserting the following
- * v2 (0, 1, 0) sse types into both the low and high parts of the avx.
+ * v1 (0, 0, 1) The full AVX type is computed by inserting the following
+ * v2 (0, 1, 0) SSE types into both the low and high parts of the AVX.
* v3 (0, 1, 1)
* v4 (1, 0, 0)
* v5 (1, 0, 1) (0, 1, 0, 1) = shuffle<0, 2, 0, 2>(shuffle<2, 2, 2, 2>(V, V + 1))
diff --git a/intern/cycles/kernel/svm/svm_sky.h b/intern/cycles/kernel/svm/svm_sky.h
index 50fe0c8232f..e877bd9a5c8 100644
--- a/intern/cycles/kernel/svm/svm_sky.h
+++ b/intern/cycles/kernel/svm/svm_sky.h
@@ -37,16 +37,16 @@ ccl_device float sky_perez_function(float *lam, float theta, float gamma)
(1.0f + lam[2] * expf(lam[3] * gamma) + lam[4] * cgamma * cgamma);
}
-ccl_device float3 sky_radiance_old(KernelGlobals *kg,
- float3 dir,
- float sunphi,
- float suntheta,
- float radiance_x,
- float radiance_y,
- float radiance_z,
- float *config_x,
- float *config_y,
- float *config_z)
+ccl_device float3 sky_radiance_preetham(KernelGlobals *kg,
+ float3 dir,
+ float sunphi,
+ float suntheta,
+ float radiance_x,
+ float radiance_y,
+ float radiance_z,
+ float *config_x,
+ float *config_y,
+ float *config_z)
{
/* convert vector to spherical coordinates */
float2 spherical = direction_to_spherical(dir);
@@ -90,16 +90,16 @@ ccl_device float sky_radiance_internal(float *configuration, float theta, float
configuration[6] * mieM + configuration[7] * zenith);
}
-ccl_device float3 sky_radiance_new(KernelGlobals *kg,
- float3 dir,
- float sunphi,
- float suntheta,
- float radiance_x,
- float radiance_y,
- float radiance_z,
- float *config_x,
- float *config_y,
- float *config_z)
+ccl_device float3 sky_radiance_hosek(KernelGlobals *kg,
+ float3 dir,
+ float sunphi,
+ float suntheta,
+ float radiance_x,
+ float radiance_y,
+ float radiance_z,
+ float *config_x,
+ float *config_y,
+ float *config_z)
{
/* convert vector to spherical coordinates */
float2 spherical = direction_to_spherical(dir);
@@ -121,93 +121,206 @@ ccl_device float3 sky_radiance_new(KernelGlobals *kg,
return xyz_to_rgb(kg, make_float3(x, y, z)) * (M_2PI_F / 683);
}
+/* Nishita improved sky model */
+ccl_device float3 geographical_to_direction(float lat, float lon)
+{
+ return make_float3(cos(lat) * cos(lon), cos(lat) * sin(lon), sin(lat));
+}
+
+ccl_device float3 sky_radiance_nishita(KernelGlobals *kg,
+ float3 dir,
+ float *nishita_data,
+ uint texture_id)
+{
+ /* definitions */
+ float sun_elevation = nishita_data[6];
+ float sun_rotation = nishita_data[7];
+ float angular_diameter = nishita_data[8];
+ bool sun_disc = (angular_diameter > 0.0f);
+ float3 xyz;
+ /* convert dir to spherical coordinates */
+ float2 direction = direction_to_spherical(dir);
+
+ /* render above the horizon */
+ if (dir.z >= 0.0f) {
+ /* definitions */
+ float3 sun_dir = geographical_to_direction(sun_elevation, sun_rotation + M_PI_2_F);
+ float sun_dir_angle = acos(dot(dir, sun_dir));
+ float half_angular = angular_diameter / 2.0f;
+ float dir_elevation = M_PI_2_F - direction.x;
+
+ /* if ray inside sun disc render it, otherwise render sky */
+ if (sun_disc && sun_dir_angle < half_angular) {
+ /* get 3 pixels data */
+ float3 pixel_bottom = make_float3(nishita_data[0], nishita_data[1], nishita_data[2]);
+ float3 pixel_top = make_float3(nishita_data[3], nishita_data[4], nishita_data[5]);
+ float y;
+
+ /* sun interpolation */
+ if (sun_elevation - half_angular > 0.0f) {
+ if (sun_elevation + half_angular > 0.0f) {
+ y = ((dir_elevation - sun_elevation) / angular_diameter) + 0.5f;
+ xyz = interp(pixel_bottom, pixel_top, y);
+ }
+ }
+ else {
+ if (sun_elevation + half_angular > 0.0f) {
+ y = dir_elevation / (sun_elevation + half_angular);
+ xyz = interp(pixel_bottom, pixel_top, y);
+ }
+ }
+ /* limb darkening, coefficient is 0.6f */
+ float limb_darkening = (1.0f -
+ 0.6f * (1.0f - sqrtf(1.0f - sqr(sun_dir_angle / half_angular))));
+ xyz *= limb_darkening;
+ }
+ /* sky */
+ else {
+ /* sky interpolation */
+ float x = (direction.y + M_PI_F + sun_rotation) / M_2PI_F;
+ float y = dir_elevation / M_PI_2_F;
+ if (x > 1.0f) {
+ x -= 1.0f;
+ }
+ xyz = float4_to_float3(kernel_tex_image_interp(kg, texture_id, x, y));
+ }
+ }
+ /* ground */
+ else {
+ if (dir.z < -0.4f) {
+ xyz = make_float3(0.0f, 0.0f, 0.0f);
+ }
+ else {
+ /* black ground fade */
+ float fade = 1.0f + dir.z * 2.5f;
+ fade = sqr(fade) * fade;
+ /* interpolation */
+ float x = (direction.y + M_PI_F + sun_rotation) / M_2PI_F;
+ if (x > 1.0f) {
+ x -= 1.0f;
+ }
+ xyz = float4_to_float3(kernel_tex_image_interp(kg, texture_id, x, -0.5)) * fade;
+ }
+ }
+
+ /* convert to rgb and adjust strength */
+ return xyz_to_rgb(kg, xyz) * 120000.0f;
+}
+
ccl_device void svm_node_tex_sky(
KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int *offset)
{
- /* Define variables */
- float sunphi, suntheta, radiance_x, radiance_y, radiance_z;
- float config_x[9], config_y[9], config_z[9];
-
/* Load data */
uint dir_offset = node.y;
uint out_offset = node.z;
int sky_model = node.w;
- float4 data = read_node_float(kg, offset);
- sunphi = data.x;
- suntheta = data.y;
- radiance_x = data.z;
- radiance_y = data.w;
-
- data = read_node_float(kg, offset);
- radiance_z = data.x;
- config_x[0] = data.y;
- config_x[1] = data.z;
- config_x[2] = data.w;
-
- data = read_node_float(kg, offset);
- config_x[3] = data.x;
- config_x[4] = data.y;
- config_x[5] = data.z;
- config_x[6] = data.w;
-
- data = read_node_float(kg, offset);
- config_x[7] = data.x;
- config_x[8] = data.y;
- config_y[0] = data.z;
- config_y[1] = data.w;
-
- data = read_node_float(kg, offset);
- config_y[2] = data.x;
- config_y[3] = data.y;
- config_y[4] = data.z;
- config_y[5] = data.w;
-
- data = read_node_float(kg, offset);
- config_y[6] = data.x;
- config_y[7] = data.y;
- config_y[8] = data.z;
- config_z[0] = data.w;
-
- data = read_node_float(kg, offset);
- config_z[1] = data.x;
- config_z[2] = data.y;
- config_z[3] = data.z;
- config_z[4] = data.w;
-
- data = read_node_float(kg, offset);
- config_z[5] = data.x;
- config_z[6] = data.y;
- config_z[7] = data.z;
- config_z[8] = data.w;
-
float3 dir = stack_load_float3(stack, dir_offset);
float3 f;
- /* Compute Sky */
- if (sky_model == 0) {
- f = sky_radiance_old(kg,
- dir,
- sunphi,
- suntheta,
- radiance_x,
- radiance_y,
- radiance_z,
- config_x,
- config_y,
- config_z);
+ /* Preetham and Hosek share the same data */
+ if (sky_model == 0 || sky_model == 1) {
+ /* Define variables */
+ float sunphi, suntheta, radiance_x, radiance_y, radiance_z;
+ float config_x[9], config_y[9], config_z[9];
+
+ float4 data = read_node_float(kg, offset);
+ sunphi = data.x;
+ suntheta = data.y;
+ radiance_x = data.z;
+ radiance_y = data.w;
+
+ data = read_node_float(kg, offset);
+ radiance_z = data.x;
+ config_x[0] = data.y;
+ config_x[1] = data.z;
+ config_x[2] = data.w;
+
+ data = read_node_float(kg, offset);
+ config_x[3] = data.x;
+ config_x[4] = data.y;
+ config_x[5] = data.z;
+ config_x[6] = data.w;
+
+ data = read_node_float(kg, offset);
+ config_x[7] = data.x;
+ config_x[8] = data.y;
+ config_y[0] = data.z;
+ config_y[1] = data.w;
+
+ data = read_node_float(kg, offset);
+ config_y[2] = data.x;
+ config_y[3] = data.y;
+ config_y[4] = data.z;
+ config_y[5] = data.w;
+
+ data = read_node_float(kg, offset);
+ config_y[6] = data.x;
+ config_y[7] = data.y;
+ config_y[8] = data.z;
+ config_z[0] = data.w;
+
+ data = read_node_float(kg, offset);
+ config_z[1] = data.x;
+ config_z[2] = data.y;
+ config_z[3] = data.z;
+ config_z[4] = data.w;
+
+ data = read_node_float(kg, offset);
+ config_z[5] = data.x;
+ config_z[6] = data.y;
+ config_z[7] = data.z;
+ config_z[8] = data.w;
+
+ /* Compute Sky */
+ if (sky_model == 0) {
+ f = sky_radiance_preetham(kg,
+ dir,
+ sunphi,
+ suntheta,
+ radiance_x,
+ radiance_y,
+ radiance_z,
+ config_x,
+ config_y,
+ config_z);
+ }
+ else {
+ f = sky_radiance_hosek(kg,
+ dir,
+ sunphi,
+ suntheta,
+ radiance_x,
+ radiance_y,
+ radiance_z,
+ config_x,
+ config_y,
+ config_z);
+ }
}
+ /* Nishita */
else {
- f = sky_radiance_new(kg,
- dir,
- sunphi,
- suntheta,
- radiance_x,
- radiance_y,
- radiance_z,
- config_x,
- config_y,
- config_z);
+ /* Define variables */
+ float nishita_data[9];
+
+ float4 data = read_node_float(kg, offset);
+ nishita_data[0] = data.x;
+ nishita_data[1] = data.y;
+ nishita_data[2] = data.z;
+ nishita_data[3] = data.w;
+
+ data = read_node_float(kg, offset);
+ nishita_data[4] = data.x;
+ nishita_data[5] = data.y;
+ nishita_data[6] = data.z;
+ nishita_data[7] = data.w;
+
+ data = read_node_float(kg, offset);
+ nishita_data[8] = data.x;
+ uint texture_id = __float_as_uint(data.y);
+
+ /* Compute Sky */
+ f = sky_radiance_nishita(kg, dir, nishita_data, texture_id);
}
stack_store_float3(stack, out_offset, f);
diff --git a/intern/cycles/kernel/svm/svm_types.h b/intern/cycles/kernel/svm/svm_types.h
index e913d9e0489..f1ebb37e23e 100644
--- a/intern/cycles/kernel/svm/svm_types.h
+++ b/intern/cycles/kernel/svm/svm_types.h
@@ -414,7 +414,7 @@ typedef enum NodeWaveProfile {
NODE_WAVE_PROFILE_TRI,
} NodeWaveProfile;
-typedef enum NodeSkyType { NODE_SKY_OLD, NODE_SKY_NEW } NodeSkyType;
+typedef enum NodeSkyType { NODE_SKY_PREETHAM, NODE_SKY_HOSEK, NODE_SKY_NISHITA } NodeSkyType;
typedef enum NodeGradientType {
NODE_BLEND_LINEAR,
diff --git a/intern/cycles/render/CMakeLists.txt b/intern/cycles/render/CMakeLists.txt
index 472b5a0c101..e37a0407976 100644
--- a/intern/cycles/render/CMakeLists.txt
+++ b/intern/cycles/render/CMakeLists.txt
@@ -24,6 +24,7 @@ set(SRC
hair.cpp
image.cpp
image_oiio.cpp
+ image_sky.cpp
image_vdb.cpp
integrator.cpp
jitter.cpp
@@ -64,6 +65,7 @@ set(SRC_HEADERS
hair.h
image.h
image_oiio.h
+ image_sky.h
image_vdb.h
integrator.h
light.h
diff --git a/intern/cycles/render/curves.cpp b/intern/cycles/render/curves.cpp
index 1907bb33d06..db48d8b6430 100644
--- a/intern/cycles/render/curves.cpp
+++ b/intern/cycles/render/curves.cpp
@@ -36,13 +36,12 @@ void curvebounds(float *lower, float *upper, float3 *p, int dim)
float *p2 = &p[2].x;
float *p3 = &p[3].x;
- float fc = 0.71f;
+ /* Catmull-Rom weights. */
float curve_coef[4];
curve_coef[0] = p1[dim];
- curve_coef[1] = -fc * p0[dim] + fc * p2[dim];
- curve_coef[2] = 2.0f * fc * p0[dim] + (fc - 3.0f) * p1[dim] + (3.0f - 2.0f * fc) * p2[dim] -
- fc * p3[dim];
- curve_coef[3] = -fc * p0[dim] + (2.0f - fc) * p1[dim] + (fc - 2.0f) * p2[dim] + fc * p3[dim];
+ curve_coef[1] = 0.5f * (-p0[dim] + p2[dim]);
+ curve_coef[2] = 0.5f * (2 * p0[dim] - 5 * p1[dim] + 4 * p2[dim] - p3[dim]);
+ curve_coef[3] = 0.5f * (-p0[dim] + 3 * p1[dim] - 3 * p2[dim] + p3[dim]);
float discroot = curve_coef[2] * curve_coef[2] - 3 * curve_coef[3] * curve_coef[1];
float ta = -1.0f;
@@ -77,105 +76,4 @@ void curvebounds(float *lower, float *upper, float3 *p, int dim)
*lower = min(*lower, min(exa, exb));
}
-/* Hair System Manager */
-
-CurveSystemManager::CurveSystemManager()
-{
- primitive = CURVE_LINE_SEGMENTS;
- curve_shape = CURVE_THICK;
- line_method = CURVE_CORRECTED;
- triangle_method = CURVE_CAMERA_TRIANGLES;
- resolution = 3;
- subdivisions = 3;
-
- use_curves = true;
- use_encasing = true;
- use_backfacing = false;
- use_tangent_normal_geometry = false;
-
- need_update = true;
- need_mesh_update = false;
-}
-
-CurveSystemManager::~CurveSystemManager()
-{
-}
-
-void CurveSystemManager::device_update(Device *device,
- DeviceScene *dscene,
- Scene * /*scene*/,
- Progress &progress)
-{
- if (!need_update)
- return;
-
- device_free(device, dscene);
-
- progress.set_status("Updating Hair settings", "Copying Hair settings to device");
-
- KernelCurves *kcurve = &dscene->data.curve;
-
- kcurve->curveflags = 0;
-
- if (use_curves) {
- if (primitive == CURVE_SEGMENTS || primitive == CURVE_RIBBONS)
- kcurve->curveflags |= CURVE_KN_INTERPOLATE;
- if (primitive == CURVE_RIBBONS)
- kcurve->curveflags |= CURVE_KN_RIBBONS;
-
- if (line_method == CURVE_ACCURATE)
- kcurve->curveflags |= CURVE_KN_ACCURATE;
- else if (line_method == CURVE_CORRECTED)
- kcurve->curveflags |= CURVE_KN_INTERSECTCORRECTION;
-
- if (use_tangent_normal_geometry)
- kcurve->curveflags |= CURVE_KN_TRUETANGENTGNORMAL;
- if (use_backfacing)
- kcurve->curveflags |= CURVE_KN_BACKFACING;
- if (use_encasing)
- kcurve->curveflags |= CURVE_KN_ENCLOSEFILTER;
-
- kcurve->subdivisions = subdivisions;
- }
-
- if (progress.get_cancel())
- return;
-
- need_update = false;
-}
-
-void CurveSystemManager::device_free(Device * /*device*/, DeviceScene * /*dscene*/)
-{
-}
-
-bool CurveSystemManager::modified(const CurveSystemManager &CurveSystemManager)
-{
- return !(
- curve_shape == CurveSystemManager.curve_shape &&
- line_method == CurveSystemManager.line_method && primitive == CurveSystemManager.primitive &&
- use_encasing == CurveSystemManager.use_encasing &&
- use_tangent_normal_geometry == CurveSystemManager.use_tangent_normal_geometry &&
- use_backfacing == CurveSystemManager.use_backfacing &&
- triangle_method == CurveSystemManager.triangle_method &&
- resolution == CurveSystemManager.resolution && use_curves == CurveSystemManager.use_curves &&
- subdivisions == CurveSystemManager.subdivisions);
-}
-
-bool CurveSystemManager::modified_mesh(const CurveSystemManager &CurveSystemManager)
-{
- return !(
- primitive == CurveSystemManager.primitive && curve_shape == CurveSystemManager.curve_shape &&
- triangle_method == CurveSystemManager.triangle_method &&
- resolution == CurveSystemManager.resolution && use_curves == CurveSystemManager.use_curves);
-}
-
-void CurveSystemManager::tag_update(Scene * /*scene*/)
-{
- need_update = true;
-}
-
-void CurveSystemManager::tag_update_mesh()
-{
- need_mesh_update = true;
-}
CCL_NAMESPACE_END
diff --git a/intern/cycles/render/curves.h b/intern/cycles/render/curves.h
index ade289a402e..c52fcb9c882 100644
--- a/intern/cycles/render/curves.h
+++ b/intern/cycles/render/curves.h
@@ -20,6 +20,8 @@
#include "util/util_array.h"
#include "util/util_types.h"
+#include "render/hair.h"
+
CCL_NAMESPACE_BEGIN
class Device;
@@ -29,33 +31,6 @@ class Scene;
void curvebounds(float *lower, float *upper, float3 *p, int dim);
-typedef enum CurvePrimitiveType {
- CURVE_TRIANGLES = 0,
- CURVE_LINE_SEGMENTS = 1,
- CURVE_SEGMENTS = 2,
- CURVE_RIBBONS = 3,
-
- CURVE_NUM_PRIMITIVE_TYPES,
-} CurvePrimitiveType;
-
-typedef enum CurveShapeType {
- CURVE_RIBBON = 0,
- CURVE_THICK = 1,
-
- CURVE_NUM_SHAPE_TYPES,
-} CurveShapeType;
-
-typedef enum CurveTriangleMethod {
- CURVE_CAMERA_TRIANGLES,
- CURVE_TESSELATED_TRIANGLES
-} CurveTriangleMethod;
-
-typedef enum CurveLineMethod {
- CURVE_ACCURATE,
- CURVE_CORRECTED,
- CURVE_UNCORRECTED
-} CurveLineMethod;
-
class ParticleCurveData {
public:
@@ -75,43 +50,12 @@ class ParticleCurveData {
array<int> curve_keynum;
array<float> curve_length;
array<float2> curve_uv;
- array<float3> curve_vcol;
+ array<float4> curve_vcol;
array<float3> curvekey_co;
array<float> curvekey_time;
};
-/* HairSystem Manager */
-
-class CurveSystemManager {
- public:
- CurvePrimitiveType primitive;
- CurveShapeType curve_shape;
- CurveLineMethod line_method;
- CurveTriangleMethod triangle_method;
- int resolution;
- int subdivisions;
-
- bool use_curves;
- bool use_encasing;
- bool use_backfacing;
- bool use_tangent_normal_geometry;
-
- bool need_update;
- bool need_mesh_update;
-
- CurveSystemManager();
- ~CurveSystemManager();
-
- void device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress);
- void device_free(Device *device, DeviceScene *dscene);
- bool modified(const CurveSystemManager &CurveSystemManager);
- bool modified_mesh(const CurveSystemManager &CurveSystemManager);
-
- void tag_update(Scene *scene);
- void tag_update_mesh();
-};
-
CCL_NAMESPACE_END
#endif /* __CURVES_H__ */
diff --git a/intern/cycles/render/denoising.cpp b/intern/cycles/render/denoising.cpp
index 4d819d1119e..4055bc4773b 100644
--- a/intern/cycles/render/denoising.cpp
+++ b/intern/cycles/render/denoising.cpp
@@ -21,6 +21,7 @@
#include "util/util_foreach.h"
#include "util/util_map.h"
#include "util/util_system.h"
+#include "util/util_task.h"
#include "util/util_time.h"
#include <OpenImageIO/filesystem.h>
@@ -377,8 +378,9 @@ void DenoiseTask::create_task(DeviceTask &task)
/* Denoising parameters. */
task.denoising = denoiser->params;
- task.denoising_do_filter = true;
- task.denoising_write_passes = false;
+ task.denoising.type = DENOISER_NLM;
+ task.denoising.use = true;
+ task.denoising.store_passes = false;
task.denoising_from_render = false;
task.denoising_frames.resize(neighbor_frames.size());
diff --git a/intern/cycles/render/geometry.cpp b/intern/cycles/render/geometry.cpp
index d46ed430c4f..3d1b6e1d865 100644
--- a/intern/cycles/render/geometry.cpp
+++ b/intern/cycles/render/geometry.cpp
@@ -16,10 +16,9 @@
#include "bvh/bvh.h"
#include "bvh/bvh_build.h"
+#include "bvh/bvh_embree.h"
-#ifdef WITH_EMBREE
-# include "bvh/bvh_embree.h"
-#endif
+#include "device/device.h"
#include "render/attribute.h"
#include "render/camera.h"
@@ -212,8 +211,7 @@ void Geometry::compute_bvh(
bparams.num_motion_triangle_steps = params->num_bvh_time_steps;
bparams.num_motion_curve_steps = params->num_bvh_time_steps;
bparams.bvh_type = params->bvh_type;
- bparams.curve_flags = dscene->data.curve.curveflags;
- bparams.curve_subdivisions = dscene->data.curve.subdivisions;
+ bparams.curve_subdivisions = params->curve_subdivisions();
delete bvh;
bvh = BVH::create(bparams, geometry, objects);
@@ -1027,28 +1025,18 @@ void GeometryManager::device_update_bvh(Device *device,
bparams.num_motion_triangle_steps = scene->params.num_bvh_time_steps;
bparams.num_motion_curve_steps = scene->params.num_bvh_time_steps;
bparams.bvh_type = scene->params.bvh_type;
- bparams.curve_flags = dscene->data.curve.curveflags;
- bparams.curve_subdivisions = dscene->data.curve.subdivisions;
+ bparams.curve_subdivisions = scene->params.curve_subdivisions();
VLOG(1) << "Using " << bvh_layout_name(bparams.bvh_layout) << " layout.";
-#ifdef WITH_EMBREE
- if (bparams.bvh_layout == BVH_LAYOUT_EMBREE) {
- if (dscene->data.bvh.scene) {
- BVHEmbree::destroy(dscene->data.bvh.scene);
- }
- }
-#endif
-
BVH *bvh = BVH::create(bparams, scene->geometry, scene->objects);
bvh->build(progress, &device->stats);
if (progress.get_cancel()) {
#ifdef WITH_EMBREE
- if (bparams.bvh_layout == BVH_LAYOUT_EMBREE) {
- if (dscene->data.bvh.scene) {
- BVHEmbree::destroy(dscene->data.bvh.scene);
- }
+ if (dscene->data.bvh.scene) {
+ BVHEmbree::destroy(dscene->data.bvh.scene);
+ dscene->data.bvh.scene = NULL;
}
#endif
delete bvh;
@@ -1104,6 +1092,7 @@ void GeometryManager::device_update_bvh(Device *device,
dscene->data.bvh.root = pack.root_index;
dscene->data.bvh.bvh_layout = bparams.bvh_layout;
dscene->data.bvh.use_bvh_steps = (scene->params.num_bvh_time_steps != 0);
+ dscene->data.bvh.curve_subdivisions = scene->params.curve_subdivisions();
bvh->copy_to_device(progress, dscene);
@@ -1146,6 +1135,12 @@ void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Pro
create_volume_mesh(mesh, progress);
}
}
+
+ if (geom->type == Geometry::HAIR) {
+ /* Set curve shape, still a global scene setting for now. */
+ Hair *hair = static_cast<Hair *>(geom);
+ hair->curve_shape = scene->params.hair_shape;
+ }
}
need_flags_update = false;
@@ -1413,6 +1408,14 @@ void GeometryManager::device_update(Device *device,
void GeometryManager::device_free(Device *device, DeviceScene *dscene)
{
+#ifdef WITH_EMBREE
+ if (dscene->data.bvh.scene) {
+ if (dscene->data.bvh.bvh_layout == BVH_LAYOUT_EMBREE)
+ BVHEmbree::destroy(dscene->data.bvh.scene);
+ dscene->data.bvh.scene = NULL;
+ }
+#endif
+
dscene->bvh_nodes.free();
dscene->bvh_leaf_nodes.free();
dscene->object_node.free();
diff --git a/intern/cycles/render/hair.cpp b/intern/cycles/render/hair.cpp
index 3daa4cc1e35..816c15cf4ef 100644
--- a/intern/cycles/render/hair.cpp
+++ b/intern/cycles/render/hair.cpp
@@ -294,6 +294,7 @@ NODE_DEFINE(Hair)
Hair::Hair() : Geometry(node_type, Geometry::HAIR)
{
curvekey_offset = 0;
+ curve_shape = CURVE_RIBBON;
}
Hair::~Hair()
diff --git a/intern/cycles/render/hair.h b/intern/cycles/render/hair.h
index 79f77a78753..39d6a34d799 100644
--- a/intern/cycles/render/hair.h
+++ b/intern/cycles/render/hair.h
@@ -96,6 +96,7 @@ class Hair : public Geometry {
/* BVH */
size_t curvekey_offset;
+ CurveShapeType curve_shape;
/* Constructor/Destructor */
Hair();
diff --git a/intern/cycles/render/image.cpp b/intern/cycles/render/image.cpp
index 75050b66bf2..8d187814d64 100644
--- a/intern/cycles/render/image.cpp
+++ b/intern/cycles/render/image.cpp
@@ -27,6 +27,7 @@
#include "util/util_logging.h"
#include "util/util_path.h"
#include "util/util_progress.h"
+#include "util/util_task.h"
#include "util/util_texture.h"
#include "util/util_unique_ptr.h"
diff --git a/intern/cycles/render/image.h b/intern/cycles/render/image.h
index 2000582ce70..fffe7c5152a 100644
--- a/intern/cycles/render/image.h
+++ b/intern/cycles/render/image.h
@@ -17,7 +17,6 @@
#ifndef __IMAGE_H__
#define __IMAGE_H__
-#include "device/device.h"
#include "device/device_memory.h"
#include "render/colorspace.h"
@@ -31,6 +30,7 @@
CCL_NAMESPACE_BEGIN
class Device;
+class DeviceInfo;
class ImageHandle;
class ImageKey;
class ImageMetaData;
diff --git a/intern/cycles/render/image_sky.cpp b/intern/cycles/render/image_sky.cpp
new file mode 100644
index 00000000000..442e1d7941f
--- /dev/null
+++ b/intern/cycles/render/image_sky.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2011-2020 Blender Foundation
+ *
+ * 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.
+ */
+
+#include "render/image_sky.h"
+
+#include "util/util_image.h"
+#include "util/util_logging.h"
+#include "util/util_path.h"
+#include "util/util_sky_model.h"
+#include "util/util_task.h"
+
+CCL_NAMESPACE_BEGIN
+
+SkyLoader::SkyLoader(
+ float sun_elevation, int altitude, float air_density, float dust_density, float ozone_density)
+ : sun_elevation(sun_elevation),
+ altitude(altitude),
+ air_density(air_density),
+ dust_density(dust_density),
+ ozone_density(ozone_density)
+{
+}
+
+SkyLoader::~SkyLoader(){};
+
+bool SkyLoader::load_metadata(ImageMetaData &metadata)
+{
+ metadata.width = 512;
+ metadata.height = 128;
+ metadata.channels = 3;
+ metadata.depth = 1;
+ metadata.type = IMAGE_DATA_TYPE_FLOAT4;
+ metadata.compress_as_srgb = false;
+ return true;
+}
+
+bool SkyLoader::load_pixels(const ImageMetaData &metadata,
+ void *pixels,
+ const size_t /*pixels_size*/,
+ const bool /*associate_alpha*/)
+{
+ /* definitions */
+ int width = metadata.width;
+ int height = metadata.height;
+ float *pixel_data = (float *)pixels;
+ float altitude_f = (float)altitude;
+
+ /* precompute sky texture */
+ const int rows_per_task = divide_up(1024, width);
+ parallel_for(blocked_range<size_t>(0, height, rows_per_task),
+ [&](const blocked_range<size_t> &r) {
+ nishita_skymodel_precompute_texture(pixel_data,
+ metadata.channels,
+ r.begin(),
+ r.end(),
+ width,
+ height,
+ sun_elevation,
+ altitude_f,
+ air_density,
+ dust_density,
+ ozone_density);
+ });
+
+ return true;
+}
+
+string SkyLoader::name() const
+{
+ return "sky_nishita";
+}
+
+bool SkyLoader::equals(const ImageLoader & /*other*/) const
+{
+ return false;
+}
+
+CCL_NAMESPACE_END
diff --git a/intern/cycles/render/image_sky.h b/intern/cycles/render/image_sky.h
new file mode 100644
index 00000000000..cf4a3e8942c
--- /dev/null
+++ b/intern/cycles/render/image_sky.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2011-2020 Blender Foundation
+ *
+ * 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.
+ */
+
+#include "render/image.h"
+
+CCL_NAMESPACE_BEGIN
+
+class SkyLoader : public ImageLoader {
+ private:
+ float sun_elevation;
+ int altitude;
+ float air_density;
+ float dust_density;
+ float ozone_density;
+
+ public:
+ SkyLoader(float sun_elevation,
+ int altitude,
+ float air_density,
+ float dust_density,
+ float ozone_density);
+ ~SkyLoader();
+
+ bool load_metadata(ImageMetaData &metadata) override;
+
+ bool load_pixels(const ImageMetaData &metadata,
+ void *pixels,
+ const size_t /*pixels_size*/,
+ const bool /*associate_alpha*/) override;
+
+ string name() const override;
+
+ bool equals(const ImageLoader & /*other*/) const override;
+};
+
+CCL_NAMESPACE_END
diff --git a/intern/cycles/render/integrator.cpp b/intern/cycles/render/integrator.cpp
index d4beb06e57b..eff416efa2b 100644
--- a/intern/cycles/render/integrator.cpp
+++ b/intern/cycles/render/integrator.cpp
@@ -29,6 +29,7 @@
#include "util/util_foreach.h"
#include "util/util_hash.h"
#include "util/util_logging.h"
+#include "util/util_task.h"
CCL_NAMESPACE_BEGIN
diff --git a/intern/cycles/render/light.cpp b/intern/cycles/render/light.cpp
index cb7474017fa..c0615c6217b 100644
--- a/intern/cycles/render/light.cpp
+++ b/intern/cycles/render/light.cpp
@@ -31,6 +31,7 @@
#include "util/util_logging.h"
#include "util/util_path.h"
#include "util/util_progress.h"
+#include "util/util_task.h"
CCL_NAMESPACE_BEGIN
@@ -450,6 +451,7 @@ void LightManager::device_update_distribution(Device *,
/* update device */
KernelIntegrator *kintegrator = &dscene->data.integrator;
+ KernelBackground *kbackground = &dscene->data.background;
KernelFilm *kfilm = &dscene->data.film;
kintegrator->use_direct_light = (totarea > 0.0f);
@@ -493,15 +495,18 @@ void LightManager::device_update_distribution(Device *,
/* Portals */
if (num_portals > 0) {
- kintegrator->portal_offset = light_index;
- kintegrator->num_portals = num_portals;
- kintegrator->portal_pdf = background_mis ? 0.5f : 1.0f;
+ kbackground->portal_offset = light_index;
+ kbackground->num_portals = num_portals;
+ kbackground->portal_weight = 1.0f;
}
else {
- kintegrator->num_portals = 0;
- kintegrator->portal_offset = 0;
- kintegrator->portal_pdf = 0.0f;
+ kbackground->num_portals = 0;
+ kbackground->portal_offset = 0;
+ kbackground->portal_weight = 0.0f;
}
+
+ /* Map */
+ kbackground->map_weight = background_mis ? 1.0f : 0.0f;
}
else {
dscene->light_distribution.free();
@@ -511,9 +516,12 @@ void LightManager::device_update_distribution(Device *,
kintegrator->pdf_triangles = 0.0f;
kintegrator->pdf_lights = 0.0f;
kintegrator->use_lamp_mis = false;
- kintegrator->num_portals = 0;
- kintegrator->portal_offset = 0;
- kintegrator->portal_pdf = 0.0f;
+
+ kbackground->num_portals = 0;
+ kbackground->portal_offset = 0;
+ kbackground->portal_weight = 0.0f;
+ kbackground->sun_weight = 0.0f;
+ kbackground->map_weight = 0.0f;
kfilm->pass_shadow_scale = 1.0f;
}
@@ -562,7 +570,7 @@ void LightManager::device_update_background(Device *device,
Scene *scene,
Progress &progress)
{
- KernelIntegrator *kintegrator = &dscene->data.integrator;
+ KernelBackground *kbackground = &dscene->data.background;
Light *background_light = NULL;
/* find background light */
@@ -575,31 +583,79 @@ void LightManager::device_update_background(Device *device,
/* no background light found, signal renderer to skip sampling */
if (!background_light || !background_light->is_enabled) {
- kintegrator->pdf_background_res_x = 0;
- kintegrator->pdf_background_res_y = 0;
+ kbackground->map_res_x = 0;
+ kbackground->map_res_y = 0;
+ kbackground->map_weight = 0.0f;
+ kbackground->sun_weight = 0.0f;
+ kbackground->use_mis = (kbackground->portal_weight > 0.0f);
return;
}
progress.set_status("Updating Lights", "Importance map");
- assert(kintegrator->use_direct_light);
+ assert(dscene->data.integrator.use_direct_light);
+
+ int2 environment_res = make_int2(0, 0);
+ Shader *shader = scene->background->get_shader(scene);
+ int num_suns = 0;
+ foreach (ShaderNode *node, shader->graph->nodes) {
+ if (node->type == EnvironmentTextureNode::node_type) {
+ EnvironmentTextureNode *env = (EnvironmentTextureNode *)node;
+ ImageMetaData metadata;
+ if (!env->handle.empty()) {
+ ImageMetaData metadata = env->handle.metadata();
+ environment_res.x = max(environment_res.x, metadata.width);
+ environment_res.y = max(environment_res.y, metadata.height);
+ }
+ }
+ if (node->type == SkyTextureNode::node_type) {
+ SkyTextureNode *sky = (SkyTextureNode *)node;
+ if (sky->type == NODE_SKY_NISHITA && sky->sun_disc) {
+ /* Ensure that the input coordinates aren't transformed before they reach the node.
+ * If that is the case, the logic used for sampling the sun's location does not work
+ * and we have to fall back to map-based sampling. */
+ const ShaderInput *vec_in = sky->input("Vector");
+ if (vec_in && vec_in->link && vec_in->link->parent) {
+ ShaderNode *vec_src = vec_in->link->parent;
+ if ((vec_src->type != TextureCoordinateNode::node_type) ||
+ (vec_in->link != vec_src->output("Generated"))) {
+ environment_res.x = max(environment_res.x, 4096);
+ environment_res.y = max(environment_res.y, 2048);
+ continue;
+ }
+ }
+
+ float latitude = sky->sun_elevation;
+ float longitude = M_2PI_F - sky->sun_rotation + M_PI_2_F;
+ float half_angle = sky->sun_size * 0.5f;
+ kbackground->sun = make_float4(cosf(latitude) * cosf(longitude),
+ cosf(latitude) * sinf(longitude),
+ sinf(latitude),
+ half_angle);
+ kbackground->sun_weight = 4.0f;
+ environment_res.x = max(environment_res.x, 512);
+ environment_res.y = max(environment_res.y, 256);
+ num_suns++;
+ }
+ }
+ }
+
+ /* If there's more than one sun, fall back to map sampling instead. */
+ if (num_suns != 1) {
+ kbackground->sun_weight = 0.0f;
+ environment_res.x = max(environment_res.x, 4096);
+ environment_res.y = max(environment_res.y, 2048);
+ }
+
+ /* Enable MIS for background sampling if any strategy is active. */
+ kbackground->use_mis = (kbackground->portal_weight + kbackground->map_weight +
+ kbackground->sun_weight) > 0.0f;
/* get the resolution from the light's size (we stuff it in there) */
int2 res = make_int2(background_light->map_resolution, background_light->map_resolution / 2);
/* If the resolution isn't set manually, try to find an environment texture. */
if (res.x == 0) {
- Shader *shader = scene->background->get_shader(scene);
- foreach (ShaderNode *node, shader->graph->nodes) {
- if (node->type == EnvironmentTextureNode::node_type) {
- EnvironmentTextureNode *env = (EnvironmentTextureNode *)node;
- ImageMetaData metadata;
- if (!env->handle.empty()) {
- ImageMetaData metadata = env->handle.metadata();
- res.x = max(res.x, metadata.width);
- res.y = max(res.y, metadata.height);
- }
- }
- }
+ res = environment_res;
if (res.x > 0 && res.y > 0) {
VLOG(2) << "Automatically set World MIS resolution to " << res.x << " by " << res.y << "\n";
}
@@ -609,8 +665,8 @@ void LightManager::device_update_background(Device *device,
res = make_int2(1024, 512);
VLOG(2) << "Setting World MIS resolution to default\n";
}
- kintegrator->pdf_background_res_x = res.x;
- kintegrator->pdf_background_res_y = res.y;
+ kbackground->map_res_x = res.x;
+ kbackground->map_res_y = res.y;
vector<float3> pixels;
shade_background_pixels(device, dscene, res.x, res.y, pixels, progress);
@@ -624,29 +680,13 @@ void LightManager::device_update_background(Device *device,
float2 *cond_cdf = dscene->light_background_conditional_cdf.alloc(cdf_width * res.y);
double time_start = time_dt();
- if (max(res.x, res.y) < 512) {
- /* Small enough resolution, faster to do single-threaded. */
- background_cdf(0, res.y, res.x, res.y, &pixels, cond_cdf);
- }
- else {
- /* Threaded evaluation for large resolution. */
- const int num_blocks = TaskScheduler::num_threads();
- const int chunk_size = res.y / num_blocks;
- int start_row = 0;
- TaskPool pool;
- for (int i = 0; i < num_blocks; ++i) {
- const int current_chunk_size = (i != num_blocks - 1) ? chunk_size : (res.y - i * chunk_size);
- pool.push(function_bind(&background_cdf,
- start_row,
- start_row + current_chunk_size,
- res.x,
- res.y,
- &pixels,
- cond_cdf));
- start_row += current_chunk_size;
- }
- pool.wait_work();
- }
+
+ /* Create CDF in parallel. */
+ const int rows_per_task = divide_up(10240, res.x);
+ parallel_for(blocked_range<size_t>(0, res.y, rows_per_task),
+ [&](const blocked_range<size_t> &r) {
+ background_cdf(r.begin(), r.end(), res.x, res.y, &pixels, cond_cdf);
+ });
/* marginal CDFs (column, V direction, sum of rows) */
marg_cdf[0].x = cond_cdf[res.x].x;
diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp
index cdcaeb246dd..ab392839e52 100644
--- a/intern/cycles/render/nodes.cpp
+++ b/intern/cycles/render/nodes.cpp
@@ -19,6 +19,7 @@
#include "render/constant_fold.h"
#include "render/film.h"
#include "render/image.h"
+#include "render/image_sky.h"
#include "render/integrator.h"
#include "render/light.h"
#include "render/mesh.h"
@@ -630,7 +631,7 @@ typedef struct SunSky {
/* Parameter */
float radiance_x, radiance_y, radiance_z;
- float config_x[9], config_y[9], config_z[9];
+ float config_x[9], config_y[9], config_z[9], nishita_data[9];
} SunSky;
/* Preetham model */
@@ -640,7 +641,7 @@ static float sky_perez_function(float lam[6], float theta, float gamma)
(1.0f + lam[2] * expf(lam[3] * gamma) + lam[4] * cosf(gamma) * cosf(gamma));
}
-static void sky_texture_precompute_old(SunSky *sunsky, float3 dir, float turbidity)
+static void sky_texture_precompute_preetham(SunSky *sunsky, float3 dir, float turbidity)
{
/*
* We re-use the SunSky struct of the new model, to avoid extra variables
@@ -703,10 +704,10 @@ static void sky_texture_precompute_old(SunSky *sunsky, float3 dir, float turbidi
}
/* Hosek / Wilkie */
-static void sky_texture_precompute_new(SunSky *sunsky,
- float3 dir,
- float turbidity,
- float ground_albedo)
+static void sky_texture_precompute_hosek(SunSky *sunsky,
+ float3 dir,
+ float turbidity,
+ float ground_albedo)
{
/* Calculate Sun Direction and save coordinates */
float2 spherical = sky_spherical_coordinates(dir);
@@ -743,6 +744,34 @@ static void sky_texture_precompute_new(SunSky *sunsky,
arhosekskymodelstate_free(sky_state);
}
+/* Nishita improved */
+static void sky_texture_precompute_nishita(SunSky *sunsky,
+ bool sun_disc,
+ float sun_size,
+ float sun_elevation,
+ float sun_rotation,
+ int altitude,
+ float air_density,
+ float dust_density)
+{
+ /* sample 2 sun pixels */
+ float pixel_bottom[3];
+ float pixel_top[3];
+ float altitude_f = (float)altitude;
+ nishita_skymodel_precompute_sun(
+ sun_elevation, sun_size, altitude_f, air_density, dust_density, pixel_bottom, pixel_top);
+ /* send data to svm_sky */
+ sunsky->nishita_data[0] = pixel_bottom[0];
+ sunsky->nishita_data[1] = pixel_bottom[1];
+ sunsky->nishita_data[2] = pixel_bottom[2];
+ sunsky->nishita_data[3] = pixel_top[0];
+ sunsky->nishita_data[4] = pixel_top[1];
+ sunsky->nishita_data[5] = pixel_top[2];
+ sunsky->nishita_data[6] = sun_elevation;
+ sunsky->nishita_data[7] = M_2PI_F - sun_rotation;
+ sunsky->nishita_data[8] = sun_disc ? sun_size : 0.0f;
+}
+
NODE_DEFINE(SkyTextureNode)
{
NodeType *type = NodeType::add("sky_texture", create, NodeType::SHADER);
@@ -750,13 +779,22 @@ NODE_DEFINE(SkyTextureNode)
TEXTURE_MAPPING_DEFINE(SkyTextureNode);
static NodeEnum type_enum;
- type_enum.insert("preetham", NODE_SKY_OLD);
- type_enum.insert("hosek_wilkie", NODE_SKY_NEW);
- SOCKET_ENUM(type, "Type", type_enum, NODE_SKY_NEW);
+ type_enum.insert("preetham", NODE_SKY_PREETHAM);
+ type_enum.insert("hosek_wilkie", NODE_SKY_HOSEK);
+ type_enum.insert("nishita_improved", NODE_SKY_NISHITA);
+ SOCKET_ENUM(type, "Type", type_enum, NODE_SKY_NISHITA);
SOCKET_VECTOR(sun_direction, "Sun Direction", make_float3(0.0f, 0.0f, 1.0f));
SOCKET_FLOAT(turbidity, "Turbidity", 2.2f);
SOCKET_FLOAT(ground_albedo, "Ground Albedo", 0.3f);
+ SOCKET_BOOLEAN(sun_disc, "Sun Disc", true);
+ SOCKET_FLOAT(sun_size, "Sun Size", 0.009512f);
+ SOCKET_FLOAT(sun_elevation, "Sun Elevation", M_PI_2_F);
+ SOCKET_FLOAT(sun_rotation, "Sun Rotation", 0.0f);
+ SOCKET_INT(altitude, "Altitude", 0);
+ SOCKET_FLOAT(air_density, "Air", 1.0f);
+ SOCKET_FLOAT(dust_density, "Dust", 1.0f);
+ SOCKET_FLOAT(ozone_density, "Ozone", 1.0f);
SOCKET_IN_POINT(
vector, "Vector", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_TEXTURE_GENERATED);
@@ -776,10 +814,32 @@ void SkyTextureNode::compile(SVMCompiler &compiler)
ShaderOutput *color_out = output("Color");
SunSky sunsky;
- if (type == NODE_SKY_OLD)
- sky_texture_precompute_old(&sunsky, sun_direction, turbidity);
- else if (type == NODE_SKY_NEW)
- sky_texture_precompute_new(&sunsky, sun_direction, turbidity, ground_albedo);
+ if (type == NODE_SKY_PREETHAM)
+ sky_texture_precompute_preetham(&sunsky, sun_direction, turbidity);
+ else if (type == NODE_SKY_HOSEK)
+ sky_texture_precompute_hosek(&sunsky, sun_direction, turbidity, ground_albedo);
+ else if (type == NODE_SKY_NISHITA) {
+ sky_texture_precompute_nishita(&sunsky,
+ sun_disc,
+ sun_size,
+ sun_elevation,
+ sun_rotation,
+ altitude,
+ air_density,
+ dust_density);
+ /* precomputed texture image parameters */
+ ImageManager *image_manager = compiler.scene->image_manager;
+ ImageParams impar;
+ impar.interpolation = INTERPOLATION_LINEAR;
+ impar.extension = EXTENSION_EXTEND;
+
+ /* precompute sky texture */
+ if (handle.empty()) {
+ SkyLoader *loader = new SkyLoader(
+ sun_elevation, altitude, air_density, dust_density, ozone_density);
+ handle = image_manager->add_image(loader, impar);
+ }
+ }
else
assert(false);
@@ -787,38 +847,52 @@ void SkyTextureNode::compile(SVMCompiler &compiler)
compiler.stack_assign(color_out);
compiler.add_node(NODE_TEX_SKY, vector_offset, compiler.stack_assign(color_out), type);
- compiler.add_node(__float_as_uint(sunsky.phi),
- __float_as_uint(sunsky.theta),
- __float_as_uint(sunsky.radiance_x),
- __float_as_uint(sunsky.radiance_y));
- compiler.add_node(__float_as_uint(sunsky.radiance_z),
- __float_as_uint(sunsky.config_x[0]),
- __float_as_uint(sunsky.config_x[1]),
- __float_as_uint(sunsky.config_x[2]));
- compiler.add_node(__float_as_uint(sunsky.config_x[3]),
- __float_as_uint(sunsky.config_x[4]),
- __float_as_uint(sunsky.config_x[5]),
- __float_as_uint(sunsky.config_x[6]));
- compiler.add_node(__float_as_uint(sunsky.config_x[7]),
- __float_as_uint(sunsky.config_x[8]),
- __float_as_uint(sunsky.config_y[0]),
- __float_as_uint(sunsky.config_y[1]));
- compiler.add_node(__float_as_uint(sunsky.config_y[2]),
- __float_as_uint(sunsky.config_y[3]),
- __float_as_uint(sunsky.config_y[4]),
- __float_as_uint(sunsky.config_y[5]));
- compiler.add_node(__float_as_uint(sunsky.config_y[6]),
- __float_as_uint(sunsky.config_y[7]),
- __float_as_uint(sunsky.config_y[8]),
- __float_as_uint(sunsky.config_z[0]));
- compiler.add_node(__float_as_uint(sunsky.config_z[1]),
- __float_as_uint(sunsky.config_z[2]),
- __float_as_uint(sunsky.config_z[3]),
- __float_as_uint(sunsky.config_z[4]));
- compiler.add_node(__float_as_uint(sunsky.config_z[5]),
- __float_as_uint(sunsky.config_z[6]),
- __float_as_uint(sunsky.config_z[7]),
- __float_as_uint(sunsky.config_z[8]));
+ /* nishita doesn't need this data */
+ if (type != NODE_SKY_NISHITA) {
+ compiler.add_node(__float_as_uint(sunsky.phi),
+ __float_as_uint(sunsky.theta),
+ __float_as_uint(sunsky.radiance_x),
+ __float_as_uint(sunsky.radiance_y));
+ compiler.add_node(__float_as_uint(sunsky.radiance_z),
+ __float_as_uint(sunsky.config_x[0]),
+ __float_as_uint(sunsky.config_x[1]),
+ __float_as_uint(sunsky.config_x[2]));
+ compiler.add_node(__float_as_uint(sunsky.config_x[3]),
+ __float_as_uint(sunsky.config_x[4]),
+ __float_as_uint(sunsky.config_x[5]),
+ __float_as_uint(sunsky.config_x[6]));
+ compiler.add_node(__float_as_uint(sunsky.config_x[7]),
+ __float_as_uint(sunsky.config_x[8]),
+ __float_as_uint(sunsky.config_y[0]),
+ __float_as_uint(sunsky.config_y[1]));
+ compiler.add_node(__float_as_uint(sunsky.config_y[2]),
+ __float_as_uint(sunsky.config_y[3]),
+ __float_as_uint(sunsky.config_y[4]),
+ __float_as_uint(sunsky.config_y[5]));
+ compiler.add_node(__float_as_uint(sunsky.config_y[6]),
+ __float_as_uint(sunsky.config_y[7]),
+ __float_as_uint(sunsky.config_y[8]),
+ __float_as_uint(sunsky.config_z[0]));
+ compiler.add_node(__float_as_uint(sunsky.config_z[1]),
+ __float_as_uint(sunsky.config_z[2]),
+ __float_as_uint(sunsky.config_z[3]),
+ __float_as_uint(sunsky.config_z[4]));
+ compiler.add_node(__float_as_uint(sunsky.config_z[5]),
+ __float_as_uint(sunsky.config_z[6]),
+ __float_as_uint(sunsky.config_z[7]),
+ __float_as_uint(sunsky.config_z[8]));
+ }
+ else {
+ compiler.add_node(__float_as_uint(sunsky.nishita_data[0]),
+ __float_as_uint(sunsky.nishita_data[1]),
+ __float_as_uint(sunsky.nishita_data[2]),
+ __float_as_uint(sunsky.nishita_data[3]));
+ compiler.add_node(__float_as_uint(sunsky.nishita_data[4]),
+ __float_as_uint(sunsky.nishita_data[5]),
+ __float_as_uint(sunsky.nishita_data[6]),
+ __float_as_uint(sunsky.nishita_data[7]));
+ compiler.add_node(__float_as_uint(sunsky.nishita_data[8]), handle.svm_slot(), 0, 0);
+ }
tex_mapping.compile_end(compiler, vector_in, vector_offset);
}
@@ -828,10 +902,32 @@ void SkyTextureNode::compile(OSLCompiler &compiler)
tex_mapping.compile(compiler);
SunSky sunsky;
- if (type == NODE_SKY_OLD)
- sky_texture_precompute_old(&sunsky, sun_direction, turbidity);
- else if (type == NODE_SKY_NEW)
- sky_texture_precompute_new(&sunsky, sun_direction, turbidity, ground_albedo);
+ if (type == NODE_SKY_PREETHAM)
+ sky_texture_precompute_preetham(&sunsky, sun_direction, turbidity);
+ else if (type == NODE_SKY_HOSEK)
+ sky_texture_precompute_hosek(&sunsky, sun_direction, turbidity, ground_albedo);
+ else if (type == NODE_SKY_NISHITA) {
+ sky_texture_precompute_nishita(&sunsky,
+ sun_disc,
+ sun_size,
+ sun_elevation,
+ sun_rotation,
+ altitude,
+ air_density,
+ dust_density);
+ /* precomputed texture image parameters */
+ ImageManager *image_manager = compiler.scene->image_manager;
+ ImageParams impar;
+ impar.interpolation = INTERPOLATION_LINEAR;
+ impar.extension = EXTENSION_EXTEND;
+
+ /* precompute sky texture */
+ if (handle.empty()) {
+ SkyLoader *loader = new SkyLoader(
+ sun_elevation, altitude, air_density, dust_density, ozone_density);
+ handle = image_manager->add_image(loader, impar);
+ }
+ }
else
assert(false);
@@ -843,6 +939,11 @@ void SkyTextureNode::compile(OSLCompiler &compiler)
compiler.parameter_array("config_x", sunsky.config_x, 9);
compiler.parameter_array("config_y", sunsky.config_y, 9);
compiler.parameter_array("config_z", sunsky.config_z, 9);
+ compiler.parameter_array("nishita_data", sunsky.nishita_data, 9);
+ /* nishita texture */
+ if (type == NODE_SKY_NISHITA) {
+ compiler.parameter_texture("filename", handle.svm_slot());
+ }
compiler.add(this, "node_sky_texture");
}
diff --git a/intern/cycles/render/nodes.h b/intern/cycles/render/nodes.h
index 83c3ad071ae..846ba7423e5 100644
--- a/intern/cycles/render/nodes.h
+++ b/intern/cycles/render/nodes.h
@@ -168,7 +168,16 @@ class SkyTextureNode : public TextureNode {
float3 sun_direction;
float turbidity;
float ground_albedo;
+ bool sun_disc;
+ float sun_size;
+ float sun_elevation;
+ float sun_rotation;
+ int altitude;
+ float air_density;
+ float dust_density;
+ float ozone_density;
float3 vector;
+ ImageHandle handle;
};
class OutputNode : public ShaderNode {
diff --git a/intern/cycles/render/object.cpp b/intern/cycles/render/object.cpp
index 61deef4cd76..c45ae5553a8 100644
--- a/intern/cycles/render/object.cpp
+++ b/intern/cycles/render/object.cpp
@@ -31,6 +31,7 @@
#include "util/util_murmurhash.h"
#include "util/util_progress.h"
#include "util/util_set.h"
+#include "util/util_task.h"
#include "util/util_vector.h"
#include "subd/subd_patch_table.h"
@@ -77,7 +78,6 @@ struct UpdateObjectTransformState {
Scene *scene;
/* Some locks to keep everything thread-safe. */
- thread_spin_lock queue_lock;
thread_spin_lock surface_area_lock;
/* First unused object index in the queue. */
@@ -219,7 +219,6 @@ void Object::tag_update(Scene *scene)
}
scene->camera->need_flags_update = true;
- scene->curve_system_manager->need_update = true;
scene->geometry_manager->need_update = true;
scene->object_manager->need_update = true;
}
@@ -550,41 +549,6 @@ void ObjectManager::device_update_object_transform(UpdateObjectTransformState *s
}
}
-bool ObjectManager::device_update_object_transform_pop_work(UpdateObjectTransformState *state,
- int *start_index,
- int *num_objects)
-{
- /* Tweakable parameter, number of objects per chunk.
- * Too small value will cause some extra overhead due to spin lock,
- * too big value might not use all threads nicely.
- */
- static const int OBJECTS_PER_TASK = 32;
- bool have_work = false;
- state->queue_lock.lock();
- int num_scene_objects = state->scene->objects.size();
- if (state->queue_start_object < num_scene_objects) {
- int count = min(OBJECTS_PER_TASK, num_scene_objects - state->queue_start_object);
- *start_index = state->queue_start_object;
- *num_objects = count;
- state->queue_start_object += count;
- have_work = true;
- }
- state->queue_lock.unlock();
- return have_work;
-}
-
-void ObjectManager::device_update_object_transform_task(UpdateObjectTransformState *state)
-{
- int start_index, num_objects;
- while (device_update_object_transform_pop_work(state, &start_index, &num_objects)) {
- for (int i = 0; i < num_objects; ++i) {
- const int object_index = start_index + i;
- Object *ob = state->scene->objects[object_index];
- device_update_object_transform(state, ob);
- }
- }
-}
-
void ObjectManager::device_update_transforms(DeviceScene *dscene, Scene *scene, Progress &progress)
{
UpdateObjectTransformState state;
@@ -630,28 +594,19 @@ void ObjectManager::device_update_transforms(DeviceScene *dscene, Scene *scene,
numparticles += psys->particles.size();
}
- /* NOTE: If it's just a handful of objects we deal with them in a single
- * thread to avoid threading overhead. However, this threshold is might
- * need some tweaks to make mid-complex scenes optimal.
- */
- if (scene->objects.size() < 64) {
- foreach (Object *ob, scene->objects) {
- device_update_object_transform(&state, ob);
- if (progress.get_cancel()) {
- return;
- }
- }
- }
- else {
- const int num_threads = TaskScheduler::num_threads();
- TaskPool pool;
- for (int i = 0; i < num_threads; ++i) {
- pool.push(function_bind(&ObjectManager::device_update_object_transform_task, this, &state));
- }
- pool.wait_work();
- if (progress.get_cancel()) {
- return;
- }
+ /* Parallel object update, with grain size to avoid too much threading overhead
+ * for individual objects. */
+ static const int OBJECTS_PER_TASK = 32;
+ parallel_for(blocked_range<size_t>(0, scene->objects.size(), OBJECTS_PER_TASK),
+ [&](const blocked_range<size_t> &r) {
+ for (size_t i = r.begin(); i != r.end(); i++) {
+ Object *ob = state.scene->objects[i];
+ device_update_object_transform(&state, ob);
+ }
+ });
+
+ if (progress.get_cancel()) {
+ return;
}
dscene->objects.copy_to_device();
@@ -664,7 +619,6 @@ void ObjectManager::device_update_transforms(DeviceScene *dscene, Scene *scene,
dscene->data.bvh.have_motion = state.have_motion;
dscene->data.bvh.have_curves = state.have_curves;
- dscene->data.bvh.have_instancing = true;
}
void ObjectManager::device_update(Device *device,
@@ -839,7 +793,6 @@ void ObjectManager::apply_static_transforms(DeviceScene *dscene, Scene *scene, P
bool motion_blur = need_motion == Scene::MOTION_BLUR;
bool apply_to_motion = need_motion != Scene::MOTION_PASS;
int i = 0;
- bool have_instancing = false;
foreach (Object *object, scene->objects) {
map<Geometry *, int>::iterator it = geometry_users.find(object->geometry);
@@ -885,22 +838,15 @@ void ObjectManager::apply_static_transforms(DeviceScene *dscene, Scene *scene, P
if (geom->transform_negative_scaled)
object_flag[i] |= SD_OBJECT_NEGATIVE_SCALE_APPLIED;
}
- else
- have_instancing = true;
}
- else
- have_instancing = true;
i++;
}
-
- dscene->data.bvh.have_instancing = have_instancing;
}
void ObjectManager::tag_update(Scene *scene)
{
need_update = true;
- scene->curve_system_manager->need_update = true;
scene->geometry_manager->need_update = true;
scene->light_manager->need_update = true;
}
diff --git a/intern/cycles/render/scene.cpp b/intern/cycles/render/scene.cpp
index f5b68d5a4fe..9016a8d325f 100644
--- a/intern/cycles/render/scene.cpp
+++ b/intern/cycles/render/scene.cpp
@@ -108,7 +108,6 @@ Scene::Scene(const SceneParams &params_, Device *device)
integrator = new Integrator();
image_manager = new ImageManager(device->info);
particle_system_manager = new ParticleSystemManager();
- curve_system_manager = new CurveSystemManager();
bake_manager = new BakeManager();
/* OSL only works on the CPU */
@@ -156,7 +155,6 @@ void Scene::free_memory(bool final)
light_manager->device_free(device, &dscene);
particle_system_manager->device_free(device, &dscene);
- curve_system_manager->device_free(device, &dscene);
bake_manager->device_free(device, &dscene);
@@ -180,7 +178,6 @@ void Scene::free_memory(bool final)
delete shader_manager;
delete light_manager;
delete particle_system_manager;
- delete curve_system_manager;
delete image_manager;
delete bake_manager;
}
@@ -233,12 +230,6 @@ void Scene::device_update(Device *device_, Progress &progress)
if (progress.get_cancel() || device->have_error())
return;
- progress.set_status("Updating Hair Systems");
- curve_system_manager->device_update(device, &dscene, this, progress);
-
- if (progress.get_cancel() || device->have_error())
- return;
-
progress.set_status("Updating Particle Systems");
particle_system_manager->device_update(device, &dscene, this, progress);
@@ -369,8 +360,7 @@ bool Scene::need_data_update()
return (background->need_update || image_manager->need_update || object_manager->need_update ||
geometry_manager->need_update || light_manager->need_update ||
lookup_tables->need_update || integrator->need_update || shader_manager->need_update ||
- particle_system_manager->need_update || curve_system_manager->need_update ||
- bake_manager->need_update || film->need_update);
+ particle_system_manager->need_update || bake_manager->need_update || film->need_update);
}
bool Scene::need_reset()
@@ -393,7 +383,6 @@ void Scene::reset()
geometry_manager->tag_update(this);
light_manager->tag_update(this);
particle_system_manager->tag_update(this);
- curve_system_manager->tag_update(this);
}
void Scene::device_free()
diff --git a/intern/cycles/render/scene.h b/intern/cycles/render/scene.h
index 6b10a901d7b..67616262c03 100644
--- a/intern/cycles/render/scene.h
+++ b/intern/cycles/render/scene.h
@@ -168,6 +168,8 @@ class SceneParams {
bool use_bvh_spatial_split;
bool use_bvh_unaligned_nodes;
int num_bvh_time_steps;
+ int hair_subdivisions;
+ CurveShapeType hair_shape;
bool persistent_data;
int texture_limit;
@@ -181,6 +183,8 @@ class SceneParams {
use_bvh_spatial_split = false;
use_bvh_unaligned_nodes = true;
num_bvh_time_steps = 0;
+ hair_subdivisions = 3;
+ hair_shape = CURVE_RIBBON;
persistent_data = false;
texture_limit = 0;
background = true;
@@ -193,8 +197,15 @@ class SceneParams {
use_bvh_spatial_split == params.use_bvh_spatial_split &&
use_bvh_unaligned_nodes == params.use_bvh_unaligned_nodes &&
num_bvh_time_steps == params.num_bvh_time_steps &&
+ hair_subdivisions == params.hair_subdivisions && hair_shape == params.hair_shape &&
persistent_data == params.persistent_data && texture_limit == params.texture_limit);
}
+
+ int curve_subdivisions()
+ {
+ /* Matching the tesselation rate limit in Embree. */
+ return clamp(1 << hair_subdivisions, 1, 16);
+ }
};
/* Scene */
@@ -226,7 +237,6 @@ class Scene {
GeometryManager *geometry_manager;
ObjectManager *object_manager;
ParticleSystemManager *particle_system_manager;
- CurveSystemManager *curve_system_manager;
BakeManager *bake_manager;
/* default shaders */
diff --git a/intern/cycles/render/session.cpp b/intern/cycles/render/session.cpp
index f5bfebbaf78..1a94d3e9db7 100644
--- a/intern/cycles/render/session.cpp
+++ b/intern/cycles/render/session.cpp
@@ -61,8 +61,10 @@ Session::Session(const SessionParams &params_)
TaskScheduler::init(params.threads);
+ /* Create CPU/GPU devices. */
device = Device::create(params.device, stats, profiler, params.background);
+ /* Create buffers for interactive rendering. */
if (params.background && !params.write_render_cb) {
buffers = NULL;
display = NULL;
@@ -72,6 +74,9 @@ Session::Session(const SessionParams &params_)
display = new DisplayBuffer(device, params.display_buffer_linear);
}
+ /* Validate denoising parameters. */
+ set_denoising(params.denoising);
+
session_thread = NULL;
scene = NULL;
@@ -773,6 +778,7 @@ DeviceRequestedFeatures Session::get_requested_device_features()
*/
bool use_motion = scene->need_motion() == Scene::MotionType::MOTION_BLUR;
requested_features.use_hair = false;
+ requested_features.use_hair_thick = (scene->params.hair_shape == CURVE_THICK);
requested_features.use_object_motion = false;
requested_features.use_camera_motion = use_motion && scene->camera->use_motion();
foreach (Object *object, scene->objects) {
@@ -804,7 +810,7 @@ DeviceRequestedFeatures Session::get_requested_device_features()
requested_features.use_baking = bake_manager->get_baking();
requested_features.use_integrator_branched = (scene->integrator->method ==
Integrator::BRANCHED_PATH);
- if (params.run_denoising) {
+ if (params.denoising.use || params.denoising.store_passes) {
requested_features.use_denoising = true;
requested_features.use_shadow_tricks = true;
}
@@ -941,24 +947,35 @@ void Session::set_pause(bool pause_)
pause_cond.notify_all();
}
-void Session::set_denoising(bool denoising, bool optix_denoising)
+void Session::set_denoising(const DenoiseParams &denoising)
{
+ bool need_denoise = denoising.need_denoising_task();
+
/* Lock buffers so no denoising operation is triggered while the settings are changed here. */
thread_scoped_lock buffers_lock(buffers_mutex);
+ params.denoising = denoising;
+
+ if (!(params.device.denoisers & denoising.type)) {
+ if (need_denoise) {
+ progress.set_error("Denoiser type not supported by compute device");
+ }
- params.run_denoising = denoising;
- params.full_denoising = !optix_denoising;
- params.optix_denoising = optix_denoising;
+ params.denoising.use = false;
+ need_denoise = false;
+ }
// TODO(pmours): Query the required overlap value for denoising from the device?
- tile_manager.slice_overlap = denoising && !params.background ? 64 : 0;
- tile_manager.schedule_denoising = denoising && !buffers;
+ tile_manager.slice_overlap = need_denoise && !params.background ? 64 : 0;
+
+ /* Schedule per tile denoising for final renders if we are either denoising or
+ * need prefiltered passes for the native denoiser. */
+ tile_manager.schedule_denoising = need_denoise && !buffers;
}
void Session::set_denoising_start_sample(int sample)
{
- if (sample != params.denoising_start_sample) {
- params.denoising_start_sample = sample;
+ if (sample != params.denoising.start_sample) {
+ params.denoising.start_sample = sample;
pause_cond.notify_all();
}
@@ -1078,10 +1095,10 @@ void Session::update_status_time(bool show_pause, bool show_done)
*/
substatus += string_printf(", Sample %d/%d", progress.get_current_sample(), num_samples);
}
- if (params.full_denoising || params.optix_denoising) {
+ if (params.denoising.use && params.denoising.type != DENOISER_OPENIMAGEDENOISE) {
substatus += string_printf(", Denoised %d tiles", progress.get_denoised_tiles());
}
- else if (params.run_denoising) {
+ else if (params.denoising.store_passes && params.denoising.type == DENOISER_NLM) {
substatus += string_printf(", Prefiltered %d tiles", progress.get_denoised_tiles());
}
}
@@ -1110,7 +1127,7 @@ bool Session::render_need_denoise(bool &delayed)
delayed = false;
/* Denoising enabled? */
- if (!params.run_denoising) {
+ if (!params.denoising.need_denoising_task()) {
return false;
}
@@ -1127,7 +1144,7 @@ bool Session::render_need_denoise(bool &delayed)
}
/* Do not denoise until the sample at which denoising should start is reached. */
- if (tile_manager.state.sample < params.denoising_start_sample) {
+ if (tile_manager.state.sample < min(params.denoising.start_sample, params.samples - 1)) {
return false;
}
@@ -1178,9 +1195,6 @@ void Session::render(bool need_denoise)
task.pass_denoising_clean = scene->film->denoising_clean_offset;
task.denoising_from_render = true;
- task.denoising_do_filter = params.full_denoising;
- task.denoising_use_optix = params.optix_denoising;
- task.denoising_write_passes = params.write_denoising_passes;
if (tile_manager.schedule_denoising) {
/* Acquire denoising tiles during rendering. */
diff --git a/intern/cycles/render/session.h b/intern/cycles/render/session.h
index 2707eed5531..0141629762c 100644
--- a/intern/cycles/render/session.h
+++ b/intern/cycles/render/session.h
@@ -62,10 +62,6 @@ class SessionParams {
bool display_buffer_linear;
- bool run_denoising;
- bool write_denoising_passes;
- bool full_denoising;
- bool optix_denoising;
DenoiseParams denoising;
double cancel_timeout;
@@ -94,11 +90,6 @@ class SessionParams {
use_profiling = false;
- run_denoising = false;
- write_denoising_passes = false;
- full_denoising = false;
- optix_denoising = false;
-
display_buffer_linear = false;
cancel_timeout = 0.1;
@@ -125,7 +116,8 @@ class SessionParams {
cancel_timeout == params.cancel_timeout && reset_timeout == params.reset_timeout &&
text_timeout == params.text_timeout &&
progressive_update_timeout == params.progressive_update_timeout &&
- tile_order == params.tile_order && shadingsystem == params.shadingsystem);
+ tile_order == params.tile_order && shadingsystem == params.shadingsystem &&
+ denoising.type == params.denoising.type);
}
};
@@ -161,7 +153,7 @@ class Session {
void reset(BufferParams &params, int samples);
void set_pause(bool pause);
void set_samples(int samples);
- void set_denoising(bool denoising, bool optix_denoising);
+ void set_denoising(const DenoiseParams &denoising);
void set_denoising_start_sample(int sample);
bool update_scene();
diff --git a/intern/cycles/render/shader.cpp b/intern/cycles/render/shader.cpp
index 39ba45a751a..1120d909e98 100644
--- a/intern/cycles/render/shader.cpp
+++ b/intern/cycles/render/shader.cpp
@@ -33,6 +33,7 @@
#include "util/util_foreach.h"
#include "util/util_murmurhash.h"
+#include "util/util_task.h"
#ifdef WITH_OCIO
# include <OpenColorIO/OpenColorIO.h>
diff --git a/intern/cycles/render/svm.cpp b/intern/cycles/render/svm.cpp
index ea3dbaf8e03..88714e20a90 100644
--- a/intern/cycles/render/svm.cpp
+++ b/intern/cycles/render/svm.cpp
@@ -94,8 +94,7 @@ void SVMShaderManager::device_update(Device *device,
scene,
scene->shaders[i],
&progress,
- &shader_svm_nodes[i]),
- false);
+ &shader_svm_nodes[i]));
}
task_pool.wait_work();
diff --git a/intern/cycles/test/render_graph_finalize_test.cpp b/intern/cycles/test/render_graph_finalize_test.cpp
index 87389ebfb16..4ea3470cda8 100644
--- a/intern/cycles/test/render_graph_finalize_test.cpp
+++ b/intern/cycles/test/render_graph_finalize_test.cpp
@@ -17,11 +17,15 @@
#include "testing/mock_log.h"
#include "testing/testing.h"
+#include "device/device.h"
+
#include "render/graph.h"
#include "render/nodes.h"
#include "render/scene.h"
+
#include "util/util_array.h"
#include "util/util_logging.h"
+#include "util/util_stats.h"
#include "util/util_string.h"
#include "util/util_vector.h"
diff --git a/intern/cycles/util/CMakeLists.txt b/intern/cycles/util/CMakeLists.txt
index c1f71461dfd..ad4ea9c86e0 100644
--- a/intern/cycles/util/CMakeLists.txt
+++ b/intern/cycles/util/CMakeLists.txt
@@ -29,7 +29,7 @@ set(SRC
)
set(LIB
-
+ ${TBB_LIBRARIES}
)
if(WITH_CYCLES_STANDALONE)
@@ -86,6 +86,7 @@ set(SRC_HEADERS
util_math_matrix.h
util_md5.h
util_murmurhash.h
+ util_openimagedenoise.h
util_opengl.h
util_optimization.h
util_param.h
@@ -100,6 +101,7 @@ set(SRC_HEADERS
util_sky_model.cpp
util_sky_model.h
util_sky_model_data.h
+ util_sky_nishita.cpp
util_avxf.h
util_avxb.h
util_semaphore.h
@@ -112,6 +114,7 @@ set(SRC_HEADERS
util_string.h
util_system.h
util_task.h
+ util_tbb.h
util_texture.h
util_thread.h
util_time.h
diff --git a/intern/cycles/util/util_debug.cpp b/intern/cycles/util/util_debug.cpp
index 3ce65802cff..6ad4f709ab5 100644
--- a/intern/cycles/util/util_debug.cpp
+++ b/intern/cycles/util/util_debug.cpp
@@ -31,7 +31,7 @@ DebugFlags::CPU::CPU()
sse41(true),
sse3(true),
sse2(true),
- bvh_layout(BVH_LAYOUT_DEFAULT),
+ bvh_layout(BVH_LAYOUT_AUTO),
split_kernel(false)
{
reset();
@@ -57,18 +57,7 @@ void DebugFlags::CPU::reset()
#undef STRINGIFY
#undef CHECK_CPU_FLAGS
- if (getenv("CYCLES_BVH2") != NULL) {
- bvh_layout = BVH_LAYOUT_BVH2;
- }
- else if (getenv("CYCLES_BVH4") != NULL) {
- bvh_layout = BVH_LAYOUT_BVH4;
- }
- else if (getenv("CYCLES_BVH8") != NULL) {
- bvh_layout = BVH_LAYOUT_BVH8;
- }
- else {
- bvh_layout = BVH_LAYOUT_DEFAULT;
- }
+ bvh_layout = BVH_LAYOUT_AUTO;
split_kernel = false;
}
diff --git a/intern/cycles/util/util_debug.h b/intern/cycles/util/util_debug.h
index cf6b442b878..da9f5408b59 100644
--- a/intern/cycles/util/util_debug.h
+++ b/intern/cycles/util/util_debug.h
@@ -73,10 +73,10 @@ class DebugFlags {
return sse2;
}
- /* Requested BVH size.
+ /* Requested BVH layout.
*
- * Rendering will use widest possible BVH which is below or equal
- * this one.
+ * By default the fastest will be used. For debugging the BVH used by other
+ * CPUs and GPUs can be selected here instead.
*/
BVHLayout bvh_layout;
diff --git a/intern/cycles/util/util_math_fast.h b/intern/cycles/util/util_math_fast.h
index dbed83ab84d..e979bd9e0c0 100644
--- a/intern/cycles/util/util_math_fast.h
+++ b/intern/cycles/util/util_math_fast.h
@@ -446,6 +446,11 @@ ccl_device_inline float fast_expf(float x)
}
#ifndef __KERNEL_GPU__
+/* MSVC seems to have a code-gen bug here in at least SSE41/AVX
+ * see T78047 for details. */
+# ifdef _MSC_VER
+# pragma optimize("", off)
+# endif
ccl_device float4 fast_exp2f4(float4 x)
{
const float4 one = make_float4(1.0f);
@@ -461,6 +466,9 @@ ccl_device float4 fast_exp2f4(float4 x)
r = madd4(x, r, make_float4(1.0f));
return __int4_as_float4(__float4_as_int4(r) + (m << 23));
}
+# ifdef _MSC_VER
+# pragma optimize("", on)
+# endif
ccl_device_inline float4 fast_expf4(float4 x)
{
diff --git a/intern/cycles/util/util_openimagedenoise.h b/intern/cycles/util/util_openimagedenoise.h
new file mode 100644
index 00000000000..aafa69cb530
--- /dev/null
+++ b/intern/cycles/util/util_openimagedenoise.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2011-2013 Blender Foundation
+ *
+ * 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.
+ */
+
+#ifndef __UTIL_OPENIMAGEDENOISE_H__
+#define __UTIL_OPENIMAGEDENOISE_H__
+
+#ifdef WITH_OPENIMAGEDENOISE
+# include <OpenImageDenoise/oidn.hpp>
+#endif
+
+#include "util_system.h"
+
+CCL_NAMESPACE_BEGIN
+
+static inline bool openimagedenoise_supported()
+{
+#ifdef WITH_OPENIMAGEDENOISE
+ return system_cpu_support_sse41();
+#else
+ return false;
+#endif
+}
+
+CCL_NAMESPACE_END
+
+#endif /* __UTIL_OPENIMAGEDENOISE_H__ */
diff --git a/intern/cycles/util/util_sky_model.h b/intern/cycles/util/util_sky_model.h
index 84340614b2c..36f1079a16d 100644
--- a/intern/cycles/util/util_sky_model.h
+++ b/intern/cycles/util/util_sky_model.h
@@ -298,6 +298,8 @@ HINT #1: if you want to model the sky of an earth-like planet that orbits
previous paragraph.
*/
+#include "util/util_types.h"
+
CCL_NAMESPACE_BEGIN
#ifndef _SKY_MODEL_H_
@@ -426,4 +428,26 @@ double arhosekskymodel_solar_radiance(ArHosekSkyModelState *state,
#endif // _SKY_MODEL_H_
+/* Nishita improved sky model */
+
+void nishita_skymodel_precompute_texture(float *pixels,
+ int stride,
+ int start_y,
+ int end_y,
+ int width,
+ int height,
+ float sun_elevation,
+ float altitude,
+ float air_density,
+ float dust_density,
+ float ozone_density);
+
+void nishita_skymodel_precompute_sun(float sun_elevation,
+ float angular_diameter,
+ float altitude,
+ float air_density,
+ float dust_density,
+ float *pixel_bottom,
+ float *pixel_top);
+
CCL_NAMESPACE_END
diff --git a/intern/cycles/util/util_sky_nishita.cpp b/intern/cycles/util/util_sky_nishita.cpp
new file mode 100644
index 00000000000..92397804d43
--- /dev/null
+++ b/intern/cycles/util/util_sky_nishita.cpp
@@ -0,0 +1,371 @@
+/*
+ * Copyright 2011-2020 Blender Foundation
+ *
+ * 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.
+ */
+
+#include "util/util_math.h"
+#include "util/util_sky_model.h"
+
+CCL_NAMESPACE_BEGIN
+
+/* Constants */
+static const float rayleigh_scale = 8000.0f; // Rayleigh scale height (m)
+static const float mie_scale = 1200.0f; // Mie scale height (m)
+static const float mie_coeff = 2e-5f; // Mie scattering coefficient
+static const float mie_G = 0.76f; // aerosols anisotropy
+static const float earth_radius = 6360000.0f; // radius of Earth (m)
+static const float atmosphere_radius = 6420000.0f; // radius of atmosphere (m)
+static const int steps = 32; // segments per primary ray
+static const int steps_light = 16; // segments per sun connection ray
+static const int num_wavelengths = 21; // number of wavelengths
+/* irradiance at top of atmosphere */
+static const float irradiance[] = {
+ 1.45756829855592995315f, 1.56596305559738380175f, 1.65148449067670455293f,
+ 1.71496242737209314555f, 1.75797983805020541226f, 1.78256407885924539336f,
+ 1.79095108475838560302f, 1.78541550133410664714f, 1.76815554864306845317f,
+ 1.74122069647250410362f, 1.70647127164943679389f, 1.66556087452739887134f,
+ 1.61993437242451854274f, 1.57083597368892080581f, 1.51932335059305478886f,
+ 1.46628494965214395407f, 1.41245852740172450623f, 1.35844961970384092709f,
+ 1.30474913844739281998f, 1.25174963272610817455f, 1.19975998755420620867f};
+/* Rayleigh scattering coefficient */
+static const float rayleigh_coeff[] = {
+ 0.00005424820087636473f, 0.00004418549866505454f, 0.00003635151910165377f,
+ 0.00003017929012024763f, 0.00002526320226989157f, 0.00002130859310621843f,
+ 0.00001809838025320633f, 0.00001547057129129042f, 0.00001330284977336850f,
+ 0.00001150184784075764f, 0.00000999557429990163f, 0.00000872799973630707f,
+ 0.00000765513700977967f, 0.00000674217203751443f, 0.00000596134125832052f,
+ 0.00000529034598065810f, 0.00000471115687557433f, 0.00000420910481110487f,
+ 0.00000377218381260133f, 0.00000339051255477280f, 0.00000305591531679811f};
+/* Ozone absorption coefficient */
+static const float ozone_coeff[] = {
+ 0.00000000325126849861f, 0.00000000585395365047f, 0.00000001977191155085f,
+ 0.00000007309568762914f, 0.00000020084561514287f, 0.00000040383958096161f,
+ 0.00000063551335912363f, 0.00000096707041180970f, 0.00000154797400424410f,
+ 0.00000209038647223331f, 0.00000246128056164565f, 0.00000273551299461512f,
+ 0.00000215125863128643f, 0.00000159051840791988f, 0.00000112356197979857f,
+ 0.00000073527551487574f, 0.00000046450130357806f, 0.00000033096079921048f,
+ 0.00000022512612292678f, 0.00000014879129266490f, 0.00000016828623364192f};
+/* CIE XYZ color matching functions */
+static const float cmf_xyz[][3] = {{0.00136800000f, 0.00003900000f, 0.00645000100f},
+ {0.01431000000f, 0.00039600000f, 0.06785001000f},
+ {0.13438000000f, 0.00400000000f, 0.64560000000f},
+ {0.34828000000f, 0.02300000000f, 1.74706000000f},
+ {0.29080000000f, 0.06000000000f, 1.66920000000f},
+ {0.09564000000f, 0.13902000000f, 0.81295010000f},
+ {0.00490000000f, 0.32300000000f, 0.27200000000f},
+ {0.06327000000f, 0.71000000000f, 0.07824999000f},
+ {0.29040000000f, 0.95400000000f, 0.02030000000f},
+ {0.59450000000f, 0.99500000000f, 0.00390000000f},
+ {0.91630000000f, 0.87000000000f, 0.00165000100f},
+ {1.06220000000f, 0.63100000000f, 0.00080000000f},
+ {0.85444990000f, 0.38100000000f, 0.00019000000f},
+ {0.44790000000f, 0.17500000000f, 0.00002000000f},
+ {0.16490000000f, 0.06100000000f, 0.00000000000f},
+ {0.04677000000f, 0.01700000000f, 0.00000000000f},
+ {0.01135916000f, 0.00410200000f, 0.00000000000f},
+ {0.00289932700f, 0.00104700000f, 0.00000000000f},
+ {0.00069007860f, 0.00024920000f, 0.00000000000f},
+ {0.00016615050f, 0.00006000000f, 0.00000000000f},
+ {0.00004150994f, 0.00001499000f, 0.00000000000f}};
+
+static float3 geographical_to_direction(float lat, float lon)
+{
+ return make_float3(cosf(lat) * cosf(lon), cosf(lat) * sinf(lon), sinf(lat));
+}
+
+static float3 spec_to_xyz(float *spectrum)
+{
+ float3 xyz = make_float3(0.0f, 0.0f, 0.0f);
+ for (int i = 0; i < num_wavelengths; i++) {
+ xyz.x += cmf_xyz[i][0] * spectrum[i];
+ xyz.y += cmf_xyz[i][1] * spectrum[i];
+ xyz.z += cmf_xyz[i][2] * spectrum[i];
+ }
+ return xyz * (20 * 683 * 1e-9f);
+}
+
+/* Atmosphere volume models */
+
+static float density_rayleigh(float height)
+{
+ return expf(-height / rayleigh_scale);
+}
+
+static float density_mie(float height)
+{
+ return expf(-height / mie_scale);
+}
+
+static float density_ozone(float height)
+{
+ float den = 0.0f;
+ if (height >= 10000.0f && height < 25000.0f)
+ den = 1.0f / 15000.0f * height - 2.0f / 3.0f;
+ else if (height >= 25000 && height < 40000)
+ den = -(1.0f / 15000.0f * height - 8.0f / 3.0f);
+ return den;
+}
+
+static float phase_rayleigh(float mu)
+{
+ return 3.0f / (16.0f * M_PI_F) * (1.0f + sqr(mu));
+}
+
+static float phase_mie(float mu)
+{
+ static const float sqr_G = mie_G * mie_G;
+
+ return (3.0f * (1.0f - sqr_G) * (1.0f + sqr(mu))) /
+ (8.0f * M_PI_F * (2.0f + sqr_G) * powf((1.0f + sqr_G - 2.0f * mie_G * mu), 1.5));
+}
+
+/* Intersection helpers */
+static bool surface_intersection(float3 pos, float3 dir)
+{
+ if (dir.z >= 0)
+ return false;
+ float t = dot(dir, -pos) / len_squared(dir);
+ float D = pos.x * pos.x - 2.0f * (-pos.x) * dir.x * t + dir.x * t * dir.x * t + pos.y * pos.y -
+ 2.0f * (-pos.y) * dir.y * t + (dir.y * t) * (dir.y * t) + pos.z * pos.z -
+ 2.0f * (-pos.z) * dir.z * t + dir.z * t * dir.z * t;
+ return (D <= sqr(earth_radius));
+}
+
+static float3 atmosphere_intersection(float3 pos, float3 dir)
+{
+ float b = -2.0f * dot(dir, -pos);
+ float c = len_squared(pos) - sqr(atmosphere_radius);
+ float t = (-b + sqrtf(b * b - 4.0f * c)) / 2.0f;
+ return make_float3(pos.x + dir.x * t, pos.y + dir.y * t, pos.z + dir.z * t);
+}
+
+static float3 ray_optical_depth(float3 ray_origin, float3 ray_dir)
+{
+ /* This code computes the optical depth along a ray through the atmosphere. */
+ float3 ray_end = atmosphere_intersection(ray_origin, ray_dir);
+ float ray_length = distance(ray_origin, ray_end);
+
+ /* To compute the optical depth, we step along the ray in segments and
+ * accumulate the optical depth along each segment. */
+ float segment_length = ray_length / steps_light;
+ float3 segment = segment_length * ray_dir;
+
+ /* Instead of tracking the transmission spectrum across all wavelengths directly,
+ * we use the fact that the density always has the same spectrum for each type of
+ * scattering, so we split the density into a constant spectrum and a factor and
+ * only track the factors. */
+ float3 optical_depth = make_float3(0.0f, 0.0f, 0.0f);
+
+ /* The density of each segment is evaluated at its middle. */
+ float3 P = ray_origin + 0.5f * segment;
+ for (int i = 0; i < steps_light; i++) {
+ /* Compute height above sea level. */
+ float height = len(P) - earth_radius;
+
+ /* Accumulate optical depth of this segment (density is assumed to be constant along it). */
+ float3 density = make_float3(
+ density_rayleigh(height), density_mie(height), density_ozone(height));
+ optical_depth += segment_length * density;
+
+ /* Advance along ray. */
+ P += segment;
+ }
+
+ return optical_depth;
+}
+
+/* Single Scattering implementation */
+static void single_scattering(float3 ray_dir,
+ float3 sun_dir,
+ float3 ray_origin,
+ float air_density,
+ float dust_density,
+ float ozone_density,
+ float *r_spectrum)
+{
+ /* This code computes single-inscattering along a ray through the atmosphere. */
+ float3 ray_end = atmosphere_intersection(ray_origin, ray_dir);
+ float ray_length = distance(ray_origin, ray_end);
+
+ /* To compute the inscattering, we step along the ray in segments and accumulate
+ * the inscattering as well as the optical depth along each segment. */
+ float segment_length = ray_length / steps;
+ float3 segment = segment_length * ray_dir;
+
+ /* Instead of tracking the transmission spectrum across all wavelengths directly,
+ * we use the fact that the density always has the same spectrum for each type of
+ * scattering, so we split the density into a constant spectrum and a factor and
+ * only track the factors. */
+ float3 optical_depth = make_float3(0.0f, 0.0f, 0.0f);
+
+ /* Zero out light accumulation. */
+ for (int wl = 0; wl < num_wavelengths; wl++) {
+ r_spectrum[wl] = 0.0f;
+ }
+
+ /* Compute phase function for scattering and the density scale factor. */
+ float mu = dot(ray_dir, sun_dir);
+ float3 phase_function = make_float3(phase_rayleigh(mu), phase_mie(mu), 0.0f);
+ float3 density_scale = make_float3(air_density, dust_density, ozone_density);
+
+ /* The density and in-scattering of each segment is evaluated at its middle. */
+ float3 P = ray_origin + 0.5f * segment;
+ for (int i = 0; i < steps; i++) {
+ /* Compute height above sea level. */
+ float height = len(P) - earth_radius;
+
+ /* Evaluate and accumulate optical depth along the ray. */
+ float3 density = density_scale * make_float3(density_rayleigh(height),
+ density_mie(height),
+ density_ozone(height));
+ optical_depth += segment_length * density;
+
+ /* If the earth isn't in the way, evaluate inscattering from the sun. */
+ if (!surface_intersection(P, sun_dir)) {
+ float3 light_optical_depth = density_scale * ray_optical_depth(P, sun_dir);
+ float3 total_optical_depth = optical_depth + light_optical_depth;
+
+ /* attenuation of light */
+ for (int wl = 0; wl < num_wavelengths; wl++) {
+ float3 extinction_density = total_optical_depth * make_float3(rayleigh_coeff[wl],
+ 1.11f * mie_coeff,
+ ozone_coeff[wl]);
+ float attenuation = expf(-reduce_add(extinction_density));
+
+ float3 scattering_density = density * make_float3(rayleigh_coeff[wl], mie_coeff, 0.0f);
+
+ /* The total inscattered radiance from one segment is:
+ * Tr(A<->B) * Tr(B<->C) * sigma_s * phase * L * segment_length
+ *
+ * These terms are:
+ * Tr(A<->B): Transmission from start to scattering position (tracked in optical_depth)
+ * Tr(B<->C): Transmission from scattering position to light (computed in
+ * ray_optical_depth) sigma_s: Scattering density phase: Phase function of the scattering
+ * type (Rayleigh or Mie) L: Radiance coming from the light source segment_length: The
+ * length of the segment
+ *
+ * The code here is just that, with a bit of additional optimization to not store full
+ * spectra for the optical depth.
+ */
+ r_spectrum[wl] += attenuation * reduce_add(phase_function * scattering_density) *
+ irradiance[wl] * segment_length;
+ }
+ }
+
+ /* Advance along ray. */
+ P += segment;
+ }
+}
+
+/* calculate texture array */
+void nishita_skymodel_precompute_texture(float *pixels,
+ int stride,
+ int start_y,
+ int end_y,
+ int width,
+ int height,
+ float sun_elevation,
+ float altitude,
+ float air_density,
+ float dust_density,
+ float ozone_density)
+{
+ /* calculate texture pixels */
+ float spectrum[num_wavelengths];
+ int half_width = width / 2;
+ float3 cam_pos = make_float3(0, 0, earth_radius + altitude);
+ float3 sun_dir = geographical_to_direction(sun_elevation, 0.0f);
+
+ float latitude_step = M_PI_2_F / height;
+ float longitude_step = M_2PI_F / width;
+
+ for (int y = start_y; y < end_y; y++) {
+ float latitude = latitude_step * y;
+
+ float *pixel_row = pixels + (y * width) * stride;
+ for (int x = 0; x < half_width; x++) {
+ float longitude = longitude_step * x - M_PI_F;
+
+ float3 dir = geographical_to_direction(latitude, longitude);
+ single_scattering(dir, sun_dir, cam_pos, air_density, dust_density, ozone_density, spectrum);
+ float3 xyz = spec_to_xyz(spectrum);
+
+ pixel_row[x * stride + 0] = xyz.x;
+ pixel_row[x * stride + 1] = xyz.y;
+ pixel_row[x * stride + 2] = xyz.z;
+ int mirror_x = width - x - 1;
+ pixel_row[mirror_x * stride + 0] = xyz.x;
+ pixel_row[mirror_x * stride + 1] = xyz.y;
+ pixel_row[mirror_x * stride + 2] = xyz.z;
+ }
+ }
+}
+
+/* Sun disc */
+static void sun_radiation(float3 cam_dir,
+ float altitude,
+ float air_density,
+ float dust_density,
+ float solid_angle,
+ float *r_spectrum)
+{
+ float3 cam_pos = make_float3(0, 0, earth_radius + altitude);
+ float3 optical_depth = ray_optical_depth(cam_pos, cam_dir);
+
+ /* Compute final spectrum. */
+ for (int i = 0; i < num_wavelengths; i++) {
+ /* Combine spectra and the optical depth into transmittance. */
+ float transmittance = rayleigh_coeff[i] * optical_depth.x * air_density +
+ 1.11f * mie_coeff * optical_depth.y * dust_density;
+ r_spectrum[i] = (irradiance[i] / solid_angle) * expf(-transmittance);
+ }
+}
+
+void nishita_skymodel_precompute_sun(float sun_elevation,
+ float angular_diameter,
+ float altitude,
+ float air_density,
+ float dust_density,
+ float *pixel_bottom,
+ float *pixel_top)
+{
+ /* definitions */
+ float half_angular = angular_diameter / 2.0f;
+ float solid_angle = M_2PI_F * (1.0f - cosf(half_angular));
+ float spectrum[num_wavelengths];
+ float bottom = sun_elevation - half_angular;
+ float top = sun_elevation + half_angular;
+ float elevation_bottom, elevation_top;
+ float3 pix_bottom, pix_top, sun_dir;
+
+ /* compute 2 pixels for sun disc */
+ elevation_bottom = (bottom > 0.0f) ? bottom : 0.0f;
+ elevation_top = (top > 0.0f) ? top : 0.0f;
+ sun_dir = geographical_to_direction(elevation_bottom, 0.0f);
+ sun_radiation(sun_dir, altitude, air_density, dust_density, solid_angle, spectrum);
+ pix_bottom = spec_to_xyz(spectrum);
+ sun_dir = geographical_to_direction(elevation_top, 0.0f);
+ sun_radiation(sun_dir, altitude, air_density, dust_density, solid_angle, spectrum);
+ pix_top = spec_to_xyz(spectrum);
+
+ /* store pixels */
+ pixel_bottom[0] = pix_bottom.x;
+ pixel_bottom[1] = pix_bottom.y;
+ pixel_bottom[2] = pix_bottom.z;
+ pixel_top[0] = pix_top.x;
+ pixel_top[1] = pix_top.y;
+ pixel_top[2] = pix_top.z;
+}
+
+CCL_NAMESPACE_END
diff --git a/intern/cycles/util/util_task.cpp b/intern/cycles/util/util_task.cpp
index 61aa28c6815..4fb61392e92 100644
--- a/intern/cycles/util/util_task.cpp
+++ b/intern/cycles/util/util_task.cpp
@@ -20,100 +20,28 @@
#include "util/util_system.h"
#include "util/util_time.h"
-//#define THREADING_DEBUG_ENABLED
-
-#ifdef THREADING_DEBUG_ENABLED
-# include <stdio.h>
-# define THREADING_DEBUG(...) \
- do { \
- printf(__VA_ARGS__); \
- fflush(stdout); \
- } while (0)
-#else
-# define THREADING_DEBUG(...)
-#endif
-
CCL_NAMESPACE_BEGIN
/* Task Pool */
-TaskPool::TaskPool()
+TaskPool::TaskPool() : start_time(time_dt()), num_tasks_handled(0)
{
- num_tasks_handled = 0;
- num = 0;
- do_cancel = false;
}
TaskPool::~TaskPool()
{
- stop();
+ cancel();
}
-void TaskPool::push(Task *task, bool front)
+void TaskPool::push(TaskRunFunction &&task)
{
- TaskScheduler::Entry entry;
-
- entry.task = task;
- entry.pool = this;
-
- TaskScheduler::push(entry, front);
-}
-
-void TaskPool::push(const TaskRunFunction &run, bool front)
-{
- push(new Task(run), front);
+ tbb_group.run(std::move(task));
+ num_tasks_handled++;
}
void TaskPool::wait_work(Summary *stats)
{
- thread_scoped_lock num_lock(num_mutex);
-
- while (num != 0) {
- num_lock.unlock();
-
- thread_scoped_lock queue_lock(TaskScheduler::queue_mutex);
-
- /* find task from this pool. if we get a task from another pool,
- * we can get into deadlock */
- TaskScheduler::Entry work_entry;
- bool found_entry = false;
- list<TaskScheduler::Entry>::iterator it;
-
- for (it = TaskScheduler::queue.begin(); it != TaskScheduler::queue.end(); it++) {
- TaskScheduler::Entry &entry = *it;
-
- if (entry.pool == this) {
- work_entry = entry;
- found_entry = true;
- TaskScheduler::queue.erase(it);
- break;
- }
- }
-
- queue_lock.unlock();
-
- /* if found task, do it, otherwise wait until other tasks are done */
- if (found_entry) {
- /* run task */
- work_entry.task->run(0);
-
- /* delete task */
- delete work_entry.task;
-
- /* notify pool task was done */
- num_decrease(1);
- }
-
- num_lock.lock();
- if (num == 0)
- break;
-
- if (!found_entry) {
- THREADING_DEBUG("num==%d, Waiting for condition in TaskPool::wait_work !found_entry\n", num);
- num_cond.wait(num_lock);
- THREADING_DEBUG("num==%d, condition wait done in TaskPool::wait_work !found_entry\n", num);
- }
- }
+ tbb_group.wait();
if (stats != NULL) {
stats->time_total = time_dt() - start_time;
@@ -123,193 +51,21 @@ void TaskPool::wait_work(Summary *stats)
void TaskPool::cancel()
{
- do_cancel = true;
-
- TaskScheduler::clear(this);
-
- {
- thread_scoped_lock num_lock(num_mutex);
-
- while (num) {
- THREADING_DEBUG("num==%d, Waiting for condition in TaskPool::cancel\n", num);
- num_cond.wait(num_lock);
- THREADING_DEBUG("num==%d condition wait done in TaskPool::cancel\n", num);
- }
- }
-
- do_cancel = false;
-}
-
-void TaskPool::stop()
-{
- TaskScheduler::clear(this);
-
- assert(num == 0);
+ tbb_group.cancel();
+ tbb_group.wait();
}
bool TaskPool::canceled()
{
- return do_cancel;
-}
-
-bool TaskPool::finished()
-{
- thread_scoped_lock num_lock(num_mutex);
- return num == 0;
-}
-
-void TaskPool::num_decrease(int done)
-{
- num_mutex.lock();
- num -= done;
-
- assert(num >= 0);
- if (num == 0) {
- THREADING_DEBUG("num==%d, notifying all in TaskPool::num_decrease\n", num);
- num_cond.notify_all();
- }
-
- num_mutex.unlock();
-}
-
-void TaskPool::num_increase()
-{
- thread_scoped_lock num_lock(num_mutex);
- if (num_tasks_handled == 0) {
- start_time = time_dt();
- }
- num++;
- num_tasks_handled++;
- THREADING_DEBUG("num==%d, notifying all in TaskPool::num_increase\n", num);
- num_cond.notify_all();
+ return tbb_group.is_canceling();
}
/* Task Scheduler */
thread_mutex TaskScheduler::mutex;
int TaskScheduler::users = 0;
-vector<thread *> TaskScheduler::threads;
-bool TaskScheduler::do_exit = false;
-
-list<TaskScheduler::Entry> TaskScheduler::queue;
-thread_mutex TaskScheduler::queue_mutex;
-thread_condition_variable TaskScheduler::queue_cond;
-
-namespace {
-
-/* Get number of processors on each of the available nodes. The result is sized
- * by the highest node index, and element corresponds to number of processors on
- * that node.
- * If node is not available, then the corresponding number of processors is
- * zero. */
-void get_per_node_num_processors(vector<int> *num_per_node_processors)
-{
- const int num_nodes = system_cpu_num_numa_nodes();
- if (num_nodes == 0) {
- LOG(ERROR) << "Zero available NUMA nodes, is not supposed to happen.";
- return;
- }
- num_per_node_processors->resize(num_nodes);
- for (int node = 0; node < num_nodes; ++node) {
- if (!system_cpu_is_numa_node_available(node)) {
- (*num_per_node_processors)[node] = 0;
- continue;
- }
- (*num_per_node_processors)[node] = system_cpu_num_numa_node_processors(node);
- }
-}
-
-/* Calculate total number of processors on all available nodes.
- * This is similar to system_cpu_thread_count(), but uses pre-calculated number
- * of processors on each of the node, avoiding extra system calls and checks for
- * the node availability. */
-int get_num_total_processors(const vector<int> &num_per_node_processors)
-{
- int num_total_processors = 0;
- foreach (int num_node_processors, num_per_node_processors) {
- num_total_processors += num_node_processors;
- }
- return num_total_processors;
-}
-
-/* Compute NUMA node for every thread to run on, for the best performance. */
-vector<int> distribute_threads_on_nodes(const int num_threads)
-{
- /* Start with all threads unassigned to any specific NUMA node. */
- vector<int> thread_nodes(num_threads, -1);
- const int num_active_group_processors = system_cpu_num_active_group_processors();
- VLOG(1) << "Detected " << num_active_group_processors << " processors "
- << "in active group.";
- if (num_active_group_processors >= num_threads) {
- /* If the current thread is set up in a way that its affinity allows to
- * use at least requested number of threads we do not explicitly set
- * affinity to the worker threads.
- * This way we allow users to manually edit affinity of the parent
- * thread, and here we follow that affinity. This way it's possible to
- * have two Cycles/Blender instances running manually set to a different
- * dies on a CPU. */
- VLOG(1) << "Not setting thread group affinity.";
- return thread_nodes;
- }
- vector<int> num_per_node_processors;
- get_per_node_num_processors(&num_per_node_processors);
- if (num_per_node_processors.size() == 0) {
- /* Error was already reported, here we can't do anything, so we simply
- * leave default affinity to all the worker threads. */
- return thread_nodes;
- }
- const int num_nodes = num_per_node_processors.size();
- int thread_index = 0;
- /* First pass: fill in all the nodes to their maximum.
- *
- * If there is less threads than the overall nodes capacity, some of the
- * nodes or parts of them will idle.
- *
- * TODO(sergey): Consider picking up fastest nodes if number of threads
- * fits on them. For example, on Threadripper2 we might consider using nodes
- * 0 and 2 if user requested 32 render threads. */
- const int num_total_node_processors = get_num_total_processors(num_per_node_processors);
- int current_node_index = 0;
- while (thread_index < num_total_node_processors && thread_index < num_threads) {
- const int num_node_processors = num_per_node_processors[current_node_index];
- for (int processor_index = 0; processor_index < num_node_processors; ++processor_index) {
- VLOG(1) << "Scheduling thread " << thread_index << " to node " << current_node_index << ".";
- thread_nodes[thread_index] = current_node_index;
- ++thread_index;
- if (thread_index == num_threads) {
- /* All threads are scheduled on their nodes. */
- return thread_nodes;
- }
- }
- ++current_node_index;
- }
- /* Second pass: keep scheduling threads to each node one by one,
- * uniformly filling them in.
- * This is where things becomes tricky to predict for the maximum
- * performance: on the one hand this avoids too much threading overhead on
- * few nodes, but for the final performance having all the overhead on one
- * node might be better idea (since other nodes will have better chance of
- * rendering faster).
- * But more tricky is that nodes might have difference capacity, so we might
- * want to do some weighted scheduling. For example, if node 0 has 16
- * processors and node 1 has 32 processors, we'd better schedule 1 extra
- * thread on node 0 and 2 extra threads on node 1. */
- current_node_index = 0;
- while (thread_index < num_threads) {
- /* Skip unavailable nodes. */
- /* TODO(sergey): Add sanity check against deadlock. */
- while (num_per_node_processors[current_node_index] == 0) {
- current_node_index = (current_node_index + 1) % num_nodes;
- }
- VLOG(1) << "Scheduling thread " << thread_index << " to node " << current_node_index << ".";
- ++thread_index;
- current_node_index = (current_node_index + 1) % num_nodes;
- }
-
- return thread_nodes;
-}
-
-} // namespace
+int TaskScheduler::active_num_threads = 0;
+tbb::global_control *TaskScheduler::global_control = nullptr;
void TaskScheduler::init(int num_threads)
{
@@ -320,22 +76,15 @@ void TaskScheduler::init(int num_threads)
if (users != 1) {
return;
}
- do_exit = false;
- const bool use_auto_threads = (num_threads == 0);
- if (use_auto_threads) {
+ if (num_threads > 0) {
/* Automatic number of threads. */
- num_threads = system_cpu_thread_count();
+ VLOG(1) << "Overriding number of TBB threads to " << num_threads << ".";
+ global_control = new tbb::global_control(tbb::global_control::max_allowed_parallelism,
+ num_threads);
+ active_num_threads = num_threads;
}
- VLOG(1) << "Creating pool of " << num_threads << " threads.";
-
- /* Compute distribution on NUMA nodes. */
- vector<int> thread_nodes = distribute_threads_on_nodes(num_threads);
-
- /* Launch threads that will be waiting for work. */
- threads.resize(num_threads);
- for (int thread_index = 0; thread_index < num_threads; ++thread_index) {
- threads[thread_index] = new thread(function_bind(&TaskScheduler::thread_run, thread_index + 1),
- thread_nodes[thread_index]);
+ else {
+ active_num_threads = system_cpu_thread_count();
}
}
@@ -344,105 +93,20 @@ void TaskScheduler::exit()
thread_scoped_lock lock(mutex);
users--;
if (users == 0) {
- VLOG(1) << "De-initializing thread pool of task scheduler.";
- /* stop all waiting threads */
- TaskScheduler::queue_mutex.lock();
- do_exit = true;
- TaskScheduler::queue_cond.notify_all();
- TaskScheduler::queue_mutex.unlock();
-
- /* delete threads */
- foreach (thread *t, threads) {
- t->join();
- delete t;
- }
- threads.clear();
+ delete global_control;
+ global_control = nullptr;
+ active_num_threads = 0;
}
}
void TaskScheduler::free_memory()
{
assert(users == 0);
- threads.free_memory();
-}
-
-bool TaskScheduler::thread_wait_pop(Entry &entry)
-{
- thread_scoped_lock queue_lock(queue_mutex);
-
- while (queue.empty() && !do_exit)
- queue_cond.wait(queue_lock);
-
- if (queue.empty()) {
- assert(do_exit);
- return false;
- }
-
- entry = queue.front();
- queue.pop_front();
-
- return true;
}
-void TaskScheduler::thread_run(int thread_id)
+int TaskScheduler::num_threads()
{
- Entry entry;
-
- /* todo: test affinity/denormal mask */
-
- /* keep popping off tasks */
- while (thread_wait_pop(entry)) {
- /* run task */
- entry.task->run(thread_id);
-
- /* delete task */
- delete entry.task;
-
- /* notify pool task was done */
- entry.pool->num_decrease(1);
- }
-}
-
-void TaskScheduler::push(Entry &entry, bool front)
-{
- entry.pool->num_increase();
-
- /* add entry to queue */
- TaskScheduler::queue_mutex.lock();
- if (front)
- TaskScheduler::queue.push_front(entry);
- else
- TaskScheduler::queue.push_back(entry);
-
- TaskScheduler::queue_cond.notify_one();
- TaskScheduler::queue_mutex.unlock();
-}
-
-void TaskScheduler::clear(TaskPool *pool)
-{
- thread_scoped_lock queue_lock(TaskScheduler::queue_mutex);
-
- /* erase all tasks from this pool from the queue */
- list<Entry>::iterator it = queue.begin();
- int done = 0;
-
- while (it != queue.end()) {
- Entry &entry = *it;
-
- if (entry.pool == pool) {
- done++;
- delete entry.task;
-
- it = queue.erase(it);
- }
- else
- it++;
- }
-
- queue_lock.unlock();
-
- /* notify done */
- pool->num_decrease(done);
+ return active_num_threads;
}
/* Dedicated Task Pool */
@@ -458,31 +122,30 @@ DedicatedTaskPool::DedicatedTaskPool()
DedicatedTaskPool::~DedicatedTaskPool()
{
- stop();
+ wait();
+
+ do_exit = true;
+ queue_cond.notify_all();
+
worker_thread->join();
delete worker_thread;
}
-void DedicatedTaskPool::push(Task *task, bool front)
+void DedicatedTaskPool::push(TaskRunFunction &&task, bool front)
{
num_increase();
/* add task to queue */
queue_mutex.lock();
if (front)
- queue.push_front(task);
+ queue.emplace_front(std::move(task));
else
- queue.push_back(task);
+ queue.emplace_back(std::move(task));
queue_cond.notify_one();
queue_mutex.unlock();
}
-void DedicatedTaskPool::push(const TaskRunFunction &run, bool front)
-{
- push(new Task(run), front);
-}
-
void DedicatedTaskPool::wait()
{
thread_scoped_lock num_lock(num_mutex);
@@ -501,18 +164,6 @@ void DedicatedTaskPool::cancel()
do_cancel = false;
}
-void DedicatedTaskPool::stop()
-{
- clear();
-
- do_exit = true;
- queue_cond.notify_all();
-
- wait();
-
- assert(num == 0);
-}
-
bool DedicatedTaskPool::canceled()
{
return do_cancel;
@@ -535,7 +186,7 @@ void DedicatedTaskPool::num_increase()
num_cond.notify_all();
}
-bool DedicatedTaskPool::thread_wait_pop(Task *&task)
+bool DedicatedTaskPool::thread_wait_pop(TaskRunFunction &task)
{
thread_scoped_lock queue_lock(queue_mutex);
@@ -555,15 +206,15 @@ bool DedicatedTaskPool::thread_wait_pop(Task *&task)
void DedicatedTaskPool::thread_run()
{
- Task *task;
+ TaskRunFunction task;
/* keep popping off tasks */
while (thread_wait_pop(task)) {
/* run task */
- task->run(0);
+ task();
/* delete task */
- delete task;
+ task = nullptr;
/* notify task was done */
num_decrease(1);
@@ -575,15 +226,8 @@ void DedicatedTaskPool::clear()
thread_scoped_lock queue_lock(queue_mutex);
/* erase all tasks from the queue */
- list<Task *>::iterator it = queue.begin();
- int done = 0;
-
- while (it != queue.end()) {
- done++;
- delete *it;
-
- it = queue.erase(it);
- }
+ int done = queue.size();
+ queue.clear();
queue_lock.unlock();
diff --git a/intern/cycles/util/util_task.h b/intern/cycles/util/util_task.h
index fd30a33d8ef..a56ca62f62c 100644
--- a/intern/cycles/util/util_task.h
+++ b/intern/cycles/util/util_task.h
@@ -19,48 +19,16 @@
#include "util/util_list.h"
#include "util/util_string.h"
+#include "util/util_tbb.h"
#include "util/util_thread.h"
#include "util/util_vector.h"
CCL_NAMESPACE_BEGIN
-class Task;
class TaskPool;
class TaskScheduler;
-/* Notes on Thread ID
- *
- * Thread ID argument reports the 0-based ID of a working thread from which
- * the run() callback is being invoked. Thread ID of 0 denotes the thread from
- * which wait_work() was called.
- *
- * DO NOT use this ID to control execution flaw, use it only for things like
- * emulating TLS which does not affect on scheduling. Don't use this ID to make
- * any decisions.
- *
- * It is to be noted here that dedicated task pool will always report thread ID
- * of 0.
- */
-
-typedef function<void(int thread_id)> TaskRunFunction;
-
-/* Task
- *
- * Base class for tasks to be executed in threads. */
-
-class Task {
- public:
- Task(){};
- explicit Task(const TaskRunFunction &run_) : run(run_)
- {
- }
-
- virtual ~Task()
- {
- }
-
- TaskRunFunction run;
-};
+typedef function<void(void)> TaskRunFunction;
/* Task Pool
*
@@ -68,8 +36,7 @@ class Task {
* pool, we can wait for all tasks to be done, or cancel them before they are
* done.
*
- * The run callback that actually executes the task may be created like this:
- * function_bind(&MyClass::task_execute, this, _1, _2) */
+ * TaskRunFunction may be created with std::bind or lambda expressions. */
class TaskPool {
public:
@@ -89,27 +56,15 @@ class TaskPool {
TaskPool();
~TaskPool();
- void push(Task *task, bool front = false);
- void push(const TaskRunFunction &run, bool front = false);
+ void push(TaskRunFunction &&task);
void wait_work(Summary *stats = NULL); /* work and wait until all tasks are done */
- void cancel(); /* cancel all tasks, keep worker threads running */
- void stop(); /* stop all worker threads */
- bool finished(); /* check if all work has been completed */
+ void cancel(); /* cancel all tasks and wait until they are no longer executing */
bool canceled(); /* for worker threads, test if canceled */
protected:
- friend class TaskScheduler;
-
- void num_decrease(int done);
- void num_increase();
-
- thread_mutex num_mutex;
- thread_condition_variable num_cond;
-
- int num;
- bool do_cancel;
+ tbb::task_group tbb_group;
/* ** Statistics ** */
@@ -131,40 +86,19 @@ class TaskScheduler {
static void exit();
static void free_memory();
- /* number of threads that can work on task */
- static int num_threads()
- {
- return threads.size();
- }
-
- /* test if any session is using the scheduler */
- static bool active()
- {
- return users != 0;
- }
+ /* Approximate number of threads that will work on task, which may be lower
+ * or higher than the actual number of threads. Use as little as possible and
+ * leave splitting up tasks to the scheduler.. */
+ static int num_threads();
protected:
- friend class TaskPool;
-
- struct Entry {
- Task *task;
- TaskPool *pool;
- };
-
static thread_mutex mutex;
static int users;
- static vector<thread *> threads;
- static bool do_exit;
+ static int active_num_threads;
- static list<Entry> queue;
- static thread_mutex queue_mutex;
- static thread_condition_variable queue_cond;
-
- static void thread_run(int thread_id);
- static bool thread_wait_pop(Entry &entry);
-
- static void push(Entry &entry, bool front);
- static void clear(TaskPool *pool);
+#ifdef WITH_TBB_GLOBAL_CONTROL
+ static tbb::global_control *global_control;
+#endif
};
/* Dedicated Task Pool
@@ -179,12 +113,10 @@ class DedicatedTaskPool {
DedicatedTaskPool();
~DedicatedTaskPool();
- void push(Task *task, bool front = false);
- void push(const TaskRunFunction &run, bool front = false);
+ void push(TaskRunFunction &&run, bool front = false);
void wait(); /* wait until all tasks are done */
void cancel(); /* cancel all tasks, keep worker thread running */
- void stop(); /* stop worker thread */
bool canceled(); /* for worker thread, test if canceled */
@@ -193,14 +125,14 @@ class DedicatedTaskPool {
void num_increase();
void thread_run();
- bool thread_wait_pop(Task *&entry);
+ bool thread_wait_pop(TaskRunFunction &task);
void clear();
thread_mutex num_mutex;
thread_condition_variable num_cond;
- list<Task *> queue;
+ list<TaskRunFunction> queue;
thread_mutex queue_mutex;
thread_condition_variable queue_cond;
diff --git a/intern/cycles/util/util_tbb.h b/intern/cycles/util/util_tbb.h
new file mode 100644
index 00000000000..301cb80c5b0
--- /dev/null
+++ b/intern/cycles/util/util_tbb.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2011-2020 Blender Foundation
+ *
+ * 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.
+ */
+
+#ifndef __UTIL_TBB_H__
+#define __UTIL_TBB_H__
+
+/* TBB includes <windows.h>, do it ourselves first so we are sure
+ * WIN32_LEAN_AND_MEAN and similar are defined beforehand. */
+#include "util_windows.h"
+
+#define TBB_SUPPRESS_DEPRECATED_MESSAGES 1
+#include <tbb/tbb.h>
+
+#if TBB_INTERFACE_VERSION_MAJOR >= 10
+# define WITH_TBB_GLOBAL_CONTROL
+#endif
+
+CCL_NAMESPACE_BEGIN
+
+using tbb::blocked_range;
+using tbb::enumerable_thread_specific;
+using tbb::parallel_for;
+
+CCL_NAMESPACE_END
+
+#endif /* __UTIL_TBB_H__ */
diff --git a/intern/cycles/util/util_version.h b/intern/cycles/util/util_version.h
index bb2c99cc6d7..8bce5ff85aa 100644
--- a/intern/cycles/util/util_version.h
+++ b/intern/cycles/util/util_version.h
@@ -22,7 +22,7 @@
CCL_NAMESPACE_BEGIN
#define CYCLES_VERSION_MAJOR 1
-#define CYCLES_VERSION_MINOR 12
+#define CYCLES_VERSION_MINOR 13
#define CYCLES_VERSION_PATCH 0
#define CYCLES_MAKE_VERSION_STRING2(a, b, c) #a "." #b "." #c
diff --git a/intern/ghost/intern/GHOST_IXrGraphicsBinding.h b/intern/ghost/intern/GHOST_IXrGraphicsBinding.h
index 5794a682023..b199c5f9b28 100644
--- a/intern/ghost/intern/GHOST_IXrGraphicsBinding.h
+++ b/intern/ghost/intern/GHOST_IXrGraphicsBinding.h
@@ -56,7 +56,8 @@ class GHOST_IXrGraphicsBinding {
std::string *r_requirement_info) const = 0;
virtual void initFromGhostContext(class GHOST_Context *ghost_ctx) = 0;
virtual bool chooseSwapchainFormat(const std::vector<int64_t> &runtime_formats,
- int64_t *r_result) const = 0;
+ int64_t &r_result,
+ bool &r_is_rgb_format) const = 0;
virtual std::vector<XrSwapchainImageBaseHeader *> createSwapchainImages(
uint32_t image_count) = 0;
virtual void submitToSwapchainImage(XrSwapchainImageBaseHeader *swapchain_image,
diff --git a/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp b/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp
index 71e6af3fa4f..7d7405a974d 100644
--- a/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp
+++ b/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp
@@ -36,7 +36,7 @@
static bool choose_swapchain_format_from_candidates(std::vector<int64_t> gpu_binding_formats,
std::vector<int64_t> runtime_formats,
- int64_t *r_result)
+ int64_t &r_result)
{
if (gpu_binding_formats.empty()) {
return false;
@@ -50,7 +50,7 @@ static bool choose_swapchain_format_from_candidates(std::vector<int64_t> gpu_bin
return false;
}
- *r_result = *res;
+ r_result = *res;
return true;
}
@@ -132,10 +132,20 @@ class GHOST_XrGraphicsBindingOpenGL : public GHOST_IXrGraphicsBinding {
}
bool chooseSwapchainFormat(const std::vector<int64_t> &runtime_formats,
- int64_t *r_result) const override
+ int64_t &r_result,
+ bool &r_is_srgb_format) const override
{
- std::vector<int64_t> gpu_binding_formats = {GL_RGBA8};
- return choose_swapchain_format_from_candidates(gpu_binding_formats, runtime_formats, r_result);
+ std::vector<int64_t> gpu_binding_formats = {
+ GL_RGBA8,
+ GL_SRGB8_ALPHA8,
+ };
+
+ if (choose_swapchain_format_from_candidates(gpu_binding_formats, runtime_formats, r_result)) {
+ r_is_srgb_format = (r_result == GL_SRGB8_ALPHA8);
+ return true;
+ }
+
+ return false;
}
std::vector<XrSwapchainImageBaseHeader *> createSwapchainImages(uint32_t image_count) override
@@ -248,10 +258,19 @@ class GHOST_XrGraphicsBindingD3D : public GHOST_IXrGraphicsBinding {
}
bool chooseSwapchainFormat(const std::vector<int64_t> &runtime_formats,
- int64_t *r_result) const override
+ int64_t &r_result,
+ bool &r_is_srgb_format) const override
{
- std::vector<int64_t> gpu_binding_formats = {DXGI_FORMAT_R8G8B8A8_UNORM};
- return choose_swapchain_format_from_candidates(gpu_binding_formats, runtime_formats, r_result);
+ std::vector<int64_t> gpu_binding_formats = {
+ DXGI_FORMAT_R8G8B8A8_UNORM,
+ DXGI_FORMAT_R8G8B8A8_UNORM_SRGB,
+ };
+
+ if (choose_swapchain_format_from_candidates(gpu_binding_formats, runtime_formats, r_result)) {
+ r_is_srgb_format = (r_result == DXGI_FORMAT_R8G8B8A8_UNORM_SRGB);
+ return true;
+ }
+ return false;
}
std::vector<XrSwapchainImageBaseHeader *> createSwapchainImages(uint32_t image_count) override
diff --git a/intern/ghost/intern/GHOST_XrSession.cpp b/intern/ghost/intern/GHOST_XrSession.cpp
index 7ae819dbfb2..5a747b1e787 100644
--- a/intern/ghost/intern/GHOST_XrSession.cpp
+++ b/intern/ghost/intern/GHOST_XrSession.cpp
@@ -375,15 +375,6 @@ static void ghost_xr_draw_view_info_from_view(const XrView &view, GHOST_XrDrawVi
r_info.fov.angle_down = view.fov.angleDown;
}
-static bool ghost_xr_draw_view_expects_srgb_buffer(const GHOST_XrContext *context)
-{
- /* Monado seems to be faulty and doesn't do OETF transform correctly. So expect a SRGB buffer to
- * compensate. You get way too dark rendering without this, it's pretty obvious (even in the
- * default startup scene). */
- GHOST_TXrOpenXRRuntimeID runtime_id = context->getOpenXRRuntimeID();
- return (runtime_id == OPENXR_RUNTIME_MONADO) || (runtime_id == OPENXR_RUNTIME_STEAMVR);
-}
-
void GHOST_XrSession::drawView(GHOST_XrSwapchain &swapchain,
XrCompositionLayerProjectionView &r_proj_layer_view,
XrSpaceLocation &view_location,
@@ -398,7 +389,7 @@ void GHOST_XrSession::drawView(GHOST_XrSwapchain &swapchain,
r_proj_layer_view.fov = view.fov;
swapchain.updateCompositionLayerProjectViewSubImage(r_proj_layer_view.subImage);
- draw_view_info.expects_srgb_buffer = ghost_xr_draw_view_expects_srgb_buffer(m_context);
+ draw_view_info.expects_srgb_buffer = swapchain.isBufferSRGB();
draw_view_info.ofsx = r_proj_layer_view.subImage.imageRect.offset.x;
draw_view_info.ofsy = r_proj_layer_view.subImage.imageRect.offset.y;
draw_view_info.width = r_proj_layer_view.subImage.imageRect.extent.width;
diff --git a/intern/ghost/intern/GHOST_XrSwapchain.cpp b/intern/ghost/intern/GHOST_XrSwapchain.cpp
index f50cfde0687..f7808c20112 100644
--- a/intern/ghost/intern/GHOST_XrSwapchain.cpp
+++ b/intern/ghost/intern/GHOST_XrSwapchain.cpp
@@ -68,7 +68,7 @@ GHOST_XrSwapchain::GHOST_XrSwapchain(GHOST_IXrGraphicsBinding &gpu_binding,
"Failed to get swapchain image formats.");
assert(swapchain_formats.size() == format_count);
- if (!gpu_binding.chooseSwapchainFormat(swapchain_formats, &chosen_format)) {
+ if (!gpu_binding.chooseSwapchainFormat(swapchain_formats, chosen_format, m_is_srgb_buffer)) {
throw GHOST_XrException(
"Error: No format matching OpenXR runtime supported swapchain formats found.");
}
@@ -132,6 +132,11 @@ void GHOST_XrSwapchain::updateCompositionLayerProjectViewSubImage(XrSwapchainSub
r_sub_image.imageRect.extent = {m_image_width, m_image_height};
}
+bool GHOST_XrSwapchain::isBufferSRGB()
+{
+ return m_is_srgb_buffer;
+}
+
void GHOST_XrSwapchain::releaseImage()
{
XrSwapchainImageReleaseInfo release_info = {XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO};
diff --git a/intern/ghost/intern/GHOST_XrSwapchain.h b/intern/ghost/intern/GHOST_XrSwapchain.h
index ab0a6736c9c..7a3e7fcea68 100644
--- a/intern/ghost/intern/GHOST_XrSwapchain.h
+++ b/intern/ghost/intern/GHOST_XrSwapchain.h
@@ -38,9 +38,12 @@ class GHOST_XrSwapchain {
void updateCompositionLayerProjectViewSubImage(XrSwapchainSubImage &r_sub_image);
+ bool isBufferSRGB();
+
private:
std::unique_ptr<OpenXRSwapchainData> m_oxr; /* Could use stack, but PImpl is preferable. */
int32_t m_image_width, m_image_height;
+ bool m_is_srgb_buffer = false;
};
#endif // GHOST_XRSWAPCHAIN_H
diff --git a/intern/guardedalloc/MEM_guardedalloc.h b/intern/guardedalloc/MEM_guardedalloc.h
index 602297576c8..bbba69edf1d 100644
--- a/intern/guardedalloc/MEM_guardedalloc.h
+++ b/intern/guardedalloc/MEM_guardedalloc.h
@@ -187,7 +187,8 @@ extern size_t (*MEM_get_peak_memory)(void) ATTR_WARN_UNUSED_RESULT;
do { \
typeof(&(v)) _v = &(v); \
if (*_v) { \
- MEM_freeN(*_v); \
+ /* Cast so we can free constant arrays. */ \
+ MEM_freeN((void *)*_v); \
*_v = NULL; \
} \
} while (0)
diff --git a/intern/libmv/libmv/multiview/projection_test.cc b/intern/libmv/libmv/multiview/projection_test.cc
index 460a186e7c4..40e766bfae7 100644
--- a/intern/libmv/libmv/multiview/projection_test.cc
+++ b/intern/libmv/libmv/multiview/projection_test.cc
@@ -68,10 +68,10 @@ TEST(Projection, isInFrontOfCamera) {
Vec4 X_front = GetRandomPoint();
Vec4 X_back = GetRandomPoint();
- X_front(2) = 10; // Any point in the positive Z direction
- // where Z > 1 is infront of the camera.
- X_back(2) = -10; // Any point int he negative Z dirstaion
- // is behind the camera.
+ X_front(2) = 10; /* Any point in the positive Z direction
+ * where Z > 1 is in front of the camera. */
+ X_back(2) = -10; /* Any point in the negative Z direction
+ * is behind the camera. */
bool res_front = isInFrontOfCamera(P, X_front);
bool res_back = isInFrontOfCamera(P, X_back);
diff --git a/intern/libmv/libmv/simple_pipeline/bundle.cc b/intern/libmv/libmv/simple_pipeline/bundle.cc
index 2ecc0505e1f..22ab0cdf864 100644
--- a/intern/libmv/libmv/simple_pipeline/bundle.cc
+++ b/intern/libmv/libmv/simple_pipeline/bundle.cc
@@ -21,6 +21,7 @@
#include "libmv/simple_pipeline/bundle.h"
#include <map>
+#include <thread>
#include "ceres/ceres.h"
#include "ceres/rotation.h"
@@ -35,10 +36,6 @@
#include "libmv/simple_pipeline/tracks.h"
#include "libmv/simple_pipeline/distortion_models.h"
-#ifdef _OPENMP
-# include <omp.h>
-#endif
-
namespace libmv {
// The intrinsics need to get combined into a single parameter block; use these
@@ -642,11 +639,7 @@ void EuclideanBundlePointsOnly(const CameraIntrinsics *invariant_intrinsics,
options.use_explicit_schur_complement = true;
options.use_inner_iterations = true;
options.max_num_iterations = 100;
-
-#ifdef _OPENMP
- options.num_threads = omp_get_max_threads();
- options.num_linear_solver_threads = omp_get_max_threads();
-#endif
+ options.num_threads = std::thread::hardware_concurrency();
// Solve!
ceres::Solver::Summary summary;
@@ -807,11 +800,7 @@ void EuclideanBundleCommonIntrinsics(
options.use_explicit_schur_complement = true;
options.use_inner_iterations = true;
options.max_num_iterations = 100;
-
-#ifdef _OPENMP
- options.num_threads = omp_get_max_threads();
- options.num_linear_solver_threads = omp_get_max_threads();
-#endif
+ options.num_threads = std::thread::hardware_concurrency();
// Solve!
ceres::Solver::Summary summary;
diff --git a/intern/mantaflow/extern/manta_fluid_API.h b/intern/mantaflow/extern/manta_fluid_API.h
index 7825ad14d7d..d78aa6732b1 100644
--- a/intern/mantaflow/extern/manta_fluid_API.h
+++ b/intern/mantaflow/extern/manta_fluid_API.h
@@ -41,23 +41,23 @@ int manta_write_config(struct MANTA *fluid, struct FluidModifierData *mmd, int f
int manta_write_data(struct MANTA *fluid, struct FluidModifierData *mmd, int framenr);
int manta_write_noise(struct MANTA *fluid, struct FluidModifierData *mmd, int framenr);
int manta_read_config(struct MANTA *fluid, struct FluidModifierData *mmd, int framenr);
-int manta_read_data(struct MANTA *fluid, struct FluidModifierData *mmd, int framenr);
-int manta_read_noise(struct MANTA *fluid, struct FluidModifierData *mmd, int framenr);
+int manta_read_data(struct MANTA *fluid,
+ struct FluidModifierData *mmd,
+ int framenr,
+ bool resumable);
+int manta_read_noise(struct MANTA *fluid,
+ struct FluidModifierData *mmd,
+ int framenr,
+ bool resumable);
int manta_read_mesh(struct MANTA *fluid, struct FluidModifierData *mmd, int framenr);
-int manta_read_particles(struct MANTA *fluid, struct FluidModifierData *mmd, int framenr);
+int manta_read_particles(struct MANTA *fluid,
+ struct FluidModifierData *mmd,
+ int framenr,
+ bool resumable);
int manta_read_guiding(struct MANTA *fluid,
struct FluidModifierData *mmd,
int framenr,
bool sourceDomain);
-int manta_update_liquid_structures(struct MANTA *fluid,
- struct FluidModifierData *mmd,
- int framenr);
-int manta_update_mesh_structures(struct MANTA *fluid, struct FluidModifierData *mmd, int framenr);
-int manta_update_particle_structures(struct MANTA *fluid,
- struct FluidModifierData *mmd,
- int framenr);
-int manta_update_smoke_structures(struct MANTA *fluid, struct FluidModifierData *mmd, int framenr);
-int manta_update_noise_structures(struct MANTA *fluid, struct FluidModifierData *mmd, int framenr);
int manta_bake_data(struct MANTA *fluid, struct FluidModifierData *mmd, int framenr);
int manta_bake_noise(struct MANTA *fluid, struct FluidModifierData *mmd, int framenr);
int manta_bake_mesh(struct MANTA *fluid, struct FluidModifierData *mmd, int framenr);
diff --git a/intern/mantaflow/intern/MANTA_main.cpp b/intern/mantaflow/intern/MANTA_main.cpp
index e760cef8641..a008100a3e2 100644
--- a/intern/mantaflow/intern/MANTA_main.cpp
+++ b/intern/mantaflow/intern/MANTA_main.cpp
@@ -301,9 +301,8 @@ void MANTA::initDomain(FluidModifierData *mmd)
string tmpString = fluid_variables + fluid_solver + fluid_alloc + fluid_cache_helper +
fluid_bake_multiprocessing + fluid_bake_data + fluid_bake_noise +
fluid_bake_mesh + fluid_bake_particles + fluid_bake_guiding +
- fluid_file_import + fluid_file_export + fluid_save_data + fluid_load_data +
- fluid_pre_step + fluid_post_step + fluid_adapt_time_step +
- fluid_time_stepping;
+ fluid_file_import + fluid_file_export + fluid_pre_step + fluid_post_step +
+ fluid_adapt_time_step + fluid_time_stepping;
string finalString = parseScript(tmpString, mmd);
pythonCommands.push_back(finalString);
runPythonString(pythonCommands);
@@ -674,7 +673,7 @@ void MANTA::initializeRNAMap(FluidModifierData *mmd)
if (!mmd) {
if (with_debug)
- cout << "No modifier data given in RNA map setup - returning early" << endl;
+ cout << "Fluid: No modifier data given in RNA map setup - returning early" << endl;
return;
}
@@ -730,6 +729,20 @@ void MANTA::initializeRNAMap(FluidModifierData *mmd)
float viscosity = mds->viscosity_base * pow(10.0f, -mds->viscosity_exponent);
float domainSize = MAX3(mds->global_size[0], mds->global_size[1], mds->global_size[2]);
+ string vdbCompressionMethod = "Compression_None";
+ if (mds->openvdb_compression == VDB_COMPRESSION_NONE)
+ vdbCompressionMethod = "Compression_None";
+ else if (mds->openvdb_compression == VDB_COMPRESSION_ZIP)
+ vdbCompressionMethod = "Compression_Zip";
+ else if (mds->openvdb_compression == VDB_COMPRESSION_BLOSC)
+ vdbCompressionMethod = "Compression_Blosc";
+
+ string vdbPrecisionHalf = "True";
+ if (mds->openvdb_data_depth == VDB_PRECISION_HALF_FLOAT)
+ vdbPrecisionHalf = "True";
+ else if (mds->openvdb_data_depth == VDB_PRECISION_FULL_FLOAT)
+ vdbPrecisionHalf = "False";
+
mRNAMap["USING_SMOKE"] = getBooleanString(mds->type == FLUID_DOMAIN_TYPE_GAS);
mRNAMap["USING_LIQUID"] = getBooleanString(mds->type == FLUID_DOMAIN_TYPE_LIQUID);
mRNAMap["USING_COLORS"] = getBooleanString(mds->active_fields & FLUID_DOMAIN_ACTIVE_COLORS);
@@ -742,8 +755,8 @@ void MANTA::initializeRNAMap(FluidModifierData *mmd)
mRNAMap["USING_OUTFLOW"] = getBooleanString(mds->active_fields & FLUID_DOMAIN_ACTIVE_OUTFLOW);
mRNAMap["USING_LOG_DISSOLVE"] = getBooleanString(mds->flags & FLUID_DOMAIN_USE_DISSOLVE_LOG);
mRNAMap["USING_DISSOLVE"] = getBooleanString(mds->flags & FLUID_DOMAIN_USE_DISSOLVE);
- mRNAMap["DO_OPEN"] = getBooleanString(mds->border_collisions == 0);
- mRNAMap["CACHE_RESUMABLE"] = getBooleanString(mds->cache_type != FLUID_DOMAIN_CACHE_FINAL);
+ mRNAMap["DOMAIN_CLOSED"] = getBooleanString(borderCollisions.compare("") == 0);
+ mRNAMap["CACHE_RESUMABLE"] = getBooleanString(mds->flags & FLUID_DOMAIN_USE_RESUMABLE_CACHE);
mRNAMap["USING_ADAPTIVETIME"] = getBooleanString(mds->flags & FLUID_DOMAIN_USE_ADAPTIVE_TIME);
mRNAMap["USING_SPEEDVECTORS"] = getBooleanString(mds->flags & FLUID_DOMAIN_USE_SPEED_VECTORS);
mRNAMap["USING_FRACTIONS"] = getBooleanString(mds->flags & FLUID_DOMAIN_USE_FRACTIONS);
@@ -850,6 +863,9 @@ void MANTA::initializeRNAMap(FluidModifierData *mmd)
mRNAMap["LIQUID_SURFACE_TENSION"] = to_string(mds->surface_tension);
mRNAMap["FLUID_VISCOSITY"] = to_string(viscosity);
mRNAMap["FLUID_DOMAIN_SIZE"] = to_string(domainSize);
+ mRNAMap["FLUID_DOMAIN_SIZE_X"] = to_string(mds->global_size[0]);
+ mRNAMap["FLUID_DOMAIN_SIZE_Y"] = to_string(mds->global_size[1]);
+ mRNAMap["FLUID_DOMAIN_SIZE_Z"] = to_string(mds->global_size[2]);
mRNAMap["SNDPARTICLE_TYPES"] = particleTypesStr;
mRNAMap["GUIDING_ALPHA"] = to_string(mds->guide_alpha);
mRNAMap["GUIDING_BETA"] = to_string(mds->guide_beta);
@@ -858,6 +874,8 @@ void MANTA::initializeRNAMap(FluidModifierData *mmd)
mRNAMap["GRAVITY_Y"] = to_string(mds->gravity[1]);
mRNAMap["GRAVITY_Z"] = to_string(mds->gravity[2]);
mRNAMap["CACHE_DIR"] = cacheDirectory;
+ mRNAMap["COMPRESSION_OPENVDB"] = vdbCompressionMethod;
+ mRNAMap["PRECISION_OPENVDB"] = vdbPrecisionHalf;
/* Fluid object names. */
mRNAMap["NAME_FLAGS"] = FLUID_NAME_FLAGS;
@@ -900,6 +918,8 @@ void MANTA::initializeRNAMap(FluidModifierData *mmd)
mRNAMap["NAME_DENSITYIN"] = FLUID_NAME_DENSITYIN;
mRNAMap["NAME_HEAT"] = FLUID_NAME_HEAT;
mRNAMap["NAME_HEATIN"] = FLUID_NAME_HEATIN;
+ mRNAMap["NAME_TEMPERATURE"] = FLUID_NAME_TEMPERATURE;
+ mRNAMap["NAME_TEMPERATUREIN"] = FLUID_NAME_TEMPERATUREIN;
mRNAMap["NAME_COLORR"] = FLUID_NAME_COLORR;
mRNAMap["NAME_COLORG"] = FLUID_NAME_COLORG;
mRNAMap["NAME_COLORB"] = FLUID_NAME_COLORB;
@@ -921,6 +941,8 @@ void MANTA::initializeRNAMap(FluidModifierData *mmd)
mRNAMap["NAME_MAPWEIGHTS"] = FLUID_NAME_MAPWEIGHTS;
mRNAMap["NAME_PP"] = FLUID_NAME_PP;
mRNAMap["NAME_PVEL"] = FLUID_NAME_PVEL;
+ mRNAMap["NAME_PARTS"] = FLUID_NAME_PARTS;
+ mRNAMap["NAME_PARTSVELOCITY"] = FLUID_NAME_PARTSVELOCITY;
mRNAMap["NAME_PINDEX"] = FLUID_NAME_PINDEX;
mRNAMap["NAME_GPI"] = FLUID_NAME_GPI;
mRNAMap["NAME_CURVATURE"] = FLUID_NAME_CURVATURE;
@@ -967,6 +989,10 @@ void MANTA::initializeRNAMap(FluidModifierData *mmd)
mRNAMap["NAME_PVEL_PARTICLES"] = FLUID_NAME_PVEL_PARTICLES;
mRNAMap["NAME_PFORCE_PARTICLES"] = FLUID_NAME_PFORCE_PARTICLES;
mRNAMap["NAME_PLIFE_PARTICLES"] = FLUID_NAME_PLIFE_PARTICLES;
+ mRNAMap["NAME_PARTS_PARTICLES"] = FLUID_NAME_PARTS_PARTICLES;
+ mRNAMap["NAME_PARTSVEL_PARTICLES"] = FLUID_NAME_PARTSVEL_PARTICLES;
+ mRNAMap["NAME_PARTSFORCE_PARTICLES"] = FLUID_NAME_PARTSFORCE_PARTICLES;
+ mRNAMap["NAME_PARTSLIFE_PARTICLES"] = FLUID_NAME_PARTSLIFE_PARTICLES;
mRNAMap["NAME_VELOCITY_PARTICLES"] = FLUID_NAME_VELOCITY_PARTICLES;
mRNAMap["NAME_FLAGS_PARTICLES"] = FLUID_NAME_FLAGS_PARTICLES;
mRNAMap["NAME_PHI_PARTICLES"] = FLUID_NAME_PHI_PARTICLES;
@@ -1000,9 +1026,6 @@ void MANTA::initializeRNAMap(FluidModifierData *mmd)
string MANTA::getRealValue(const string &varName)
{
- if (with_debug)
- cout << "MANTA::getRealValue()" << endl;
-
unordered_map<string, string>::iterator it;
it = mRNAMap.find(varName);
@@ -1011,9 +1034,6 @@ string MANTA::getRealValue(const string &varName)
<< endl;
return "";
}
- if (with_debug) {
- cout << "Found variable " << varName << " with value " << it->second << endl;
- }
return it->second;
}
@@ -1062,409 +1082,6 @@ string MANTA::parseScript(const string &setup_string, FluidModifierData *mmd)
return res.str();
}
-bool MANTA::updateFlipStructures(FluidModifierData *mmd, int framenr)
-{
- if (MANTA::with_debug)
- cout << "MANTA::updateFlipStructures()" << endl;
-
- FluidDomainSettings *mds = mmd->domain;
- mFlipFromFile = false;
-
- if (!mUsingLiquid)
- return false;
- if (BLI_path_is_rel(mds->cache_directory))
- return false;
-
- int result = 0;
- int expected = 0; /* Expected number of read successes for this frame. */
-
- /* Ensure empty data structures at start. */
- if (!mFlipParticleData || !mFlipParticleVelocity)
- return false;
-
- mFlipParticleData->clear();
- mFlipParticleVelocity->clear();
-
- string pformat = getCacheFileEnding(mds->cache_particle_format);
- string file = getFile(mmd, FLUID_DOMAIN_DIR_DATA, FLUID_NAME_PP, pformat, framenr);
-
- expected += 1;
- if (BLI_exists(file.c_str())) {
- result += updateParticlesFromFile(file, false, false);
- assert(result == expected);
- }
-
- file = getFile(mmd, FLUID_DOMAIN_DIR_DATA, FLUID_NAME_PVEL, pformat, framenr);
- expected += 1;
- if (BLI_exists(file.c_str())) {
- result += updateParticlesFromFile(file, false, true);
- assert(result == expected);
- }
-
- return mFlipFromFile = (result == expected);
-}
-
-bool MANTA::updateMeshStructures(FluidModifierData *mmd, int framenr)
-{
- if (MANTA::with_debug)
- cout << "MANTA::updateMeshStructures()" << endl;
-
- FluidDomainSettings *mds = mmd->domain;
- mMeshFromFile = false;
-
- if (!mUsingMesh)
- return false;
- if (BLI_path_is_rel(mds->cache_directory))
- return false;
-
- int result = 0;
- int expected = 0; /* Expected number of read successes for this frame. */
-
- /* Ensure empty data structures at start. */
- if (!mMeshNodes || !mMeshTriangles)
- return false;
-
- mMeshNodes->clear();
- mMeshTriangles->clear();
-
- if (mMeshVelocities)
- mMeshVelocities->clear();
-
- string mformat = getCacheFileEnding(mds->cache_mesh_format);
- string dformat = getCacheFileEnding(mds->cache_data_format);
- string file = getFile(mmd, FLUID_DOMAIN_DIR_MESH, FLUID_NAME_LMESH, mformat, framenr);
-
- expected += 1;
- if (BLI_exists(file.c_str())) {
- result += updateMeshFromFile(file);
- assert(result == expected);
- }
-
- if (mUsingMVel) {
- file = getFile(mmd, FLUID_DOMAIN_DIR_MESH, FLUID_NAME_VELOCITYVEC_MESH, dformat, framenr);
- expected += 1;
- if (BLI_exists(file.c_str())) {
- result += updateMeshFromFile(file);
- assert(result == expected);
- }
- }
-
- return mMeshFromFile = (result == expected);
-}
-
-bool MANTA::updateParticleStructures(FluidModifierData *mmd, int framenr)
-{
- if (MANTA::with_debug)
- cout << "MANTA::updateParticleStructures()" << endl;
-
- FluidDomainSettings *mds = mmd->domain;
- mParticlesFromFile = false;
-
- if (!mUsingDrops && !mUsingBubbles && !mUsingFloats && !mUsingTracers)
- return false;
- if (BLI_path_is_rel(mds->cache_directory))
- return false;
-
- int result = 0;
- int expected = 0; /* Expected number of read successes for this frame. */
-
- /* Ensure empty data structures at start. */
- if (!mSndParticleData || !mSndParticleVelocity || !mSndParticleLife)
- return false;
-
- mSndParticleData->clear();
- mSndParticleVelocity->clear();
- mSndParticleLife->clear();
-
- string pformat = getCacheFileEnding(mds->cache_particle_format);
- string file = getFile(
- mmd, FLUID_DOMAIN_DIR_PARTICLES, FLUID_NAME_PP_PARTICLES, pformat, framenr);
-
- expected += 1;
- if (BLI_exists(file.c_str())) {
- result += updateParticlesFromFile(file, true, false);
- assert(result == expected);
- }
-
- file = getFile(mmd, FLUID_DOMAIN_DIR_PARTICLES, FLUID_NAME_PVEL_PARTICLES, pformat, framenr);
- expected += 1;
- if (BLI_exists(file.c_str())) {
- result += updateParticlesFromFile(file, true, true);
- assert(result == expected);
- }
-
- file = getFile(mmd, FLUID_DOMAIN_DIR_PARTICLES, FLUID_NAME_PLIFE_PARTICLES, pformat, framenr);
- expected += 1;
- if (BLI_exists(file.c_str())) {
- result += updateParticlesFromFile(file, true, false);
- assert(result == expected);
- }
-
- return mParticlesFromFile = (result == expected);
-}
-
-static void assertGridItems(vector<MANTA::GridItem> gList)
-{
- vector<MANTA::GridItem>::iterator gIter = gList.begin();
- int *resPrev = (*gIter).res;
-
- for (vector<MANTA::GridItem>::iterator it = gList.begin(); it != gList.end(); ++it) {
- MANTA::GridItem item = *it;
- assert(
- ELEM(item.type, FLUID_DOMAIN_GRID_FLOAT, FLUID_DOMAIN_GRID_INT, FLUID_DOMAIN_GRID_VEC3F));
- assert(item.pointer[0]);
- if (item.type == FLUID_DOMAIN_GRID_VEC3F) {
- assert(item.pointer[1] && item.pointer[2]);
- }
- assert(item.res[0] == resPrev[0] && item.res[1] == resPrev[1] && item.res[2] == resPrev[2]);
- assert((item.name).compare("") != 0);
- }
-
- UNUSED_VARS(resPrev);
-}
-
-bool MANTA::updateSmokeStructures(FluidModifierData *mmd, int framenr)
-{
- if (MANTA::with_debug)
- cout << "MANTA::updateGridStructures()" << endl;
-
- FluidDomainSettings *mds = mmd->domain;
- mSmokeFromFile = false;
-
- if (!mUsingSmoke)
- return false;
- if (BLI_path_is_rel(mds->cache_directory))
- return false;
-
- int result = 0;
- string dformat = getCacheFileEnding(mds->cache_data_format);
-
- vector<FileItem> filesData;
- vector<GridItem> gridsData;
-
- int res[] = {mResX, mResY, mResZ};
-
- /* Put grid pointers into pointer lists, some grids have more than 1 pointer. */
- void *aDensity[] = {mDensity};
- void *aShadow[] = {mShadow};
- void *aVelocities[] = {mVelocityX, mVelocityY, mVelocityZ};
- void *aHeat[] = {mHeat};
- void *aColorR[] = {mColorR};
- void *aColorG[] = {mColorG};
- void *aColorB[] = {mColorB};
- void *aFlame[] = {mFlame};
- void *aFuel[] = {mFuel};
- void *aReact[] = {mReact};
-
- /* File names for grids. */
- string fDensity = getFile(mmd, FLUID_DOMAIN_DIR_DATA, FLUID_NAME_DENSITY, dformat, framenr);
- string fShadow = getFile(mmd, FLUID_DOMAIN_DIR_DATA, FLUID_NAME_SHADOW, dformat, framenr);
- string fVel = getFile(mmd, FLUID_DOMAIN_DIR_DATA, FLUID_NAME_VELOCITY, dformat, framenr);
- string fHeat = getFile(mmd, FLUID_DOMAIN_DIR_DATA, FLUID_NAME_HEAT, dformat, framenr);
- string fColorR = getFile(mmd, FLUID_DOMAIN_DIR_DATA, FLUID_NAME_COLORR, dformat, framenr);
- string fColorG = getFile(mmd, FLUID_DOMAIN_DIR_DATA, FLUID_NAME_COLORG, dformat, framenr);
- string fColorB = getFile(mmd, FLUID_DOMAIN_DIR_DATA, FLUID_NAME_COLORB, dformat, framenr);
- string fFlame = getFile(mmd, FLUID_DOMAIN_DIR_DATA, FLUID_NAME_FLAME, dformat, framenr);
- string fFuel = getFile(mmd, FLUID_DOMAIN_DIR_DATA, FLUID_NAME_FUEL, dformat, framenr);
- string fReact = getFile(mmd, FLUID_DOMAIN_DIR_DATA, FLUID_NAME_REACT, dformat, framenr);
- string fFluid = getFile(mmd, FLUID_DOMAIN_DIR_DATA, FLUID_NAME_DATA, dformat, framenr);
-
- /* Prepare grid info containers. */
- GridItem gDensity = {aDensity, FLUID_DOMAIN_GRID_FLOAT, res, FLUID_NAME_DENSITY};
- GridItem gShadow = {aShadow, FLUID_DOMAIN_GRID_FLOAT, res, FLUID_NAME_SHADOW};
- GridItem gVel = {aVelocities, FLUID_DOMAIN_GRID_VEC3F, res, FLUID_NAME_VELOCITY};
- GridItem gHeat = {aHeat, FLUID_DOMAIN_GRID_FLOAT, res, FLUID_NAME_HEAT};
- GridItem gColorR = {aColorR, FLUID_DOMAIN_GRID_FLOAT, res, FLUID_NAME_COLORR};
- GridItem gColorG = {aColorG, FLUID_DOMAIN_GRID_FLOAT, res, FLUID_NAME_COLORG};
- GridItem gColorB = {aColorB, FLUID_DOMAIN_GRID_FLOAT, res, FLUID_NAME_COLORB};
- GridItem gFlame = {aFlame, FLUID_DOMAIN_GRID_FLOAT, res, FLUID_NAME_FLAME};
- GridItem gFuel = {aFuel, FLUID_DOMAIN_GRID_FLOAT, res, FLUID_NAME_FUEL};
- GridItem gReact = {aReact, FLUID_DOMAIN_GRID_FLOAT, res, FLUID_NAME_REACT};
-
- /* TODO (sebbas): For now, only allow single file mode. Combined grid file export is todo. */
- const int fileMode = FLUID_DOMAIN_CACHE_FILES_SINGLE;
- if (fileMode == FLUID_DOMAIN_CACHE_FILES_SINGLE) {
-
- filesData.push_back({fDensity, {gDensity}});
- filesData.push_back({fShadow, {gShadow}});
- filesData.push_back({fVel, {gVel}});
- if (mUsingHeat) {
- filesData.push_back({fHeat, {gHeat}});
- }
- if (mUsingColors) {
- filesData.push_back({fColorR, {gColorR}});
- filesData.push_back({fColorG, {gColorG}});
- filesData.push_back({fColorB, {gColorB}});
- }
- if (mUsingFire) {
- filesData.push_back({fFlame, {gFlame}});
- filesData.push_back({fFuel, {gFuel}});
- filesData.push_back({fReact, {gReact}});
- }
- }
- else if (fileMode == FLUID_DOMAIN_CACHE_FILES_COMBINED) {
-
- gridsData.push_back(gDensity);
- gridsData.push_back(gShadow);
- gridsData.push_back(gVel);
- if (mUsingHeat) {
- gridsData.push_back(gHeat);
- }
- if (mUsingColors) {
- gridsData.push_back(gColorR);
- gridsData.push_back(gColorG);
- gridsData.push_back(gColorB);
- }
- if (mUsingFire) {
- gridsData.push_back(gFlame);
- gridsData.push_back(gFuel);
- gridsData.push_back(gReact);
- }
-
- if (with_debug) {
- assertGridItems(gridsData);
- }
- filesData.push_back({fFluid, gridsData});
- }
-
- /* Update files from data directory. */
- for (vector<FileItem>::iterator it = filesData.begin(); it != filesData.end(); ++it) {
- FileItem item = *it;
- if (BLI_exists(item.filename.c_str())) {
- result += updateGridsFromFile(item.filename, item.grids);
- assert(result);
- }
- }
-
- return mSmokeFromFile = result;
-}
-
-bool MANTA::updateNoiseStructures(FluidModifierData *mmd, int framenr)
-{
- if (MANTA::with_debug)
- cout << "MANTA::updateNoiseStructures()" << endl;
-
- FluidDomainSettings *mds = mmd->domain;
- mNoiseFromFile = false;
-
- if (!mUsingSmoke || !mUsingNoise)
- return false;
- if (BLI_path_is_rel(mds->cache_directory))
- return false;
-
- int result = 0;
- string dformat = getCacheFileEnding(mds->cache_data_format);
- string nformat = getCacheFileEnding(mds->cache_noise_format);
-
- vector<FileItem> filesData, filesNoise;
- vector<GridItem> gridsData, gridsNoise;
-
- int resData[] = {mResX, mResY, mResZ};
- int resNoise[] = {mResXNoise, mResYNoise, mResZNoise};
-
- /* Put grid pointers into pointer lists, some grids have more than 1 pointer. */
- void *aShadow[] = {mShadow};
- void *aVelocities[] = {mVelocityX, mVelocityY, mVelocityZ};
- void *aDensity[] = {mDensityHigh};
- void *aColorR[] = {mColorRHigh};
- void *aColorG[] = {mColorGHigh};
- void *aColorB[] = {mColorBHigh};
- void *aFlame[] = {mFlameHigh};
- void *aFuel[] = {mFuelHigh};
- void *aReact[] = {mReactHigh};
-
- /* File names for grids. */
- string fShadow = getFile(mmd, FLUID_DOMAIN_DIR_DATA, FLUID_NAME_SHADOW, dformat, framenr);
- string fVel = getFile(mmd, FLUID_DOMAIN_DIR_DATA, FLUID_NAME_VELOCITY, dformat, framenr);
- string fFluid = getFile(mmd, FLUID_DOMAIN_DIR_NOISE, FLUID_NAME_DATA, dformat, framenr);
-
- string fDensity = getFile(
- mmd, FLUID_DOMAIN_DIR_NOISE, FLUID_NAME_DENSITY_NOISE, nformat, framenr);
- string fColorR = getFile(mmd, FLUID_DOMAIN_DIR_NOISE, FLUID_NAME_COLORR_NOISE, nformat, framenr);
- string fColorG = getFile(mmd, FLUID_DOMAIN_DIR_NOISE, FLUID_NAME_COLORG_NOISE, nformat, framenr);
- string fColorB = getFile(mmd, FLUID_DOMAIN_DIR_NOISE, FLUID_NAME_COLORB_NOISE, nformat, framenr);
- string fFlame = getFile(mmd, FLUID_DOMAIN_DIR_NOISE, FLUID_NAME_FLAME_NOISE, nformat, framenr);
- string fFuel = getFile(mmd, FLUID_DOMAIN_DIR_NOISE, FLUID_NAME_FUEL_NOISE, nformat, framenr);
- string fReact = getFile(mmd, FLUID_DOMAIN_DIR_NOISE, FLUID_NAME_REACT_NOISE, nformat, framenr);
- string fNoise = getFile(mmd, FLUID_DOMAIN_DIR_NOISE, FLUID_NAME_NOISE, nformat, framenr);
-
- /* Prepare grid info containers. */
- GridItem gShadow = {aShadow, FLUID_DOMAIN_GRID_FLOAT, resData, FLUID_NAME_SHADOW};
- GridItem gVel = {aVelocities, FLUID_DOMAIN_GRID_VEC3F, resData, FLUID_NAME_VELOCITY};
-
- GridItem gDensity = {aDensity, FLUID_DOMAIN_GRID_FLOAT, resNoise, FLUID_NAME_DENSITY_NOISE};
- GridItem gColorR = {aColorR, FLUID_DOMAIN_GRID_FLOAT, resNoise, FLUID_NAME_COLORR_NOISE};
- GridItem gColorG = {aColorG, FLUID_DOMAIN_GRID_FLOAT, resNoise, FLUID_NAME_COLORG_NOISE};
- GridItem gColorB = {aColorB, FLUID_DOMAIN_GRID_FLOAT, resNoise, FLUID_NAME_COLORB_NOISE};
- GridItem gFlame = {aFlame, FLUID_DOMAIN_GRID_FLOAT, resNoise, FLUID_NAME_FLAME_NOISE};
- GridItem gFuel = {aFuel, FLUID_DOMAIN_GRID_FLOAT, resNoise, FLUID_NAME_FUEL_NOISE};
- GridItem gReact = {aReact, FLUID_DOMAIN_GRID_FLOAT, resNoise, FLUID_NAME_REACT_NOISE};
-
- /* TODO (sebbas): For now, only allow single file mode. Combined grid file export is todo. */
- const int fileMode = FLUID_DOMAIN_CACHE_FILES_SINGLE;
- if (fileMode == FLUID_DOMAIN_CACHE_FILES_SINGLE) {
-
- filesData.push_back({fShadow, {gShadow}});
- filesData.push_back({fVel, {gVel}});
-
- filesNoise.push_back({fDensity, {gDensity}});
- if (mUsingColors) {
- filesNoise.push_back({fColorR, {gColorR}});
- filesNoise.push_back({fColorG, {gColorG}});
- filesNoise.push_back({fColorB, {gColorB}});
- }
- if (mUsingFire) {
- filesNoise.push_back({fFlame, {gFlame}});
- filesNoise.push_back({fFuel, {gFuel}});
- filesNoise.push_back({fReact, {gReact}});
- }
- }
- else if (fileMode == FLUID_DOMAIN_CACHE_FILES_COMBINED) {
-
- gridsData.push_back(gShadow);
- gridsData.push_back(gVel);
-
- gridsNoise.push_back(gDensity);
- if (mUsingColors) {
- gridsNoise.push_back(gColorR);
- gridsNoise.push_back(gColorG);
- gridsNoise.push_back(gColorB);
- }
- if (mUsingFire) {
- gridsNoise.push_back(gFlame);
- gridsNoise.push_back(gFuel);
- gridsNoise.push_back(gReact);
- }
-
- if (with_debug) {
- assertGridItems(gridsData);
- assertGridItems(gridsNoise);
- }
- filesData.push_back({fFluid, gridsData});
- filesNoise.push_back({fNoise, gridsNoise});
- }
-
- /* Update files from data directory. */
- for (vector<FileItem>::iterator it = filesData.begin(); it != filesData.end(); ++it) {
- FileItem item = *it;
- if (BLI_exists(item.filename.c_str())) {
- result += updateGridsFromFile(item.filename, item.grids);
- assert(result);
- }
- }
-
- /* Update files from noise directory. */
- for (vector<FileItem>::iterator it = filesNoise.begin(); it != filesNoise.end(); ++it) {
- FileItem item = *it;
- if (BLI_exists(item.filename.c_str())) {
- result += updateGridsFromFile(item.filename, item.grids);
- assert(result);
- }
- }
-
- return mNoiseFromFile = result;
-}
-
/* Dirty hack: Needed to format paths from python code that is run via PyRun_SimpleString */
static string escapeSlashes(string const &s)
{
@@ -1514,6 +1131,7 @@ bool MANTA::writeConfiguration(FluidModifierData *mmd, int framenr)
gzwrite(gzf, &mds->res_max, 3 * sizeof(int));
gzwrite(gzf, &mds->active_color, 3 * sizeof(float));
gzwrite(gzf, &mds->time_total, sizeof(int));
+ gzwrite(gzf, &FLUID_CACHE_VERSION, 4 * sizeof(char));
return (gzclose(gzf) == Z_OK);
}
@@ -1528,27 +1146,19 @@ bool MANTA::writeData(FluidModifierData *mmd, int framenr)
FluidDomainSettings *mds = mmd->domain;
string directory = getDirectory(mmd, FLUID_DOMAIN_DIR_DATA);
- string dformat = getCacheFileEnding(mds->cache_data_format);
- string pformat = getCacheFileEnding(mds->cache_particle_format);
-
- bool final_cache = (mds->cache_type == FLUID_DOMAIN_CACHE_FINAL);
- string resumable_cache = (final_cache) ? "False" : "True";
-
- ss.str("");
- ss << "fluid_save_data_" << mCurrentID << "('" << escapeSlashes(directory) << "', " << framenr
- << ", '" << dformat << "', " << resumable_cache << ")";
- pythonCommands.push_back(ss.str());
+ string volume_format = getCacheFileEnding(mds->cache_data_format);
+ string resumable_cache = !(mds->flags & FLUID_DOMAIN_USE_RESUMABLE_CACHE) ? "False" : "True";
if (mUsingSmoke) {
ss.str("");
ss << "smoke_save_data_" << mCurrentID << "('" << escapeSlashes(directory) << "', " << framenr
- << ", '" << dformat << "', " << resumable_cache << ")";
+ << ", '" << volume_format << "', " << resumable_cache << ")";
pythonCommands.push_back(ss.str());
}
if (mUsingLiquid) {
ss.str("");
ss << "liquid_save_data_" << mCurrentID << "('" << escapeSlashes(directory) << "', " << framenr
- << ", '" << dformat << "', " << resumable_cache << ")";
+ << ", '" << volume_format << "', " << resumable_cache << ")";
pythonCommands.push_back(ss.str());
}
return runPythonString(pythonCommands);
@@ -1564,15 +1174,13 @@ bool MANTA::writeNoise(FluidModifierData *mmd, int framenr)
FluidDomainSettings *mds = mmd->domain;
string directory = getDirectory(mmd, FLUID_DOMAIN_DIR_NOISE);
- string nformat = getCacheFileEnding(mds->cache_noise_format);
-
- bool final_cache = (mds->cache_type == FLUID_DOMAIN_CACHE_FINAL);
- string resumable_cache = (final_cache) ? "False" : "True";
+ string volume_format = getCacheFileEnding(mds->cache_data_format);
+ string resumable_cache = !(mds->flags & FLUID_DOMAIN_USE_RESUMABLE_CACHE) ? "False" : "True";
if (mUsingSmoke && mUsingNoise) {
ss.str("");
ss << "smoke_save_noise_" << mCurrentID << "('" << escapeSlashes(directory) << "', " << framenr
- << ", '" << nformat << "', " << resumable_cache << ")";
+ << ", '" << volume_format << "', " << resumable_cache << ")";
pythonCommands.push_back(ss.str());
}
return runPythonString(pythonCommands);
@@ -1593,7 +1201,7 @@ bool MANTA::readConfiguration(FluidModifierData *mmd, int framenr)
if (!hasConfig(mmd, framenr))
return false;
- gzFile gzf = (gzFile)BLI_gzopen(file.c_str(), "rb"); // do some compression
+ gzFile gzf = (gzFile)BLI_gzopen(file.c_str(), "rb"); /* Do some compression. */
if (!gzf) {
cerr << "Fluid Error -- Cannot open file " << file << endl;
return false;
@@ -1602,7 +1210,7 @@ bool MANTA::readConfiguration(FluidModifierData *mmd, int framenr)
gzread(gzf, &mds->active_fields, sizeof(int));
gzread(gzf, &mds->res, 3 * sizeof(int));
gzread(gzf, &mds->dx, sizeof(float));
- gzread(gzf, &dummy, sizeof(float)); // dt not needed right now
+ gzread(gzf, &dummy, sizeof(float)); /* dt not needed right now. */
gzread(gzf, &mds->p0, 3 * sizeof(float));
gzread(gzf, &mds->p1, 3 * sizeof(float));
gzread(gzf, &mds->dp0, 3 * sizeof(float));
@@ -1614,13 +1222,14 @@ bool MANTA::readConfiguration(FluidModifierData *mmd, int framenr)
gzread(gzf, &mds->res_max, 3 * sizeof(int));
gzread(gzf, &mds->active_color, 3 * sizeof(float));
gzread(gzf, &mds->time_total, sizeof(int));
+ gzread(gzf, &mds->cache_id, 4 * sizeof(char)); /* Older caches might have no id. */
mds->total_cells = mds->res[0] * mds->res[1] * mds->res[2];
return (gzclose(gzf) == Z_OK);
}
-bool MANTA::readData(FluidModifierData *mmd, int framenr)
+bool MANTA::readData(FluidModifierData *mmd, int framenr, bool resumable)
{
if (with_debug)
cout << "MANTA::readData()" << endl;
@@ -1634,39 +1243,31 @@ bool MANTA::readData(FluidModifierData *mmd, int framenr)
bool result = true;
string directory = getDirectory(mmd, FLUID_DOMAIN_DIR_DATA);
- string dformat = getCacheFileEnding(mds->cache_data_format);
- string pformat = getCacheFileEnding(mds->cache_particle_format);
-
- bool final_cache = (mds->cache_type == FLUID_DOMAIN_CACHE_FINAL);
- string resumable_cache = (final_cache) ? "False" : "True";
+ string volume_format = getCacheFileEnding(mds->cache_data_format);
+ string resumable_cache = (!resumable) ? "False" : "True";
/* Sanity check: Are cache files present? */
if (!hasData(mmd, framenr))
return false;
- ss.str("");
- ss << "fluid_load_data_" << mCurrentID << "('" << escapeSlashes(directory) << "', " << framenr
- << ", '" << dformat << "', " << resumable_cache << ")";
- pythonCommands.push_back(ss.str());
-
if (mUsingSmoke) {
ss.str("");
ss << "smoke_load_data_" << mCurrentID << "('" << escapeSlashes(directory) << "', " << framenr
- << ", '" << dformat << "', " << resumable_cache << ")";
+ << ", '" << volume_format << "', " << resumable_cache << ")";
pythonCommands.push_back(ss.str());
result &= runPythonString(pythonCommands);
}
if (mUsingLiquid) {
ss.str("");
ss << "liquid_load_data_" << mCurrentID << "('" << escapeSlashes(directory) << "', " << framenr
- << ", '" << dformat << "', " << resumable_cache << ")";
+ << ", '" << volume_format << "', " << resumable_cache << ")";
pythonCommands.push_back(ss.str());
result &= runPythonString(pythonCommands);
}
return result;
}
-bool MANTA::readNoise(FluidModifierData *mmd, int framenr)
+bool MANTA::readNoise(FluidModifierData *mmd, int framenr, bool resumable)
{
if (with_debug)
cout << "MANTA::readNoise()" << endl;
@@ -1679,10 +1280,12 @@ bool MANTA::readNoise(FluidModifierData *mmd, int framenr)
FluidDomainSettings *mds = mmd->domain;
string directory = getDirectory(mmd, FLUID_DOMAIN_DIR_NOISE);
- string nformat = getCacheFileEnding(mds->cache_noise_format);
+ string resumable_cache = (!resumable) ? "False" : "True";
- bool final_cache = (mds->cache_type == FLUID_DOMAIN_CACHE_FINAL);
- string resumable_cache = (final_cache) ? "False" : "True";
+ /* Support older caches which had more granular file format control. */
+ char format = (!strcmp(mds->cache_id, FLUID_CACHE_VERSION)) ? mds->cache_data_format :
+ mds->cache_noise_format;
+ string volume_format = getCacheFileEnding(format);
/* Sanity check: Are cache files present? */
if (!hasNoise(mmd, framenr))
@@ -1690,15 +1293,12 @@ bool MANTA::readNoise(FluidModifierData *mmd, int framenr)
ss.str("");
ss << "smoke_load_noise_" << mCurrentID << "('" << escapeSlashes(directory) << "', " << framenr
- << ", '" << nformat << "', " << resumable_cache << ")";
+ << ", '" << volume_format << "', " << resumable_cache << ")";
pythonCommands.push_back(ss.str());
return runPythonString(pythonCommands);
}
-/* Deprecated! This function reads mesh data via the Manta Python API.
- * MANTA:updateMeshStructures() reads cache files directly from disk
- * and is preferred due to its better performance. */
bool MANTA::readMesh(FluidModifierData *mmd, int framenr)
{
if (with_debug)
@@ -1712,8 +1312,8 @@ bool MANTA::readMesh(FluidModifierData *mmd, int framenr)
FluidDomainSettings *mds = mmd->domain;
string directory = getDirectory(mmd, FLUID_DOMAIN_DIR_MESH);
- string mformat = getCacheFileEnding(mds->cache_mesh_format);
- string dformat = getCacheFileEnding(mds->cache_data_format);
+ string mesh_format = getCacheFileEnding(mds->cache_mesh_format);
+ string volume_format = getCacheFileEnding(mds->cache_data_format);
/* Sanity check: Are cache files present? */
if (!hasMesh(mmd, framenr))
@@ -1721,23 +1321,20 @@ bool MANTA::readMesh(FluidModifierData *mmd, int framenr)
ss.str("");
ss << "liquid_load_mesh_" << mCurrentID << "('" << escapeSlashes(directory) << "', " << framenr
- << ", '" << mformat << "')";
+ << ", '" << mesh_format << "')";
pythonCommands.push_back(ss.str());
if (mUsingMVel) {
ss.str("");
ss << "liquid_load_meshvel_" << mCurrentID << "('" << escapeSlashes(directory) << "', "
- << framenr << ", '" << dformat << "')";
+ << framenr << ", '" << volume_format << "')";
pythonCommands.push_back(ss.str());
}
return runPythonString(pythonCommands);
}
-/* Deprecated! This function reads particle data via the Manta Python API.
- * MANTA:updateParticleStructures() reads cache files directly from disk
- * and is preferred due to its better performance. */
-bool MANTA::readParticles(FluidModifierData *mmd, int framenr)
+bool MANTA::readParticles(FluidModifierData *mmd, int framenr, bool resumable)
{
if (with_debug)
cout << "MANTA::readParticles()" << endl;
@@ -1752,10 +1349,12 @@ bool MANTA::readParticles(FluidModifierData *mmd, int framenr)
FluidDomainSettings *mds = mmd->domain;
string directory = getDirectory(mmd, FLUID_DOMAIN_DIR_PARTICLES);
- string pformat = getCacheFileEnding(mds->cache_particle_format);
+ string resumable_cache = (!resumable) ? "False" : "True";
- bool final_cache = (mds->cache_type == FLUID_DOMAIN_CACHE_FINAL);
- string resumable_cache = (final_cache) ? "False" : "True";
+ /* Support older caches which had more granular file format control. */
+ char format = (!strcmp(mds->cache_id, FLUID_CACHE_VERSION)) ? mds->cache_data_format :
+ mds->cache_particle_format;
+ string volume_format = getCacheFileEnding(format);
/* Sanity check: Are cache files present? */
if (!hasParticles(mmd, framenr))
@@ -1763,7 +1362,7 @@ bool MANTA::readParticles(FluidModifierData *mmd, int framenr)
ss.str("");
ss << "liquid_load_particles_" << mCurrentID << "('" << escapeSlashes(directory) << "', "
- << framenr << ", '" << pformat << "', " << resumable_cache << ")";
+ << framenr << ", '" << volume_format << "', " << resumable_cache << ")";
pythonCommands.push_back(ss.str());
return runPythonString(pythonCommands);
@@ -1786,7 +1385,7 @@ bool MANTA::readGuiding(FluidModifierData *mmd, int framenr, bool sourceDomain)
string directory = (sourceDomain) ? getDirectory(mmd, FLUID_DOMAIN_DIR_DATA) :
getDirectory(mmd, FLUID_DOMAIN_DIR_GUIDE);
- string gformat = getCacheFileEnding(mds->cache_data_format);
+ string volume_format = getCacheFileEnding(mds->cache_data_format);
/* Sanity check: Are cache files present? */
if (!hasGuiding(mmd, framenr, sourceDomain))
@@ -1795,12 +1394,12 @@ bool MANTA::readGuiding(FluidModifierData *mmd, int framenr, bool sourceDomain)
if (sourceDomain) {
ss.str("");
ss << "fluid_load_vel_" << mCurrentID << "('" << escapeSlashes(directory) << "', " << framenr
- << ", '" << gformat << "')";
+ << ", '" << volume_format << "')";
}
else {
ss.str("");
ss << "fluid_load_guiding_" << mCurrentID << "('" << escapeSlashes(directory) << "', "
- << framenr << ", '" << gformat << "')";
+ << framenr << ", '" << volume_format << "')";
}
pythonCommands.push_back(ss.str());
@@ -1821,9 +1420,7 @@ bool MANTA::bakeData(FluidModifierData *mmd, int framenr)
cacheDirData[0] = '\0';
cacheDirGuiding[0] = '\0';
- string dformat = getCacheFileEnding(mds->cache_data_format);
- string pformat = getCacheFileEnding(mds->cache_particle_format);
- string gformat = dformat; // Use same data format for guiding format
+ string volume_format = getCacheFileEnding(mds->cache_data_format);
BLI_path_join(
cacheDirData, sizeof(cacheDirData), mds->cache_directory, FLUID_DOMAIN_DIR_DATA, nullptr);
@@ -1836,9 +1433,8 @@ bool MANTA::bakeData(FluidModifierData *mmd, int framenr)
BLI_path_make_safe(cacheDirGuiding);
ss.str("");
- ss << "bake_fluid_data_" << mCurrentID << "('" << escapeSlashes(cacheDirData) << "', '"
- << escapeSlashes(cacheDirGuiding) << "', " << framenr << ", '" << dformat << "', '" << pformat
- << "', '" << gformat << "')";
+ ss << "bake_fluid_data_" << mCurrentID << "('" << escapeSlashes(cacheDirData) << "', " << framenr
+ << ", '" << volume_format << "')";
pythonCommands.push_back(ss.str());
return runPythonString(pythonCommands);
@@ -1853,27 +1449,18 @@ bool MANTA::bakeNoise(FluidModifierData *mmd, int framenr)
vector<string> pythonCommands;
FluidDomainSettings *mds = mmd->domain;
- char cacheDirData[FILE_MAX], cacheDirNoise[FILE_MAX];
- cacheDirData[0] = '\0';
+ char cacheDirNoise[FILE_MAX];
cacheDirNoise[0] = '\0';
- string dformat = getCacheFileEnding(mds->cache_data_format);
- string nformat = getCacheFileEnding(mds->cache_noise_format);
+ string volume_format = getCacheFileEnding(mds->cache_data_format);
- bool final_cache = (mds->cache_type == FLUID_DOMAIN_CACHE_FINAL);
- string resumable_cache = (final_cache) ? "False" : "True";
-
- BLI_path_join(
- cacheDirData, sizeof(cacheDirData), mds->cache_directory, FLUID_DOMAIN_DIR_DATA, nullptr);
BLI_path_join(
cacheDirNoise, sizeof(cacheDirNoise), mds->cache_directory, FLUID_DOMAIN_DIR_NOISE, nullptr);
- BLI_path_make_safe(cacheDirData);
BLI_path_make_safe(cacheDirNoise);
ss.str("");
- ss << "bake_noise_" << mCurrentID << "('" << escapeSlashes(cacheDirData) << "', '"
- << escapeSlashes(cacheDirNoise) << "', " << framenr << ", '" << dformat << "', '" << nformat
- << "', " << resumable_cache << ")";
+ ss << "bake_noise_" << mCurrentID << "('" << escapeSlashes(cacheDirNoise) << "', " << framenr
+ << ", '" << volume_format << "')";
pythonCommands.push_back(ss.str());
return runPythonString(pythonCommands);
@@ -1888,25 +1475,19 @@ bool MANTA::bakeMesh(FluidModifierData *mmd, int framenr)
vector<string> pythonCommands;
FluidDomainSettings *mds = mmd->domain;
- char cacheDirData[FILE_MAX], cacheDirMesh[FILE_MAX];
- cacheDirData[0] = '\0';
+ char cacheDirMesh[FILE_MAX];
cacheDirMesh[0] = '\0';
- string dformat = getCacheFileEnding(mds->cache_data_format);
- string mformat = getCacheFileEnding(mds->cache_mesh_format);
- string pformat = getCacheFileEnding(mds->cache_particle_format);
+ string volume_format = getCacheFileEnding(mds->cache_data_format);
+ string mesh_format = getCacheFileEnding(mds->cache_mesh_format);
BLI_path_join(
- cacheDirData, sizeof(cacheDirData), mds->cache_directory, FLUID_DOMAIN_DIR_DATA, nullptr);
- BLI_path_join(
cacheDirMesh, sizeof(cacheDirMesh), mds->cache_directory, FLUID_DOMAIN_DIR_MESH, nullptr);
- BLI_path_make_safe(cacheDirData);
BLI_path_make_safe(cacheDirMesh);
ss.str("");
- ss << "bake_mesh_" << mCurrentID << "('" << escapeSlashes(cacheDirData) << "', '"
- << escapeSlashes(cacheDirMesh) << "', " << framenr << ", '" << dformat << "', '" << mformat
- << "', '" << pformat << "')";
+ ss << "bake_mesh_" << mCurrentID << "('" << escapeSlashes(cacheDirMesh) << "', " << framenr
+ << ", '" << volume_format << "', '" << mesh_format << "')";
pythonCommands.push_back(ss.str());
return runPythonString(pythonCommands);
@@ -1921,30 +1502,22 @@ bool MANTA::bakeParticles(FluidModifierData *mmd, int framenr)
vector<string> pythonCommands;
FluidDomainSettings *mds = mmd->domain;
- char cacheDirData[FILE_MAX], cacheDirParticles[FILE_MAX];
- cacheDirData[0] = '\0';
+ char cacheDirParticles[FILE_MAX];
cacheDirParticles[0] = '\0';
- string dformat = getCacheFileEnding(mds->cache_data_format);
- string pformat = getCacheFileEnding(mds->cache_particle_format);
-
- bool final_cache = (mds->cache_type == FLUID_DOMAIN_CACHE_FINAL);
- string resumable_cache = (final_cache) ? "False" : "True";
+ string volume_format = getCacheFileEnding(mds->cache_data_format);
+ string resumable_cache = !(mds->flags & FLUID_DOMAIN_USE_RESUMABLE_CACHE) ? "False" : "True";
- BLI_path_join(
- cacheDirData, sizeof(cacheDirData), mds->cache_directory, FLUID_DOMAIN_DIR_DATA, nullptr);
BLI_path_join(cacheDirParticles,
sizeof(cacheDirParticles),
mds->cache_directory,
FLUID_DOMAIN_DIR_PARTICLES,
nullptr);
- BLI_path_make_safe(cacheDirData);
BLI_path_make_safe(cacheDirParticles);
ss.str("");
- ss << "bake_particles_" << mCurrentID << "('" << escapeSlashes(cacheDirData) << "', '"
- << escapeSlashes(cacheDirParticles) << "', " << framenr << ", '" << dformat << "', '"
- << pformat << "', " << resumable_cache << ")";
+ ss << "bake_particles_" << mCurrentID << "('" << escapeSlashes(cacheDirParticles) << "', "
+ << framenr << ", '" << volume_format << "', " << resumable_cache << ")";
pythonCommands.push_back(ss.str());
return runPythonString(pythonCommands);
@@ -1962,10 +1535,7 @@ bool MANTA::bakeGuiding(FluidModifierData *mmd, int framenr)
char cacheDirGuiding[FILE_MAX];
cacheDirGuiding[0] = '\0';
- string gformat = getCacheFileEnding(mds->cache_data_format);
-
- bool final_cache = (mds->cache_type == FLUID_DOMAIN_CACHE_FINAL);
- string resumable_cache = (final_cache) ? "False" : "True";
+ string volume_format = getCacheFileEnding(mds->cache_data_format);
BLI_path_join(cacheDirGuiding,
sizeof(cacheDirGuiding),
@@ -1976,7 +1546,7 @@ bool MANTA::bakeGuiding(FluidModifierData *mmd, int framenr)
ss.str("");
ss << "bake_guiding_" << mCurrentID << "('" << escapeSlashes(cacheDirGuiding) << "', " << framenr
- << ", '" << gformat << "', " << resumable_cache << ")";
+ << ", '" << volume_format << "')";
pythonCommands.push_back(ss.str());
return runPythonString(pythonCommands);
@@ -2092,8 +1662,7 @@ void MANTA::exportSmokeScript(FluidModifierData *mmd)
manta_script += header_time + fluid_time_stepping + fluid_adapt_time_step;
// Import
- manta_script += header_import + fluid_file_import + fluid_cache_helper + fluid_load_data +
- smoke_load_data;
+ manta_script += header_import + fluid_file_import + fluid_cache_helper + smoke_load_data;
if (noise)
manta_script += smoke_load_noise;
if (guiding)
@@ -2198,8 +1767,7 @@ void MANTA::exportLiquidScript(FluidModifierData *mmd)
manta_script += header_time + fluid_time_stepping + fluid_adapt_time_step;
// Import
- manta_script += header_import + fluid_file_import + fluid_cache_helper + fluid_load_data +
- liquid_load_data;
+ manta_script += header_import + fluid_file_import + fluid_cache_helper + liquid_load_data;
if (mesh)
manta_script += liquid_load_mesh;
if (drops || bubble || floater || tracer)
@@ -2241,7 +1809,7 @@ static PyObject *callPythonFunction(string varName, string functionName, bool is
{
if ((varName == "") || (functionName == "")) {
if (MANTA::with_debug)
- cout << "Missing Python variable name and/or function name -- name is: " << varName
+ cout << "Fluid: Missing Python variable name and/or function name -- name is: " << varName
<< ", function name is: " << functionName << endl;
return nullptr;
}
@@ -2384,952 +1952,6 @@ void MANTA::adaptTimestep()
runPythonString(pythonCommands);
}
-bool MANTA::updateMeshFromFile(string filename)
-{
- string fname(filename);
- string::size_type idx;
-
- idx = fname.rfind('.');
- if (idx != string::npos) {
- string extension = fname.substr(idx + 1);
-
- if (extension.compare("gz") == 0)
- return updateMeshFromBobj(filename);
- else if (extension.compare("obj") == 0)
- return updateMeshFromObj(filename);
- else if (extension.compare("uni") == 0)
- return updateMeshFromUni(filename);
- else
- cerr << "Fluid Error -- updateMeshFromFile(): Invalid file extension in file: " << filename
- << endl;
- }
- else {
- cerr << "Fluid Error -- updateMeshFromFile(): Unable to open file: " << filename << endl;
- }
- return false;
-}
-
-bool MANTA::updateMeshFromBobj(string filename)
-{
- if (with_debug)
- cout << "MANTA::updateMeshFromBobj()" << endl;
-
- gzFile gzf;
-
- gzf = (gzFile)BLI_gzopen(filename.c_str(), "rb1"); // do some compression
- if (!gzf) {
- cerr << "Fluid Error -- updateMeshFromBobj(): Unable to open file: " << filename << endl;
- return false;
- }
-
- int numBuffer = 0, readBytes = 0;
-
- // Num vertices
- readBytes = gzread(gzf, &numBuffer, sizeof(int));
- if (!readBytes) {
- cerr << "Fluid Error -- updateMeshFromBobj(): Unable to read number of mesh vertices from "
- << filename << endl;
- gzclose(gzf);
- return false;
- }
-
- if (with_debug)
- cout << "read mesh , num verts: " << numBuffer << " , in file: " << filename << endl;
-
- int numChunks = (int)(ceil((float)numBuffer / NODE_CHUNK));
- int readLen, readStart, readEnd, k;
-
- if (numBuffer) {
- // Vertices
- int todoVertices = numBuffer;
- float *bufferVerts = (float *)MEM_malloc_arrayN(
- NODE_CHUNK, sizeof(float) * 3, "fluid_mesh_vertices");
-
- mMeshNodes->resize(numBuffer);
-
- for (int i = 0; i < numChunks && todoVertices > 0; ++i) {
- readLen = NODE_CHUNK;
- if (todoVertices < NODE_CHUNK) {
- readLen = todoVertices;
- }
-
- readBytes = gzread(gzf, bufferVerts, readLen * sizeof(float) * 3);
- if (!readBytes) {
- cerr << "Fluid Error -- updateMeshFromBobj(): Unable to read mesh vertices from "
- << filename << endl;
- MEM_freeN(bufferVerts);
- gzclose(gzf);
- return false;
- }
-
- readStart = (numBuffer - todoVertices);
- CLAMP(readStart, 0, numBuffer);
- readEnd = readStart + readLen;
- CLAMP(readEnd, 0, numBuffer);
-
- k = 0;
- for (vector<MANTA::Node>::size_type j = readStart; j < readEnd; j++, k += 3) {
- mMeshNodes->at(j).pos[0] = bufferVerts[k];
- mMeshNodes->at(j).pos[1] = bufferVerts[k + 1];
- mMeshNodes->at(j).pos[2] = bufferVerts[k + 2];
- }
- todoVertices -= readLen;
- }
- MEM_freeN(bufferVerts);
- }
-
- // Num normals
- readBytes = gzread(gzf, &numBuffer, sizeof(int));
- if (!readBytes) {
- cerr << "Fluid Error -- updateMeshFromBobj(): Unable to read number of mesh normals from "
- << filename << endl;
- gzclose(gzf);
- return false;
- }
-
- if (with_debug)
- cout << "read mesh , num normals : " << numBuffer << " , in file: " << filename << endl;
-
- if (numBuffer) {
- // Normals
- int todoNormals = numBuffer;
- float *bufferNormals = (float *)MEM_malloc_arrayN(
- NODE_CHUNK, sizeof(float) * 3, "fluid_mesh_normals");
-
- if (!getNumVertices())
- mMeshNodes->resize(numBuffer);
-
- for (int i = 0; i < numChunks && todoNormals > 0; ++i) {
- readLen = NODE_CHUNK;
- if (todoNormals < NODE_CHUNK) {
- readLen = todoNormals;
- }
-
- readBytes = gzread(gzf, bufferNormals, readLen * sizeof(float) * 3);
- if (!readBytes) {
- cerr << "Fluid Error -- updateMeshFromBobj(): Unable to read mesh normals from "
- << filename << endl;
- MEM_freeN(bufferNormals);
- gzclose(gzf);
- return false;
- }
-
- readStart = (numBuffer - todoNormals);
- CLAMP(readStart, 0, numBuffer);
- readEnd = readStart + readLen;
- CLAMP(readEnd, 0, numBuffer);
-
- k = 0;
- for (vector<MANTA::Node>::size_type j = readStart; j < readEnd; j++, k += 3) {
- mMeshNodes->at(j).normal[0] = bufferNormals[k];
- mMeshNodes->at(j).normal[1] = bufferNormals[k + 1];
- mMeshNodes->at(j).normal[2] = bufferNormals[k + 2];
- }
- todoNormals -= readLen;
- }
- MEM_freeN(bufferNormals);
- }
-
- // Num triangles
- readBytes = gzread(gzf, &numBuffer, sizeof(int));
- if (!readBytes) {
- cerr << "Fluid Error -- updateMeshFromBobj(): Unable to read number of mesh triangles from "
- << filename << endl;
- gzclose(gzf);
- return false;
- }
-
- if (with_debug)
- cout << "Fluid: Read mesh , num triangles : " << numBuffer << " , in file: " << filename
- << endl;
-
- numChunks = (int)(ceil((float)numBuffer / TRIANGLE_CHUNK));
-
- if (numBuffer) {
- // Triangles
- int todoTriangles = numBuffer;
- int *bufferTriangles = (int *)MEM_malloc_arrayN(
- TRIANGLE_CHUNK, sizeof(int) * 3, "fluid_mesh_triangles");
-
- mMeshTriangles->resize(numBuffer);
-
- for (int i = 0; i < numChunks && todoTriangles > 0; ++i) {
- readLen = TRIANGLE_CHUNK;
- if (todoTriangles < TRIANGLE_CHUNK) {
- readLen = todoTriangles;
- }
-
- readBytes = gzread(gzf, bufferTriangles, readLen * sizeof(int) * 3);
- if (!readBytes) {
- cerr << "Fluid Error -- updateMeshFromBobj(): Unable to read mesh triangles from "
- << filename << endl;
- MEM_freeN(bufferTriangles);
- gzclose(gzf);
- return false;
- }
-
- readStart = (numBuffer - todoTriangles);
- CLAMP(readStart, 0, numBuffer);
- readEnd = readStart + readLen;
- CLAMP(readEnd, 0, numBuffer);
-
- k = 0;
- for (vector<MANTA::Triangle>::size_type j = readStart; j < readEnd; j++, k += 3) {
- mMeshTriangles->at(j).c[0] = bufferTriangles[k];
- mMeshTriangles->at(j).c[1] = bufferTriangles[k + 1];
- mMeshTriangles->at(j).c[2] = bufferTriangles[k + 2];
- }
- todoTriangles -= readLen;
- }
- MEM_freeN(bufferTriangles);
- }
- return (gzclose(gzf) == Z_OK);
-}
-
-bool MANTA::updateMeshFromObj(string filename)
-{
- if (with_debug)
- cout << "MANTA::updateMeshFromObj()" << endl;
-
- ifstream ifs(filename);
- float fbuffer[3];
- int ibuffer[3];
- int cntVerts = 0, cntNormals = 0, cntTris = 0;
-
- if (!ifs.good()) {
- cerr << "Fluid Error -- updateMeshFromObj(): Unable to open file: " << filename << endl;
- return false;
- }
-
- while (ifs.good() && !ifs.eof()) {
- string id;
- ifs >> id;
-
- if (id[0] == '#') {
- // comment
- getline(ifs, id);
- continue;
- }
- if (id == "vt") {
- // tex coord, ignore
- }
- else if (id == "vn") {
- // normals
- if (getNumVertices() != cntVerts) {
- cerr << "Fluid Error -- updateMeshFromObj(): Invalid number of mesh nodes in file: "
- << filename << endl;
- return false;
- }
-
- ifs >> fbuffer[0] >> fbuffer[1] >> fbuffer[2];
- MANTA::Node *node = &mMeshNodes->at(cntNormals);
- (*node).normal[0] = fbuffer[0];
- (*node).normal[1] = fbuffer[1];
- (*node).normal[2] = fbuffer[2];
- cntNormals++;
- }
- else if (id == "v") {
- // vertex
- ifs >> fbuffer[0] >> fbuffer[1] >> fbuffer[2];
- MANTA::Node node;
- node.pos[0] = fbuffer[0];
- node.pos[1] = fbuffer[1];
- node.pos[2] = fbuffer[2];
- mMeshNodes->push_back(node);
- cntVerts++;
- }
- else if (id == "g") {
- // group
- string group;
- ifs >> group;
- }
- else if (id == "f") {
- // face
- string face;
- for (int i = 0; i < 3; i++) {
- ifs >> face;
- if (face.find('/') != string::npos)
- face = face.substr(0, face.find('/')); // ignore other indices
- int idx = atoi(face.c_str()) - 1;
- if (idx < 0) {
- cerr << "Fluid Error -- updateMeshFromObj(): Invalid face encountered in file: "
- << filename << endl;
- return false;
- }
- ibuffer[i] = idx;
- }
- MANTA::Triangle triangle;
- triangle.c[0] = ibuffer[0];
- triangle.c[1] = ibuffer[1];
- triangle.c[2] = ibuffer[2];
- mMeshTriangles->push_back(triangle);
- cntTris++;
- }
- else {
- // whatever, ignore
- }
- // kill rest of line
- getline(ifs, id);
- }
- ifs.close();
- return true;
-}
-
-bool MANTA::updateMeshFromUni(string filename)
-{
- if (with_debug)
- cout << "MANTA::updateMeshFromUni()" << endl;
-
- gzFile gzf;
- float fbuffer[4];
- int ibuffer[4];
-
- gzf = (gzFile)BLI_gzopen(filename.c_str(), "rb1"); // do some compression
- if (!gzf) {
- cerr << "Fluid Error -- updateMeshFromUni(): Unable to open file: " << filename << endl;
- return false;
- }
-
- int readBytes = 0;
- char file_magic[5] = {0, 0, 0, 0, 0};
- readBytes = gzread(gzf, file_magic, 4);
- if (!readBytes) {
- cerr << "Fluid Error -- updateMeshFromUni(): Unable to read header in file: " << filename
- << endl;
- gzclose(gzf);
- return false;
- }
-
- vector<pVel> *velocityPointer = mMeshVelocities;
-
- // mdata uni header
- const int STR_LEN_PDATA = 256;
- int elementType, bytesPerElement, numParticles;
- char info[STR_LEN_PDATA]; // mantaflow build information
- unsigned long long timestamp; // creation time
-
- // read mesh header
- gzread(gzf, &ibuffer, sizeof(int) * 4); // num particles, dimX, dimY, dimZ
- gzread(gzf, &elementType, sizeof(int));
- gzread(gzf, &bytesPerElement, sizeof(int));
- gzread(gzf, &info, sizeof(info));
- gzread(gzf, &timestamp, sizeof(unsigned long long));
-
- if (with_debug)
- cout << "Fluid: Read " << ibuffer[0] << " vertices in file: " << filename << endl;
-
- // Sanity checks
- const int meshSize = sizeof(float) * 3 + sizeof(int);
- if (!(bytesPerElement == meshSize) && (elementType == 0)) {
- cerr << "Fluid Error -- updateMeshFromUni(): Invalid header in file: " << filename << endl;
- gzclose(gzf);
- return false;
- }
- if (!ibuffer[0]) { // Any vertices present?
- cerr << "Fluid Error -- updateMeshFromUni(): No vertices present in file: " << filename
- << endl;
- gzclose(gzf);
- return false;
- }
-
- // Reading mesh
- if (!strcmp(file_magic, "MB01")) {
- // TODO (sebbas): Future update could add uni mesh support
- }
- // Reading mesh data file v1 with vec3
- else if (!strcmp(file_magic, "MD01")) {
- numParticles = ibuffer[0];
-
- velocityPointer->resize(numParticles);
- MANTA::pVel *bufferPVel;
- for (vector<pVel>::iterator it = velocityPointer->begin(); it != velocityPointer->end();
- ++it) {
- gzread(gzf, fbuffer, sizeof(float) * 3);
- bufferPVel = (MANTA::pVel *)fbuffer;
- it->pos[0] = bufferPVel->pos[0];
- it->pos[1] = bufferPVel->pos[1];
- it->pos[2] = bufferPVel->pos[2];
- }
- }
- return (gzclose(gzf) == Z_OK);
-}
-
-bool MANTA::updateParticlesFromFile(string filename, bool isSecondarySys, bool isVelData)
-{
- if (with_debug)
- cout << "MANTA::updateParticlesFromFile()" << endl;
-
- string fname(filename);
- string::size_type idx;
-
- idx = fname.rfind('.');
- if (idx != string::npos) {
- string extension = fname.substr(idx + 1);
-
- if (extension.compare("uni") == 0)
- return updateParticlesFromUni(filename, isSecondarySys, isVelData);
- else
- cerr << "Fluid Error -- updateParticlesFromFile(): Invalid file extension in file: "
- << filename << endl;
- return false;
- }
- else {
- cerr << "Fluid Error -- updateParticlesFromFile(): Unable to open file: " << filename << endl;
- return false;
- }
-}
-
-bool MANTA::updateParticlesFromUni(string filename, bool isSecondarySys, bool isVelData)
-{
- if (with_debug)
- cout << "MANTA::updateParticlesFromUni()" << endl;
-
- gzFile gzf;
- int ibuffer[4];
-
- gzf = (gzFile)BLI_gzopen(filename.c_str(), "rb1"); // do some compression
- if (!gzf) {
- cerr << "Fluid Error -- updateParticlesFromUni(): Unable to open file: " << filename << endl;
- return false;
- }
-
- int readBytes = 0;
- char file_magic[5] = {0, 0, 0, 0, 0};
- readBytes = gzread(gzf, file_magic, 4);
- if (!readBytes) {
- cerr << "Fluid Error -- updateParticlesFromUni(): Unable to read header in file: " << filename
- << endl;
- gzclose(gzf);
- return false;
- }
-
- if (!strcmp(file_magic, "PB01")) {
- cerr << "Fluid Error -- updateParticlesFromUni(): Particle uni file format v01 not "
- "supported anymore."
- << endl;
- gzclose(gzf);
- return false;
- }
-
- // Pointer to FLIP system or to secondary particle system
- vector<pData> *dataPointer = nullptr;
- vector<pVel> *velocityPointer = nullptr;
- vector<float> *lifePointer = nullptr;
-
- if (isSecondarySys) {
- dataPointer = mSndParticleData;
- velocityPointer = mSndParticleVelocity;
- lifePointer = mSndParticleLife;
- }
- else {
- dataPointer = mFlipParticleData;
- velocityPointer = mFlipParticleVelocity;
- }
-
- // pdata uni header
- const int STR_LEN_PDATA = 256;
- int elementType, bytesPerElement, numParticles;
- char info[STR_LEN_PDATA]; // mantaflow build information
- unsigned long long timestamp; // creation time
-
- // read particle header
- gzread(gzf, &ibuffer, sizeof(int) * 4); // num particles, dimX, dimY, dimZ
- gzread(gzf, &elementType, sizeof(int));
- gzread(gzf, &bytesPerElement, sizeof(int));
- gzread(gzf, &info, sizeof(info));
- gzread(gzf, &timestamp, sizeof(unsigned long long));
-
- if (with_debug)
- cout << "Fluid: Read " << ibuffer[0] << " particles in file: " << filename << endl;
-
- // Sanity checks
- const int partSysSize = sizeof(float) * 3 + sizeof(int);
- if (!(bytesPerElement == partSysSize) && (elementType == 0)) {
- cerr << "Fluid Error -- updateParticlesFromUni(): Invalid header in file: " << filename
- << endl;
- gzclose(gzf);
- return false;
- }
- if (!ibuffer[0]) { // Any particles present?
- if (with_debug)
- cout << "Fluid: No particles present in file: " << filename << endl;
- gzclose(gzf);
- return true; // return true since having no particles in a cache file is valid
- }
-
- numParticles = ibuffer[0];
-
- const int numChunks = (int)(ceil((float)numParticles / PARTICLE_CHUNK));
- int todoParticles, readLen;
- int readStart, readEnd;
-
- // Reading base particle system file v2
- if (!strcmp(file_magic, "PB02")) {
- MANTA::pData *bufferPData;
- todoParticles = numParticles;
- bufferPData = (MANTA::pData *)MEM_malloc_arrayN(
- PARTICLE_CHUNK, sizeof(MANTA::pData), "fluid_particle_data");
-
- dataPointer->resize(numParticles);
-
- for (int i = 0; i < numChunks && todoParticles > 0; ++i) {
- readLen = PARTICLE_CHUNK;
- if (todoParticles < PARTICLE_CHUNK) {
- readLen = todoParticles;
- }
-
- readBytes = gzread(gzf, bufferPData, readLen * sizeof(pData));
- if (!readBytes) {
- cerr << "Fluid Error -- updateParticlesFromUni(): Unable to read particle data in file: "
- << filename << endl;
- MEM_freeN(bufferPData);
- gzclose(gzf);
- return false;
- }
-
- readStart = (numParticles - todoParticles);
- CLAMP(readStart, 0, numParticles);
- readEnd = readStart + readLen;
- CLAMP(readEnd, 0, numParticles);
-
- int k = 0;
- for (vector<MANTA::pData>::size_type j = readStart; j < readEnd; j++, k++) {
- dataPointer->at(j).pos[0] = bufferPData[k].pos[0];
- dataPointer->at(j).pos[1] = bufferPData[k].pos[1];
- dataPointer->at(j).pos[2] = bufferPData[k].pos[2];
- dataPointer->at(j).flag = bufferPData[k].flag;
- }
- todoParticles -= readLen;
- }
- MEM_freeN(bufferPData);
- }
- // Reading particle data file v1 with velocities
- else if (!strcmp(file_magic, "PD01") && isVelData) {
- MANTA::pVel *bufferPVel;
- todoParticles = numParticles;
- bufferPVel = (MANTA::pVel *)MEM_malloc_arrayN(
- PARTICLE_CHUNK, sizeof(MANTA::pVel), "fluid_particle_velocity");
-
- velocityPointer->resize(numParticles);
-
- for (int i = 0; i < numChunks && todoParticles > 0; ++i) {
- readLen = PARTICLE_CHUNK;
- if (todoParticles < PARTICLE_CHUNK) {
- readLen = todoParticles;
- }
-
- readBytes = gzread(gzf, bufferPVel, readLen * sizeof(pVel));
- if (!readBytes) {
- cerr << "Fluid Error -- updateParticlesFromUni(): Unable to read particle velocities "
- "in file: "
- << filename << endl;
- MEM_freeN(bufferPVel);
- gzclose(gzf);
- return false;
- }
-
- readStart = (numParticles - todoParticles);
- CLAMP(readStart, 0, numParticles);
- readEnd = readStart + readLen;
- CLAMP(readEnd, 0, numParticles);
-
- int k = 0;
- for (vector<MANTA::pVel>::size_type j = readStart; j < readEnd; j++, k++) {
- velocityPointer->at(j).pos[0] = bufferPVel[k].pos[0];
- velocityPointer->at(j).pos[1] = bufferPVel[k].pos[1];
- velocityPointer->at(j).pos[2] = bufferPVel[k].pos[2];
- }
- todoParticles -= readLen;
- }
- MEM_freeN(bufferPVel);
- }
- // Reading particle data file v1 with lifetime
- else if (!strcmp(file_magic, "PD01")) {
- float *bufferPLife;
- todoParticles = numParticles;
- bufferPLife = (float *)MEM_malloc_arrayN(PARTICLE_CHUNK, sizeof(float), "fluid_particle_life");
-
- lifePointer->resize(numParticles);
-
- for (int i = 0; i < numChunks && todoParticles > 0; ++i) {
- readLen = PARTICLE_CHUNK;
- if (todoParticles < PARTICLE_CHUNK) {
- readLen = todoParticles;
- }
-
- readBytes = gzread(gzf, bufferPLife, readLen * sizeof(float));
- if (!readBytes) {
- cerr << "Fluid Error -- updateParticlesFromUni(): Unable to read particle life in file: "
- << filename << endl;
- MEM_freeN(bufferPLife);
- gzclose(gzf);
- return false;
- }
-
- readStart = (numParticles - todoParticles);
- CLAMP(readStart, 0, numParticles);
- readEnd = readStart + readLen;
- CLAMP(readEnd, 0, numParticles);
-
- int k = 0;
- for (vector<float>::size_type j = readStart; j < readEnd; j++, k++) {
- lifePointer->at(j) = bufferPLife[k];
- }
- todoParticles -= readLen;
- }
- MEM_freeN(bufferPLife);
- }
- return (gzclose(gzf) == Z_OK);
-}
-
-bool MANTA::updateGridsFromFile(string filename, vector<GridItem> grids)
-{
- if (with_debug)
- cout << "MANTA::updateGridsFromFile()" << endl;
-
- if (grids.empty()) {
- cerr << "Fluid Error -- updateGridsFromFile(): Cannot read into uninitialized grid vector."
- << endl;
- return false;
- }
-
- string fname(filename);
- string::size_type idx;
-
- idx = fname.rfind('.');
- if (idx != string::npos) {
- string extension = fname.substr(idx);
-
- if (extension.compare(FLUID_DOMAIN_EXTENSION_UNI) == 0) {
- return updateGridsFromUni(filename, grids);
- }
-#if OPENVDB == 1
- else if (extension.compare(FLUID_DOMAIN_EXTENSION_OPENVDB) == 0) {
- return updateGridsFromVDB(filename, grids);
- }
-#endif
- else if (extension.compare(FLUID_DOMAIN_EXTENSION_RAW) == 0) {
- return updateGridsFromRaw(filename, grids);
- }
- else {
- cerr << "Fluid Error -- updateGridsFromFile(): Invalid file extension in file: " << filename
- << endl;
- }
- return false;
- }
- else {
- cerr << "Fluid Error -- updateGridsFromFile(): Unable to open file: " << filename << endl;
- return false;
- }
-}
-
-bool MANTA::updateGridsFromUni(string filename, vector<GridItem> grids)
-{
- if (with_debug)
- cout << "MANTA::updateGridsFromUni()" << endl;
-
- gzFile gzf;
- int expectedBytes = 0, readBytes = 0;
- int ibuffer[4];
-
- gzf = (gzFile)BLI_gzopen(filename.c_str(), "rb1");
- if (!gzf) {
- cerr << "Fluid Error -- updateGridsFromUni(): Unable to open file: " << filename << endl;
- return false;
- }
-
- char file_magic[5] = {0, 0, 0, 0, 0};
- readBytes = gzread(gzf, file_magic, 4);
- if (!readBytes) {
- cerr << "Fluid Error -- updateGridsFromUni(): Invalid header in file: " << filename << endl;
- gzclose(gzf);
- return false;
- }
- if (!strcmp(file_magic, "DDF2") || !strcmp(file_magic, "MNT1") || !strcmp(file_magic, "MNT2")) {
- cerr << "Fluid Error -- updateGridsFromUni(): Unsupported header in file: " << filename
- << endl;
- gzclose(gzf);
- return false;
- }
-
- if (!strcmp(file_magic, "MNT3")) {
-
- // grid uni header
- const int STR_LEN_GRID = 252;
- int elementType, bytesPerElement; // data type info
- char info[STR_LEN_GRID]; // mantaflow build information
- int dimT; // optionally store forth dimension for 4d grids
- unsigned long long timestamp; // creation time
-
- // read grid header
- gzread(gzf, &ibuffer, sizeof(int) * 4); // dimX, dimY, dimZ, gridType
- gzread(gzf, &elementType, sizeof(int));
- gzread(gzf, &bytesPerElement, sizeof(int));
- gzread(gzf, &info, sizeof(info));
- gzread(gzf, &dimT, sizeof(int));
- gzread(gzf, &timestamp, sizeof(unsigned long long));
-
- if (with_debug)
- cout << "Fluid: Read " << ibuffer[3] << " grid type in file: " << filename << endl;
-
- for (vector<GridItem>::iterator gIter = grids.begin(); gIter != grids.end(); ++gIter) {
- GridItem gridItem = *gIter;
- void **pointerList = gridItem.pointer;
- int type = gridItem.type;
- int *res = gridItem.res;
- assert(pointerList[0]);
- assert(res[0] == res[0] && res[1] == res[1] && res[2] == res[2]);
- UNUSED_VARS(res);
-
- switch (type) {
- case FLUID_DOMAIN_GRID_VEC3F: {
- assert(pointerList[1] && pointerList[2]);
- float **fpointers = (float **)pointerList;
- expectedBytes = sizeof(float) * 3 * ibuffer[0] * ibuffer[1] * ibuffer[2];
- readBytes = 0;
- for (int i = 0; i < ibuffer[0] * ibuffer[1] * ibuffer[2]; ++i) {
- for (int j = 0; j < 3; ++j) {
- readBytes += gzread(gzf, fpointers[j], sizeof(float));
- ++fpointers[j];
- }
- }
- break;
- }
- case FLUID_DOMAIN_GRID_FLOAT: {
- float **fpointers = (float **)pointerList;
- expectedBytes = sizeof(float) * ibuffer[0] * ibuffer[1] * ibuffer[2];
- readBytes = gzread(
- gzf, fpointers[0], sizeof(float) * ibuffer[0] * ibuffer[1] * ibuffer[2]);
- break;
- }
- default: {
- cerr << "Fluid Error -- Unknown grid type" << endl;
- }
- }
-
- if (!readBytes) {
- cerr << "Fluid Error -- updateGridFromRaw(): Unable to read raw file: " << filename
- << endl;
- gzclose(gzf);
- return false;
- }
- assert(expectedBytes == readBytes);
- UNUSED_VARS(expectedBytes);
-
- if (with_debug)
- cout << "Fluid: Read successfully: " << filename << endl;
- }
- }
- else {
- cerr << "Fluid Error -- updateGridsFromUni(): Unknown header in file: " << filename << endl;
- gzclose(gzf);
- return false;
- }
-
- return (gzclose(gzf) == Z_OK);
-}
-
-#if OPENVDB == 1
-bool MANTA::updateGridsFromVDB(string filename, vector<GridItem> grids)
-{
- if (with_debug)
- cout << "MANTA::updateGridsFromVDB()" << endl;
-
- openvdb::initialize();
- openvdb::io::File file(filename);
- try {
- file.open();
- }
- catch (const openvdb::IoError &) {
- cerr << "Fluid Error -- updateGridsFromVDB(): IOError, invalid OpenVDB file: " << filename
- << endl;
- return false;
- }
- if (grids.empty()) {
- cerr << "Fluid Error -- updateGridsFromVDB(): No grids found in grid vector" << endl;
- return false;
- }
-
- unordered_map<string, openvdb::FloatGrid::Accessor> floatAccessors;
- unordered_map<string, openvdb::Vec3SGrid::Accessor> vec3fAccessors;
- openvdb::GridBase::Ptr baseGrid;
-
- /* Get accessors to all grids in this OpenVDB file.*/
- for (vector<GridItem>::iterator gIter = grids.begin(); gIter != grids.end(); ++gIter) {
- GridItem gridItem = *gIter;
- string itemName = gridItem.name;
- int itemType = gridItem.type;
-
- for (openvdb::io::File::NameIterator nameIter = file.beginName(); nameIter != file.endName();
- ++nameIter) {
- string vdbName = nameIter.gridName();
- bool nameMatch = !itemName.compare(vdbName);
-
- /* Support for <= 2.83: If file has only one grid in it, use that grid. */
- openvdb::io::File::NameIterator peekNext = nameIter;
- bool onlyGrid = (++peekNext == file.endName());
- if (onlyGrid) {
- vdbName = itemName;
- }
-
- if (nameMatch || onlyGrid) {
- baseGrid = file.readGrid(nameIter.gridName());
-
- switch (itemType) {
- case FLUID_DOMAIN_GRID_VEC3F: {
- openvdb::Vec3SGrid::Ptr gridVDB = openvdb::gridPtrCast<openvdb::Vec3SGrid>(baseGrid);
- openvdb::Vec3SGrid::Accessor vdbAccessor = gridVDB->getAccessor();
- vec3fAccessors.emplace(vdbName, vdbAccessor);
- break;
- }
- case FLUID_DOMAIN_GRID_FLOAT: {
- openvdb::FloatGrid::Ptr gridVDB = openvdb::gridPtrCast<openvdb::FloatGrid>(baseGrid);
- openvdb::FloatGrid::Accessor vdbAccessor = gridVDB->getAccessor();
- floatAccessors.emplace(vdbName, vdbAccessor);
- break;
- }
- default: {
- cerr << "Fluid Error -- Unknown grid type" << endl;
- }
- }
- }
- else {
- cerr << "Fluid Error -- Could not read grid from file" << endl;
- return false;
- }
- }
- }
- file.close();
-
- size_t index = 0;
-
- /* Use res of first grid for grid loop. All grids must be same size anyways. */
- vector<GridItem>::iterator gIter = grids.begin();
- int *res = (*gIter).res;
-
- for (int z = 0; z < res[2]; ++z) {
- for (int y = 0; y < res[1]; ++y) {
- for (int x = 0; x < res[0]; ++x, ++index) {
- openvdb::Coord xyz(x, y, z);
-
- for (vector<GridItem>::iterator gIter = grids.begin(); gIter != grids.end(); ++gIter) {
- GridItem gridItem = *gIter;
- void **pointerList = gridItem.pointer;
- int type = gridItem.type;
- int *res = gridItem.res;
- assert(pointerList[0]);
- assert(res[0] == res[0] && res[1] == res[1] && res[2] == res[2]);
- UNUSED_VARS(res);
-
- switch (type) {
- case FLUID_DOMAIN_GRID_VEC3F: {
- unordered_map<string, openvdb::Vec3SGrid::Accessor>::iterator it;
- it = vec3fAccessors.find(gridItem.name);
- if (it == vec3fAccessors.end()) {
- cerr << "Fluid Error -- '" << gridItem.name << "' not in vdb grid map" << endl;
- return false;
- }
- openvdb::Vec3f v = it->second.getValue(xyz);
-
- assert(pointerList[1] && pointerList[2]);
- float **fpointers = (float **)pointerList;
- for (int j = 0; j < 3; ++j) {
- (fpointers[j])[index] = (float)v[j];
- }
- break;
- }
- case FLUID_DOMAIN_GRID_FLOAT: {
- unordered_map<string, openvdb::FloatGrid::Accessor>::iterator it;
- it = floatAccessors.find(gridItem.name);
- if (it == floatAccessors.end()) {
- cerr << "Fluid Error -- '" << gridItem.name << "' not in vdb grid map" << endl;
- return false;
- }
- float v = it->second.getValue(xyz);
- float **fpointers = (float **)pointerList;
- (fpointers[0])[index] = v;
- break;
- }
- default: {
- cerr << "Fluid Error -- Unknown grid type" << endl;
- }
- }
- }
- }
- }
- }
- if (with_debug)
- cout << "Fluid: Read successfully: " << filename << endl;
-
- return true;
-}
-#endif
-
-bool MANTA::updateGridsFromRaw(string filename, vector<GridItem> grids)
-{
- if (with_debug)
- cout << "MANTA::updateGridsFromRaw()" << endl;
-
- gzFile gzf;
- int expectedBytes, readBytes;
-
- gzf = (gzFile)BLI_gzopen(filename.c_str(), "rb");
- if (!gzf) {
- cout << "MANTA::updateGridsFromRaw(): unable to open file" << endl;
- return false;
- }
-
- for (vector<GridItem>::iterator gIter = grids.begin(); gIter != grids.end(); ++gIter) {
- GridItem gridItem = *gIter;
- void **pointerList = gridItem.pointer;
- int type = gridItem.type;
- int *res = gridItem.res;
- assert(pointerList[0]);
- assert(res[0] == res[0] && res[1] == res[1] && res[2] == res[2]);
- UNUSED_VARS(res);
-
- switch (type) {
- case FLUID_DOMAIN_GRID_VEC3F: {
- assert(pointerList[1] && pointerList[2]);
- float **fpointers = (float **)pointerList;
- expectedBytes = sizeof(float) * 3 * res[0] * res[1] * res[2];
- readBytes = 0;
- for (int i = 0; i < res[0] * res[1] * res[2]; ++i) {
- for (int j = 0; j < 3; ++j) {
- readBytes += gzread(gzf, fpointers[j], sizeof(float));
- ++fpointers[j];
- }
- }
- break;
- }
- case FLUID_DOMAIN_GRID_FLOAT: {
- float **fpointers = (float **)pointerList;
- expectedBytes = sizeof(float) * res[0] * res[1] * res[2];
- readBytes = gzread(gzf, fpointers[0], expectedBytes);
- break;
- }
- default: {
- cerr << "Fluid Error -- Unknown grid type" << endl;
- }
- }
-
- if (!readBytes) {
- cerr << "Fluid Error -- updateGridsFromRaw(): Unable to read raw file: " << filename << endl;
- gzclose(gzf);
- return false;
- }
- assert(expectedBytes == readBytes);
-
- if (with_debug)
- cout << "Fluid: Read successfully: " << filename << endl;
- }
-
- if (with_debug)
- cout << "Fluid: Read successfully: " << filename << endl;
-
- return (gzclose(gzf) == Z_OK);
-}
-
void MANTA::updatePointers()
{
if (with_debug)
@@ -3487,43 +2109,78 @@ bool MANTA::hasData(FluidModifierData *mmd, int framenr)
string filename = (mUsingSmoke) ? FLUID_NAME_DENSITY : FLUID_NAME_PP;
exists = BLI_exists(getFile(mmd, FLUID_DOMAIN_DIR_DATA, filename, extension, framenr).c_str());
}
+ if (with_debug)
+ cout << "Fluid: Has Data: " << exists << endl;
+
return exists;
}
bool MANTA::hasNoise(FluidModifierData *mmd, int framenr)
{
- string extension = getCacheFileEnding(mmd->domain->cache_noise_format);
+ string extension = getCacheFileEnding(mmd->domain->cache_data_format);
bool exists = BLI_exists(
getFile(mmd, FLUID_DOMAIN_DIR_NOISE, FLUID_NAME_NOISE, extension, framenr).c_str());
/* Check single file naming. */
if (!exists) {
+ extension = getCacheFileEnding(mmd->domain->cache_data_format);
+ exists = BLI_exists(
+ getFile(mmd, FLUID_DOMAIN_DIR_NOISE, FLUID_NAME_DENSITY_NOISE, extension, framenr)
+ .c_str());
+ }
+ /* Check single file naming with deprecated extension. */
+ if (!exists) {
+ extension = getCacheFileEnding(mmd->domain->cache_noise_format);
exists = BLI_exists(
getFile(mmd, FLUID_DOMAIN_DIR_NOISE, FLUID_NAME_DENSITY_NOISE, extension, framenr)
.c_str());
}
+ if (with_debug)
+ cout << "Fluid: Has Noise: " << exists << endl;
+
return exists;
}
bool MANTA::hasMesh(FluidModifierData *mmd, int framenr)
{
string extension = getCacheFileEnding(mmd->domain->cache_mesh_format);
- return BLI_exists(
- getFile(mmd, FLUID_DOMAIN_DIR_MESH, FLUID_NAME_LMESH, extension, framenr).c_str());
+ bool exists = BLI_exists(
+ getFile(mmd, FLUID_DOMAIN_DIR_MESH, FLUID_NAME_MESH, extension, framenr).c_str());
+
+ /* Check old file naming. */
+ if (!exists) {
+ exists = BLI_exists(
+ getFile(mmd, FLUID_DOMAIN_DIR_MESH, FLUID_NAME_LMESH, extension, framenr).c_str());
+ }
+ if (with_debug)
+ cout << "Fluid: Has Mesh: " << exists << endl;
+
+ return exists;
}
bool MANTA::hasParticles(FluidModifierData *mmd, int framenr)
{
- string extension = getCacheFileEnding(mmd->domain->cache_particle_format);
+ string extension = getCacheFileEnding(mmd->domain->cache_data_format);
bool exists = BLI_exists(
getFile(mmd, FLUID_DOMAIN_DIR_PARTICLES, FLUID_NAME_PARTICLES, extension, framenr).c_str());
/* Check single file naming. */
if (!exists) {
+ extension = getCacheFileEnding(mmd->domain->cache_data_format);
exists = BLI_exists(
getFile(mmd, FLUID_DOMAIN_DIR_PARTICLES, FLUID_NAME_PP_PARTICLES, extension, framenr)
.c_str());
}
+ /* Check single file naming with deprecated extension. */
+ if (!exists) {
+ extension = getCacheFileEnding(mmd->domain->cache_particle_format);
+ exists = BLI_exists(
+ getFile(mmd, FLUID_DOMAIN_DIR_PARTICLES, FLUID_NAME_PP_PARTICLES, extension, framenr)
+ .c_str());
+ }
+ if (with_debug)
+ cout << "Fluid: Has Particles: " << exists << endl;
+
return exists;
}
@@ -3532,7 +2189,11 @@ bool MANTA::hasGuiding(FluidModifierData *mmd, int framenr, bool sourceDomain)
string subdirectory = (sourceDomain) ? FLUID_DOMAIN_DIR_DATA : FLUID_DOMAIN_DIR_GUIDE;
string filename = (sourceDomain) ? FLUID_NAME_VELOCITY : FLUID_NAME_GUIDEVEL;
string extension = getCacheFileEnding(mmd->domain->cache_data_format);
- return BLI_exists(getFile(mmd, subdirectory, filename, extension, framenr).c_str());
+ bool exists = BLI_exists(getFile(mmd, subdirectory, filename, extension, framenr).c_str());
+ if (with_debug)
+ cout << "Fluid: Has Guiding: " << exists << endl;
+
+ return exists;
}
string MANTA::getDirectory(FluidModifierData *mmd, string subdirectory)
diff --git a/intern/mantaflow/intern/MANTA_main.h b/intern/mantaflow/intern/MANTA_main.h
index 6a8484c75d9..38cbd33ea0a 100644
--- a/intern/mantaflow/intern/MANTA_main.h
+++ b/intern/mantaflow/intern/MANTA_main.h
@@ -60,19 +60,6 @@ struct MANTA {
int flags;
} Triangle;
- // Cache helper typedefs
- typedef struct GridItem {
- void **pointer; /* Array of pointers for this grid.*/
- int type;
- int *res;
- string name;
- } GridItem;
-
- typedef struct FileItem {
- string filename;
- vector<GridItem> grids;
- } FileItem;
-
// Manta step, handling everything
void step(struct FluidModifierData *mmd, int startFrame);
@@ -104,10 +91,10 @@ struct MANTA {
// Read cache (via Manta save/load)
bool readConfiguration(FluidModifierData *mmd, int framenr);
- bool readData(FluidModifierData *mmd, int framenr);
- bool readNoise(FluidModifierData *mmd, int framenr);
+ bool readData(FluidModifierData *mmd, int framenr, bool resumable);
+ bool readNoise(FluidModifierData *mmd, int framenr, bool resumable);
bool readMesh(FluidModifierData *mmd, int framenr);
- bool readParticles(FluidModifierData *mmd, int framenr);
+ bool readParticles(FluidModifierData *mmd, int framenr, bool resumable);
bool readGuiding(FluidModifierData *mmd, int framenr, bool sourceDomain);
// Read cache (via file read functions in MANTA - e.g. read .bobj.gz meshes, .uni particles)
@@ -899,16 +886,6 @@ struct MANTA {
string getRealValue(const string &varName);
string parseLine(const string &line);
string parseScript(const string &setup_string, FluidModifierData *mmd = NULL);
- bool updateMeshFromBobj(string filename);
- bool updateMeshFromObj(string filename);
- bool updateMeshFromUni(string filename);
- bool updateParticlesFromUni(string filename, bool isSecondarySys, bool isVelData);
- bool updateGridsFromUni(string filename, vector<GridItem> grids);
- bool updateGridsFromVDB(string filename, vector<GridItem> grids);
- bool updateGridsFromRaw(string filename, vector<GridItem> grids);
- bool updateMeshFromFile(string filename);
- bool updateParticlesFromFile(string filename, bool isSecondarySys, bool isVelData);
- bool updateGridsFromFile(string filename, vector<GridItem> grids);
string getDirectory(struct FluidModifierData *mmd, string subdirectory);
string getFile(struct FluidModifierData *mmd,
string subdirectory,
diff --git a/intern/mantaflow/intern/manta_fluid_API.cpp b/intern/mantaflow/intern/manta_fluid_API.cpp
index 49bc224b3fa..f1607f1bd99 100644
--- a/intern/mantaflow/intern/manta_fluid_API.cpp
+++ b/intern/mantaflow/intern/manta_fluid_API.cpp
@@ -94,18 +94,18 @@ int manta_read_config(MANTA *fluid, FluidModifierData *mmd, int framenr)
return fluid->readConfiguration(mmd, framenr);
}
-int manta_read_data(MANTA *fluid, FluidModifierData *mmd, int framenr)
+int manta_read_data(MANTA *fluid, FluidModifierData *mmd, int framenr, bool resumable)
{
if (!fluid || !mmd)
return 0;
- return fluid->readData(mmd, framenr);
+ return fluid->readData(mmd, framenr, resumable);
}
-int manta_read_noise(MANTA *fluid, FluidModifierData *mmd, int framenr)
+int manta_read_noise(MANTA *fluid, FluidModifierData *mmd, int framenr, bool resumable)
{
if (!fluid || !mmd)
return 0;
- return fluid->readNoise(mmd, framenr);
+ return fluid->readNoise(mmd, framenr, resumable);
}
int manta_read_mesh(MANTA *fluid, FluidModifierData *mmd, int framenr)
@@ -115,11 +115,11 @@ int manta_read_mesh(MANTA *fluid, FluidModifierData *mmd, int framenr)
return fluid->readMesh(mmd, framenr);
}
-int manta_read_particles(MANTA *fluid, FluidModifierData *mmd, int framenr)
+int manta_read_particles(MANTA *fluid, FluidModifierData *mmd, int framenr, bool resumable)
{
if (!fluid || !mmd)
return 0;
- return fluid->readParticles(mmd, framenr);
+ return fluid->readParticles(mmd, framenr, resumable);
}
int manta_read_guiding(MANTA *fluid, FluidModifierData *mmd, int framenr, bool sourceDomain)
@@ -129,41 +129,6 @@ int manta_read_guiding(MANTA *fluid, FluidModifierData *mmd, int framenr, bool s
return fluid->readGuiding(mmd, framenr, sourceDomain);
}
-int manta_update_liquid_structures(MANTA *fluid, FluidModifierData *mmd, int framenr)
-{
- if (!fluid || !mmd)
- return 0;
- return fluid->updateFlipStructures(mmd, framenr);
-}
-
-int manta_update_mesh_structures(MANTA *fluid, FluidModifierData *mmd, int framenr)
-{
- if (!fluid || !mmd)
- return 0;
- return fluid->updateMeshStructures(mmd, framenr);
-}
-
-int manta_update_particle_structures(MANTA *fluid, FluidModifierData *mmd, int framenr)
-{
- if (!fluid || !mmd)
- return 0;
- return fluid->updateParticleStructures(mmd, framenr);
-}
-
-int manta_update_smoke_structures(MANTA *fluid, FluidModifierData *mmd, int framenr)
-{
- if (!fluid || !mmd)
- return 0;
- return fluid->updateSmokeStructures(mmd, framenr);
-}
-
-int manta_update_noise_structures(MANTA *fluid, FluidModifierData *mmd, int framenr)
-{
- if (!fluid || !mmd)
- return 0;
- return fluid->updateNoiseStructures(mmd, framenr);
-}
-
int manta_bake_data(MANTA *fluid, FluidModifierData *mmd, int framenr)
{
if (!fluid || !mmd)
diff --git a/intern/mantaflow/intern/strings/fluid_script.h b/intern/mantaflow/intern/strings/fluid_script.h
index 637dd22f128..62274101859 100644
--- a/intern/mantaflow/intern/strings/fluid_script.h
+++ b/intern/mantaflow/intern/strings/fluid_script.h
@@ -33,7 +33,7 @@ from manta import *\n\
import os.path, shutil, math, sys, gc, multiprocessing, platform, time\n\
\n\
withMPBake = False # Bake files asynchronously\n\
-withMPSave = True # Save files asynchronously\n\
+withMPSave = False # Save files asynchronously\n\
isWindows = platform.system() != 'Darwin' and platform.system() != 'Linux'\n\
# TODO (sebbas): Use this to simulate Windows multiprocessing (has default mode spawn)\n\
#try:\n\
@@ -96,7 +96,7 @@ gravity_s$ID$ = vec3($GRAVITY_X$, $GRAVITY_Y$, $GRAVITY_Z$) # in SI unit (e.g. m
gs_s$ID$ = vec3($RESX$, $RESY$, $RESZ$)\n\
maxVel_s$ID$ = 0\n\
\n\
-doOpen_s$ID$ = $DO_OPEN$\n\
+domainClosed_s$ID$ = $DOMAIN_CLOSED$\n\
boundConditions_s$ID$ = '$BOUND_CONDITIONS$'\n\
boundaryWidth_s$ID$ = $BOUNDARY_WIDTH$\n\
deleteInObstacle_s$ID$ = $DELETE_IN_OBSTACLE$\n\
@@ -161,7 +161,19 @@ mantaMsg('scaleSpeed is ' + str(scaleSpeedFrames_s$ID$))\n\
scaleSpeedTime_s$ID$ = ratioResToBLength_s$ID$ * ratioBTimeToTimstep_s$ID$ # [blength/btime] to [cells/frameLength]\n\
mantaMsg('scaleSpeedTime is ' + str(scaleSpeedTime_s$ID$))\n\
\n\
-gravity_s$ID$ *= scaleAcceleration_s$ID$ # scale from world acceleration to cell based acceleration\n";
+gravity_s$ID$ *= scaleAcceleration_s$ID$ # scale from world acceleration to cell based acceleration\n\
+\n\
+# OpenVDB options\n\
+vdbCompression_s$ID$ = $COMPRESSION_OPENVDB$\n\
+vdbPrecisionHalf_s$ID$ = $PRECISION_OPENVDB$\n\
+\n\
+# Cache file names\n\
+file_data_s$ID$ = '$NAME_DATA$'\n\
+file_noise_s$ID$ = '$NAME_NOISE$'\n\
+file_mesh_s$ID$ = '$NAME_MESH$'\n\
+file_meshvel_s$ID$ = '$NAME_MESH$'\n\
+file_particles_s$ID$ = '$NAME_PARTICLES$'\n\
+file_guiding_s$ID$ = '$NAME_GUIDING$'";
const std::string fluid_variables_noise =
"\n\
@@ -282,8 +294,8 @@ phiIn_s$ID$.setConst(9999)\n\
phiOut_s$ID$.setConst(9999)\n\
\n\
# Keep track of important objects in dict to load them later on\n\
-fluid_data_dict_final_s$ID$ = dict(vel=vel_s$ID$, velTmp=velTmp_s$ID$)\n\
-fluid_data_dict_resume_s$ID$ = dict(phiObs=phiObs_s$ID$, phiIn=phiIn_s$ID$, phiOut=phiOut_s$ID$, flags=flags_s$ID$)\n";
+fluid_data_dict_final_s$ID$ = { 'vel' : vel_s$ID$ }\n\
+fluid_data_dict_resume_s$ID$ = { 'phiObs' : phiObs_s$ID$, 'phiIn' : phiIn_s$ID$, 'phiOut' : phiOut_s$ID$, 'flags' : flags_s$ID$, 'velTmp' : velTmp_s$ID$ }\n";
const std::string fluid_alloc_obstacle =
"\n\
@@ -497,10 +509,12 @@ def fluid_cache_get_framenr_formatted_$ID$(framenr):\n\
const std::string fluid_bake_multiprocessing =
"\n\
-def fluid_cache_multiprocessing_start_$ID$(function, framenr, format_data=None, format_noise=None, format_mesh=None, format_particles=None, format_guiding=None, path_data=None, path_noise=None, path_mesh=None, path_particles=None, path_guiding=None, dict=None, do_join=True, resumable=False):\n\
+def fluid_cache_multiprocessing_start_$ID$(function, framenr, file_name=None, format_data=None, format_noise=None, format_mesh=None, format_particles=None, format_guiding=None, path_data=None, path_noise=None, path_mesh=None, path_particles=None, path_guiding=None, dict=None, do_join=True, resumable=False):\n\
mantaMsg('Multiprocessing cache')\n\
if __name__ == '__main__':\n\
args = (framenr,)\n\
+ if file_name:\n\
+ args += (file_name,)\n\
if format_data:\n\
args += (format_data,)\n\
if format_noise:\n\
@@ -531,7 +545,7 @@ def fluid_cache_multiprocessing_start_$ID$(function, framenr, format_data=None,
const std::string fluid_bake_data =
"\n\
-def bake_fluid_process_data_$ID$(framenr, format_data, format_particles, format_guiding, path_data, path_guiding):\n\
+def bake_fluid_process_data_$ID$(framenr, format_data, path_data):\n\
mantaMsg('Bake fluid data')\n\
\n\
s$ID$.frame = framenr\n\
@@ -545,15 +559,15 @@ def bake_fluid_process_data_$ID$(framenr, format_data, format_particles, format_
liquid_adaptive_step_$ID$(framenr)\n\
mantaMsg('--- Step: %s seconds ---' % (time.time() - start_time))\n\
\n\
-def bake_fluid_data_$ID$(path_data, path_guiding, framenr, format_data, format_particles, format_guiding):\n\
+def bake_fluid_data_$ID$(path_data, framenr, format_data):\n\
if not withMPBake or isWindows:\n\
- bake_fluid_process_data_$ID$(framenr, format_data, format_particles, format_guiding, path_data, path_guiding)\n\
+ bake_fluid_process_data_$ID$(framenr, format_data, path_data)\n\
else:\n\
- fluid_cache_multiprocessing_start_$ID$(function=bake_fluid_process_data_$ID$, framenr=framenr, format_data=format_data, format_particles=format_particles, format_guiding=format_guiding, path_data=path_data, path_guiding=path_guiding, do_join=False)\n";
+ fluid_cache_multiprocessing_start_$ID$(function=bake_fluid_process_data_$ID$, framenr=framenr, format_data=format_data, path_data=path_data, do_join=False)\n";
const std::string fluid_bake_noise =
"\n\
-def bake_noise_process_$ID$(framenr, format_data, format_noise, path_data, path_noise, resumable):\n\
+def bake_noise_process_$ID$(framenr, format_noise, path_noise):\n\
mantaMsg('Bake fluid noise')\n\
\n\
sn$ID$.frame = framenr\n\
@@ -563,15 +577,15 @@ def bake_noise_process_$ID$(framenr, format_data, format_noise, path_data, path_
\n\
smoke_step_noise_$ID$(framenr)\n\
\n\
-def bake_noise_$ID$(path_data, path_noise, framenr, format_data, format_noise, resumable):\n\
+def bake_noise_$ID$(path_noise, framenr, format_noise):\n\
if not withMPBake or isWindows:\n\
- bake_noise_process_$ID$(framenr, format_data, format_noise, path_data, path_noise, resumable)\n\
+ bake_noise_process_$ID$(framenr, format_noise, path_noise)\n\
else:\n\
- fluid_cache_multiprocessing_start_$ID$(function=bake_noise_process_$ID$, framenr=framenr, format_data=format_data, format_noise=format_noise, path_data=path_data, path_noise=path_noise, resumable=resumable)\n";
+ fluid_cache_multiprocessing_start_$ID$(function=bake_noise_process_$ID$, framenr=framenr, format_noise=format_noise, path_noise=path_noise)\n";
const std::string fluid_bake_mesh =
"\n\
-def bake_mesh_process_$ID$(framenr, format_data, format_mesh, format_particles, path_data, path_mesh):\n\
+def bake_mesh_process_$ID$(framenr, format_data, format_mesh, path_mesh):\n\
mantaMsg('Bake fluid mesh')\n\
\n\
sm$ID$.frame = framenr\n\
@@ -587,15 +601,15 @@ def bake_mesh_process_$ID$(framenr, format_data, format_mesh, format_particles,
if using_speedvectors_s$ID$:\n\
liquid_save_meshvel_$ID$(path_mesh, framenr, format_data)\n\
\n\
-def bake_mesh_$ID$(path_data, path_mesh, framenr, format_data, format_mesh, format_particles):\n\
+def bake_mesh_$ID$(path_mesh, framenr, format_data, format_mesh):\n\
if not withMPBake or isWindows:\n\
- bake_mesh_process_$ID$(framenr, format_data, format_mesh, format_particles, path_data, path_mesh)\n\
+ bake_mesh_process_$ID$(framenr, format_data, format_mesh, path_mesh)\n\
else:\n\
- fluid_cache_multiprocessing_start_$ID$(function=bake_mesh_process_$ID$, framenr=framenr, format_data=format_data, format_mesh=format_mesh, format_particles=format_particles, path_data=path_data, path_mesh=path_mesh)\n";
+ fluid_cache_multiprocessing_start_$ID$(function=bake_mesh_process_$ID$, framenr=framenr, format_data=format_data, format_mesh=format_mesh, path_mesh=path_mesh)\n";
const std::string fluid_bake_particles =
"\n\
-def bake_particles_process_$ID$(framenr, format_data, format_particles, path_data, path_particles, resumable):\n\
+def bake_particles_process_$ID$(framenr, format_particles, path_particles, resumable):\n\
mantaMsg('Bake secondary particles')\n\
\n\
sp$ID$.frame = framenr\n\
@@ -609,11 +623,11 @@ def bake_particles_process_$ID$(framenr, format_data, format_particles, path_dat
liquid_step_particles_$ID$()\n\
liquid_save_particles_$ID$(path_particles, framenr, format_particles, resumable)\n\
\n\
-def bake_particles_$ID$(path_data, path_particles, framenr, format_data, format_particles, resumable):\n\
+def bake_particles_$ID$(path_particles, framenr, format_particles, resumable):\n\
if not withMPBake or isWindows:\n\
- bake_particles_process_$ID$(framenr, format_data, format_particles, path_data, path_particles, resumable)\n\
+ bake_particles_process_$ID$(framenr, format_particles, path_particles, resumable)\n\
else:\n\
- fluid_cache_multiprocessing_start_$ID$(function=bake_particles_process_$ID$, framenr=framenr, format_data=format_data, format_particles=format_particles, path_data=path_data, path_particles=path_particles, resumable=resumable)\n";
+ fluid_cache_multiprocessing_start_$ID$(function=bake_particles_process_$ID$, framenr=framenr, format_particles=format_particles, path_particles=path_particles, resumable=resumable)\n";
const std::string fluid_bake_guiding =
"\n\
@@ -650,43 +664,47 @@ def bake_guiding_$ID$(path_guiding, framenr, format_guiding, resumable):\n\
const std::string fluid_file_import =
"\n\
-def fluid_file_import_s$ID$(dict, path, framenr, file_format):\n\
+def fluid_file_import_s$ID$(dict, path, framenr, file_format, file_name=None):\n\
+ mantaMsg('Fluid file import, frame: ' + str(framenr))\n\
try:\n\
framenr = fluid_cache_get_framenr_formatted_$ID$(framenr)\n\
- for name, object in dict.items():\n\
- file = os.path.join(path, name + '_' + framenr + file_format)\n\
+ # New cache: Try to load the data from a single file\n\
+ loadCombined = 0\n\
+ if file_name is not None:\n\
+ file = os.path.join(path, file_name + '_' + framenr + file_format)\n\
if os.path.isfile(file):\n\
- object.load(file)\n\
- else:\n\
- mantaMsg('Could not load file ' + str(file))\n\
- except:\n\
- mantaMsg('exception found')\n\
- #mantaMsg(str(e))\n\
- pass # Just skip file load errors for now\n";
-
-const std::string fluid_load_data =
- "\n\
-def fluid_load_data_$ID$(path, framenr, file_format, resumable):\n\
- mantaMsg('Fluid load data, frame ' + str(framenr))\n\
- fluid_file_import_s$ID$(dict=fluid_data_dict_final_s$ID$, path=path, framenr=framenr, file_format=file_format)\n\
- \n\
- if resumable:\n\
- fluid_file_import_s$ID$(dict=fluid_data_dict_resume_s$ID$, path=path, framenr=framenr, file_format=file_format)\n\
+ if file_format == '.vdb':\n\
+ loadCombined = load(name=file, objects=list(dict.values()), worldSize=domainSize_s$ID$)\n\
+ elif file_format == '.bobj.gz' or file_format == '.obj':\n\
+ for name, object in dict.items():\n\
+ if os.path.isfile(file):\n\
+ loadCombined = object.load(file)\n\
+ \n\
+ # Old cache: Try to load the data from separate files, i.e. per object with the object based load() function\n\
+ if not loadCombined:\n\
+ for name, object in dict.items():\n\
+ file = os.path.join(path, name + '_' + framenr + file_format)\n\
+ if os.path.isfile(file):\n\
+ loadCombined = object.load(file)\n\
\n\
- # When adaptive domain bake is resumed we need correct values in xyz vel grids\n\
- copyVec3ToReal(source=vel_s$ID$, targetX=x_vel_s$ID$, targetY=y_vel_s$ID$, targetZ=z_vel_s$ID$)\n";
+ if not loadCombined:\n\
+ mantaMsg('Could not load file ' + str(file))\n\
+ \n\
+ except Exception as e:\n\
+ mantaMsg('Exception in Python fluid file import: ' + str(e))\n\
+ pass # Just skip file load errors for now\n";
const std::string fluid_load_guiding =
"\n\
def fluid_load_guiding_$ID$(path, framenr, file_format):\n\
mantaMsg('Fluid load guiding, frame ' + str(framenr))\n\
- fluid_file_import_s$ID$(dict=fluid_guiding_dict_s$ID$, path=path, framenr=framenr, file_format=file_format)\n";
+ fluid_file_import_s$ID$(dict=fluid_guiding_dict_s$ID$, path=path, framenr=framenr, file_format=file_format, file_name=file_guiding_s$ID$)\n";
const std::string fluid_load_vel =
"\n\
def fluid_load_vel_$ID$(path, framenr, file_format):\n\
mantaMsg('Fluid load vel, frame ' + str(framenr))\n\
- fluid_vel_dict_s$ID$ = dict(vel=guidevel_sg$ID$)\n\
+ fluid_vel_dict_s$ID$ = { 'vel' : guidevel_sg$ID$ }\n\
fluid_file_import_s$ID$(dict=fluid_vel_dict_s$ID$, path=path, framenr=framenr, file_format=file_format)\n";
//////////////////////////////////////////////////////////////////////
@@ -695,7 +713,7 @@ def fluid_load_vel_$ID$(path, framenr, file_format):\n\
const std::string fluid_file_export =
"\n\
-def fluid_file_export_s$ID$(framenr, file_format, path, dict, mode_override=True, skip_subframes=True):\n\
+def fluid_file_export_s$ID$(framenr, file_format, path, dict, file_name=None, mode_override=True, skip_subframes=True):\n\
if skip_subframes and ((timePerFrame_s$ID$ + dt0_s$ID$) < frameLength_s$ID$):\n\
return\n\
mantaMsg('Fluid file export, frame: ' + str(framenr))\n\
@@ -703,36 +721,37 @@ def fluid_file_export_s$ID$(framenr, file_format, path, dict, mode_override=True
framenr = fluid_cache_get_framenr_formatted_$ID$(framenr)\n\
if not os.path.exists(path):\n\
os.makedirs(path)\n\
- for name, object in dict.items():\n\
- file = os.path.join(path, name + '_' + framenr + file_format)\n\
- if not os.path.isfile(file) or mode_override: object.save(file)\n\
+ \n\
+ # New cache: Try to save the data to a single file\n\
+ saveCombined = 0\n\
+ if file_name is not None:\n\
+ file = os.path.join(path, file_name + '_' + framenr + file_format)\n\
+ if not os.path.isfile(file) or mode_override:\n\
+ if file_format == '.vdb':\n\
+ saveCombined = save(name=file, objects=list(dict.values()), worldSize=domainSize_s$ID$, skipDeletedParts=True, compression=vdbCompression_s$ID$, precisionHalf=vdbPrecisionHalf_s$ID$)\n\
+ elif file_format == '.bobj.gz' or file_format == '.obj':\n\
+ for name, object in dict.items():\n\
+ if not os.path.isfile(file) or mode_override:\n\
+ saveCombined = object.save(file)\n\
+ \n\
+ # Old cache: Try to save the data to separate files, i.e. per object with the object based save() function\n\
+ if not saveCombined:\n\
+ for name, object in dict.items():\n\
+ file = os.path.join(path, name + '_' + framenr + file_format)\n\
+ if not os.path.isfile(file) or mode_override: object.save(file)\n\
+ \n\
except Exception as e:\n\
- mantaMsg(str(e))\n\
+ mantaMsg('Exception in Python fluid file export: ' + str(e))\n\
pass # Just skip file save errors for now\n";
-const std::string fluid_save_data =
- "\n\
-def fluid_save_data_$ID$(path, framenr, file_format, resumable):\n\
- mantaMsg('Fluid save data, frame ' + str(framenr))\n\
- start_time = time.time()\n\
- if not withMPSave or isWindows:\n\
- fluid_file_export_s$ID$(framenr=framenr, file_format=file_format, path=path, dict=fluid_data_dict_final_s$ID$)\n\
- if resumable:\n\
- fluid_file_export_s$ID$(framenr=framenr, file_format=file_format, path=path, dict=fluid_data_dict_resume_s$ID$)\n\
- else:\n\
- fluid_cache_multiprocessing_start_$ID$(function=fluid_file_export_s$ID$, framenr=framenr, format_data=file_format, path_data=path, dict=fluid_data_dict_final_s$ID$, do_join=False)\n\
- if resumable:\n\
- fluid_cache_multiprocessing_start_$ID$(function=fluid_file_export_s$ID$, framenr=framenr, format_data=file_format, path_data=path, dict=fluid_data_dict_resume_s$ID$, do_join=False)\n\
- mantaMsg('--- Save: %s seconds ---' % (time.time() - start_time))\n";
-
const std::string fluid_save_guiding =
"\n\
-def fluid_save_guiding_$ID$(path, framenr, file_format, resumable):\n\
+def fluid_save_guiding_$ID$(path, framenr, file_format):\n\
mantaMsg('Fluid save guiding, frame ' + str(framenr))\n\
if not withMPSave or isWindows:\n\
- fluid_file_export_s$ID$(dict=fluid_guiding_dict_s$ID$, framenr=framenr, file_format=file_format, path=path)\n\
+ fluid_file_export_s$ID$(dict=fluid_guiding_dict_s$ID$, framenr=framenr, file_format=file_format, path=path, file_name=file_guiding_s$ID$)\n\
else:\n\
- fluid_cache_multiprocessing_start_$ID$(function=fluid_file_export_s$ID$, framenr=framenr, format_data=file_format, path_data=path, dict=fluid_guiding_dict_s$ID$, do_join=False)\n";
+ fluid_cache_multiprocessing_start_$ID$(function=fluid_file_export_s$ID$, file_name=file_guiding_s$ID$, framenr=framenr, format_data=file_format, path_data=path, dict=fluid_guiding_dict_s$ID$, do_join=False)\n";
//////////////////////////////////////////////////////////////////////
// STANDALONE MODE
diff --git a/intern/mantaflow/intern/strings/liquid_script.h b/intern/mantaflow/intern/strings/liquid_script.h
index 26b6644f231..04505206601 100644
--- a/intern/mantaflow/intern/strings/liquid_script.h
+++ b/intern/mantaflow/intern/strings/liquid_script.h
@@ -86,16 +86,16 @@ mapWeights_s$ID$ = s$ID$.create(MACGrid, name='$NAME_MAPWEIGHTS$')\n\
fractions_s$ID$ = None # allocated dynamically\n\
curvature_s$ID$ = None\n\
\n\
-pp_s$ID$ = s$ID$.create(BasicParticleSystem, name='$NAME_PP$')\n\
-pVel_pp$ID$ = pp_s$ID$.create(PdataVec3, name='$NAME_PVEL$')\n\
+pp_s$ID$ = s$ID$.create(BasicParticleSystem, name='$NAME_PARTS$')\n\
+pVel_pp$ID$ = pp_s$ID$.create(PdataVec3, name='$NAME_PARTSVELOCITY$')\n\
\n\
# Acceleration data for particle nbs\n\
pindex_s$ID$ = s$ID$.create(ParticleIndexSystem, name='$NAME_PINDEX$')\n\
gpi_s$ID$ = s$ID$.create(IntGrid, name='$NAME_GPI$')\n\
\n\
# Keep track of important objects in dict to load them later on\n\
-liquid_data_dict_final_s$ID$ = dict(pp=pp_s$ID$, pVel=pVel_pp$ID$)\n\
-liquid_data_dict_resume_s$ID$ = dict(phiParts=phiParts_s$ID$, phi=phi_s$ID$, phiTmp=phiTmp_s$ID$)\n";
+liquid_data_dict_final_s$ID$ = { 'pVel' : pVel_pp$ID$, 'pp' : pp_s$ID$ }\n\
+liquid_data_dict_resume_s$ID$ = { 'phiParts' : phiParts_s$ID$, 'phi' : phi_s$ID$, 'phiTmp' : phiTmp_s$ID$ }\n";
const std::string liquid_alloc_mesh =
"\n\
@@ -104,7 +104,7 @@ phiParts_sm$ID$ = sm$ID$.create(LevelsetGrid, name='$NAME_PHIPARTS_MESH$')\n\
phi_sm$ID$ = sm$ID$.create(LevelsetGrid, name='$NAME_PHI_MESH$')\n\
pp_sm$ID$ = sm$ID$.create(BasicParticleSystem, name='$NAME_PP_MESH$')\n\
flags_sm$ID$ = sm$ID$.create(FlagGrid, name='$NAME_FLAGS_MESH$')\n\
-mesh_sm$ID$ = sm$ID$.create(Mesh, name='$NAME_LMESH$')\n\
+mesh_sm$ID$ = sm$ID$.create(Mesh, name='$NAME_MESH$')\n\
\n\
if using_speedvectors_s$ID$:\n\
mVel_mesh$ID$ = mesh_sm$ID$.create(MdataVec3, name='$NAME_VELOCITYVEC_MESH$')\n\
@@ -119,10 +119,10 @@ phiParts_sm$ID$.setConst(9999)\n\
phi_sm$ID$.setConst(9999)\n\
\n\
# Keep track of important objects in dict to load them later on\n\
-liquid_mesh_dict_s$ID$ = dict(lMesh=mesh_sm$ID$)\n\
+liquid_mesh_dict_s$ID$ = { 'lMesh' : mesh_sm$ID$ }\n\
\n\
if using_speedvectors_s$ID$:\n\
- liquid_meshvel_dict_s$ID$ = dict(lVelMesh=mVel_mesh$ID$)\n";
+ liquid_meshvel_dict_s$ID$ = { 'lVelMesh' : mVel_mesh$ID$ }\n";
const std::string liquid_alloc_curvature =
"\n\
@@ -131,20 +131,20 @@ curvature_s$ID$ = s$ID$.create(RealGrid, name='$NAME_CURVATURE$')\n";
const std::string liquid_alloc_particles =
"\n\
-ppSnd_sp$ID$ = sp$ID$.create(BasicParticleSystem, name='$FLUID_NAME_PP_PARTICLES$')\n\
-pVelSnd_pp$ID$ = ppSnd_sp$ID$.create(PdataVec3, name='$FLUID_NAME_PVEL_PARTICLES$')\n\
-pForceSnd_pp$ID$ = ppSnd_sp$ID$.create(PdataVec3, name='$FLUID_NAME_PFORCE_PARTICLES$')\n\
-pLifeSnd_pp$ID$ = ppSnd_sp$ID$.create(PdataReal, name='$FLUID_NAME_PLIFE_PARTICLES$')\n\
-vel_sp$ID$ = sp$ID$.create(MACGrid, name='$FLUID_NAME_VELOCITY_PARTICLES$')\n\
-flags_sp$ID$ = sp$ID$.create(FlagGrid, name='$FLUID_NAME_FLAGS_PARTICLES$')\n\
-phi_sp$ID$ = sp$ID$.create(LevelsetGrid, name='$FLUID_NAME_PHI_PARTICLES$')\n\
-phiObs_sp$ID$ = sp$ID$.create(LevelsetGrid, name='$FLUID_NAME_PHIOBS_PARTICLES$')\n\
-phiOut_sp$ID$ = sp$ID$.create(LevelsetGrid, name='$FLUID_NAME_PHIOUT_PARTICLES$')\n\
-normal_sp$ID$ = sp$ID$.create(VecGrid, name='$FLUID_NAME_NORMAL_PARTICLES$')\n\
-neighborRatio_sp$ID$ = sp$ID$.create(RealGrid, name='$FLUID_NAME_NEIGHBORRATIO_PARTICLES$')\n\
-trappedAir_sp$ID$ = sp$ID$.create(RealGrid, name='$FLUID_NAME_TRAPPEDAIR_PARTICLES$')\n\
-waveCrest_sp$ID$ = sp$ID$.create(RealGrid, name='$FLUID_NAME_WAVECREST_PARTICLES$')\n\
-kineticEnergy_sp$ID$ = sp$ID$.create(RealGrid, name='$FLUID_NAME_KINETICENERGY_PARTICLES$')\n\
+ppSnd_sp$ID$ = sp$ID$.create(BasicParticleSystem, name='$NAME_PARTS_PARTICLES$')\n\
+pVelSnd_pp$ID$ = ppSnd_sp$ID$.create(PdataVec3, name='$NAME_PARTSVEL_PARTICLES$')\n\
+pForceSnd_pp$ID$ = ppSnd_sp$ID$.create(PdataVec3, name='$NAME_PARTSFORCE_PARTICLES$')\n\
+pLifeSnd_pp$ID$ = ppSnd_sp$ID$.create(PdataReal, name='$NAME_PARTSLIFE_PARTICLES$')\n\
+vel_sp$ID$ = sp$ID$.create(MACGrid, name='$NAME_VELOCITY_PARTICLES$')\n\
+flags_sp$ID$ = sp$ID$.create(FlagGrid, name='$NAME_FLAGS_PARTICLES$')\n\
+phi_sp$ID$ = sp$ID$.create(LevelsetGrid, name='$NAME_PHI_PARTICLES$')\n\
+phiObs_sp$ID$ = sp$ID$.create(LevelsetGrid, name='$NAME_PHIOBS_PARTICLES$')\n\
+phiOut_sp$ID$ = sp$ID$.create(LevelsetGrid, name='$NAME_PHIOUT_PARTICLES$')\n\
+normal_sp$ID$ = sp$ID$.create(VecGrid, name='$NAME_NORMAL_PARTICLES$')\n\
+neighborRatio_sp$ID$ = sp$ID$.create(RealGrid, name='$NAME_NEIGHBORRATIO_PARTICLES$')\n\
+trappedAir_sp$ID$ = sp$ID$.create(RealGrid, name='$NAME_TRAPPEDAIR_PARTICLES$')\n\
+waveCrest_sp$ID$ = sp$ID$.create(RealGrid, name='$NAME_WAVECREST_PARTICLES$')\n\
+kineticEnergy_sp$ID$ = sp$ID$.create(RealGrid, name='$NAME_KINETICENERGY_PARTICLES$')\n\
\n\
# Set some initial values\n\
phi_sp$ID$.setConst(9999)\n\
@@ -152,8 +152,8 @@ phiObs_sp$ID$.setConst(9999)\n\
phiOut_sp$ID$.setConst(9999)\n\
\n\
# Keep track of important objects in dict to load them later on\n\
-liquid_particles_dict_final_s$ID$ = dict(ppSnd=ppSnd_sp$ID$, pVelSnd=pVelSnd_pp$ID$, pLifeSnd=pLifeSnd_pp$ID$)\n\
-liquid_particles_dict_resume_s$ID$ = dict(trappedAir=trappedAir_sp$ID$, waveCrest=waveCrest_sp$ID$, kineticEnergy=kineticEnergy_sp$ID$)\n";
+liquid_particles_dict_final_s$ID$ = { 'pVelSnd' : pVelSnd_pp$ID$, 'pLifeSnd' : pLifeSnd_pp$ID$, 'ppSnd' : ppSnd_sp$ID$ }\n\
+liquid_particles_dict_resume_s$ID$ = { 'trappedAir' : trappedAir_sp$ID$, 'waveCrest' : waveCrest_sp$ID$, 'kineticEnergy' : kineticEnergy_sp$ID$ }\n";
const std::string liquid_init_phi =
"\n\
@@ -211,10 +211,10 @@ def liquid_adaptive_step_$ID$(framenr):\n\
if using_invel_s$ID$:\n\
extrapolateVec3Simple(vel=invelC_s$ID$, phi=phiIn_s$ID$, distance=6, inside=True)\n\
resampleVec3ToMac(source=invelC_s$ID$, target=invel_s$ID$)\n\
- pVel_pp$ID$.setSource(invel_s$ID$, isMAC=True)\n\
- # ensure that pvel has vel as source (important when resuming bake jobs)\n\
+ pVel_pp$ID$.setSource(grid=invel_s$ID$, isMAC=True)\n\
+ # reset pvel grid source before sampling new particles - ensures that new particles are initialized with 0 velocity\n\
else:\n\
- pVel_pp$ID$.setSource(vel_s$ID$, isMAC=True)\n\
+ pVel_pp$ID$.setSource(grid=None, isMAC=False)\n\
\n\
sampleLevelsetWithParticles(phi=phiIn_s$ID$, flags=flags_s$ID$, parts=pp_s$ID$, discretization=particleNumber_s$ID$, randomness=randomness_s$ID$)\n\
flags_s$ID$.updateFromLevelset(phi_s$ID$)\n\
@@ -257,7 +257,7 @@ def liquid_step_$ID$():\n\
extrapolateLsSimple(phi=phi_s$ID$, distance=3)\n\
phi_s$ID$.setBoundNeumann(0) # make sure no particles are placed at outer boundary\n\
\n\
- if doOpen_s$ID$ or using_outflow_s$ID$:\n\
+ if not domainClosed_s$ID$ or using_outflow_s$ID$:\n\
resetOutflow(flags=flags_s$ID$, phi=phi_s$ID$, parts=pp_s$ID$, index=gpi_s$ID$, indexSys=pindex_s$ID$)\n\
flags_s$ID$.updateFromLevelset(phi_s$ID$)\n\
\n\
@@ -298,10 +298,10 @@ def liquid_step_$ID$():\n\
\n\
if using_guiding_s$ID$:\n\
mantaMsg('Guiding and pressure')\n\
- PD_fluid_guiding(vel=vel_s$ID$, velT=velT_s$ID$, flags=flags_s$ID$, phi=phi_s$ID$, curv=curvature_s$ID$, surfTens=surfaceTension_s$ID$, fractions=fractions_s$ID$, weight=weightGuide_s$ID$, blurRadius=beta_sg$ID$, pressure=pressure_s$ID$, tau=tau_sg$ID$, sigma=sigma_sg$ID$, theta=theta_sg$ID$, zeroPressureFixing=not doOpen_s$ID$)\n\
+ PD_fluid_guiding(vel=vel_s$ID$, velT=velT_s$ID$, flags=flags_s$ID$, phi=phi_s$ID$, curv=curvature_s$ID$, surfTens=surfaceTension_s$ID$, fractions=fractions_s$ID$, weight=weightGuide_s$ID$, blurRadius=beta_sg$ID$, pressure=pressure_s$ID$, tau=tau_sg$ID$, sigma=sigma_sg$ID$, theta=theta_sg$ID$, zeroPressureFixing=domainClosed_s$ID$)\n\
else:\n\
mantaMsg('Pressure')\n\
- solvePressure(flags=flags_s$ID$, vel=vel_s$ID$, pressure=pressure_s$ID$, phi=phi_s$ID$, curv=curvature_s$ID$, surfTens=surfaceTension_s$ID$, fractions=fractions_s$ID$, obvel=obvel_s$ID$ if using_fractions_s$ID$ else None)\n\
+ solvePressure(flags=flags_s$ID$, vel=vel_s$ID$, pressure=pressure_s$ID$, phi=phi_s$ID$, curv=curvature_s$ID$, surfTens=surfaceTension_s$ID$, fractions=fractions_s$ID$, obvel=obvel_s$ID$ if using_fractions_s$ID$ else None, zeroPressureFixing=domainClosed_s$ID$)\n\
\n\
extrapolateMACSimple(flags=flags_s$ID$, vel=vel_s$ID$, distance=4, intoObs=True if using_fractions_s$ID$ else False)\n\
setWallBcs(flags=flags_s$ID$, vel=vel_s$ID$, obvel=None if using_fractions_s$ID$ else obvel_s$ID$, phiObs=phiObs_s$ID$, fractions=fractions_s$ID$)\n\
@@ -310,7 +310,7 @@ def liquid_step_$ID$():\n\
extrapolateMACSimple(flags=flags_s$ID$, vel=vel_s$ID$)\n\
\n\
# set source grids for resampling, used in adjustNumber!\n\
- pVel_pp$ID$.setSource(vel_s$ID$, isMAC=True)\n\
+ pVel_pp$ID$.setSource(grid=vel_s$ID$, isMAC=True)\n\
adjustNumber(parts=pp_s$ID$, vel=vel_s$ID$, flags=flags_s$ID$, minParticles=minParticles_s$ID$, maxParticles=maxParticles_s$ID$, phi=phi_s$ID$, exclude=phiObs_s$ID$, radiusFactor=radiusFactor_s$ID$, narrowBand=adjustedNarrowBandWidth_s$ID$)\n\
flipVelocityUpdate(vel=vel_s$ID$, velOld=velOld_s$ID$, flags=flags_s$ID$, parts=pp_s$ID$, partVel=pVel_pp$ID$, flipRatio=flipRatio_s$ID$)\n";
@@ -347,7 +347,7 @@ def liquid_step_mesh_$ID$():\n\
# Vert vel vector needs to pull data from vel grid with correct dim\n\
if using_speedvectors_s$ID$:\n\
interpolateMACGrid(target=vel_sm$ID$, source=vel_s$ID$)\n\
- mVel_mesh$ID$.setSource(vel_sm$ID$, isMAC=True)\n\
+ mVel_mesh$ID$.setSource(grid=vel_sm$ID$, isMAC=True)\n\
\n\
# Set 0.5 boundary at walls + account for extra wall thickness in fractions mode + account for grid scaling:\n\
# E.g. at upres=1 we expect 1 cell border (or 2 with fractions), at upres=2 we expect 2 cell border (or 4 with fractions), etc.\n\
@@ -401,27 +401,29 @@ const std::string liquid_load_data =
"\n\
def liquid_load_data_$ID$(path, framenr, file_format, resumable):\n\
mantaMsg('Liquid load data')\n\
- fluid_file_import_s$ID$(dict=liquid_data_dict_final_s$ID$, path=path, framenr=framenr, file_format=file_format)\n\
- if resumable:\n\
- fluid_file_import_s$ID$(dict=liquid_data_dict_resume_s$ID$, path=path, framenr=framenr, file_format=file_format)\n";
+ dict = { **fluid_data_dict_final_s$ID$, **fluid_data_dict_resume_s$ID$, **liquid_data_dict_final_s$ID$, **liquid_data_dict_resume_s$ID$ } if resumable else { **fluid_data_dict_final_s$ID$, **liquid_data_dict_final_s$ID$ }\n\
+ fluid_file_import_s$ID$(dict=dict, path=path, framenr=framenr, file_format=file_format, file_name=file_data_s$ID$)\n\
+ \n\
+ copyVec3ToReal(source=vel_s$ID$, targetX=x_vel_s$ID$, targetY=y_vel_s$ID$, targetZ=z_vel_s$ID$)\n";
const std::string liquid_load_mesh =
"\n\
def liquid_load_mesh_$ID$(path, framenr, file_format):\n\
mantaMsg('Liquid load mesh')\n\
- fluid_file_import_s$ID$(dict=liquid_mesh_dict_s$ID$, path=path, framenr=framenr, file_format=file_format)\n\
+ dict = liquid_mesh_dict_s$ID$\n\
+ fluid_file_import_s$ID$(dict=dict, path=path, framenr=framenr, file_format=file_format, file_name=file_mesh_s$ID$)\n\
\n\
def liquid_load_meshvel_$ID$(path, framenr, file_format):\n\
mantaMsg('Liquid load meshvel')\n\
- fluid_file_import_s$ID$(dict=liquid_meshvel_dict_s$ID$, path=path, framenr=framenr, file_format=file_format)\n";
+ dict = liquid_meshvel_dict_s$ID$\n\
+ fluid_file_import_s$ID$(dict=dict, path=path, framenr=framenr, file_format=file_format, file_name=file_meshvel_s$ID$)\n";
const std::string liquid_load_particles =
"\n\
def liquid_load_particles_$ID$(path, framenr, file_format, resumable):\n\
mantaMsg('Liquid load particles')\n\
- fluid_file_import_s$ID$(dict=liquid_particles_dict_final_s$ID$, path=path, framenr=framenr, file_format=file_format)\n\
- if resumable:\n\
- fluid_file_import_s$ID$(dict=liquid_particles_dict_resume_s$ID$, path=path, framenr=framenr, file_format=file_format)\n";
+ dict = { **liquid_particles_dict_final_s$ID$, **liquid_particles_dict_resume_s$ID$ } if resumable else { **liquid_particles_dict_final_s$ID$ }\n\
+ fluid_file_import_s$ID$(dict=dict, path=path, framenr=framenr, file_format=file_format, file_name=file_particles_s$ID$)\n";
//////////////////////////////////////////////////////////////////////
// EXPORT
@@ -431,43 +433,39 @@ const std::string liquid_save_data =
"\n\
def liquid_save_data_$ID$(path, framenr, file_format, resumable):\n\
mantaMsg('Liquid save data')\n\
+ dict = { **fluid_data_dict_final_s$ID$, **fluid_data_dict_resume_s$ID$, **liquid_data_dict_final_s$ID$, **liquid_data_dict_resume_s$ID$ } if resumable else { **fluid_data_dict_final_s$ID$, **liquid_data_dict_final_s$ID$ }\n\
if not withMPSave or isWindows:\n\
- fluid_file_export_s$ID$(dict=liquid_data_dict_final_s$ID$, path=path, framenr=framenr, file_format=file_format)\n\
- if resumable:\n\
- fluid_file_export_s$ID$(dict=liquid_data_dict_resume_s$ID$, path=path, framenr=framenr, file_format=file_format)\n\
+ fluid_file_export_s$ID$(dict=dict, path=path, framenr=framenr, file_format=file_format, file_name=file_data_s$ID$)\n\
else:\n\
- fluid_cache_multiprocessing_start_$ID$(function=fluid_file_export_s$ID$, framenr=framenr, format_data=file_format, path_data=path, dict=liquid_data_dict_final_s$ID$, do_join=False)\n\
- if resumable:\n\
- fluid_cache_multiprocessing_start_$ID$(function=fluid_file_export_s$ID$, framenr=framenr, format_data=file_format, path_data=path, dict=liquid_data_dict_resume_s$ID$, do_join=False)\n";
+ fluid_cache_multiprocessing_start_$ID$(function=fluid_file_export_s$ID$, file_name=file_data_s$ID$, framenr=framenr, format_data=file_format, path_data=path, dict=dict, do_join=False)\n";
const std::string liquid_save_mesh =
"\n\
def liquid_save_mesh_$ID$(path, framenr, file_format):\n\
mantaMsg('Liquid save mesh')\n\
+ dict = liquid_mesh_dict_s$ID$\n\
if not withMPSave or isWindows:\n\
- fluid_file_export_s$ID$(dict=liquid_mesh_dict_s$ID$, path=path, framenr=framenr, file_format=file_format)\n\
+ fluid_file_export_s$ID$(dict=dict, path=path, framenr=framenr, file_format=file_format, file_name=file_mesh_s$ID$)\n\
else:\n\
- fluid_cache_multiprocessing_start_$ID$(function=fluid_file_export_s$ID$, framenr=framenr, format_data=file_format, path_data=path, dict=liquid_mesh_dict_s$ID$, do_join=False)\n\
+ fluid_cache_multiprocessing_start_$ID$(function=fluid_file_export_s$ID$, file_name=file_mesh_s$ID$, framenr=framenr, format_data=file_format, path_data=path, dict=dict, do_join=False)\n\
\n\
def liquid_save_meshvel_$ID$(path, framenr, file_format):\n\
mantaMsg('Liquid save mesh vel')\n\
+ dict = liquid_meshvel_dict_s$ID$\n\
if not withMPSave or isWindows:\n\
- fluid_file_export_s$ID$(dict=liquid_meshvel_dict_s$ID$, path=path, framenr=framenr, file_format=file_format)\n\
+ fluid_file_export_s$ID$(dict=dict, path=path, framenr=framenr, file_format=file_format)\n\
else:\n\
- fluid_cache_multiprocessing_start_$ID$(function=fluid_file_export_s$ID$, framenr=framenr, format_data=file_format, path_data=path, dict=liquid_meshvel_dict_s$ID$, do_join=False)\n";
+ fluid_cache_multiprocessing_start_$ID$(function=fluid_file_export_s$ID$, framenr=framenr, format_data=file_format, path_data=path, dict=dict, do_join=False)\n";
const std::string liquid_save_particles =
"\n\
def liquid_save_particles_$ID$(path, framenr, file_format, resumable):\n\
mantaMsg('Liquid save particles')\n\
+ dict = { **liquid_particles_dict_final_s$ID$, **liquid_particles_dict_resume_s$ID$ } if resumable else { **liquid_particles_dict_final_s$ID$ }\n\
if not withMPSave or isWindows:\n\
- fluid_file_export_s$ID$(dict=liquid_particles_dict_final_s$ID$, path=path, framenr=framenr, file_format=file_format)\n\
- if resumable:\n\
- fluid_file_export_s$ID$(dict=liquid_particles_dict_resume_s$ID$, path=path, framenr=framenr, file_format=file_format)\n\
+ fluid_file_export_s$ID$(dict=dict, path=path, framenr=framenr, file_format=file_format, file_name=file_particles_s$ID$)\n\
else:\n\
- fluid_cache_multiprocessing_start_$ID$(function=fluid_file_export_s$ID$, framenr=framenr, format_data=file_format, path_data=path, dict=liquid_particles_dict_final_s$ID$, do_join=False)\n\
- if resumable:\n\
- fluid_cache_multiprocessing_start_$ID$(function=fluid_file_export_s$ID$, framenr=framenr, format_data=file_format, path_data=path, dict=liquid_particles_dict_resume_s$ID$, do_join=False)\n";
+ fluid_cache_multiprocessing_start_$ID$(function=fluid_file_export_s$ID$, file_name=file_particles_s$ID$, framenr=framenr, format_data=file_format, path_data=path, dict=dict, do_join=False)\n";
//////////////////////////////////////////////////////////////////////
// STANDALONE MODE
@@ -477,7 +475,6 @@ const std::string liquid_standalone =
"\n\
# Helper function to call cache load functions\n\
def load(frame, cache_resumable):\n\
- fluid_load_data_$ID$(os.path.join(cache_dir, 'data'), frame, file_format_data, cache_resumable)\n\
liquid_load_data_$ID$(os.path.join(cache_dir, 'data'), frame, file_format_data, cache_resumable)\n\
if using_sndparts_s$ID$:\n\
liquid_load_particles_$ID$(os.path.join(cache_dir, 'particles'), frame, file_format_particles, cache_resumable)\n\
diff --git a/intern/mantaflow/intern/strings/smoke_script.h b/intern/mantaflow/intern/strings/smoke_script.h
index a592ad8644a..612d01b85ef 100644
--- a/intern/mantaflow/intern/strings/smoke_script.h
+++ b/intern/mantaflow/intern/strings/smoke_script.h
@@ -101,8 +101,8 @@ color_g_in_s$ID$ = None\n\
color_b_in_s$ID$ = None\n\
\n\
# Keep track of important objects in dict to load them later on\n\
-smoke_data_dict_final_s$ID$ = dict(density=density_s$ID$, shadow=shadow_s$ID$)\n\
-smoke_data_dict_resume_s$ID$ = dict(densityIn=densityIn_s$ID$, emission=emission_s$ID$)\n";
+smoke_data_dict_final_s$ID$ = { 'density' : density_s$ID$, 'shadow' : shadow_s$ID$ }\n\
+smoke_data_dict_resume_s$ID$ = { 'densityIn' : densityIn_s$ID$, 'emission' : emission_s$ID$ }\n";
const std::string smoke_alloc_noise =
"\n\
@@ -213,8 +213,8 @@ if 'heat_s$ID$' in globals(): del heat_s$ID$\n\
if 'heatIn_s$ID$' in globals(): del heatIn_s$ID$\n\
\n\
mantaMsg('Allocating heat')\n\
-heat_s$ID$ = s$ID$.create(RealGrid, name='$NAME_HEAT$')\n\
-heatIn_s$ID$ = s$ID$.create(RealGrid, name='$NAME_HEATIN$')\n\
+heat_s$ID$ = s$ID$.create(RealGrid, name='$NAME_TEMPERATURE$')\n\
+heatIn_s$ID$ = s$ID$.create(RealGrid, name='$NAME_TEMPERATUREIN$')\n\
\n\
# Add objects to dict to load them later on\n\
if 'smoke_data_dict_final_s$ID$' in globals():\n\
@@ -365,7 +365,7 @@ def smoke_step_$ID$():\n\
mantaMsg('Advecting velocity')\n\
advectSemiLagrange(flags=flags_s$ID$, vel=vel_s$ID$, grid=vel_s$ID$, order=2)\n\
\n\
- if doOpen_s$ID$ or using_outflow_s$ID$:\n\
+ if not domainClosed_s$ID$ or using_outflow_s$ID$:\n\
resetOutflow(flags=flags_s$ID$, real=density_s$ID$)\n\
\n\
mantaMsg('Vorticity')\n\
@@ -406,10 +406,10 @@ def smoke_step_$ID$():\n\
mantaMsg('Using preconditioner: ' + str(preconditioner_s$ID$))\n\
if using_guiding_s$ID$:\n\
mantaMsg('Guiding and pressure')\n\
- PD_fluid_guiding(vel=vel_s$ID$, velT=velT_s$ID$, flags=flags_s$ID$, weight=weightGuide_s$ID$, blurRadius=beta_sg$ID$, pressure=pressure_s$ID$, tau=tau_sg$ID$, sigma=sigma_sg$ID$, theta=theta_sg$ID$, preconditioner=preconditioner_s$ID$, zeroPressureFixing=not doOpen_s$ID$)\n\
+ PD_fluid_guiding(vel=vel_s$ID$, velT=velT_s$ID$, flags=flags_s$ID$, weight=weightGuide_s$ID$, blurRadius=beta_sg$ID$, pressure=pressure_s$ID$, tau=tau_sg$ID$, sigma=sigma_sg$ID$, theta=theta_sg$ID$, preconditioner=preconditioner_s$ID$, zeroPressureFixing=domainClosed_s$ID$)\n\
else:\n\
mantaMsg('Pressure')\n\
- solvePressure(flags=flags_s$ID$, vel=vel_s$ID$, pressure=pressure_s$ID$, preconditioner=preconditioner_s$ID$, zeroPressureFixing=not doOpen_s$ID$) # closed domains require pressure fixing\n\
+ solvePressure(flags=flags_s$ID$, vel=vel_s$ID$, pressure=pressure_s$ID$, preconditioner=preconditioner_s$ID$, zeroPressureFixing=domainClosed_s$ID$) # closed domains require pressure fixing\n\
\n\
def process_burn_$ID$():\n\
mantaMsg('Process burn')\n\
@@ -542,19 +542,19 @@ const std::string smoke_load_data =
"\n\
def smoke_load_data_$ID$(path, framenr, file_format, resumable):\n\
mantaMsg('Smoke load data')\n\
- fluid_file_import_s$ID$(dict=smoke_data_dict_final_s$ID$, path=path, framenr=framenr, file_format=file_format)\n\
- if resumable:\n\
- fluid_file_import_s$ID$(dict=smoke_data_dict_resume_s$ID$, path=path, framenr=framenr, file_format=file_format)\n";
+ dict = { **fluid_data_dict_final_s$ID$, **fluid_data_dict_resume_s$ID$, **smoke_data_dict_final_s$ID$, **smoke_data_dict_resume_s$ID$ } if resumable else { **fluid_data_dict_final_s$ID$, **smoke_data_dict_final_s$ID$ }\n\
+ fluid_file_import_s$ID$(dict=dict, path=path, framenr=framenr, file_format=file_format, file_name=file_data_s$ID$)\n\
+ \n\
+ copyVec3ToReal(source=vel_s$ID$, targetX=x_vel_s$ID$, targetY=y_vel_s$ID$, targetZ=z_vel_s$ID$)\n";
const std::string smoke_load_noise =
"\n\
def smoke_load_noise_$ID$(path, framenr, file_format, resumable):\n\
mantaMsg('Smoke load noise')\n\
- fluid_file_import_s$ID$(dict=smoke_noise_dict_final_s$ID$, path=path, framenr=framenr, file_format=file_format)\n\
+ dict = { **smoke_noise_dict_final_s$ID$, **smoke_noise_dict_resume_s$ID$ } if resumable else { **smoke_noise_dict_final_s$ID$ } \n\
+ fluid_file_import_s$ID$(dict=dict, path=path, framenr=framenr, file_format=file_format, file_name=file_noise_s$ID$)\n\
\n\
if resumable:\n\
- fluid_file_import_s$ID$(dict=smoke_noise_dict_resume_s$ID$, path=path, framenr=framenr, file_format=file_format)\n\
- \n\
# Fill up xyz texture grids, important when resuming a bake\n\
copyVec3ToReal(source=uvGrid0_s$ID$, targetX=texture_u_s$ID$, targetY=texture_v_s$ID$, targetZ=texture_w_s$ID$)\n\
copyVec3ToReal(source=uvGrid1_s$ID$, targetX=texture_u2_s$ID$, targetY=texture_v2_s$ID$, targetZ=texture_w2_s$ID$)\n";
@@ -568,28 +568,22 @@ const std::string smoke_save_data =
def smoke_save_data_$ID$(path, framenr, file_format, resumable):\n\
mantaMsg('Smoke save data')\n\
start_time = time.time()\n\
+ dict = { **fluid_data_dict_final_s$ID$, **fluid_data_dict_resume_s$ID$, **smoke_data_dict_final_s$ID$, **smoke_data_dict_resume_s$ID$ } if resumable else { **fluid_data_dict_final_s$ID$, **smoke_data_dict_final_s$ID$ } \n\
if not withMPSave or isWindows:\n\
- fluid_file_export_s$ID$(framenr=framenr, file_format=file_format, path=path, dict=smoke_data_dict_final_s$ID$,)\n\
- if resumable:\n\
- fluid_file_export_s$ID$(framenr=framenr, file_format=file_format, path=path, dict=smoke_data_dict_resume_s$ID$,)\n\
+ fluid_file_export_s$ID$(dict=dict, path=path, framenr=framenr, file_format=file_format, file_name=file_data_s$ID$)\n\
else:\n\
- fluid_cache_multiprocessing_start_$ID$(function=fluid_file_export_s$ID$, framenr=framenr, format_data=file_format, path_data=path, dict=smoke_data_dict_final_s$ID$, do_join=False)\n\
- if resumable:\n\
- fluid_cache_multiprocessing_start_$ID$(function=fluid_file_export_s$ID$, framenr=framenr, format_data=file_format, path_data=path, dict=smoke_data_dict_resume_s$ID$, do_join=False)\n\
+ fluid_cache_multiprocessing_start_$ID$(function=fluid_file_export_s$ID$, file_name=file_data_s$ID$, framenr=framenr, format_data=file_format, path_data=path, dict=dict, do_join=False)\n\
mantaMsg('--- Save: %s seconds ---' % (time.time() - start_time))\n";
const std::string smoke_save_noise =
"\n\
def smoke_save_noise_$ID$(path, framenr, file_format, resumable):\n\
mantaMsg('Smoke save noise')\n\
+ dict = { **smoke_noise_dict_final_s$ID$, **smoke_noise_dict_resume_s$ID$ } if resumable else { **smoke_noise_dict_final_s$ID$ } \n\
if not withMPSave or isWindows:\n\
- fluid_file_export_s$ID$(dict=smoke_noise_dict_final_s$ID$, framenr=framenr, file_format=file_format, path=path)\n\
- if resumable:\n\
- fluid_file_export_s$ID$(dict=smoke_noise_dict_resume_s$ID$, framenr=framenr, file_format=file_format, path=path)\n\
+ fluid_file_export_s$ID$(dict=dict, framenr=framenr, file_format=file_format, path=path, file_name=file_noise_s$ID$)\n\
else:\n\
- fluid_cache_multiprocessing_start_$ID$(function=fluid_file_export_s$ID$, framenr=framenr, format_data=file_format, path_data=path, dict=smoke_noise_dict_final_s$ID$, do_join=False)\n\
- if resumable:\n\
- fluid_cache_multiprocessing_start_$ID$(function=fluid_file_export_s$ID$, framenr=framenr, format_data=file_format, path_data=path, dict=smoke_noise_dict_resume_s$ID$, do_join=False)\n";
+ fluid_cache_multiprocessing_start_$ID$(function=fluid_file_export_s$ID$, file_name=file_noise_s$ID$, framenr=framenr, format_data=file_format, path_data=path, dict=dict, do_join=False)\n";
//////////////////////////////////////////////////////////////////////
// STANDALONE MODE
@@ -599,7 +593,6 @@ const std::string smoke_standalone =
"\n\
# Helper function to call cache load functions\n\
def load(frame, cache_resumable):\n\
- fluid_load_data_$ID$(os.path.join(cache_dir, 'data'), frame, file_format_data, cache_resumable)\n\
smoke_load_data_$ID$(os.path.join(cache_dir, 'data'), frame, file_format_data, cache_resumable)\n\
if using_noise_s$ID$:\n\
smoke_load_noise_$ID$(os.path.join(cache_dir, 'noise'), frame, file_format_noise, cache_resumable)\n\
diff --git a/intern/quadriflow/quadriflow_capi.cpp b/intern/quadriflow/quadriflow_capi.cpp
index 302c7a0ae30..53237289874 100644
--- a/intern/quadriflow/quadriflow_capi.cpp
+++ b/intern/quadriflow/quadriflow_capi.cpp
@@ -49,7 +49,7 @@ struct ObjVertex {
}
};
-struct ObjVertexHash : std::unary_function<ObjVertex, size_t> {
+struct ObjVertexHash {
std::size_t operator()(const ObjVertex &v) const
{
size_t hash = std::hash<uint32_t>()(v.p);
diff --git a/intern/rigidbody/RBI_api.h b/intern/rigidbody/RBI_api.h
index 9546b840419..d46cb5a7eed 100644
--- a/intern/rigidbody/RBI_api.h
+++ b/intern/rigidbody/RBI_api.h
@@ -175,7 +175,7 @@ void RB_body_set_linear_velocity(rbRigidBody *body, const float v_in[3]);
void RB_body_get_angular_velocity(rbRigidBody *body, float v_out[3]);
void RB_body_set_angular_velocity(rbRigidBody *body, const float v_in[3]);
-/* Linear/Angular Factor, used to lock translation/roation axes */
+/* Linear/Angular Factor, used to lock translation/rotation axes */
void RB_body_set_linear_factor(rbRigidBody *object, float x, float y, float z);
void RB_body_set_angular_factor(rbRigidBody *object, float x, float y, float z);
diff --git a/release/datafiles/blender_icons_geom_update.py b/release/datafiles/blender_icons_geom_update.py
index 3b7724756db..5b95961ae6b 100755
--- a/release/datafiles/blender_icons_geom_update.py
+++ b/release/datafiles/blender_icons_geom_update.py
@@ -21,10 +21,10 @@ def edit_text_file(filename, marker_begin, marker_end, content):
while data[marker_end_index - 1] in {'\t', ' '}:
marker_end_index -= 1
if marker_begin_index == -1:
- print('Error: {!r} not found'.format(marker_begin))
+ print('Error: %r not found' % marker_begin)
return
if marker_end_index == -1:
- print('Error: {!r} not found'.format(marker_end))
+ print('Error: %r not found' % marker_end)
return
marker_begin_index += len(marker_begin) + 1
data_update = data[:marker_begin_index] + content + data[marker_end_index:]
diff --git a/release/datafiles/icons/ops.mesh.extrude_manifold.dat b/release/datafiles/icons/ops.mesh.extrude_manifold.dat
new file mode 100644
index 00000000000..c1cc5b2d723
--- /dev/null
+++ b/release/datafiles/icons/ops.mesh.extrude_manifold.dat
Binary files differ
diff --git a/release/datafiles/locale b/release/datafiles/locale
-Subproject 72e5040232a544b293dca05dac5707bd4e4bffa
+Subproject caf68fed42f55e606b14c7105f5df694957ce03
diff --git a/release/scripts/addons b/release/scripts/addons
-Subproject 8397b98b8cb6510869d28a84592b8d7213c3b6c
+Subproject bc1262f4d61feeba235bb75046e65e0e8411241
diff --git a/release/scripts/addons_contrib b/release/scripts/addons_contrib
-Subproject 7c36b48507f79ca62f8c038bad0fb3468c4f48e
+Subproject 45aa940dabda64f7877c6d5dd843998a86f0a83
diff --git a/release/scripts/modules/addon_utils.py b/release/scripts/modules/addon_utils.py
index a65ff15393a..8a074d23db9 100644
--- a/release/scripts/modules/addon_utils.py
+++ b/release/scripts/modules/addon_utils.py
@@ -172,8 +172,8 @@ def modules_refresh(module_cache=addons_fake_modules):
if mod.__file__ != mod_path:
print(
"multiple addons with the same name:\n"
- " " f"{mod.__file__!r}" "\n"
- " " f"{mod_path!r}"
+ " %r\n"
+ " %r" % (mod.__file__, mod_path)
)
error_duplicates.append((mod.bl_info["name"], mod.__file__, mod_path))
@@ -241,7 +241,7 @@ def check(module_name):
if loaded_state is Ellipsis:
print(
- "Warning: addon-module " f"{module_name:s}" " found module "
+ "Warning: addon-module", module_name, "found module "
"but without '__addon_enabled__' field, "
"possible name collision from file:",
repr(getattr(mod, "__file__", "<unknown>")),
@@ -439,8 +439,9 @@ def disable(module_name, *, default_set=False, handle_error=None):
handle_error(ex)
else:
print(
- "addon_utils.disable: " f"{module_name:s}" " not",
- ("disabled" if mod is None else "loaded")
+ "addon_utils.disable: %s not %s" % (
+ module_name,
+ "disabled" if mod is None else "loaded")
)
# could be in more than once, unlikely but better do this just in case.
@@ -502,7 +503,7 @@ def _blender_manual_url_prefix():
else:
manual_version = "dev"
- return f"https://docs.blender.org/manual/en/{manual_version}"
+ return "https://docs.blender.org/manual/en/" + manual_version
def module_bl_info(mod, info_basis=None):
@@ -544,11 +545,11 @@ def module_bl_info(mod, info_basis=None):
addon_info["doc_url"] = doc_url
if _bpy.app.debug:
print(
- "Warning: add-on \"{addon_name}\": 'wiki_url' in 'bl_info' "
+ "Warning: add-on \"%s\": 'wiki_url' in 'bl_info' "
"is deprecated please use 'doc_url' instead!\n"
- " {addon_path}".format(
- addon_name=addon_info['name'],
- addon_path=getattr(mod, "__file__", None),
+ " %s" % (
+ addon_info['name'],
+ getattr(mod, "__file__", None),
)
)
diff --git a/release/scripts/modules/bl_previews_utils/bl_previews_render.py b/release/scripts/modules/bl_previews_utils/bl_previews_render.py
index b79c0b744d0..73004cee731 100644
--- a/release/scripts/modules/bl_previews_utils/bl_previews_render.py
+++ b/release/scripts/modules/bl_previews_utils/bl_previews_render.py
@@ -505,7 +505,7 @@ def main():
if __name__ == "__main__":
- print("\n\n *** Running {} *** \n".format(__file__))
- print(" *** Blend file {} *** \n".format(bpy.data.filepath))
+ print("\n\n *** Running %s *** \n" % __file__)
+ print(" *** Blend file %s *** \n" % bpy.data.filepath)
main()
bpy.ops.wm.quit_blender()
diff --git a/release/scripts/modules/bl_ui_utils/bug_report_url.py b/release/scripts/modules/bl_ui_utils/bug_report_url.py
index 2adee70bc86..5676e0d6815 100644
--- a/release/scripts/modules/bl_ui_utils/bug_report_url.py
+++ b/release/scripts/modules/bl_ui_utils/bug_report_url.py
@@ -31,13 +31,13 @@ def url_prefill_from_blender(addon_info=None):
fh.write("**System Information**\n")
fh.write(
- "Operating system: {!s} {!s} Bits\n".format(
+ "Operating system: %s %d Bits\n" % (
platform.platform(),
struct.calcsize("P") * 8,
)
)
fh.write(
- "Graphics card: {!s} {!s} {!s}\n".format(
+ "Graphics card: %s %s %s\n" % (
bgl.glGetString(bgl.GL_RENDERER),
bgl.glGetString(bgl.GL_VENDOR),
bgl.glGetString(bgl.GL_VERSION),
@@ -48,7 +48,7 @@ def url_prefill_from_blender(addon_info=None):
"**Blender Version**\n"
)
fh.write(
- "Broken: version: {!s}, branch: {!s}, commit date: {!s} {!s}, hash: `rB{!s}`\n".format(
+ "Broken: version: %s, branch: %s, commit date: %s %s, hash: `rB%s`\n" % (
bpy.app.version_string,
bpy.app.build_branch.decode('utf-8', 'replace'),
bpy.app.build_commit_date.decode('utf-8', 'replace'),
diff --git a/release/scripts/modules/bpy/ops.py b/release/scripts/modules/bpy/ops.py
index 8f8f42bcd46..4e226f80f79 100644
--- a/release/scripts/modules/bpy/ops.py
+++ b/release/scripts/modules/bpy/ops.py
@@ -123,7 +123,7 @@ class BPyOpsSubModOp:
# op_class = getattr(bpy.types, idname)
op_class = op_get_rna_type(idname)
descr = op_class.description
- return f"{sig}\n{descr}"
+ return "%s\n%s" % (sig, descr)
@staticmethod
def _parse_args(args):
diff --git a/release/scripts/modules/bpy/utils/previews.py b/release/scripts/modules/bpy/utils/previews.py
index bfdf28e0db4..7f337677635 100644
--- a/release/scripts/modules/bpy/utils/previews.py
+++ b/release/scripts/modules/bpy/utils/previews.py
@@ -76,8 +76,7 @@ class ImagePreviewCollection(dict):
return
raise ResourceWarning(
- f"{self!r}: left open, remove with "
- "'bpy.utils.previews.remove()'"
+ "%r: left open, remove with 'bpy.utils.previews.remove()'" % self
)
self.close()
@@ -116,7 +115,9 @@ class ImagePreviewCollection(dict):
super().__delitem__(key)
def __repr__(self):
- return f"<{self.__class__.__name__:s} id={self._uuid:s}[{len(self):d}], {super()!r}>"
+ return "<%s id=%s[%d], %r>" % (
+ self.__class__.__name__, self._uuid, len(self), super()
+ )
def new():
diff --git a/release/scripts/modules/rna_keymap_ui.py b/release/scripts/modules/rna_keymap_ui.py
index 844daded570..6076bf00063 100644
--- a/release/scripts/modules/rna_keymap_ui.py
+++ b/release/scripts/modules/rna_keymap_ui.py
@@ -240,6 +240,9 @@ def draw_filtered(display_keymaps, filter_type, filter_text, layout):
"*": 'NUMPAD_ASTERIX',
"/": 'NUMPAD_SLASH',
'+': 'NUMPAD_PLUS',
+ "-": 'NUMPAD_MINUS',
+ ".": 'NUMPAD_PERIOD',
+ "'": 'QUOTE',
"RMB": 'RIGHTMOUSE',
"LMB": 'LEFTMOUSE',
"MMB": 'MIDDLEMOUSE',
diff --git a/release/scripts/modules/rna_prop_ui.py b/release/scripts/modules/rna_prop_ui.py
index 8dda8c90f85..662c1d908fc 100644
--- a/release/scripts/modules/rna_prop_ui.py
+++ b/release/scripts/modules/rna_prop_ui.py
@@ -130,7 +130,7 @@ def rna_idprop_ui_prop_default_set(item, prop, value):
try:
prop_type, is_array = rna_idprop_value_item_type(item[prop])
- if prop_type in {int, float}:
+ if prop_type in {int, float, str}:
if is_array and isinstance(value, ARRAY_TYPES):
value = [prop_type(item) for item in value]
if any(value):
diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
index bbbe520441c..57b31175728 100644
--- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py
+++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
@@ -695,9 +695,12 @@ def km_user_interface(_params):
("ui.copy_data_path_button", {"type": 'C', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True},
{"properties": [("full_path", True)]}),
# Keyframes and drivers
- ("anim.keyframe_insert_button", {"type": 'I', "value": 'PRESS'}, None),
- ("anim.keyframe_delete_button", {"type": 'I', "value": 'PRESS', "alt": True}, None),
- ("anim.keyframe_clear_button", {"type": 'I', "value": 'PRESS', "shift": True, "alt": True}, None),
+ ("anim.keyframe_insert_button", {"type": 'I', "value": 'PRESS'},
+ {"properties": [("all", True)]}),
+ ("anim.keyframe_delete_button", {"type": 'I', "value": 'PRESS', "alt": True},
+ {"properties": [("all", True)]}),
+ ("anim.keyframe_clear_button", {"type": 'I', "value": 'PRESS', "shift": True, "alt": True},
+ {"properties": [("all", True)]}),
("anim.driver_button_add", {"type": 'D', "value": 'PRESS', "ctrl": True}, None),
("anim.driver_button_remove", {"type": 'D', "value": 'PRESS', "ctrl": True, "alt": True}, None),
("anim.keyingset_button_add", {"type": 'K', "value": 'PRESS'}, None),
@@ -725,6 +728,16 @@ def km_property_editor(_params):
{"properties": [("direction", 'PREV'), ], },),
("screen.space_context_cycle", {"type": 'WHEELDOWNMOUSE', "value": 'PRESS', "ctrl": True},
{"properties": [("direction", 'NEXT'), ], },),
+ # Modifier panels
+ ("object.modifier_remove", {"type": 'X', "value": 'PRESS'}, {"properties": [("report", True)]}),
+ ("object.modifier_remove", {"type": 'DEL', "value": 'PRESS'}, {"properties": [("report", True)]}),
+ ("object.modifier_copy", {"type": 'D', "value": 'PRESS', "shift": True}, None),
+ ("object.modifier_apply", {"type": 'A', "value": 'PRESS', "ctrl": True}, {"properties": [("report", True)]}),
+ # Grease pencil modifier panels
+ ("object.gpencil_modifier_remove", {"type": 'X', "value": 'PRESS'}, {"properties": [("report", True)]}),
+ ("object.gpencil_modifier_remove", {"type": 'DEL', "value": 'PRESS'}, {"properties": [("report", True)]}),
+ ("object.gpencil_modifier_copy", {"type": 'D', "value": 'PRESS', "shift": True}, None),
+ ("object.gpencil_modifier_apply", {"type": 'A', "value": 'PRESS', "ctrl": True}, {"properties": [("report", True)]}),
])
return keymap
@@ -1893,7 +1906,7 @@ def km_file_browser_main(params):
{"properties": [("open", False), ("deselect_all", not params.legacy)]}),
("file.select", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'}, None),
("file.select", {"type": 'LEFTMOUSE', "value": 'CLICK', "ctrl": True},
- {"properties": [("extend", True)]}),
+ {"properties": [("extend", True), ("open", False)]}),
("file.select", {"type": 'LEFTMOUSE', "value": 'CLICK', "shift": True},
{"properties": [("extend", True), ("fill", True), ("open", False)]}),
("file.select_walk", {"type": 'UP_ARROW', "value": 'PRESS'},
@@ -2464,7 +2477,7 @@ def km_sequencer(params):
{"properties": [("all", False)]}),
("sequencer.gap_remove", {"type": 'BACK_SPACE', "value": 'PRESS', "shift": True},
{"properties": [("all", True)]}),
- ("sequencer.gap_insert", {"type": 'BACK_SPACE', "value": 'PRESS', "ctrl": True}, None),
+ ("sequencer.gap_insert", {"type": 'EQUAL', "value": 'PRESS', "shift": True}, None),
("sequencer.snap", {"type": 'S', "value": 'PRESS', "shift": True}, None),
("sequencer.swap_inputs", {"type": 'S', "value": 'PRESS', "alt": True}, None),
*(
@@ -2520,14 +2533,6 @@ def km_sequencer(params):
{"properties": [("side", 'LEFT')]}),
("sequencer.select_side_of_frame", {"type": 'RIGHT_BRACKET', "value": 'PRESS'},
{"properties": [("side", 'RIGHT')]}),
- ("sequencer.select_side_of_frame", {"type": 'EQUAL', "value": 'PRESS'},
- {"properties": [("side", 'OVERLAP')]}),
- ("sequencer.select_side_of_frame", {"type": 'LEFT_BRACKET', "value": 'PRESS', "shift": True},
- {"properties": [("side", 'LEFT'), ("extend", True)]}),
- ("sequencer.select_side_of_frame", {"type": 'RIGHT_BRACKET', "value": 'PRESS', "shift": True},
- {"properties": [("side", 'RIGHT'), ("extend", True)]}),
- ("sequencer.select_side_of_frame", {"type": 'EQUAL', "value": 'PRESS', "shift": True},
- {"properties": [("side", 'OVERLAP'), ("extend", True)]}),
*_template_items_context_menu("SEQUENCER_MT_context_menu", params.context_menu_event),
])
@@ -4348,6 +4353,8 @@ def km_sculpt(params):
("object.voxel_remesh", {"type": 'R', "value": 'PRESS', "ctrl": True}, None),
("object.voxel_size_edit", {"type": 'R', "value": 'PRESS', "shift": True}, None),
("object.quadriflow_remesh", {"type": 'R', "value": 'PRESS', "ctrl": True, "alt": True}, None),
+ # Color
+ ("sculpt.sample_color", {"type": 'S', "value": 'PRESS'}, None),
# Brush properties
("brush.scale_size", {"type": 'LEFT_BRACKET', "value": 'PRESS'},
{"properties": [("scalar", 0.9)]}),
@@ -4370,7 +4377,7 @@ def km_sculpt(params):
# Tools
("paint.brush_select", {"type": 'X', "value": 'PRESS'},
{"properties": [("sculpt_tool", 'DRAW')]}),
- ("paint.brush_select", {"type": 'S', "value": 'PRESS'},
+ ("paint.brush_select", {"type": 'S', "value": 'PRESS', "shift": True},
{"properties": [("sculpt_tool", 'SMOOTH')]}),
("paint.brush_select", {"type": 'P', "value": 'PRESS'},
{"properties": [("sculpt_tool", 'PINCH')]}),
@@ -5184,7 +5191,7 @@ def km_bevel_modal_map(_params):
("MARK_SHARP_TOGGLE", {"type": 'K', "value": 'PRESS', "any": True}, None),
("OUTER_MITER_CHANGE", {"type": 'O', "value": 'PRESS', "any": True}, None),
("INNER_MITER_CHANGE", {"type": 'I', "value": 'PRESS', "any": True}, None),
- ("CUSTOM_PROFILE_TOGGLE", {"type": 'Z', "value": 'PRESS', "any": True}, None),
+ ("PROFILE_TYPE_CHANGE", {"type": 'Z', "value": 'PRESS', "any": True}, None),
("VERTEX_MESH_CHANGE", {"type": 'N', "value": 'PRESS', "any": True}, None),
])
@@ -5914,12 +5921,12 @@ def km_3d_view_tool_edit_mesh_extrude_region(params):
]},
)
-def km_3d_view_tool_edit_mesh_extrude_dissolve_and_intersect(params):
+def km_3d_view_tool_edit_mesh_extrude_manifold(params):
return (
- "3D View Tool: Edit Mesh, Extrude Dissolve and Intersect",
+ "3D View Tool: Edit Mesh, Extrude Manifold",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("mesh.extrude_region_dissolve_move_intersect", {"type": params.tool_tweak, "value": 'ANY'},
+ ("mesh.extrude_manifold", {"type": params.tool_tweak, "value": 'ANY'},
{"properties": [
("MESH_OT_extrude_region", [("use_dissolve_ortho_edges", True)]),
("TRANSFORM_OT_translate", [
@@ -6215,9 +6222,9 @@ def km_3d_view_tool_edit_curve_extrude(params):
)
-def km_3d_view_tool_edit_curve_extrude_cursor(params):
+def km_3d_view_tool_edit_curve_extrude_to_cursor(params):
return (
- "3D View Tool: Edit Curve, Extrude Cursor",
+ "3D View Tool: Edit Curve, Extrude to Cursor",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
("curve.vertex_add", {"type": params.tool_mouse, "value": 'PRESS'}, None),
@@ -6285,6 +6292,16 @@ def km_3d_view_tool_sculpt_cloth_filter(params):
]},
)
+def km_3d_view_tool_sculpt_color_filter(params):
+ return (
+ "3D View Tool: Sculpt, Color Filter",
+ {"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
+ {"items": [
+ ("sculpt.color_filter", {"type": params.tool_tweak, "value": 'ANY'},
+ None)
+ ]},
+ )
+
def km_3d_view_tool_paint_weight_sample_weight(params):
return (
"3D View Tool: Paint Weight, Sample Weight",
@@ -6792,7 +6809,7 @@ def generate_keymaps(params=None):
km_3d_view_tool_edit_armature_extrude(params),
km_3d_view_tool_edit_armature_extrude_to_cursor(params),
km_3d_view_tool_edit_mesh_extrude_region(params),
- km_3d_view_tool_edit_mesh_extrude_dissolve_and_intersect(params),
+ km_3d_view_tool_edit_mesh_extrude_manifold(params),
km_3d_view_tool_edit_mesh_extrude_along_normals(params),
km_3d_view_tool_edit_mesh_extrude_individual(params),
km_3d_view_tool_edit_mesh_extrude_to_cursor(params),
@@ -6819,12 +6836,13 @@ def generate_keymaps(params=None):
km_3d_view_tool_edit_curve_tilt(params),
km_3d_view_tool_edit_curve_randomize(params),
km_3d_view_tool_edit_curve_extrude(params),
- km_3d_view_tool_edit_curve_extrude_cursor(params),
+ km_3d_view_tool_edit_curve_extrude_to_cursor(params),
km_3d_view_tool_sculpt_box_hide(params),
km_3d_view_tool_sculpt_box_mask(params),
km_3d_view_tool_sculpt_lasso_mask(params),
km_3d_view_tool_sculpt_mesh_filter(params),
km_3d_view_tool_sculpt_cloth_filter(params),
+ km_3d_view_tool_sculpt_color_filter(params),
km_3d_view_tool_paint_weight_sample_weight(params),
km_3d_view_tool_paint_weight_sample_vertex_group(params),
km_3d_view_tool_paint_weight_gradient(params),
diff --git a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py
index 4cb6cefc960..f275d892abe 100644
--- a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py
+++ b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py
@@ -1246,7 +1246,7 @@ def km_file_browser_main(params):
("file.select", {"type": 'LEFTMOUSE', "value": 'CLICK'},
{"properties": [("open", False), ("deselect_all", True)]}),
("file.select", {"type": 'LEFTMOUSE', "value": 'CLICK', "ctrl": True},
- {"properties": [("extend", True)]}),
+ {"properties": [("extend", True), ("open", False)]}),
("file.select", {"type": 'LEFTMOUSE', "value": 'CLICK', "shift": True,},
{"properties": [("extend", True), ("fill", True), ("open", False)]}),
("file.select", {"type": 'RIGHTMOUSE', "value": 'CLICK', "shift": True},
diff --git a/release/scripts/startup/bl_operators/__init__.py b/release/scripts/startup/bl_operators/__init__.py
index d7df29f1769..c39a7afcff9 100644
--- a/release/scripts/startup/bl_operators/__init__.py
+++ b/release/scripts/startup/bl_operators/__init__.py
@@ -49,6 +49,7 @@ _modules = [
"uvcalc_smart_project",
"vertexpaint_dirt",
"view3d",
+ "gpencil_mesh_bake",
"wm",
]
diff --git a/release/scripts/startup/bl_operators/gpencil_mesh_bake.py b/release/scripts/startup/bl_operators/gpencil_mesh_bake.py
new file mode 100644
index 00000000000..ae75fa0e4d9
--- /dev/null
+++ b/release/scripts/startup/bl_operators/gpencil_mesh_bake.py
@@ -0,0 +1,162 @@
+# ##### 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.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+# <pep8-80 compliant>
+
+import bpy
+from bpy.types import Operator
+from bpy.props import (
+ IntProperty,
+ FloatProperty,
+ BoolProperty,
+ EnumProperty,
+)
+
+gp_object_items = []
+
+
+def my_objlist_callback(scene, context):
+ gp_object_items.clear()
+ gp_object_items.append(('*NEW', "New Object", ""))
+ for o in context.scene.objects:
+ if o.type == 'GPENCIL':
+ gp_object_items.append((o.name, o.name, ""))
+
+ return gp_object_items
+
+
+class GPENCIL_OT_mesh_bake(Operator):
+ """Bake all mesh animation into grease pencil strokes"""
+ bl_idname = "gpencil.mesh_bake"
+ bl_label = "Bake Mesh to Grease Pencil"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ frame_start: IntProperty(
+ name="Start Frame",
+ description="Start frame for baking",
+ min=0, max=300000,
+ default=1,
+ )
+ frame_end: IntProperty(
+ name="End Frame",
+ description="End frame for baking",
+ min=1, max=300000,
+ default=250,
+ )
+ step: IntProperty(
+ name="Frame Step",
+ description="Frame Step",
+ min=1, max=120,
+ default=1,
+ )
+ thickness: IntProperty(
+ name="Thickness",
+ description="Thickness of the stroke lines",
+ min=1, max=100,
+ default=1,
+ )
+ angle: FloatProperty(
+ name="Threshold Angle",
+ description="Threshold to determine ends of the strokes",
+ min=0,
+ max=+3.141592,
+ default=+1.22173, # 70 Degress
+ subtype='ANGLE',
+ )
+ offset: FloatProperty(
+ name="Stroke Offset",
+ description="Offset strokes from fill",
+ soft_min=0.0, soft_max=100.0,
+ min=0.0, max=100.0,
+ default=0.001,
+ precision=3,
+ step=1,
+ subtype='DISTANCE',
+ unit='LENGTH',
+ )
+ seams: BoolProperty(
+ name="Only Seam Edges",
+ description="Convert only seam edges",
+ default=False,
+ )
+ faces: BoolProperty(
+ name="Export Faces",
+ description="Export faces as filled strokes",
+ default=True,
+ )
+ target: EnumProperty(
+ name="Target Object",
+ description="Grease Pencil Object",
+ items=my_objlist_callback
+ )
+ frame_target: IntProperty(
+ name="Target Frame",
+ description="Destination frame for the baked animation",
+ min=1, max=300000,
+ default=1,
+ )
+ project_type: EnumProperty(
+ name="Reproject Type",
+ description="Type of projection",
+ items=(
+ ("KEEP", "No Reproject", ""),
+ ("FRONT", "Front", "Reproject the strokes using the X-Z plane"),
+ ("SIDE", "Side", "Reproject the strokes using the Y-Z plane"),
+ ("TOP", "Top", "Reproject the strokes using the X-Y plane"),
+ ("VIEW", "View", "Reproject the strokes to current viewpoint"),
+ ("CURSOR", "Cursor", "Reproject the strokes using the orientation of 3D cursor")
+ )
+ )
+
+ @classmethod
+ def poll(self, context):
+ ob = context.active_object
+ return ((ob is not None) and
+ (ob.type in {'EMPTY', 'MESH'}) and
+ (context.mode == 'OBJECT'))
+
+ def execute(self, context):
+ bpy.ops.gpencil.bake_mesh_animation(
+ frame_start=self.frame_start,
+ frame_end=self.frame_end,
+ step=self.step,
+ angle=self.angle,
+ thickness=self.thickness,
+ seams=self.seams,
+ faces=self.faces,
+ offset=self.offset,
+ target=self.target,
+ frame_target=self.frame_target,
+ project_type=self.project_type
+ )
+
+ return {'FINISHED'}
+
+ def invoke(self, context, _event):
+ scene = context.scene
+ self.frame_start = scene.frame_start
+ self.frame_end = scene.frame_end
+ self.frame_target = scene.frame_start
+
+ wm = context.window_manager
+ return wm.invoke_props_dialog(self)
+
+
+classes = (
+ GPENCIL_OT_mesh_bake,
+)
diff --git a/release/scripts/startup/bl_operators/node.py b/release/scripts/startup/bl_operators/node.py
index 9fdaebc7540..1c5ebb4fa17 100644
--- a/release/scripts/startup/bl_operators/node.py
+++ b/release/scripts/startup/bl_operators/node.py
@@ -94,9 +94,16 @@ class NodeAddOperator:
for setting in self.settings:
# XXX catch exceptions here?
value = eval(setting.value)
+ node_data = node
+ node_attr_name = setting.name
+
+ # Support path to nested data.
+ if '.' in node_attr_name:
+ node_data_path, node_attr_name = node_attr_name.rsplit(".", 1)
+ node_data = node.path_resolve(node_data_path)
try:
- setattr(node, setting.name, value)
+ setattr(node_data, node_attr_name, value)
except AttributeError as e:
self.report(
{'ERROR_INVALID_INPUT'},
diff --git a/release/scripts/startup/bl_operators/object.py b/release/scripts/startup/bl_operators/object.py
index 09a98100796..cf91cd3c961 100644
--- a/release/scripts/startup/bl_operators/object.py
+++ b/release/scripts/startup/bl_operators/object.py
@@ -194,7 +194,7 @@ class SelectHierarchy(Operator):
for obj in selected_objects:
parent = obj.parent
- if parent:
+ if parent and parent.visible_get():
if obj_act == obj:
act_new = parent
@@ -202,12 +202,13 @@ class SelectHierarchy(Operator):
else:
for obj in selected_objects:
- select_new.extend(obj.children)
+ select_new.extend([child for child in obj.children if child.visible_get()])
if select_new:
select_new.sort(key=lambda obj_iter: obj_iter.name)
act_new = select_new[0]
+
# don't edit any object settings above this
if select_new:
if not self.extend:
diff --git a/release/scripts/startup/bl_operators/object_quick_effects.py b/release/scripts/startup/bl_operators/object_quick_effects.py
index 71153ba8b74..311631ac65f 100644
--- a/release/scripts/startup/bl_operators/object_quick_effects.py
+++ b/release/scripts/startup/bl_operators/object_quick_effects.py
@@ -399,8 +399,8 @@ class QuickSmoke(ObjectModeOperator, Operator):
if self.style == 'FIRE' or self.style == 'BOTH':
obj.modifiers[-1].domain_settings.use_noise = True
- # set correct cache file format for smoke
- obj.modifiers[-1].domain_settings.cache_data_format = 'UNI'
+ # ensure correct cache file format for smoke
+ obj.modifiers[-1].domain_settings.cache_data_format = 'OPENVDB'
# Setup material
@@ -514,7 +514,8 @@ class QuickLiquid(Operator):
obj.modifiers[-1].domain_settings.use_collision_border_top = True
obj.modifiers[-1].domain_settings.use_collision_border_bottom = True
- # set correct cache file format for liquid
+ # ensure correct cache file formats for liquid
+ obj.modifiers[-1].domain_settings.cache_data_format = 'OPENVDB'
obj.modifiers[-1].domain_settings.cache_mesh_format = 'BOBJECT'
# change domain type, will also allocate and show particle system for FLIP
diff --git a/release/scripts/startup/bl_operators/presets.py b/release/scripts/startup/bl_operators/presets.py
index c83d0b9f4d8..2ea93a1aee9 100644
--- a/release/scripts/startup/bl_operators/presets.py
+++ b/release/scripts/startup/bl_operators/presets.py
@@ -80,7 +80,8 @@ class AddPresetBase:
name = name.lower().strip()
name = bpy.path.display_name_to_filepath(name)
trans = maketrans_init()
- return name.translate(trans)
+ # Strip surrounding "_" as they are displayed as spaces.
+ return name.translate(trans).strip("_")
def execute(self, context):
import os
@@ -92,15 +93,16 @@ class AddPresetBase:
preset_menu_class = getattr(bpy.types, self.preset_menu)
is_xml = getattr(preset_menu_class, "preset_type", None) == 'XML'
+ is_preset_add = not (self.remove_name or self.remove_active)
if is_xml:
ext = ".xml"
else:
ext = ".py"
- name = self.name.strip()
- if not (self.remove_name or self.remove_active):
+ name = self.name.strip() if is_preset_add else self.name
+ if is_preset_add:
if not name:
return {'FINISHED'}
diff --git a/release/scripts/startup/bl_operators/screen_play_rendered_anim.py b/release/scripts/startup/bl_operators/screen_play_rendered_anim.py
index 3900fc3f2c2..255852d3b26 100644
--- a/release/scripts/startup/bl_operators/screen_play_rendered_anim.py
+++ b/release/scripts/startup/bl_operators/screen_play_rendered_anim.py
@@ -32,25 +32,11 @@ def guess_player_path(preset):
if preset == 'INTERNAL':
return bpy.app.binary_path
- elif preset == 'BLENDER24':
- player_path = "blender"
-
- if sys.platform == "darwin":
- test_path = "/Applications/blender 2.49.app/Contents/MacOS/blender"
- elif sys.platform[:3] == "win":
- test_path = "/Program Files/Blender Foundation/Blender/blender.exe"
-
- if os.path.exists(test_path):
- player_path = test_path
elif preset == 'DJV':
- player_path = "djv_view"
-
+ player_path = "djv"
if sys.platform == "darwin":
- # TODO, crummy supporting only 1 version,
- # could find the newest installed version
- test_path = ("/Applications/djv-0.8.2.app"
- "/Contents/Resources/bin/djv_view")
+ test_path = "/Applications/DJV2.app/Contents/Resources/bin/djv"
if os.path.exists(test_path):
player_path = test_path
@@ -148,7 +134,13 @@ class PlayRenderedAnim(Operator):
]
cmd.extend(opts)
elif preset == 'DJV':
- opts = [file, "-playback_speed", str(int(fps_final))]
+ opts = [
+ file,
+ "-speed", str(fps_final),
+ "-in_out", str(frame_start), str(frame_end),
+ "-frame", str(scene.frame_current),
+ "-time_units", "Frames"
+ ]
cmd.extend(opts)
elif preset == 'FRAMECYCLER':
opts = [file, f"{scene.frame_start:d}-{scene.frame_end:d}"]
@@ -175,14 +167,8 @@ class PlayRenderedAnim(Operator):
# launch it
print("Executing command:\n ", " ".join(quote(c) for c in cmd))
- # workaround for boost 1.46, can be eventually removed. bug: [#32350]
- env_copy = os.environ.copy()
- if preset == 'INTERNAL':
- env_copy["LC_ALL"] = "C"
- # end workaround
-
try:
- subprocess.Popen(cmd, env=env_copy)
+ subprocess.Popen(cmd)
except Exception as e:
err_msg = tip_("Couldn't run external animation player with command %r\n%s") % (cmd, e)
self.report(
diff --git a/release/scripts/startup/bl_operators/sequencer.py b/release/scripts/startup/bl_operators/sequencer.py
index af071561232..9ffb23287f8 100644
--- a/release/scripts/startup/bl_operators/sequencer.py
+++ b/release/scripts/startup/bl_operators/sequencer.py
@@ -164,6 +164,7 @@ class SequencerFadesClear(Operator):
if curve:
fcurves.remove(curve)
setattr(sequence, animated_property, 1.0)
+ sequence.invalidate('COMPOSITE')
return {'FINISHED'}
@@ -184,8 +185,10 @@ class SequencerFadesAdd(Operator):
('IN_OUT', 'Fade In And Out', 'Fade selected strips in and out'),
('IN', 'Fade In', 'Fade in selected strips'),
('OUT', 'Fade Out', 'Fade out selected strips'),
- ('CURSOR_FROM', 'From Playhead', 'Fade from the time cursor to the end of overlapping sequences'),
- ('CURSOR_TO', 'To Playhead', 'Fade from the start of sequences under the time cursor to the current frame'),
+ ('CURSOR_FROM', 'From Current Frame',
+ 'Fade from the time cursor to the end of overlapping sequences'),
+ ('CURSOR_TO', 'To Current Frame',
+ 'Fade from the start of sequences under the time cursor to the current frame'),
),
name="Fade type",
description="Fade in, out, both in and out, to, or from the current frame. Default is both in and out",
@@ -230,9 +233,10 @@ class SequencerFadesAdd(Operator):
self.fade_animation_clear(fade_fcurve, fades)
self.fade_animation_create(fade_fcurve, fades)
faded_sequences.append(sequence)
+ sequence.invalidate('COMPOSITE')
sequence_string = "sequence" if len(faded_sequences) == 1 else "sequences"
- self.report({'INFO'}, "Added fade animation to {} {}.".format(len(faded_sequences), sequence_string))
+ self.report({'INFO'}, "Added fade animation to %d %s." % (len(faded_sequences), sequence_string))
return {'FINISHED'}
def calculate_fade_duration(self, context, sequence):
@@ -360,7 +364,7 @@ class Fade:
return max_value if max_value > 0.0 else 1.0
def __repr__(self):
- return "Fade {}: {} to {}".format(self.type, self.start, self.end)
+ return "Fade %r: %r to %r" % (self.type, self.start, self.end)
def calculate_duration_frames(context, duration_seconds):
diff --git a/release/scripts/startup/bl_operators/vertexpaint_dirt.py b/release/scripts/startup/bl_operators/vertexpaint_dirt.py
index 7024582ad30..62ef3cb34b5 100644
--- a/release/scripts/startup/bl_operators/vertexpaint_dirt.py
+++ b/release/scripts/startup/bl_operators/vertexpaint_dirt.py
@@ -32,7 +32,7 @@ def get_vcolor_layer_data(me):
return lay.data
-def applyVertexDirt(me, blur_iterations, blur_strength, clamp_dirt, clamp_clean, dirt_only):
+def applyVertexDirt(me, blur_iterations, blur_strength, clamp_dirt, clamp_clean, dirt_only, normalize):
from mathutils import Vector
from math import acos
import array
@@ -74,14 +74,14 @@ def applyVertexDirt(me, blur_iterations, blur_strength, clamp_dirt, clamp_clean,
tot_con = len(con[i])
if tot_con == 0:
- continue
+ ang = pi / 2.0 # assume 90°, i. e. flat
+ else:
+ vec /= tot_con
- vec /= tot_con
-
- # angle is the acos() of the dot product between normal and connected verts.
- # > 90 degrees: convex
- # < 90 degrees: concave
- ang = acos(no.dot(vec))
+ # angle is the acos() of the dot product between normal and connected verts.
+ # > 90 degrees: convex
+ # < 90 degrees: concave
+ ang = acos(no.dot(vec))
# enforce min/max
ang = max(clamp_dirt, ang)
@@ -104,8 +104,12 @@ def applyVertexDirt(me, blur_iterations, blur_strength, clamp_dirt, clamp_clean,
vert_tone[j] /= len(c) * blur_strength + 1
del orig_vert_tone
- min_tone = min(vert_tone)
- max_tone = max(vert_tone)
+ if normalize:
+ min_tone = min(vert_tone)
+ max_tone = max(vert_tone)
+ else:
+ min_tone = clamp_dirt
+ max_tone = clamp_clean
tone_range = max_tone - min_tone
@@ -181,6 +185,11 @@ class VertexPaintDirt(Operator):
description="Don't calculate cleans for convex areas",
default=False,
)
+ normalize: BoolProperty(
+ name="Normalize",
+ description="Normalize the colors, increasing the contrast",
+ default=True,
+ )
@classmethod
def poll(cls, context):
@@ -198,6 +207,7 @@ class VertexPaintDirt(Operator):
self.dirt_angle,
self.clean_angle,
self.dirt_only,
+ self.normalize,
)
return ret
diff --git a/release/scripts/startup/bl_operators/view3d.py b/release/scripts/startup/bl_operators/view3d.py
index 88fa06a913f..a8e04eb2f24 100644
--- a/release/scripts/startup/bl_operators/view3d.py
+++ b/release/scripts/startup/bl_operators/view3d.py
@@ -98,7 +98,7 @@ class VIEW3D_OT_edit_mesh_extrude_move(Operator):
TRANSFORM_OT_shrink_fatten={},
)
elif dissolve_and_intersect:
- bpy.ops.mesh.extrude_region_dissolve_move_intersect(
+ bpy.ops.mesh.extrude_manifold(
'INVOKE_REGION_WIN',
MESH_OT_extrude_region={
"use_dissolve_ortho_edges": True,
diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py
index 4c4736cd669..a543ea6685c 100644
--- a/release/scripts/startup/bl_operators/wm.py
+++ b/release/scripts/startup/bl_operators/wm.py
@@ -1457,7 +1457,7 @@ class WM_OT_properties_edit(Operator):
proptype, is_array = rna_idprop_value_item_type(value)
row = layout.row()
- row.enabled = proptype in {int, float}
+ row.enabled = proptype in {int, float, str}
row.prop(self, "default")
row = layout.row(align=True)
@@ -1697,7 +1697,7 @@ class WM_OT_tool_set_by_id(Operator):
tool_settings.workspace_tool_type = 'FALLBACK'
return {'FINISHED'}
else:
- self.report({'WARNING'}, f"Tool {self.name!r:s} not found for space {space_type!r:s}.")
+ self.report({'WARNING'}, "Tool %r not found for space %r." % (self.name, space_type))
return {'CANCELLED'}
@@ -2216,8 +2216,8 @@ class WM_OT_batch_rename(Operator):
elif ty == 'STRIP':
chars = action.strip_chars
chars_strip = (
- "{:s}{:s}{:s}"
- ).format(
+ "%s%s%s"
+ ) % (
string.punctuation if 'PUNCT' in chars else "",
string.digits if 'DIGIT' in chars else "",
" " if 'SPACE' in chars else "",
@@ -2282,7 +2282,7 @@ class WM_OT_batch_rename(Operator):
split.prop(self, "data_type", text="")
split = layout.split(factor=0.5)
- split.label(text="Rename {:d} {:s}:".format(len(self._data[0]), self._data[2]))
+ split.label(text="Rename %d %s:" % (len(self._data[0]), self._data[2]))
split.row().prop(self, "data_source", expand=True)
for action in self.actions:
@@ -2397,7 +2397,7 @@ class WM_OT_batch_rename(Operator):
change_len += 1
total_len += 1
- self.report({'INFO'}, "Renamed {:d} of {:d} {:s}".format(change_len, total_len, descr))
+ self.report({'INFO'}, "Renamed %d of %d %s" % (change_len, total_len, descr))
return {'FINISHED'}
diff --git a/release/scripts/startup/bl_ui/properties_constraint.py b/release/scripts/startup/bl_ui/properties_constraint.py
index 3fc54ff6d12..8bae32775a9 100644
--- a/release/scripts/startup/bl_ui/properties_constraint.py
+++ b/release/scripts/startup/bl_ui/properties_constraint.py
@@ -20,894 +20,762 @@
from bpy.types import Panel
-class ConstraintButtonsPanel:
+class ObjectConstraintPanel(Panel):
+ bl_context = "constraint"
+
+ @classmethod
+ def poll(cls, context):
+ return (context.object)
+
+
+class BoneConstraintPanel(Panel):
+ bl_context = "bone_constraint"
+
+ @classmethod
+ def poll(cls, context):
+ return (context.pose_bone)
+
+
+class OBJECT_PT_constraints(ObjectConstraintPanel):
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
- bl_context = "constraint"
+ bl_label = "Object Constraints"
+ bl_options = {'HIDE_HEADER'}
- def draw_constraint(self, context, con):
+ def draw(self, context):
layout = self.layout
- box = layout.template_constraint(con)
+ layout.operator_menu_enum("object.constraint_add", "type", text="Add Object Constraint")
- if box:
- # match enum type to our functions, avoids a lookup table.
- getattr(self, con.type)(context, box, con)
+ layout.template_constraints(use_bone_constraints=False)
- if con.type in {'RIGID_BODY_JOINT', 'NULL'}:
- return
- if con.type in {'IK', 'SPLINE_IK'}:
- # constraint.disable_keep_transform doesn't work well
- # for these constraints.
- box.prop(con, "influence")
- else:
- row = box.row(align=True)
- row.prop(con, "influence")
- row.operator("constraint.disable_keep_transform", text="", icon='CANCEL')
+class BONE_PT_constraints(BoneConstraintPanel):
+ bl_space_type = 'PROPERTIES'
+ bl_region_type = 'WINDOW'
+ bl_label = "Bone Constraints"
+ bl_options = {'HIDE_HEADER'}
- @staticmethod
- def space_template(layout, con, target=True, owner=True):
- if target or owner:
+ def draw(self, context):
+ layout = self.layout
- split = layout.split(factor=0.2)
+ layout.operator_menu_enum("pose.constraint_add", "type", text="Add Bone Constraint")
- split.label(text="Space:")
- row = split.row()
+ layout.template_constraints(use_bone_constraints=True)
- if target:
- row.prop(con, "target_space", text="")
- if target and owner:
- row.label(icon='ARROW_LEFTRIGHT')
+# Parent class for constraint panels, with templates and drawing methods
+# shared between the bone and object constraint panels
+class ConstraintButtonsPanel(Panel):
+ bl_space_type = 'PROPERTIES'
+ bl_region_type = 'WINDOW'
+ bl_label = ""
+ bl_options = {'INSTANCED', 'HEADER_LAYOUT_EXPAND', 'DRAW_BOX'}
+
+ @staticmethod
+ def draw_influence(layout, con):
+ layout.separator()
+ if con.type in {'IK', 'SPLINE_IK'}:
+ # constraint.disable_keep_transform doesn't work well
+ # for these constraints.
+ layout.prop(con, "influence")
+ else:
+ row = layout.row(align=True)
+ row.prop(con, "influence")
+ row.operator("constraint.disable_keep_transform", text="", icon='CANCEL')
+ @staticmethod
+ def space_template(layout, con, target=True, owner=True):
+ if target or owner:
+ layout.separator()
+ if target:
+ layout.prop(con, "target_space", text="Target")
if owner:
- row.prop(con, "owner_space", text="")
+ layout.prop(con, "owner_space", text="Owner")
@staticmethod
def target_template(layout, con, subtargets=True):
- layout.prop(con, "target") # XXX limiting settings for only 'curves' or some type of object
+ col = layout.column()
+ col.prop(con, "target") # XXX limiting settings for only 'curves' or some type of object
if con.target and subtargets:
if con.target.type == 'ARMATURE':
- layout.prop_search(con, "subtarget", con.target.data, "bones", text="Bone")
+ col.prop_search(con, "subtarget", con.target.data, "bones", text="Bone")
- if hasattr(con, "head_tail"):
- row = layout.row(align=True)
- row.label(text="Head/Tail:")
- row.prop(con, "head_tail", text="")
+ if con.subtarget and hasattr(con, "head_tail"):
+ row = col.row(align=True)
+ row.use_property_decorate = False
+ sub = row.row(align=True)
+ sub.prop(con, "head_tail")
# XXX icon, and only when bone has segments?
- row.prop(con, "use_bbone_shape", text="", icon='IPO_BEZIER')
+ sub.prop(con, "use_bbone_shape", text="", icon='IPO_BEZIER')
+ row.prop_decorator(con, "head_tail")
elif con.target.type in {'MESH', 'LATTICE'}:
- layout.prop_search(con, "subtarget", con.target, "vertex_groups", text="Vertex Group")
+ col.prop_search(con, "subtarget", con.target, "vertex_groups", text="Vertex Group")
- @staticmethod
- def ik_template(layout, con):
- # only used for iTaSC
- layout.prop(con, "pole_target")
+ def get_constraint(self, context):
+ con = None
+ if context.pose_bone:
+ con = context.pose_bone.constraints[self.list_panel_index]
+ else:
+ con = context.object.constraints[self.list_panel_index]
+ self.layout.context_pointer_set("constraint", con)
+ return con
- if con.pole_target and con.pole_target.type == 'ARMATURE':
- layout.prop_search(con, "pole_subtarget", con.pole_target.data, "bones", text="Bone")
+ def draw_header(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
- if con.pole_target:
- row = layout.row()
- row.label()
- row.prop(con, "pole_angle")
+ layout.template_constraint_header(con)
- split = layout.split(factor=0.33)
- col = split.column()
- col.prop(con, "use_tail")
- col.prop(con, "use_stretch")
+ # Drawing methods for specific constraints. (Shared by object and bone constraint panels)
- col = split.column()
- col.prop(con, "chain_count")
+ def draw_childof(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
- def CHILD_OF(self, _context, layout, con):
self.target_template(layout, con)
- split = layout.split()
-
- col = split.column()
- col.label(text="Location:")
- col.prop(con, "use_location_x", text="X")
- col.prop(con, "use_location_y", text="Y")
- col.prop(con, "use_location_z", text="Z")
-
- col = split.column()
- col.label(text="Rotation:")
- col.prop(con, "use_rotation_x", text="X")
- col.prop(con, "use_rotation_y", text="Y")
- col.prop(con, "use_rotation_z", text="Z")
-
- col = split.column()
- col.label(text="Scale:")
- col.prop(con, "use_scale_x", text="X")
- col.prop(con, "use_scale_y", text="Y")
- col.prop(con, "use_scale_z", text="Z")
+ row = layout.row(heading="Location")
+ row.use_property_decorate = False
+ row.prop(con, "use_location_x", text="X", toggle=True)
+ row.prop(con, "use_location_y", text="Y", toggle=True)
+ row.prop(con, "use_location_z", text="Z", toggle=True)
+ row.label(icon='BLANK1')
+
+ row = layout.row(heading="Rotation")
+ row.use_property_decorate = False
+ row.prop(con, "use_rotation_x", text="X", toggle=True)
+ row.prop(con, "use_rotation_y", text="Y", toggle=True)
+ row.prop(con, "use_rotation_z", text="Z", toggle=True)
+ row.label(icon='BLANK1')
+
+ row = layout.row(heading="Scale")
+ row.use_property_decorate = False
+ row.prop(con, "use_scale_x", text="X", toggle=True)
+ row.prop(con, "use_scale_y", text="Y", toggle=True)
+ row.prop(con, "use_scale_z", text="Z", toggle=True)
+ row.label(icon='BLANK1')
row = layout.row()
row.operator("constraint.childof_set_inverse")
row.operator("constraint.childof_clear_inverse")
- def TRACK_TO(self, _context, layout, con):
- self.target_template(layout, con)
-
- row = layout.row()
- row.label(text="To:")
- row.prop(con, "track_axis", expand=True)
-
- row = layout.row()
- row.prop(con, "up_axis", text="Up")
- row.prop(con, "use_target_z")
-
- self.space_template(layout, con)
-
- def IK(self, context, layout, con):
- if context.object.pose.ik_solver == 'ITASC':
- layout.prop(con, "ik_type")
- getattr(self, 'IK_' + con.ik_type)(context, layout, con)
- else:
- # Standard IK constraint
- self.target_template(layout, con)
- layout.prop(con, "pole_target")
-
- if con.pole_target and con.pole_target.type == 'ARMATURE':
- layout.prop_search(con, "pole_subtarget", con.pole_target.data, "bones", text="Bone")
-
- if con.pole_target:
- row = layout.row()
- row.prop(con, "pole_angle")
- row.label()
-
- split = layout.split()
- col = split.column()
- col.prop(con, "iterations")
- col.prop(con, "chain_count")
-
- col = split.column()
- col.prop(con, "use_tail")
- col.prop(con, "use_stretch")
-
- layout.label(text="Weight:")
-
- split = layout.split()
- col = split.column()
- row = col.row(align=True)
- row.prop(con, "use_location", text="")
- sub = row.row(align=True)
- sub.active = con.use_location
- sub.prop(con, "weight", text="Position", slider=True)
-
- col = split.column()
- row = col.row(align=True)
- row.prop(con, "use_rotation", text="")
- sub = row.row(align=True)
- sub.active = con.use_rotation
- sub.prop(con, "orient_weight", text="Rotation", slider=True)
+ self.draw_influence(layout, con)
- def IK_COPY_POSE(self, _context, layout, con):
- self.target_template(layout, con)
- self.ik_template(layout, con)
+ def draw_trackto(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
- row = layout.row()
- row.label(text="Axis Ref:")
- row.prop(con, "reference_axis", expand=True)
- split = layout.split(factor=0.33)
- split.row().prop(con, "use_location")
- row = split.row()
- row.prop(con, "weight", text="Weight", slider=True)
- row.active = con.use_location
- split = layout.split(factor=0.33)
- row = split.row()
- row.label(text="Lock:")
- row = split.row()
- row.prop(con, "lock_location_x", text="X")
- row.prop(con, "lock_location_y", text="Y")
- row.prop(con, "lock_location_z", text="Z")
- split.active = con.use_location
-
- split = layout.split(factor=0.33)
- split.row().prop(con, "use_rotation")
- row = split.row()
- row.prop(con, "orient_weight", text="Weight", slider=True)
- row.active = con.use_rotation
- split = layout.split(factor=0.33)
- row = split.row()
- row.label(text="Lock:")
- row = split.row()
- row.prop(con, "lock_rotation_x", text="X")
- row.prop(con, "lock_rotation_y", text="Y")
- row.prop(con, "lock_rotation_z", text="Z")
- split.active = con.use_rotation
-
- def IK_DISTANCE(self, _context, layout, con):
self.target_template(layout, con)
- self.ik_template(layout, con)
- layout.prop(con, "limit_mode")
+ layout.prop(con, "track_axis", expand=True)
+ layout.prop(con, "up_axis", text="Up", expand=True)
+ layout.prop(con, "use_target_z")
- row = layout.row()
- row.prop(con, "weight", text="Weight", slider=True)
- row.prop(con, "distance", text="Distance", slider=True)
+ self.space_template(layout, con)
- def FOLLOW_PATH(self, _context, layout, con):
- self.target_template(layout, con)
- layout.operator("constraint.followpath_path_animate", text="Animate Path", icon='ANIM_DATA')
+ self.draw_influence(layout, con)
- split = layout.split()
+ def draw_follow_path(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
- col = split.column()
- col.prop(con, "use_curve_follow")
- col.prop(con, "use_curve_radius")
+ self.target_template(layout, con)
- col = split.column()
- col.prop(con, "use_fixed_location")
if con.use_fixed_location:
- col.prop(con, "offset_factor", text="Offset")
+ layout.prop(con, "offset_factor", text="Offset Factor")
else:
- col.prop(con, "offset")
+ layout.prop(con, "offset")
- row = layout.row()
- row.label(text="Forward:")
- row.prop(con, "forward_axis", expand=True)
+ layout.prop(con, "forward_axis", expand=True)
+ layout.prop(con, "up_axis", expand=True)
- row = layout.row()
- row.prop(con, "up_axis", text="Up")
- row.label()
+ col = layout.column()
+ col.prop(con, "use_fixed_location")
+ col.prop(con, "use_curve_radius")
+ col.prop(con, "use_curve_follow")
- def LIMIT_ROTATION(self, _context, layout, con):
- split = layout.split()
+ layout.operator("constraint.followpath_path_animate", text="Animate Path", icon='ANIM_DATA')
- col = split.column(align=True)
- col.prop(con, "use_limit_x")
- sub = col.column(align=True)
+ self.draw_influence(layout, con)
+
+ def draw_rot_limit(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
+
+ # Decorators and property split are really buggy with these properties
+ row = layout.row(heading="Limit X", align=True)
+ row.use_property_decorate = False
+ row.prop(con, "use_limit_x", text="")
+ sub = row.column(align=True)
sub.active = con.use_limit_x
sub.prop(con, "min_x", text="Min")
sub.prop(con, "max_x", text="Max")
+ row.label(icon="BLANK1")
- col = split.column(align=True)
- col.prop(con, "use_limit_y")
- sub = col.column(align=True)
+ row = layout.row(heading="Y", align=True)
+ row.use_property_decorate = False
+ row.prop(con, "use_limit_y", text="")
+ sub = row.column(align=True)
sub.active = con.use_limit_y
sub.prop(con, "min_y", text="Min")
sub.prop(con, "max_y", text="Max")
+ row.label(icon="BLANK1")
- col = split.column(align=True)
- col.prop(con, "use_limit_z")
- sub = col.column(align=True)
+ row = layout.row(heading="Z", align=True)
+ row.use_property_decorate = False
+ row.prop(con, "use_limit_z", text="")
+ sub = row.column(align=True)
sub.active = con.use_limit_z
sub.prop(con, "min_z", text="Min")
sub.prop(con, "max_z", text="Max")
+ row.label(icon="BLANK1")
layout.prop(con, "use_transform_limit")
+ layout.prop(con, "owner_space")
- row = layout.row()
- row.label(text="Convert:")
- row.prop(con, "owner_space", text="")
-
- def LIMIT_LOCATION(self, _context, layout, con):
- split = layout.split()
-
- col = split.column()
- col.prop(con, "use_min_x")
- sub = col.column()
- sub.active = con.use_min_x
- sub.prop(con, "min_x", text="")
- col.prop(con, "use_max_x")
- sub = col.column()
- sub.active = con.use_max_x
- sub.prop(con, "max_x", text="")
-
- col = split.column()
- col.prop(con, "use_min_y")
- sub = col.column()
- sub.active = con.use_min_y
- sub.prop(con, "min_y", text="")
- col.prop(con, "use_max_y")
- sub = col.column()
- sub.active = con.use_max_y
- sub.prop(con, "max_y", text="")
-
- col = split.column()
- col.prop(con, "use_min_z")
- sub = col.column()
- sub.active = con.use_min_z
- sub.prop(con, "min_z", text="")
- col.prop(con, "use_max_z")
- sub = col.column()
- sub.active = con.use_max_z
- sub.prop(con, "max_z", text="")
+ self.draw_influence(layout, con)
- row = layout.row()
- row.prop(con, "use_transform_limit")
- row.label()
+ def draw_loc_limit(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
- row = layout.row()
- row.label(text="Convert:")
- row.prop(con, "owner_space", text="")
-
- def LIMIT_SCALE(self, _context, layout, con):
- split = layout.split()
-
- col = split.column()
- col.prop(con, "use_min_x")
- sub = col.column()
- sub.active = con.use_min_x
- sub.prop(con, "min_x", text="")
- col.prop(con, "use_max_x")
- sub = col.column()
- sub.active = con.use_max_x
- sub.prop(con, "max_x", text="")
-
- col = split.column()
- col.prop(con, "use_min_y")
- sub = col.column()
- sub.active = con.use_min_y
- sub.prop(con, "min_y", text="")
- col.prop(con, "use_max_y")
- sub = col.column()
- sub.active = con.use_max_y
- sub.prop(con, "max_y", text="")
-
- col = split.column()
- col.prop(con, "use_min_z")
- sub = col.column()
- sub.active = con.use_min_z
- sub.prop(con, "min_z", text="")
- col.prop(con, "use_max_z")
- sub = col.column()
- sub.active = con.use_max_z
- sub.prop(con, "max_z", text="")
+ col = layout.column()
- row = layout.row()
- row.prop(con, "use_transform_limit")
- row.label()
+ row = col.row(heading="Minimum X", align=True)
+ row.use_property_decorate = False
+ sub = row.row(align=True)
+ sub.prop(con, "use_min_x", text="")
+ subsub = sub.row(align=True)
+ subsub.active = con.use_min_x
+ subsub.prop(con, "min_x", text="")
+ row.prop_decorator(con, "min_x")
+
+ row = col.row(heading="Y", align=True)
+ row.use_property_decorate = False
+ sub = row.row(align=True)
+ sub.prop(con, "use_min_y", text="")
+ subsub = sub.row(align=True)
+ subsub.active = con.use_min_y
+ subsub.prop(con, "min_y", text="")
+ row.prop_decorator(con, "min_y")
+
+ row = col.row(heading="Z", align=True)
+ row.use_property_decorate = False
+ sub = row.row(align=True)
+ sub.prop(con, "use_min_z", text="")
+ subsub = sub.row(align=True)
+ subsub.active = con.use_min_z
+ subsub.prop(con, "min_z", text="")
+ row.prop_decorator(con, "min_z")
+
+ col.separator()
+
+ row = col.row(heading="Maximum X", align=True)
+ row.use_property_decorate = False
+ sub = row.row(align=True)
+ sub.prop(con, "use_max_x", text="")
+ subsub = sub.row(align=True)
+ subsub.active = con.use_max_x
+ subsub.prop(con, "max_x", text="")
+ row.prop_decorator(con, "max_x")
+
+ row = col.row(heading="Y", align=True)
+ row.use_property_decorate = False
+ sub = row.row(align=True)
+ sub.prop(con, "use_max_y", text="")
+ subsub = sub.row(align=True)
+ subsub.active = con.use_max_y
+ subsub.prop(con, "max_y", text="")
+ row.prop_decorator(con, "max_y")
+
+ row = col.row(heading="Z", align=True)
+ row.use_property_decorate = False
+ sub = row.row(align=True)
+ sub.prop(con, "use_max_z", text="")
+ subsub = sub.row(align=True)
+ subsub.active = con.use_max_z
+ subsub.prop(con, "max_z", text="")
+ row.prop_decorator(con, "max_z")
- row = layout.row()
- row.label(text="Convert:")
- row.prop(con, "owner_space", text="")
+ layout.prop(con, "use_transform_limit")
+ layout.prop(con, "owner_space")
- def COPY_ROTATION(self, _context, layout, con):
- self.target_template(layout, con)
+ self.draw_influence(layout, con)
- layout.prop(con, "euler_order", text="Order")
+ def draw_size_limit(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
- split = layout.split()
+ col = layout.column()
- col = split.column()
- col.prop(con, "use_x", text="X")
- sub = col.column()
- sub.active = con.use_x
- sub.prop(con, "invert_x", text="Invert")
+ row = col.row(heading="Minimum X", align=True)
+ row.use_property_decorate = False
+ sub = row.row(align=True)
+ sub.prop(con, "use_min_x", text="")
+ subsub = sub.row(align=True)
+ subsub.active = con.use_min_x
+ subsub.prop(con, "min_x", text="")
+ row.prop_decorator(con, "min_x")
+
+ row = col.row(heading="Y", align=True)
+ row.use_property_decorate = False
+ sub = row.row(align=True)
+ sub.prop(con, "use_min_y", text="")
+ subsub = sub.row(align=True)
+ subsub.active = con.use_min_y
+ subsub.prop(con, "min_y", text="")
+ row.prop_decorator(con, "min_y")
+
+ row = col.row(heading="Z", align=True)
+ row.use_property_decorate = False
+ sub = row.row(align=True)
+ sub.prop(con, "use_min_z", text="")
+ subsub = sub.row(align=True)
+ subsub.active = con.use_min_z
+ subsub.prop(con, "min_z", text="")
+ row.prop_decorator(con, "min_z")
+
+ col.separator()
+
+ row = col.row(heading="Maximum X", align=True)
+ row.use_property_decorate = False
+ sub = row.row(align=True)
+ sub.prop(con, "use_max_x", text="")
+ subsub = sub.row(align=True)
+ subsub.active = con.use_max_x
+ subsub.prop(con, "max_x", text="")
+ row.prop_decorator(con, "max_x")
+
+ row = col.row(heading="Y", align=True)
+ row.use_property_decorate = False
+ sub = row.row(align=True)
+ sub.prop(con, "use_max_y", text="")
+ subsub = sub.row(align=True)
+ subsub.active = con.use_max_y
+ subsub.prop(con, "max_y", text="")
+ row.prop_decorator(con, "max_y")
+
+ row = col.row(heading="Z", align=True)
+ row.use_property_decorate = False
+ sub = row.row(align=True)
+ sub.prop(con, "use_max_z", text="")
+ subsub = sub.row(align=True)
+ subsub.active = con.use_max_z
+ subsub.prop(con, "max_z", text="")
+ row.prop_decorator(con, "max_z")
- col = split.column()
- col.prop(con, "use_y", text="Y")
- sub = col.column()
- sub.active = con.use_y
- sub.prop(con, "invert_y", text="Invert")
- col = split.column()
- col.prop(con, "use_z", text="Z")
- sub = col.column()
- sub.active = con.use_z
- sub.prop(con, "invert_z", text="Invert")
+ layout.prop(con, "use_transform_limit")
+ layout.prop(con, "owner_space")
- layout.prop(con, "mix_mode", text="Mix")
+ self.draw_influence(layout, con)
- self.space_template(layout, con)
+ def draw_rotate_like(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
- def COPY_LOCATION(self, _context, layout, con):
self.target_template(layout, con)
- split = layout.split()
+ layout.prop(con, "euler_order", text="Order")
- col = split.column()
- col.prop(con, "use_x", text="X")
- sub = col.column()
- sub.active = con.use_x
- sub.prop(con, "invert_x", text="Invert")
+ row = layout.row(heading="Axis", align=True)
+ row.use_property_decorate = False
+ sub = row.row(align=True)
+ sub.prop(con, "use_x", text="X", toggle=True)
+ sub.prop(con, "use_y", text="Y", toggle=True)
+ sub.prop(con, "use_z", text="Z", toggle=True)
+ row.label(icon='BLANK1')
+
+ row = layout.row(heading="Invert", align=True)
+ row.use_property_decorate = False
+ sub = row.row(align=True)
+ sub.prop(con, "invert_x", text="X", toggle=True)
+ sub.prop(con, "invert_y", text="Y", toggle=True)
+ sub.prop(con, "invert_z", text="Z", toggle=True)
+ row.label(icon='BLANK1')
- col = split.column()
- col.prop(con, "use_y", text="Y")
- sub = col.column()
- sub.active = con.use_y
- sub.prop(con, "invert_y", text="Invert")
+ layout.prop(con, "mix_mode", text="Mix")
- col = split.column()
- col.prop(con, "use_z", text="Z")
- sub = col.column()
- sub.active = con.use_z
- sub.prop(con, "invert_z", text="Invert")
+ self.space_template(layout, con)
+
+ self.draw_influence(layout, con)
+
+ def draw_locate_like(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
+
+ self.target_template(layout, con)
+
+ row = layout.row(heading="Axis", align=True)
+ row.use_property_decorate = False
+ sub = row.row(align=True)
+ sub.prop(con, "use_x", text="X", toggle=True)
+ sub.prop(con, "use_y", text="Y", toggle=True)
+ sub.prop(con, "use_z", text="Z", toggle=True)
+ row.label(icon='BLANK1')
+
+ row = layout.row(heading="Invert", align=True)
+ row.use_property_decorate = False
+ sub = row.row(align=True)
+ sub.prop(con, "invert_x", text="X", toggle=True)
+ sub.prop(con, "invert_y", text="Y", toggle=True)
+ sub.prop(con, "invert_z", text="Z", toggle=True)
+ row.label(icon='BLANK1')
layout.prop(con, "use_offset")
self.space_template(layout, con)
- def COPY_SCALE(self, _context, layout, con):
+ self.draw_influence(layout, con)
+
+ def draw_size_like(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
+
self.target_template(layout, con)
- row = layout.row(align=True)
- row.prop(con, "use_x", text="X")
- row.prop(con, "use_y", text="Y")
- row.prop(con, "use_z", text="Z")
+ row = layout.row(heading="Axis", align=True)
+ row.use_property_decorate = False
+ sub = row.row(align=True)
+ sub.prop(con, "use_x", text="X", toggle=True)
+ sub.prop(con, "use_y", text="Y", toggle=True)
+ sub.prop(con, "use_z", text="Z", toggle=True)
+ row.label(icon='BLANK1')
- layout.prop(con, "power")
- layout.prop(con, "use_make_uniform")
+ col = layout.column()
+ col.prop(con, "power")
+ col.prop(con, "use_make_uniform")
- row = layout.row()
- row.prop(con, "use_offset")
- row = row.row()
+ col.prop(con, "use_offset")
+ row = col.row()
row.active = con.use_offset
row.prop(con, "use_add")
self.space_template(layout, con)
- def MAINTAIN_VOLUME(self, _context, layout, con):
+ self.draw_influence(layout, con)
+
+ def draw_same_volume(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
layout.prop(con, "mode")
- row = layout.row()
- row.label(text="Free:")
+ row = layout.row(heading="Free Axis")
row.prop(con, "free_axis", expand=True)
layout.prop(con, "volume")
- row = layout.row()
- row.label(text="Convert:")
- row.prop(con, "owner_space", text="")
-
- def COPY_TRANSFORMS(self, _context, layout, con):
- self.target_template(layout, con)
-
- layout.prop(con, "mix_mode", text="Mix")
+ layout.prop(con, "owner_space")
- self.space_template(layout, con)
+ self.draw_influence(layout, con)
- # def SCRIPT(self, context, layout, con):
+ def draw_trans_like(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
- def ACTION(self, _context, layout, con):
self.target_template(layout, con)
- split = layout.split()
-
- col = split.column()
- col.label(text="From Target:")
- col.prop(con, "transform_channel", text="")
- col.prop(con, "target_space", text="")
-
- col = split.column()
- col.label(text="To Action:")
- col.prop(con, "action", text="")
- col.prop(con, "use_bone_object_action")
-
- split = layout.split()
-
- col = split.column(align=True)
- col.label(text="Target Range:")
- col.prop(con, "min", text="Min")
- col.prop(con, "max", text="Max")
-
- col = split.column(align=True)
- col.label(text="Action Range:")
- col.prop(con, "frame_start", text="Start")
- col.prop(con, "frame_end", text="End")
-
layout.prop(con, "mix_mode", text="Mix")
- def LOCKED_TRACK(self, _context, layout, con):
- self.target_template(layout, con)
+ self.space_template(layout, con)
- row = layout.row()
- row.label(text="To:")
- row.prop(con, "track_axis", expand=True)
+ self.draw_influence(layout, con)
- row = layout.row()
- row.label(text="Lock:")
- row.prop(con, "lock_axis", expand=True)
+ def draw_action(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
- def LIMIT_DISTANCE(self, _context, layout, con):
self.target_template(layout, con)
- col = layout.column(align=True)
- col.prop(con, "distance")
- col.operator("constraint.limitdistance_reset")
-
- row = layout.row()
- row.label(text="Clamp Region:")
- row.prop(con, "limit_mode", text="")
+ layout.prop(con, "mix_mode", text="Mix")
- row = layout.row()
- row.prop(con, "use_transform_limit")
- row.label()
+ self.draw_influence(layout, con)
- self.space_template(layout, con)
+ def draw_lock_track(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
- def STRETCH_TO(self, _context, layout, con):
self.target_template(layout, con)
- row = layout.row()
- row.prop(con, "rest_length", text="Rest Length")
- row.operator("constraint.stretchto_reset", text="Reset")
-
- layout.prop(con, "bulge", text="Volume Variation")
- split = layout.split()
- col = split.column(align=True)
- col.prop(con, "use_bulge_min", text="Volume Min")
- sub = col.column()
- sub.active = con.use_bulge_min
- sub.prop(con, "bulge_min", text="")
- col = split.column(align=True)
- col.prop(con, "use_bulge_max", text="Volume Max")
- sub = col.column()
- sub.active = con.use_bulge_max
- sub.prop(con, "bulge_max", text="")
- col = layout.column()
- col.active = con.use_bulge_min or con.use_bulge_max
- col.prop(con, "bulge_smooth", text="Smooth")
+ layout.prop(con, "track_axis", expand=True)
+ layout.prop(con, "lock_axis", expand=True)
- split = layout.split(factor=0.3)
- split.label(text="Volume:")
- row = split.row()
- row.prop(con, "volume", expand=True)
+ self.draw_influence(layout, con)
- split = layout.split(factor=0.3)
- split.label(text="Rotation:")
- row = split.row()
- row.prop(con, "keep_axis", expand=True)
+ def draw_dist_limit(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
- def FLOOR(self, _context, layout, con):
self.target_template(layout, con)
- layout.prop(con, "use_rotation")
- layout.prop(con, "offset")
-
row = layout.row()
- row.label(text="Min/Max:")
- row.prop(con, "floor_location", expand=True)
+ row.prop(con, "distance")
+ row.operator("constraint.limitdistance_reset", text="", icon="X")
- self.space_template(layout, con)
+ layout.prop(con, "limit_mode", text="Clamp Region")
- def RIGID_BODY_JOINT(self, _context, layout, con):
- self.target_template(layout, con, subtargets=False)
-
- layout.prop(con, "pivot_type")
- layout.prop(con, "child")
+ layout.prop(con, "use_transform_limit")
- row = layout.row()
- row.prop(con, "use_linked_collision", text="Linked Collision")
- row.prop(con, "show_pivot", text="Display Pivot")
-
- split = layout.split()
-
- col = split.column(align=True)
- col.label(text="Pivot:")
- col.prop(con, "pivot_x", text="X")
- col.prop(con, "pivot_y", text="Y")
- col.prop(con, "pivot_z", text="Z")
-
- col = split.column(align=True)
- col.label(text="Axis:")
- col.prop(con, "axis_x", text="X")
- col.prop(con, "axis_y", text="Y")
- col.prop(con, "axis_z", text="Z")
-
- if con.pivot_type == 'CONE_TWIST':
- layout.label(text="Limits:")
- split = layout.split()
-
- col = split.column()
- col.prop(con, "use_angular_limit_x", text="Angle X")
- sub = col.column()
- sub.active = con.use_angular_limit_x
- sub.prop(con, "limit_angle_max_x", text="")
-
- col = split.column()
- col.prop(con, "use_angular_limit_y", text="Angle Y")
- sub = col.column()
- sub.active = con.use_angular_limit_y
- sub.prop(con, "limit_angle_max_y", text="")
-
- col = split.column()
- col.prop(con, "use_angular_limit_z", text="Angle Z")
- sub = col.column()
- sub.active = con.use_angular_limit_z
- sub.prop(con, "limit_angle_max_z", text="")
-
- elif con.pivot_type == 'GENERIC_6_DOF':
- layout.label(text="Limits:")
- split = layout.split()
-
- col = split.column(align=True)
- col.prop(con, "use_limit_x", text="X")
- sub = col.column(align=True)
- sub.active = con.use_limit_x
- sub.prop(con, "limit_min_x", text="Min")
- sub.prop(con, "limit_max_x", text="Max")
-
- col = split.column(align=True)
- col.prop(con, "use_limit_y", text="Y")
- sub = col.column(align=True)
- sub.active = con.use_limit_y
- sub.prop(con, "limit_min_y", text="Min")
- sub.prop(con, "limit_max_y", text="Max")
-
- col = split.column(align=True)
- col.prop(con, "use_limit_z", text="Z")
- sub = col.column(align=True)
- sub.active = con.use_limit_z
- sub.prop(con, "limit_min_z", text="Min")
- sub.prop(con, "limit_max_z", text="Max")
-
- split = layout.split()
-
- col = split.column(align=True)
- col.prop(con, "use_angular_limit_x", text="Angle X")
- sub = col.column(align=True)
- sub.active = con.use_angular_limit_x
- sub.prop(con, "limit_angle_min_x", text="Min")
- sub.prop(con, "limit_angle_max_x", text="Max")
-
- col = split.column(align=True)
- col.prop(con, "use_angular_limit_y", text="Angle Y")
- sub = col.column(align=True)
- sub.active = con.use_angular_limit_y
- sub.prop(con, "limit_angle_min_y", text="Min")
- sub.prop(con, "limit_angle_max_y", text="Max")
-
- col = split.column(align=True)
- col.prop(con, "use_angular_limit_z", text="Angle Z")
- sub = col.column(align=True)
- sub.active = con.use_angular_limit_z
- sub.prop(con, "limit_angle_min_z", text="Min")
- sub.prop(con, "limit_angle_max_z", text="Max")
-
- elif con.pivot_type == 'HINGE':
- layout.label(text="Limits:")
- split = layout.split()
+ self.space_template(layout, con)
- row = split.row(align=True)
- col = row.column()
- col.prop(con, "use_angular_limit_x", text="Angle X")
+ self.draw_influence(layout, con)
- col = row.column()
- col.active = con.use_angular_limit_x
- col.prop(con, "limit_angle_min_x", text="Min")
- col = row.column()
- col.active = con.use_angular_limit_x
- col.prop(con, "limit_angle_max_x", text="Max")
+ def draw_stretch_to(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
- def CLAMP_TO(self, _context, layout, con):
self.target_template(layout, con)
row = layout.row()
- row.label(text="Main Axis:")
- row.prop(con, "main_axis", expand=True)
-
- layout.prop(con, "use_cyclic")
-
- def TRANSFORM(self, _context, layout, con):
- self.target_template(layout, con)
+ row.prop(con, "rest_length")
+ row.operator("constraint.stretchto_reset", text="", icon="X")
- layout.prop(con, "use_motion_extrapolate", text="Extrapolate")
+ layout.separator()
col = layout.column()
- col.row().label(text="Source:")
- col.row().prop(con, "map_from", expand=True)
-
- if con.map_from == 'ROTATION':
- layout.prop(con, "from_rotation_mode", text="Mode")
-
- split = layout.split()
- ext = "" if con.map_from == 'LOCATION' else "_rot" if con.map_from == 'ROTATION' else "_scale"
+ col.prop(con, "bulge", text="Volume Variation")
+
+ row = col.row(heading="Volume Min", align=True)
+ row.use_property_decorate = False
+ sub = row.row(align=True)
+ sub.prop(con, "use_bulge_min", text="")
+ subsub = sub.row(align=True)
+ subsub.active = con.use_bulge_min
+ subsub.prop(con, "bulge_min", text="")
+ row.prop_decorator(con, "bulge_min")
+
+ row = col.row(heading="Max", align=True)
+ row.use_property_decorate = False
+ sub = row.row(align=True)
+ sub.prop(con, "use_bulge_max", text="")
+ subsub = sub.row(align=True)
+ subsub.active = con.use_bulge_max
+ subsub.prop(con, "bulge_max", text="")
+ row.prop_decorator(con, "bulge_max")
- sub = split.column(align=True)
- sub.label(text="X:")
- sub.prop(con, "from_min_x" + ext, text="Min")
- sub.prop(con, "from_max_x" + ext, text="Max")
+ row = col.row()
+ row.active = con.use_bulge_min or con.use_bulge_max
+ row.prop(con, "bulge_smooth", text="Smooth")
- sub = split.column(align=True)
- sub.label(text="Y:")
- sub.prop(con, "from_min_y" + ext, text="Min")
- sub.prop(con, "from_max_y" + ext, text="Max")
+ layout.prop(con, "volume", expand=True)
+ layout.prop(con, "keep_axis", text="Rotation", expand=True)
- sub = split.column(align=True)
- sub.label(text="Z:")
- sub.prop(con, "from_min_z" + ext, text="Min")
- sub.prop(con, "from_max_z" + ext, text="Max")
+ self.draw_influence(layout, con)
- col = layout.column()
- row = col.row()
- row.label(text="Source to Destination Mapping:")
+ def draw_min_max(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
- # note: chr(187) is the ASCII arrow ( >> ). Blender Text Editor can't
- # open it. Thus we are using the hard-coded value instead.
- row = col.row()
- row.prop(con, "map_to_x_from", expand=False, text="")
- row.label(text=" %s X" % chr(187))
+ self.target_template(layout, con)
- row = col.row()
- row.prop(con, "map_to_y_from", expand=False, text="")
- row.label(text=" %s Y" % chr(187))
+ layout.prop(con, "offset")
+ layout.prop(con, "floor_location", expand=True, text="Min/Max")
+ layout.prop(con, "use_rotation")
- row = col.row()
- row.prop(con, "map_to_z_from", expand=False, text="")
- row.label(text=" %s Z" % chr(187))
+ self.space_template(layout, con)
- split = layout.split()
+ self.draw_influence(layout, con)
- col = split.column()
- col.label(text="Destination:")
- col.row().prop(con, "map_to", expand=True)
+ def draw_clamp_to(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
- if con.map_to == 'ROTATION':
- layout.prop(con, "to_euler_order", text="Order")
+ self.target_template(layout, con)
- split = layout.split()
- ext = "" if con.map_to == 'LOCATION' else "_rot" if con.map_to == 'ROTATION' else "_scale"
+ layout.prop(con, "main_axis", expand=True)
- col = split.column()
- col.label(text="X:")
+ layout.prop(con, "use_cyclic")
- sub = col.column(align=True)
- sub.prop(con, "to_min_x" + ext, text="Min")
- sub.prop(con, "to_max_x" + ext, text="Max")
+ self.draw_influence(layout, con)
- col = split.column()
- col.label(text="Y:")
+ def draw_transform(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
- sub = col.column(align=True)
- sub.prop(con, "to_min_y" + ext, text="Min")
- sub.prop(con, "to_max_y" + ext, text="Max")
+ self.target_template(layout, con)
- col = split.column()
- col.label(text="Z:")
+ layout.prop(con, "use_motion_extrapolate", text="Extrapolate")
- sub = col.column(align=True)
- sub.prop(con, "to_min_z" + ext, text="Min")
- sub.prop(con, "to_max_z" + ext, text="Max")
+ self.space_template(layout, con)
- layout.prop(con, "mix_mode" + ext, text="Mix")
+ self.draw_influence(layout, con)
- self.space_template(layout, con)
+ def draw_shrinkwrap(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
- def SHRINKWRAP(self, _context, layout, con):
self.target_template(layout, con, False)
layout.prop(con, "distance")
- layout.prop(con, "shrinkwrap_type")
+ layout.prop(con, "shrinkwrap_type", text="Mode")
- if con.shrinkwrap_type in {'PROJECT', 'NEAREST_SURFACE', 'TARGET_PROJECT'}:
- layout.prop(con, "wrap_mode", text="Snap Mode")
+ layout.separator()
if con.shrinkwrap_type == 'PROJECT':
- row = layout.row(align=True)
- row.prop(con, "project_axis", expand=True)
- split = layout.split(factor=0.4)
- split.label(text="Axis Space:")
- rowsub = split.row()
- rowsub.prop(con, "project_axis_space", text="")
- split = layout.split(factor=0.4)
- split.label(text="Face Culling:")
- rowsub = split.row()
- rowsub.prop(con, "cull_face", expand=True)
- row = layout.row()
- row.prop(con, "use_project_opposite")
- rowsub = row.row()
- rowsub.active = con.use_project_opposite and con.cull_face != 'OFF'
- rowsub.prop(con, "use_invert_cull")
- layout.prop(con, "project_limit")
+ layout.prop(con, "project_axis", expand=True, text="Project Axis")
+ layout.prop(con, "project_axis_space", text="Space")
+ layout.prop(con, "project_limit", text="Distance")
+ layout.prop(con, "use_project_opposite")
- if con.shrinkwrap_type in {'PROJECT', 'NEAREST_SURFACE', 'TARGET_PROJECT'}:
- layout.prop(con, "use_track_normal")
+ layout.separator()
- row = layout.row(align=True)
- row.active = con.use_track_normal
- row.prop(con, "track_axis", expand=True)
+ col = layout.column()
+ row = col.row()
+ row.prop(con, "cull_face", expand=True)
+ row = col.row()
+ row.active = con.use_project_opposite and con.cull_face != 'OFF'
+ row.prop(con, "use_invert_cull")
- def DAMPED_TRACK(self, _context, layout, con):
- self.target_template(layout, con)
+ layout.separator()
- row = layout.row()
- row.label(text="To:")
- row.prop(con, "track_axis", expand=True)
+ if con.shrinkwrap_type in {'PROJECT', 'NEAREST_SURFACE', 'TARGET_PROJECT'}:
+ layout.prop(con, "wrap_mode", text="Snap Mode")
+ row = layout.row(heading="Align to Normal", align=True)
+ row.use_property_decorate = False
+ sub = row.row(align=True)
+ sub.prop(con, "use_track_normal", text="")
+ subsub = sub.row(align=True)
+ subsub.active = con.use_track_normal
+ subsub.prop(con, "track_axis", text="")
+ row.prop_decorator(con, "track_axis")
+
+ self.draw_influence(layout, con)
+
+ def draw_damp_track(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
- def SPLINE_IK(self, _context, layout, con):
self.target_template(layout, con)
- col = layout.column()
- col.label(text="Spline Fitting:")
- col.prop(con, "chain_count")
- col.prop(con, "use_even_divisions")
- col.prop(con, "use_chain_offset")
+ layout.prop(con, "track_axis", expand=True)
- col = layout.column()
- col.label(text="Chain Scaling:")
- col.prop(con, "use_curve_radius")
+ self.draw_influence(layout, con)
- layout.prop(con, "y_scale_mode")
- layout.prop(con, "xz_scale_mode")
+ def draw_spline_ik(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
- if con.xz_scale_mode in {'INVERSE_PRESERVE', 'VOLUME_PRESERVE'}:
- layout.prop(con, "use_original_scale")
+ self.target_template(layout, con)
- if con.xz_scale_mode == 'VOLUME_PRESERVE':
- layout.prop(con, "bulge", text="Volume Variation")
- split = layout.split()
- col = split.column(align=True)
- col.prop(con, "use_bulge_min", text="Volume Min")
- sub = col.column()
- sub.active = con.use_bulge_min
- sub.prop(con, "bulge_min", text="")
- col = split.column(align=True)
- col.prop(con, "use_bulge_max", text="Volume Max")
- sub = col.column()
- sub.active = con.use_bulge_max
- sub.prop(con, "bulge_max", text="")
- col = layout.column()
- col.active = con.use_bulge_min or con.use_bulge_max
- col.prop(con, "bulge_smooth", text="Smooth")
+ self.draw_influence(layout, con)
+
+ def draw_pivot(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
- def PIVOT(self, _context, layout, con):
self.target_template(layout, con)
if con.target:
- col = layout.column()
- col.prop(con, "offset", text="Pivot Offset")
+ layout.prop(con, "offset", text="Pivot Offset")
else:
- col = layout.column()
- col.prop(con, "use_relative_location")
+ layout.prop(con, "use_relative_location")
if con.use_relative_location:
- col.prop(con, "offset", text="Relative Pivot Point")
+ layout.prop(con, "offset", text="Pivot Point")
else:
- col.prop(con, "offset", text="Absolute Pivot Point")
+ layout.prop(con, "offset", text="Pivot Point")
col = layout.column()
- col.prop(con, "rotation_range", text="Pivot When")
+ col.prop(con, "rotation_range", text="Rotation Range")
- @staticmethod
- def _getConstraintClip(context, con):
- if not con.use_active_clip:
- return con.clip
+ self.draw_influence(layout, con)
+
+ def draw_follow_track(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
+
+ clip = None
+ if con.use_active_clip:
+ clip = context.scene.active_clip
else:
- return context.scene.active_clip
+ clip = con.clip
- def FOLLOW_TRACK(self, context, layout, con):
- clip = self._getConstraintClip(context, con)
+ layout.prop(con, "use_active_clip")
+ layout.prop(con, "use_3d_position")
row = layout.row()
- row.prop(con, "use_active_clip")
- row.prop(con, "use_3d_position")
-
- sub = row.column()
- sub.active = not con.use_3d_position
- sub.prop(con, "use_undistorted_position")
+ row.active = not con.use_3d_position
+ row.prop(con, "use_undistorted_position")
- col = layout.column()
if not con.use_active_clip:
- col.prop(con, "clip")
+ layout.prop(con, "clip")
- row = col.row()
- row.prop(con, "frame_method", expand=True)
+ layout.prop(con, "frame_method")
if clip:
tracking = clip.tracking
- col.prop_search(con, "object", tracking, "objects", icon='OBJECT_DATA')
+ layout.prop_search(con, "object", tracking, "objects", icon='OBJECT_DATA')
tracking_object = tracking.objects.get(con.object, tracking.objects[0])
- col.prop_search(con, "track", tracking_object, "tracks", icon='ANIM_DATA')
+ layout.prop_search(con, "track", tracking_object, "tracks", icon='ANIM_DATA')
- col.prop(con, "camera")
+ layout.prop(con, "camera")
- row = col.row()
+ row = layout.row()
row.active = not con.use_3d_position
row.prop(con, "depth_object")
layout.operator("clip.constraint_to_fcurve")
- def CAMERA_SOLVER(self, _context, layout, con):
+ self.draw_influence(layout, con)
+
+ def draw_camera_solver(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
+
layout.prop(con, "use_active_clip")
if not con.use_active_clip:
@@ -915,8 +783,19 @@ class ConstraintButtonsPanel:
layout.operator("clip.constraint_to_fcurve")
- def OBJECT_SOLVER(self, context, layout, con):
- clip = self._getConstraintClip(context, con)
+ self.draw_influence(layout, con)
+
+ def draw_object_solver(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
+
+ clip = None
+ if con.use_active_clip:
+ clip = context.scene.active_clip
+ else:
+ clip = con.clip
layout.prop(con, "use_active_clip")
@@ -934,36 +813,240 @@ class ConstraintButtonsPanel:
layout.operator("clip.constraint_to_fcurve")
- def TRANSFORM_CACHE(self, _context, layout, con):
- layout.label(text="Cache File Properties:")
- box = layout.box()
- box.template_cache_file(con, "cache_file")
+ self.draw_influence(layout, con)
- cache_file = con.cache_file
+ def draw_transform_cache(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
+
+ layout.template_cache_file(con, "cache_file")
- layout.label(text="Constraint Properties:")
- box = layout.box()
+ cache_file = con.cache_file
if cache_file is not None:
- box.prop_search(con, "object_path", cache_file, "object_paths")
+ layout.prop_search(con, "object_path", cache_file, "object_paths")
+
+ self.draw_influence(layout, con)
- def SCRIPT(self, _context, layout, _con):
+ def draw_python_constraint(self, context):
+ layout = self.layout
layout.label(text="Blender 2.6 doesn't support python constraints yet")
- def ARMATURE(self, context, layout, con):
- topcol = layout.column()
- topcol.use_property_split = True
- topcol.operator("constraint.add_target", text="Add Target Bone")
+ def draw_armature(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
+
+ col = layout.column()
+ col.prop(con, "use_deform_preserve_volume")
+ col.prop(con, "use_bone_envelopes")
+
+ if context.pose_bone:
+ col.prop(con, "use_current_location")
+
+ layout.operator("constraint.add_target", text="Add Target Bone")
+
+ layout.operator("constraint.normalize_target_weights")
+
+ self.draw_influence(layout, con)
if not con.targets:
- box = topcol.box()
- box.label(text="No target bones were added", icon='ERROR')
+ layout.label(text="No target bones added", icon='ERROR')
- for i, tgt in enumerate(con.targets):
- box = topcol.box()
+ def draw_kinematic(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
+
+ self.target_template(layout, con)
+
+ if context.object.pose.ik_solver == 'ITASC':
+ layout.prop(con, "ik_type")
+ # This button gives itself too much padding, so put it in a column with the subtarget
+ col = layout.column()
+ col.prop(con, "pole_target")
+
+ if con.pole_target and con.pole_target.type == 'ARMATURE':
+ col.prop_search(con, "pole_subtarget", con.pole_target.data, "bones", text="Bone")
+
+ col = layout.column()
+ if con.pole_target:
+ col.prop(con, "pole_angle")
+ col.prop(con, "use_tail")
+ col.prop(con, "use_stretch")
+ col.prop(con, "chain_count")
+
+ if con.ik_type == 'COPY_POSE':
+ layout.prop(con, "reference_axis", expand=True)
+
+ # Use separate rows and columns here to avoid an alignment issue with the lock buttons
+ loc_col = layout.column()
+ loc_col.prop(con, "use_location")
+
+ row = loc_col.row()
+ row.active = con.use_location
+ row.prop(con, "weight", text="Weight", slider=True)
+
+ row = loc_col.row(heading="Lock", align=True)
+ row.use_property_decorate = False
+ row.active = con.use_location
+ sub = row.row(align=True)
+ sub.prop(con, "lock_location_x", text="X", toggle=True)
+ sub.prop(con, "lock_location_y", text="Y", toggle=True)
+ sub.prop(con, "lock_location_z", text="Z", toggle=True)
+ row.label(icon='BLANK1')
+
+ rot_col = layout.column()
+ rot_col.prop(con, "use_rotation")
+
+ row = rot_col.row()
+ row.active = con.use_rotation
+ row.prop(con, "orient_weight", text="Weight", slider=True)
+
+ row = rot_col.row(heading="Lock", align=True)
+ row.use_property_decorate = False
+ row.active = con.use_rotation
+ sub = row.row(align=True)
+ sub.prop(con, "lock_rotation_x", text="X", toggle=True)
+ sub.prop(con, "lock_rotation_y", text="Y", toggle=True)
+ sub.prop(con, "lock_rotation_z", text="Z", toggle=True)
+ row.label(icon='BLANK1')
+
+ elif con.ik_type == 'DISTANCE':
+ layout.prop(con, "limit_mode")
+
+ col = layout.column()
+ col.prop(con, "weight", text="Weight", slider=True)
+ col.prop(con, "distance", text="Distance", slider=True)
+ else:
+ # Standard IK constraint
+ col = layout.column()
+ col.prop(con, "pole_target")
+
+ if con.pole_target and con.pole_target.type == 'ARMATURE':
+ col.prop_search(con, "pole_subtarget", con.pole_target.data, "bones", text="Bone")
+
+ col = layout.column()
+ if con.pole_target:
+ col.prop(con, "pole_angle")
+ col.prop(con, "iterations")
+ col.prop(con, "chain_count")
+ col.prop(con, "use_tail")
+ col.prop(con, "use_stretch")
+
+ col = layout.column()
+ row = col.row(align=True, heading="Weight Position")
+ row.prop(con, "use_location", text="")
+ sub = row.row(align=True)
+ sub.active = con.use_location
+ sub.prop(con, "weight", text="", slider=True)
+
+ row = col.row(align=True, heading="Rotation")
+ row.prop(con, "use_rotation", text="")
+ sub = row.row(align=True)
+ sub.active = con.use_rotation
+ sub.prop(con, "orient_weight", text="", slider=True)
+
+ self.draw_influence(layout, con)
+
+
+# Parent class for constraint subpanels
+class ConstraintButtonsSubPanel(Panel):
+ bl_space_type = 'PROPERTIES'
+ bl_region_type = 'WINDOW'
+ bl_label = ""
+ bl_options = {'DRAW_BOX'}
+
+ def get_constraint(self, context):
+ con = None
+ if context.pose_bone:
+ con = context.pose_bone.constraints[self.list_panel_index]
+ else:
+ con = context.object.constraints[self.list_panel_index]
+ self.layout.context_pointer_set("constraint", con)
+ return con
+
+ def draw_transform_source(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+
+ layout.prop(con, "map_from", expand=True)
+
+ layout.use_property_split = True
+ layout.use_property_decorate = True
+
+ if con.map_from == 'ROTATION':
+ layout.prop(con, "from_rotation_mode", text="Mode")
+
+ ext = "" if con.map_from == 'LOCATION' else "_rot" if con.map_from == 'ROTATION' else "_scale"
+
+ col = layout.column(align=True)
+ col.prop(con, "from_min_x" + ext, text="X Min")
+ col.prop(con, "from_max_x" + ext, text="Max")
+
+ col = layout.column(align=True)
+ col.prop(con, "from_min_y" + ext, text="Y Min")
+ col.prop(con, "from_max_y" + ext, text="Max")
+
+ col = layout.column(align=True)
+ col.prop(con, "from_min_z" + ext, text="Z Min")
+ col.prop(con, "from_max_z" + ext, text="Max")
+
+ def draw_transform_mapping(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
+
+ layout.prop(con, "map_to_x_from", expand=False, text="Source Axis X")
+
+ layout.prop(con, "map_to_y_from", expand=False, text="Y")
+
+ layout.prop(con, "map_to_z_from", expand=False, text="Z")
+
+ def draw_transform_destination(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+
+ layout.prop(con, "map_to", expand=True)
+
+ layout.use_property_split = True
+ layout.use_property_decorate = True
+
+ if con.map_to == 'ROTATION':
+ layout.prop(con, "to_euler_order", text="Order")
+
+ ext = "" if con.map_to == 'LOCATION' else "_rot" if con.map_to == 'ROTATION' else "_scale"
+
+ col = layout.column(align=True)
+ col.prop(con, "to_min_x" + ext, text="X Min")
+ col.prop(con, "to_max_x" + ext, text="Max")
+
+ col = layout.column(align=True)
+ col.prop(con, "to_min_y" + ext, text="Y Min")
+ col.prop(con, "to_max_y" + ext, text="Max")
+
+ col = layout.column(align=True)
+ col.prop(con, "to_min_z" + ext, text="Z Min")
+ col.prop(con, "to_max_z" + ext, text="Max")
+
+ layout.prop(con, "mix_mode" + ext, text="Mix")
+
+ def draw_armature_bones(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
+
+ for i, tgt in enumerate(con.targets):
has_target = tgt.target is not None
+ box = layout.box()
header = box.row()
header.use_property_split = False
@@ -977,61 +1060,615 @@ class ConstraintButtonsPanel:
else:
row.prop(tgt, "subtarget", text="", icon='BONE_DATA')
- header.operator("constraint.remove_target", text="", icon='REMOVE').index = i
+ header.operator("constraint.remove_target", text="", icon='X').index = i
- col = box.column()
- col.active = has_target and tgt.subtarget != ""
- col.prop(tgt, "weight", slider=True)
+ row = box.row()
+ row.active = has_target and tgt.subtarget != ""
+ row.prop(tgt, "weight", slider=True, text="Weight")
- topcol.operator("constraint.normalize_target_weights")
- topcol.prop(con, "use_deform_preserve_volume")
- topcol.prop(con, "use_bone_envelopes")
+ def draw_spline_ik_fitting(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
- if context.pose_bone:
- topcol.prop(con, "use_current_location")
+ col = layout.column()
+ col.prop(con, "chain_count")
+ col.prop(con, "use_even_divisions")
+ col.prop(con, "use_chain_offset")
+ def draw_spline_ik_chain_scaling(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
-class OBJECT_PT_constraints(ConstraintButtonsPanel, Panel):
- bl_label = "Object Constraints"
- bl_context = "constraint"
- bl_options = {'HIDE_HEADER'}
+ layout.prop(con, "use_curve_radius")
- @classmethod
- def poll(cls, context):
- return (context.object)
+ layout.prop(con, "y_scale_mode")
+ layout.prop(con, "xz_scale_mode")
- def draw(self, context):
+ if con.xz_scale_mode in {'INVERSE_PRESERVE', 'VOLUME_PRESERVE'}:
+ layout.prop(con, "use_original_scale")
+
+ if con.xz_scale_mode == 'VOLUME_PRESERVE':
+ col = layout.column()
+ col.prop(con, "bulge", text="Volume Variation")
+
+ row = col.row(heading="Volume Min")
+ row.prop(con, "use_bulge_min", text="")
+ sub = row.row()
+ sub.active = con.use_bulge_min
+ sub.prop(con, "bulge_min", text="")
+
+ row = col.row(heading="Max")
+ row.prop(con, "use_bulge_max", text="")
+ sub = row.row()
+ sub.active = con.use_bulge_max
+ sub.prop(con, "bulge_max", text="")
+
+ row = layout.row()
+ row.active = con.use_bulge_min or con.use_bulge_max
+ row.prop(con, "bulge_smooth", text="Smooth")
+
+ def draw_action_target(self, context):
layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
- obj = context.object
+ layout.prop(con, "transform_channel", text="Channel")
+ layout.prop(con, "target_space")
- layout.operator_menu_enum("object.constraint_add", "type", text="Add Object Constraint")
+ col = layout.column(align=True)
+ col.prop(con, "min", text="Range Min")
+ col.prop(con, "max", text="Max")
- for con in obj.constraints:
- self.draw_constraint(context, con)
+ def draw_action_action(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
-class BONE_PT_constraints(ConstraintButtonsPanel, Panel):
- bl_label = "Bone Constraints"
- bl_context = "bone_constraint"
- bl_options = {'HIDE_HEADER'}
+ layout.prop(con, "action")
+ layout.prop(con, "use_bone_object_action")
+
+ col = layout.column(align=True)
+ col.prop(con, "frame_start", text="Frame Start")
+ col.prop(con, "frame_end", text="End")
- @classmethod
- def poll(cls, context):
- return (context.pose_bone)
+# Child Of Constraint
+
+class OBJECT_PT_bChildOfConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
def draw(self, context):
- layout = self.layout
+ self.draw_childof(context)
- layout.operator_menu_enum("pose.constraint_add", "type", text="Add Bone Constraint")
- for con in context.pose_bone.constraints:
- self.draw_constraint(context, con)
+class BONE_PT_bChildOfConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_childof(context)
+
+# Track To Constraint
+
+class OBJECT_PT_bTrackToConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_trackto(context)
+
+
+class BONE_PT_bTrackToConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_trackto(context)
+
+# Follow Path Constraint
+
+class OBJECT_PT_bFollowPathConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_follow_path(context)
+
+
+class BONE_PT_bFollowPathConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_follow_path(context)
+
+
+# Rotation Limit Constraint
+
+class OBJECT_PT_bRotLimitConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_rot_limit(context)
+
+
+class BONE_PT_bRotLimitConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_rot_limit(context)
+
+
+# Location Limit Constraint
+
+class OBJECT_PT_bLocLimitConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_loc_limit(context)
+
+
+class BONE_PT_bLocLimitConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_loc_limit(context)
+
+
+# Size Limit Constraint
+
+class OBJECT_PT_bSizeLimitConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_size_limit(context)
+
+
+class BONE_PT_bSizeLimitConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_size_limit(context)
+
+
+# Rotate Like Constraint
+
+class OBJECT_PT_bRotateLikeConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_rotate_like(context)
+
+
+class BONE_PT_bRotateLikeConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_rotate_like(context)
+
+
+# Locate Like Constraint
+
+class OBJECT_PT_bLocateLikeConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_locate_like(context)
+
+
+class BONE_PT_bLocateLikeConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_locate_like(context)
+
+
+# Size Like Constraint
+
+class OBJECT_PT_bSizeLikeConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_size_like(context)
+
+
+class BONE_PT_bSizeLikeConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_size_like(context)
+
+
+# Same Volume Constraint
+
+class OBJECT_PT_bSameVolumeConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_same_volume(context)
+
+
+class BONE_PT_bSameVolumeConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_same_volume(context)
+
+
+# Trans Like Constraint
+
+class OBJECT_PT_bTransLikeConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_trans_like(context)
+
+
+class BONE_PT_bTransLikeConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_trans_like(context)
+
+
+# Action Constraint
+
+class OBJECT_PT_bActionConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_action(context)
+
+
+class BONE_PT_bActionConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_action(context)
+
+
+class OBJECT_PT_bActionConstraint_target(ObjectConstraintPanel, ConstraintButtonsSubPanel):
+ bl_parent_id = "OBJECT_PT_bActionConstraint"
+ bl_label = "Target"
+
+ def draw(self, context):
+ self.draw_action_target(context)
+
+
+class BONE_PT_bActionConstraint_target(BoneConstraintPanel, ConstraintButtonsSubPanel):
+ bl_parent_id = "BONE_PT_bActionConstraint"
+ bl_label = "Target"
+
+ def draw(self, context):
+ self.draw_action_target(context)
+
+
+class OBJECT_PT_bActionConstraint_action(ObjectConstraintPanel, ConstraintButtonsSubPanel):
+ bl_parent_id = "OBJECT_PT_bActionConstraint"
+ bl_label = "Action"
+
+ def draw(self, context):
+ self.draw_action_action(context)
+
+
+class BONE_PT_bActionConstraint_action(BoneConstraintPanel, ConstraintButtonsSubPanel):
+ bl_parent_id = "BONE_PT_bActionConstraint"
+ bl_label = "Action"
+
+ def draw(self, context):
+ self.draw_action_action(context)
+
+
+# Lock Track Constraint
+
+class OBJECT_PT_bLockTrackConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_lock_track(context)
+
+
+class BONE_PT_bLockTrackConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_lock_track(context)
+
+
+# Disance Limit Constraint
+
+class OBJECT_PT_bDistLimitConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_dist_limit(context)
+
+
+class BONE_PT_bDistLimitConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_dist_limit(context)
+
+
+# Stretch To Constraint
+
+class OBJECT_PT_bStretchToConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_stretch_to(context)
+
+
+class BONE_PT_bStretchToConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_stretch_to(context)
+
+
+# Min Max Constraint
+
+class OBJECT_PT_bMinMaxConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_min_max(context)
+
+
+class BONE_PT_bMinMaxConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_min_max(context)
+
+
+# Clamp To Constraint
+
+class OBJECT_PT_bClampToConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_clamp_to(context)
+
+
+class BONE_PT_bClampToConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_clamp_to(context)
+
+
+# Transform Constraint
+
+class OBJECT_PT_bTransformConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_transform(context)
+
+
+class BONE_PT_bTransformConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_transform(context)
+
+
+class OBJECT_PT_bTransformConstraint_source(ObjectConstraintPanel, ConstraintButtonsSubPanel):
+ bl_parent_id = "OBJECT_PT_bTransformConstraint"
+ bl_label = "Source"
+
+ def draw(self, context):
+ self.draw_transform_source(context)
+
+
+class BONE_PT_bTransformConstraint_source(BoneConstraintPanel, ConstraintButtonsSubPanel):
+ bl_parent_id = "BONE_PT_bTransformConstraint"
+ bl_label = "Source"
+
+ def draw(self, context):
+ self.draw_transform_source(context)
+
+
+class OBJECT_PT_bTransformConstraint_mapping(ObjectConstraintPanel, ConstraintButtonsSubPanel):
+ bl_parent_id = "OBJECT_PT_bTransformConstraint"
+ bl_label = "Mapping"
+
+ def draw(self, context):
+ self.draw_transform_mapping(context)
+
+
+class BONE_PT_bTransformConstraint_mapping(BoneConstraintPanel, ConstraintButtonsSubPanel):
+ bl_parent_id = "BONE_PT_bTransformConstraint"
+ bl_label = "Mapping"
+
+ def draw(self, context):
+ self.draw_transform_mapping(context)
+
+
+class OBJECT_PT_bTransformConstraint_destination(ObjectConstraintPanel, ConstraintButtonsSubPanel):
+ bl_parent_id = "OBJECT_PT_bTransformConstraint"
+ bl_label = "Destination"
+
+ def draw(self, context):
+ self.draw_transform_destination(context)
+
+
+class BONE_PT_bTransformConstraint_destination(BoneConstraintPanel, ConstraintButtonsSubPanel):
+ bl_parent_id = "BONE_PT_bTransformConstraint"
+ bl_label = "Destination"
+
+ def draw(self, context):
+ self.draw_transform_destination(context)
+
+
+# Shrinkwrap Constraint
+
+class OBJECT_PT_bShrinkwrapConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_shrinkwrap(context)
+
+
+class BONE_PT_bShrinkwrapConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_shrinkwrap(context)
+
+
+# Damp Track Constraint
+
+class OBJECT_PT_bDampTrackConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_damp_track(context)
+
+
+class BONE_PT_bDampTrackConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_damp_track(context)
+
+
+# Spline IK Constraint
+
+class BONE_PT_bSplineIKConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_spline_ik(context)
+
+
+class BONE_PT_bSplineIKConstraint_fitting(BoneConstraintPanel, ConstraintButtonsSubPanel):
+ bl_parent_id = "BONE_PT_bSplineIKConstraint"
+ bl_label = "Fitting"
+
+ def draw(self, context):
+ self.draw_spline_ik_fitting(context)
+
+
+class BONE_PT_bSplineIKConstraint_chain_scaling(BoneConstraintPanel, ConstraintButtonsSubPanel):
+ bl_parent_id = "BONE_PT_bSplineIKConstraint"
+ bl_label = "Chain Scaling"
+
+ def draw(self, context):
+ self.draw_spline_ik_chain_scaling(context)
+
+
+# Pivot Constraint
+
+class OBJECT_PT_bPivotConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_pivot(context)
+
+
+class BONE_PT_bPivotConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_pivot(context)
+
+
+# Follow Track Constraint
+
+class OBJECT_PT_bFollowTrackConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_follow_track(context)
+
+
+class BONE_PT_bFollowTrackConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_follow_track(context)
+
+
+# Camera Solver Constraint
+
+class OBJECT_PT_bCameraSolverConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_camera_solver(context)
+
+
+class BONE_PT_bCameraSolverConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_camera_solver(context)
+
+
+# Object Solver Constraint
+
+class OBJECT_PT_bObjectSolverConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_object_solver(context)
+
+
+class BONE_PT_bObjectSolverConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_object_solver(context)
+
+
+# Transform Cache Constraint
+
+class OBJECT_PT_bTransformCacheConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_transform_cache(context)
+
+
+class BONE_PT_bTransformCacheConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_transform_cache(context)
+
+
+# Python Constraint
+
+class OBJECT_PT_bPythonConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_python_constraint(context)
+
+class BONE_PT_bPythonConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_python_constraint(context)
+
+
+
+# Armature Constraint
+
+class OBJECT_PT_bArmatureConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_armature(context)
+
+
+class BONE_PT_bArmatureConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_armature(context)
+
+
+class OBJECT_PT_bArmatureConstraint_bones(ObjectConstraintPanel, ConstraintButtonsSubPanel):
+ bl_parent_id = "OBJECT_PT_bArmatureConstraint"
+ bl_label = "Bones"
+
+ def draw(self, context):
+ self.draw_armature_bones(context)
+
+
+class BONE_PT_bArmatureConstraint_bones(BoneConstraintPanel, ConstraintButtonsSubPanel):
+ bl_parent_id = "BONE_PT_bArmatureConstraint"
+ bl_label = "Bones"
+
+ def draw(self, context):
+ self.draw_armature_bones(context)
+
+
+# Inverse Kinematic Constraint
+
+class OBJECT_PT_bKinematicConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_kinematic(context)
+
+
+class BONE_PT_bKinematicConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_kinematic(context)
+
classes = (
+ # Object Panels
OBJECT_PT_constraints,
BONE_PT_constraints,
+ OBJECT_PT_bChildOfConstraint,
+ OBJECT_PT_bTrackToConstraint,
+ OBJECT_PT_bKinematicConstraint,
+ OBJECT_PT_bFollowPathConstraint,
+ OBJECT_PT_bRotLimitConstraint,
+ OBJECT_PT_bLocLimitConstraint,
+ OBJECT_PT_bSizeLimitConstraint,
+ OBJECT_PT_bRotateLikeConstraint,
+ OBJECT_PT_bLocateLikeConstraint,
+ OBJECT_PT_bSizeLikeConstraint,
+ OBJECT_PT_bSameVolumeConstraint,
+ OBJECT_PT_bTransLikeConstraint,
+ OBJECT_PT_bActionConstraint,
+ OBJECT_PT_bActionConstraint_target,
+ OBJECT_PT_bActionConstraint_action,
+ OBJECT_PT_bLockTrackConstraint,
+ OBJECT_PT_bDistLimitConstraint,
+ OBJECT_PT_bStretchToConstraint,
+ OBJECT_PT_bMinMaxConstraint,
+ OBJECT_PT_bClampToConstraint,
+ OBJECT_PT_bTransformConstraint,
+ OBJECT_PT_bTransformConstraint_source,
+ OBJECT_PT_bTransformConstraint_mapping,
+ OBJECT_PT_bTransformConstraint_destination,
+ OBJECT_PT_bShrinkwrapConstraint,
+ OBJECT_PT_bDampTrackConstraint,
+ OBJECT_PT_bPivotConstraint,
+ OBJECT_PT_bFollowTrackConstraint,
+ OBJECT_PT_bCameraSolverConstraint,
+ OBJECT_PT_bObjectSolverConstraint,
+ OBJECT_PT_bTransformCacheConstraint,
+ OBJECT_PT_bPythonConstraint,
+ OBJECT_PT_bArmatureConstraint,
+ OBJECT_PT_bArmatureConstraint_bones,
+ # Bone panels
+ BONE_PT_bChildOfConstraint,
+ BONE_PT_bTrackToConstraint,
+ BONE_PT_bKinematicConstraint,
+ BONE_PT_bFollowPathConstraint,
+ BONE_PT_bRotLimitConstraint,
+ BONE_PT_bLocLimitConstraint,
+ BONE_PT_bSizeLimitConstraint,
+ BONE_PT_bRotateLikeConstraint,
+ BONE_PT_bLocateLikeConstraint,
+ BONE_PT_bSizeLikeConstraint,
+ BONE_PT_bSameVolumeConstraint,
+ BONE_PT_bTransLikeConstraint,
+ BONE_PT_bActionConstraint,
+ BONE_PT_bActionConstraint_target,
+ BONE_PT_bActionConstraint_action,
+ BONE_PT_bLockTrackConstraint,
+ BONE_PT_bDistLimitConstraint,
+ BONE_PT_bStretchToConstraint,
+ BONE_PT_bMinMaxConstraint,
+ BONE_PT_bClampToConstraint,
+ BONE_PT_bTransformConstraint,
+ BONE_PT_bTransformConstraint_source,
+ BONE_PT_bTransformConstraint_mapping,
+ BONE_PT_bTransformConstraint_destination,
+ BONE_PT_bShrinkwrapConstraint,
+ BONE_PT_bDampTrackConstraint,
+ BONE_PT_bSplineIKConstraint,
+ BONE_PT_bSplineIKConstraint_fitting,
+ BONE_PT_bSplineIKConstraint_chain_scaling,
+ BONE_PT_bPivotConstraint,
+ BONE_PT_bFollowTrackConstraint,
+ BONE_PT_bCameraSolverConstraint,
+ BONE_PT_bObjectSolverConstraint,
+ BONE_PT_bTransformCacheConstraint,
+ BONE_PT_bPythonConstraint,
+ BONE_PT_bArmatureConstraint,
+ BONE_PT_bArmatureConstraint_bones,
)
if __name__ == "__main__": # only for live edit.
diff --git a/release/scripts/startup/bl_ui/properties_data_camera.py b/release/scripts/startup/bl_ui/properties_data_camera.py
index 62dffa3b6ba..3de144c5a15 100644
--- a/release/scripts/startup/bl_ui/properties_data_camera.py
+++ b/release/scripts/startup/bl_ui/properties_data_camera.py
@@ -84,7 +84,6 @@ class DATA_PT_lens(CameraButtonsPanel, Panel):
col.separator()
if cam.type == 'PERSP':
- col = layout.column()
if cam.lens_unit == 'MILLIMETERS':
col.prop(cam, "lens")
elif cam.lens_unit == 'FOV':
diff --git a/release/scripts/startup/bl_ui/properties_data_gpencil.py b/release/scripts/startup/bl_ui/properties_data_gpencil.py
index 4ed5264549f..946578937bb 100644
--- a/release/scripts/startup/bl_ui/properties_data_gpencil.py
+++ b/release/scripts/startup/bl_ui/properties_data_gpencil.py
@@ -214,10 +214,6 @@ class DATA_PT_gpencil_onion_skinning(DataButtonsPanel, Panel):
layout = self.layout
layout.use_property_split = True
- layout.enabled = gpd.users <= 1
-
- if gpd.users > 1:
- layout.label(text="Multiuser datablock not supported", icon='ERROR')
col = layout.column()
col.prop(gpd, "onion_mode")
diff --git a/release/scripts/startup/bl_ui/properties_data_mesh.py b/release/scripts/startup/bl_ui/properties_data_mesh.py
index 425c94dfdcd..fbd8e2d7cff 100644
--- a/release/scripts/startup/bl_ui/properties_data_mesh.py
+++ b/release/scripts/startup/bl_ui/properties_data_mesh.py
@@ -459,6 +459,31 @@ class DATA_PT_vertex_colors(MeshButtonsPanel, Panel):
col.operator("mesh.vertex_color_remove", icon='REMOVE', text="")
+class DATA_PT_sculpt_vertex_colors(MeshButtonsPanel, Panel):
+ bl_label = "Sculpt Vertex Colors"
+ bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
+
+ def draw(self, context):
+ layout = self.layout
+
+ me = context.mesh
+
+ row = layout.row()
+ col = row.column()
+
+ col.template_list("MESH_UL_vcols", "svcols", me, "sculpt_vertex_colors", me.sculpt_vertex_colors, "active_index", rows=2)
+
+ col = row.column(align=True)
+ col.operator("mesh.sculpt_vertex_color_add", icon='ADD', text="")
+ col.operator("mesh.sculpt_vertex_color_remove", icon='REMOVE', text="")
+
+ row = layout.row()
+ col = row.column()
+ col.operator("sculpt.vertex_to_loop_colors", text="Store Sculpt Vertex Color")
+ col.operator("sculpt.loop_to_vertex_colors", text="Load Sculpt Vertex Color")
+
+
class DATA_PT_remesh(MeshButtonsPanel, Panel):
bl_label = "Remesh"
bl_options = {'DEFAULT_CLOSED'}
@@ -483,6 +508,8 @@ class DATA_PT_remesh(MeshButtonsPanel, Panel):
col.prop(mesh, "use_remesh_preserve_volume", text="Volume")
col.prop(mesh, "use_remesh_preserve_paint_mask", text="Paint Mask")
col.prop(mesh, "use_remesh_preserve_sculpt_face_sets", text="Face Sets")
+ col.prop(mesh, "use_remesh_preserve_vertex_colors", text="Vertex Colors")
+
col.operator("object.voxel_remesh", text="Voxel Remesh")
else:
col.operator("object.quadriflow_remesh", text="QuadriFlow Remesh")
@@ -537,6 +564,7 @@ classes = (
DATA_PT_shape_keys,
DATA_PT_uv_texture,
DATA_PT_vertex_colors,
+ DATA_PT_sculpt_vertex_colors,
DATA_PT_face_maps,
DATA_PT_normals,
DATA_PT_texture_space,
diff --git a/release/scripts/startup/bl_ui/properties_data_modifier.py b/release/scripts/startup/bl_ui/properties_data_modifier.py
index e7536838199..d464a3ffc6b 100644
--- a/release/scripts/startup/bl_ui/properties_data_modifier.py
+++ b/release/scripts/startup/bl_ui/properties_data_modifier.py
@@ -46,13 +46,6 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel):
class DATA_PT_gpencil_modifiers(ModifierButtonsPanel, Panel):
bl_label = "Modifiers"
- def check_conflicts(self, layout, ob):
- for md in ob.grease_pencil_modifiers:
- if md.type == 'GP_TIME':
- row = layout.row()
- row.label(text="Build and Time Offset modifier not compatible", icon='ERROR')
- break
-
@classmethod
def poll(cls, context):
ob = context.object
@@ -60,483 +53,8 @@ class DATA_PT_gpencil_modifiers(ModifierButtonsPanel, Panel):
def draw(self, context):
layout = self.layout
-
- ob = context.object
-
layout.operator_menu_enum("object.gpencil_modifier_add", "type")
-
- for md in ob.grease_pencil_modifiers:
- box = layout.template_greasepencil_modifier(md)
- if box:
- # match enum type to our functions, avoids a lookup table.
- getattr(self, md.type)(box, ob, md)
-
- # the mt.type enum is (ab)used for a lookup on function names
- # ...to avoid lengthy if statements
- # so each type must have a function here.
-
- def gpencil_masking(self, layout, ob, md, use_vertex, use_curve=False):
- gpd = ob.data
- layout.separator()
- layout.label(text="Influence Filters:")
-
- split = layout.split(factor=0.25)
-
- col1 = split.column()
-
- col1.label(text="Layer:")
- col1.label(text="Material:")
- if use_vertex:
- col1.label(text="Vertex Group:")
-
- col2 = split.column()
-
- split = col2.split(factor=0.6)
- row = split.row(align=True)
- row.prop_search(md, "layer", gpd, "layers", text="", icon='GREASEPENCIL')
- row.prop(md, "invert_layers", text="", icon='ARROW_LEFTRIGHT')
-
- row = split.row(align=True)
- row.prop(md, "layer_pass", text="Pass")
- row.prop(md, "invert_layer_pass", text="", icon='ARROW_LEFTRIGHT')
-
- split = col2.split(factor=0.6)
-
- row = split.row(align=True)
-
- valid = md.material in (slot.material for slot in ob.material_slots) or md.material is None
- if valid:
- icon = 'SHADING_TEXTURE'
- else:
- icon = 'ERROR'
-
- row.alert = not valid
- row.prop_search(md, "material", gpd, "materials", text="", icon=icon)
- row.prop(md, "invert_materials", text="", icon='ARROW_LEFTRIGHT')
-
- row = split.row(align=True)
- row.prop(md, "pass_index", text="Pass")
- row.prop(md, "invert_material_pass", text="", icon='ARROW_LEFTRIGHT')
-
- if use_vertex:
- row = col2.row(align=True)
- row.prop_search(md, "vertex_group", ob, "vertex_groups", text="")
- row.prop(md, "invert_vertex", text="", icon='ARROW_LEFTRIGHT')
-
- if use_curve:
- col = layout.column()
- col.separator()
- col.prop(md, "use_custom_curve")
- if md.use_custom_curve:
- col.template_curve_mapping(md, "curve")
-
- def GP_NOISE(self, layout, ob, md):
- split = layout.split()
-
- col = split.column()
- row = col.row(align=True)
- row.prop(md, "factor", text="Position")
- row = col.row(align=True)
- row.prop(md, "factor_strength", text="Strength")
- row = col.row(align=True)
- row.prop(md, "factor_thickness", text="Thickness")
- row = col.row(align=True)
- row.prop(md, "factor_uvs", text="UV")
-
- col.separator()
- row = col.row(align=True)
- row.prop(md, "random", text="", icon='TIME', toggle=True)
-
- subrow = row.row(align=True)
- subrow.enabled = md.random
- subrow.prop(md, "step")
- subrow.prop(md, "seed")
-
- col.separator()
- col.prop(md, "noise_scale")
-
- self.gpencil_masking(layout, ob, md, True, True)
-
- def GP_SMOOTH(self, layout, ob, md):
- col = layout.column()
- col.prop(md, "factor")
- col.prop(md, "step", text="Repeat")
-
- col.label(text="Affect:")
- row = col.row(align=True)
- row.prop(md, "use_edit_position", text="Position", toggle=True)
- row.prop(md, "use_edit_strength", text="Strength", toggle=True)
- row.prop(md, "use_edit_thickness", text="Thickness", toggle=True)
- row.prop(md, "use_edit_uv", text="UV", toggle=True)
-
- self.gpencil_masking(layout, ob, md, True, True)
-
- def GP_SUBDIV(self, layout, ob, md):
- layout.row().prop(md, "subdivision_type", expand=True)
- split = layout.split()
- col = split.column()
- row = col.row(align=True)
- row.prop(md, "level", text="Subdivisions")
-
- self.gpencil_masking(layout, ob, md, False)
-
- def GP_SIMPLIFY(self, layout, ob, md):
- gpd = ob.data
-
- row = layout.row()
- row.prop(md, "mode")
-
- split = layout.split()
-
- col = split.column()
- col.label(text="Settings:")
-
- if md.mode == 'FIXED':
- col.prop(md, "step")
- elif md.mode == 'ADAPTIVE':
- col.prop(md, "factor")
- elif md.mode == 'SAMPLE':
- col.prop(md, "length")
- elif md.mode == 'MERGE':
- col.prop(md, "distance")
-
- self.gpencil_masking(layout, ob, md, False)
-
- def GP_THICK(self, layout, ob, md):
- col = layout.column()
-
- col.prop(md, "normalize_thickness")
-
- if md.normalize_thickness:
- col.prop(md, "thickness")
- else:
- col.prop(md, "thickness_factor")
-
- self.gpencil_masking(layout, ob, md, True, True)
-
- def GP_TEXTURE(self, layout, ob, md):
- col = layout.column()
-
- col.prop(md, "mode")
- if md.mode in {'STROKE', 'STROKE_AND_FILL'}:
- col.label(text="Stroke Texture:")
- col.prop(md, "fit_method")
- col.prop(md, "uv_offset")
- col.prop(md, "uv_scale")
-
- if md.mode == 'STROKE_AND_FILL':
- col.separator()
-
- if md.mode in {'FILL', 'STROKE_AND_FILL'}:
- col.label(text="Fill Texture:")
- col.prop(md, "fill_rotation", text="Rotation")
- col.prop(md, "fill_offset", text="Location")
- col.prop(md, "fill_scale", text="Scale")
-
- self.gpencil_masking(layout, ob, md, True)
-
- def GP_TINT(self, layout, ob, md):
- layout.row().prop(md, "tint_type", expand=True)
-
- if md.tint_type == 'UNIFORM':
- col = layout.column()
- col.prop(md, "color")
-
- col.separator()
- col.prop(md, "factor")
-
- if md.tint_type == 'GRADIENT':
- col = layout.column()
- col.label(text="Colors:")
- col.template_color_ramp(md, "colors")
-
- col.separator()
-
- col.label(text="Object:")
- col.prop(md, "object", text="")
-
- col.separator()
- row = col.row(align=True)
- row.prop(md, "radius")
- row.prop(md, "factor")
-
- col.separator()
- col.prop(md, "vertex_mode")
-
- self.gpencil_masking(layout, ob, md, True, True)
-
- def GP_TIME(self, layout, ob, md):
- gpd = ob.data
-
- row = layout.row()
- row.prop(md, "mode", text="Mode")
-
- row = layout.row()
- if md.mode == 'FIX':
- txt = "Frame"
- else:
- txt = "Frame Offset"
- row.prop(md, "offset", text=txt)
-
- row = layout.row()
- row.enabled = md.mode != 'FIX'
- row.prop(md, "frame_scale")
-
- row = layout.row()
- row.separator()
-
- row = layout.row()
- row.enabled = md.mode != 'FIX'
- row.prop(md, "use_custom_frame_range")
-
- row = layout.row(align=True)
- row.enabled = md.mode != 'FIX' and md.use_custom_frame_range is True
- row.prop(md, "frame_start")
- row.prop(md, "frame_end")
-
- row = layout.row()
- row.enabled = md.mode != 'FIX'
- row.prop(md, "use_keep_loop")
-
- row = layout.row()
- row.label(text="Layer:")
- row = layout.row(align=True)
- row.prop_search(md, "layer", gpd, "layers", text="", icon='GREASEPENCIL')
- row.prop(md, "invert_layers", text="", icon='ARROW_LEFTRIGHT')
-
- row = layout.row(align=True)
- row.prop(md, "layer_pass", text="Pass")
- row.prop(md, "invert_layer_pass", text="", icon='ARROW_LEFTRIGHT')
-
- def GP_COLOR(self, layout, ob, md):
- split = layout.split()
-
- col = split.column()
- col.label(text="Color:")
- col.prop(md, "hue", text="H", slider=True)
- col.prop(md, "saturation", text="S", slider=True)
- col.prop(md, "value", text="V", slider=True)
-
- row = layout.row()
- row.prop(md, "modify_color")
-
- self.gpencil_masking(layout, ob, md, False, True)
-
- def GP_OPACITY(self, layout, ob, md):
- split = layout.split()
-
- col = split.column()
- col.prop(md, "modify_color")
-
- if md.modify_color == 'HARDNESS':
- col.prop(md, "hardness")
- show = False
- else:
- col.prop(md, "normalize_opacity")
- if md.normalize_opacity is True:
- text="Strength"
- else:
- text="Opacity Factor"
-
- col.prop(md, "factor", text=text)
- show = True
- self.gpencil_masking(layout, ob, md, show, show)
-
- def GP_ARRAY(self, layout, ob, md):
- col = layout.column()
- col.prop(md, "count")
-
- split = layout.split()
- col = split.column()
- col.prop(md, "use_constant_offset", text="Constant Offset")
- subcol = col.column()
- subcol.enabled = md.use_constant_offset
- subcol.prop(md, "constant_offset", text="")
-
- col.prop(md, "use_object_offset")
- subcol = col.column()
- subcol.enabled = md.use_object_offset
- subcol.prop(md, "offset_object", text="")
-
- col = split.column()
- col.prop(md, "use_relative_offset", text="Relative Offset")
- subcol = col.column()
- subcol.enabled = md.use_relative_offset
- subcol.prop(md, "relative_offset", text="")
-
- split = layout.split()
- col = split.column()
- col.label(text="Random Offset:")
- col.prop(md, "random_offset", text="")
-
- col = split.column()
- col.label(text="Random Rotation:")
- col.prop(md, "random_rotation", text="")
-
- col = split.column()
- col.label(text="Random Scale:")
- col.prop(md, "random_scale", text="")
-
- col = layout.column()
- col.prop(md, "seed")
- col.separator()
- col.prop(md, "replace_material", text="Material Override")
-
- self.gpencil_masking(layout, ob, md, False)
-
- def GP_BUILD(self, layout, ob, md):
- gpd = ob.data
-
- split = layout.split()
-
- col = split.column()
- self.check_conflicts(col, ob)
-
- col.prop(md, "mode")
- if md.mode == 'CONCURRENT':
- col.prop(md, "concurrent_time_alignment")
-
- col.separator()
- col.prop(md, "transition")
- sub = col.column(align=True)
- sub.prop(md, "start_delay")
- sub.prop(md, "length")
-
- col = layout.column(align=True)
- col.prop(md, "use_restrict_frame_range")
- sub = col.column(align=True)
- sub.active = md.use_restrict_frame_range
- sub.prop(md, "frame_start", text="Start")
- sub.prop(md, "frame_end", text="End")
-
- col.prop(md, "use_percentage")
- sub = col.column(align=True)
- sub.active = md.use_percentage
- sub.prop(md, "percentage_factor")
-
- layout.label(text="Influence Filters:")
-
- split = layout.split(factor=0.25)
-
- col1 = split.column()
-
- col1.label(text="Layer:")
-
- col2 = split.column()
-
- split = col2.split(factor=0.6)
- row = split.row(align=True)
- row.prop_search(md, "layer", gpd, "layers", text="", icon='GREASEPENCIL')
- row.prop(md, "invert_layers", text="", icon='ARROW_LEFTRIGHT')
-
- row = split.row(align=True)
- row.prop(md, "layer_pass", text="Pass")
- row.prop(md, "invert_layer_pass", text="", icon='ARROW_LEFTRIGHT')
-
- def GP_LATTICE(self, layout, ob, md):
- split = layout.split()
-
- col = split.column()
- col.label(text="Object:")
- col.prop(md, "object", text="")
-
- layout.prop(md, "strength", slider=True)
-
- self.gpencil_masking(layout, ob, md, True)
-
- def GP_MIRROR(self, layout, ob, md):
- row = layout.row(align=True)
- row.prop(md, "x_axis")
- row.prop(md, "y_axis")
- row.prop(md, "z_axis")
-
- layout.label(text="Mirror Object:")
- layout.prop(md, "object", text="")
-
- self.gpencil_masking(layout, ob, md, False)
-
- def GP_HOOK(self, layout, ob, md):
- split = layout.split()
-
- col = split.column()
- col.label(text="Object:")
- col.prop(md, "object", text="")
- if md.object and md.object.type == 'ARMATURE':
- col.label(text="Bone:")
- col.prop_search(md, "subtarget", md.object.data, "bones", text="")
-
- use_falloff = (md.falloff_type != 'NONE')
-
- layout.separator()
-
- row = layout.row(align=True)
- if use_falloff:
- row.prop(md, "falloff_radius")
- row.prop(md, "strength", slider=True)
- layout.prop(md, "falloff_type")
-
- col = layout.column()
- if use_falloff:
- if md.falloff_type == 'CURVE':
- col.template_curve_mapping(md, "falloff_curve")
-
- split = layout.split()
-
- col = split.column()
- col.prop(md, "use_falloff_uniform")
-
- self.gpencil_masking(layout, ob, md, True)
-
- def GP_OFFSET(self, layout, ob, md):
- split = layout.split()
-
- split.column().prop(md, "location")
- split.column().prop(md, "rotation")
- split.column().prop(md, "scale")
-
- self.gpencil_masking(layout, ob, md, True)
-
- def GP_ARMATURE(self, layout, ob, md):
- split = layout.split()
-
- col = split.column()
- col.label(text="Object:")
- col.prop(md, "object", text="")
- # col.prop(md, "use_deform_preserve_volume")
-
- col = split.column()
- col.label(text="Bind To:")
- col.prop(md, "use_vertex_groups", text="Vertex Groups")
- col.prop(md, "use_bone_envelopes", text="Bone Envelopes")
-
- layout.separator()
-
- row = layout.row(align=True)
- row.label(text="Vertex Group:")
- row = layout.row(align=True)
- row.prop_search(md, "vertex_group", ob, "vertex_groups", text="")
- sub = row.row(align=True)
- sub.active = bool(md.vertex_group)
- sub.prop(md, "invert_vertex_group", text="", icon='ARROW_LEFTRIGHT')
-
- def GP_MULTIPLY(self, layout, ob, md):
- col = layout.column()
-
- col.prop(md, "duplicates")
- subcol = col.column()
- subcol.enabled = md.duplicates > 0
- subcol.prop(md, "distance")
- subcol.prop(md, "offset", slider=True)
-
- subcol.separator()
-
- subcol.prop(md, "use_fade")
- if md.use_fade:
- subcol.prop(md, "fading_center")
- subcol.prop(md, "fading_thickness", slider=True)
- subcol.prop(md, "fading_opacity", slider=True)
-
- self.gpencil_masking(layout, ob, md, False)
+ layout.template_grease_pencil_modifiers()
classes = (
diff --git a/release/scripts/startup/bl_ui/properties_data_shaderfx.py b/release/scripts/startup/bl_ui/properties_data_shaderfx.py
index 1d4bf37b282..a96fef018c7 100644
--- a/release/scripts/startup/bl_ui/properties_data_shaderfx.py
+++ b/release/scripts/startup/bl_ui/properties_data_shaderfx.py
@@ -24,11 +24,11 @@ class ShaderFxButtonsPanel:
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "shaderfx"
- bl_options = {'HIDE_HEADER'}
class DATA_PT_shader_fx(ShaderFxButtonsPanel, Panel):
bl_label = "Effects"
+ bl_options = {'HIDE_HEADER'}
# Unused: always show for now.
@@ -39,122 +39,8 @@ class DATA_PT_shader_fx(ShaderFxButtonsPanel, Panel):
def draw(self, context):
layout = self.layout
-
- ob = context.object
-
layout.operator_menu_enum("object.shaderfx_add", "type")
-
- for fx in ob.shader_effects:
- box = layout.template_shaderfx(fx)
- if box:
- # match enum type to our functions, avoids a lookup table.
- getattr(self, fx.type)(box, fx)
-
- # the mt.type enum is (ab)used for a lookup on function names
- # ...to avoid lengthy if statements
- # so each type must have a function here.
-
- def FX_BLUR(self, layout, fx):
-
- layout.prop(fx, "use_dof_mode", text="Use Depth of Field")
- layout.separator()
-
- col = layout.column()
- col.enabled = not fx.use_dof_mode
- col.prop(fx, "size", text="Size")
- col.separator()
- col.prop(fx, "rotation")
-
- layout.prop(fx, "samples", text="Samples")
-
-
- def FX_COLORIZE(self, layout, fx):
- layout.prop(fx, "mode", text="Mode")
-
- if fx.mode == 'DUOTONE':
- layout.prop(fx, "low_color", text="Low Color")
- if fx.mode == 'CUSTOM':
- layout.prop(fx, "low_color", text="Color")
-
- if fx.mode == 'DUOTONE':
- layout.prop(fx, "high_color", text="High Color")
-
- layout.prop(fx, "factor")
-
- def FX_WAVE(self, layout, fx):
- row = layout.row(align=True)
- row.prop(fx, "orientation", expand=True)
-
- layout.separator()
- layout.prop(fx, "amplitude")
- layout.prop(fx, "period")
- layout.prop(fx, "phase")
-
- def FX_PIXEL(self, layout, fx):
- layout.prop(fx, "size", text="Size")
-
- def FX_RIM(self, layout, fx):
- layout.prop(fx, "offset", text="Offset")
-
- layout.prop(fx, "rim_color")
- layout.prop(fx, "mask_color")
- layout.prop(fx, "mode", text="Blend")
- layout.prop(fx, "blur")
- layout.prop(fx, "samples")
-
- def FX_SHADOW(self, layout, fx):
- layout.prop(fx, "offset", text="Offset")
-
- layout.prop(fx, "shadow_color")
- layout.prop(fx, "scale")
- layout.prop(fx, "rotation")
-
- layout.separator()
- layout.prop(fx, "blur")
- layout.prop(fx, "samples")
-
- layout.separator()
- layout.prop(fx, "use_object", text="Use Object as Pivot")
- if fx.use_object:
- row = layout.row()
- row.prop(fx, "object", text="Object")
-
- layout.separator()
- layout.prop(fx, "use_wave", text="Use Wave Effect")
- if fx.use_wave is True:
- row = layout.row(align=True)
- row.prop(fx, "orientation", expand=True)
- layout.prop(fx, "amplitude")
- layout.prop(fx, "period")
- layout.prop(fx, "phase")
-
- def FX_GLOW(self, layout, fx):
- layout.prop(fx, "mode")
- if fx.mode == 'LUMINANCE':
- layout.prop(fx, "threshold")
- else:
- layout.prop(fx, "select_color")
-
- layout.prop(fx, "glow_color")
- layout.separator()
- layout.prop(fx, "blend_mode", text="Blend")
- layout.prop(fx, "opacity")
-
- layout.prop(fx, "size")
- layout.prop(fx, "rotation")
- layout.prop(fx, "samples")
-
- layout.prop(fx, "use_glow_under", text="Glow Under")
-
- def FX_SWIRL(self, layout, fx):
- layout.prop(fx, "object", text="Object")
-
- layout.prop(fx, "radius")
- layout.prop(fx, "angle")
-
- def FX_FLIP(self, layout, fx):
- layout.prop(fx, "flip_horizontal")
- layout.prop(fx, "flip_vertical")
+ layout.template_shaderfx()
classes = (
diff --git a/release/scripts/startup/bl_ui/properties_output.py b/release/scripts/startup/bl_ui/properties_output.py
index 5f0fd3374d2..69c557d336f 100644
--- a/release/scripts/startup/bl_ui/properties_output.py
+++ b/release/scripts/startup/bl_ui/properties_output.py
@@ -411,6 +411,8 @@ class RENDER_PT_encoding_audio(RenderOutputButtonsPanel, Panel):
layout.prop(ffmpeg, "audio_codec", text="Audio Codec")
if ffmpeg.audio_codec != 'NONE':
+ layout.prop(ffmpeg, "audio_channels")
+ layout.prop(ffmpeg, "audio_mixrate", text="Sample Rate")
layout.prop(ffmpeg, "audio_bitrate")
layout.prop(ffmpeg, "audio_volume", slider=True)
diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py
index 1612cce3c51..209231cacb0 100644
--- a/release/scripts/startup/bl_ui/properties_paint_common.py
+++ b/release/scripts/startup/bl_ui/properties_paint_common.py
@@ -607,6 +607,16 @@ def brush_settings(layout, context, brush, popover=False):
layout.operator("sculpt.set_persistent_base")
layout.separator()
+ if capabilities.has_color:
+ ups = context.scene.tool_settings.unified_paint_settings
+ row = layout.row(align=True)
+ UnifiedPaintPanel.prop_unified_color(row, context, brush, "color", text="")
+ UnifiedPaintPanel.prop_unified_color(row, context, brush, "secondary_color", text="")
+ row.separator()
+ row.operator("paint.brush_colors_flip", icon='FILE_REFRESH', text="", emboss=False)
+ row.prop(ups, "use_unified_color", text="", icon='BRUSHES_ALL')
+ layout.prop(brush, "blend", text="Blend Mode")
+
if brush.sculpt_tool == 'CLAY_STRIPS':
row = layout.row()
row.prop(brush, "tip_roundness")
@@ -623,9 +633,13 @@ def brush_settings(layout, context, brush, popover=False):
layout.prop(brush, "pose_origin_type")
layout.prop(brush, "pose_offset")
layout.prop(brush, "pose_smooth_iterations")
- if brush.pose_deform_type == 'ROTATE_TWIST' and brush.pose_origin_type in ('TOPOLOGY','FACE_SETS'):
+ if brush.pose_deform_type == 'ROTATE_TWIST' and brush.pose_origin_type in {'TOPOLOGY', 'FACE_SETS'}:
layout.prop(brush, "pose_ik_segments")
layout.prop(brush, "use_pose_ik_anchored")
+ layout.prop(brush, "use_connected_only")
+ layout.prop(brush, "disconnected_distance_max")
+
+
layout.separator()
if brush.sculpt_tool == 'CLOTH':
@@ -655,6 +669,15 @@ def brush_settings(layout, context, brush, popover=False):
if brush.sculpt_tool == 'GRAB':
layout.prop(brush, "use_grab_active_vertex")
+ if brush.sculpt_tool == 'PAINT':
+ col = layout.column()
+ col.prop(brush, "flow")
+ col.prop(brush, "wet_mix")
+ col.prop(brush, "wet_persistence")
+ col.prop(brush, "density")
+ col.prop(brush, "tip_roundness")
+ col.prop(brush, "tip_scale_x")
+
if brush.sculpt_tool == 'MULTIPLANE_SCRAPE':
col = layout.column()
col.prop(brush, "multiplane_scrape_angle")
diff --git a/release/scripts/startup/bl_ui/properties_physics_common.py b/release/scripts/startup/bl_ui/properties_physics_common.py
index 210d850ad06..db33fda3b17 100644
--- a/release/scripts/startup/bl_ui/properties_physics_common.py
+++ b/release/scripts/startup/bl_ui/properties_physics_common.py
@@ -330,6 +330,7 @@ def basic_force_field_settings_ui(self, field):
col.prop(field, "use_gravity_falloff", text="Gravitation")
col.prop(field, "use_absorption")
+ col.prop(field, "wind_factor")
def basic_force_field_falloff_ui(self, field):
diff --git a/release/scripts/startup/bl_ui/properties_physics_fluid.py b/release/scripts/startup/bl_ui/properties_physics_fluid.py
index 833cd05dd81..0bfa70b008b 100644
--- a/release/scripts/startup/bl_ui/properties_physics_fluid.py
+++ b/release/scripts/startup/bl_ui/properties_physics_fluid.py
@@ -210,10 +210,10 @@ class PHYSICS_PT_settings(PhysicButtonsPanel, Panel):
note.label(icon='INFO', text="Unbaked Guides: Bake Guides or disable them")
split = layout.split()
- split.enabled = note_flag
+ split.enabled = note_flag and ob.mode == 'OBJECT'
bake_incomplete = (domain.cache_frame_pause_data < domain.cache_frame_end)
- if domain.has_cache_baked_data and not domain.is_cache_baking_data and bake_incomplete:
+ if domain.cache_resumable and domain.has_cache_baked_data and not domain.is_cache_baking_data and bake_incomplete:
col = split.column()
col.operator("fluid.bake_data", text="Resume")
col = split.column()
@@ -705,6 +705,7 @@ class PHYSICS_PT_noise(PhysicButtonsPanel, Panel):
layout = self.layout
layout.use_property_split = True
+ ob = context.object
domain = context.fluid.domain_settings
layout.active = domain.use_noise
@@ -736,7 +737,7 @@ class PHYSICS_PT_noise(PhysicButtonsPanel, Panel):
note.label(icon='INFO', text="Unbaked Data: Bake Data first")
split = layout.split()
- split.enabled = domain.has_cache_baked_data and note_flag
+ split.enabled = domain.has_cache_baked_data and note_flag and ob.mode == 'OBJECT'
bake_incomplete = (domain.cache_frame_pause_noise < domain.cache_frame_end)
if domain.has_cache_baked_noise and not domain.is_cache_baking_noise and bake_incomplete:
@@ -777,6 +778,7 @@ class PHYSICS_PT_mesh(PhysicButtonsPanel, Panel):
layout = self.layout
layout.use_property_split = True
+ ob = context.object
domain = context.fluid.domain_settings
layout.active = domain.use_mesh
@@ -821,7 +823,7 @@ class PHYSICS_PT_mesh(PhysicButtonsPanel, Panel):
note.label(icon='INFO', text="Unbaked Data: Bake Data first")
split = layout.split()
- split.enabled = domain.has_cache_baked_data and note_flag
+ split.enabled = domain.has_cache_baked_data and note_flag and ob.mode == 'OBJECT'
bake_incomplete = (domain.cache_frame_pause_mesh < domain.cache_frame_end)
if domain.has_cache_baked_mesh and not domain.is_cache_baking_mesh and bake_incomplete:
@@ -855,6 +857,7 @@ class PHYSICS_PT_particles(PhysicButtonsPanel, Panel):
layout = self.layout
layout.use_property_split = True
+ ob = context.object
domain = context.fluid.domain_settings
is_baking_any = domain.is_cache_baking_any
@@ -936,6 +939,7 @@ class PHYSICS_PT_particles(PhysicButtonsPanel, Panel):
split = layout.split()
split.enabled = (
note_flag and
+ ob.mode == 'OBJECT' and
domain.has_cache_baked_data and
(domain.use_spray_particles or
domain.use_bubble_particles or
@@ -1114,14 +1118,13 @@ class PHYSICS_PT_cache(PhysicButtonsPanel, Panel):
def draw(self, context):
layout = self.layout
+ ob = context.object
md = context.fluid
domain = context.fluid.domain_settings
is_baking_any = domain.is_cache_baking_any
has_baked_data = domain.has_cache_baked_data
- has_baked_noise = domain.has_cache_baked_noise
has_baked_mesh = domain.has_cache_baked_mesh
- has_baked_particles = domain.has_cache_baked_particles
col = layout.column()
col.prop(domain, "cache_directory", text="")
@@ -1146,35 +1149,33 @@ class PHYSICS_PT_cache(PhysicButtonsPanel, Panel):
col.separator()
col = flow.column()
+ col.enabled = not is_baking_any and not has_baked_data
+ col.prop(domain, "cache_resumable", text="Is Resumable")
+
row = col.row()
row.enabled = not is_baking_any and not has_baked_data
- row.prop(domain, "cache_data_format", text="Data File Format")
-
- if md.domain_settings.domain_type in {'GAS'}:
- if domain.use_noise:
- row = col.row()
- row.enabled = not is_baking_any and not has_baked_noise
- row.prop(domain, "cache_noise_format", text="Noise File Format")
+ row.prop(domain, "cache_data_format", text="Format Volumes")
- if md.domain_settings.domain_type in {'LIQUID'}:
- # File format for all particle systemes (FLIP and secondary)
+ if md.domain_settings.domain_type in {'LIQUID'} and domain.use_mesh:
row = col.row()
- row.enabled = not is_baking_any and not has_baked_particles and not has_baked_data
- row.prop(domain, "cache_particle_format", text="Particle File Format")
+ row.enabled = not is_baking_any and not has_baked_mesh
+ row.prop(domain, "cache_mesh_format", text="Meshes")
- if domain.use_mesh:
- row = col.row()
- row.enabled = not is_baking_any and not has_baked_mesh
- row.prop(domain, "cache_mesh_format", text="Mesh File Format")
-
- if domain.cache_type == 'FINAL':
+ if domain.cache_type == 'ALL':
col.separator()
split = layout.split()
+ split.enabled = ob.mode == 'OBJECT'
- if domain.is_cache_baking_data and not domain.has_cache_baked_data:
+ bake_incomplete = (domain.cache_frame_pause_data < domain.cache_frame_end)
+ if domain.cache_resumable and domain.has_cache_baked_data and not domain.is_cache_baking_data and bake_incomplete:
+ col = split.column()
+ col.operator("fluid.bake_all", text="Resume")
+ col = split.column()
+ col.operator("fluid.free_all", text="Free")
+ elif domain.is_cache_baking_data and not domain.has_cache_baked_data:
split.enabled = False
- split.operator("fluid.pause_bake", text="Baking All - ESC to cancel")
+ split.operator("fluid.pause_bake", text="Baking All - ESC to pause")
elif not domain.has_cache_baked_data and not domain.is_cache_baking_data:
split.operator("fluid.bake_all", text="Bake All")
else:
@@ -1189,8 +1190,8 @@ class PHYSICS_PT_export(PhysicButtonsPanel, Panel):
@classmethod
def poll(cls, context):
- # Only show the advanced panel to advanced users who know Mantaflow's birthday :)
- if not PhysicButtonsPanel.poll_fluid_domain(context) or bpy.app.debug_value != 3001:
+ domain = context.fluid.domain_settings
+ if not PhysicButtonsPanel.poll_fluid_domain(context) or (domain.cache_data_format != 'OPENVDB' and bpy.app.debug_value != 3001):
return False
return (context.engine in cls.COMPAT_ENGINES)
@@ -1203,12 +1204,24 @@ class PHYSICS_PT_export(PhysicButtonsPanel, Panel):
is_baking_any = domain.is_cache_baking_any
has_baked_any = domain.has_cache_baked_any
+ has_baked_data = domain.has_cache_baked_data
flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
flow.enabled = not is_baking_any and not has_baked_any
col = flow.column()
- col.prop(domain, "export_manta_script", text="Export Mantaflow Script")
+
+ if domain.cache_data_format == 'OPENVDB':
+ col.enabled = not is_baking_any and not has_baked_data
+ col.prop(domain, "openvdb_cache_compress_type", text="Compression Volumes")
+
+ col = flow.column()
+ col.prop(domain, "openvdb_data_depth", text="Precision Volumes")
+
+ # Only show the advanced panel to advanced users who know Mantaflow's birthday :)
+ if bpy.app.debug_value == 3001:
+ col = flow.column()
+ col.prop(domain, "export_manta_script", text="Export Mantaflow Script")
class PHYSICS_PT_field_weights(PhysicButtonsPanel, Panel):
diff --git a/release/scripts/startup/bl_ui/properties_render.py b/release/scripts/startup/bl_ui/properties_render.py
index 11b02cfb552..fcaa715cfd8 100644
--- a/release/scripts/startup/bl_ui/properties_render.py
+++ b/release/scripts/startup/bl_ui/properties_render.py
@@ -173,8 +173,10 @@ class RENDER_PT_eevee_motion_blur(RenderButtonsPanel, Panel):
layout.active = props.use_motion_blur
col = layout.column()
- col.prop(props, "motion_blur_samples")
col.prop(props, "motion_blur_shutter")
+ col.prop(props, "motion_blur_depth_scale")
+ col.prop(props, "motion_blur_max")
+ col.prop(props, "motion_blur_steps", text="Steps")
class RENDER_PT_eevee_depth_of_field(RenderButtonsPanel, Panel):
diff --git a/release/scripts/startup/bl_ui/properties_scene.py b/release/scripts/startup/bl_ui/properties_scene.py
index 0b4ca2902c1..22f455fe5be 100644
--- a/release/scripts/startup/bl_ui/properties_scene.py
+++ b/release/scripts/startup/bl_ui/properties_scene.py
@@ -289,8 +289,6 @@ class SCENE_PT_audio(SceneButtonsPanel, Panel):
layout.use_property_split = True
scene = context.scene
- rd = context.scene.render
- ffmpeg = rd.ffmpeg
flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
@@ -299,17 +297,8 @@ class SCENE_PT_audio(SceneButtonsPanel, Panel):
col.separator()
- col.prop(scene, "audio_distance_model")
- col.prop(ffmpeg, "audio_channels")
-
- col.separator()
-
- col = flow.column()
- col.prop(ffmpeg, "audio_mixrate", text="Sample Rate")
-
- col.separator()
-
col = col.column(align=True)
+ col.prop(scene, "audio_distance_model")
col.prop(scene, "audio_doppler_speed", text="Doppler Speed")
col.prop(scene, "audio_doppler_factor", text="Doppler Factor")
diff --git a/release/scripts/startup/bl_ui/space_outliner.py b/release/scripts/startup/bl_ui/space_outliner.py
index ee8015df273..aa4d0b94b7f 100644
--- a/release/scripts/startup/bl_ui/space_outliner.py
+++ b/release/scripts/startup/bl_ui/space_outliner.py
@@ -213,7 +213,7 @@ class OUTLINER_MT_collection(Menu):
layout.separator()
layout.operator("outliner.delete", text="Delete", icon='X')
- layout.operator("outliner.collection_hierarchy_delete")
+ layout.operator("outliner.delete", text="Delete Hierarchy").hierarchy = True
layout.separator()
@@ -279,9 +279,7 @@ class OUTLINER_MT_object(Menu):
layout.separator()
layout.operator("outliner.delete", text="Delete", icon='X')
-
- if space.display_mode == 'VIEW_LAYER' and not space.use_filter_collection:
- layout.operator("outliner.object_operation", text="Delete Hierarchy").type = 'DELETE_HIERARCHY'
+ layout.operator("outliner.delete", text="Delete Hierarchy").hierarchy = True
layout.separator()
diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py
index abe6f614f28..8dfa182f52d 100644
--- a/release/scripts/startup/bl_ui/space_sequencer.py
+++ b/release/scripts/startup/bl_ui/space_sequencer.py
@@ -474,7 +474,7 @@ class SEQUENCER_MT_navigation(Menu):
layout.separator()
- layout.operator("sequencer.view_frame", text="Go to Playhead")
+ layout.operator("sequencer.view_frame")
layout.separator()
@@ -635,7 +635,7 @@ class SEQUENCER_MT_strip_transform(Menu):
layout = self.layout
layout.operator("transform.seq_slide", text="Move")
- layout.operator("transform.transform", text="Move/Extend from Playhead").mode = 'TIME_EXTEND'
+ layout.operator("transform.transform", text="Move/Extend from Current Frame").mode = 'TIME_EXTEND'
layout.operator("sequencer.slip", text="Slip Strip Contents")
layout.separator()
@@ -731,7 +731,7 @@ class SEQUENCER_MT_strip(Menu):
layout.operator("sequencer.copy", text="Copy")
layout.operator("sequencer.paste", text="Paste")
layout.operator("sequencer.duplicate_move")
- layout.operator("sequencer.delete", text="Delete...")
+ layout.operator("sequencer.delete", text="Delete")
strip = act_strip(context)
@@ -1528,7 +1528,7 @@ class SEQUENCER_PT_time(SequencerButtonsPanel, Panel):
split.alignment = 'RIGHT'
split.label(text="End")
split = split.split(factor=0.8 + max_factor, align=True)
- split.label(text="{:>14}".format(smpte_from_frame(frame_final_end)))
+ split.label(text="%14s" % smpte_from_frame(frame_final_end))
split.alignment = 'RIGHT'
split.label(text=str(frame_final_end) + " ")
@@ -1569,10 +1569,10 @@ class SEQUENCER_PT_time(SequencerButtonsPanel, Panel):
split = col.split(factor=0.5 + max_factor, align=True)
split.alignment = 'RIGHT'
- split.label(text="Playhead")
+ split.label(text="Current Frame")
split = split.split(factor=0.8 + max_factor, align=True)
frame_display = frame_current - frame_final_start
- split.label(text="{:>14}".format(smpte_from_frame(frame_display)))
+ split.label(text="%14s" % smpte_from_frame(frame_display))
split.alignment = 'RIGHT'
split.label(text=str(frame_display) + " ")
diff --git a/release/scripts/startup/bl_ui/space_text.py b/release/scripts/startup/bl_ui/space_text.py
index 553d79fb349..4f518d8e2d4 100644
--- a/release/scripts/startup/bl_ui/space_text.py
+++ b/release/scripts/startup/bl_ui/space_text.py
@@ -174,7 +174,10 @@ class TEXT_PT_find(Panel):
row = col.row(align=True)
row.prop(st, "replace_text", icon='DECORATE_OVERRIDE', text="")
row.operator("text.replace_set_selected", text="", icon='EYEDROPPER')
- col.operator("text.replace")
+
+ row = col.row(align=True)
+ row.operator("text.replace")
+ row.operator("text.replace", text = "Replace all").all = True
layout.separator()
diff --git a/release/scripts/startup/bl_ui/space_time.py b/release/scripts/startup/bl_ui/space_time.py
index 257e0c26a5d..1a9244a3051 100644
--- a/release/scripts/startup/bl_ui/space_time.py
+++ b/release/scripts/startup/bl_ui/space_time.py
@@ -243,8 +243,8 @@ class TIME_PT_playback(TimelinePanelButtons, Panel):
layout.prop(scene, "show_subframe", text="Subframes")
- layout.prop(scene, "lock_frame_selection_to_range", text="Limit Playhead to Frame Range")
- layout.prop(screen, "use_follow", text="Follow Playhead")
+ layout.prop(scene, "lock_frame_selection_to_range", text="Limit Playback to Frame Range")
+ layout.prop(screen, "use_follow", text="Follow Current Frame")
layout.separator()
diff --git a/release/scripts/startup/bl_ui/space_toolsystem_common.py b/release/scripts/startup/bl_ui/space_toolsystem_common.py
index e0651dcac2b..080e66b59e7 100644
--- a/release/scripts/startup/bl_ui/space_toolsystem_common.py
+++ b/release/scripts/startup/bl_ui/space_toolsystem_common.py
@@ -36,7 +36,7 @@ __all__ = (
# Support reloading icons.
if "_icon_cache" in locals():
release = bpy.app.icons.release
- for icon_value in _icon_cache.values():
+ for icon_value in set(_icon_cache.values()):
if icon_value != 0:
release(icon_value)
del release
@@ -450,7 +450,7 @@ class ToolSelectPanelHelper:
@classmethod
def _km_action_simple(cls, kc_default, kc, context_descr, label, keymap_fn):
- km_idname = f"{cls.keymap_prefix:s} {context_descr:s}, {label:s}"
+ km_idname = "%s %s, %s" % (cls.keymap_prefix, context_descr, label)
km = kc.keymaps.get(km_idname)
km_kwargs = dict(space_type=cls.bl_space_type, region_type='WINDOW', tool=True)
if km is None:
diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
index 2c3108e0a11..2391678b99b 100644
--- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
@@ -124,12 +124,12 @@ class _defs_view3d_generic:
kmi_remove = None
return tip_(
"Measure distance and angles.\n"
- "\u2022 {} anywhere for new measurement.\n"
+ "\u2022 %s anywhere for new measurement.\n"
"\u2022 Drag ruler segment to measure an angle.\n"
- "\u2022 {} to remove the active ruler.\n"
+ "\u2022 %s to remove the active ruler.\n"
"\u2022 Ctrl while dragging to snap.\n"
"\u2022 Shift while dragging to measure surface thickness"
- ).format(
+ ) % (
kmi_to_string_or_none(kmi_add),
kmi_to_string_or_none(kmi_remove),
)
@@ -482,7 +482,7 @@ class _defs_view3d_add:
return dict(
idname="builtin.primitive_cone_add",
label="Add Cone",
- icon="ops.mesh.primitive_cube_add_gizmo",
+ icon="ops.mesh.primitive_cone_add_gizmo",
description=(
"Add cone to mesh interactively"
),
@@ -738,27 +738,40 @@ class _defs_edit_mesh:
def bevel():
def draw_settings(context, layout, tool, *, extra=False):
props = tool.operator_properties("mesh.bevel")
- region_type = context.region.type
+
+ region_is_header = context.region.type == 'TOOL_HEADER'
if not extra:
- if region_type == 'TOOL_HEADER':
+ if region_is_header:
layout.prop(props, "offset_type", text="")
else:
layout.prop(props, "offset_type")
layout.prop(props, "segments")
- layout.prop(props, "profile", slider=True)
- if region_type == 'TOOL_HEADER':
+ row = layout.row()
+ row.prop(props, "profile_type", text="" if region_is_header else None)
+ if props.profile_type == 'SUPERELLIPSE':
+ layout.prop(props, "profile", text="Shape", slider=True)
+
+ if region_is_header:
layout.popover("TOPBAR_PT_tool_settings_extra", text="...")
else:
extra = True
- if extra or region_type != 'TOOL_HEADER':
- layout.prop(props, "vertex_only")
- layout.prop(props, "clamp_overlap")
- layout.prop(props, "loop_slide")
- layout.prop(props, "harden_normals")
+ if extra:
+ layout.use_property_split = True
+ layout.use_property_decorate = False
+
+ if props.profile_type == 'CUSTOM':
+ layout.prop(props, "profile", text="Miter Shape", slider=True)
+
+ col = layout.column()
+ col.prop(props, "vertex_only")
+ col.prop(props, "clamp_overlap")
+ col.prop(props, "loop_slide")
+ col.prop(props, "harden_normals")
+
col = layout.column(heading="Mark")
col.prop(props, "mark_seam", text="Seam")
col.prop(props, "mark_sharp", text="Sharp")
@@ -770,8 +783,7 @@ class _defs_edit_mesh:
if props.miter_inner == 'ARC':
layout.prop(props, "spread")
- layout.prop(props, "use_custom_profile")
- if props.use_custom_profile:
+ if props.profile_type == 'CUSTOM':
tool_settings = context.tool_settings
layout.template_curveprofile(tool_settings, "custom_bevel_profile_preset")
@@ -802,14 +814,14 @@ class _defs_edit_mesh:
)
@ToolDef.from_fn
- def extrude_dissolve_and_intersect():
+ def extrude_manifold():
return dict(
- idname="builtin.extrude_dissolve_and_intersect",
- label="Extrude Dissolve and Intersect",
+ idname="builtin.extrude_manifold",
+ label="Extrude Manifold",
description=(
"Extrude, dissolves edges whose faces form a flat surface and intersect new edges"
),
- icon="none",
+ icon="ops.mesh.extrude_manifold",
widget="VIEW3D_GGT_tool_generic_handle_normal",
keymap=(),
)
@@ -1212,10 +1224,10 @@ class _defs_sculpt:
layout.prop(props, "strength")
layout.prop(props, "deform_axis")
layout.prop(props, "use_face_sets")
- if (props.type == "SURFACE_SMOOTH"):
+ if props.type == 'SURFACE_SMOOTH':
layout.prop(props, "surface_smooth_shape_preservation", expand=False)
layout.prop(props, "surface_smooth_current_vertex", expand=False)
- if (props.type == "SHARPEN"):
+ elif props.type == 'SHARPEN':
layout.prop(props, "sharpen_smooth_ratio", expand=False)
return dict(
@@ -1246,6 +1258,24 @@ class _defs_sculpt:
draw_settings=draw_settings,
)
+ @ToolDef.from_fn
+ def color_filter():
+ def draw_settings(_context, layout, tool):
+ props = tool.operator_properties("sculpt.color_filter")
+ layout.prop(props, "type", expand=False)
+ if props.type == 'FILL':
+ layout.prop(props, "fill_color", expand=False)
+ layout.prop(props, "strength")
+
+ return dict(
+ idname="builtin.color_filter",
+ label="Color Filter",
+ icon="ops.sculpt.color_filter",
+ widget=None,
+ keymap=(),
+ draw_settings=draw_settings,
+ )
+
class _defs_vertex_paint:
@@ -2327,7 +2357,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel):
None,
(
_defs_edit_mesh.extrude,
- _defs_edit_mesh.extrude_dissolve_and_intersect,
+ _defs_edit_mesh.extrude_manifold,
_defs_edit_mesh.extrude_normals,
_defs_edit_mesh.extrude_individual,
_defs_edit_mesh.extrude_cursor,
@@ -2421,6 +2451,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel):
None,
_defs_sculpt.mesh_filter,
_defs_sculpt.cloth_filter,
+ _defs_sculpt.color_filter,
None,
_defs_transform.translate,
_defs_transform.rotate,
diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py
index 436d866886b..e5171df597a 100644
--- a/release/scripts/startup/bl_ui/space_userpref.py
+++ b/release/scripts/startup/bl_ui/space_userpref.py
@@ -45,7 +45,7 @@ class USERPREF_HT_header(Header):
# Show '*' to let users know the preferences have been modified.
layout.operator(
"wm.save_userpref",
- text="Save Preferences{:s}".format(" *" if prefs.is_dirty else ""),
+ text="Save Preferences" + (" *" if prefs.is_dirty else ""),
)
def draw(self, context):
@@ -1903,8 +1903,10 @@ class USERPREF_PT_addons(AddOnPanel, Panel):
"wm.url_open", text="Report a Bug", icon='URL',
).url = info["tracker_url"]
elif not user_addon:
- addon_info = ("Name: {} {}\nAuthor: {}\n").format(
- info["name"], info["version"], info["author"])
+ addon_info = (
+ "Name: %s %s\n"
+ "Author: %s\n"
+ ) % (info["name"], str(info["version"]), info["author"])
props = sub.operator(
"wm.url_open_preset", text="Report a Bug", icon='URL',
)
@@ -1987,7 +1989,7 @@ class StudioLightPanelMixin:
for studio_light in lights:
self.draw_studio_light(flow, studio_light)
else:
- layout.label(text="No custom {} configured".format(self.bl_label))
+ layout.label(text="No custom %s configured" % self.bl_label)
def draw_studio_light(self, layout, studio_light):
box = layout.box()
@@ -2112,8 +2114,10 @@ class ExperimentalPanel:
split = layout.split(factor=0.66)
col = split.split()
col.prop(experimental, **prop_keywords)
- col = split.split()
- col.operator("wm.url_open", text=task, icon='URL').url = self.url_prefix + task
+
+ if task:
+ col = split.split()
+ col.operator("wm.url_open", text=task, icon='URL').url = self.url_prefix + task
"""
# Example panel, leave it here so we always have a template to follow even
@@ -2132,13 +2136,36 @@ class USERPREF_PT_experimental_virtual_reality(ExperimentalPanel, Panel):
"""
-class USERPREF_PT_experimental_system(ExperimentalPanel, Panel):
- bl_label = "System"
+class USERPREF_PT_experimental_new_features(ExperimentalPanel, Panel):
+ bl_label = "New Features"
+
+ def draw(self, context):
+ self._draw_items(
+ context, (
+ ({"property": "use_new_particle_system"}, "T73324"),
+ ),
+ )
+
+
+class USERPREF_PT_experimental_prototypes(ExperimentalPanel, Panel):
+ bl_label = "Prototypes"
+
+ def draw(self, context):
+ self._draw_items(
+ context, (
+ ({"property": "use_new_hair_type"}, "T68981"),
+ ),
+ )
+
+
+class USERPREF_PT_experimental_debugging(ExperimentalPanel, Panel):
+ bl_label = "Debugging"
def draw(self, context):
self._draw_items(
context, (
({"property": "use_undo_legacy"}, "T60695"),
+ ({"property": "use_cycles_debug"}, None),
),
)
@@ -2233,7 +2260,9 @@ classes = (
# Popovers.
USERPREF_PT_ndof_settings,
- USERPREF_PT_experimental_system,
+ USERPREF_PT_experimental_new_features,
+ USERPREF_PT_experimental_prototypes,
+ USERPREF_PT_experimental_debugging,
# Add dynamically generated editor theme panels last,
# so they show up last in the theme section.
diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py
index c982d8e93a9..1403e43a8af 100644
--- a/release/scripts/startup/bl_ui/space_view3d.py
+++ b/release/scripts/startup/bl_ui/space_view3d.py
@@ -131,7 +131,7 @@ class VIEW3D_HT_tool_header(Header):
if is_valid_context:
brush = context.tool_settings.gpencil_sculpt_paint.brush
tool = brush.gpencil_tool
- if tool in ('SMOOTH', 'RANDOMIZE'):
+ if tool in {'SMOOTH', 'RANDOMIZE'}:
layout.popover("VIEW3D_PT_tools_grease_pencil_sculpt_options")
layout.popover("VIEW3D_PT_tools_grease_pencil_sculpt_appearance")
elif tool_mode == 'WEIGHT_GPENCIL':
@@ -295,6 +295,11 @@ class _draw_tool_settings_context_mode:
if not capabilities.has_direction:
layout.row().prop(brush, "direction", expand=True, text="")
+ if capabilities.has_color:
+ UnifiedPaintPanel.prop_unified_color(layout, context, brush, "color", text = "")
+ layout.prop(brush, "blend", text="", expand = False)
+
+
return True
@staticmethod
@@ -2171,9 +2176,9 @@ class VIEW3D_MT_add(Menu):
layout.menu("VIEW3D_MT_surface_add", icon='OUTLINER_OB_SURFACE')
layout.menu("VIEW3D_MT_metaball_add", text="Metaball", icon='OUTLINER_OB_META')
layout.operator("object.text_add", text="Text", icon='OUTLINER_OB_FONT')
- if hasattr(bpy.data, "hairs"):
+ if context.preferences.experimental.use_new_hair_type:
layout.operator("object.hair_add", text="Hair", icon='OUTLINER_OB_HAIR')
- if hasattr(bpy.data, "pointclouds"):
+ if context.preferences.experimental.use_new_particle_system:
layout.operator("object.pointcloud_add", text="Point Cloud", icon='OUTLINER_OB_POINTCLOUD')
layout.menu("VIEW3D_MT_volume_add", text="Volume", icon='OUTLINER_OB_VOLUME')
layout.operator_menu_enum("object.gpencil_add", "type", text="Grease Pencil", icon='OUTLINER_OB_GREASEPENCIL')
@@ -2348,6 +2353,7 @@ class VIEW3D_MT_object_animation(Menu):
layout.separator()
layout.operator("nla.bake", text="Bake Action...")
+ layout.operator("gpencil.mesh_bake", text="Bake Mesh to Grease Pencil...")
class VIEW3D_MT_object_rigid_body(Menu):
@@ -5620,8 +5626,8 @@ class VIEW3D_PT_object_type_visibility(Panel):
elif attr == "pointcloud" and not hasattr(bpy.data, "pointclouds"):
continue
- attr_v = "show_object_viewport_" f"{attr:s}"
- attr_s = "show_object_select_" f"{attr:s}"
+ attr_v = "show_object_viewport_" + attr
+ attr_s = "show_object_select_" + attr
icon_v = 'HIDE_OFF' if getattr(view, attr_v) else 'HIDE_ON'
icon_s = 'RESTRICT_SELECT_OFF' if getattr(view, attr_s) else 'RESTRICT_SELECT_ON'
@@ -6786,7 +6792,10 @@ class VIEW3D_PT_overlay_gpencil_options(Panel):
if context.object.mode in {'PAINT_GPENCIL', 'VERTEX_GPENCIL'}:
layout.label(text="Vertex Paint")
- layout.prop(overlay, "gpencil_vertex_paint_opacity", text="Opacity", slider=True)
+ row = layout.row()
+ shading = VIEW3D_PT_shading.get_shading(context)
+ row.enabled = shading.type not in {'WIREFRAME', 'RENDERED'}
+ row.prop(overlay, "gpencil_vertex_paint_opacity", text="Opacity", slider=True)
class VIEW3D_PT_quad_view(Panel):
@@ -7329,6 +7338,12 @@ class VIEW3D_PT_sculpt_context_menu(Panel):
brush = context.tool_settings.sculpt.brush
capabilities = brush.sculpt_capabilities
+ if capabilities.has_color:
+ split = layout.split(factor=0.1)
+ UnifiedPaintPanel.prop_unified_color(split, context, brush, "color", text="")
+ UnifiedPaintPanel.prop_unified_color_picker(split, context, brush, "color", value_slider=True)
+ layout.prop(brush, "blend", text="")
+
UnifiedPaintPanel.prop_unified(
layout,
context,
diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
index 3d72a2a588c..549b89938e0 100644
--- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
@@ -187,6 +187,7 @@ class VIEW3D_PT_tools_meshedit_options(View3DPanel, Panel):
bl_context = ".mesh_edit" # dot on purpose (access from topbar)
bl_label = "Options"
bl_options = {'DEFAULT_CLOSED'}
+ bl_ui_units_x = 12
@classmethod
def poll(cls, context):
@@ -198,11 +199,15 @@ class VIEW3D_PT_tools_meshedit_options(View3DPanel, Panel):
layout.use_property_split = True
layout.use_property_decorate = False
+ tool_settings = context.tool_settings
ob = context.active_object
mesh = ob.data
split = layout.split()
+ row = layout.row(align=True, heading="Transform")
+ row.prop(tool_settings, "use_transform_correct_face_attributes")
+
row = layout.row(heading="Mirror")
sub = row.row(align=True)
sub.prop(mesh, "use_mirror_x", text="X", toggle=True)
@@ -806,6 +811,8 @@ class VIEW3D_PT_sculpt_voxel_remesh(Panel, View3DPaintPanel):
col.prop(mesh, "use_remesh_preserve_volume", text="Volume")
col.prop(mesh, "use_remesh_preserve_paint_mask", text="Paint Mask")
col.prop(mesh, "use_remesh_preserve_sculpt_face_sets", text="Face Sets")
+ col.prop(mesh, "use_remesh_preserve_vertex_colors", text="Vertex Colors")
+
layout.operator("object.voxel_remesh", text="Remesh")
@@ -829,7 +836,6 @@ class VIEW3D_PT_sculpt_options(Panel, View3DPaintPanel):
sculpt = tool_settings.sculpt
col = layout.column(heading="Display", align=True)
- col.prop(sculpt, "use_threaded", text="Threaded Sculpt")
col.prop(sculpt, "show_low_resolution")
col.prop(sculpt, "use_sculpt_delay_updates")
col.prop(sculpt, "use_deform_only")
@@ -1694,7 +1700,7 @@ class GreasePencilSculptPanel:
@classmethod
def poll(cls, context):
- if context.space_data.type in ('VIEW_3D', 'PROPERTIES'):
+ if context.space_data.type in {'VIEW_3D', 'PROPERTIES'}:
if context.gpencil_data is None:
return False
@@ -1768,7 +1774,7 @@ class GreasePencilWeightPanel:
@classmethod
def poll(cls, context):
- if context.space_data.type in ('VIEW_3D', 'PROPERTIES'):
+ if context.space_data.type in {'VIEW_3D', 'PROPERTIES'}:
if context.gpencil_data is None:
return False
@@ -1843,7 +1849,7 @@ class GreasePencilVertexPanel:
@classmethod
def poll(cls, context):
- if context.space_data.type in ('VIEW_3D', 'PROPERTIES'):
+ if context.space_data.type in {'VIEW_3D', 'PROPERTIES'}:
if context.gpencil_data is None:
return False
diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h
index 9d948dfd57b..229b5193e9c 100644
--- a/source/blender/blenkernel/BKE_blender_version.h
+++ b/source/blender/blenkernel/BKE_blender_version.h
@@ -40,7 +40,7 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
-#define BLENDER_FILE_SUBVERSION 4
+#define BLENDER_FILE_SUBVERSION 5
/* 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_blendfile.h b/source/blender/blenkernel/BKE_blendfile.h
index 2bff684948d..e835137bfa1 100644
--- a/source/blender/blenkernel/BKE_blendfile.h
+++ b/source/blender/blenkernel/BKE_blendfile.h
@@ -74,6 +74,7 @@ void BKE_blendfile_write_partial_begin(struct Main *bmain_src);
bool BKE_blendfile_write_partial(struct Main *bmain_src,
const char *filepath,
const int write_flags,
+ const int remap_mode,
struct ReportList *reports);
void BKE_blendfile_write_partial_end(struct Main *bmain_src);
diff --git a/source/blender/blenkernel/BKE_collection.h b/source/blender/blenkernel/BKE_collection.h
index f4b56aa152f..4cf33640ebd 100644
--- a/source/blender/blenkernel/BKE_collection.h
+++ b/source/blender/blenkernel/BKE_collection.h
@@ -24,6 +24,8 @@
#include "BLI_compiler_compat.h"
#include "BLI_ghash.h"
#include "BLI_iterator.h"
+#include "BLI_sys_types.h"
+
#include "DNA_listBase.h"
#ifdef __cplusplus
@@ -57,16 +59,11 @@ void BKE_collection_add_from_object(struct Main *bmain,
void BKE_collection_free(struct Collection *collection);
bool BKE_collection_delete(struct Main *bmain, struct Collection *collection, bool hierarchy);
-struct Collection *BKE_collection_copy(struct Main *bmain,
- struct Collection *parent,
- struct Collection *collection);
-
struct Collection *BKE_collection_duplicate(struct Main *bmain,
struct Collection *parent,
struct Collection *collection,
- const bool do_hierarchy,
- const bool do_objects,
- const bool do_obdata);
+ const uint duplicate_flags,
+ const uint duplicate_options);
/* Master Collection for Scene */
diff --git a/source/blender/blenkernel/BKE_colortools.h b/source/blender/blenkernel/BKE_colortools.h
index 94b8d59b3db..0623e0e5395 100644
--- a/source/blender/blenkernel/BKE_colortools.h
+++ b/source/blender/blenkernel/BKE_colortools.h
@@ -37,6 +37,8 @@ struct Histogram;
struct ImBuf;
struct Scopes;
struct rctf;
+struct BlendWriter;
+struct BlendDataReader;
void BKE_curvemapping_set_defaults(
struct CurveMapping *cumap, int tot, float minx, float miny, float maxx, float maxy);
@@ -100,6 +102,11 @@ void BKE_curvemapping_table_RGBA(const struct CurveMapping *cumap, float **array
/* non-const, these modify the curve */
void BKE_curvemapping_premultiply(struct CurveMapping *cumap, int restore);
+void BKE_curvemapping_blend_write(struct BlendWriter *writer, const struct CurveMapping *cumap);
+void BKE_curvemapping_curves_blend_write(struct BlendWriter *writer,
+ const struct CurveMapping *cumap);
+void BKE_curvemapping_blend_read(struct BlendDataReader *reader, struct CurveMapping *cumap);
+
void BKE_histogram_update_sample_line(struct Histogram *hist,
struct ImBuf *ibuf,
const struct ColorManagedViewSettings *view_settings,
diff --git a/source/blender/blenkernel/BKE_constraint.h b/source/blender/blenkernel/BKE_constraint.h
index 8fe3bd77a26..8d7fe875c37 100644
--- a/source/blender/blenkernel/BKE_constraint.h
+++ b/source/blender/blenkernel/BKE_constraint.h
@@ -145,6 +145,11 @@ struct bConstraint *BKE_constraint_duplicate_ex(struct bConstraint *src,
const int flag,
const bool do_extern);
+struct bConstraint *BKE_constraint_copy_for_pose(struct Object *ob,
+ struct bPoseChannel *pchan,
+ struct bConstraint *src);
+struct bConstraint *BKE_constraint_copy_for_object(struct Object *ob, struct bConstraint *src);
+
void BKE_constraints_free(struct ListBase *list);
void BKE_constraints_free_ex(struct ListBase *list, bool do_id_user);
void BKE_constraints_copy(struct ListBase *dst, const struct ListBase *src, bool do_extern);
diff --git a/source/blender/blenkernel/BKE_curveprofile.h b/source/blender/blenkernel/BKE_curveprofile.h
index ecbaa365bac..877ab887138 100644
--- a/source/blender/blenkernel/BKE_curveprofile.h
+++ b/source/blender/blenkernel/BKE_curveprofile.h
@@ -28,6 +28,8 @@
extern "C" {
#endif
+struct BlendWriter;
+struct BlendDataReader;
struct CurveProfile;
struct CurveProfilePoint;
@@ -43,6 +45,16 @@ void BKE_curveprofile_copy_data(struct CurveProfile *target, const struct CurveP
struct CurveProfile *BKE_curveprofile_copy(const struct CurveProfile *profile);
+bool BKE_curveprofile_move_handle(struct CurveProfilePoint *point,
+ const bool handle_1,
+ const bool snap,
+ const float delta[2]);
+
+bool BKE_curveprofile_move_point(struct CurveProfile *profile,
+ struct CurveProfilePoint *point,
+ const bool snap,
+ const float delta[2]);
+
bool BKE_curveprofile_remove_point(struct CurveProfile *profile, struct CurveProfilePoint *point);
void BKE_curveprofile_remove_by_flag(struct CurveProfile *profile, const short flag);
@@ -63,7 +75,12 @@ void BKE_curveprofile_create_samples(struct CurveProfile *profile,
void BKE_curveprofile_initialize(struct CurveProfile *profile, short segments_len);
/* Called for a complete update of the widget after modifications */
-void BKE_curveprofile_update(struct CurveProfile *profile, const bool rem_doubles);
+enum {
+ PROF_UPDATE_NONE = 0,
+ PROF_UPDATE_REMOVE_DOUBLES = (1 << 0),
+ PROF_UPDATE_CLIP = (1 << 1),
+};
+void BKE_curveprofile_update(struct CurveProfile *profile, const int update_flags);
/* Need to find the total length of the curve to sample a portion of it */
float BKE_curveprofile_total_length(const struct CurveProfile *profile);
@@ -78,6 +95,9 @@ void BKE_curveprofile_evaluate_length_portion(const struct CurveProfile *profile
float *x_out,
float *y_out);
+void BKE_curveprofile_blend_write(struct BlendWriter *writer, const struct CurveProfile *profile);
+void BKE_curveprofile_blend_read(struct BlendDataReader *reader, struct CurveProfile *profile);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenkernel/BKE_derived_node_tree.hh b/source/blender/blenkernel/BKE_derived_node_tree.hh
index 009d9b747f9..84b36f675e0 100644
--- a/source/blender/blenkernel/BKE_derived_node_tree.hh
+++ b/source/blender/blenkernel/BKE_derived_node_tree.hh
@@ -32,7 +32,8 @@
#include "BKE_node_tree_ref.hh"
-namespace BKE {
+namespace blender {
+namespace bke {
class DSocket;
class DInputSocket;
@@ -42,7 +43,7 @@ class DParentNode;
class DGroupInput;
class DerivedNodeTree;
-class DSocket : blender::NonCopyable, blender::NonMovable {
+class DSocket : NonCopyable, NonMovable {
protected:
DNode *m_node;
const SocketRef *m_socket_ref;
@@ -66,6 +67,11 @@ class DSocket : blender::NonCopyable, blender::NonMovable {
PointerRNA *rna() const;
StringRefNull idname() const;
StringRefNull name() const;
+
+ const SocketRef &socket_ref() const;
+ bNodeSocket *bsocket() const;
+
+ bool is_available() const;
};
class DInputSocket : public DSocket {
@@ -95,7 +101,7 @@ class DOutputSocket : public DSocket {
Span<const DInputSocket *> linked_sockets() const;
};
-class DGroupInput : blender::NonCopyable, blender::NonMovable {
+class DGroupInput : NonCopyable, NonMovable {
private:
const InputSocketRef *m_socket_ref;
DParentNode *m_parent;
@@ -106,13 +112,14 @@ class DGroupInput : blender::NonCopyable, blender::NonMovable {
public:
const InputSocketRef &socket_ref() const;
+ bNodeSocket *bsocket() const;
const DParentNode *parent() const;
Span<const DInputSocket *> linked_sockets() const;
uint id() const;
StringRefNull name() const;
};
-class DNode : blender::NonCopyable, blender::NonMovable {
+class DNode : NonCopyable, NonMovable {
private:
const NodeRef *m_node_ref;
DParentNode *m_parent;
@@ -144,7 +151,7 @@ class DNode : blender::NonCopyable, blender::NonMovable {
void destruct_with_sockets();
};
-class DParentNode : blender::NonCopyable, blender::NonMovable {
+class DParentNode : NonCopyable, NonMovable {
private:
const NodeRef *m_node_ref;
DParentNode *m_parent;
@@ -160,7 +167,7 @@ class DParentNode : blender::NonCopyable, blender::NonMovable {
using NodeTreeRefMap = Map<bNodeTree *, std::unique_ptr<const NodeTreeRef>>;
-class DerivedNodeTree : blender::NonCopyable, blender::NonMovable {
+class DerivedNodeTree : NonCopyable, NonMovable {
private:
LinearAllocator<> m_allocator;
bNodeTree *m_btree;
@@ -172,19 +179,22 @@ class DerivedNodeTree : blender::NonCopyable, blender::NonMovable {
Vector<DInputSocket *> m_input_sockets;
Vector<DOutputSocket *> m_output_sockets;
- Map<std::string, Vector<DNode *>> m_nodes_by_idname;
+ Map<const bNodeType *, Vector<DNode *>> m_nodes_by_type;
public:
DerivedNodeTree(bNodeTree *btree, NodeTreeRefMap &node_tree_refs);
~DerivedNodeTree();
Span<const DNode *> nodes() const;
- Span<const DNode *> nodes_with_idname(StringRef idname) const;
+ Span<const DNode *> nodes_by_type(StringRefNull idname) const;
+ Span<const DNode *> nodes_by_type(const bNodeType *nodetype) const;
Span<const DSocket *> sockets() const;
Span<const DInputSocket *> input_sockets() const;
Span<const DOutputSocket *> output_sockets() const;
+ Span<const DGroupInput *> group_inputs() const;
+
std::string to_dot() const;
private:
@@ -278,6 +288,21 @@ inline StringRefNull DSocket::name() const
return m_socket_ref->name();
}
+inline const SocketRef &DSocket::socket_ref() const
+{
+ return *m_socket_ref;
+}
+
+inline bNodeSocket *DSocket::bsocket() const
+{
+ return m_socket_ref->bsocket();
+}
+
+inline bool DSocket::is_available() const
+{
+ return (m_socket_ref->bsocket()->flag & SOCK_UNAVAIL) == 0;
+}
+
/* --------------------------------------------------------------------
* DInputSocket inline methods.
*/
@@ -325,6 +350,11 @@ inline const InputSocketRef &DGroupInput::socket_ref() const
return *m_socket_ref;
}
+inline bNodeSocket *DGroupInput::bsocket() const
+{
+ return m_socket_ref->bsocket();
+}
+
inline const DParentNode *DGroupInput::parent() const
{
return m_parent;
@@ -427,9 +457,15 @@ inline Span<const DNode *> DerivedNodeTree::nodes() const
return m_nodes_by_id.as_span();
}
-inline Span<const DNode *> DerivedNodeTree::nodes_with_idname(StringRef idname) const
+inline Span<const DNode *> DerivedNodeTree::nodes_by_type(StringRefNull idname) const
+{
+ const bNodeType *nodetype = nodeTypeFind(idname.data());
+ return this->nodes_by_type(nodetype);
+}
+
+inline Span<const DNode *> DerivedNodeTree::nodes_by_type(const bNodeType *nodetype) const
{
- const Vector<DNode *> *nodes = m_nodes_by_idname.lookup_ptr(idname);
+ const Vector<DNode *> *nodes = m_nodes_by_type.lookup_ptr(nodetype);
if (nodes == nullptr) {
return {};
}
@@ -438,6 +474,11 @@ inline Span<const DNode *> DerivedNodeTree::nodes_with_idname(StringRef idname)
}
}
+inline Span<const DSocket *> DerivedNodeTree::sockets() const
+{
+ return m_sockets_by_id.as_span();
+}
+
inline Span<const DInputSocket *> DerivedNodeTree::input_sockets() const
{
return m_input_sockets.as_span();
@@ -448,6 +489,12 @@ inline Span<const DOutputSocket *> DerivedNodeTree::output_sockets() const
return m_output_sockets.as_span();
}
-} // namespace BKE
+inline Span<const DGroupInput *> DerivedNodeTree::group_inputs() const
+{
+ return m_group_inputs.as_span();
+}
+
+} // namespace bke
+} // namespace blender
#endif /* __BKE_DERIVED_NODE_TREE_HH__ */
diff --git a/source/blender/blenkernel/BKE_effect.h b/source/blender/blenkernel/BKE_effect.h
index 6935b3aecce..0518ce8ffa3 100644
--- a/source/blender/blenkernel/BKE_effect.h
+++ b/source/blender/blenkernel/BKE_effect.h
@@ -130,6 +130,7 @@ void BKE_effectors_apply(struct ListBase *effectors,
struct EffectorWeights *weights,
struct EffectedPoint *point,
float *force,
+ float *wind_force,
float *impulse);
void BKE_effectors_free(struct ListBase *lb);
diff --git a/source/blender/blenkernel/BKE_global.h b/source/blender/blenkernel/BKE_global.h
index 5bd1961ca89..8d573a4621b 100644
--- a/source/blender/blenkernel/BKE_global.h
+++ b/source/blender/blenkernel/BKE_global.h
@@ -173,7 +173,7 @@ enum {
G_FILE_AUTOPACK = (1 << 0),
G_FILE_COMPRESS = (1 << 1),
- G_FILE_USERPREFS = (1 << 9),
+ // G_FILE_DEPRECATED_9 = (1 << 9),
G_FILE_NO_UI = (1 << 10),
/* Bits 11 to 22 (inclusive) are deprecated & need to be cleared */
@@ -181,19 +181,16 @@ enum {
/** On read, use #FileGlobal.filename instead of the real location on-disk,
* needed for recovering temp files so relative paths resolve */
G_FILE_RECOVER = (1 << 23),
- /** On write, remap relative file paths to the new file location. */
- G_FILE_RELATIVE_REMAP = (1 << 24),
- /** On write, make backup `.blend1`, `.blend2` ... files, when the users preference is enabled */
- G_FILE_HISTORY = (1 << 25),
/** BMesh option to save as older mesh format */
/* #define G_FILE_MESH_COMPAT (1 << 26) */
- /** On write, restore paths after editing them (G_FILE_RELATIVE_REMAP) */
- G_FILE_SAVE_COPY = (1 << 27),
/* #define G_FILE_GLSL_NO_ENV_LIGHTING (1 << 28) */ /* deprecated */
};
-/** Don't overwrite these flags when reading a file. */
-#define G_FILE_FLAG_ALL_RUNTIME (G_FILE_NO_UI | G_FILE_RELATIVE_REMAP | G_FILE_SAVE_COPY)
+/**
+ * Run-time only #G.fileflags which are never read or written to/from Blend files.
+ * This means we can change the values without worrying about do-versions.
+ */
+#define G_FILE_FLAG_ALL_RUNTIME (G_FILE_NO_UI)
/** ENDIAN_ORDER: indicates what endianness the platform where the file was written had. */
#if !defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__)
diff --git a/source/blender/blenkernel/BKE_gpencil.h b/source/blender/blenkernel/BKE_gpencil.h
index 85ba8175143..cd434566e43 100644
--- a/source/blender/blenkernel/BKE_gpencil.h
+++ b/source/blender/blenkernel/BKE_gpencil.h
@@ -96,6 +96,7 @@ void BKE_gpencil_free_layers(struct ListBase *list);
void BKE_gpencil_free(struct bGPdata *gpd, bool free_all);
void BKE_gpencil_eval_delete(struct bGPdata *gpd_eval);
void BKE_gpencil_free_layer_masks(struct bGPDlayer *gpl);
+void BKE_gpencil_tag(struct bGPdata *gpd);
void BKE_gpencil_batch_cache_dirty_tag(struct bGPdata *gpd);
void BKE_gpencil_batch_cache_free(struct bGPdata *gpd);
diff --git a/source/blender/blenkernel/BKE_gpencil_geom.h b/source/blender/blenkernel/BKE_gpencil_geom.h
index b26016aa26c..b79bbf3948f 100644
--- a/source/blender/blenkernel/BKE_gpencil_geom.h
+++ b/source/blender/blenkernel/BKE_gpencil_geom.h
@@ -98,6 +98,19 @@ bool BKE_gpencil_stroke_shrink(struct bGPDstroke *gps, const float dist);
float BKE_gpencil_stroke_length(const struct bGPDstroke *gps, bool use_3d);
+void BKE_gpencil_convert_mesh(struct Main *bmain,
+ struct Depsgraph *depsgraph,
+ struct Scene *scene,
+ struct Object *ob_gp,
+ struct Object *ob_mesh,
+ const float angle,
+ const int thickness,
+ const float offset,
+ const float matrix[4][4],
+ const int frame_offset,
+ const bool use_seams,
+ const bool use_faces);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenkernel/BKE_gpencil_modifier.h b/source/blender/blenkernel/BKE_gpencil_modifier.h
index 966d3a98234..ac5f4607838 100644
--- a/source/blender/blenkernel/BKE_gpencil_modifier.h
+++ b/source/blender/blenkernel/BKE_gpencil_modifier.h
@@ -20,12 +20,14 @@
* \ingroup bke
*/
+#include "BLI_compiler_attrs.h"
#include "DNA_gpencil_modifier_types.h" /* needed for all enum typdefs */
#ifdef __cplusplus
extern "C" {
#endif
+struct ARegionType;
struct Depsgraph;
struct GpencilModifierData;
struct ID;
@@ -255,11 +257,17 @@ typedef struct GpencilModifierTypeInfo {
struct Object *ob,
GreasePencilTexWalkFunc walk,
void *userData);
+
+ /* Register the panel types for the modifier's UI. */
+ void (*panelRegister)(struct ARegionType *region_type);
} GpencilModifierTypeInfo;
+#define GPENCIL_MODIFIER_TYPE_PANEL_PREFIX "MOD_PT_gpencil_"
+
/* Initialize modifier's global data (type info and some common global storages). */
void BKE_gpencil_modifier_init(void);
+void BKE_gpencil_modifierType_panel_id(GpencilModifierType type, char *r_idname);
const GpencilModifierTypeInfo *BKE_gpencil_modifier_get_info(GpencilModifierType type);
struct GpencilModifierData *BKE_gpencil_modifier_new(int type);
void BKE_gpencil_modifier_free_ex(struct GpencilModifierData *md, const int flag);
@@ -276,6 +284,8 @@ void BKE_gpencil_modifier_copydata(struct GpencilModifierData *md,
void BKE_gpencil_modifier_copydata_ex(struct GpencilModifierData *md,
struct GpencilModifierData *target,
const int flag);
+void BKE_gpencil_modifier_set_error(struct GpencilModifierData *md, const char *format, ...)
+ ATTR_PRINTF_FORMAT(2, 3);
void BKE_gpencil_modifiers_foreach_ID_link(struct Object *ob,
GreasePencilIDWalkFunc walk,
void *userData);
diff --git a/source/blender/blenkernel/BKE_idprop.h b/source/blender/blenkernel/BKE_idprop.h
index 1272127daa0..dc01e8ea27b 100644
--- a/source/blender/blenkernel/BKE_idprop.h
+++ b/source/blender/blenkernel/BKE_idprop.h
@@ -110,9 +110,9 @@ bool IDP_InsertToGroup(struct IDProperty *group,
void IDP_RemoveFromGroup(struct IDProperty *group, struct IDProperty *prop) ATTR_NONNULL();
void IDP_FreeFromGroup(struct IDProperty *group, struct IDProperty *prop) ATTR_NONNULL();
-IDProperty *IDP_GetPropertyFromGroup(struct IDProperty *prop,
+IDProperty *IDP_GetPropertyFromGroup(const struct IDProperty *prop,
const char *name) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
-IDProperty *IDP_GetPropertyTypeFromGroup(struct IDProperty *prop,
+IDProperty *IDP_GetPropertyTypeFromGroup(const struct IDProperty *prop,
const char *name,
const char type) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h
index 7f5a6e3e36a..bc72afdd08d 100644
--- a/source/blender/blenkernel/BKE_lib_id.h
+++ b/source/blender/blenkernel/BKE_lib_id.h
@@ -144,6 +144,16 @@ struct ID *BKE_libblock_find_name(struct Main *bmain,
const short type,
const char *name) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
+/**
+ * Duplicate (a.k.a. deep copy) common processing options.
+ * See also eDupli_ID_Flags for options controlling what kind of IDs to duplicate.
+ */
+typedef enum eLibIDDuplicateFlags {
+ /** This call to a duplicate function is part of another call for some parent ID.
+ * Therefore, this sub-process should not clear `newid` pointers, nor handle remapping itself. */
+ LIB_ID_DUPLICATE_IS_SUBPROCESS = 1 << 0,
+} eLibIDDuplicateFlags;
+
/* lib_remap.c (keep here since they're general functions) */
/**
* New freeing logic options.
@@ -217,6 +227,10 @@ bool id_single_user(struct bContext *C,
bool BKE_id_copy_is_allowed(const struct ID *id);
bool BKE_id_copy(struct Main *bmain, const struct ID *id, struct ID **newid);
bool BKE_id_copy_ex(struct Main *bmain, const struct ID *id, struct ID **r_newid, const int flag);
+struct ID *BKE_id_copy_for_duplicate(struct Main *bmain,
+ struct ID *id,
+ const bool is_owner_id_liboverride,
+ const uint duplicate_flags);
void BKE_lib_id_swap(struct Main *bmain, struct ID *id_a, struct ID *id_b);
void BKE_lib_id_swap_full(struct Main *bmain, struct ID *id_a, struct ID *id_b);
@@ -253,6 +267,7 @@ void BKE_main_id_repair_duplicate_names_listbase(struct ListBase *lb);
void BKE_id_full_name_get(char name[MAX_ID_FULL_NAME], const struct ID *id, char separator_str);
void BKE_id_full_name_ui_prefix_get(char name[MAX_ID_FULL_NAME_UI],
const struct ID *id,
+ const bool add_lib_hint,
char separator_char);
char *BKE_id_to_unique_string_key(const struct ID *id);
diff --git a/source/blender/blenkernel/BKE_lib_override.h b/source/blender/blenkernel/BKE_lib_override.h
index 786d0b5ba97..411df66fe36 100644
--- a/source/blender/blenkernel/BKE_lib_override.h
+++ b/source/blender/blenkernel/BKE_lib_override.h
@@ -108,9 +108,7 @@ bool BKE_lib_override_library_property_operation_operands_validate(
bool BKE_lib_override_library_status_check_local(struct Main *bmain, struct ID *local);
bool BKE_lib_override_library_status_check_reference(struct Main *bmain, struct ID *local);
-bool BKE_lib_override_library_operations_create(struct Main *bmain,
- struct ID *local,
- const bool force_auto);
+bool BKE_lib_override_library_operations_create(struct Main *bmain, struct ID *local);
void BKE_lib_override_library_main_operations_create(struct Main *bmain, const bool force_auto);
void BKE_lib_override_library_operations_tag(struct IDOverrideLibraryProperty *override_property,
diff --git a/source/blender/blenkernel/BKE_main.h b/source/blender/blenkernel/BKE_main.h
index 516148728d2..b7c70168a49 100644
--- a/source/blender/blenkernel/BKE_main.h
+++ b/source/blender/blenkernel/BKE_main.h
@@ -173,6 +173,7 @@ void BKE_main_unlock(struct Main *bmain);
void BKE_main_relations_create(struct Main *bmain, const short flag);
void BKE_main_relations_free(struct Main *bmain);
+void BKE_main_relations_ID_remove(struct Main *bmain, struct ID *id);
struct GSet *BKE_main_gset_create(struct Main *bmain, struct GSet *gset);
diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h
index 46c6f68384e..7d989bfcf69 100644
--- a/source/blender/blenkernel/BKE_mesh.h
+++ b/source/blender/blenkernel/BKE_mesh.h
@@ -484,6 +484,7 @@ void BKE_mesh_poly_edgebitmap_insert(unsigned int *edge_bitmap,
const struct MLoop *mloop);
bool BKE_mesh_center_median(const struct Mesh *me, float r_cent[3]);
+bool BKE_mesh_center_median_from_polys(const struct Mesh *me, float r_cent[3]);
bool BKE_mesh_center_bounds(const struct Mesh *me, float r_cent[3]);
bool BKE_mesh_center_of_surface(const struct Mesh *me, float r_cent[3]);
bool BKE_mesh_center_of_volume(const struct Mesh *me, float r_cent[3]);
diff --git a/source/blender/blenkernel/BKE_mesh_remesh_voxel.h b/source/blender/blenkernel/BKE_mesh_remesh_voxel.h
index b63f9a9814b..24f95f7ed20 100644
--- a/source/blender/blenkernel/BKE_mesh_remesh_voxel.h
+++ b/source/blender/blenkernel/BKE_mesh_remesh_voxel.h
@@ -60,6 +60,7 @@ struct Mesh *BKE_mesh_remesh_quadriflow_to_mesh_nomain(struct Mesh *mesh,
/* Data reprojection functions */
void BKE_mesh_remesh_reproject_paint_mask(struct Mesh *target, struct Mesh *source);
+void BKE_remesh_reproject_vertex_paint(struct Mesh *target, struct Mesh *source);
void BKE_remesh_reproject_sculpt_face_sets(struct Mesh *target, struct Mesh *source);
#ifdef __cplusplus
diff --git a/source/blender/blenkernel/BKE_mesh_runtime.h b/source/blender/blenkernel/BKE_mesh_runtime.h
index fdddafcc71f..468ec6a44cd 100644
--- a/source/blender/blenkernel/BKE_mesh_runtime.h
+++ b/source/blender/blenkernel/BKE_mesh_runtime.h
@@ -49,6 +49,7 @@ void BKE_mesh_runtime_looptri_recalc(struct Mesh *mesh);
const struct MLoopTri *BKE_mesh_runtime_looptri_ensure(struct Mesh *mesh);
bool BKE_mesh_runtime_ensure_edit_data(struct Mesh *mesh);
bool BKE_mesh_runtime_clear_edit_data(struct Mesh *mesh);
+bool BKE_mesh_runtime_reset_edit_data(struct Mesh *mesh);
void BKE_mesh_runtime_clear_geometry(struct Mesh *mesh);
void BKE_mesh_runtime_clear_cache(struct Mesh *mesh);
diff --git a/source/blender/blenkernel/BKE_modifier.h b/source/blender/blenkernel/BKE_modifier.h
index 766ca78dc50..e16a9284425 100644
--- a/source/blender/blenkernel/BKE_modifier.h
+++ b/source/blender/blenkernel/BKE_modifier.h
@@ -48,25 +48,28 @@ typedef enum {
/* Should not be used, only for None modifier type */
eModifierTypeType_None,
- /* Modifier only does deformation, implies that modifier
+ /**
+ * Modifier only does deformation, implies that modifier
* type should have a valid deformVerts function. OnlyDeform
* style modifiers implicitly accept either mesh or CV
* input but should still declare flags appropriately.
*/
eModifierTypeType_OnlyDeform,
- /* Modifier adds geometry. */
+ /** Modifier adds geometry. */
eModifierTypeType_Constructive,
/* Modifier can add and remove geometry. */
eModifierTypeType_Nonconstructive,
- /* both deformVerts & applyModifier are valid calls
+ /**
+ * Both deformVerts & applyModifier are valid calls
* used for particles modifier that doesn't actually modify the object
* unless it's a mesh and can be exploded -> curve can also emit particles
*/
eModifierTypeType_DeformOrConstruct,
- /* Like eModifierTypeType_Nonconstructive, but does not affect the geometry
+ /**
+ * Like eModifierTypeType_Nonconstructive, but does not affect the geometry
* of the object, rather some of its CustomData layers.
* E.g. UVProject and WeightVG modifiers. */
eModifierTypeType_NonGeometrical,
@@ -78,7 +81,8 @@ typedef enum {
eModifierTypeFlag_SupportsMapping = (1 << 2),
eModifierTypeFlag_SupportsEditmode = (1 << 3),
- /* For modifiers that support editmode this determines if the
+ /**
+ * For modifiers that support editmode this determines if the
* modifier should be enabled by default in editmode. This should
* only be used by modifiers that are relatively speedy and
* also generally used in editmode, otherwise let the user enable
@@ -86,22 +90,25 @@ typedef enum {
*/
eModifierTypeFlag_EnableInEditmode = (1 << 4),
- /* For modifiers that require original data and so cannot
+ /**
+ * For modifiers that require original data and so cannot
* be placed after any non-deformative modifier.
*/
eModifierTypeFlag_RequiresOriginalData = (1 << 5),
- /* For modifiers that support pointcache,
- * so we can check to see if it has files we need to deal with. */
+ /**
+ * For modifiers that support pointcache,
+ * so we can check to see if it has files we need to deal with.
+ */
eModifierTypeFlag_UsesPointCache = (1 << 6),
- /* For physics modifiers, max one per type */
+ /** For physics modifiers, max one per type */
eModifierTypeFlag_Single = (1 << 7),
- /* Some modifier can't be added manually by user */
+ /** Some modifier can't be added manually by user */
eModifierTypeFlag_NoUserAdd = (1 << 8),
- /* For modifiers that use CD_PREVIEW_MCOL for preview. */
+ /** For modifiers that use CD_PREVIEW_MCOL for preview. */
eModifierTypeFlag_UsesPreview = (1 << 9),
eModifierTypeFlag_AcceptsVertexCosOnly = (1 << 10),
@@ -169,7 +176,8 @@ typedef struct ModifierTypeInfo {
/********************* Non-optional functions *********************/
- /* Copy instance data for this modifier type. Should copy all user
+ /**
+ * Copy instance data for this modifier type. Should copy all user
* level settings to the target modifier.
*
* \param flag: Copying options (see BKE_lib_id.h's LIB_ID_COPY_... flags for more).
@@ -178,7 +186,8 @@ typedef struct ModifierTypeInfo {
/********************* Deform modifier functions *********************/
- /* Only for deform types, should apply the deformation
+ /**
+ * Only for deform types, should apply the deformation
* to the given vertex array. If the deformer requires information from
* the object it can obtain it from the mesh argument if non-NULL,
* and otherwise the ob argument.
@@ -189,15 +198,17 @@ typedef struct ModifierTypeInfo {
float (*vertexCos)[3],
int numVerts);
- /* Like deformMatricesEM but called from object mode (for supporting modifiers in sculpt mode) */
+ /**
+ * Like deformMatricesEM but called from object mode (for supporting modifiers in sculpt mode).
+ */
void (*deformMatrices)(struct ModifierData *md,
const struct ModifierEvalContext *ctx,
struct Mesh *mesh,
float (*vertexCos)[3],
float (*defMats)[3][3],
int numVerts);
-
- /* Like deformVerts but called during editmode (for supporting modifiers)
+ /**
+ * Like deformVerts but called during editmode (for supporting modifiers)
*/
void (*deformVertsEM)(struct ModifierData *md,
const struct ModifierEvalContext *ctx,
@@ -206,7 +217,7 @@ typedef struct ModifierTypeInfo {
float (*vertexCos)[3],
int numVerts);
- /* Set deform matrix per vertex for crazyspace correction */
+ /* Set deform matrix per vertex for crazy-space correction */
void (*deformMatricesEM)(struct ModifierData *md,
const struct ModifierEvalContext *ctx,
struct BMEditMesh *editData,
@@ -217,7 +228,8 @@ typedef struct ModifierTypeInfo {
/********************* Non-deform modifier functions *********************/
- /* For non-deform types: apply the modifier and return a mesh data-block.
+ /**
+ * For non-deform types: apply the modifier and return a mesh data-block.
*
* The mesh argument should always be non-NULL; the modifier should use the
* passed in mesh data-block rather than object->data, as it contains the mesh
@@ -242,14 +254,16 @@ typedef struct ModifierTypeInfo {
/********************* Optional functions *********************/
- /* Initialize new instance data for this modifier type, this function
+ /**
+ * Initialize new instance data for this modifier type, this function
* should set modifier variables to their default values.
*
* This function is optional.
*/
void (*initData)(struct ModifierData *md);
- /* Should add to passed \a r_cddata_masks the data types that this
+ /**
+ * Should add to passed \a r_cddata_masks the data types that this
* modifier needs. If (mask & (1 << (layer type))) != 0, this modifier
* needs that custom data layer. It can change required layers
* depending on the modifier's settings.
@@ -266,7 +280,8 @@ typedef struct ModifierTypeInfo {
struct ModifierData *md,
struct CustomData_MeshMasks *r_cddata_masks);
- /* Free internal modifier data variables, this function should
+ /**
+ * Free internal modifier data variables, this function should
* not free the md variable itself.
*
* This function is responsible for freeing the runtime data as well.
@@ -275,7 +290,8 @@ typedef struct ModifierTypeInfo {
*/
void (*freeData)(struct ModifierData *md);
- /* Return a boolean value indicating if this modifier is able to be
+ /**
+ * Return a boolean value indicating if this modifier is able to be
* calculated based on the modifier data. This is *not* regarding the
* md->flag, that is tested by the system, this is just if the data
* validates (for example, a lattice will return false if the lattice
@@ -285,29 +301,33 @@ typedef struct ModifierTypeInfo {
*/
bool (*isDisabled)(const struct Scene *scene, struct ModifierData *md, bool userRenderParams);
- /* Add the appropriate relations to the dependency graph.
+ /**
+ * Add the appropriate relations to the dependency graph.
*
* This function is optional.
*/
void (*updateDepsgraph)(struct ModifierData *md, const ModifierUpdateDepsgraphContext *ctx);
- /* Should return true if the modifier needs to be recalculated on time
+ /**
+ * Should return true if the modifier needs to be recalculated on time
* changes.
*
* This function is optional (assumes false if not present).
*/
bool (*dependsOnTime)(struct ModifierData *md);
- /* True when a deform modifier uses normals, the requiredDataMask
+ /**
+ * True when a deform modifier uses normals, the requiredDataMask
* cant be used here because that refers to a normal layer whereas
* in this case we need to know if the deform modifier uses normals.
*
* this is needed because applying 2 deform modifiers will give the
* second modifier bogus normals.
- * */
+ */
bool (*dependsOnNormals)(struct ModifierData *md);
- /* Should call the given walk function on with a pointer to each Object
+ /**
+ * Should call the given walk function on with a pointer to each Object
* pointer that the modifier data stores. This is used for linking on file
* load and for unlinking objects or forwarding object references.
*
@@ -318,7 +338,8 @@ typedef struct ModifierTypeInfo {
ObjectWalkFunc walk,
void *userData);
- /* Should call the given walk function with a pointer to each ID
+ /**
+ * Should call the given walk function with a pointer to each ID
* pointer (i.e. each data-block pointer) that the modifier data
* stores. This is used for linking on file load and for
* unlinking data-blocks or forwarding data-block references.
@@ -331,7 +352,8 @@ typedef struct ModifierTypeInfo {
IDWalkFunc walk,
void *userData);
- /* Should call the given walk function for each texture that the
+ /**
+ * Should call the given walk function for each texture that the
* modifier data stores. This is used for finding all textures in
* the context for the UI.
*
@@ -343,7 +365,8 @@ typedef struct ModifierTypeInfo {
TexWalkFunc walk,
void *userData);
- /* Free given run-time data.
+ /**
+ * Free given run-time data.
*
* This data is coming from a modifier of the corresponding type, but actual
* modifier data is not known here.
@@ -355,10 +378,11 @@ typedef struct ModifierTypeInfo {
*/
void (*freeRuntimeData)(void *runtime_data);
- /* Register the panel types for the modifier's UI. */
+ /** Register the panel types for the modifier's UI. */
void (*panelRegister)(struct ARegionType *region_type);
- /* Is called when the modifier is written to a file. The modifier data struct itself is written
+ /**
+ * Is called when the modifier is written to a file. The modifier data struct itself is written
* already.
*
* This method should write any additional arrays and referenced structs that should be
@@ -366,7 +390,8 @@ typedef struct ModifierTypeInfo {
*/
void (*blendWrite)(struct BlendWriter *writer, const struct ModifierData *md);
- /* Is called when the modifier is read from a file.
+ /**
+ * Is called when the modifier is read from a file.
*
* It can be used to update pointers to arrays and other structs. Furthermore, fields that have
* not been written (e.g. runtime data) can be reset.
@@ -447,7 +472,8 @@ typedef struct CDMaskLink {
struct CustomData_MeshMasks mask;
} CDMaskLink;
-/* Calculates and returns a linked list of CustomData_MeshMasks and modified
+/**
+ * Calculates and returns a linked list of CustomData_MeshMasks and modified
* final datamask, indicating the data required by each modifier in the stack
* pointed to by md for correct evaluation, assuming the data indicated by
* final_datamask is required at the end of the stack.
@@ -473,7 +499,7 @@ typedef struct VirtualModifierData {
struct ModifierData *BKE_modifiers_get_virtual_modifierlist(const struct Object *ob,
struct VirtualModifierData *data);
-/* ensure modifier correctness when changing ob->data */
+/** Ensure modifier correctness when changing ob->data. */
void BKE_modifiers_test_object(struct Object *ob);
/* here for do_versions */
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h
index 536d04f8bd3..bdcbe2129c8 100644
--- a/source/blender/blenkernel/BKE_node.h
+++ b/source/blender/blenkernel/BKE_node.h
@@ -500,7 +500,7 @@ const char *nodeStaticSocketInterfaceType(int type, int subtype);
} \
((void)0)
-struct bNodeSocket *nodeFindSocket(struct bNode *node, int in_out, const char *identifier);
+struct bNodeSocket *nodeFindSocket(const struct bNode *node, int in_out, const char *identifier);
struct bNodeSocket *nodeAddSocket(struct bNodeTree *ntree,
struct bNode *node,
int in_out,
diff --git a/source/blender/blenkernel/BKE_node_tree_ref.hh b/source/blender/blenkernel/BKE_node_tree_ref.hh
index 043ef957af7..557eb44e5df 100644
--- a/source/blender/blenkernel/BKE_node_tree_ref.hh
+++ b/source/blender/blenkernel/BKE_node_tree_ref.hh
@@ -58,17 +58,8 @@
#include "RNA_access.h"
-namespace BKE {
-
-using blender::Array;
-using blender::IndexRange;
-using blender::LinearAllocator;
-using blender::Map;
-using blender::MutableSpan;
-using blender::Span;
-using blender::StringRef;
-using blender::StringRefNull;
-using blender::Vector;
+namespace blender {
+namespace bke {
class SocketRef;
class InputSocketRef;
@@ -76,7 +67,7 @@ class OutputSocketRef;
class NodeRef;
class NodeTreeRef;
-class SocketRef : blender::NonCopyable, blender::NonMovable {
+class SocketRef : NonCopyable, NonMovable {
protected:
NodeRef *m_node;
bNodeSocket *m_bsocket;
@@ -129,7 +120,7 @@ class OutputSocketRef final : public SocketRef {
Span<const InputSocketRef *> directly_linked_sockets() const;
};
-class NodeRef : blender::NonCopyable, blender::NonMovable {
+class NodeRef : NonCopyable, NonMovable {
private:
NodeTreeRef *m_tree;
bNode *m_bnode;
@@ -164,7 +155,7 @@ class NodeRef : blender::NonCopyable, blender::NonMovable {
bool is_group_output_node() const;
};
-class NodeTreeRef : blender::NonCopyable, blender::NonMovable {
+class NodeTreeRef : NonCopyable, NonMovable {
private:
LinearAllocator<> m_allocator;
bNodeTree *m_btree;
@@ -172,14 +163,15 @@ class NodeTreeRef : blender::NonCopyable, blender::NonMovable {
Vector<SocketRef *> m_sockets_by_id;
Vector<InputSocketRef *> m_input_sockets;
Vector<OutputSocketRef *> m_output_sockets;
- Map<std::string, Vector<NodeRef *>> m_nodes_by_idname;
+ Map<const bNodeType *, Vector<NodeRef *>> m_nodes_by_type;
public:
NodeTreeRef(bNodeTree *btree);
~NodeTreeRef();
Span<const NodeRef *> nodes() const;
- Span<const NodeRef *> nodes_with_idname(StringRef idname) const;
+ Span<const NodeRef *> nodes_by_type(StringRefNull idname) const;
+ Span<const NodeRef *> nodes_by_type(const bNodeType *nodetype) const;
Span<const SocketRef *> sockets() const;
Span<const InputSocketRef *> input_sockets() const;
@@ -412,9 +404,15 @@ inline Span<const NodeRef *> NodeTreeRef::nodes() const
return m_nodes_by_id.as_span();
}
-inline Span<const NodeRef *> NodeTreeRef::nodes_with_idname(StringRef idname) const
+inline Span<const NodeRef *> NodeTreeRef::nodes_by_type(StringRefNull idname) const
{
- const Vector<NodeRef *> *nodes = m_nodes_by_idname.lookup_ptr(idname);
+ const bNodeType *nodetype = nodeTypeFind(idname.data());
+ return this->nodes_by_type(nodetype);
+}
+
+inline Span<const NodeRef *> NodeTreeRef::nodes_by_type(const bNodeType *nodetype) const
+{
+ const Vector<NodeRef *> *nodes = m_nodes_by_type.lookup_ptr(nodetype);
if (nodes == nullptr) {
return {};
}
@@ -443,6 +441,7 @@ inline bNodeTree *NodeTreeRef::btree() const
return m_btree;
}
-} // namespace BKE
+} // namespace bke
+} // namespace blender
#endif /* __BKE_NODE_TREE_REF_HH__ */
diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h
index 3710ec810ce..d830a35dda0 100644
--- a/source/blender/blenkernel/BKE_object.h
+++ b/source/blender/blenkernel/BKE_object.h
@@ -23,6 +23,7 @@
*/
#include "BLI_compiler_attrs.h"
+#include "BLI_sys_types.h"
#include "DNA_object_enums.h"
@@ -137,8 +138,9 @@ bool BKE_object_is_libdata(const struct Object *ob);
bool BKE_object_obdata_is_libdata(const struct Object *ob);
struct Object *BKE_object_duplicate(struct Main *bmain,
- const struct Object *ob,
- const int dupflag);
+ struct Object *ob,
+ const uint dupflag,
+ const uint duplicate_options);
void BKE_object_obdata_size_init(struct Object *ob, const float scale);
diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h
index 6e4f2efeeb8..78092884655 100644
--- a/source/blender/blenkernel/BKE_paint.h
+++ b/source/blender/blenkernel/BKE_paint.h
@@ -37,8 +37,8 @@ struct Brush;
struct CurveMapping;
struct Depsgraph;
struct EnumPropertyItem;
+struct EdgeSet;
struct GHash;
-struct GSet;
struct GridPaintMask;
struct ImagePool;
struct MLoop;
@@ -269,7 +269,7 @@ typedef struct SculptClothLengthConstraint {
typedef struct SculptClothSimulation {
SculptClothLengthConstraint *length_constraints;
int tot_length_constraints;
- struct GSet *created_length_constraints;
+ struct EdgeSet *created_length_constraints;
int capacity_length_constraints;
float *length_constraint_tweak;
@@ -283,11 +283,27 @@ typedef struct SculptClothSimulation {
} SculptClothSimulation;
-typedef struct SculptLayerPersistentBase {
+typedef struct SculptPersistentBase {
float co[3];
float no[3];
float disp;
-} SculptLayerPersistentBase;
+} SculptPersistentBase;
+
+typedef struct SculptVertexInfo {
+ /* Idexed by vertex, stores and ID of its topologycally connected component. */
+ int *connected_component;
+} SculptVertexInfo;
+
+typedef struct SculptFakeNeighbors {
+ bool use_fake_neighbors;
+
+ /* Max distance used to calculate neighborhood information. */
+ float current_max_distance;
+
+ /* Idexed by vertex, stores the vertex index of its fake neighbor if available. */
+ int *fake_neighbor_index;
+
+} SculptFakeNeighbors;
/* Session data (mode-specific) */
@@ -308,6 +324,7 @@ typedef struct SculptSession {
int totvert, totpoly;
struct KeyBlock *shapekey_active;
+ struct MPropCol *vcol;
float *vmask;
/* Mesh connectivity */
@@ -338,10 +355,10 @@ typedef struct SculptSession {
bool show_face_sets;
/* Painting on deformed mesh */
- bool deform_modifiers_active; /* object is deformed with some modifiers */
- float (*orig_cos)[3]; /* coords of undeformed mesh */
- float (*deform_cos)[3]; /* coords of deformed mesh but without stroke displacement */
- float (*deform_imats)[3][3]; /* crazyspace deformation matrices */
+ bool deform_modifiers_active; /* Object is deformed with some modifiers. */
+ float (*orig_cos)[3]; /* Coords of un-deformed mesh. */
+ float (*deform_cos)[3]; /* Coords of deformed mesh but without stroke displacement. */
+ float (*deform_imats)[3][3]; /* Crazy-space deformation matrices. */
/* Used to cache the render of the active texture */
unsigned int texcache_side, *texcache, texcache_actual;
@@ -365,6 +382,7 @@ typedef struct SculptSession {
/* TODO(jbakker): Replace rv3d adn v3d with ViewContext */
struct RegionView3D *rv3d;
struct View3D *v3d;
+ struct Scene *scene;
/* Dynamic mesh preview */
int *preview_vert_index_list;
@@ -374,9 +392,12 @@ typedef struct SculptSession {
float pose_origin[3];
SculptPoseIKChain *pose_ik_chain_preview;
- /* Layer brush persistence between strokes */
+ /* Mesh State Persistence */
/* This is freed with the PBVH, so it is always in sync with the mesh. */
- SculptLayerPersistentBase *layer_base;
+ SculptPersistentBase *persistent_base;
+
+ SculptVertexInfo vertex_info;
+ SculptFakeNeighbors fake_neighbors;
/* Transform operator */
float pivot_pos[3];
@@ -430,7 +451,8 @@ void BKE_sculptsession_bm_to_me_for_render(struct Object *object);
void BKE_sculpt_update_object_for_edit(struct Depsgraph *depsgraph,
struct Object *ob_orig,
bool need_pmap,
- bool need_mask);
+ bool need_mask,
+ bool need_colors);
void BKE_sculpt_update_object_before_eval(struct Object *ob_eval);
void BKE_sculpt_update_object_after_eval(struct Depsgraph *depsgraph, struct Object *ob_eval);
diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h
index 8fb6f140822..87875314aec 100644
--- a/source/blender/blenkernel/BKE_pbvh.h
+++ b/source/blender/blenkernel/BKE_pbvh.h
@@ -58,6 +58,10 @@ typedef struct {
float (*co)[3];
} PBVHProxyNode;
+typedef struct {
+ float (*color)[4];
+} PBVHColorBufferNode;
+
typedef enum {
PBVH_Leaf = 1 << 0,
@@ -75,6 +79,7 @@ typedef enum {
PBVH_FullyUnmasked = 1 << 12,
PBVH_UpdateTopology = 1 << 13,
+ PBVH_UpdateColor = 1 << 14,
} PBVHNodeFlags;
typedef struct PBVHFrustumPlanes {
@@ -252,6 +257,7 @@ bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh,
void BKE_pbvh_node_mark_update(PBVHNode *node);
void BKE_pbvh_node_mark_update_mask(PBVHNode *node);
+void BKE_pbvh_node_mark_update_color(PBVHNode *node);
void BKE_pbvh_node_mark_update_visibility(PBVHNode *node);
void BKE_pbvh_node_mark_rebuild_draw(PBVHNode *node);
void BKE_pbvh_node_mark_redraw(PBVHNode *node);
@@ -352,6 +358,7 @@ typedef struct PBVHVertexIter {
struct MVert *mverts;
int totvert;
const int *vert_indices;
+ struct MPropCol *vcol;
float *vmask;
/* bmesh */
@@ -368,6 +375,7 @@ typedef struct PBVHVertexIter {
short *no;
float *fno;
float *mask;
+ float *col;
bool visible;
} PBVHVertexIter;
@@ -419,7 +427,9 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m
vi.no = vi.mvert->no; \
vi.index = vi.vert_indices[vi.i]; \
if (vi.vmask) \
- vi.mask = &vi.vmask[vi.vert_indices[vi.gx]]; \
+ vi.mask = &vi.vmask[vi.index]; \
+ if (vi.vcol) \
+ vi.col = vi.vcol[vi.index].color; \
} \
else { \
if (!BLI_gsetIterator_done(&vi.bm_unique_verts)) { \
@@ -472,6 +482,9 @@ void BKE_pbvh_parallel_range_settings(struct TaskParallelSettings *settings,
struct MVert *BKE_pbvh_get_verts(const PBVH *pbvh);
+PBVHColorBufferNode *BKE_pbvh_node_color_buffer_get(PBVHNode *node);
+void BKE_pbvh_node_color_buffer_free(PBVH *pbvh);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenkernel/BKE_pointcache.h b/source/blender/blenkernel/BKE_pointcache.h
index a8c6fe4416f..8f9c53ffc05 100644
--- a/source/blender/blenkernel/BKE_pointcache.h
+++ b/source/blender/blenkernel/BKE_pointcache.h
@@ -132,7 +132,7 @@ typedef struct PTCacheID {
struct PTCacheID *next, *prev;
struct Scene *scene;
- struct Object *ob;
+ struct ID *owner_id;
void *calldata;
unsigned int type, file_type;
unsigned int stack_index;
diff --git a/source/blender/blenkernel/BKE_screen.h b/source/blender/blenkernel/BKE_screen.h
index fc7146b8cf4..91f241018ec 100644
--- a/source/blender/blenkernel/BKE_screen.h
+++ b/source/blender/blenkernel/BKE_screen.h
@@ -142,6 +142,8 @@ typedef struct ARegionType {
void (*exit)(struct wmWindowManager *wm, struct ARegion *region);
/* draw entirely, view changes should be handled here */
void (*draw)(const struct bContext *C, struct ARegion *region);
+ /* Handler to draw overlays. This handler is called every draw loop. */
+ void (*draw_overlay)(const struct bContext *C, struct ARegion *region);
/* optional, compute button layout before drawing for dynamic size */
void (*layout)(const struct bContext *C, struct ARegion *region);
/* snap the size of the region (can be NULL for no snapping). */
@@ -211,7 +213,7 @@ typedef struct PanelType {
char context[BKE_ST_MAXNAME]; /* for buttons window */
char category[BKE_ST_MAXNAME]; /* for category tabs */
char owner_id[BKE_ST_MAXNAME]; /* for work-spaces to selectively show. */
- char parent_id[BKE_ST_MAXNAME]; /* parent idname for subpanels */
+ char parent_id[BKE_ST_MAXNAME]; /* parent idname for sub-panels */
short space_type;
short region_type;
/* For popovers, 0 for default. */
diff --git a/source/blender/blenkernel/BKE_shader_fx.h b/source/blender/blenkernel/BKE_shader_fx.h
index bdc782a606e..31e14c6c884 100644
--- a/source/blender/blenkernel/BKE_shader_fx.h
+++ b/source/blender/blenkernel/BKE_shader_fx.h
@@ -28,6 +28,7 @@
extern "C" {
#endif
+struct ARegionType;
struct ID;
struct ListBase;
struct ModifierUpdateDepsgraphContext;
@@ -157,11 +158,17 @@ typedef struct ShaderFxTypeInfo {
struct Object *ob,
ShaderFxIDWalkFunc walk,
void *userData);
+
+ /* Register the panel types for the effect's UI. */
+ void (*panelRegister)(struct ARegionType *region_type);
} ShaderFxTypeInfo;
+#define SHADERFX_TYPE_PANEL_PREFIX "FX_PT_"
+
/* Initialize global data (type info and some common global storages). */
void BKE_shaderfx_init(void);
+void BKE_shaderfxType_panel_id(ShaderFxType type, char *panel_id);
const ShaderFxTypeInfo *BKE_shaderfx_get_info(ShaderFxType type);
struct ShaderFxData *BKE_shaderfx_new(int type);
void BKE_shaderfx_free_ex(struct ShaderFxData *fx, const int flag);
diff --git a/source/blender/blenkernel/BKE_subdiv_ccg.h b/source/blender/blenkernel/BKE_subdiv_ccg.h
index 8d2565c31f7..78e91d3ad2f 100644
--- a/source/blender/blenkernel/BKE_subdiv_ccg.h
+++ b/source/blender/blenkernel/BKE_subdiv_ccg.h
@@ -214,6 +214,12 @@ typedef struct SubdivCCG {
/* Corresponds to MULTIRES_HIDDEN_MODIFIED. */
bool hidden;
} dirty;
+
+ /* Cached values, are not supposed to be accessed directly. */
+ struct {
+ /* Indexed by face, indicates index of the first grid which corresponds to the face. */
+ int *start_face_grid_index;
+ } cache_;
} SubdivCCG;
/* Create CCG representation of subdivision surface.
@@ -307,6 +313,15 @@ void BKE_subdiv_ccg_neighbor_coords_get(const SubdivCCG *subdiv_ccg,
int BKE_subdiv_ccg_grid_to_face_index(const SubdivCCG *subdiv_ccg, const int grid_index);
+/* Get array which is indexed by face index and contains index of a first grid of the face.
+ *
+ * The "ensure" version allocates the mapping if it's not know yet and stores it in the subdiv_ccg
+ * descriptor. This function is NOT safe for threading.
+ *
+ * The "get" version simply returns cached array. */
+const int *BKE_subdiv_ccg_start_face_grid_index_ensure(SubdivCCG *subdiv_ccg);
+const int *BKE_subdiv_ccg_start_face_grid_index_get(const SubdivCCG *subdiv_ccg);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenkernel/BKE_undo_system.h b/source/blender/blenkernel/BKE_undo_system.h
index f462a7fab71..b32c3e315ff 100644
--- a/source/blender/blenkernel/BKE_undo_system.h
+++ b/source/blender/blenkernel/BKE_undo_system.h
@@ -120,7 +120,9 @@ typedef struct UndoType {
/**
* \note When freeing all steps,
- * free from the last since #MemFileUndoType will merge with the next undo type in the list. */
+ * free from the last since #BKE_UNDOSYS_TYPE_MEMFILE
+ * will merge with the next undo type in the list.
+ */
void (*step_free)(UndoStep *us);
void (*step_foreach_ID_ref)(UndoStep *us,
diff --git a/source/blender/blenkernel/intern/DerivedMesh.c b/source/blender/blenkernel/intern/DerivedMesh.c
index b4e2cd772c9..8a5be6b9cb3 100644
--- a/source/blender/blenkernel/intern/DerivedMesh.c
+++ b/source/blender/blenkernel/intern/DerivedMesh.c
@@ -1669,7 +1669,9 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph,
else {
Mesh *me_orig = mesh_input;
if (me_orig->id.tag & LIB_TAG_COPIED_ON_WRITE) {
- BKE_mesh_runtime_ensure_edit_data(me_orig);
+ if (!BKE_mesh_runtime_ensure_edit_data(me_orig)) {
+ BKE_mesh_runtime_reset_edit_data(me_orig);
+ }
me_orig->runtime.edit_data->vertexCos = MEM_dupallocN(deformed_verts);
}
mesh_cage = BKE_mesh_wrapper_from_editmesh_with_coords(
diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c
index c776f0d077d..b35d2183408 100644
--- a/source/blender/blenkernel/intern/action.c
+++ b/source/blender/blenkernel/intern/action.c
@@ -939,7 +939,7 @@ void BKE_pose_channel_free_ex(bPoseChannel *pchan, bool do_id_user)
BKE_constraints_free_ex(&pchan->constraints, do_id_user);
if (pchan->prop) {
- IDP_FreeProperty(pchan->prop);
+ IDP_FreeProperty_ex(pchan->prop, do_id_user);
pchan->prop = NULL;
}
diff --git a/source/blender/blenkernel/intern/anim_data.c b/source/blender/blenkernel/intern/anim_data.c
index cbb34cbee30..841fdaa3b2c 100644
--- a/source/blender/blenkernel/intern/anim_data.c
+++ b/source/blender/blenkernel/intern/anim_data.c
@@ -399,6 +399,8 @@ void BKE_animdata_copy_id_action(Main *bmain, ID *id, const bool set_newid)
if (ntree) {
BKE_animdata_copy_id_action(bmain, &ntree->id, set_newid);
}
+ /* Note that collections are not animatable currently, so no need to handle scenes' master
+ * collection here. */
}
/* Merge copies of the data from the src AnimData into the destination AnimData */
diff --git a/source/blender/blenkernel/intern/armature_deform.c b/source/blender/blenkernel/intern/armature_deform.c
index e757e30e524..44b50ab96d3 100644
--- a/source/blender/blenkernel/intern/armature_deform.c
+++ b/source/blender/blenkernel/intern/armature_deform.c
@@ -349,8 +349,7 @@ static void armature_vert_task_with_dvert(const ArmatureUserdata *data,
pchan_bone_deform(pchan, weight, vec, dq, smat, co, &contrib);
}
}
- /* if there are vertexgroups but not groups with bones
- * (like for softbody groups) */
+ /* If there are vertex-groups but not groups with bones (like for soft-body groups). */
if (deformed == 0 && use_envelope) {
for (pchan = data->ob_arm->pose->chanbase.first; pchan; pchan = pchan->next) {
if (!(pchan->bone->flag & BONE_NO_DEFORM)) {
diff --git a/source/blender/blenkernel/intern/blender_copybuffer.c b/source/blender/blenkernel/intern/blender_copybuffer.c
index 4d27621a861..e950e94655a 100644
--- a/source/blender/blenkernel/intern/blender_copybuffer.c
+++ b/source/blender/blenkernel/intern/blender_copybuffer.c
@@ -72,9 +72,10 @@ void BKE_copybuffer_tag_ID(ID *id)
*/
bool BKE_copybuffer_save(Main *bmain_src, const char *filename, ReportList *reports)
{
- const int write_flags = G_FILE_RELATIVE_REMAP;
+ const int write_flags = 0;
+ const eBLO_WritePathRemap remap_mode = BLO_WRITE_PATH_REMAP_RELATIVE;
- bool retval = BKE_blendfile_write_partial(bmain_src, filename, write_flags, reports);
+ bool retval = BKE_blendfile_write_partial(bmain_src, filename, write_flags, remap_mode, reports);
BKE_blendfile_write_partial_end(bmain_src);
@@ -99,7 +100,7 @@ bool BKE_copybuffer_read(Main *bmain_dst,
BKE_main_lib_objects_recalc_all(bmain_dst);
IMB_colormanagement_check_file_config(bmain_dst);
/* Append, rather than linking. */
- Library *lib = BLI_findstring(&bmain_dst->libraries, libname, offsetof(Library, filepath));
+ Library *lib = BLI_findstring(&bmain_dst->libraries, libname, offsetof(Library, filepath_abs));
BKE_library_make_local(bmain_dst, lib, NULL, true, false);
/* Important we unset, otherwise these object wont
* link into other scenes from this blend file.
@@ -154,7 +155,7 @@ int BKE_copybuffer_paste(bContext *C,
IMB_colormanagement_check_file_config(bmain);
/* append, rather than linking */
- lib = BLI_findstring(&bmain->libraries, libname, offsetof(Library, filepath));
+ lib = BLI_findstring(&bmain->libraries, libname, offsetof(Library, filepath_abs));
BKE_library_make_local(bmain, lib, NULL, true, false);
/* important we unset, otherwise these object wont
diff --git a/source/blender/blenkernel/intern/blender_undo.c b/source/blender/blenkernel/intern/blender_undo.c
index ab382d0e8ff..a6f84f3c3a4 100644
--- a/source/blender/blenkernel/intern/blender_undo.c
+++ b/source/blender/blenkernel/intern/blender_undo.c
@@ -109,7 +109,6 @@ MemFileUndoData *BKE_memfile_undo_encode(Main *bmain, MemFileUndoData *mfu_prev)
static int counter = 0;
char filename[FILE_MAX];
char numstr[32];
- int fileflags = G.fileflags & ~(G_FILE_HISTORY); /* don't do file history on undo */
/* Calculate current filename. */
counter++;
@@ -118,7 +117,8 @@ MemFileUndoData *BKE_memfile_undo_encode(Main *bmain, MemFileUndoData *mfu_prev)
BLI_snprintf(numstr, sizeof(numstr), "%d.blend", counter);
BLI_join_dirfile(filename, sizeof(filename), BKE_tempdir_session(), numstr);
- /* success = */ /* UNUSED */ BLO_write_file(bmain, filename, fileflags, NULL, NULL);
+ /* success = */ /* UNUSED */ BLO_write_file(
+ bmain, filename, G.fileflags, &(const struct BlendFileWriteParams){0}, NULL);
BLI_strncpy(mfu->filename, filename, sizeof(mfu->filename));
}
diff --git a/source/blender/blenkernel/intern/blendfile.c b/source/blender/blenkernel/intern/blendfile.c
index ef474022f19..ee60bf79611 100644
--- a/source/blender/blenkernel/intern/blendfile.c
+++ b/source/blender/blenkernel/intern/blendfile.c
@@ -646,7 +646,13 @@ bool BKE_blendfile_userdef_write(const char *filepath, ReportList *reports)
Main *mainb = MEM_callocN(sizeof(Main), "empty main");
bool ok = false;
- if (BLO_write_file(mainb, filepath, G_FILE_USERPREFS, reports, NULL)) {
+ if (BLO_write_file(mainb,
+ filepath,
+ 0,
+ &(const struct BlendFileWriteParams){
+ .use_userdef = true,
+ },
+ reports)) {
ok = true;
}
@@ -768,7 +774,7 @@ WorkspaceConfigFileData *BKE_blendfile_workspace_config_read(const char *filepat
bool BKE_blendfile_workspace_config_write(Main *bmain, const char *filepath, ReportList *reports)
{
- int fileflags = G.fileflags & ~(G_FILE_NO_UI | G_FILE_HISTORY);
+ const int fileflags = G.fileflags & ~G_FILE_NO_UI;
bool retval = false;
BKE_blendfile_write_partial_begin(bmain);
@@ -777,7 +783,8 @@ bool BKE_blendfile_workspace_config_write(Main *bmain, const char *filepath, Rep
BKE_blendfile_write_partial_tag_ID(&workspace->id, true);
}
- if (BKE_blendfile_write_partial(bmain, filepath, fileflags, reports)) {
+ if (BKE_blendfile_write_partial(
+ bmain, filepath, fileflags, BLO_WRITE_PATH_REMAP_NONE, reports)) {
retval = true;
}
@@ -829,11 +836,13 @@ static void blendfile_write_partial_cb(void *UNUSED(handle), Main *UNUSED(bmain)
}
/**
+ * \param remap_mode: Choose the kind of path remapping or none #eBLO_WritePathRemap.
* \return Success.
*/
bool BKE_blendfile_write_partial(Main *bmain_src,
const char *filepath,
const int write_flags,
+ const int remap_mode,
ReportList *reports)
{
Main *bmain_dst = MEM_callocN(sizeof(Main), "copybuffer");
@@ -875,12 +884,18 @@ bool BKE_blendfile_write_partial(Main *bmain_src,
* This happens because id_sort_by_name does not take into account
* string case or the library name, so the order is not strictly
* defined for two linked data-blocks with the same name! */
- if (write_flags & G_FILE_RELATIVE_REMAP) {
+ if (remap_mode != BLO_WRITE_PATH_REMAP_NONE) {
path_list_backup = BKE_bpath_list_backup(bmain_dst, path_list_flag);
}
/* save the buffer */
- retval = BLO_write_file(bmain_dst, filepath, write_flags, reports, NULL);
+ retval = BLO_write_file(bmain_dst,
+ filepath,
+ write_flags,
+ &(const struct BlendFileWriteParams){
+ .remap_mode = remap_mode,
+ },
+ reports);
if (path_list_backup) {
BKE_bpath_list_restore(bmain_dst, path_list_flag, path_list_backup);
diff --git a/source/blender/blenkernel/intern/boids.c b/source/blender/blenkernel/intern/boids.c
index 6197b9dbefd..d5064629451 100644
--- a/source/blender/blenkernel/intern/boids.c
+++ b/source/blender/blenkernel/intern/boids.c
@@ -218,7 +218,7 @@ static int rule_avoid_collision(BoidRule *rule,
BoidValues *val,
ParticleData *pa)
{
- const int raycast_flag = BVH_RAYCAST_DEFAULT & ~(BVH_RAYCAST_WATERTIGHT);
+ const int raycast_flag = BVH_RAYCAST_DEFAULT & ~BVH_RAYCAST_WATERTIGHT;
BoidRuleAvoidCollision *acbr = (BoidRuleAvoidCollision *)rule;
KDTreeNearest_3d *ptn = NULL;
ParticleTarget *pt;
@@ -854,7 +854,7 @@ static Object *boid_find_ground(BoidBrainData *bbd,
float ground_co[3],
float ground_nor[3])
{
- const int raycast_flag = BVH_RAYCAST_DEFAULT & ~(BVH_RAYCAST_WATERTIGHT);
+ const int raycast_flag = BVH_RAYCAST_DEFAULT & ~BVH_RAYCAST_WATERTIGHT;
BoidParticle *bpa = pa->boid;
if (bpa->data.mode == eBoidMode_Climbing) {
@@ -1388,6 +1388,7 @@ void boid_body(BoidBrainData *bbd, ParticleData *pa)
bbd->part->effector_weights,
&epoint,
force,
+ NULL,
NULL);
if (ELEM(bpa->data.mode, eBoidMode_OnLand, eBoidMode_Climbing)) {
diff --git a/source/blender/blenkernel/intern/bpath.c b/source/blender/blenkernel/intern/bpath.c
index f0e23ddad41..10783c741b6 100644
--- a/source/blender/blenkernel/intern/bpath.c
+++ b/source/blender/blenkernel/intern/bpath.c
@@ -564,8 +564,8 @@ void BKE_bpath_traverse_id(
* don't make sense to add directories to until the image has been saved
* once to give it a meaningful value. */
if (ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_MOVIE, IMA_SRC_SEQUENCE, IMA_SRC_TILED) &&
- ima->name[0]) {
- if (rewrite_path_fixed(ima->name, visit_cb, absbase, bpath_user_data)) {
+ ima->filepath[0]) {
+ if (rewrite_path_fixed(ima->filepath, visit_cb, absbase, bpath_user_data)) {
if (flag & BKE_BPATH_TRAVERSE_RELOAD_EDITED) {
if (!BKE_image_has_packedfile(ima) &&
/* image may have been painted onto (and not saved, T44543) */
@@ -643,7 +643,7 @@ void BKE_bpath_traverse_id(
case ID_SO: {
bSound *sound = (bSound *)id;
if (sound->packedfile == NULL || (flag & BKE_BPATH_TRAVERSE_SKIP_PACKED) == 0) {
- rewrite_path_fixed(sound->name, visit_cb, absbase, bpath_user_data);
+ rewrite_path_fixed(sound->filepath, visit_cb, absbase, bpath_user_data);
}
break;
}
@@ -655,15 +655,15 @@ void BKE_bpath_traverse_id(
break;
}
case ID_TXT:
- if (((Text *)id)->name) {
- rewrite_path_alloc(&((Text *)id)->name, visit_cb, absbase, bpath_user_data);
+ if (((Text *)id)->filepath) {
+ rewrite_path_alloc(&((Text *)id)->filepath, visit_cb, absbase, bpath_user_data);
}
break;
case ID_VF: {
VFont *vfont = (VFont *)id;
if (vfont->packedfile == NULL || (flag & BKE_BPATH_TRAVERSE_SKIP_PACKED) == 0) {
if (BKE_vfont_is_builtin(vfont) == false) {
- rewrite_path_fixed(((VFont *)id)->name, visit_cb, absbase, bpath_user_data);
+ rewrite_path_fixed(((VFont *)id)->filepath, visit_cb, absbase, bpath_user_data);
}
}
break;
@@ -756,15 +756,15 @@ void BKE_bpath_traverse_id(
Library *lib = (Library *)id;
/* keep packedfile paths always relative to the blend */
if (lib->packedfile == NULL) {
- if (rewrite_path_fixed(lib->name, visit_cb, absbase, bpath_user_data)) {
- BKE_library_filepath_set(bmain, lib, lib->name);
+ if (rewrite_path_fixed(lib->filepath, visit_cb, absbase, bpath_user_data)) {
+ BKE_library_filepath_set(bmain, lib, lib->filepath);
}
}
break;
}
case ID_MC: {
MovieClip *clip = (MovieClip *)id;
- rewrite_path_fixed(clip->name, visit_cb, absbase, bpath_user_data);
+ rewrite_path_fixed(clip->filepath, visit_cb, absbase, bpath_user_data);
break;
}
case ID_CF: {
diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c
index 3241518cae5..a8f52593429 100644
--- a/source/blender/blenkernel/intern/brush.c
+++ b/source/blender/blenkernel/intern/brush.c
@@ -248,6 +248,7 @@ static void brush_defaults(Brush *brush)
FROM_DEFAULT(crease_pinch_factor);
FROM_DEFAULT(normal_radius_factor);
FROM_DEFAULT(area_radius_factor);
+ FROM_DEFAULT(disconnected_distance_max);
FROM_DEFAULT(sculpt_plane);
FROM_DEFAULT(plane_offset);
FROM_DEFAULT(clone.alpha);
@@ -493,7 +494,7 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type)
brush->gpencil_settings->preset_type = type;
/* Set vertex mix factor. */
- brush->gpencil_settings->vertex_mode = GPPAINT_MODE_STROKE;
+ brush->gpencil_settings->vertex_mode = GPPAINT_MODE_BOTH;
brush->gpencil_settings->vertex_factor = 1.0f;
switch (type) {
@@ -1185,6 +1186,11 @@ void BKE_brush_gpencil_paint_presets(Main *bmain, ToolSettings *ts, const bool r
if (reset || brush_prev == NULL) {
BKE_paint_brush_set(paint, deft_draw);
}
+ else {
+ if (brush_prev != NULL) {
+ BKE_paint_brush_set(paint, brush_prev);
+ }
+ }
}
/* Create a set of grease pencil Vertex Paint presets. */
@@ -1227,6 +1233,11 @@ void BKE_brush_gpencil_vertex_presets(Main *bmain, ToolSettings *ts, const bool
if (reset || brush_prev == NULL) {
BKE_paint_brush_set(vertexpaint, deft_vertex);
}
+ else {
+ if (brush_prev != NULL) {
+ BKE_paint_brush_set(vertexpaint, brush_prev);
+ }
+ }
}
/* Create a set of grease pencil Sculpt Paint presets. */
@@ -1297,6 +1308,11 @@ void BKE_brush_gpencil_sculpt_presets(Main *bmain, ToolSettings *ts, const bool
if (reset || brush_prev == NULL) {
BKE_paint_brush_set(sculptpaint, deft_sculpt);
}
+ else {
+ if (brush_prev != NULL) {
+ BKE_paint_brush_set(sculptpaint, brush_prev);
+ }
+ }
}
/* Create a set of grease pencil Weight Paint presets. */
@@ -1318,6 +1334,11 @@ void BKE_brush_gpencil_weight_presets(Main *bmain, ToolSettings *ts, const bool
if (reset || brush_prev == NULL) {
BKE_paint_brush_set(weightpaint, deft_weight);
}
+ else {
+ if (brush_prev != NULL) {
+ BKE_paint_brush_set(weightpaint, brush_prev);
+ }
+ }
}
struct Brush *BKE_brush_first_search(struct Main *bmain, const eObjectMode ob_mode)
@@ -1538,7 +1559,7 @@ void BKE_brush_sculpt_reset(Brush *br)
case SCULPT_TOOL_POSE:
br->pose_smooth_iterations = 4;
br->pose_ik_segments = 1;
- br->flag2 |= BRUSH_POSE_IK_ANCHORED;
+ br->flag2 |= BRUSH_POSE_IK_ANCHORED | BRUSH_USE_CONNECTED_ONLY;
br->flag &= ~BRUSH_ALPHA_PRESSURE;
br->flag &= ~BRUSH_SPACE;
br->flag &= ~BRUSH_SPACE_ATTEN;
@@ -1570,6 +1591,24 @@ void BKE_brush_sculpt_reset(Brush *br)
br->alpha = 1.0f;
br->height = 0.05f;
break;
+ case SCULPT_TOOL_PAINT:
+ br->hardness = 0.4f;
+ br->spacing = 10;
+ br->alpha = 0.6f;
+ br->flow = 1.0f;
+ br->tip_scale_x = 1.0f;
+ br->tip_roundness = 1.0f;
+ br->density = 1.0f;
+ br->flag &= ~BRUSH_SPACE_ATTEN;
+ zero_v3(br->rgb);
+ break;
+ case SCULPT_TOOL_SMEAR:
+ br->alpha = 1.0f;
+ br->spacing = 5;
+ br->flag &= ~BRUSH_ALPHA_PRESSURE;
+ br->flag &= ~BRUSH_SPACE_ATTEN;
+ br->curve_preset = BRUSH_CURVE_SPHERE;
+ break;
default:
break;
}
@@ -1629,14 +1668,15 @@ void BKE_brush_sculpt_reset(Brush *br)
break;
case SCULPT_TOOL_SIMPLIFY:
+ case SCULPT_TOOL_PAINT:
case SCULPT_TOOL_MASK:
case SCULPT_TOOL_DRAW_FACE_SETS:
- br->add_col[0] = 0.750000;
- br->add_col[1] = 0.750000;
- br->add_col[2] = 0.750000;
- br->sub_col[0] = 0.750000;
- br->sub_col[1] = 0.750000;
- br->sub_col[2] = 0.750000;
+ br->add_col[0] = 0.75f;
+ br->add_col[1] = 0.75f;
+ br->add_col[2] = 0.75f;
+ br->sub_col[0] = 0.75f;
+ br->sub_col[1] = 0.75f;
+ br->sub_col[2] = 0.75f;
break;
case SCULPT_TOOL_CLOTH:
diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c
index 6f63da18a22..080d61f1500 100644
--- a/source/blender/blenkernel/intern/collection.c
+++ b/source/blender/blenkernel/intern/collection.c
@@ -28,6 +28,7 @@
#include "BLI_threads.h"
#include "BLT_translation.h"
+#include "BKE_anim_data.h"
#include "BKE_collection.h"
#include "BKE_icons.h"
#include "BKE_idprop.h"
@@ -326,16 +327,16 @@ bool BKE_collection_delete(Main *bmain, Collection *collection, bool hierarchy)
static Collection *collection_duplicate_recursive(Main *bmain,
Collection *parent,
Collection *collection_old,
- const bool do_hierarchy,
- const bool do_objects,
- const bool do_obdata)
+ const eDupli_ID_Flags duplicate_flags,
+ const eLibIDDuplicateFlags duplicate_options)
{
Collection *collection_new;
bool do_full_process = false;
- const int object_dupflag = (do_obdata) ? U.dupflag : 0;
const bool is_collection_master = (collection_old->flag & COLLECTION_IS_MASTER) != 0;
const bool is_collection_liboverride = ID_IS_OVERRIDE_LIBRARY(collection_old);
+ const bool do_objects = (duplicate_flags & USER_DUP_OBJECT) != 0;
+
if (is_collection_master) {
/* We never duplicate master collections here, but we can still deep-copy their objects and
* collections. */
@@ -343,15 +344,9 @@ static Collection *collection_duplicate_recursive(Main *bmain,
collection_new = collection_old;
do_full_process = true;
}
- else if (!do_hierarchy || collection_old->id.newid == NULL) {
- BKE_id_copy(bmain, &collection_old->id, (ID **)&collection_new);
-
- /* Copying add one user by default, need to get rid of that one. */
- id_us_min(&collection_new->id);
-
- if (do_hierarchy) {
- ID_NEW_SET(collection_old, collection_new);
- }
+ else if (collection_old->id.newid == NULL) {
+ collection_new = (Collection *)BKE_id_copy_for_duplicate(
+ bmain, (ID *)collection_old, is_collection_liboverride, duplicate_flags);
do_full_process = true;
}
else {
@@ -376,7 +371,7 @@ static Collection *collection_duplicate_recursive(Main *bmain,
/* If we are not doing any kind of deep-copy, we can return immediately.
* False do_full_process means collection_old had already been duplicated,
* no need to redo some deep-copy on it. */
- if (!do_hierarchy || !do_full_process) {
+ if (!do_full_process) {
return collection_new;
}
@@ -394,8 +389,8 @@ static Collection *collection_duplicate_recursive(Main *bmain,
}
if (ob_new == NULL) {
- ob_new = BKE_object_duplicate(bmain, ob_old, object_dupflag);
- ID_NEW_SET(ob_old, ob_new);
+ ob_new = BKE_object_duplicate(
+ bmain, ob_old, duplicate_flags, duplicate_options | LIB_ID_DUPLICATE_IS_SUBPROCESS);
}
collection_object_add(bmain, collection_new, ob_new, 0, true);
@@ -413,7 +408,7 @@ static Collection *collection_duplicate_recursive(Main *bmain,
}
collection_duplicate_recursive(
- bmain, collection_new, child_collection_old, do_hierarchy, do_objects, do_obdata);
+ bmain, collection_new, child_collection_old, duplicate_flags, duplicate_options);
collection_child_remove(collection_new, child_collection_old);
}
@@ -421,56 +416,54 @@ static Collection *collection_duplicate_recursive(Main *bmain,
}
/**
- * Makes a standard (aka shallow) ID copy of a Collection.
- *
- * Add a new collection in the same level as the old one, link any nested collections
- * and finally link the objects to the new collection (as opposed to copying them).
- */
-Collection *BKE_collection_copy(Main *bmain, Collection *parent, Collection *collection)
-{
- return BKE_collection_duplicate(bmain, parent, collection, false, false, false);
-}
-
-/**
- * Make either a shallow copy, or deeper duplicate of given collection.
+ * Make a deep copy (aka duplicate) of the given collection and all of its children, recursively.
*
- * If \a do_hierarchy and \a do_deep_copy are false, this is a regular (shallow) ID copy.
- *
- * \warning If any 'deep copy' behavior is enabled,
- * this functions will clear all \a bmain id.idnew pointers.
- *
- * \param do_hierarchy: If true, it will recursively make shallow copies of children collections.
- * \param do_objects: If true, it will also make duplicates of objects.
- * This one does nothing if \a do_hierarchy is not set.
- * \param do_obdata: If true, it will also make deep duplicates of objects,
- * using behavior defined in user settings (#U.dupflag).
- * This one does nothing if \a do_hierarchy and \a do_objects are not set.
+ * \warning This functions will clear all \a bmain #ID.idnew pointers, unless \a
+ * #LIB_ID_DUPLICATE_IS_SUBPROCESS duplicate option is passed on, in which case caller is
+ * responsible to reconstruct collection dependencies information's
+ * (i.e. call #BKE_main_collection_sync).
*/
Collection *BKE_collection_duplicate(Main *bmain,
Collection *parent,
Collection *collection,
- const bool do_hierarchy,
- const bool do_objects,
- const bool do_obdata)
+ eDupli_ID_Flags duplicate_flags,
+ eLibIDDuplicateFlags duplicate_options)
{
- if (do_hierarchy) {
+ const bool is_subprocess = (duplicate_options & LIB_ID_DUPLICATE_IS_SUBPROCESS) != 0;
+
+ if (!is_subprocess) {
BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false);
BKE_main_id_clear_newpoins(bmain);
}
Collection *collection_new = collection_duplicate_recursive(
- bmain, parent, collection, do_hierarchy, do_objects, do_obdata);
-
- /* This code will follow into all ID links using an ID tagged with LIB_TAG_NEW.*/
- BKE_libblock_relink_to_newid(&collection_new->id);
+ bmain, parent, collection, duplicate_flags, duplicate_options);
+
+ if (!is_subprocess) {
+ /* `collection_duplicate_recursive` will also tag our 'root' collection, which is not required
+ * unless its duplication is a sub-process of another one. */
+ collection_new->id.tag &= ~LIB_TAG_NEW;
+
+ /* This code will follow into all ID links using an ID tagged with LIB_TAG_NEW.*/
+ BKE_libblock_relink_to_newid(&collection_new->id);
+
+#ifndef NDEBUG
+ /* Call to `BKE_libblock_relink_to_newid` above is supposed to have cleared all those flags. */
+ ID *id_iter;
+ FOREACH_MAIN_ID_BEGIN (bmain, id_iter) {
+ if (id_iter->tag & LIB_TAG_NEW) {
+ BLI_assert((id_iter->tag & LIB_TAG_NEW) == 0);
+ }
+ }
+ FOREACH_MAIN_ID_END;
+#endif
- if (do_hierarchy) {
/* Cleanup. */
BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false);
BKE_main_id_clear_newpoins(bmain);
- }
- BKE_main_collection_sync(bmain);
+ BKE_main_collection_sync(bmain);
+ }
return collection_new;
}
@@ -927,7 +920,7 @@ bool BKE_collection_object_remove(Main *bmain,
/**
* Remove object from all collections of scene
- * \param scene_collection_skip: Don't remove base from this collection.
+ * \param collection_skip: Don't remove base from this collection.
*/
static bool scene_collections_object_remove(
Main *bmain, Scene *scene, Object *ob, const bool free_us, Collection *collection_skip)
@@ -1126,7 +1119,7 @@ static bool collection_find_instance_recursive(Collection *collection,
{
LISTBASE_FOREACH (CollectionObject *, collection_object, &collection->gobject) {
if (collection_object->ob != NULL &&
- /* Object from a given collection should never instanciate that collection either. */
+ /* Object from a given collection should never instantiate that collection either. */
ELEM(collection_object->ob->instance_collection, instance_collection, collection)) {
return true;
}
@@ -1153,7 +1146,7 @@ bool BKE_collection_find_cycle(Collection *new_ancestor, Collection *collection)
}
}
- /* Find possible objects in collection or its children, that would instanciate the given ancestor
+ /* Find possible objects in collection or its children, that would instantiate the given ancestor
* collection (that would also make a fully invalid cycle of dependencies) .*/
return collection_find_instance_recursive(collection, new_ancestor);
}
diff --git a/source/blender/blenkernel/intern/colortools.c b/source/blender/blenkernel/intern/colortools.c
index 3da384a2745..4f4eb8f9f9d 100644
--- a/source/blender/blenkernel/intern/colortools.c
+++ b/source/blender/blenkernel/intern/colortools.c
@@ -44,6 +44,8 @@
#include "IMB_colormanagement.h"
#include "IMB_imbuf_types.h"
+#include "BLO_read_write.h"
+
/* ********************************* color curve ********************* */
/* ***************** operations on full struct ************* */
@@ -1238,6 +1240,32 @@ void BKE_curvemapping_table_RGBA(const CurveMapping *cumap, float **array, int *
}
}
+void BKE_curvemapping_blend_write(BlendWriter *writer, const CurveMapping *cumap)
+{
+ BLO_write_struct(writer, CurveMapping, cumap);
+ BKE_curvemapping_curves_blend_write(writer, cumap);
+}
+
+void BKE_curvemapping_curves_blend_write(BlendWriter *writer, const CurveMapping *cumap)
+{
+ for (int a = 0; a < CM_TOT; a++) {
+ BLO_write_struct_array(writer, CurveMapPoint, cumap->cm[a].totpoint, cumap->cm[a].curve);
+ }
+}
+
+/* cumap itself has been read already. */
+void BKE_curvemapping_blend_read(BlendDataReader *reader, CurveMapping *cumap)
+{
+ /* flag seems to be able to hang? Maybe old files... not bad to clear anyway */
+ cumap->flag &= ~CUMA_PREMULLED;
+
+ for (int a = 0; a < CM_TOT; a++) {
+ BLO_read_data_address(reader, &cumap->cm[a].curve);
+ cumap->cm[a].table = NULL;
+ cumap->cm[a].premultable = NULL;
+ }
+}
+
/* ***************** Histogram **************** */
#define INV_255 (1.f / 255.f)
diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c
index 050e8d434ae..06c28776840 100644
--- a/source/blender/blenkernel/intern/constraint.c
+++ b/source/blender/blenkernel/intern/constraint.c
@@ -5412,9 +5412,16 @@ static bConstraint *add_new_constraint_internal(const char *name, short type)
/* Set up a generic constraint data-block. */
con->type = type;
- con->flag |= CONSTRAINT_EXPAND | CONSTRAINT_OVERRIDE_LIBRARY_LOCAL;
+ con->flag |= CONSTRAINT_OVERRIDE_LIBRARY_LOCAL;
con->enforce = 1.0f;
+ /* Only open the main panel when constraints are created, not the sub-panels. */
+ con->ui_expand_flag = (1 << 0);
+ if (ELEM(type, CONSTRAINT_TYPE_ACTION, CONSTRAINT_TYPE_SPLINEIK)) {
+ /* Expand the two sub-panels in the cases where the main panel barely has any properties. */
+ con->ui_expand_flag |= (1 << 1) | (1 << 2);
+ }
+
/* Determine a basic name, and info */
if (cti) {
/* initialize constraint data */
@@ -5441,18 +5448,11 @@ static bConstraint *add_new_constraint_internal(const char *name, short type)
return con;
}
-/* if pchan is not NULL then assume we're adding a pose constraint */
-static bConstraint *add_new_constraint(Object *ob,
- bPoseChannel *pchan,
- const char *name,
- short type)
+/* Add a newly created constraint to the constraint list. */
+static void add_new_constraint_to_list(Object *ob, bPoseChannel *pchan, bConstraint *con)
{
- bConstraint *con;
ListBase *list;
- /* add the constraint */
- con = add_new_constraint_internal(name, type);
-
/* find the constraint stack - bone or object? */
list = (pchan) ? (&pchan->constraints) : (&ob->constraints);
@@ -5474,6 +5474,20 @@ static bConstraint *add_new_constraint(Object *ob,
/* make this constraint the active one */
BKE_constraints_active_set(list, con);
}
+}
+
+/* if pchan is not NULL then assume we're adding a pose constraint */
+static bConstraint *add_new_constraint(Object *ob,
+ bPoseChannel *pchan,
+ const char *name,
+ short type)
+{
+ bConstraint *con;
+
+ /* add the constraint */
+ con = add_new_constraint_internal(name, type);
+
+ add_new_constraint_to_list(ob, pchan, con);
/* set type+owner specific immutable settings */
/* TODO: does action constraint need anything here - i.e. spaceonce? */
@@ -5607,6 +5621,26 @@ bConstraint *BKE_constraint_duplicate_ex(bConstraint *src, const int flag, const
return dst;
}
+/* Add a copy of the given constraint for the given bone */
+bConstraint *BKE_constraint_copy_for_pose(Object *ob, bPoseChannel *pchan, bConstraint *src)
+{
+ if (pchan == NULL) {
+ return NULL;
+ }
+
+ bConstraint *new_con = BKE_constraint_duplicate_ex(src, 0, !ID_IS_LINKED(ob));
+ add_new_constraint_to_list(ob, pchan, new_con);
+ return new_con;
+}
+
+/* Add a copy of the given constraint for the given object */
+bConstraint *BKE_constraint_copy_for_object(Object *ob, bConstraint *src)
+{
+ bConstraint *new_con = BKE_constraint_duplicate_ex(src, 0, !ID_IS_LINKED(ob));
+ add_new_constraint_to_list(ob, NULL, new_con);
+ return new_con;
+}
+
/* duplicate all of the constraints in a constraint stack */
void BKE_constraints_copy_ex(ListBase *dst, const ListBase *src, const int flag, bool do_extern)
{
diff --git a/source/blender/blenkernel/intern/crazyspace.c b/source/blender/blenkernel/intern/crazyspace.c
index 4d685a511fd..e4f851819a8 100644
--- a/source/blender/blenkernel/intern/crazyspace.c
+++ b/source/blender/blenkernel/intern/crazyspace.c
@@ -252,7 +252,7 @@ void BKE_crazyspace_set_quats_mesh(Mesh *me,
}
/**
- * Returns an array of deform matrices for crazyspace correction,
+ * Returns an array of deform matrices for crazy-space correction,
* and the number of modifiers left.
*/
int BKE_crazyspace_get_first_deform_matrices_editbmesh(struct Depsgraph *depsgraph,
@@ -327,7 +327,7 @@ int BKE_crazyspace_get_first_deform_matrices_editbmesh(struct Depsgraph *depsgra
}
/**
- * Crazyspace evaluation needs to have an object which has all the fields
+ * Crazy-space evaluation needs to have an object which has all the fields
* evaluated, but the mesh data being at undeformed state. This way it can
* re-apply modifiers and also have proper pointers to key data blocks.
*
@@ -455,8 +455,8 @@ void BKE_crazyspace_build_sculpt(struct Depsgraph *depsgraph,
depsgraph, scene, object, deformmats, deformcos);
if (totleft) {
- /* there are deformation modifier which doesn't support deformation matrices
- * calculation. Need additional crazyspace correction */
+ /* There are deformation modifier which doesn't support deformation matrices calculation.
+ * Need additional crazy-space correction. */
Mesh *mesh = (Mesh *)object->data;
Mesh *mesh_eval = NULL;
diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c
index 0b65f53708f..6b28297622c 100644
--- a/source/blender/blenkernel/intern/curve.c
+++ b/source/blender/blenkernel/intern/curve.c
@@ -4081,9 +4081,9 @@ void BKE_nurb_handle_calc(
/**
* Variant of #BKE_nurb_handle_calc() that allows calculating based on a different select flag.
*
- * \param sel_flag: The flag (bezt.f1/2/3) value to use to determine selection. Usually `SELECT`,
- * but may want to use a different one at times (if caller does not operate on
- * selection).
+ * \param handle_sel_flag: The flag (bezt.f1/2/3) value to use to determine selection.
+ * Usually #SELECT, but may want to use a different one at times
+ * (if caller does not operate on selection).
*/
void BKE_nurb_handle_calc_ex(BezTriple *bezt,
BezTriple *prev,
diff --git a/source/blender/blenkernel/intern/curveprofile.c b/source/blender/blenkernel/intern/curveprofile.c
index a748dfbab3e..6919d4fa10f 100644
--- a/source/blender/blenkernel/intern/curveprofile.c
+++ b/source/blender/blenkernel/intern/curveprofile.c
@@ -41,6 +41,8 @@
#include "BKE_curveprofile.h"
#include "BKE_fcurve.h"
+#include "BLO_read_write.h"
+
void BKE_curveprofile_free_data(CurveProfile *profile)
{
MEM_SAFE_FREE(profile->path);
@@ -63,6 +65,11 @@ void BKE_curveprofile_copy_data(CurveProfile *target, const CurveProfile *profil
target->path = MEM_dupallocN(profile->path);
target->table = MEM_dupallocN(profile->table);
target->segments = MEM_dupallocN(profile->segments);
+
+ /* Update the reference the points have to the profile. */
+ for (int i = 0; i < target->path_len; i++) {
+ target->path[i].profile = target;
+ }
}
CurveProfile *BKE_curveprofile_copy(const CurveProfile *profile)
@@ -76,6 +83,101 @@ CurveProfile *BKE_curveprofile_copy(const CurveProfile *profile)
}
/**
+ * Move a point's handle, accounting for the alignment of handles with the #HD_ALIGN type.
+ *
+ * \param handle_1: Whether to move the 1st or 2nd control point.
+ * \param delta: The *relative* change in the handle's position.
+ * \note Requires #BKE_curveprofile_update call after.
+ * \return Whether the handle moved from its start position.
+ */
+bool BKE_curveprofile_move_handle(struct CurveProfilePoint *point,
+ const bool handle_1,
+ const bool snap,
+ const float delta[2])
+{
+ short handle_type = (handle_1) ? point->h1 : point->h2;
+ float *handle_location = (handle_1) ? &point->h1_loc[0] : &point->h2_loc[0];
+
+ float start_position[2];
+ copy_v2_v2(start_position, handle_location);
+
+ /* Don't move the handle if it's not a free handle type. */
+ if (!ELEM(handle_type, HD_FREE, HD_ALIGN)) {
+ return false;
+ }
+
+ /* Move the handle. */
+ handle_location[0] += delta ? delta[0] : 0.0f;
+ handle_location[1] += delta ? delta[1] : 0.0f;
+ if (snap) {
+ handle_location[0] = 0.125f * roundf(8.0f * handle_location[0]);
+ handle_location[1] = 0.125f * roundf(8.0f * handle_location[1]);
+ }
+
+ /* Move the other handle if they are aligned. */
+ if (handle_type == HD_ALIGN) {
+ short other_handle_type = (handle_1) ? point->h2 : point->h1;
+ if (other_handle_type == HD_ALIGN) {
+ float *other_handle_location = (handle_1) ? &point->h2_loc[0] : &point->h1_loc[0];
+ other_handle_location[0] = 2.0f * point->x - handle_location[0];
+ other_handle_location[1] = 2.0f * point->y - handle_location[1];
+ }
+ }
+
+ if (!equals_v2v2(handle_location, start_position)) {
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Moves a control point, accounting for clipping and snapping, and moving free handles.
+ *
+ * \param snap: Whether to snap the point to the grid
+ * \param delta: The *relative* change of the point's location.
+ * \return Whether the point moved from its start position.
+ * \note Requires #BKE_curveprofile_update call after.
+ */
+bool BKE_curveprofile_move_point(struct CurveProfile *profile,
+ struct CurveProfilePoint *point,
+ const bool snap,
+ const float delta[2])
+{
+ float origx = point->x;
+ float origy = point->y;
+
+ point->x += delta[0];
+ point->y += delta[1];
+ if (snap) {
+ point->x = 0.125f * roundf(8.0f * point->x);
+ point->y = 0.125f * roundf(8.0f * point->y);
+ }
+
+ /* Clip here instead to test clipping here to stop handles from moving too. */
+ if (profile->flag & PROF_USE_CLIP) {
+ point->x = max_ff(point->x, profile->clip_rect.xmin);
+ point->x = min_ff(point->x, profile->clip_rect.xmax);
+ point->y = max_ff(point->y, profile->clip_rect.ymin);
+ point->y = min_ff(point->y, profile->clip_rect.ymax);
+ }
+
+ /* Also move free handles even when they aren't selected. */
+ if (ELEM(point->h1, HD_FREE, HD_ALIGN)) {
+ point->h1_loc[0] += point->x - origx;
+ point->h1_loc[1] += point->y - origy;
+ }
+ if (ELEM(point->h2, HD_FREE, HD_ALIGN)) {
+ point->h2_loc[0] += point->x - origx;
+ point->h2_loc[1] += point->y - origy;
+ }
+
+ if (point->x != origx || point->y != origy) {
+ return true;
+ }
+ return false;
+}
+
+/**
* Removes a specific point from the path of control points.
* \note Requires #BKE_curveprofile_update call after.
*/
@@ -98,8 +200,10 @@ bool BKE_curveprofile_remove_point(CurveProfile *profile, CurveProfilePoint *poi
uint i_delete = (uint)(point - profile->path);
/* Copy the before and after the deleted point. */
- memcpy(pts, profile->path, i_delete);
- memcpy(pts + i_delete, profile->path + i_delete + 1, (size_t)profile->path_len - i_delete - 1);
+ memcpy(pts, profile->path, sizeof(CurveProfilePoint) * i_delete);
+ memcpy(pts + i_delete,
+ profile->path + i_delete + 1,
+ sizeof(CurveProfilePoint) * (profile->path_len - i_delete - 1));
MEM_freeN(profile->path);
profile->path = pts;
@@ -178,12 +282,9 @@ CurveProfilePoint *BKE_curveprofile_insert(CurveProfile *profile, float x, float
"profile path");
for (int i_new = 0, i_old = 0; i_new < profile->path_len; i_new++) {
if (i_new != i_insert) {
- /* Insert old points */
- new_pts[i_new].x = profile->path[i_old].x;
- new_pts[i_new].y = profile->path[i_old].y;
- new_pts[i_new].flag = profile->path[i_old].flag & ~PROF_SELECT; /* Deselect old points. */
- new_pts[i_new].h1 = profile->path[i_old].h1;
- new_pts[i_new].h2 = profile->path[i_old].h2;
+ /* Insert old points. */
+ memcpy(&new_pts[i_new], &profile->path[i_old], sizeof(CurveProfilePoint));
+ new_pts[i_new].flag &= ~PROF_SELECT; /* Deselect old points. */
i_old++;
}
else {
@@ -199,6 +300,8 @@ CurveProfilePoint *BKE_curveprofile_insert(CurveProfile *profile, float x, float
else {
new_pt->h1 = new_pt->h2 = HD_AUTO;
}
+ /* Give new point a reference to the profile. */
+ new_pt->profile = profile;
}
}
@@ -210,35 +313,19 @@ CurveProfilePoint *BKE_curveprofile_insert(CurveProfile *profile, float x, float
/**
* Sets the handle type of the selected control points.
- * \param type_1, type_2: Either HD_VECT or HD_AUTO. Handle types for the first and second handles.
- *
+ * \param type_1, type_2: Handle type for the first handle. HD_VECT, HD_AUTO, HD_FREE, or HD_ALIGN.
* \note Requires #BKE_curveprofile_update call after.
*/
void BKE_curveprofile_selected_handle_set(CurveProfile *profile, int type_1, int type_2)
{
for (int i = 0; i < profile->path_len; i++) {
- if (profile->path[i].flag & PROF_SELECT) {
- switch (type_1) {
- case HD_AUTO:
- profile->path[i].h1 = HD_AUTO;
- break;
- case HD_VECT:
- profile->path[i].h1 = HD_VECT;
- break;
- default:
- profile->path[i].h1 = HD_AUTO;
- break;
- }
- switch (type_2) {
- case HD_AUTO:
- profile->path[i].h2 = HD_AUTO;
- break;
- case HD_VECT:
- profile->path[i].h2 = HD_VECT;
- break;
- default:
- profile->path[i].h1 = HD_AUTO;
- break;
+ if (ELEM(profile->path[i].flag, PROF_SELECT, PROF_H1_SELECT, PROF_H2_SELECT)) {
+ profile->path[i].h1 = type_1;
+ profile->path[i].h2 = type_2;
+
+ if (type_1 == HD_ALIGN && type_2 == HD_ALIGN) {
+ /* Align the handles. */
+ BKE_curveprofile_move_handle(&profile->path[i], true, false, NULL);
}
}
}
@@ -259,11 +346,24 @@ void BKE_curveprofile_reverse(CurveProfile *profile)
"profile path");
/* Mirror the new points across the y = x line */
for (int i = 0; i < profile->path_len; i++) {
- new_pts[profile->path_len - i - 1].x = profile->path[i].y;
- new_pts[profile->path_len - i - 1].y = profile->path[i].x;
- new_pts[profile->path_len - i - 1].flag = profile->path[i].flag;
- new_pts[profile->path_len - i - 1].h1 = profile->path[i].h1;
- new_pts[profile->path_len - i - 1].h2 = profile->path[i].h2;
+ int i_reversed = profile->path_len - i - 1;
+ BLI_assert(i_reversed >= 0);
+ new_pts[i_reversed].x = profile->path[i].y;
+ new_pts[i_reversed].y = profile->path[i].x;
+ new_pts[i_reversed].flag = profile->path[i].flag;
+ new_pts[i_reversed].h1 = profile->path[i].h2;
+ new_pts[i_reversed].h2 = profile->path[i].h1;
+ new_pts[i_reversed].profile = profile;
+
+ /* Mirror free handles, they can't be recalculated. */
+ if (ELEM(profile->path[i].h1, HD_FREE, HD_ALIGN)) {
+ new_pts[i_reversed].h1_loc[0] = profile->path[i].h2_loc[1];
+ new_pts[i_reversed].h1_loc[1] = profile->path[i].h2_loc[0];
+ }
+ if (ELEM(profile->path[i].h2, HD_FREE, HD_ALIGN)) {
+ new_pts[i_reversed].h2_loc[0] = profile->path[i].h1_loc[1];
+ new_pts[i_reversed].h2_loc[1] = profile->path[i].h1_loc[0];
+ }
}
/* Free the old points and use the new ones */
@@ -446,6 +546,13 @@ void BKE_curveprofile_reset(CurveProfile *profile)
break;
}
+ profile->flag &= ~PROF_DIRTY_PRESET;
+
+ /* Ensure each point has a reference to the profile. */
+ for (int i = 0; i < profile->path_len; i++) {
+ profile->path[i].profile = profile;
+ }
+
if (profile->table) {
MEM_freeN(profile->table);
profile->table = NULL;
@@ -463,7 +570,7 @@ static bool is_curved_edge(BezTriple *bezt, int i)
/**
* Used to set bezier handle locations in the sample creation process. Reduced copy of
- * #calchandleNurb_intern code in curve.c.
+ * #calchandleNurb_intern code in curve.c, mostly changed by removing the third dimension.
*/
static void calchandle_profile(BezTriple *bezt, const BezTriple *prev, const BezTriple *next)
{
@@ -598,7 +705,7 @@ static int sort_points_curvature(const void *in_a, const void *in_b)
* this is true and there are only vector edges the straight edges will still be sampled.
* \param r_samples: An array of points to put the sampled positions. Must have length n_segments.
* \return r_samples: Fill the array with the sampled locations and if the point corresponds
- * to a control point, its handle type
+ * to a control point, its handle type.
*/
void BKE_curveprofile_create_samples(CurveProfile *profile,
int n_segments,
@@ -619,21 +726,33 @@ void BKE_curveprofile_create_samples(CurveProfile *profile,
for (i = 0; i < totpoints; i++) {
bezt[i].vec[1][0] = profile->path[i].x;
bezt[i].vec[1][1] = profile->path[i].y;
- bezt[i].h1 = (profile->path[i].h1 == HD_VECT) ? HD_VECT : HD_AUTO;
- bezt[i].h2 = (profile->path[i].h2 == HD_VECT) ? HD_VECT : HD_AUTO;
- }
- /* Give the first and last bezier points the same handle type as their neighbors. */
- if (totpoints > 2) {
- bezt[0].h1 = bezt[0].h2 = bezt[1].h1;
- bezt[totpoints - 1].h1 = bezt[totpoints - 1].h2 = bezt[totpoints - 2].h2;
+ bezt[i].h1 = profile->path[i].h1;
+ bezt[i].h2 = profile->path[i].h2;
+ /* Copy handle locations if the handle type is free. */
+ if (ELEM(profile->path[i].h1, HD_FREE, HD_ALIGN)) {
+ bezt[i].vec[0][0] = profile->path[i].h1_loc[0];
+ bezt[i].vec[0][1] = profile->path[i].h1_loc[1];
+ }
+ if (ELEM(profile->path[i].h1, HD_FREE, HD_ALIGN)) {
+ bezt[i].vec[2][0] = profile->path[i].h2_loc[0];
+ bezt[i].vec[2][1] = profile->path[i].h2_loc[1];
+ }
}
- /* Get handle positions for the bezier points. */
+ /* Get handle positions for the non-free bezier points. */
calchandle_profile(&bezt[0], NULL, &bezt[1]);
for (i = 1; i < totpoints - 1; i++) {
calchandle_profile(&bezt[i], &bezt[i - 1], &bezt[i + 1]);
}
calchandle_profile(&bezt[totpoints - 1], &bezt[totpoints - 2], NULL);
+ /* Copy the handle locations back to the control points. */
+ for (i = 0; i < totpoints; i++) {
+ profile->path[i].h1_loc[0] = bezt[i].vec[0][0];
+ profile->path[i].h1_loc[1] = bezt[i].vec[0][1];
+ profile->path[i].h2_loc[0] = bezt[i].vec[2][0];
+ profile->path[i].h2_loc[1] = bezt[i].vec[2][1];
+ }
+
/* Create a list of edge indices with the most curved at the start, least curved at the end. */
curve_sorted = MEM_callocN(sizeof(CurvatureSortPoint) * totedges, "curve sorted");
for (i = 0; i < totedges; i++) {
@@ -718,7 +837,7 @@ void BKE_curveprofile_create_samples(CurveProfile *profile,
BLI_assert(j < n_segments);
}
- /* Do the sampling from bezier points, X values first, then Y values. */
+ /* Sample from the bezier points. X then Y values. */
BKE_curve_forward_diff_bezier(bezt[i].vec[1][0],
bezt[i].vec[2][0],
bezt[i + 1].vec[0][0],
@@ -738,7 +857,7 @@ void BKE_curveprofile_create_samples(CurveProfile *profile,
BLI_assert(i_sample <= n_segments);
}
-#ifdef DEBUG_profile_TABLE
+#ifdef DEBUG_PROFILE_TABLE
printf("CURVEPROFILE CREATE SAMPLES\n");
printf("n_segments: %d\n", n_segments);
printf("totedges: %d\n", totedges);
@@ -755,6 +874,7 @@ void BKE_curveprofile_create_samples(CurveProfile *profile,
}
printf("\n");
#endif
+
MEM_freeN(bezt);
MEM_freeN(curve_sorted);
MEM_freeN(n_samples);
@@ -825,8 +945,10 @@ void BKE_curveprofile_set_defaults(CurveProfile *profile)
profile->path[0].x = 1.0f;
profile->path[0].y = 0.0f;
+ profile->path[0].profile = profile;
profile->path[1].x = 1.0f;
profile->path[1].y = 1.0f;
+ profile->path[1].profile = profile;
profile->changed_timestamp = 0;
}
@@ -850,13 +972,14 @@ struct CurveProfile *BKE_curveprofile_add(int preset)
/**
* Should be called after the widget is changed. Does profile and remove double checks and more
* importantly, recreates the display / evaluation and segments tables.
+ * \param update_flags: Bitfield with fields defined in header file. Controls removing doubles and
+ * clipping.
*/
-void BKE_curveprofile_update(CurveProfile *profile, const bool remove_double)
+void BKE_curveprofile_update(CurveProfile *profile, const int update_flags)
{
CurveProfilePoint *points = profile->path;
rctf *clipr = &profile->clip_rect;
float thresh;
- float dx, dy;
int i;
profile->changed_timestamp++;
@@ -864,11 +987,16 @@ void BKE_curveprofile_update(CurveProfile *profile, const bool remove_double)
/* Clamp with the clipping rect in case something got past. */
if (profile->flag & PROF_USE_CLIP) {
/* Move points inside the clip rectangle. */
- for (i = 0; i < profile->path_len; i++) {
- points[i].x = max_ff(points[i].x, clipr->xmin);
- points[i].x = min_ff(points[i].x, clipr->xmax);
- points[i].y = max_ff(points[i].y, clipr->ymin);
- points[i].y = min_ff(points[i].y, clipr->ymax);
+ if (update_flags & PROF_UPDATE_CLIP) {
+ for (i = 0; i < profile->path_len; i++) {
+ points[i].x = max_ff(points[i].x, clipr->xmin);
+ points[i].x = min_ff(points[i].x, clipr->xmax);
+ points[i].y = max_ff(points[i].y, clipr->ymin);
+ points[i].y = min_ff(points[i].y, clipr->ymax);
+
+ /* Extra sanity assert to make sure the points have the right profile pointer. */
+ BLI_assert(points[i].profile == profile);
+ }
}
/* Ensure zoom-level respects clipping. */
if (BLI_rctf_size_x(&profile->view_rect) > BLI_rctf_size_x(&profile->clip_rect)) {
@@ -882,30 +1010,19 @@ void BKE_curveprofile_update(CurveProfile *profile, const bool remove_double)
}
/* Remove doubles with a threshold set at 1% of default range. */
- thresh = 0.01f * BLI_rctf_size_x(clipr);
- if (remove_double && profile->path_len > 2) {
+ thresh = pow2f(0.01f * BLI_rctf_size_x(clipr));
+ if (update_flags & PROF_UPDATE_REMOVE_DOUBLES && profile->path_len > 2) {
for (i = 0; i < profile->path_len - 1; i++) {
- dx = points[i].x - points[i + 1].x;
- dy = points[i].y - points[i + 1].y;
- if (sqrtf(dx * dx + dy * dy) < thresh) {
+ if (len_squared_v2v2(&points[i].x, &points[i + 1].x) < thresh) {
if (i == 0) {
- points[i + 1].flag |= HD_VECT;
- if (points[i + 1].flag & PROF_SELECT) {
- points[i].flag |= PROF_SELECT;
- }
+ BKE_curveprofile_remove_point(profile, &points[1]);
}
else {
- points[i].flag |= HD_VECT;
- if (points[i].flag & PROF_SELECT) {
- points[i + 1].flag |= PROF_SELECT;
- }
+ BKE_curveprofile_remove_point(profile, &points[i]);
}
- break; /* Assumes 1 deletion per edit is ok. */
+ break; /* Assumes 1 deletion per update call is ok. */
}
}
- if (i != profile->path_len - 1) {
- BKE_curveprofile_remove_by_flag(profile, 2);
- }
}
/* Create the high resolution table for drawing and some evaluation functions. */
@@ -925,10 +1042,13 @@ void BKE_curveprofile_update(CurveProfile *profile, const bool remove_double)
*/
void BKE_curveprofile_initialize(CurveProfile *profile, short segments_len)
{
+ if (segments_len != profile->segments_len) {
+ profile->flag |= PROF_DIRTY_PRESET;
+ }
profile->segments_len = segments_len;
/* Calculate the higher resolution / segments tables for display and evaluation. */
- BKE_curveprofile_update(profile, false);
+ BKE_curveprofile_update(profile, PROF_UPDATE_NONE);
}
/**
@@ -1070,3 +1190,22 @@ void BKE_curveprofile_evaluate_length_portion(const CurveProfile *profile,
*x_out = interpf(profile->table[i].x, profile->table[i + 1].x, lerp_factor);
*y_out = interpf(profile->table[i].y, profile->table[i + 1].y, lerp_factor);
}
+
+void BKE_curveprofile_blend_write(struct BlendWriter *writer, const struct CurveProfile *profile)
+{
+ BLO_write_struct(writer, CurveProfile, profile);
+ BLO_write_struct_array(writer, CurveProfilePoint, profile->path_len, profile->path);
+}
+
+/* Expects that the curve profile itself has been read already. */
+void BKE_curveprofile_blend_read(struct BlendDataReader *reader, struct CurveProfile *profile)
+{
+ BLO_read_data_address(reader, &profile->path);
+ profile->table = NULL;
+ profile->segments = NULL;
+
+ /* Reset the points' pointers to the profile. */
+ for (int i = 0; i < profile->path_len; i++) {
+ profile->path[i].profile = profile;
+ }
+}
diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c
index 7dd4d1178ef..2be61239ac6 100644
--- a/source/blender/blenkernel/intern/customdata.c
+++ b/source/blender/blenkernel/intern/customdata.c
@@ -858,7 +858,6 @@ static void layerDoMinMax_mloopcol(const void *data, void *vmin, void *vmax)
if (m->a < min->a) {
min->a = m->a;
}
-
if (m->r > max->r) {
max->r = m->r;
}
@@ -1355,7 +1354,7 @@ static void layerCopyValue_propcol(const void *source,
/* Modes that do a full copy or nothing. */
if (ELEM(mixmode, CDT_MIX_REPLACE_ABOVE_THRESHOLD, CDT_MIX_REPLACE_BELOW_THRESHOLD)) {
/* TODO: Check for a real valid way to get 'factor' value of our dest color? */
- const float f = (m2->col[0] + m2->col[1] + m2->col[2]) / 3.0f;
+ const float f = (m2->color[0] + m2->color[1] + m2->color[2]) / 3.0f;
if (mixmode == CDT_MIX_REPLACE_ABOVE_THRESHOLD && f < mixfactor) {
return; /* Do Nothing! */
}
@@ -1363,29 +1362,29 @@ static void layerCopyValue_propcol(const void *source,
return; /* Do Nothing! */
}
}
- copy_v3_v3(m2->col, m1->col);
+ copy_v3_v3(m2->color, m1->color);
}
else { /* Modes that support 'real' mix factor. */
if (mixmode == CDT_MIX_MIX) {
- blend_color_mix_float(tmp_col, m2->col, m1->col);
+ blend_color_mix_float(tmp_col, m2->color, m1->color);
}
else if (mixmode == CDT_MIX_ADD) {
- blend_color_add_float(tmp_col, m2->col, m1->col);
+ blend_color_add_float(tmp_col, m2->color, m1->color);
}
else if (mixmode == CDT_MIX_SUB) {
- blend_color_sub_float(tmp_col, m2->col, m1->col);
+ blend_color_sub_float(tmp_col, m2->color, m1->color);
}
else if (mixmode == CDT_MIX_MUL) {
- blend_color_mul_float(tmp_col, m2->col, m1->col);
+ blend_color_mul_float(tmp_col, m2->color, m1->color);
}
else {
- memcpy(tmp_col, m1->col, sizeof(tmp_col));
+ memcpy(tmp_col, m1->color, sizeof(tmp_col));
}
- blend_color_interpolate_float(m2->col, m2->col, tmp_col, mixfactor);
+ blend_color_interpolate_float(m2->color, m2->color, tmp_col, mixfactor);
- copy_v3_v3(m2->col, m1->col);
+ copy_v3_v3(m2->color, m1->color);
}
- m2->col[3] = m1->col[3];
+ m2->color[3] = m1->color[3];
}
static bool layerEqual_propcol(const void *data1, const void *data2)
@@ -1394,7 +1393,7 @@ static bool layerEqual_propcol(const void *data1, const void *data2)
float tot = 0;
for (int i = 0; i < 4; i++) {
- float c = (m1->col[i] - m2->col[i]);
+ float c = (m1->color[i] - m2->color[i]);
tot += c * c;
}
@@ -1404,29 +1403,29 @@ static bool layerEqual_propcol(const void *data1, const void *data2)
static void layerMultiply_propcol(void *data, float fac)
{
MPropCol *m = data;
- mul_v4_fl(m->col, fac);
+ mul_v4_fl(m->color, fac);
}
static void layerAdd_propcol(void *data1, const void *data2)
{
MPropCol *m = data1;
const MPropCol *m2 = data2;
- add_v4_v4(m->col, m2->col);
+ add_v4_v4(m->color, m2->color);
}
static void layerDoMinMax_propcol(const void *data, void *vmin, void *vmax)
{
const MPropCol *m = data;
MPropCol *min = vmin, *max = vmax;
- minmax_v4v4_v4(min->col, max->col, m->col);
+ minmax_v4v4_v4(min->color, max->color, m->color);
}
static void layerInitMinMax_propcol(void *vmin, void *vmax)
{
MPropCol *min = vmin, *max = vmax;
- copy_v4_fl(min->col, FLT_MAX);
- copy_v4_fl(max->col, FLT_MIN);
+ copy_v4_fl(min->color, FLT_MAX);
+ copy_v4_fl(max->color, FLT_MIN);
}
static void layerDefault_propcol(void *data, int count)
@@ -1436,7 +1435,7 @@ static void layerDefault_propcol(void *data, int count)
MPropCol *pcol = (MPropCol *)data;
int i;
for (i = 0; i < count; i++) {
- copy_v4_v4(pcol[i].col, default_propcol.col);
+ copy_v4_v4(pcol[i].color, default_propcol.color);
}
}
@@ -1450,14 +1449,14 @@ static void layerInterp_propcol(
float weight = weights ? weights[i] : 1.0f;
const MPropCol *src = sources[i];
if (sub_weights) {
- madd_v4_v4fl(col, src->col, (*sub_weight) * weight);
+ madd_v4_v4fl(col, src->color, (*sub_weight) * weight);
sub_weight++;
}
else {
- madd_v4_v4fl(col, src->col, weight);
+ madd_v4_v4fl(col, src->color, weight);
}
}
- copy_v4_v4(mc->col, col);
+ copy_v4_v4(mc->color, col);
}
static int layerMaxNum_propcol(void)
@@ -1784,7 +1783,7 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
{sizeof(MPropCol),
"MPropCol",
1,
- N_("Col"),
+ N_("Color"),
NULL,
NULL,
layerInterp_propcol,
@@ -1871,7 +1870,7 @@ const CustomData_MeshMasks CD_MASK_BAREMESH_ORIGINDEX = {
};
const CustomData_MeshMasks CD_MASK_MESH = {
.vmask = (CD_MASK_MVERT | CD_MASK_MDEFORMVERT | CD_MASK_MVERT_SKIN | CD_MASK_PAINT_MASK |
- CD_MASK_GENERIC_DATA),
+ CD_MASK_GENERIC_DATA | CD_MASK_PROP_COLOR),
.emask = (CD_MASK_MEDGE | CD_MASK_FREESTYLE_EDGE | CD_MASK_GENERIC_DATA),
.fmask = 0,
.lmask = (CD_MASK_MLOOP | CD_MASK_MDISPS | CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL |
@@ -1881,7 +1880,7 @@ const CustomData_MeshMasks CD_MASK_MESH = {
};
const CustomData_MeshMasks CD_MASK_EDITMESH = {
.vmask = (CD_MASK_MDEFORMVERT | CD_MASK_PAINT_MASK | CD_MASK_MVERT_SKIN | CD_MASK_SHAPEKEY |
- CD_MASK_SHAPE_KEYINDEX | CD_MASK_GENERIC_DATA),
+ CD_MASK_SHAPE_KEYINDEX | CD_MASK_GENERIC_DATA | CD_MASK_PROP_COLOR),
.emask = (CD_MASK_GENERIC_DATA),
.fmask = 0,
.lmask = (CD_MASK_MDISPS | CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL | CD_MASK_CUSTOMLOOPNORMAL |
@@ -1890,7 +1889,7 @@ const CustomData_MeshMasks CD_MASK_EDITMESH = {
};
const CustomData_MeshMasks CD_MASK_DERIVEDMESH = {
.vmask = (CD_MASK_ORIGINDEX | CD_MASK_MDEFORMVERT | CD_MASK_SHAPEKEY | CD_MASK_MVERT_SKIN |
- CD_MASK_ORCO | CD_MASK_CLOTH_ORCO | CD_MASK_GENERIC_DATA),
+ CD_MASK_ORCO | CD_MASK_CLOTH_ORCO | CD_MASK_GENERIC_DATA | CD_MASK_PROP_COLOR),
.emask = (CD_MASK_ORIGINDEX | CD_MASK_FREESTYLE_EDGE | CD_MASK_GENERIC_DATA),
.fmask = (CD_MASK_ORIGINDEX | CD_MASK_ORIGSPACE | CD_MASK_PREVIEW_MCOL | CD_MASK_TANGENT),
.lmask = (CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL | CD_MASK_CUSTOMLOOPNORMAL |
@@ -1901,7 +1900,8 @@ const CustomData_MeshMasks CD_MASK_DERIVEDMESH = {
};
const CustomData_MeshMasks CD_MASK_BMESH = {
.vmask = (CD_MASK_MDEFORMVERT | CD_MASK_BWEIGHT | CD_MASK_MVERT_SKIN | CD_MASK_SHAPEKEY |
- CD_MASK_SHAPE_KEYINDEX | CD_MASK_PAINT_MASK | CD_MASK_GENERIC_DATA),
+ CD_MASK_SHAPE_KEYINDEX | CD_MASK_PAINT_MASK | CD_MASK_GENERIC_DATA |
+ CD_MASK_PROP_COLOR),
.emask = (CD_MASK_BWEIGHT | CD_MASK_CREASE | CD_MASK_FREESTYLE_EDGE | CD_MASK_GENERIC_DATA),
.fmask = 0,
.lmask = (CD_MASK_MDISPS | CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL | CD_MASK_CUSTOMLOOPNORMAL |
@@ -1925,7 +1925,7 @@ const CustomData_MeshMasks CD_MASK_EVERYTHING = {
.vmask = (CD_MASK_MVERT | CD_MASK_BM_ELEM_PYPTR | CD_MASK_ORIGINDEX | CD_MASK_NORMAL |
CD_MASK_MDEFORMVERT | CD_MASK_BWEIGHT | CD_MASK_MVERT_SKIN | CD_MASK_ORCO |
CD_MASK_CLOTH_ORCO | CD_MASK_SHAPEKEY | CD_MASK_SHAPE_KEYINDEX | CD_MASK_PAINT_MASK |
- CD_MASK_GENERIC_DATA),
+ CD_MASK_GENERIC_DATA | CD_MASK_PROP_COLOR),
.emask = (CD_MASK_MEDGE | CD_MASK_BM_ELEM_PYPTR | CD_MASK_ORIGINDEX | CD_MASK_BWEIGHT |
CD_MASK_CREASE | CD_MASK_FREESTYLE_EDGE | CD_MASK_GENERIC_DATA),
.fmask = (CD_MASK_MFACE | CD_MASK_ORIGINDEX | CD_MASK_NORMAL | CD_MASK_MTFACE | CD_MASK_MCOL |
diff --git a/source/blender/blenkernel/intern/derived_node_tree.cc b/source/blender/blenkernel/intern/derived_node_tree.cc
index 961fef958b7..b53457812ba 100644
--- a/source/blender/blenkernel/intern/derived_node_tree.cc
+++ b/source/blender/blenkernel/intern/derived_node_tree.cc
@@ -20,12 +20,13 @@
#define UNINITIALIZED_ID UINT32_MAX
-namespace BKE {
+namespace blender {
+namespace bke {
static const NodeTreeRef &get_tree_ref(NodeTreeRefMap &node_tree_refs, bNodeTree *btree)
{
- return *node_tree_refs.lookup_or_add_cb(
- btree, [&]() { return blender::make_unique<NodeTreeRef>(btree); });
+ return *node_tree_refs.lookup_or_add_cb(btree,
+ [&]() { return std::make_unique<NodeTreeRef>(btree); });
}
DerivedNodeTree::DerivedNodeTree(bNodeTree *btree, NodeTreeRefMap &node_tree_refs) : m_btree(btree)
@@ -173,7 +174,7 @@ BLI_NOINLINE void DerivedNodeTree::relink_group_inputs(const NodeTreeRef &group_
Span<DNode *> nodes_by_id,
DNode &group_node)
{
- Span<const NodeRef *> node_refs = group_ref.nodes_with_idname("NodeGroupInput");
+ Span<const NodeRef *> node_refs = group_ref.nodes_by_type("NodeGroupInput");
if (node_refs.size() == 0) {
return;
}
@@ -220,7 +221,7 @@ BLI_NOINLINE void DerivedNodeTree::relink_group_outputs(const NodeTreeRef &group
Span<DNode *> nodes_by_id,
DNode &group_node)
{
- Span<const NodeRef *> node_refs = group_ref.nodes_with_idname("NodeGroupOutput");
+ Span<const NodeRef *> node_refs = group_ref.nodes_by_type("NodeGroupOutput");
if (node_refs.size() == 0) {
return;
}
@@ -320,7 +321,8 @@ BLI_NOINLINE void DerivedNodeTree::store_in_this_and_init_ids(
DNode *node = m_nodes_by_id[node_index];
node->m_id = node_index;
- m_nodes_by_idname.lookup_or_add_default(node->idname()).append(node);
+ const bNodeType *nodetype = node->m_node_ref->bnode()->typeinfo;
+ m_nodes_by_type.lookup_or_add_default(nodetype).append(node);
for (DInputSocket *socket : node->m_inputs) {
socket->m_id = m_sockets_by_id.append_and_get_index(socket);
@@ -356,19 +358,17 @@ DerivedNodeTree::~DerivedNodeTree()
}
}
-namespace Dot = blender::DotExport;
-
-static Dot::Cluster *get_cluster_for_parent(Dot::DirectedGraph &graph,
- Map<const DParentNode *, Dot::Cluster *> &clusters,
+static dot::Cluster *get_cluster_for_parent(dot::DirectedGraph &graph,
+ Map<const DParentNode *, dot::Cluster *> &clusters,
const DParentNode *parent)
{
if (parent == nullptr) {
return nullptr;
}
return clusters.lookup_or_add_cb(parent, [&]() {
- Dot::Cluster *parent_cluster = get_cluster_for_parent(graph, clusters, parent->parent());
+ dot::Cluster *parent_cluster = get_cluster_for_parent(graph, clusters, parent->parent());
bNodeTree *btree = (bNodeTree *)parent->node_ref().bnode()->id;
- Dot::Cluster *new_cluster = &graph.new_cluster(parent->node_ref().name() + " / " +
+ dot::Cluster *new_cluster = &graph.new_cluster(parent->node_ref().name() + " / " +
StringRef(btree->id.name + 2));
new_cluster->set_parent_cluster(parent_cluster);
return new_cluster;
@@ -377,15 +377,15 @@ static Dot::Cluster *get_cluster_for_parent(Dot::DirectedGraph &graph,
std::string DerivedNodeTree::to_dot() const
{
- Dot::DirectedGraph digraph;
- digraph.set_rankdir(Dot::Attr_rankdir::LeftToRight);
+ dot::DirectedGraph digraph;
+ digraph.set_rankdir(dot::Attr_rankdir::LeftToRight);
- Map<const DNode *, Dot::NodeWithSocketsRef> dot_nodes;
- Map<const DGroupInput *, Dot::NodeWithSocketsRef> dot_group_inputs;
- Map<const DParentNode *, Dot::Cluster *> dot_clusters;
+ Map<const DNode *, dot::NodeWithSocketsRef> dot_nodes;
+ Map<const DGroupInput *, dot::NodeWithSocketsRef> dot_group_inputs;
+ Map<const DParentNode *, dot::Cluster *> dot_clusters;
for (const DNode *node : m_nodes_by_id) {
- Dot::Node &dot_node = digraph.new_node("");
+ dot::Node &dot_node = digraph.new_node("");
dot_node.set_background_color("white");
Vector<std::string> input_names;
@@ -398,37 +398,37 @@ std::string DerivedNodeTree::to_dot() const
}
dot_nodes.add_new(node,
- Dot::NodeWithSocketsRef(dot_node, node->name(), input_names, output_names));
+ dot::NodeWithSocketsRef(dot_node, node->name(), input_names, output_names));
- Dot::Cluster *cluster = get_cluster_for_parent(digraph, dot_clusters, node->parent());
+ dot::Cluster *cluster = get_cluster_for_parent(digraph, dot_clusters, node->parent());
dot_node.set_parent_cluster(cluster);
}
for (const DGroupInput *group_input : m_group_inputs) {
- Dot::Node &dot_node = digraph.new_node("");
+ dot::Node &dot_node = digraph.new_node("");
dot_node.set_background_color("white");
std::string group_input_name = group_input->name();
dot_group_inputs.add_new(
- group_input, Dot::NodeWithSocketsRef(dot_node, "Group Input", {}, {group_input_name}));
+ group_input, dot::NodeWithSocketsRef(dot_node, "Group Input", {}, {group_input_name}));
- Dot::Cluster *cluster = get_cluster_for_parent(digraph, dot_clusters, group_input->parent());
+ dot::Cluster *cluster = get_cluster_for_parent(digraph, dot_clusters, group_input->parent());
dot_node.set_parent_cluster(cluster);
}
for (const DNode *to_node : m_nodes_by_id) {
- Dot::NodeWithSocketsRef &to_dot_node = dot_nodes.lookup(to_node);
+ dot::NodeWithSocketsRef &to_dot_node = dot_nodes.lookup(to_node);
for (const DInputSocket *to_socket : to_node->inputs()) {
for (const DOutputSocket *from_socket : to_socket->linked_sockets()) {
const DNode *from_node = &from_socket->node();
- Dot::NodeWithSocketsRef &from_dot_node = dot_nodes.lookup(from_node);
+ dot::NodeWithSocketsRef &from_dot_node = dot_nodes.lookup(from_node);
digraph.new_edge(from_dot_node.output(from_socket->index()),
to_dot_node.input(to_socket->index()));
}
for (const DGroupInput *group_input : to_socket->linked_group_inputs()) {
- Dot::NodeWithSocketsRef &from_dot_node = dot_group_inputs.lookup(group_input);
+ dot::NodeWithSocketsRef &from_dot_node = dot_group_inputs.lookup(group_input);
digraph.new_edge(from_dot_node.output(0), to_dot_node.input(to_socket->index()));
}
@@ -439,4 +439,5 @@ std::string DerivedNodeTree::to_dot() const
return digraph.to_dot_string();
}
-} // namespace BKE
+} // namespace bke
+} // namespace blender
diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c
index dae8a59fe43..2e1fa519284 100644
--- a/source/blender/blenkernel/intern/dynamicpaint.c
+++ b/source/blender/blenkernel/intern/dynamicpaint.c
@@ -5112,7 +5112,7 @@ static void dynamic_paint_prepare_effect_cb(void *__restrict userdata,
EffectedPoint epoint;
pd_point_from_loc(scene, realCoord[bData->s_pos[index]].v, vel, index, &epoint);
epoint.vel_to_sec = 1.0f;
- BKE_effectors_apply(effectors, NULL, surface->effector_weights, &epoint, forc, NULL);
+ BKE_effectors_apply(effectors, NULL, surface->effector_weights, &epoint, forc, NULL, NULL);
}
/* if global gravity is enabled, add it too */
diff --git a/source/blender/blenkernel/intern/editmesh.c b/source/blender/blenkernel/intern/editmesh.c
index 3d5f9cad1c1..1a5b7685c0e 100644
--- a/source/blender/blenkernel/intern/editmesh.c
+++ b/source/blender/blenkernel/intern/editmesh.c
@@ -237,12 +237,13 @@ void BKE_editmesh_lnorspace_update(BMEditMesh *em, Mesh *me)
{
BMesh *bm = em->bm;
- /* We need to create clnors data if none exist yet, otherwise there is no way to edit them.
- * Similar code to MESH_OT_customdata_custom_splitnormals_add operator,
- * we want to keep same shading in case we were using autosmooth so far.
+ /* We need to create custom-loop-normals (CLNORS) data if none exist yet,
+ * otherwise there is no way to edit them.
+ * Similar code to #MESH_OT_customdata_custom_splitnormals_add operator,
+ * we want to keep same shading in case we were using auto-smooth so far.
* Note: there is a problem here, which is that if someone starts a normal editing operation on
- * previously autosmooth-ed mesh, and cancel that operation, generated clnors data remain,
- * with related sharp edges (and hence autosmooth is 'lost').
+ * previously auto-smooth-ed mesh, and cancel that operation, generated CLNORS data remain,
+ * with related sharp edges (and hence auto-smooth is 'lost').
* Not sure how critical this is, and how to fix that issue? */
if (!CustomData_has_layer(&bm->ldata, CD_CUSTOMLOOPNORMAL)) {
if (me->flag & ME_AUTOSMOOTH) {
diff --git a/source/blender/blenkernel/intern/effect.c b/source/blender/blenkernel/intern/effect.c
index fe2c9ed51b8..235c834fde9 100644
--- a/source/blender/blenkernel/intern/effect.c
+++ b/source/blender/blenkernel/intern/effect.c
@@ -108,7 +108,8 @@ PartDeflect *BKE_partdeflect_new(int type)
break;
case PFIELD_WIND:
pd->shape = PFIELD_SHAPE_PLANE;
- pd->f_flow = 1.0f; /* realistic wind behavior */
+ pd->f_flow = 1.0f; /* realistic wind behavior */
+ pd->f_wind_factor = 1.0f; /* only act perpendicularly to a surface */
break;
case PFIELD_TEXTURE:
pd->f_size = 1.0f;
@@ -428,7 +429,7 @@ static float eff_calc_visibility(ListBase *colliders,
EffectorData *efd,
EffectedPoint *point)
{
- const int raycast_flag = BVH_RAYCAST_DEFAULT & ~(BVH_RAYCAST_WATERTIGHT);
+ const int raycast_flag = BVH_RAYCAST_DEFAULT & ~BVH_RAYCAST_WATERTIGHT;
ListBase *colls = colliders;
ColliderCache *col;
float norm[3], len = 0.0;
@@ -1072,7 +1073,8 @@ static void do_physical_effector(EffectorCache *eff,
* scene = scene where it runs in, for time and stuff
* lb = listbase with objects that take part in effecting
* opco = global coord, as input
- * force = force accumulator
+ * force = accumulator for force
+ * wind_force = accumulator for force only acting perpendicular to a surface
* speed = actual current speed which can be altered
* cur_time = "external" time in frames, is constant for static particles
* loc_time = "local" time in frames, range <0-1> for the lifetime of particle
@@ -1085,6 +1087,7 @@ void BKE_effectors_apply(ListBase *effectors,
EffectorWeights *weights,
EffectedPoint *point,
float *force,
+ float *wind_force,
float *impulse)
{
/*
@@ -1120,22 +1123,27 @@ void BKE_effectors_apply(ListBase *effectors,
if (efd.falloff > 0.0f) {
efd.falloff *= eff_calc_visibility(colliders, eff, &efd, point);
}
- if (efd.falloff <= 0.0f) {
- /* don't do anything */
- }
- else if (eff->pd->forcefield == PFIELD_TEXTURE) {
- do_texture_effector(eff, &efd, point, force);
- }
- else {
- float temp1[3] = {0, 0, 0}, temp2[3];
- copy_v3_v3(temp1, force);
+ if (efd.falloff > 0.0f) {
+ float out_force[3] = {0, 0, 0};
- do_physical_effector(eff, &efd, point, force);
+ if (eff->pd->forcefield == PFIELD_TEXTURE) {
+ do_texture_effector(eff, &efd, point, out_force);
+ }
+ else {
+ do_physical_effector(eff, &efd, point, out_force);
- /* for softbody backward compatibility */
- if (point->flag & PE_WIND_AS_SPEED && impulse) {
- sub_v3_v3v3(temp2, force, temp1);
- sub_v3_v3v3(impulse, impulse, temp2);
+ /* for softbody backward compatibility */
+ if (point->flag & PE_WIND_AS_SPEED && impulse) {
+ sub_v3_v3v3(impulse, impulse, out_force);
+ }
+ }
+
+ if (wind_force) {
+ madd_v3_v3fl(force, out_force, 1.0f - eff->pd->f_wind_factor);
+ madd_v3_v3fl(wind_force, out_force, eff->pd->f_wind_factor);
+ }
+ else {
+ add_v3_v3(force, out_force);
}
}
}
diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c
index d4754615c7f..bc14f525c2c 100644
--- a/source/blender/blenkernel/intern/fcurve.c
+++ b/source/blender/blenkernel/intern/fcurve.c
@@ -1063,9 +1063,9 @@ static BezTriple *cycle_offset_triple(
/**
* Variant of #calchandles_fcurve() that allows calculating based on a different select flag.
*
- * \param sel_flag: The flag (bezt.f1/2/3) value to use to determine selection. Usually `SELECT`,
- * but may want to use a different one at times (if caller does not operate on
- * selection).
+ * \param handle_sel_flag: The flag (bezt.f1/2/3) value to use to determine selection.
+ * Usually `SELECT`, but may want to use a different one at times
+ * (if caller does not operate on selection).
*/
void calchandles_fcurve_ex(FCurve *fcu, eBezTriple_Flag handle_sel_flag)
{
diff --git a/source/blender/blenkernel/intern/fcurve_driver.c b/source/blender/blenkernel/intern/fcurve_driver.c
index 78a6cf28824..a0625918a62 100644
--- a/source/blender/blenkernel/intern/fcurve_driver.c
+++ b/source/blender/blenkernel/intern/fcurve_driver.c
@@ -484,14 +484,14 @@ static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar)
dtar->flag &= ~DTAR_FLAG_INVALID;
}
- /* try to get posechannel */
+ /* Try to get pose-channel. */
pchan = BKE_pose_channel_find_name(ob->pose, dtar->pchan_name);
- /* check if object or bone, and get transform matrix accordingly
- * - "useEulers" code is used to prevent the problems associated with non-uniqueness
- * of euler decomposition from matrices [#20870]
- * - localspace is for [#21384], where parent results are not wanted
- * but local-consts is for all the common "corrective-shapes-for-limbs" situations
+ /* Check if object or bone, and get transform matrix accordingly:
+ * - "use_eulers" code is used to prevent the problems associated with non-uniqueness
+ * of euler decomposition from matrices T20870.
+ * - "local-space" is for T21384, where parent results are not wanted
+ * but #DTAR_FLAG_LOCAL_CONSTS is for all the common "corrective-shapes-for-limbs" situations.
*/
if (pchan) {
/* bone */
@@ -517,7 +517,7 @@ static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar)
}
}
else {
- /* worldspace matrix */
+ /* World-space matrix. */
mul_m4_m4m4(mat, ob->obmat, pchan->pose_mat);
}
}
diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c
index b75592836e0..98573ea98ec 100644
--- a/source/blender/blenkernel/intern/fluid.c
+++ b/source/blender/blenkernel/intern/fluid.c
@@ -3175,7 +3175,8 @@ static void update_effectors_task_cb(void *__restrict userdata,
/* do effectors */
pd_point_from_loc(data->scene, voxel_center, vel, index, &epoint);
- BKE_effectors_apply(data->effectors, NULL, mds->effector_weights, &epoint, retvel, NULL);
+ BKE_effectors_apply(
+ data->effectors, NULL, mds->effector_weights, &epoint, retvel, NULL, NULL);
/* convert retvel to local space */
mag = len_v3(retvel);
@@ -3793,8 +3794,8 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *mmd,
int next_frame = scene_framenr + 1;
int prev_frame = scene_framenr - 1;
- /* Ensure positivity of previous frame. */
- CLAMP(prev_frame, mds->cache_frame_start, prev_frame);
+ /* Ensure positive of previous frame. */
+ CLAMP_MIN(prev_frame, mds->cache_frame_start);
int data_frame = scene_framenr, noise_frame = scene_framenr;
int mesh_frame = scene_framenr, particles_frame = scene_framenr, guide_frame = scene_framenr;
@@ -3808,6 +3809,7 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *mmd,
bubble = mds->particle_type & FLUID_DOMAIN_PARTICLE_BUBBLE;
floater = mds->particle_type & FLUID_DOMAIN_PARTICLE_FOAM;
+ bool with_resumable_cache = mds->flags & FLUID_DOMAIN_USE_RESUMABLE_CACHE;
bool with_script, with_adaptive, with_noise, with_mesh, with_particles, with_guide;
with_script = mds->flags & FLUID_DOMAIN_EXPORT_MANTA_SCRIPT;
with_adaptive = mds->flags & FLUID_DOMAIN_USE_ADAPTIVE_DOMAIN;
@@ -3867,13 +3869,7 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *mmd,
/* Cache mode specific settings. */
switch (mode) {
- case FLUID_DOMAIN_CACHE_FINAL:
- /* Just load the data that has already been baked */
- if (!baking_data && !baking_noise && !baking_mesh && !baking_particles && !baking_guide) {
- read_cache = true;
- bake_cache = false;
- }
- break;
+ case FLUID_DOMAIN_CACHE_ALL:
case FLUID_DOMAIN_CACHE_MODULAR:
/* Just load the data that has already been baked */
if (!baking_data && !baking_noise && !baking_mesh && !baking_particles && !baking_guide) {
@@ -3901,10 +3897,10 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *mmd,
}
/* Noise, mesh and particles can never be baked more than data. */
- CLAMP(noise_frame, noise_frame, data_frame);
- CLAMP(mesh_frame, mesh_frame, data_frame);
- CLAMP(particles_frame, particles_frame, data_frame);
- CLAMP(guide_frame, guide_frame, mds->cache_frame_end);
+ CLAMP_MAX(noise_frame, data_frame);
+ CLAMP_MAX(mesh_frame, data_frame);
+ CLAMP_MAX(particles_frame, data_frame);
+ CLAMP_MAX(guide_frame, mds->cache_frame_end);
/* Force to read cache as we're resuming the bake */
read_cache = true;
@@ -3928,6 +3924,7 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *mmd,
break;
}
+ bool read_partial = false, read_all = false;
/* Try to read from cache and keep track of read success. */
if (read_cache) {
@@ -3936,20 +3933,16 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *mmd,
has_config = manta_read_config(mds->fluid, mmd, mesh_frame);
/* Update mesh data from file is faster than via Python (manta_read_mesh()). */
- has_mesh = manta_update_mesh_structures(mds->fluid, mmd, mesh_frame);
+ has_mesh = manta_read_mesh(mds->fluid, mmd, mesh_frame);
}
/* Read particles cache. */
if (with_liquid && with_particles) {
has_config = manta_read_config(mds->fluid, mmd, particles_frame);
- if (!baking_data && !baking_particles && next_particles) {
- /* Update particle data from file is faster than via Python (manta_read_particles()). */
- has_particles = manta_update_particle_structures(mds->fluid, mmd, particles_frame);
- }
- else {
- has_particles = manta_read_particles(mds->fluid, mmd, particles_frame);
- }
+ read_partial = !baking_data && !baking_particles && next_particles;
+ read_all = !read_partial && with_resumable_cache;
+ has_particles = manta_read_particles(mds->fluid, mmd, particles_frame, read_all);
}
/* Read guide cache. */
@@ -3967,12 +3960,10 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *mmd,
manta_needs_realloc(mds->fluid, mmd)) {
BKE_fluid_reallocate_fluid(mds, mds->res, 1);
}
- if (!baking_data && !baking_noise && next_noise) {
- has_noise = manta_update_noise_structures(mds->fluid, mmd, noise_frame);
- }
- else {
- has_noise = manta_read_noise(mds->fluid, mmd, noise_frame);
- }
+
+ read_partial = !baking_data && !baking_noise && next_noise;
+ read_all = !read_partial && with_resumable_cache;
+ has_noise = manta_read_noise(mds->fluid, mmd, noise_frame, read_all);
/* When using the adaptive domain, copy all data that was read to a new fluid object. */
if (with_adaptive && baking_noise) {
@@ -3986,12 +3977,10 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *mmd,
mds, o_res, mds->res, o_min, mds->res_min, o_max, o_shift, mds->shift);
}
}
- if (!baking_data && !baking_noise && next_data && next_noise) {
- /* Nothing to do here since we already loaded noise grids. */
- }
- else {
- has_data = manta_read_data(mds->fluid, mmd, data_frame);
- }
+
+ read_partial = !baking_data && !baking_noise && next_data && next_noise;
+ read_all = !read_partial && with_resumable_cache;
+ has_data = manta_read_data(mds->fluid, mmd, data_frame, read_all);
}
/* Read data cache only */
else {
@@ -4002,28 +3991,17 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *mmd,
if (has_config && manta_needs_realloc(mds->fluid, mmd)) {
BKE_fluid_reallocate_fluid(mds, mds->res, 1);
}
- /* Read data cache */
- if (!baking_data && !baking_particles && !baking_mesh && next_data) {
- has_data = manta_update_smoke_structures(mds->fluid, mmd, data_frame);
- }
- else {
- has_data = manta_read_data(mds->fluid, mmd, data_frame);
- }
- }
- if (with_liquid) {
- if (!baking_data && !baking_particles && !baking_mesh && next_data) {
- has_data = manta_update_liquid_structures(mds->fluid, mmd, data_frame);
- }
- else {
- has_data = manta_read_data(mds->fluid, mmd, data_frame);
- }
}
+
+ read_partial = !baking_data && !baking_particles && !baking_mesh && next_data;
+ read_all = !read_partial && with_resumable_cache;
+ has_data = manta_read_data(mds->fluid, mmd, data_frame, read_all);
}
}
/* Cache mode specific settings */
switch (mode) {
- case FLUID_DOMAIN_CACHE_FINAL:
+ case FLUID_DOMAIN_CACHE_ALL:
case FLUID_DOMAIN_CACHE_MODULAR:
if (!baking_data && !baking_noise && !baking_mesh && !baking_particles && !baking_guide) {
bake_cache = false;
@@ -4605,13 +4583,9 @@ void BKE_fluid_particles_set(FluidDomainSettings *settings, int value, bool clea
void BKE_fluid_domain_type_set(Object *object, FluidDomainSettings *settings, int type)
{
- /* Set common values for liquid/smoke domain: cache type,
- * border collision and viewport draw-type. */
+ /* Set values for border collision:
+ * Liquids should have a closed domain, smoke domains should be open. */
if (type == FLUID_DOMAIN_TYPE_GAS) {
- BKE_fluid_cachetype_mesh_set(settings, FLUID_DOMAIN_FILE_BIN_OBJECT);
- BKE_fluid_cachetype_data_set(settings, FLUID_DOMAIN_FILE_UNI);
- BKE_fluid_cachetype_particle_set(settings, FLUID_DOMAIN_FILE_UNI);
- BKE_fluid_cachetype_noise_set(settings, FLUID_DOMAIN_FILE_UNI);
BKE_fluid_collisionextents_set(settings, FLUID_DOMAIN_BORDER_FRONT, 1);
BKE_fluid_collisionextents_set(settings, FLUID_DOMAIN_BORDER_BACK, 1);
BKE_fluid_collisionextents_set(settings, FLUID_DOMAIN_BORDER_RIGHT, 1);
@@ -4621,10 +4595,6 @@ void BKE_fluid_domain_type_set(Object *object, FluidDomainSettings *settings, in
object->dt = OB_WIRE;
}
else if (type == FLUID_DOMAIN_TYPE_LIQUID) {
- BKE_fluid_cachetype_mesh_set(settings, FLUID_DOMAIN_FILE_BIN_OBJECT);
- BKE_fluid_cachetype_data_set(settings, FLUID_DOMAIN_FILE_UNI);
- BKE_fluid_cachetype_particle_set(settings, FLUID_DOMAIN_FILE_UNI);
- BKE_fluid_cachetype_noise_set(settings, FLUID_DOMAIN_FILE_UNI);
BKE_fluid_collisionextents_set(settings, FLUID_DOMAIN_BORDER_FRONT, 0);
BKE_fluid_collisionextents_set(settings, FLUID_DOMAIN_BORDER_BACK, 0);
BKE_fluid_collisionextents_set(settings, FLUID_DOMAIN_BORDER_RIGHT, 0);
@@ -4986,12 +4956,12 @@ void BKE_fluid_modifier_create_type_data(struct FluidModifierData *mmd)
/* OpenVDB cache options */
#ifdef WITH_OPENVDB_BLOSC
- mmd->domain->openvdb_comp = VDB_COMPRESSION_BLOSC;
+ mmd->domain->openvdb_compression = VDB_COMPRESSION_BLOSC;
#else
- mmd->domain->openvdb_comp = VDB_COMPRESSION_ZIP;
+ mmd->domain->openvdb_compression = VDB_COMPRESSION_ZIP;
#endif
mmd->domain->clipping = 1e-6f;
- mmd->domain->data_depth = 0;
+ mmd->domain->openvdb_data_depth = 0;
}
else if (mmd->type & MOD_FLUID_TYPE_FLOW) {
if (mmd->flow) {
@@ -5228,9 +5198,9 @@ void BKE_fluid_modifier_copy(const struct FluidModifierData *mmd,
}
/* OpenVDB cache options */
- tmds->openvdb_comp = mds->openvdb_comp;
+ tmds->openvdb_compression = mds->openvdb_compression;
tmds->clipping = mds->clipping;
- tmds->data_depth = mds->data_depth;
+ tmds->openvdb_data_depth = mds->openvdb_data_depth;
}
else if (tmmd->flow) {
FluidFlowSettings *tmfs = tmmd->flow;
diff --git a/source/blender/blenkernel/intern/font.c b/source/blender/blenkernel/intern/font.c
index 5771eb053f7..dfa5ff6975f 100644
--- a/source/blender/blenkernel/intern/font.c
+++ b/source/blender/blenkernel/intern/font.c
@@ -78,7 +78,7 @@ static void vfont_init_data(ID *id)
if (vfd) {
vfont->data = vfd;
- BLI_strncpy(vfont->name, FO_BUILTIN_NAME, sizeof(vfont->name));
+ BLI_strncpy(vfont->filepath, FO_BUILTIN_NAME, sizeof(vfont->filepath));
}
/* Free the packed file */
@@ -177,7 +177,7 @@ static int builtin_font_size = 0;
bool BKE_vfont_is_builtin(struct VFont *vfont)
{
- return STREQ(vfont->name, FO_BUILTIN_NAME);
+ return STREQ(vfont->filepath, FO_BUILTIN_NAME);
}
void BKE_vfont_builtin_register(void *mem, int size)
@@ -237,20 +237,20 @@ static VFontData *vfont_get_data(VFont *vfont)
}
}
else {
- pf = BKE_packedfile_new(NULL, vfont->name, ID_BLEND_PATH_FROM_GLOBAL(&vfont->id));
+ pf = BKE_packedfile_new(NULL, vfont->filepath, ID_BLEND_PATH_FROM_GLOBAL(&vfont->id));
if (vfont->temp_pf == NULL) {
vfont->temp_pf = BKE_packedfile_new(
- NULL, vfont->name, ID_BLEND_PATH_FROM_GLOBAL(&vfont->id));
+ NULL, vfont->filepath, ID_BLEND_PATH_FROM_GLOBAL(&vfont->id));
}
}
if (!pf) {
- CLOG_WARN(&LOG, "Font file doesn't exist: %s", vfont->name);
+ CLOG_WARN(&LOG, "Font file doesn't exist: %s", vfont->filepath);
/* DON'T DO THIS
* missing file shouldn't modify path! - campbell */
#if 0
- strcpy(vfont->name, FO_BUILTIN_NAME);
+ strcpy(vfont->filepath, FO_BUILTIN_NAME);
#endif
pf = get_builtin_packedfile();
}
@@ -301,7 +301,7 @@ VFont *BKE_vfont_load(Main *bmain, const char *filepath)
if (vfd->name[0] != '\0') {
BLI_strncpy(vfont->id.name + 2, vfd->name, sizeof(vfont->id.name) - 2);
}
- BLI_strncpy(vfont->name, filepath, sizeof(vfont->name));
+ BLI_strncpy(vfont->filepath, filepath, sizeof(vfont->filepath));
/* if autopack is on store the packedfile in de font structure */
if (!is_builtin && (G.fileflags & G_FILE_AUTOPACK)) {
@@ -333,7 +333,7 @@ VFont *BKE_vfont_load_exists_ex(struct Main *bmain, const char *filepath, bool *
/* first search an identical filepath */
for (vfont = bmain->fonts.first; vfont; vfont = vfont->id.next) {
- BLI_strncpy(strtest, vfont->name, sizeof(vfont->name));
+ BLI_strncpy(strtest, vfont->filepath, sizeof(vfont->filepath));
BLI_path_abs(strtest, ID_BLEND_PATH(bmain, &vfont->id));
if (BLI_path_cmp(strtest, str) == 0) {
diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c
index b460e6d6dc0..dece833c00f 100644
--- a/source/blender/blenkernel/intern/gpencil.c
+++ b/source/blender/blenkernel/intern/gpencil.c
@@ -289,6 +289,16 @@ void BKE_gpencil_eval_delete(bGPdata *gpd_eval)
MEM_freeN(gpd_eval);
}
+/**
+ * Tag data-block for depsgraph update.
+ * Wrapper to avoid include Depsgraph tag functions in other modules.
+ * \param gpd: Grease pencil data-block.
+ */
+void BKE_gpencil_tag(bGPdata *gpd)
+{
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+}
+
/* ************************************************** */
/* Container Creation */
@@ -417,7 +427,7 @@ bGPDlayer *BKE_gpencil_layer_addnew(bGPdata *gpd, const char *name, bool setacti
gpl_active = BKE_gpencil_layer_active_get(gpd);
- /* add to datablock */
+ /* Add to data-block. */
if (gpl_active == NULL) {
BLI_addtail(&gpd->layers, gpl);
}
@@ -469,7 +479,9 @@ bGPDlayer *BKE_gpencil_layer_addnew(bGPdata *gpd, const char *name, bool setacti
return gpl;
}
-/* add a new gp-datablock */
+/**
+ * Add a new grease pencil data-block.
+ */
bGPdata *BKE_gpencil_data_addnew(Main *bmain, const char name[])
{
bGPdata *gpd;
@@ -496,7 +508,7 @@ bGPdata *BKE_gpencil_data_addnew(Main *bmain, const char name[])
ARRAY_SET_ITEMS(gpd->grid.scale, 1.0f, 1.0f); /* Scale */
gpd->grid.lines = GP_DEFAULT_GRID_LINES; /* Number of lines */
- /* onion-skinning settings (datablock level) */
+ /* Onion-skinning settings (data-block level) */
gpd->onion_flag |= (GP_ONION_GHOST_PREVCOL | GP_ONION_GHOST_NEXTCOL);
gpd->onion_flag |= GP_ONION_FADE;
gpd->onion_mode = GP_ONION_MODE_RELATIVE;
@@ -516,8 +528,8 @@ bGPdata *BKE_gpencil_data_addnew(Main *bmain, const char name[])
/**
* Populate stroke with point data from data buffers
*
- * \param array: Flat array of point data values. Each entry has GP_PRIM_DATABUF_SIZE values
- * \param mat: 4x4 transform matrix to transform points into the right coordinate space
+ * \param array: Flat array of point data values. Each entry has #GP_PRIM_DATABUF_SIZE values.
+ * \param mat: 4x4 transform matrix to transform points into the right coordinate space.
*/
void BKE_gpencil_stroke_add_points(bGPDstroke *gps,
const float *array,
@@ -728,7 +740,9 @@ bGPDlayer *BKE_gpencil_layer_duplicate(const bGPDlayer *gpl_src)
return gpl_dst;
}
-/* Standard API to make a copy of GP datablock, separate from copying its data */
+/**
+ * Standard API to make a copy of GP data-block, separate from copying its data.
+ */
bGPdata *BKE_gpencil_copy(Main *bmain, const bGPdata *gpd)
{
bGPdata *gpd_copy;
@@ -736,8 +750,11 @@ bGPdata *BKE_gpencil_copy(Main *bmain, const bGPdata *gpd)
return gpd_copy;
}
-/* make a copy of a given gpencil datablock */
-/* XXX: Should this be deprecated? */
+/**
+ * Make a copy of a given gpencil data-block.
+ *
+ * XXX: Should this be deprecated?
+ */
bGPdata *BKE_gpencil_data_duplicate(Main *bmain, const bGPdata *gpd_src, bool internal_copy)
{
bGPdata *gpd_dst;
@@ -1319,7 +1336,7 @@ int BKE_gpencil_object_material_ensure(Main *bmain, Object *ob, Material *materi
/**
* Creates a new gpencil material and assigns it to object.
*
- * \param *r_index: value is set to zero based index of the new material if r_index is not NULL
+ * \param *r_index: value is set to zero based index of the new material if \a r_index is not NULL.
*/
Material *BKE_gpencil_object_material_new(Main *bmain, Object *ob, const char *name, int *r_index)
{
@@ -1491,9 +1508,9 @@ void BKE_gpencil_dvert_ensure(bGPDstroke *gps)
* Get range of selected frames in layer.
* Always the active frame is considered as selected, so if no more selected the range
* will be equal to the current active frame.
- * \param gpl: Layer
- * \param r_initframe: Number of first selected frame
- * \param r_endframe: Number of last selected frame
+ * \param gpl: Layer.
+ * \param r_initframe: Number of first selected frame.
+ * \param r_endframe: Number of last selected frame.
*/
void BKE_gpencil_frame_range_selected(bGPDlayer *gpl, int *r_initframe, int *r_endframe)
{
@@ -1514,11 +1531,11 @@ void BKE_gpencil_frame_range_selected(bGPDlayer *gpl, int *r_initframe, int *r_e
/**
* Get Falloff factor base on frame range
- * \param gpf: Frame
- * \param actnum: Number of active frame in layer
- * \param f_init: Number of first selected frame
- * \param f_end: Number of last selected frame
- * \param cur_falloff: Curve with falloff factors
+ * \param gpf: Frame.
+ * \param actnum: Number of active frame in layer.
+ * \param f_init: Number of first selected frame.
+ * \param f_end: Number of last selected frame.
+ * \param cur_falloff: Curve with falloff factors.
*/
float BKE_gpencil_multiframe_falloff_calc(
bGPDframe *gpf, int actnum, int f_init, int f_end, CurveMapping *cur_falloff)
@@ -1803,7 +1820,7 @@ bool BKE_gpencil_from_image(SpaceImage *sima, bGPDframe *gpf, const float size,
ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock);
- if (ibuf->rect) {
+ if (ibuf && ibuf->rect) {
int img_x = ibuf->x;
int img_y = ibuf->y;
@@ -1848,10 +1865,10 @@ bool BKE_gpencil_from_image(SpaceImage *sima, bGPDframe *gpf, const float size,
/**
* Helper to check if a layers is used as mask
- * \param view_layer Actual view layer
- * \param gpd Grease pencil datablock
- * \param gpl_mask Actual Layer
- * \return True if the layer is used as mask
+ * \param view_layer: Actual view layer.
+ * \param gpd: Grease pencil data-block.
+ * \param gpl_mask: Actual Layer.
+ * \return True if the layer is used as mask.
*/
static bool gpencil_is_layer_mask(ViewLayer *view_layer, bGPdata *gpd, bGPDlayer *gpl_mask)
{
@@ -1874,8 +1891,7 @@ static bool gpencil_is_layer_mask(ViewLayer *view_layer, bGPdata *gpd, bGPDlayer
/** \name Iterators
*
* Iterate over all visible stroke of all visible layers inside a gpObject.
- * Also take into account onion skining.
- *
+ * Also take into account onion-skinning.
* \{ */
void BKE_gpencil_visible_stroke_iter(ViewLayer *view_layer,
@@ -2081,7 +2097,7 @@ void BKE_gpencil_update_orig_pointers(const Object *ob_orig, const Object *ob_ev
/* Assign pointers to the original stroke and points to the evaluated data. This must
* be done before applying any modifier because at this moment the structure is equals,
- * so we can assume the layer index is the same in both datablocks.
+ * so we can assume the layer index is the same in both data-blocks.
* This data will be used by operators. */
bGPDlayer *gpl_eval = gpd_eval->layers.first;
diff --git a/source/blender/blenkernel/intern/gpencil_curve.c b/source/blender/blenkernel/intern/gpencil_curve.c
index 8299943cc49..66e9e2184c1 100644
--- a/source/blender/blenkernel/intern/gpencil_curve.c
+++ b/source/blender/blenkernel/intern/gpencil_curve.c
@@ -386,7 +386,8 @@ static void gpencil_convert_spline(Main *bmain,
BKE_gpencil_stroke_geometry_update(gps);
}
-/* Convert a curve object to grease pencil stroke.
+/**
+ * Convert a curve object to grease pencil stroke.
*
* \param bmain: Main thread pointer
* \param scene: Original scene.
diff --git a/source/blender/blenkernel/intern/gpencil_geom.c b/source/blender/blenkernel/intern/gpencil_geom.c
index d200e4e3a15..542f80aa9b5 100644
--- a/source/blender/blenkernel/intern/gpencil_geom.c
+++ b/source/blender/blenkernel/intern/gpencil_geom.c
@@ -32,15 +32,22 @@
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
+#include "BLI_ghash.h"
#include "BLI_math_vector.h"
#include "BLI_polyfill_2d.h"
+#include "BLT_translation.h"
+
#include "DNA_gpencil_types.h"
+#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "DNA_scene_types.h"
#include "BKE_deform.h"
#include "BKE_gpencil.h"
#include "BKE_gpencil_geom.h"
+#include "BKE_main.h"
+#include "BKE_material.h"
#include "BKE_object.h"
#include "DEG_depsgraph_query.h"
@@ -1244,7 +1251,8 @@ bool BKE_gpencil_stroke_trim(bGPDstroke *gps)
return false;
}
bool intersect = false;
- int start, end;
+ int start = 0;
+ int end = 0;
float point[3];
/* loop segments from start until we have an intersection */
for (int i = 0; i < gps->totpoints - 2; i++) {
@@ -1500,7 +1508,9 @@ void BKE_gpencil_dissolve_points(bGPDframe *gpf, bGPDstroke *gps, const short ta
}
/* Merge by distance ------------------------------------- */
-/* Reduce a series of points when the distance is below a threshold.
+
+/**
+ * Reduce a series of points when the distance is below a threshold.
* Special case for first and last points (both are keeped) for other points,
* the merge point always is at first point.
* \param gpf: Grease Pencil frame
@@ -1580,6 +1590,398 @@ void BKE_gpencil_stroke_merge_distance(bGPDframe *gpf,
BKE_gpencil_stroke_geometry_update(gps);
}
+typedef struct GpEdge {
+ uint v1, v2;
+ /* Coordinates. */
+ float v1_co[3], v2_co[3];
+ /* Normals. */
+ float n1[3], n2[3];
+ /* Direction of the segment. */
+ float vec[3];
+ int flag;
+} GpEdge;
+
+static int gpencil_next_edge(
+ GpEdge *gp_edges, int totedges, GpEdge *gped_init, const float threshold, const bool reverse)
+{
+ int edge = -1;
+ float last_angle = 999999.0f;
+ for (int i = 0; i < totedges; i++) {
+ GpEdge *gped = &gp_edges[i];
+ if (gped->flag != 0) {
+ continue;
+ }
+ if (reverse) {
+ if (gped_init->v1 != gped->v2) {
+ continue;
+ }
+ }
+ else {
+ if (gped_init->v2 != gped->v1) {
+ continue;
+ }
+ }
+ /* Look for straight lines. */
+ float angle = angle_v3v3(gped->vec, gped_init->vec);
+ if ((angle < threshold) && (angle <= last_angle)) {
+ edge = i;
+ last_angle = angle;
+ }
+ }
+
+ return edge;
+}
+
+static int gpencil_walk_edge(GHash *v_table,
+ GpEdge *gp_edges,
+ int totedges,
+ uint *stroke_array,
+ int init_idx,
+ const float angle,
+ const bool reverse)
+{
+ GpEdge *gped_init = &gp_edges[init_idx];
+ int idx = 1;
+ int edge = 0;
+ while (edge > -1) {
+ edge = gpencil_next_edge(gp_edges, totedges, gped_init, angle, reverse);
+ if (edge > -1) {
+ GpEdge *gped = &gp_edges[edge];
+ stroke_array[idx] = edge;
+ gped->flag = 1;
+ gped_init = &gp_edges[edge];
+ idx++;
+
+ /* Avoid to follow already visited vertice. */
+ if (reverse) {
+ if (BLI_ghash_haskey(v_table, POINTER_FROM_INT(gped->v1))) {
+ edge = -1;
+ }
+ else {
+ BLI_ghash_insert(v_table, POINTER_FROM_INT(gped->v1), POINTER_FROM_INT(gped->v1));
+ }
+ }
+ else {
+ if (BLI_ghash_haskey(v_table, POINTER_FROM_INT(gped->v2))) {
+ edge = -1;
+ }
+ else {
+ BLI_ghash_insert(v_table, POINTER_FROM_INT(gped->v2), POINTER_FROM_INT(gped->v2));
+ }
+ }
+ }
+ }
+
+ return idx;
+}
+
+static void gpencil_generate_edgeloops(Object *ob,
+ bGPDframe *gpf_stroke,
+ const float angle,
+ const int thickness,
+ const float offset,
+ const float matrix[4][4],
+ const bool use_seams)
+{
+ Mesh *me = (Mesh *)ob->data;
+ if (me->totedge == 0) {
+ return;
+ }
+
+ /* Arrays for all edge vertices (forward and backward) that form a edge loop.
+ * This is reused for each edgeloop to create gpencil stroke. */
+ uint *stroke = MEM_callocN(sizeof(uint) * me->totedge * 2, __func__);
+ uint *stroke_fw = MEM_callocN(sizeof(uint) * me->totedge, __func__);
+ uint *stroke_bw = MEM_callocN(sizeof(uint) * me->totedge, __func__);
+
+ /* Create array with all edges. */
+ GpEdge *gp_edges = MEM_callocN(sizeof(GpEdge) * me->totedge, __func__);
+ GpEdge *gped = NULL;
+ for (int i = 0; i < me->totedge; i++) {
+ MEdge *ed = &me->medge[i];
+ gped = &gp_edges[i];
+ MVert *mv1 = &me->mvert[ed->v1];
+ normal_short_to_float_v3(gped->n1, mv1->no);
+
+ gped->v1 = ed->v1;
+ copy_v3_v3(gped->v1_co, mv1->co);
+
+ MVert *mv2 = &me->mvert[ed->v2];
+ normal_short_to_float_v3(gped->n2, mv2->no);
+ gped->v2 = ed->v2;
+ copy_v3_v3(gped->v2_co, mv2->co);
+
+ sub_v3_v3v3(gped->vec, mv1->co, mv2->co);
+
+ /* If use seams, mark as done if not a seam. */
+ if ((use_seams) && ((ed->flag & ME_SEAM) == 0)) {
+ gped->flag = 1;
+ }
+ }
+
+ /* Loop edges to find edgeloops */
+ bool pending = true;
+ int e = 0;
+ while (pending) {
+ /* Clear arrays of stroke. */
+ memset(stroke_fw, 0, sizeof(uint) * me->totedge);
+ memset(stroke_bw, 0, sizeof(uint) * me->totedge);
+ memset(stroke, 0, sizeof(uint) * me->totedge * 2);
+
+ gped = &gp_edges[e];
+ /* Look first unused edge. */
+ if (gped->flag != 0) {
+ e++;
+ if (e == me->totedge) {
+ pending = false;
+ }
+ continue;
+ }
+ /* Add current edge to arrays. */
+ stroke_fw[0] = e;
+ stroke_bw[0] = e;
+ gped->flag = 1;
+
+ /* Hash used to avoid loop over same vertice. */
+ GHash *v_table = BLI_ghash_int_new(__func__);
+ /* Look forward edges. */
+ int totedges = gpencil_walk_edge(v_table, gp_edges, me->totedge, stroke_fw, e, angle, false);
+ /* Look backward edges. */
+ int totbw = gpencil_walk_edge(v_table, gp_edges, me->totedge, stroke_bw, e, angle, true);
+
+ BLI_ghash_free(v_table, NULL, NULL);
+
+ /* Join both arrays. */
+ int array_len = 0;
+ for (int i = totbw - 1; i > 0; i--) {
+ stroke[array_len] = stroke_bw[i];
+ array_len++;
+ }
+ for (int i = 0; i < totedges; i++) {
+ stroke[array_len] = stroke_fw[i];
+ array_len++;
+ }
+
+ /* Create Stroke. */
+ bGPDstroke *gps_stroke = BKE_gpencil_stroke_add(
+ gpf_stroke, 0, array_len + 1, thickness * thickness, false);
+
+ /* Create first segment. */
+ float fpt[3];
+ uint v = stroke[0];
+ gped = &gp_edges[v];
+ bGPDspoint *pt = &gps_stroke->points[0];
+ mul_v3_v3fl(fpt, gped->n1, offset);
+ add_v3_v3v3(&pt->x, gped->v1_co, fpt);
+ mul_m4_v3(matrix, &pt->x);
+
+ pt->pressure = 1.0f;
+ pt->strength = 1.0f;
+
+ pt = &gps_stroke->points[1];
+ mul_v3_v3fl(fpt, gped->n2, offset);
+ add_v3_v3v3(&pt->x, gped->v2_co, fpt);
+ mul_m4_v3(matrix, &pt->x);
+
+ pt->pressure = 1.0f;
+ pt->strength = 1.0f;
+
+ /* Add next segments. */
+ for (int i = 1; i < array_len; i++) {
+ v = stroke[i];
+ gped = &gp_edges[v];
+
+ pt = &gps_stroke->points[i + 1];
+ mul_v3_v3fl(fpt, gped->n2, offset);
+ add_v3_v3v3(&pt->x, gped->v2_co, fpt);
+ mul_m4_v3(matrix, &pt->x);
+
+ pt->pressure = 1.0f;
+ pt->strength = 1.0f;
+ }
+
+ BKE_gpencil_stroke_geometry_update(gps_stroke);
+ }
+
+ /* Free memory. */
+ MEM_SAFE_FREE(stroke);
+ MEM_SAFE_FREE(stroke_fw);
+ MEM_SAFE_FREE(stroke_bw);
+ MEM_SAFE_FREE(gp_edges);
+}
+
+/* Helper: Add gpencil material using material as base. */
+static Material *gpencil_add_material(Main *bmain,
+ Object *ob_gp,
+ const char *name,
+ const float color[4],
+ const bool use_stroke,
+ const bool use_fill,
+ int *r_idx)
+{
+ Material *mat_gp = BKE_gpencil_object_material_new(bmain, ob_gp, name, r_idx);
+ MaterialGPencilStyle *gp_style = mat_gp->gp_style;
+
+ /* Stroke color. */
+ if (use_stroke) {
+ ARRAY_SET_ITEMS(gp_style->stroke_rgba, 0.0f, 0.0f, 0.0f, 1.0f);
+ gp_style->flag |= GP_MATERIAL_STROKE_SHOW;
+ }
+ else {
+ linearrgb_to_srgb_v4(gp_style->stroke_rgba, color);
+ gp_style->flag &= ~GP_MATERIAL_STROKE_SHOW;
+ }
+
+ /* Fill color. */
+ linearrgb_to_srgb_v4(gp_style->fill_rgba, color);
+ if (use_fill) {
+ gp_style->flag |= GP_MATERIAL_FILL_SHOW;
+ }
+
+ /* Check at least one is enabled. */
+ if (((gp_style->flag & GP_MATERIAL_STROKE_SHOW) == 0) &&
+ ((gp_style->flag & GP_MATERIAL_FILL_SHOW) == 0)) {
+ gp_style->flag |= GP_MATERIAL_STROKE_SHOW;
+ }
+
+ return mat_gp;
+}
+
+/**
+ * Convert a mesh object to grease pencil stroke.
+ *
+ * \param bmain: Main thread pointer.
+ * \param depsgraph: Original depsgraph.
+ * \param scene: Original scene.
+ * \param ob_gp: Grease pencil object to add strokes.
+ * \param ob_mesh: Mesh to convert.
+ * \param angle: Limit angle to consider a edgeloop ends.
+ * \param thickness: Thickness of the strokes.
+ * \param offset: Offset along the normals.
+ * \param matrix: Transformation matrix.
+ * \param frame_offset: Destination frame number offset.
+ * \param use_seams: Only export seam edges.
+ * \param use_faces: Export faces as filled strokes.
+ */
+void BKE_gpencil_convert_mesh(Main *bmain,
+ Depsgraph *depsgraph,
+ Scene *scene,
+ Object *ob_gp,
+ Object *ob_mesh,
+ const float angle,
+ const int thickness,
+ const float offset,
+ const float matrix[4][4],
+ const int frame_offset,
+ const bool use_seams,
+ const bool use_faces)
+{
+ if (ELEM(NULL, ob_gp, ob_mesh) || (ob_gp->type != OB_GPENCIL) || (ob_gp->data == NULL)) {
+ return;
+ }
+
+ bGPdata *gpd = (bGPdata *)ob_gp->data;
+
+ /* Use evaluated data to get mesh with all modifiers on top. */
+ Object *ob_eval = (Object *)DEG_get_evaluated_object(depsgraph, ob_mesh);
+ Mesh *me_eval = BKE_object_get_evaluated_mesh(ob_eval);
+ MPoly *mp, *mpoly = me_eval->mpoly;
+ MLoop *mloop = me_eval->mloop;
+ int mpoly_len = me_eval->totpoly;
+ int i;
+
+ /* If the object has enough materials means it was created in a previous step. */
+ const bool create_mat = ((ob_gp->totcol > 0) && (ob_gp->totcol >= ob_mesh->totcol)) ? false :
+ true;
+
+ /* Need at least an edge. */
+ if (me_eval->totvert < 2) {
+ return;
+ }
+
+ int r_idx;
+ const float default_colors[2][4] = {{0.0f, 0.0f, 0.0f, 1.0f}, {0.7f, 0.7f, 0.7f, 1.0f}};
+ /* Create stroke material. */
+ if (create_mat) {
+ gpencil_add_material(bmain, ob_gp, "Stroke", default_colors[0], true, false, &r_idx);
+ }
+ /* Export faces as filled strokes. */
+ if (use_faces) {
+ if (create_mat) {
+ /* Find a material slot with material assigned */
+ bool material_found = false;
+ for (i = 0; i < ob_mesh->totcol; i++) {
+ Material *ma = BKE_object_material_get(ob_mesh, i + 1);
+ if (ma != NULL) {
+ material_found = true;
+ break;
+ }
+ }
+
+ /* If no materials, create a simple fill. */
+ if (!material_found) {
+ gpencil_add_material(bmain, ob_gp, "Fill", default_colors[1], false, true, &r_idx);
+ }
+ else {
+ /* Create all materials for fill. */
+ for (i = 0; i < ob_mesh->totcol; i++) {
+ Material *ma = BKE_object_material_get(ob_mesh, i + 1);
+ if (ma == NULL) {
+ continue;
+ }
+ float color[4];
+ copy_v3_v3(color, &ma->r);
+ color[3] = 1.0f;
+ gpencil_add_material(bmain, ob_gp, ma->id.name + 2, color, false, true, &r_idx);
+ }
+ }
+ }
+
+ /* Read all polygons and create fill for each. */
+ if (mpoly_len > 0) {
+ bGPDlayer *gpl_fill = BKE_gpencil_layer_named_get(gpd, DATA_("Fills"));
+ if (gpl_fill == NULL) {
+ gpl_fill = BKE_gpencil_layer_addnew(gpd, DATA_("Fills"), true);
+ }
+ bGPDframe *gpf_fill = BKE_gpencil_layer_frame_get(
+ gpl_fill, CFRA + frame_offset, GP_GETFRAME_ADD_NEW);
+ for (i = 0, mp = mpoly; i < mpoly_len; i++, mp++) {
+ MLoop *ml = &mloop[mp->loopstart];
+ /* Create fill stroke. */
+ bGPDstroke *gps_fill = BKE_gpencil_stroke_add(
+ gpf_fill, mp->mat_nr + 1, mp->totloop, 10, false);
+ gps_fill->flag |= GP_STROKE_CYCLIC;
+
+ /* Add points to strokes. */
+ int j;
+ for (j = 0; j < mp->totloop; j++, ml++) {
+ MVert *mv = &me_eval->mvert[ml->v];
+
+ bGPDspoint *pt = &gps_fill->points[j];
+ copy_v3_v3(&pt->x, mv->co);
+ mul_m4_v3(matrix, &pt->x);
+ pt->pressure = 1.0f;
+ pt->strength = 1.0f;
+ }
+
+ BKE_gpencil_stroke_geometry_update(gps_fill);
+ }
+ }
+ }
+
+ /* Create stroke from edges. */
+ bGPDlayer *gpl_stroke = BKE_gpencil_layer_named_get(gpd, DATA_("Lines"));
+ if (gpl_stroke == NULL) {
+ gpl_stroke = BKE_gpencil_layer_addnew(gpd, DATA_("Lines"), true);
+ }
+ bGPDframe *gpf_stroke = BKE_gpencil_layer_frame_get(
+ gpl_stroke, CFRA + frame_offset, GP_GETFRAME_ADD_NEW);
+ gpencil_generate_edgeloops(ob_eval, gpf_stroke, angle, thickness, offset, matrix, use_seams);
+
+ /* Tag for recalculation */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
+}
+
/* Apply Transforms */
void BKE_gpencil_transform(bGPdata *gpd, float mat[4][4])
{
diff --git a/source/blender/blenkernel/intern/gpencil_modifier.c b/source/blender/blenkernel/intern/gpencil_modifier.c
index 2f8500bd1df..fa0cbb0ab5c 100644
--- a/source/blender/blenkernel/intern/gpencil_modifier.c
+++ b/source/blender/blenkernel/intern/gpencil_modifier.c
@@ -53,6 +53,9 @@
#include "MOD_gpencil_modifiertypes.h"
+#include "CLG_log.h"
+
+static CLG_LogRef LOG = {"bke.gpencil_modifier"};
static GpencilModifierTypeInfo *modifier_gpencil_types[NUM_GREASEPENCIL_MODIFIER_TYPES] = {NULL};
/* *************************************************** */
@@ -275,8 +278,7 @@ void BKE_gpencil_stroke_simplify_fixed(bGPDstroke *gps)
/* init lattice deform data */
void BKE_gpencil_lattice_init(Object *ob)
{
- GpencilModifierData *md;
- for (md = ob->greasepencil_modifiers.first; md; md = md->next) {
+ LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) {
if (md->type == eGpencilModifierType_Lattice) {
LatticeGpencilModifierData *mmd = (LatticeGpencilModifierData *)md;
Object *latob = NULL;
@@ -298,8 +300,7 @@ void BKE_gpencil_lattice_init(Object *ob)
/* clear lattice deform data */
void BKE_gpencil_lattice_clear(Object *ob)
{
- GpencilModifierData *md;
- for (md = ob->greasepencil_modifiers.first; md; md = md->next) {
+ LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) {
if (md->type == eGpencilModifierType_Lattice) {
LatticeGpencilModifierData *mmd = (LatticeGpencilModifierData *)md;
if ((mmd) && (mmd->cache_data)) {
@@ -316,8 +317,7 @@ void BKE_gpencil_lattice_clear(Object *ob)
/* check if exist geometry modifiers */
bool BKE_gpencil_has_geometry_modifiers(Object *ob)
{
- GpencilModifierData *md;
- for (md = ob->greasepencil_modifiers.first; md; md = md->next) {
+ LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) {
const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(md->type);
if (mti && mti->generateStrokes) {
@@ -330,8 +330,7 @@ bool BKE_gpencil_has_geometry_modifiers(Object *ob)
/* check if exist time modifiers */
bool BKE_gpencil_has_time_modifiers(Object *ob)
{
- GpencilModifierData *md;
- for (md = ob->greasepencil_modifiers.first; md; md = md->next) {
+ LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) {
const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(md->type);
if (mti && mti->remapTime) {
@@ -344,8 +343,7 @@ bool BKE_gpencil_has_time_modifiers(Object *ob)
/* Check if exist transform stroke modifiers (to rotate sculpt or edit). */
bool BKE_gpencil_has_transform_modifiers(Object *ob)
{
- GpencilModifierData *md;
- for (md = ob->greasepencil_modifiers.first; md; md = md->next) {
+ LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) {
/* Only if enabled in edit mode. */
if (!GPENCIL_MODIFIER_EDIT(md, true) && GPENCIL_MODIFIER_ACTIVE(md, false)) {
if ((md->type == eGpencilModifierType_Armature) || (md->type == eGpencilModifierType_Hook) ||
@@ -362,12 +360,11 @@ bool BKE_gpencil_has_transform_modifiers(Object *ob)
static int gpencil_time_modifier(
Depsgraph *depsgraph, Scene *scene, Object *ob, bGPDlayer *gpl, int cfra, bool is_render)
{
- GpencilModifierData *md;
bGPdata *gpd = ob->data;
const bool is_edit = GPENCIL_ANY_EDIT_MODE(gpd);
int nfra = cfra;
- for (md = ob->greasepencil_modifiers.first; md; md = md->next) {
+ LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) {
if (GPENCIL_MODIFIER_ACTIVE(md, is_render)) {
const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(md->type);
@@ -428,9 +425,9 @@ GpencilModifierData *BKE_gpencil_modifier_new(int type)
BLI_strncpy(md->name, DATA_(mti->name), sizeof(md->name));
md->type = type;
- md->mode = eGpencilModifierMode_Realtime | eGpencilModifierMode_Render |
- eGpencilModifierMode_Expanded;
+ md->mode = eGpencilModifierMode_Realtime | eGpencilModifierMode_Render;
md->flag = eGpencilModifierFlag_OverrideLibrary_Local;
+ md->ui_expand_flag = 1; /* Only expand the parent panel at first. */
if (mti->flags & eGpencilModifierTypeFlag_EnableInEditmode) {
md->mode |= eGpencilModifierMode_Editmode;
@@ -508,7 +505,8 @@ bool BKE_gpencil_modifier_depends_ontime(GpencilModifierData *md)
const GpencilModifierTypeInfo *BKE_gpencil_modifier_get_info(GpencilModifierType type)
{
/* type unsigned, no need to check < 0 */
- if (type < NUM_GREASEPENCIL_MODIFIER_TYPES && modifier_gpencil_types[type]->name[0] != '\0') {
+ if (type < NUM_GREASEPENCIL_MODIFIER_TYPES && type > 0 &&
+ modifier_gpencil_types[type]->name[0] != '\0') {
return modifier_gpencil_types[type];
}
else {
@@ -516,6 +514,17 @@ const GpencilModifierTypeInfo *BKE_gpencil_modifier_get_info(GpencilModifierType
}
}
+/**
+ * Get the idname of the modifier type's panel, which was defined in the #panelRegister callback.
+ */
+void BKE_gpencil_modifierType_panel_id(GpencilModifierType type, char *r_idname)
+{
+ const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(type);
+
+ strcpy(r_idname, GPENCIL_MODIFIER_TYPE_PANEL_PREFIX);
+ strcat(r_idname, mti->name);
+}
+
void BKE_gpencil_modifier_copydata_generic(const GpencilModifierData *md_src,
GpencilModifierData *md_dst)
{
@@ -553,6 +562,7 @@ void BKE_gpencil_modifier_copydata_ex(GpencilModifierData *md,
target->mode = md->mode;
target->flag = md->flag;
+ target->ui_expand_flag = md->ui_expand_flag; /* Expand the parent panel by default. */
if (mti->copyData) {
mti->copyData(md, target);
@@ -587,6 +597,26 @@ GpencilModifierData *BKE_gpencil_modifiers_findby_type(Object *ob, GpencilModifi
return md;
}
+void BKE_gpencil_modifier_set_error(GpencilModifierData *md, const char *_format, ...)
+{
+ char buffer[512];
+ va_list ap;
+ const char *format = TIP_(_format);
+
+ va_start(ap, _format);
+ vsnprintf(buffer, sizeof(buffer), format, ap);
+ va_end(ap);
+ buffer[sizeof(buffer) - 1] = '\0';
+
+ if (md->error) {
+ MEM_freeN(md->error);
+ }
+
+ md->error = BLI_strdup(buffer);
+
+ CLOG_STR_ERROR(&LOG, md->error);
+}
+
void BKE_gpencil_modifiers_foreach_ID_link(Object *ob, GreasePencilIDWalkFunc walk, void *userData)
{
GpencilModifierData *md = ob->greasepencil_modifiers.first;
diff --git a/source/blender/blenkernel/intern/idprop.c b/source/blender/blenkernel/intern/idprop.c
index 669539ca574..c3c67b9ed51 100644
--- a/source/blender/blenkernel/intern/idprop.c
+++ b/source/blender/blenkernel/intern/idprop.c
@@ -732,14 +732,14 @@ void IDP_FreeFromGroup(IDProperty *group, IDProperty *prop)
IDP_FreeProperty(prop);
}
-IDProperty *IDP_GetPropertyFromGroup(IDProperty *prop, const char *name)
+IDProperty *IDP_GetPropertyFromGroup(const IDProperty *prop, const char *name)
{
BLI_assert(prop->type == IDP_GROUP);
return (IDProperty *)BLI_findstring(&prop->data.group, name, offsetof(IDProperty, name));
}
/** same as above but ensure type match */
-IDProperty *IDP_GetPropertyTypeFromGroup(IDProperty *prop, const char *name, const char type)
+IDProperty *IDP_GetPropertyTypeFromGroup(const IDProperty *prop, const char *name, const char type)
{
IDProperty *idprop = IDP_GetPropertyFromGroup(prop, name);
return (idprop && idprop->type == type) ? idprop : NULL;
diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c
index b4a3f249c63..bb793b58a1d 100644
--- a/source/blender/blenkernel/intern/image.c
+++ b/source/blender/blenkernel/intern/image.c
@@ -360,13 +360,7 @@ void BKE_image_free_buffers_ex(Image *ima, bool do_lock)
ima->rr = NULL;
}
- if (!G.background) {
- /* Background mode doesn't use OpenGL,
- * so we can avoid freeing GPU images and save some
- * time by skipping mutex lock.
- */
- GPU_free_image(ima);
- }
+ GPU_free_image(ima);
LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) {
tile->ok = IMA_OK;
@@ -648,7 +642,7 @@ char BKE_image_alpha_mode_from_extension_ex(const char *filepath)
void BKE_image_alpha_mode_from_extension(Image *image)
{
- image->alpha_mode = BKE_image_alpha_mode_from_extension_ex(image->name);
+ image->alpha_mode = BKE_image_alpha_mode_from_extension_ex(image->filepath);
}
Image *BKE_image_load(Main *bmain, const char *filepath)
@@ -668,7 +662,7 @@ Image *BKE_image_load(Main *bmain, const char *filepath)
close(file);
ima = image_alloc(bmain, BLI_path_basename(filepath), IMA_SRC_FILE, IMA_TYPE_IMAGE);
- STRNCPY(ima->name, filepath);
+ STRNCPY(ima->filepath, filepath);
if (BLI_path_extension_check_array(filepath, imb_ext_movie)) {
ima->source = IMA_SRC_MOVIE;
@@ -694,7 +688,7 @@ Image *BKE_image_load_exists_ex(Main *bmain, const char *filepath, bool *r_exist
/* first search an identical filepath */
for (ima = bmain->images.first; ima; ima = ima->id.next) {
if (ima->source != IMA_SRC_VIEWER && ima->source != IMA_SRC_GENERATED) {
- STRNCPY(strtest, ima->name);
+ STRNCPY(strtest, ima->filepath);
BLI_path_abs(strtest, ID_BLEND_PATH(bmain, &ima->id));
if (BLI_path_cmp(strtest, str) == 0) {
@@ -830,7 +824,7 @@ Image *BKE_image_add_generated(Main *bmain,
int view_id;
const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME};
- /* STRNCPY(ima->name, name); */ /* don't do this, this writes in ain invalid filepath! */
+ /* STRNCPY(ima->filepath, name); */ /* don't do this, this writes in ain invalid filepath! */
ima->gen_x = width;
ima->gen_y = height;
ima->gen_type = gen_type;
@@ -846,7 +840,7 @@ Image *BKE_image_add_generated(Main *bmain,
for (view_id = 0; view_id < 2; view_id++) {
ImBuf *ibuf;
ibuf = add_ibuf_size(
- width, height, ima->name, depth, floatbuf, gen_type, color, &ima->colorspace_settings);
+ width, height, ima->filepath, depth, floatbuf, gen_type, color, &ima->colorspace_settings);
int index = tiled ? 0 : IMA_NO_INDEX;
int entry = tiled ? 1001 : 0;
image_assign_ibuf(ima, ibuf, stereo3d ? view_id : index, entry);
@@ -881,7 +875,7 @@ Image *BKE_image_add_from_imbuf(Main *bmain, ImBuf *ibuf, const char *name)
ima = image_alloc(bmain, name, IMA_SRC_FILE, IMA_TYPE_IMAGE);
if (ima) {
- STRNCPY(ima->name, ibuf->name);
+ STRNCPY(ima->filepath, ibuf->name);
image_assign_ibuf(ima, ibuf, IMA_NO_INDEX, 0);
ImageTile *tile = BKE_image_get_tile(ima, 0);
tile->ok = IMA_OK_LOADED;
@@ -981,9 +975,9 @@ void BKE_image_packfiles(ReportList *reports, Image *ima, const char *basepath)
if (totfiles == 1) {
ImagePackedFile *imapf = MEM_mallocN(sizeof(ImagePackedFile), "Image packed file");
BLI_addtail(&ima->packedfiles, imapf);
- imapf->packedfile = BKE_packedfile_new(reports, ima->name, basepath);
+ imapf->packedfile = BKE_packedfile_new(reports, ima->filepath, basepath);
if (imapf->packedfile) {
- STRNCPY(imapf->filepath, ima->name);
+ STRNCPY(imapf->filepath, ima->filepath);
}
else {
BLI_freelinkN(&ima->packedfiles, imapf);
@@ -1020,7 +1014,7 @@ void BKE_image_packfiles_from_mem(ReportList *reports,
ImagePackedFile *imapf = MEM_mallocN(sizeof(ImagePackedFile), __func__);
BLI_addtail(&ima->packedfiles, imapf);
imapf->packedfile = BKE_packedfile_new_from_memory(data, data_len);
- STRNCPY(imapf->filepath, ima->name);
+ STRNCPY(imapf->filepath, ima->filepath);
}
}
@@ -3385,7 +3379,7 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal)
* Here we ensure original image path wouldn't be used when saving
* generated image.
*/
- ima->name[0] = '\0';
+ ima->filepath[0] = '\0';
}
if (ima->source != IMA_SRC_TILED) {
@@ -3643,7 +3637,7 @@ bool BKE_image_fill_tile(struct Image *ima,
image_free_tile(ima, tile);
ImBuf *tile_ibuf = add_ibuf_size(
- width, height, ima->name, planes, is_float, gen_type, color, &ima->colorspace_settings);
+ width, height, ima->filepath, planes, is_float, gen_type, color, &ima->colorspace_settings);
if (tile_ibuf != NULL) {
image_assign_ibuf(ima, tile_ibuf, 0, tile->tile_number);
@@ -3807,7 +3801,7 @@ bool BKE_image_is_openexr(struct Image *ima)
{
#ifdef WITH_OPENEXR
if (ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_SEQUENCE, IMA_SRC_TILED)) {
- return BLI_path_extension_check(ima->name, ".exr");
+ return BLI_path_extension_check(ima->filepath, ".exr");
}
#else
UNUSED_VARS(ima);
@@ -4893,7 +4887,7 @@ static ImBuf *image_acquire_ibuf(Image *ima, ImageUser *iuser, void **r_lock)
}
ibuf = add_ibuf_size(ima->gen_x,
ima->gen_y,
- ima->name,
+ ima->filepath,
ima->gen_depth,
(ima->gen_flag & IMA_GEN_FLOAT) != 0,
ima->gen_type,
@@ -5300,11 +5294,11 @@ void BKE_image_user_file_path(ImageUser *iuser, Image *ima, char *filepath)
BLI_strncpy(filepath, iv->filepath, FILE_MAX);
}
else {
- BLI_strncpy(filepath, ima->name, FILE_MAX);
+ BLI_strncpy(filepath, ima->filepath, FILE_MAX);
}
}
else {
- BLI_strncpy(filepath, ima->name, FILE_MAX);
+ BLI_strncpy(filepath, ima->filepath, FILE_MAX);
}
if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_TILED)) {
@@ -5461,7 +5455,7 @@ float *BKE_image_get_float_pixels_for_frame(struct Image *image, int frame, int
int BKE_image_sequence_guess_offset(Image *image)
{
- return BLI_path_sequence_decode(image->name, NULL, NULL, NULL);
+ return BLI_path_sequence_decode(image->filepath, NULL, NULL, NULL);
}
bool BKE_image_has_anim(Image *ima)
@@ -5478,7 +5472,7 @@ bool BKE_image_has_filepath(Image *ima)
{
/* This could be improved to detect cases like //../../, currently path
* remapping empty file paths empty. */
- return ima->name[0] != '\0';
+ return ima->filepath[0] != '\0';
}
/* Checks the image buffer changes with time (not keyframed values). */
@@ -5653,14 +5647,14 @@ static void image_update_views_format(Image *ima, ImageUser *iuser)
const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME};
for (i = 0; i < 2; i++) {
- image_add_view(ima, names[i], ima->name);
+ image_add_view(ima, names[i], ima->filepath);
}
return;
}
else {
/* R_IMF_VIEWS_INDIVIDUAL */
char prefix[FILE_MAX] = {'\0'};
- char *name = ima->name;
+ char *name = ima->filepath;
const char *ext = NULL;
BKE_scene_multiview_view_prefix_get(scene, name, prefix, &ext);
diff --git a/source/blender/blenkernel/intern/image_save.c b/source/blender/blenkernel/intern/image_save.c
index c034fe895a6..19eb3380b8e 100644
--- a/source/blender/blenkernel/intern/image_save.c
+++ b/source/blender/blenkernel/intern/image_save.c
@@ -73,7 +73,7 @@ static void image_save_post(ReportList *reports,
if (opts->do_newpath) {
BLI_strncpy(ibuf->name, filepath, sizeof(ibuf->name));
- BLI_strncpy(ima->name, filepath, sizeof(ima->name));
+ BLI_strncpy(ima->filepath, filepath, sizeof(ima->filepath));
}
ibuf->userflags &= ~IB_BITMAPDIRTY;
@@ -106,7 +106,7 @@ static void image_save_post(ReportList *reports,
/* only image path, never ibuf */
if (opts->relative) {
const char *relbase = ID_BLEND_PATH(opts->bmain, &ima->id);
- BLI_path_rel(ima->name, relbase); /* only after saving */
+ BLI_path_rel(ima->filepath, relbase); /* only after saving */
}
ColorManagedColorspaceSettings old_colorspace_settings;
@@ -135,7 +135,7 @@ static void imbuf_save_post(ImBuf *ibuf, ImBuf *colormanaged_ibuf)
/**
* \return success.
- * \note ``ima->name`` and ``ibuf->name`` should end up the same.
+ * \note ``ima->filepath`` and ``ibuf->name`` should end up the same.
* \note for multiview the first ``ibuf`` is important to get the settings.
*/
static bool image_save_single(ReportList *reports,
diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c
index ab9b11f436a..7c09ae51344 100644
--- a/source/blender/blenkernel/intern/lib_id.c
+++ b/source/blender/blenkernel/intern/lib_id.c
@@ -114,7 +114,7 @@ IDTypeInfo IDType_ID_LINK_PLACEHOLDER = {
* Also note that the id _must_ have a library - campbell */
static void lib_id_library_local_paths(Main *bmain, Library *lib, ID *id)
{
- const char *bpath_user_data[2] = {BKE_main_blendfile_path(bmain), lib->filepath};
+ const char *bpath_user_data[2] = {BKE_main_blendfile_path(bmain), lib->filepath_abs};
BKE_bpath_traverse_id(bmain,
id,
@@ -160,7 +160,7 @@ static void lib_id_clear_library_data_ex(Main *bmain, ID *id)
BKE_lib_libblock_session_uuid_renew(id);
/* We need to tag this IDs and all of its users, conceptually new local ID and original linked
- * ones are two completely different data-blocks that were virtually remaped, even though in
+ * ones are two completely different data-blocks that were virtually remapped, even though in
* reality they remain the same data. For undo this info is critical now. */
DEG_id_tag_update_ex(bmain, id, ID_RECALC_COPY_ON_WRITE);
ID *id_iter;
@@ -225,7 +225,7 @@ void id_us_ensure_real(ID *id)
CLOG_ERROR(&LOG,
"ID user count error: %s (from '%s')",
id->name,
- id->lib ? id->lib->filepath : "[Main]");
+ id->lib ? id->lib->filepath_abs : "[Main]");
BLI_assert(0);
}
id->us = limit + 1;
@@ -283,7 +283,7 @@ void id_us_min(ID *id)
CLOG_ERROR(&LOG,
"ID user decrement error: %s (from '%s'): %d <= %d",
id->name,
- id->lib ? id->lib->filepath : "[Main]",
+ id->lib ? id->lib->filepath_abs : "[Main]",
id->us,
limit);
if (GS(id->name) != ID_IP) {
@@ -446,9 +446,9 @@ void BKE_lib_id_make_local_generic(Main *bmain, ID *id, const int flags)
/**
* Calls the appropriate make_local method for the block, unless test is set.
*
- * \note Always set ID->newid pointer in case it gets duplicated...
+ * \note Always set #ID.newid pointer in case it gets duplicated.
*
- * \param lib_local: Special flag used when making a whole library's content local,
+ * \param flags: Special flag used when making a whole library's content local,
* it needs specific handling.
*
* \return true if the block can be made local.
@@ -608,6 +608,49 @@ bool BKE_id_copy(Main *bmain, const ID *id, ID **newid)
}
/**
+ * Invokes the appropriate copy method for the block and returns the result in
+ * newid, unless test. Returns true if the block can be copied.
+ */
+ID *BKE_id_copy_for_duplicate(Main *bmain,
+ ID *id,
+ const bool is_owner_id_liboverride,
+ const eDupli_ID_Flags duplicate_flags)
+{
+ if (id == NULL) {
+ return NULL;
+ }
+ if (id->newid == NULL) {
+ if (!is_owner_id_liboverride || !ID_IS_LINKED(id)) {
+ ID *id_new;
+ BKE_id_copy(bmain, id, &id_new);
+ /* Copying add one user by default, need to get rid of that one. */
+ id_us_min(id_new);
+ ID_NEW_SET(id, id_new);
+
+ /* Shape keys are always copied with their owner ID, by default. */
+ ID *key_new = (ID *)BKE_key_from_id(id_new);
+ ID *key = (ID *)BKE_key_from_id(id);
+ if (key != NULL) {
+ ID_NEW_SET(key, key_new);
+ }
+
+ /* Note: embedded data (root nodetrees and master collections) should never be referenced by
+ * anything else, so we do not need to set their newid pointer and flag. */
+
+ if (duplicate_flags & USER_DUP_ACT) {
+ BKE_animdata_copy_id_action(bmain, id_new, true);
+ if (key_new != NULL) {
+ BKE_animdata_copy_id_action(bmain, key_new, true);
+ }
+ /* Note that actions of embedded data (root nodetrees and master collections) are handled
+ * by `BKE_animdata_copy_id_action` as well. */
+ }
+ }
+ }
+ return id->newid;
+}
+
+/**
* Does a mere memory swap over the whole IDs data (including type-specific memory).
* \note Most internal ID data itself is not swapped (only IDProperties are).
*/
@@ -636,6 +679,9 @@ static void id_swap(Main *bmain, ID *id_a, ID *id_b, const bool do_full_id)
/* Exception: IDProperties. */
id_a->properties = id_b_back.properties;
id_b->properties = id_a_back.properties;
+ /* Exception: recalc flags. */
+ id_a->recalc = id_b_back.recalc;
+ id_b->recalc = id_a_back.recalc;
}
if (bmain != NULL) {
@@ -1183,7 +1229,7 @@ void BKE_libblock_copy_ex(Main *bmain, const ID *id, ID **r_newid, const int ori
/* We may need our own flag to control that at some point, but for now 'no main' one should be
* good enough. */
- if ((orig_flag & LIB_ID_CREATE_NO_MAIN) == 0 && id->override_library != NULL) {
+ if ((orig_flag & LIB_ID_CREATE_NO_MAIN) == 0 && ID_IS_OVERRIDE_LIBRARY(id)) {
/* We do not want to copy existing override rules here, as they would break the proper
* remapping between IDs. Proper overrides rules will be re-generated anyway. */
BKE_lib_override_library_copy(new_id, id, false);
@@ -2129,13 +2175,18 @@ void BKE_id_full_name_get(char name[MAX_ID_FULL_NAME], const ID *id, char separa
*/
void BKE_id_full_name_ui_prefix_get(char name[MAX_ID_FULL_NAME_UI],
const ID *id,
+ const bool add_lib_hint,
char separator_char)
{
- name[0] = id->lib ? (ID_MISSING(id) ? 'M' : 'L') : ID_IS_OVERRIDE_LIBRARY(id) ? 'O' : ' ';
- name[1] = (id->flag & LIB_FAKEUSER) ? 'F' : ((id->us == 0) ? '0' : ' ');
- name[2] = ' ';
+ int i = 0;
+
+ if (add_lib_hint) {
+ name[i++] = id->lib ? (ID_MISSING(id) ? 'M' : 'L') : ID_IS_OVERRIDE_LIBRARY(id) ? 'O' : ' ';
+ }
+ name[i++] = (id->flag & LIB_FAKEUSER) ? 'F' : ((id->us == 0) ? '0' : ' ');
+ name[i++] = ' ';
- BKE_id_full_name_get(name + 3, id, separator_char);
+ BKE_id_full_name_get(name + i, id, separator_char);
}
/**
diff --git a/source/blender/blenkernel/intern/lib_id_delete.c b/source/blender/blenkernel/intern/lib_id_delete.c
index 6fce898fd1e..9d7671322ea 100644
--- a/source/blender/blenkernel/intern/lib_id_delete.c
+++ b/source/blender/blenkernel/intern/lib_id_delete.c
@@ -24,6 +24,7 @@
/* all types are needed here, in order to do memory operations */
#include "DNA_ID.h"
+#include "DNA_key_types.h"
#include "BLI_utildefines.h"
@@ -32,6 +33,7 @@
#include "BKE_anim_data.h"
#include "BKE_idprop.h"
#include "BKE_idtype.h"
+#include "BKE_key.h"
#include "BKE_lib_id.h"
#include "BKE_lib_override.h"
#include "BKE_lib_remap.h"
@@ -148,6 +150,13 @@ void BKE_id_free_ex(Main *bmain, void *idv, int flag, const bool use_flag_from_i
BKE_libblock_relink_ex(bmain, id, NULL, NULL, 0);
}
+ if ((flag & LIB_ID_FREE_NO_MAIN) == 0) {
+ Key *key = BKE_key_from_id(id);
+ if (key != NULL) {
+ BKE_id_free_ex(bmain, &key->id, flag, use_flag_from_idtag);
+ }
+ }
+
BKE_libblock_free_datablock(id, flag);
/* avoid notifying on removed data */
diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c
index d6f037f64a4..457d096f983 100644
--- a/source/blender/blenkernel/intern/lib_override.c
+++ b/source/blender/blenkernel/intern/lib_override.c
@@ -27,11 +27,15 @@
#include "MEM_guardedalloc.h"
#include "DNA_ID.h"
+#include "DNA_key_types.h"
#include "DNA_object_types.h"
#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_build.h"
#include "BKE_armature.h"
+#include "BKE_idtype.h"
+#include "BKE_key.h"
#include "BKE_lib_id.h"
#include "BKE_lib_override.h"
#include "BKE_lib_remap.h"
@@ -40,6 +44,7 @@
#include "BLI_ghash.h"
#include "BLI_listbase.h"
#include "BLI_string.h"
+#include "BLI_task.h"
#include "BLI_utildefines.h"
#include "RNA_access.h"
@@ -113,7 +118,7 @@ IDOverrideLibrary *BKE_lib_override_library_init(ID *local_id, ID *reference_id)
/** Shalow or deep copy of a whole override from \a src_id to \a dst_id. */
void BKE_lib_override_library_copy(ID *dst_id, const ID *src_id, const bool do_full_copy)
{
- BLI_assert(src_id->override_library != NULL);
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY(src_id));
if (dst_id->override_library != NULL) {
if (src_id->override_library == NULL) {
@@ -125,6 +130,7 @@ void BKE_lib_override_library_copy(ID *dst_id, const ID *src_id, const bool do_f
}
}
else if (src_id->override_library == NULL) {
+ /* Virtual overrides of embedded data does not require any extra work. */
return;
}
else {
@@ -198,6 +204,16 @@ static ID *lib_override_library_create_from(Main *bmain, ID *reference_id)
BKE_lib_override_library_init(local_id, reference_id);
+ /* Note: From liboverride perspective (and RNA one), shape keys are considered as local embedded
+ * data-blocks, just like root node trees or master collections. Therefore, we never need to
+ * create overrides for them. We need a way to mark them as overrides though. */
+ Key *reference_key;
+ if ((reference_key = BKE_key_from_id(reference_id)) != NULL) {
+ Key *local_key = BKE_key_from_id(local_id);
+ BLI_assert(local_key != NULL);
+ local_key->id.flag |= LIB_EMBEDDED_DATA_LIB_OVERRIDE;
+ }
+
return local_id;
}
@@ -212,6 +228,12 @@ ID *BKE_lib_override_library_create_from_id(Main *bmain,
ID *local_id = lib_override_library_create_from(bmain, reference_id);
if (do_tagged_remap) {
+ Key *reference_key, *local_key = NULL;
+ if ((reference_key = BKE_key_from_id(reference_id)) != NULL) {
+ local_key = BKE_key_from_id(local_id);
+ BLI_assert(local_key != NULL);
+ }
+
ID *other_id;
FOREACH_MAIN_ID_BEGIN (bmain, other_id) {
if ((other_id->tag & LIB_TAG_DOIT) != 0 && other_id->lib == NULL) {
@@ -222,6 +244,13 @@ ID *BKE_lib_override_library_create_from_id(Main *bmain,
reference_id,
local_id,
ID_REMAP_SKIP_INDIRECT_USAGE | ID_REMAP_SKIP_OVERRIDE_LIBRARY);
+ if (reference_key != NULL) {
+ BKE_libblock_relink_ex(bmain,
+ other_id,
+ &reference_key->id,
+ &local_key->id,
+ ID_REMAP_SKIP_INDIRECT_USAGE | ID_REMAP_SKIP_OVERRIDE_LIBRARY);
+ }
}
}
FOREACH_MAIN_ID_END;
@@ -246,14 +275,15 @@ ID *BKE_lib_override_library_create_from_id(Main *bmain,
bool BKE_lib_override_library_create_from_tag(Main *bmain)
{
ID *reference_id;
- bool ret = true;
+ bool success = true;
ListBase todo_ids = {NULL};
LinkData *todo_id_iter;
/* Get all IDs we want to override. */
FOREACH_MAIN_ID_BEGIN (bmain, reference_id) {
- if ((reference_id->tag & LIB_TAG_DOIT) != 0 && reference_id->lib != NULL) {
+ if ((reference_id->tag & LIB_TAG_DOIT) != 0 && reference_id->lib != NULL &&
+ BKE_idtype_idcode_is_linkable(GS(reference_id->name))) {
todo_id_iter = MEM_callocN(sizeof(*todo_id_iter), __func__);
todo_id_iter->data = reference_id;
BLI_addtail(&todo_ids, todo_id_iter);
@@ -265,44 +295,77 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain)
for (todo_id_iter = todo_ids.first; todo_id_iter != NULL; todo_id_iter = todo_id_iter->next) {
reference_id = todo_id_iter->data;
if ((reference_id->newid = lib_override_library_create_from(bmain, reference_id)) == NULL) {
- ret = false;
+ success = false;
+ break;
}
- else {
+ /* We also tag the new IDs so that in next step we can remap their pointers too. */
+ reference_id->newid->tag |= LIB_TAG_DOIT;
+
+ Key *reference_key;
+ if ((reference_key = BKE_key_from_id(reference_id)) != NULL) {
+ reference_key->id.tag |= LIB_TAG_DOIT;
+
+ Key *local_key = BKE_key_from_id(reference_id->newid);
+ BLI_assert(local_key != NULL);
+ reference_key->id.newid = &local_key->id;
/* We also tag the new IDs so that in next step we can remap their pointers too. */
- reference_id->newid->tag |= LIB_TAG_DOIT;
+ local_key->id.tag |= LIB_TAG_DOIT;
}
}
/* Only remap new local ID's pointers, we don't want to force our new overrides onto our whole
* existing linked IDs usages. */
- for (todo_id_iter = todo_ids.first; todo_id_iter != NULL; todo_id_iter = todo_id_iter->next) {
- ID *other_id;
- reference_id = todo_id_iter->data;
+ if (success) {
+ for (todo_id_iter = todo_ids.first; todo_id_iter != NULL; todo_id_iter = todo_id_iter->next) {
+ ID *other_id;
+ reference_id = todo_id_iter->data;
+ ID *local_id = reference_id->newid;
+
+ if (local_id == NULL) {
+ continue;
+ }
- if (reference_id->newid == NULL) {
- continue;
- }
+ Key *reference_key, *local_key = NULL;
+ if ((reference_key = BKE_key_from_id(reference_id)) != NULL) {
+ local_key = BKE_key_from_id(reference_id->newid);
+ BLI_assert(local_key != NULL);
+ }
- /* Still checking the whole Main, that way we can tag other local IDs as needing to be remapped
- * to use newly created overriding IDs, if needed. */
- FOREACH_MAIN_ID_BEGIN (bmain, other_id) {
- if ((other_id->tag & LIB_TAG_DOIT) != 0 && other_id->lib == NULL) {
- ID *local_id = reference_id->newid;
- /* Note that using ID_REMAP_SKIP_INDIRECT_USAGE below is superfluous, as we only remap
- * local IDs usages anyway... */
- BKE_libblock_relink_ex(bmain,
- other_id,
- reference_id,
- local_id,
- ID_REMAP_SKIP_INDIRECT_USAGE | ID_REMAP_SKIP_OVERRIDE_LIBRARY);
+ /* Still checking the whole Main, that way we can tag other local IDs as needing to be
+ * remapped to use newly created overriding IDs, if needed. */
+ FOREACH_MAIN_ID_BEGIN (bmain, other_id) {
+ if ((other_id->tag & LIB_TAG_DOIT) != 0 && other_id->lib == NULL) {
+ /* Note that using ID_REMAP_SKIP_INDIRECT_USAGE below is superfluous, as we only remap
+ * local IDs usages anyway... */
+ BKE_libblock_relink_ex(bmain,
+ other_id,
+ reference_id,
+ local_id,
+ ID_REMAP_SKIP_INDIRECT_USAGE | ID_REMAP_SKIP_OVERRIDE_LIBRARY);
+ if (reference_key != NULL) {
+ BKE_libblock_relink_ex(bmain,
+ other_id,
+ &reference_key->id,
+ &local_key->id,
+ ID_REMAP_SKIP_INDIRECT_USAGE | ID_REMAP_SKIP_OVERRIDE_LIBRARY);
+ }
+ }
}
+ FOREACH_MAIN_ID_END;
+ }
+ }
+ else {
+ /* We need to cleanup potentially already created data. */
+ for (todo_id_iter = todo_ids.first; todo_id_iter != NULL; todo_id_iter = todo_id_iter->next) {
+ reference_id = todo_id_iter->data;
+ BKE_id_delete(bmain, reference_id->newid);
+ reference_id->newid = NULL;
}
- FOREACH_MAIN_ID_END;
}
BLI_freelistN(&todo_ids);
- return ret;
+ return success;
}
/* We only build override GHash on request. */
@@ -310,7 +373,8 @@ BLI_INLINE IDOverrideLibraryRuntime *override_library_rna_path_mapping_ensure(
IDOverrideLibrary *override)
{
if (override->runtime == NULL) {
- override->runtime = BLI_ghash_new(BLI_ghashutil_strhash_p, BLI_ghashutil_strcmp, __func__);
+ override->runtime = BLI_ghash_new(
+ BLI_ghashutil_strhash_p_murmur, BLI_ghashutil_strcmp, __func__);
for (IDOverrideLibraryProperty *op = override->properties.first; op != NULL; op = op->next) {
BLI_ghash_insert(override->runtime, op->rna_path, op);
}
@@ -469,7 +533,8 @@ IDOverrideLibraryPropertyOperation *BKE_lib_override_library_property_operation_
return ELEM(subitem_locindex, -1, opop->subitem_local_index) ? opop : NULL;
}
- /* index == -1 means all indices, that is valid fallback in case we requested specific index. */
+ /* index == -1 means all indices, that is valid fallback in case we requested specific index.
+ */
if (!strict && (subitem_locindex != subitem_defindex) &&
(opop = BLI_listbase_bytes_find(
&override_property->operations,
@@ -617,7 +682,7 @@ bool BKE_lib_override_library_property_operation_operands_validate(
* \return true if status is OK, false otherwise. */
bool BKE_lib_override_library_status_check_local(Main *bmain, ID *local)
{
- BLI_assert(local->override_library != NULL);
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(local));
ID *reference = local->override_library->reference;
@@ -649,6 +714,7 @@ bool BKE_lib_override_library_status_check_local(Main *bmain, ID *local)
&rnaptr_local,
&rnaptr_reference,
NULL,
+ 0,
local->override_library,
RNA_OVERRIDE_COMPARE_IGNORE_NON_OVERRIDABLE |
RNA_OVERRIDE_COMPARE_IGNORE_OVERRIDDEN,
@@ -672,7 +738,7 @@ bool BKE_lib_override_library_status_check_local(Main *bmain, ID *local)
* \return true if status is OK, false otherwise. */
bool BKE_lib_override_library_status_check_reference(Main *bmain, ID *local)
{
- BLI_assert(local->override_library != NULL);
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(local));
ID *reference = local->override_library->reference;
@@ -712,6 +778,7 @@ bool BKE_lib_override_library_status_check_reference(Main *bmain, ID *local)
&rnaptr_local,
&rnaptr_reference,
NULL,
+ 0,
local->override_library,
RNA_OVERRIDE_COMPARE_IGNORE_OVERRIDDEN,
NULL)) {
@@ -735,9 +802,7 @@ bool BKE_lib_override_library_status_check_reference(Main *bmain, ID *local)
* Generating diff values and applying overrides are much cheaper.
*
* \return true if new overriding op was created, or some local data was reset. */
-bool BKE_lib_override_library_operations_create(Main *bmain,
- ID *local,
- const bool UNUSED(force_auto))
+bool BKE_lib_override_library_operations_create(Main *bmain, ID *local)
{
BLI_assert(local->override_library != NULL);
const bool is_template = (local->override_library->reference == NULL);
@@ -752,9 +817,9 @@ bool BKE_lib_override_library_operations_create(Main *bmain,
}
if (GS(local->name) == ID_OB) {
- /* Our beloved pose's bone cross-data pointers... Usually, depsgraph evaluation would ensure
- * this is valid, but in some situations (like hidden collections etc.) this won't be the
- * case, so we need to take care of this ourselves. */
+ /* Our beloved pose's bone cross-data pointers... Usually, depsgraph evaluation would
+ * ensure this is valid, but in some situations (like hidden collections etc.) this won't
+ * be the case, so we need to take care of this ourselves. */
Object *ob_local = (Object *)local;
if (ob_local->data != NULL && ob_local->type == OB_ARMATURE && ob_local->pose != NULL &&
ob_local->pose->flag & POSE_RECALC) {
@@ -771,6 +836,7 @@ bool BKE_lib_override_library_operations_create(Main *bmain,
&rnaptr_local,
&rnaptr_reference,
NULL,
+ 0,
local->override_library,
RNA_OVERRIDE_COMPARE_CREATE | RNA_OVERRIDE_COMPARE_RESTORE,
&report_flags);
@@ -792,29 +858,51 @@ bool BKE_lib_override_library_operations_create(Main *bmain,
return ret;
}
+static void lib_override_library_operations_create_cb(TaskPool *__restrict pool, void *taskdata)
+{
+ Main *bmain = BLI_task_pool_user_data(pool);
+ ID *id = taskdata;
+
+ BKE_lib_override_library_operations_create(bmain, id);
+}
+
/** Check all overrides from given \a bmain and create/update overriding operations as needed. */
void BKE_lib_override_library_main_operations_create(Main *bmain, const bool force_auto)
{
ID *id;
+#ifdef DEBUG_OVERRIDE_TIMEIT
+ TIMEIT_START_AVERAGED(BKE_lib_override_library_main_operations_create);
+#endif
+
/* When force-auto is set, we also remove all unused existing override properties & operations.
*/
if (force_auto) {
BKE_lib_override_library_main_tag(bmain, IDOVERRIDE_LIBRARY_TAG_UNUSED, true);
}
+ TaskPool *task_pool = BLI_task_pool_create(bmain, TASK_PRIORITY_HIGH);
+
FOREACH_MAIN_ID_BEGIN (bmain, id) {
- if ((ID_IS_OVERRIDE_LIBRARY(id) && force_auto) ||
+ if ((ID_IS_OVERRIDE_LIBRARY_REAL(id) && force_auto) ||
(id->tag & LIB_TAG_OVERRIDE_LIBRARY_AUTOREFRESH)) {
- BKE_lib_override_library_operations_create(bmain, id, force_auto);
+ BLI_task_pool_push(task_pool, lib_override_library_operations_create_cb, id, false, NULL);
id->tag &= ~LIB_TAG_OVERRIDE_LIBRARY_AUTOREFRESH;
}
}
FOREACH_MAIN_ID_END;
+ BLI_task_pool_work_and_wait(task_pool);
+
+ BLI_task_pool_free(task_pool);
+
if (force_auto) {
BKE_lib_override_library_main_unused_cleanup(bmain);
}
+
+#ifdef DEBUG_OVERRIDE_TIMEIT
+ TIMEIT_END_AVERAGED(BKE_lib_override_library_main_operations_create);
+#endif
}
/** Set or clear given tag in all operations as unused in that override property data. */
@@ -869,7 +957,7 @@ void BKE_lib_override_library_main_tag(struct Main *bmain, const short tag, cons
/** Remove all tagged-as-unused properties and operations from that ID override data. */
void BKE_lib_override_library_id_unused_cleanup(struct ID *local)
{
- if (local->override_library != NULL) {
+ if (ID_IS_OVERRIDE_LIBRARY_REAL(local)) {
LISTBASE_FOREACH_MUTABLE (
IDOverrideLibraryProperty *, op, &local->override_library->properties) {
if (op->tag & IDOVERRIDE_LIBRARY_TAG_UNUSED) {
@@ -902,7 +990,7 @@ void BKE_lib_override_library_main_unused_cleanup(struct Main *bmain)
/** Update given override from its reference (re-applying overridden properties). */
void BKE_lib_override_library_update(Main *bmain, ID *local)
{
- if (local->override_library == NULL || local->override_library->reference == NULL) {
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(local)) {
return;
}
@@ -924,8 +1012,8 @@ void BKE_lib_override_library_update(Main *bmain, ID *local)
* So we work on temp copy of reference, and 'swap' its content with local. */
/* XXX We need a way to get off-Main copies of IDs (similar to localized mats/texts/ etc.)!
- * However, this is whole bunch of code work in itself, so for now plain stupid ID copy will
- * do, as inn-efficient as it is. :/
+ * However, this is whole bunch of code work in itself, so for now plain stupid ID copy
+ * will do, as inn-efficient as it is. :/
* Actually, maybe not! Since we are swapping with original ID's local content, we want to
* keep user-count in correct state when freeing tmp_id
* (and that user-counts of IDs used by 'new' local data also remain correct). */
@@ -942,11 +1030,21 @@ void BKE_lib_override_library_update(Main *bmain, ID *local)
}
/* This ID name is problematic, since it is an 'rna name property' it should not be editable or
- * different from reference linked ID. But local ID names need to be unique in a given type list
- * of Main, so we cannot always keep it identical, which is why we need this special manual
- * handling here. */
+ * different from reference linked ID. But local ID names need to be unique in a given type
+ * list of Main, so we cannot always keep it identical, which is why we need this special
+ * manual handling here. */
BLI_strncpy(tmp_id->name, local->name, sizeof(tmp_id->name));
+ /* Those ugly loopback pointers again... Luckily we only need to deal with the shape keys here,
+ * collections' parents are fully runtime and reconstructed later. */
+ Key *local_key = BKE_key_from_id(local);
+ Key *tmp_key = BKE_key_from_id(tmp_id);
+ if (local_key != NULL && tmp_key != NULL) {
+ /* This is some kind of hard-coded 'always enforced override'... */
+ tmp_key->from = local_key->from;
+ tmp_key->id.flag |= (local_key->id.flag & LIB_EMBEDDED_DATA_LIB_OVERRIDE);
+ }
+
PointerRNA rnaptr_src, rnaptr_dst, rnaptr_storage_stack, *rnaptr_storage = NULL;
RNA_id_pointer_create(local, &rnaptr_src);
RNA_id_pointer_create(tmp_id, &rnaptr_dst);
@@ -978,9 +1076,9 @@ void BKE_lib_override_library_update(Main *bmain, ID *local)
local->tag |= LIB_TAG_OVERRIDE_LIBRARY_REFOK;
/* Full rebuild of Depsgraph! */
-
- /* XXX Is this actual valid replacement for old DAG_relations_tag_update(bmain) ? */
- DEG_on_visible_update(bmain, true);
+ /* Note: this is really brute force, in theory updates from RNA should have handle this already,
+ * but for now let's play it safe. */
+ DEG_relations_tag_update(bmain);
}
/** Update all overrides from given \a bmain. */
@@ -1028,17 +1126,16 @@ ID *BKE_lib_override_library_operations_store_start(Main *bmain,
OverrideLibraryStorage *override_storage,
ID *local)
{
- BLI_assert(local->override_library != NULL);
- BLI_assert(override_storage != NULL);
- const bool is_template = (local->override_library->reference == NULL);
-
- if (is_template) {
+ if (ID_IS_OVERRIDE_LIBRARY_TEMPLATE(local) || ID_IS_OVERRIDE_LIBRARY_VIRTUAL(local)) {
/* This is actually purely local data with an override template, nothing to do here! */
return NULL;
}
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(local));
+ BLI_assert(override_storage != NULL);
+
/* Forcefully ensure we know about all needed override operations. */
- BKE_lib_override_library_operations_create(bmain, local, false);
+ BKE_lib_override_library_operations_create(bmain, local);
ID *storage_id;
#ifdef DEBUG_OVERRIDE_TIMEIT
@@ -1046,8 +1143,8 @@ ID *BKE_lib_override_library_operations_store_start(Main *bmain,
#endif
/* XXX TODO We may also want a specialized handling of things here too, to avoid copying heavy
- * never-overridable data (like Mesh geometry etc.)? And also maybe avoid lib reference-counting
- * completely (shallow copy...). */
+ * never-overridable data (like Mesh geometry etc.)? And also maybe avoid lib
+ * reference-counting completely (shallow copy...). */
/* This would imply change in handling of user-count all over RNA
* (and possibly all over Blender code).
* Not impossible to do, but would rather see first is extra useless usual user handling is
@@ -1080,7 +1177,7 @@ ID *BKE_lib_override_library_operations_store_start(Main *bmain,
void BKE_lib_override_library_operations_store_end(
OverrideLibraryStorage *UNUSED(override_storage), ID *local)
{
- BLI_assert(local->override_library != NULL);
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(local));
/* Nothing else to do here really, we need to keep all temp override storage data-blocks in
* memory until whole file is written anyway (otherwise we'd get mem pointers overlap...). */
@@ -1089,8 +1186,8 @@ void BKE_lib_override_library_operations_store_end(
void BKE_lib_override_library_operations_store_finalize(OverrideLibraryStorage *override_storage)
{
- /* We cannot just call BKE_main_free(override_storage), not until we have option to make 'ghost'
- * copies of IDs without increasing usercount of used data-blocks. */
+ /* We cannot just call BKE_main_free(override_storage), not until we have option to make
+ * 'ghost' copies of IDs without increasing usercount of used data-blocks. */
ID *id;
FOREACH_MAIN_ID_BEGIN (override_storage, id) {
diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c
index 3026dd617b5..777a4fb9dfa 100644
--- a/source/blender/blenkernel/intern/lib_remap.c
+++ b/source/blender/blenkernel/intern/lib_remap.c
@@ -667,9 +667,10 @@ static int id_relink_to_newid_looper(LibraryIDLinkCallbackData *cb_data)
/* See: NEW_ID macro */
if (id->newid) {
BKE_library_update_ID_link_user(id->newid, id, cb_flag);
- *id_pointer = id->newid;
+ id = id->newid;
+ *id_pointer = id;
}
- else if (id->tag & LIB_TAG_NEW) {
+ if (id->tag & LIB_TAG_NEW) {
id->tag &= ~LIB_TAG_NEW;
BKE_libblock_relink_to_newid(id);
}
diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c
index adb672b579e..4624818586a 100644
--- a/source/blender/blenkernel/intern/library.c
+++ b/source/blender/blenkernel/intern/library.c
@@ -86,23 +86,23 @@ void BKE_library_filepath_set(Main *bmain, Library *lib, const char *filepath)
/* in some cases this is used to update the absolute path from the
* relative */
- if (lib->name != filepath) {
- BLI_strncpy(lib->name, filepath, sizeof(lib->name));
+ if (lib->filepath != filepath) {
+ BLI_strncpy(lib->filepath, filepath, sizeof(lib->filepath));
}
- BLI_strncpy(lib->filepath, filepath, sizeof(lib->filepath));
+ BLI_strncpy(lib->filepath_abs, filepath, sizeof(lib->filepath_abs));
- /* not essential but set filepath is an absolute copy of value which
- * is more useful if its kept in sync */
- if (BLI_path_is_rel(lib->filepath)) {
+ /* Not essential but set `filepath_abs` is an absolute copy of value which
+ * is more useful if its kept in sync. */
+ if (BLI_path_is_rel(lib->filepath_abs)) {
/* note that the file may be unsaved, in this case, setting the
- * filepath on an indirectly linked path is not allowed from the
+ * `filepath_abs` on an indirectly linked path is not allowed from the
* outliner, and its not really supported but allow from here for now
* since making local could cause this to be directly linked - campbell
*/
/* Never make paths relative to parent lib - reading code (blenloader) always set *all*
- * lib->name relative to current main, not to their parent for indirectly linked ones. */
+ * `lib->filepath` relative to current main, not to their parent for indirectly linked ones. */
const char *basepath = BKE_main_blendfile_path(bmain);
- BLI_path_abs(lib->filepath, basepath);
+ BLI_path_abs(lib->filepath_abs, basepath);
}
}
diff --git a/source/blender/blenkernel/intern/main.c b/source/blender/blenkernel/intern/main.c
index ea3bee8b2f6..4b577ccec2c 100644
--- a/source/blender/blenkernel/intern/main.c
+++ b/source/blender/blenkernel/intern/main.c
@@ -288,6 +288,29 @@ void BKE_main_relations_free(Main *bmain)
}
/**
+ * Remove an ID from the relations (the two entries for that ID, not the ID from entries in other
+ * IDs' relationships).
+ *
+ * Does not free any allocated memory.
+ * Allows to use those relations as a way to mark an ID as already processed, without requiring any
+ * additional tagging or GSet.
+ * Obviously, relations should be freed after use then, since this will make them fully invalid.
+ */
+void BKE_main_relations_ID_remove(Main *bmain, ID *id)
+{
+ if (bmain->relations) {
+ /* Note: we do not free the entries from the mempool, those will be dealt with when finally
+ * freeing the whole relations. */
+ if (bmain->relations->id_used_to_user) {
+ BLI_ghash_remove(bmain->relations->id_used_to_user, id, NULL, NULL);
+ }
+ if (bmain->relations->id_user_to_used) {
+ BLI_ghash_remove(bmain->relations->id_user_to_used, id, NULL, NULL);
+ }
+ }
+}
+
+/**
* Create a GSet storing all IDs present in given \a bmain, by their pointers.
*
* \param gset: If not NULL, given GSet will be extended with IDs from given \a bmain,
diff --git a/source/blender/blenkernel/intern/mesh_evaluate.c b/source/blender/blenkernel/intern/mesh_evaluate.c
index 433db26ded8..b298a6a2787 100644
--- a/source/blender/blenkernel/intern/mesh_evaluate.c
+++ b/source/blender/blenkernel/intern/mesh_evaluate.c
@@ -2112,7 +2112,7 @@ void BKE_mesh_set_custom_normals(Mesh *mesh, float (*r_custom_loopnors)[3])
* Higher level functions hiding most of the code needed around call to
* #BKE_mesh_normals_loop_custom_from_vertices_set().
*
- * \param r_custom_loopnors: is not const, since code will replace zero_v3 normals there
+ * \param r_custom_vertnors: is not const, since code will replace zero_v3 normals there
* with automatically computed vectors.
*/
void BKE_mesh_set_custom_normals_from_vertices(Mesh *mesh, float (*r_custom_vertnors)[3])
@@ -2386,10 +2386,10 @@ float BKE_mesh_calc_poly_uv_area(const MPoly *mpoly, const MLoopUV *uv_array)
* - The resulting volume will only be correct if the mesh is manifold and has consistent
* face winding (non-contiguous face normals or holes in the mesh surface).
*/
-static float mesh_calc_poly_volume_centroid(const MPoly *mpoly,
- const MLoop *loopstart,
- const MVert *mvarray,
- float r_cent[3])
+static float UNUSED_FUNCTION(mesh_calc_poly_volume_centroid)(const MPoly *mpoly,
+ const MLoop *loopstart,
+ const MVert *mvarray,
+ float r_cent[3])
{
const float *v_pivot, *v_step1;
float total_volume = 0.0f;
@@ -2424,6 +2424,36 @@ static float mesh_calc_poly_volume_centroid(const MPoly *mpoly,
}
/**
+ * A version of mesh_calc_poly_volume_centroid that takes an initial reference center,
+ * use this to increase numeric stability as the quality of the result becomes
+ * very low quality as the value moves away from 0.0, see: T65986.
+ */
+static float mesh_calc_poly_volume_centroid_with_reference_center(const MPoly *mpoly,
+ const MLoop *loopstart,
+ const MVert *mvarray,
+ const float reference_center[3],
+ float r_cent[3])
+{
+ /* See: mesh_calc_poly_volume_centroid for comments. */
+ float v_pivot[3], v_step1[3];
+ float total_volume = 0.0f;
+ zero_v3(r_cent);
+ sub_v3_v3v3(v_pivot, mvarray[loopstart[0].v].co, reference_center);
+ sub_v3_v3v3(v_step1, mvarray[loopstart[1].v].co, reference_center);
+ for (int i = 2; i < mpoly->totloop; i++) {
+ float v_step2[3];
+ sub_v3_v3v3(v_step2, mvarray[loopstart[i].v].co, reference_center);
+ const float tetra_volume = volume_tri_tetrahedron_signed_v3_6x(v_pivot, v_step1, v_step2);
+ total_volume += tetra_volume;
+ for (uint j = 0; j < 3; j++) {
+ r_cent[j] += tetra_volume * (v_pivot[j] + v_step1[j] + v_step2[j]);
+ }
+ copy_v3_v3(v_step1, v_step2);
+ }
+ return total_volume;
+}
+
+/**
* \note
* - Results won't be correct if polygon is non-planar.
* - This has the advantage over #mesh_calc_poly_volume_centroid
@@ -2536,10 +2566,35 @@ bool BKE_mesh_center_median(const Mesh *me, float r_cent[3])
if (me->totvert) {
mul_v3_fl(r_cent, 1.0f / (float)me->totvert);
}
-
return (me->totvert != 0);
}
+/**
+ * Calculate the center from polygons,
+ * use when we want to ignore vertex locations that don't have connected faces.
+ */
+bool BKE_mesh_center_median_from_polys(const Mesh *me, float r_cent[3])
+{
+ int i = me->totpoly;
+ int tot = 0;
+ const MPoly *mpoly = me->mpoly;
+ const MLoop *mloop = me->mloop;
+ const MVert *mvert = me->mvert;
+ zero_v3(r_cent);
+ for (mpoly = me->mpoly; i--; mpoly++) {
+ int loopend = mpoly->loopstart + mpoly->totloop;
+ for (int j = mpoly->loopstart; j < loopend; j++) {
+ add_v3_v3(r_cent, mvert[mloop[j].v].co);
+ }
+ tot += mpoly->totloop;
+ }
+ /* otherwise we get NAN for 0 verts */
+ if (me->totpoly) {
+ mul_v3_fl(r_cent, 1.0f / (float)tot);
+ }
+ return (me->totpoly != 0);
+}
+
bool BKE_mesh_center_bounds(const Mesh *me, float r_cent[3])
{
float min[3], max[3];
@@ -2595,12 +2650,16 @@ bool BKE_mesh_center_of_volume(const Mesh *me, float r_cent[3])
float total_volume = 0.0f;
float poly_cent[3];
+ /* Use an initial center to avoid numeric instability of geometry far away from the center. */
+ float init_cent[3];
+ const bool init_cent_result = BKE_mesh_center_median_from_polys(me, init_cent);
+
zero_v3(r_cent);
/* calculate a weighted average of polyhedron centroids */
for (mpoly = me->mpoly; i--; mpoly++) {
- poly_volume = mesh_calc_poly_volume_centroid(
- mpoly, me->mloop + mpoly->loopstart, me->mvert, poly_cent);
+ poly_volume = mesh_calc_poly_volume_centroid_with_reference_center(
+ mpoly, me->mloop + mpoly->loopstart, me->mvert, init_cent, poly_cent);
/* poly_cent is already volume-weighted, so no need to multiply by the volume */
add_v3_v3(r_cent, poly_cent);
@@ -2616,9 +2675,10 @@ bool BKE_mesh_center_of_volume(const Mesh *me, float r_cent[3])
/* this can happen for non-manifold objects, fallback to median */
if (UNLIKELY(!is_finite_v3(r_cent))) {
- return BKE_mesh_center_median(me, r_cent);
+ copy_v3_v3(r_cent, init_cent);
+ return init_cent_result;
}
-
+ add_v3_v3(r_cent, init_cent);
return (me->totpoly != 0);
}
diff --git a/source/blender/blenkernel/intern/mesh_remesh_voxel.c b/source/blender/blenkernel/intern/mesh_remesh_voxel.c
index 3daf9f2752e..010b306ec76 100644
--- a/source/blender/blenkernel/intern/mesh_remesh_voxel.c
+++ b/source/blender/blenkernel/intern/mesh_remesh_voxel.c
@@ -405,6 +405,37 @@ void BKE_remesh_reproject_sculpt_face_sets(Mesh *target, Mesh *source)
free_bvhtree_from_mesh(&bvhtree);
}
+void BKE_remesh_reproject_vertex_paint(Mesh *target, Mesh *source)
+{
+ BVHTreeFromMesh bvhtree = {
+ .nearest_callback = NULL,
+ };
+ BKE_bvhtree_from_mesh_get(&bvhtree, source, BVHTREE_FROM_VERTS, 2);
+
+ int tot_color_layer = CustomData_number_of_layers(&source->vdata, CD_PROP_COLOR);
+
+ for (int layer_n = 0; layer_n < tot_color_layer; layer_n++) {
+ const char *layer_name = CustomData_get_layer_name(&source->vdata, CD_PROP_COLOR, layer_n);
+ CustomData_add_layer_named(
+ &target->vdata, CD_PROP_COLOR, CD_CALLOC, NULL, target->totvert, layer_name);
+
+ MPropCol *target_color = CustomData_get_layer_n(&target->vdata, CD_PROP_COLOR, layer_n);
+ MVert *target_verts = CustomData_get_layer(&target->vdata, CD_MVERT);
+ MPropCol *source_color = CustomData_get_layer_n(&source->vdata, CD_PROP_COLOR, layer_n);
+ for (int i = 0; i < target->totvert; i++) {
+ BVHTreeNearest nearest;
+ nearest.index = -1;
+ nearest.dist_sq = FLT_MAX;
+ BLI_bvhtree_find_nearest(
+ bvhtree.tree, target_verts[i].co, &nearest, bvhtree.nearest_callback, &bvhtree);
+ if (nearest.index != -1) {
+ copy_v4_v4(target_color[i].color, source_color[nearest.index].color);
+ }
+ }
+ }
+ free_bvhtree_from_mesh(&bvhtree);
+}
+
struct Mesh *BKE_mesh_remesh_voxel_fix_poles(struct Mesh *mesh)
{
const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh);
diff --git a/source/blender/blenkernel/intern/mesh_runtime.c b/source/blender/blenkernel/intern/mesh_runtime.c
index 8bce577897b..932423bc445 100644
--- a/source/blender/blenkernel/intern/mesh_runtime.c
+++ b/source/blender/blenkernel/intern/mesh_runtime.c
@@ -203,26 +203,31 @@ bool BKE_mesh_runtime_ensure_edit_data(struct Mesh *mesh)
return true;
}
+bool BKE_mesh_runtime_reset_edit_data(Mesh *mesh)
+{
+ EditMeshData *edit_data = mesh->runtime.edit_data;
+ if (edit_data == NULL) {
+ return false;
+ }
+
+ MEM_SAFE_FREE(edit_data->polyCos);
+ MEM_SAFE_FREE(edit_data->polyNos);
+ MEM_SAFE_FREE(edit_data->vertexCos);
+ MEM_SAFE_FREE(edit_data->vertexNos);
+
+ return true;
+}
+
bool BKE_mesh_runtime_clear_edit_data(Mesh *mesh)
{
if (mesh->runtime.edit_data == NULL) {
return false;
}
+ BKE_mesh_runtime_reset_edit_data(mesh);
- if (mesh->runtime.edit_data->polyCos != NULL) {
- MEM_freeN((void *)mesh->runtime.edit_data->polyCos);
- }
- if (mesh->runtime.edit_data->polyNos != NULL) {
- MEM_freeN((void *)mesh->runtime.edit_data->polyNos);
- }
- if (mesh->runtime.edit_data->vertexCos != NULL) {
- MEM_freeN((void *)mesh->runtime.edit_data->vertexCos);
- }
- if (mesh->runtime.edit_data->vertexNos != NULL) {
- MEM_freeN((void *)mesh->runtime.edit_data->vertexNos);
- }
+ MEM_freeN(mesh->runtime.edit_data);
+ mesh->runtime.edit_data = NULL;
- MEM_SAFE_FREE(mesh->runtime.edit_data);
return true;
}
diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c
index 7c77746ea1c..327e8bfca7a 100644
--- a/source/blender/blenkernel/intern/modifier.c
+++ b/source/blender/blenkernel/intern/modifier.c
@@ -141,7 +141,7 @@ ModifierData *BKE_modifier_new(int type)
md->type = type;
md->mode = eModifierMode_Realtime | eModifierMode_Render;
md->flag = eModifierFlag_OverrideLibrary_Local;
- md->ui_expand_flag = 1; /* Only open the main panel at the beginning, not the subpanels. */
+ md->ui_expand_flag = 1; /* Only open the main panel at the beginning, not the sub-panels. */
if (mti->flags & eModifierTypeFlag_EnableInEditmode) {
md->mode |= eModifierMode_Editmode;
diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c
index fe7c2055aef..f0efc9b8c50 100644
--- a/source/blender/blenkernel/intern/movieclip.c
+++ b/source/blender/blenkernel/intern/movieclip.c
@@ -220,19 +220,19 @@ static void get_sequence_fname(const MovieClip *clip, const int framenr, char *n
char head[FILE_MAX], tail[FILE_MAX];
int offset;
- BLI_strncpy(name, clip->name, sizeof(clip->name));
+ BLI_strncpy(name, clip->filepath, sizeof(clip->filepath));
BLI_path_sequence_decode(name, head, tail, &numlen);
/* Movie-clips always points to first image from sequence, auto-guess offset for now.
* Could be something smarter in the future. */
- offset = sequence_guess_offset(clip->name, strlen(head), numlen);
+ offset = sequence_guess_offset(clip->filepath, strlen(head), numlen);
if (numlen) {
BLI_path_sequence_encode(
name, head, tail, numlen, offset + framenr - clip->start_frame + clip->frame_offset);
}
else {
- BLI_strncpy(name, clip->name, sizeof(clip->name));
+ BLI_strncpy(name, clip->filepath, sizeof(clip->filepath));
}
BLI_path_abs(name, ID_BLEND_PATH_FROM_GLOBAL(&clip->id));
@@ -246,7 +246,7 @@ static void get_proxy_fname(
char dir[FILE_MAX], clipdir[FILE_MAX], clipfile[FILE_MAX];
int proxynr = framenr - clip->start_frame + 1 + clip->frame_offset;
- BLI_split_dirfile(clip->name, clipdir, clipfile, FILE_MAX, FILE_MAX);
+ BLI_split_dirfile(clip->filepath, clipdir, clipfile, FILE_MAX, FILE_MAX);
if (clip->flag & MCLIP_USE_PROXY_CUSTOM_DIR) {
BLI_strncpy(dir, clip->proxy.dir, sizeof(dir));
@@ -395,7 +395,7 @@ static void movieclip_open_anim_file(MovieClip *clip)
char str[FILE_MAX];
if (!clip->anim) {
- BLI_strncpy(str, clip->name, FILE_MAX);
+ BLI_strncpy(str, clip->filepath, FILE_MAX);
BLI_path_abs(str, ID_BLEND_PATH_FROM_GLOBAL(&clip->id));
/* FIXME: make several stream accessible in image editor, too */
@@ -445,7 +445,7 @@ static void movieclip_calc_length(MovieClip *clip)
unsigned short numlen;
char name[FILE_MAX], head[FILE_MAX], tail[FILE_MAX];
- BLI_path_sequence_decode(clip->name, head, tail, &numlen);
+ BLI_path_sequence_decode(clip->filepath, head, tail, &numlen);
if (numlen == 0) {
/* there's no number group in file name, assume it's single framed sequence */
@@ -531,10 +531,10 @@ static int user_frame_to_cache_frame(MovieClip *clip, int framenr)
unsigned short numlen;
char head[FILE_MAX], tail[FILE_MAX];
- BLI_path_sequence_decode(clip->name, head, tail, &numlen);
+ BLI_path_sequence_decode(clip->filepath, head, tail, &numlen);
/* see comment in get_sequence_fname */
- clip->cache->sequence_offset = sequence_guess_offset(clip->name, strlen(head), numlen);
+ clip->cache->sequence_offset = sequence_guess_offset(clip->filepath, strlen(head), numlen);
}
index += clip->cache->sequence_offset;
@@ -674,7 +674,7 @@ static bool put_imbuf_cache(
clip->cache->sequence_offset = -1;
if (clip->source == MCLIP_SRC_SEQUENCE) {
unsigned short numlen;
- BLI_path_sequence_decode(clip->name, NULL, NULL, &numlen);
+ BLI_path_sequence_decode(clip->filepath, NULL, NULL, &numlen);
clip->cache->is_still_sequence = (numlen == 0);
}
}
@@ -758,7 +758,7 @@ static void detect_clip_source(Main *bmain, MovieClip *clip)
ImBuf *ibuf;
char name[FILE_MAX];
- BLI_strncpy(name, clip->name, sizeof(name));
+ BLI_strncpy(name, clip->filepath, sizeof(name));
BLI_path_abs(name, BKE_main_blendfile_path(bmain));
ibuf = IMB_testiffname(name, IB_rect | IB_multilayer);
@@ -795,7 +795,7 @@ MovieClip *BKE_movieclip_file_add(Main *bmain, const char *name)
/* create a short library name */
clip = movieclip_alloc(bmain, BLI_path_basename(name));
- BLI_strncpy(clip->name, name, sizeof(clip->name));
+ BLI_strncpy(clip->filepath, name, sizeof(clip->filepath));
detect_clip_source(bmain, clip);
@@ -821,7 +821,7 @@ MovieClip *BKE_movieclip_file_add_exists_ex(Main *bmain, const char *filepath, b
/* first search an identical filepath */
for (clip = bmain->movieclips.first; clip; clip = clip->id.next) {
- BLI_strncpy(strtest, clip->name, sizeof(clip->name));
+ BLI_strncpy(strtest, clip->filepath, sizeof(clip->filepath));
BLI_path_abs(strtest, ID_BLEND_PATH(bmain, &clip->id));
if (BLI_path_cmp(strtest, str) == 0) {
@@ -1739,7 +1739,7 @@ void BKE_movieclip_filename_for_frame(MovieClip *clip, MovieClipUser *user, char
}
}
else {
- BLI_strncpy(name, clip->name, FILE_MAX);
+ BLI_strncpy(name, clip->filepath, FILE_MAX);
BLI_path_abs(name, ID_BLEND_PATH_FROM_GLOBAL(&clip->id));
}
}
diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c
index c0b908ff9d2..c21b0b1b2d6 100644
--- a/source/blender/blenkernel/intern/node.c
+++ b/source/blender/blenkernel/intern/node.c
@@ -748,7 +748,7 @@ GHashIterator *nodeSocketTypeGetIterator(void)
return BLI_ghashIterator_new(nodesockettypes_hash);
}
-struct bNodeSocket *nodeFindSocket(bNode *node, int in_out, const char *identifier)
+struct bNodeSocket *nodeFindSocket(const bNode *node, int in_out, const char *identifier)
{
bNodeSocket *sock = (in_out == SOCK_IN ? node->inputs.first : node->outputs.first);
for (; sock; sock = sock->next) {
@@ -3216,7 +3216,7 @@ void BKE_node_clipboard_add_node(bNode *node)
BLI_strncpy(node_info->id_name, node->id->name, sizeof(node_info->id_name));
if (ID_IS_LINKED_DATABLOCK(node->id)) { /* Don't want virtual libraries here... */
BLI_strncpy(
- node_info->library_name, node->id->lib->filepath, sizeof(node_info->library_name));
+ node_info->library_name, node->id->lib->filepath_abs, sizeof(node_info->library_name));
}
else {
node_info->library_name[0] = '\0';
diff --git a/source/blender/blenkernel/intern/node_tree_ref.cc b/source/blender/blenkernel/intern/node_tree_ref.cc
index 517c1b85aff..6bd3e2d2e5a 100644
--- a/source/blender/blenkernel/intern/node_tree_ref.cc
+++ b/source/blender/blenkernel/intern/node_tree_ref.cc
@@ -18,7 +18,8 @@
#include "BLI_dot_export.hh"
-namespace BKE {
+namespace blender {
+namespace bke {
NodeTreeRef::NodeTreeRef(bNodeTree *btree) : m_btree(btree)
{
@@ -78,7 +79,8 @@ NodeTreeRef::NodeTreeRef(bNodeTree *btree) : m_btree(btree)
}
for (NodeRef *node : m_nodes_by_id) {
- m_nodes_by_idname.lookup_or_add_default(node->idname()).append(node);
+ const bNodeType *nodetype = node->m_bnode->typeinfo;
+ m_nodes_by_type.lookup_or_add_default(nodetype).append(node);
}
}
@@ -138,15 +140,13 @@ void NodeTreeRef::find_targets_skipping_reroutes(OutputSocketRef &socket,
std::string NodeTreeRef::to_dot() const
{
- namespace Dot = blender::DotExport;
+ dot::DirectedGraph digraph;
+ digraph.set_rankdir(dot::Attr_rankdir::LeftToRight);
- Dot::DirectedGraph digraph;
- digraph.set_rankdir(Dot::Attr_rankdir::LeftToRight);
-
- Map<const NodeRef *, Dot::NodeWithSocketsRef> dot_nodes;
+ Map<const NodeRef *, dot::NodeWithSocketsRef> dot_nodes;
for (const NodeRef *node : m_nodes_by_id) {
- Dot::Node &dot_node = digraph.new_node("");
+ dot::Node &dot_node = digraph.new_node("");
dot_node.set_background_color("white");
Vector<std::string> input_names;
@@ -159,13 +159,13 @@ std::string NodeTreeRef::to_dot() const
}
dot_nodes.add_new(node,
- Dot::NodeWithSocketsRef(dot_node, node->name(), input_names, output_names));
+ dot::NodeWithSocketsRef(dot_node, node->name(), input_names, output_names));
}
for (const OutputSocketRef *from_socket : m_output_sockets) {
for (const InputSocketRef *to_socket : from_socket->directly_linked_sockets()) {
- Dot::NodeWithSocketsRef &from_dot_node = dot_nodes.lookup(&from_socket->node());
- Dot::NodeWithSocketsRef &to_dot_node = dot_nodes.lookup(&to_socket->node());
+ dot::NodeWithSocketsRef &from_dot_node = dot_nodes.lookup(&from_socket->node());
+ dot::NodeWithSocketsRef &to_dot_node = dot_nodes.lookup(&to_socket->node());
digraph.new_edge(from_dot_node.output(from_socket->index()),
to_dot_node.input(to_socket->index()));
@@ -175,4 +175,5 @@ std::string NodeTreeRef::to_dot() const
return digraph.to_dot_string();
}
-} // namespace BKE
+} // namespace bke
+} // namespace blender
diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c
index 127c5243c7e..2ddbdd02883 100644
--- a/source/blender/blenkernel/intern/object.c
+++ b/source/blender/blenkernel/intern/object.c
@@ -1758,300 +1758,171 @@ Object *BKE_object_copy(Main *bmain, const Object *ob)
* \note Caller MUST free \a newid pointers itself (#BKE_main_id_clear_newpoins()) and call updates
* of DEG too (#DAG_relations_tag_update()).
*/
-Object *BKE_object_duplicate(Main *bmain, const Object *ob, const int dupflag)
+Object *BKE_object_duplicate(Main *bmain,
+ Object *ob,
+ const eDupli_ID_Flags dupflag,
+ const eLibIDDuplicateFlags duplicate_options)
{
+ const bool is_subprocess = (duplicate_options & LIB_ID_DUPLICATE_IS_SUBPROCESS) != 0;
+
+ if (!is_subprocess) {
+ BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false);
+ BKE_main_id_clear_newpoins(bmain);
+ }
+
Material ***matarar;
- ID *id;
- int a, didit;
const bool is_object_liboverride = ID_IS_OVERRIDE_LIBRARY(ob);
- Object *obn = BKE_object_copy(bmain, ob);
+ Object *obn;
+ BKE_id_copy(bmain, &ob->id, (ID **)&obn);
+ id_us_min(&obn->id);
+ if (is_subprocess) {
+ ID_NEW_SET(ob, obn);
+ }
/* 0 == full linked. */
if (dupflag == 0) {
return obn;
}
-#define ID_NEW_REMAP_US(a) \
- if ((a)->id.newid) { \
- (a) = (void *)(a)->id.newid; \
- (a)->id.us++; \
- }
-#define ID_NEW_REMAP_US2(a) \
- if (((ID *)a)->newid) { \
- (a) = ((ID *)a)->newid; \
- ((ID *)a)->us++; \
- }
-
/* duplicates using userflags */
if (dupflag & USER_DUP_ACT) {
BKE_animdata_copy_id_action(bmain, &obn->id, true);
}
if (dupflag & USER_DUP_MAT) {
- for (a = 0; a < obn->totcol; a++) {
- id = (ID *)obn->mat[a];
- if (id) {
- if (is_object_liboverride && ID_IS_LINKED(id)) {
- continue;
- }
- ID_NEW_REMAP_US(obn->mat[a])
- else
- {
- obn->mat[a] = ID_NEW_SET(obn->mat[a], BKE_material_copy(bmain, obn->mat[a]));
- if (dupflag & USER_DUP_ACT) {
- BKE_animdata_copy_id_action(bmain, &obn->mat[a]->id, true);
- }
- }
- id_us_min(id);
- }
+ for (int i = 0; i < obn->totcol; i++) {
+ BKE_id_copy_for_duplicate(bmain, (ID *)obn->mat[i], is_object_liboverride, dupflag);
}
}
if (dupflag & USER_DUP_PSYS) {
ParticleSystem *psys;
for (psys = obn->particlesystem.first; psys; psys = psys->next) {
- id = (ID *)psys->part;
- if (id) {
- if (is_object_liboverride && ID_IS_LINKED(id)) {
- continue;
- }
- ID_NEW_REMAP_US(psys->part)
- else
- {
- psys->part = ID_NEW_SET(psys->part, BKE_particlesettings_copy(bmain, psys->part));
- if (dupflag & USER_DUP_ACT) {
- BKE_animdata_copy_id_action(bmain, &psys->part->id, true);
- }
- }
- id_us_min(id);
- }
+ BKE_id_copy_for_duplicate(bmain, (ID *)psys->part, is_object_liboverride, dupflag);
}
}
- id = obn->data;
- didit = 0;
+ ID *id = obn->data;
+ ID *id_new = NULL;
+ const bool need_to_duplicate_obdata = (id != NULL) && (id->newid == NULL);
- if (!is_object_liboverride || !ID_IS_LINKED(id)) {
- switch (obn->type) {
- case OB_MESH:
- if (dupflag & USER_DUP_MESH) {
- ID_NEW_REMAP_US2(obn->data)
- else
- {
- obn->data = ID_NEW_SET(obn->data, BKE_mesh_copy(bmain, obn->data));
- didit = 1;
- }
- id_us_min(id);
- }
- break;
- case OB_CURVE:
- if (dupflag & USER_DUP_CURVE) {
- ID_NEW_REMAP_US2(obn->data)
- else
- {
- obn->data = ID_NEW_SET(obn->data, BKE_curve_copy(bmain, obn->data));
- didit = 1;
- }
- id_us_min(id);
- }
- break;
- case OB_SURF:
- if (dupflag & USER_DUP_SURF) {
- ID_NEW_REMAP_US2(obn->data)
- else
- {
- obn->data = ID_NEW_SET(obn->data, BKE_curve_copy(bmain, obn->data));
- didit = 1;
- }
- id_us_min(id);
- }
- break;
- case OB_FONT:
- if (dupflag & USER_DUP_FONT) {
- ID_NEW_REMAP_US2(obn->data)
- else
- {
- obn->data = ID_NEW_SET(obn->data, BKE_curve_copy(bmain, obn->data));
- didit = 1;
- }
- id_us_min(id);
- }
- break;
- case OB_MBALL:
- if (dupflag & USER_DUP_MBALL) {
- ID_NEW_REMAP_US2(obn->data)
- else
- {
- obn->data = ID_NEW_SET(obn->data, BKE_mball_copy(bmain, obn->data));
- didit = 1;
- }
- id_us_min(id);
- }
- break;
- case OB_LAMP:
- if (dupflag & USER_DUP_LAMP) {
- ID_NEW_REMAP_US2(obn->data)
- else
- {
- obn->data = ID_NEW_SET(obn->data, BKE_light_copy(bmain, obn->data));
- didit = 1;
- }
- id_us_min(id);
- }
- break;
- case OB_ARMATURE:
- if (dupflag != 0) {
- DEG_id_tag_update(&obn->id, ID_RECALC_GEOMETRY);
- if (obn->pose) {
- BKE_pose_tag_recalc(bmain, obn->pose);
- }
- if (dupflag & USER_DUP_ARM) {
- ID_NEW_REMAP_US2(obn->data)
- else
- {
- obn->data = ID_NEW_SET(obn->data, BKE_armature_copy(bmain, obn->data));
- BKE_pose_rebuild(bmain, obn, obn->data, true);
- didit = 1;
- }
- id_us_min(id);
- }
- }
- break;
- case OB_LATTICE:
- if (dupflag != 0) {
- ID_NEW_REMAP_US2(obn->data)
- else
- {
- obn->data = ID_NEW_SET(obn->data, BKE_lattice_copy(bmain, obn->data));
- didit = 1;
- }
- id_us_min(id);
- }
- break;
- case OB_CAMERA:
- if (dupflag != 0) {
- ID_NEW_REMAP_US2(obn->data)
- else
- {
- obn->data = ID_NEW_SET(obn->data, BKE_camera_copy(bmain, obn->data));
- didit = 1;
- }
- id_us_min(id);
- }
- break;
- case OB_LIGHTPROBE:
- if (dupflag & USER_DUP_LIGHTPROBE) {
- ID_NEW_REMAP_US2(obn->data)
- else
- {
- obn->data = ID_NEW_SET(obn->data, BKE_lightprobe_copy(bmain, obn->data));
- didit = 1;
- }
- id_us_min(id);
- }
- break;
- case OB_SPEAKER:
- if (dupflag != 0) {
- ID_NEW_REMAP_US2(obn->data)
- else
- {
- obn->data = ID_NEW_SET(obn->data, BKE_speaker_copy(bmain, obn->data));
- didit = 1;
- }
- id_us_min(id);
- }
- break;
- case OB_GPENCIL:
- if (dupflag & USER_DUP_GPENCIL) {
- ID_NEW_REMAP_US2(obn->data)
- else
- {
- obn->data = ID_NEW_SET(obn->data, BKE_gpencil_copy(bmain, obn->data));
- didit = 1;
- }
- id_us_min(id);
- }
- break;
- case OB_HAIR:
- if (dupflag & USER_DUP_HAIR) {
- ID_NEW_REMAP_US2(obn->data)
- else
- {
- obn->data = ID_NEW_SET(obn->data, BKE_hair_copy(bmain, obn->data));
- didit = 1;
- }
- id_us_min(id);
- }
- break;
- case OB_POINTCLOUD:
- if (dupflag & USER_DUP_POINTCLOUD) {
- ID_NEW_REMAP_US2(obn->data)
- else
- {
- obn->data = ID_NEW_SET(obn->data, BKE_pointcloud_copy(bmain, obn->data));
- didit = 1;
- }
- id_us_min(id);
- }
- break;
- case OB_VOLUME:
- if (dupflag & USER_DUP_VOLUME) {
- ID_NEW_REMAP_US2(obn->data)
- else
- {
- obn->data = ID_NEW_SET(obn->data, BKE_volume_copy(bmain, obn->data));
- didit = 1;
- }
- id_us_min(id);
- }
- break;
- }
- }
-
- /* Check if obdata is copied. */
- if (didit) {
- Key *key = BKE_key_from_object(obn);
-
- Key *oldkey = BKE_key_from_object(ob);
- if (oldkey != NULL) {
- ID_NEW_SET(oldkey, key);
- }
-
- if (dupflag & USER_DUP_ACT) {
- BKE_animdata_copy_id_action(bmain, (ID *)obn->data, true);
- if (key) {
- BKE_animdata_copy_id_action(bmain, (ID *)key, true);
+ switch (obn->type) {
+ case OB_MESH:
+ if (dupflag & USER_DUP_MESH) {
+ id_new = BKE_id_copy_for_duplicate(bmain, id, is_object_liboverride, dupflag);
}
- }
+ break;
+ case OB_CURVE:
+ if (dupflag & USER_DUP_CURVE) {
+ id_new = BKE_id_copy_for_duplicate(bmain, id, is_object_liboverride, dupflag);
+ }
+ break;
+ case OB_SURF:
+ if (dupflag & USER_DUP_SURF) {
+ id_new = BKE_id_copy_for_duplicate(bmain, id, is_object_liboverride, dupflag);
+ }
+ break;
+ case OB_FONT:
+ if (dupflag & USER_DUP_FONT) {
+ id_new = BKE_id_copy_for_duplicate(bmain, id, is_object_liboverride, dupflag);
+ }
+ break;
+ case OB_MBALL:
+ if (dupflag & USER_DUP_MBALL) {
+ id_new = BKE_id_copy_for_duplicate(bmain, id, is_object_liboverride, dupflag);
+ }
+ break;
+ case OB_LAMP:
+ if (dupflag & USER_DUP_LAMP) {
+ id_new = BKE_id_copy_for_duplicate(bmain, id, is_object_liboverride, dupflag);
+ }
+ break;
+ case OB_ARMATURE:
+ if (dupflag & USER_DUP_ARM) {
+ id_new = BKE_id_copy_for_duplicate(bmain, id, is_object_liboverride, dupflag);
+ }
+ break;
+ case OB_LATTICE:
+ if (dupflag != 0) {
+ id_new = BKE_id_copy_for_duplicate(bmain, id, is_object_liboverride, dupflag);
+ }
+ break;
+ case OB_CAMERA:
+ if (dupflag != 0) {
+ id_new = BKE_id_copy_for_duplicate(bmain, id, is_object_liboverride, dupflag);
+ }
+ break;
+ case OB_LIGHTPROBE:
+ if (dupflag & USER_DUP_LIGHTPROBE) {
+ id_new = BKE_id_copy_for_duplicate(bmain, id, is_object_liboverride, dupflag);
+ }
+ break;
+ case OB_SPEAKER:
+ if (dupflag != 0) {
+ id_new = BKE_id_copy_for_duplicate(bmain, id, is_object_liboverride, dupflag);
+ }
+ break;
+ case OB_GPENCIL:
+ if (dupflag & USER_DUP_GPENCIL) {
+ id_new = BKE_id_copy_for_duplicate(bmain, id, is_object_liboverride, dupflag);
+ }
+ break;
+ case OB_HAIR:
+ if (dupflag & USER_DUP_HAIR) {
+ id_new = BKE_id_copy_for_duplicate(bmain, id, is_object_liboverride, dupflag);
+ }
+ break;
+ case OB_POINTCLOUD:
+ if (dupflag & USER_DUP_POINTCLOUD) {
+ id_new = BKE_id_copy_for_duplicate(bmain, id, is_object_liboverride, dupflag);
+ }
+ break;
+ case OB_VOLUME:
+ if (dupflag & USER_DUP_VOLUME) {
+ id_new = BKE_id_copy_for_duplicate(bmain, id, is_object_liboverride, dupflag);
+ }
+ break;
+ }
+ /* If obdata has been copied, we may also have to duplicate the materials assigned to it. */
+ if (need_to_duplicate_obdata && id_new != NULL) {
if (dupflag & USER_DUP_MAT) {
matarar = BKE_object_material_array_p(obn);
if (matarar) {
- for (a = 0; a < obn->totcol; a++) {
- id = (ID *)(*matarar)[a];
- if (id) {
- if (is_object_liboverride && ID_IS_LINKED(id)) {
- continue;
- }
- ID_NEW_REMAP_US((*matarar)[a])
- else
- {
- (*matarar)[a] = ID_NEW_SET((*matarar)[a], BKE_material_copy(bmain, (*matarar)[a]));
- if (dupflag & USER_DUP_ACT) {
- BKE_animdata_copy_id_action(bmain, &(*matarar)[a]->id, true);
- }
- }
- id_us_min(id);
- }
+ for (int i = 0; i < obn->totcol; i++) {
+ BKE_id_copy_for_duplicate(bmain, (ID *)(*matarar)[i], is_object_liboverride, dupflag);
}
}
}
}
-#undef ID_NEW_REMAP_US
-#undef ID_NEW_REMAP_US2
+ if (!is_subprocess) {
+ /* This code will follow into all ID links using an ID tagged with LIB_TAG_NEW.*/
+ BKE_libblock_relink_to_newid(&obn->id);
+
+#ifndef NDEBUG
+ /* Call to `BKE_libblock_relink_to_newid` above is supposed to have cleared all those flags. */
+ ID *id_iter;
+ FOREACH_MAIN_ID_BEGIN (bmain, id_iter) {
+ BLI_assert((id_iter->tag & LIB_TAG_NEW) == 0);
+ }
+ FOREACH_MAIN_ID_END;
+#endif
+
+ /* Cleanup. */
+ BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false);
+ BKE_main_id_clear_newpoins(bmain);
+ }
+
+ if (obn->type == OB_ARMATURE) {
+ DEG_id_tag_update(&obn->id, ID_RECALC_GEOMETRY);
+ if (obn->pose) {
+ BKE_pose_tag_recalc(bmain, obn->pose);
+ }
+ // BKE_pose_rebuild(bmain, obn, obn->data, true);
+ }
- if (ob->data != NULL) {
+ if (obn->data != NULL) {
DEG_id_tag_update_ex(bmain, (ID *)obn->data, ID_RECALC_EDITORS);
}
@@ -2512,7 +2383,6 @@ void BKE_object_matrix_local_get(struct Object *ob, float mat[4][4])
}
/**
- * \param depsgraph: Used for dupli-frame time.
* \return success if \a mat is set.
*/
static bool ob_parcurve(Object *ob, Object *par, float mat[4][4])
diff --git a/source/blender/blenkernel/intern/object_update.c b/source/blender/blenkernel/intern/object_update.c
index c5ef5acb08b..d8c3e0bf714 100644
--- a/source/blender/blenkernel/intern/object_update.c
+++ b/source/blender/blenkernel/intern/object_update.c
@@ -183,7 +183,7 @@ void BKE_object_handle_data_update(Depsgraph *depsgraph, Scene *scene, Object *o
#endif
/* Always compute UVs, vertex colors as orcos for render. */
cddata_masks.lmask |= CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL;
- cddata_masks.vmask |= CD_MASK_ORCO;
+ cddata_masks.vmask |= CD_MASK_ORCO | CD_MASK_PROP_COLOR;
}
if (em) {
makeDerivedMesh(depsgraph, scene, ob, em, &cddata_masks); /* was CD_MASK_BAREMESH */
diff --git a/source/blender/blenkernel/intern/packedFile.c b/source/blender/blenkernel/intern/packedFile.c
index ff6aa09ec89..2cd5588ccb8 100644
--- a/source/blender/blenkernel/intern/packedFile.c
+++ b/source/blender/blenkernel/intern/packedFile.c
@@ -264,14 +264,16 @@ void BKE_packedfile_pack_all(Main *bmain, ReportList *reports, bool verbose)
for (vfont = bmain->fonts.first; vfont; vfont = vfont->id.next) {
if (vfont->packedfile == NULL && !ID_IS_LINKED(vfont) &&
BKE_vfont_is_builtin(vfont) == false) {
- vfont->packedfile = BKE_packedfile_new(reports, vfont->name, BKE_main_blendfile_path(bmain));
+ vfont->packedfile = BKE_packedfile_new(
+ reports, vfont->filepath, BKE_main_blendfile_path(bmain));
tot++;
}
}
for (sound = bmain->sounds.first; sound; sound = sound->id.next) {
if (sound->packedfile == NULL && !ID_IS_LINKED(sound)) {
- sound->packedfile = BKE_packedfile_new(reports, sound->name, BKE_main_blendfile_path(bmain));
+ sound->packedfile = BKE_packedfile_new(
+ reports, sound->filepath, BKE_main_blendfile_path(bmain));
tot++;
}
}
@@ -566,14 +568,14 @@ int BKE_packedfile_unpack_vfont(Main *bmain,
if (vfont != NULL) {
unpack_generate_paths(
- vfont->name, (ID *)vfont, absname, localname, sizeof(absname), sizeof(localname));
+ vfont->filepath, (ID *)vfont, absname, localname, sizeof(absname), sizeof(localname));
newname = BKE_packedfile_unpack_to_file(
reports, BKE_main_blendfile_path(bmain), absname, localname, vfont->packedfile, how);
if (newname != NULL) {
ret_value = RET_OK;
BKE_packedfile_free(vfont->packedfile);
vfont->packedfile = NULL;
- BLI_strncpy(vfont->name, newname, sizeof(vfont->name));
+ BLI_strncpy(vfont->filepath, newname, sizeof(vfont->filepath));
MEM_freeN(newname);
}
}
@@ -592,11 +594,11 @@ int BKE_packedfile_unpack_sound(Main *bmain,
if (sound != NULL) {
unpack_generate_paths(
- sound->name, (ID *)sound, absname, localname, sizeof(absname), sizeof(localname));
+ sound->filepath, (ID *)sound, absname, localname, sizeof(absname), sizeof(localname));
newname = BKE_packedfile_unpack_to_file(
reports, BKE_main_blendfile_path(bmain), absname, localname, sound->packedfile, how);
if (newname != NULL) {
- BLI_strncpy(sound->name, newname, sizeof(sound->name));
+ BLI_strncpy(sound->filepath, newname, sizeof(sound->filepath));
MEM_freeN(newname);
BKE_packedfile_free(sound->packedfile);
@@ -644,7 +646,7 @@ 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->name, newname, sizeof(imapf->filepath));
+ BLI_strncpy(ima->filepath, newname, sizeof(imapf->filepath));
}
MEM_freeN(newname);
}
@@ -701,12 +703,12 @@ int BKE_packedfile_unpack_all_libraries(Main *bmain, ReportList *reports)
int ret_value = RET_ERROR;
for (lib = bmain->libraries.first; lib; lib = lib->id.next) {
- if (lib->packedfile && lib->name[0]) {
+ if (lib->packedfile && lib->filepath[0]) {
newname = BKE_packedfile_unpack_to_file(reports,
BKE_main_blendfile_path(bmain),
- lib->filepath,
- lib->filepath,
+ lib->filepath_abs,
+ lib->filepath_abs,
lib->packedfile,
PF_WRITE_ORIGINAL);
if (newname != NULL) {
@@ -731,19 +733,19 @@ void BKE_packedfile_pack_all_libraries(Main *bmain, ReportList *reports)
/* test for relativenss */
for (lib = bmain->libraries.first; lib; lib = lib->id.next) {
- if (!BLI_path_is_rel(lib->name)) {
+ if (!BLI_path_is_rel(lib->filepath)) {
break;
}
}
if (lib) {
- BKE_reportf(reports, RPT_ERROR, "Cannot pack absolute file: '%s'", lib->name);
+ BKE_reportf(reports, RPT_ERROR, "Cannot pack absolute file: '%s'", lib->filepath);
return;
}
for (lib = bmain->libraries.first; lib; lib = lib->id.next) {
if (lib->packedfile == NULL) {
- lib->packedfile = BKE_packedfile_new(reports, lib->name, BKE_main_blendfile_path(bmain));
+ lib->packedfile = BKE_packedfile_new(reports, lib->filepath, BKE_main_blendfile_path(bmain));
}
}
}
@@ -844,7 +846,7 @@ void BKE_packedfile_id_unpack(Main *bmain, ID *id, ReportList *reports, enum ePF
}
case ID_LI: {
Library *li = (Library *)id;
- BKE_reportf(reports, RPT_ERROR, "Cannot unpack individual Library file, '%s'", li->name);
+ BKE_reportf(reports, RPT_ERROR, "Cannot unpack individual Library file, '%s'", li->filepath);
break;
}
default:
diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c
index f26b478c680..b3ab856468c 100644
--- a/source/blender/blenkernel/intern/paint.c
+++ b/source/blender/blenkernel/intern/paint.c
@@ -1310,7 +1310,7 @@ static void sculptsession_free_pbvh(Object *object)
MEM_SAFE_FREE(ss->pmap);
MEM_SAFE_FREE(ss->pmap_mem);
- MEM_SAFE_FREE(ss->layer_base);
+ MEM_SAFE_FREE(ss->persistent_base);
MEM_SAFE_FREE(ss->preview_vert_index_list);
ss->preview_vert_index_count = 0;
@@ -1368,6 +1368,9 @@ void BKE_sculptsession_free(Object *ob)
MEM_SAFE_FREE(ss->preview_vert_index_list);
+ MEM_SAFE_FREE(ss->vertex_info.connected_component);
+ MEM_SAFE_FREE(ss->fake_neighbors.fake_neighbor_index);
+
if (ss->pose_ik_chain_preview) {
for (int i = 0; i < ss->pose_ik_chain_preview->tot_segments; i++) {
MEM_SAFE_FREE(ss->pose_ik_chain_preview->segments[i].weights);
@@ -1474,8 +1477,12 @@ static bool sculpt_modifiers_active(Scene *scene, Sculpt *sd, Object *ob)
/**
* \param need_mask: So that the evaluated mesh that is returned has mask data.
*/
-static void sculpt_update_object(
- Depsgraph *depsgraph, Object *ob, Mesh *me_eval, bool need_pmap, bool need_mask)
+static void sculpt_update_object(Depsgraph *depsgraph,
+ Object *ob,
+ Mesh *me_eval,
+ bool need_pmap,
+ bool need_mask,
+ bool need_colors)
{
Scene *scene = DEG_get_input_scene(depsgraph);
Sculpt *sd = scene->toolsettings->sculpt;
@@ -1490,6 +1497,8 @@ static void sculpt_update_object(
ss->building_vp_handle = false;
+ ss->scene = scene;
+
if (need_mask) {
if (mmd == NULL) {
if (!CustomData_has_layer(&me->vdata, CD_PAINT_MASK)) {
@@ -1503,6 +1512,16 @@ static void sculpt_update_object(
}
}
+ /* Add a color layer if a color tool is used. */
+ Mesh *orig_me = BKE_object_get_original_mesh(ob);
+ if (need_colors) {
+ if (!CustomData_has_layer(&orig_me->vdata, CD_PROP_COLOR)) {
+ CustomData_add_layer(&orig_me->vdata, CD_PROP_COLOR, CD_DEFAULT, NULL, orig_me->totvert);
+ BKE_mesh_update_customdata_pointers(orig_me, true);
+ DEG_id_tag_update(&orig_me->id, ID_RECALC_GEOMETRY);
+ }
+ }
+
/* tessfaces aren't used and will become invalid */
BKE_mesh_tessface_clear(me);
@@ -1535,6 +1554,7 @@ static void sculpt_update_object(
ss->multires.modifier = NULL;
ss->multires.level = 0;
ss->vmask = CustomData_get_layer(&me->vdata, CD_PAINT_MASK);
+ ss->vcol = CustomData_get_layer(&me->vdata, CD_PROP_COLOR);
}
/* Sculpt Face Sets. */
@@ -1663,13 +1683,11 @@ void BKE_sculpt_update_object_after_eval(Depsgraph *depsgraph, Object *ob_eval)
BLI_assert(me_eval != NULL);
- sculpt_update_object(depsgraph, ob_orig, me_eval, false, false);
+ sculpt_update_object(depsgraph, ob_orig, me_eval, false, false, false);
}
-void BKE_sculpt_update_object_for_edit(Depsgraph *depsgraph,
- Object *ob_orig,
- bool need_pmap,
- bool need_mask)
+void BKE_sculpt_update_object_for_edit(
+ Depsgraph *depsgraph, Object *ob_orig, bool need_pmap, bool need_mask, bool need_colors)
{
/* Update from sculpt operators and undo, to update sculpt session
* and PBVH after edits. */
@@ -1679,7 +1697,7 @@ void BKE_sculpt_update_object_for_edit(Depsgraph *depsgraph,
BLI_assert(ob_orig == DEG_get_original_object(ob_orig));
- sculpt_update_object(depsgraph, ob_orig, me_eval, need_pmap, need_mask);
+ sculpt_update_object(depsgraph, ob_orig, me_eval, need_pmap, need_mask, need_colors);
}
int BKE_sculpt_mask_layers_ensure(Object *ob, MultiresModifierData *mmd)
diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c
index c201cb83c44..b589db33edb 100644
--- a/source/blender/blenkernel/intern/particle.c
+++ b/source/blender/blenkernel/intern/particle.c
@@ -2262,6 +2262,7 @@ static void do_path_effectors(ParticleSimulationData *sim,
sim->psys->part->effector_weights,
&epoint,
force,
+ NULL,
NULL);
mul_v3_fl(force,
@@ -4786,7 +4787,7 @@ void psys_get_dupli_texture(ParticleSystem *psys,
int num;
/* XXX: on checking '(psmd->dm != NULL)'
- * This is incorrect but needed for metaball evaluation.
+ * This is incorrect but needed for meta-ball evaluation.
* Ideally this would be calculated via the depsgraph, however with meta-balls,
* the entire scenes dupli's are scanned, which also looks into uncalculated data.
*
diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c
index 606291a9ae0..d09f9a8eb09 100644
--- a/source/blender/blenkernel/intern/particle_system.c
+++ b/source/blender/blenkernel/intern/particle_system.c
@@ -2243,8 +2243,13 @@ static void basic_force_cb(void *efdata_v, ParticleKey *state, float *force, flo
/* add effectors */
pd_point_from_particle(efdata->sim, efdata->pa, state, &epoint);
if (part->type != PART_HAIR || part->effector_weights->flag & EFF_WEIGHT_DO_HAIR) {
- BKE_effectors_apply(
- sim->psys->effectors, sim->colliders, part->effector_weights, &epoint, force, impulse);
+ BKE_effectors_apply(sim->psys->effectors,
+ sim->colliders,
+ part->effector_weights,
+ &epoint,
+ force,
+ NULL,
+ impulse);
}
mul_v3_fl(force, efdata->ptex.field);
diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c
index 19f28047b80..8d7dabf9859 100644
--- a/source/blender/blenkernel/intern/pbvh.c
+++ b/source/blender/blenkernel/intern/pbvh.c
@@ -1325,6 +1325,7 @@ static void pbvh_update_draw_buffer_cb(void *__restrict userdata,
CustomData_get_layer(pbvh->pdata, CD_SCULPT_FACE_SETS),
pbvh->face_sets_color_seed,
pbvh->face_sets_color_default,
+ CustomData_get_layer(pbvh->vdata, CD_PROP_COLOR),
update_flags);
break;
case PBVH_BMESH:
@@ -1442,6 +1443,10 @@ void BKE_pbvh_update_vertex_data(PBVH *pbvh, int flag)
pbvh_update_mask_redraw(pbvh, nodes, totnode, flag);
}
+ if (flag & (PBVH_UpdateColor)) {
+ /* Do nothing */
+ }
+
if (flag & (PBVH_UpdateVisibility)) {
pbvh_update_visibility_redraw(pbvh, nodes, totnode, flag);
}
@@ -1729,6 +1734,11 @@ void BKE_pbvh_node_mark_update_mask(PBVHNode *node)
node->flag |= PBVH_UpdateMask | PBVH_UpdateDrawBuffers | PBVH_UpdateRedraw;
}
+void BKE_pbvh_node_mark_update_color(PBVHNode *node)
+{
+ node->flag |= PBVH_UpdateColor | PBVH_UpdateDrawBuffers | PBVH_UpdateRedraw;
+}
+
void BKE_pbvh_node_mark_update_visibility(PBVHNode *node)
{
node->flag |= PBVH_UpdateVisibility | PBVH_RebuildDrawBuffers | PBVH_UpdateDrawBuffers |
@@ -2905,6 +2915,26 @@ void BKE_pbvh_gather_proxies(PBVH *pbvh, PBVHNode ***r_array, int *r_tot)
*r_tot = tot;
}
+PBVHColorBufferNode *BKE_pbvh_node_color_buffer_get(PBVHNode *node)
+{
+
+ if (!node->color_buffer.color) {
+ node->color_buffer.color = MEM_callocN(node->uniq_verts * sizeof(float) * 4, "Color buffer");
+ }
+ return &node->color_buffer;
+}
+
+void BKE_pbvh_node_color_buffer_free(PBVH *pbvh)
+{
+ PBVHNode **nodes;
+ int totnode;
+ BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode);
+ for (int i = 0; i < totnode; i++) {
+ MEM_SAFE_FREE(nodes[i]->color_buffer.color);
+ }
+ MEM_SAFE_FREE(nodes);
+}
+
void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int mode)
{
struct CCGElem **grids;
@@ -2958,6 +2988,7 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m
vi->mask = NULL;
if (pbvh->type == PBVH_FACES) {
vi->vmask = CustomData_get_layer(pbvh->vdata, CD_PAINT_MASK);
+ vi->vcol = CustomData_get_layer(pbvh->vdata, CD_PROP_COLOR);
}
}
diff --git a/source/blender/blenkernel/intern/pbvh_intern.h b/source/blender/blenkernel/intern/pbvh_intern.h
index 7397f939894..6f8bae822ea 100644
--- a/source/blender/blenkernel/intern/pbvh_intern.h
+++ b/source/blender/blenkernel/intern/pbvh_intern.h
@@ -105,6 +105,9 @@ struct PBVHNode {
float (*bm_orco)[3];
int (*bm_ortri)[3];
int bm_tot_ortri;
+
+ /* Used to store the brush color during a stroke and composite it over the original color */
+ PBVHColorBufferNode color_buffer;
};
typedef enum {
diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c
index 836fe59d7bd..ab392e50053 100644
--- a/source/blender/blenkernel/intern/pointcache.c
+++ b/source/blender/blenkernel/intern/pointcache.c
@@ -1062,7 +1062,7 @@ static int ptcache_smoke_openvdb_write(struct OpenVDBWriter *writer, void *smoke
FluidModifierData *mmd = (FluidModifierData *)smoke_v;
FluidDomainSettings *mds = mmd->domain;
- OpenVDBWriter_set_flags(writer, mds->openvdb_comp, (mds->data_depth == 16));
+ OpenVDBWriter_set_flags(writer, mds->openvdb_compression, (mds->openvdb_data_depth == 16));
OpenVDBWriter_add_meta_int(writer, "blender/smoke/active_fields", mds->active_fields);
OpenVDBWriter_add_meta_v3_int(writer, "blender/smoke/resolution", mds->res);
@@ -1593,7 +1593,7 @@ void BKE_ptcache_id_from_softbody(PTCacheID *pid, Object *ob, SoftBody *sb)
{
memset(pid, 0, sizeof(PTCacheID));
- pid->ob = ob;
+ pid->owner_id = &ob->id;
pid->calldata = sb;
pid->type = PTCACHE_TYPE_SOFTBODY;
pid->cache = sb->shared->pointcache;
@@ -1632,7 +1632,7 @@ void BKE_ptcache_id_from_particles(PTCacheID *pid, Object *ob, ParticleSystem *p
{
memset(pid, 0, sizeof(PTCacheID));
- pid->ob = ob;
+ pid->owner_id = &ob->id;
pid->calldata = psys;
pid->type = PTCACHE_TYPE_PARTICLES;
pid->stack_index = psys->pointcache->index;
@@ -1697,7 +1697,7 @@ void BKE_ptcache_id_from_cloth(PTCacheID *pid, Object *ob, ClothModifierData *cl
{
memset(pid, 0, sizeof(PTCacheID));
- pid->ob = ob;
+ pid->owner_id = &ob->id;
pid->calldata = clmd;
pid->type = PTCACHE_TYPE_CLOTH;
pid->stack_index = clmd->point_cache->index;
@@ -1738,7 +1738,7 @@ void BKE_ptcache_id_from_smoke(PTCacheID *pid, struct Object *ob, struct FluidMo
memset(pid, 0, sizeof(PTCacheID));
- pid->ob = ob;
+ pid->owner_id = &ob->id;
pid->calldata = mmd;
pid->type = PTCACHE_TYPE_SMOKE_DOMAIN;
@@ -1788,7 +1788,7 @@ void BKE_ptcache_id_from_dynamicpaint(PTCacheID *pid, Object *ob, DynamicPaintSu
memset(pid, 0, sizeof(PTCacheID));
- pid->ob = ob;
+ pid->owner_id = &ob->id;
pid->calldata = surface;
pid->type = PTCACHE_TYPE_DYNAMICPAINT;
pid->cache = surface->pointcache;
@@ -1829,7 +1829,7 @@ void BKE_ptcache_id_from_rigidbody(PTCacheID *pid, Object *ob, RigidBodyWorld *r
memset(pid, 0, sizeof(PTCacheID));
- pid->ob = ob;
+ pid->owner_id = &ob->id;
pid->calldata = rbw;
pid->type = PTCACHE_TYPE_RIGIDBODY;
pid->cache = rbw->shared->pointcache;
@@ -2188,9 +2188,9 @@ static int ptcache_frame_from_filename(const char *filename, const char *ext)
static int ptcache_path(PTCacheID *pid, char *filename)
{
- Library *lib = (pid->ob) ? pid->ob->id.lib : NULL;
+ Library *lib = (pid->owner_id) ? pid->owner_id->lib : NULL;
const char *blendfilename = (lib && (pid->cache->flag & PTCACHE_IGNORE_LIBPATH) == 0) ?
- lib->filepath :
+ lib->filepath_abs :
BKE_main_blendfile_path_from_global();
size_t i;
@@ -2246,7 +2246,7 @@ static int ptcache_filename(PTCacheID *pid, char *filename, int cfra, short do_p
newname += len;
}
if (pid->cache->name[0] == '\0' && (pid->cache->flag & PTCACHE_EXTERNAL) == 0) {
- idname = (pid->ob->id.name + 2);
+ idname = (pid->owner_id->name + 2);
/* convert chars to hex so they are always a valid filename */
while ('\0' != *idname) {
BLI_snprintf(newname, MAX_PTCACHE_FILE, "%02X", (unsigned int)(*idname++));
@@ -2263,7 +2263,8 @@ static int ptcache_filename(PTCacheID *pid, char *filename, int cfra, short do_p
if (do_ext) {
if (pid->cache->index < 0) {
- pid->cache->index = pid->stack_index = BKE_object_insert_ptcache(pid->ob);
+ BLI_assert(GS(pid->owner_id->name) == ID_OB);
+ pid->cache->index = pid->stack_index = BKE_object_insert_ptcache((Object *)pid->owner_id);
}
const char *ext = ptcache_file_extension(pid);
@@ -2297,7 +2298,7 @@ static PTCacheFile *ptcache_file_open(PTCacheID *pid, int mode, int cfra)
#ifndef DURIAN_POINTCACHE_LIB_OK
/* don't allow writing for linked objects */
- if (pid->ob->id.lib && mode == PTCACHE_FILE_WRITE) {
+ if (pid->owner_id->lib && mode == PTCACHE_FILE_WRITE) {
return NULL;
}
#endif
@@ -3508,7 +3509,7 @@ void BKE_ptcache_id_clear(PTCacheID *pid, int mode, unsigned int cfra)
#ifndef DURIAN_POINTCACHE_LIB_OK
/* don't allow clearing for linked objects */
- if (pid->ob->id.lib) {
+ if (pid->owner_id->lib) {
return;
}
#endif
@@ -3688,7 +3689,6 @@ void BKE_ptcache_id_time(
* is probably to interpolate results from two frames for that ..
*/
- /* ob= pid->ob; */ /* UNUSED */
cache = pid->cache;
if (timescale) {
@@ -4119,7 +4119,7 @@ void BKE_ptcache_bake(PTCacheBaker *baker)
G.is_break = false;
/* set caches to baking mode and figure out start frame */
- if (pid->ob) {
+ if (pid->owner_id) {
/* cache/bake a single object */
cache = pid->cache;
if ((cache->flag & PTCACHE_BAKED) == 0) {
@@ -4136,7 +4136,8 @@ void BKE_ptcache_bake(PTCacheBaker *baker)
/* get all pids from the object and search for smoke low res */
ListBase pidlist2;
PTCacheID *pid2;
- BKE_ptcache_ids_from_object(&pidlist2, pid->ob, scene, MAX_DUPLI_RECUR);
+ BLI_assert(GS(pid->owner_id->name) == ID_OB);
+ BKE_ptcache_ids_from_object(&pidlist2, (Object *)pid->owner_id, scene, MAX_DUPLI_RECUR);
for (pid2 = pidlist2.first; pid2; pid2 = pid2->next) {
if (pid2->type == PTCACHE_TYPE_SMOKE_DOMAIN) {
if (pid2->cache && !(pid2->cache->flag & PTCACHE_BAKED)) {
@@ -4425,7 +4426,7 @@ void BKE_ptcache_toggle_disk_cache(PTCacheID *pid)
if ((cache->flag & PTCACHE_DISK_CACHE) == 0) {
if (cache->index) {
- BKE_object_delete_ptcache(pid->ob, cache->index);
+ BKE_object_delete_ptcache((Object *)pid->owner_id, cache->index);
cache->index = -1;
}
}
diff --git a/source/blender/blenkernel/intern/rigidbody.c b/source/blender/blenkernel/intern/rigidbody.c
index c9911d2cf85..4752782eaeb 100644
--- a/source/blender/blenkernel/intern/rigidbody.c
+++ b/source/blender/blenkernel/intern/rigidbody.c
@@ -1348,9 +1348,10 @@ void BKE_rigidbody_main_collection_object_add(Main *bmain, Collection *collectio
/* ************************************** */
/* Utilities API */
-/* Get RigidBody world for the given scene, creating one if needed
+/**
+ * Get RigidBody world for the given scene, creating one if needed
*
- * \param scene: Scene to find active Rigid Body world for
+ * \param scene: Scene to find active Rigid Body world for.
*/
RigidBodyWorld *BKE_rigidbody_get_world(Scene *scene)
{
@@ -1646,7 +1647,7 @@ static void rigidbody_update_sim_ob(
/* Calculate net force of effectors, and apply to sim object:
* - we use 'central force' since apply force requires a "relative position"
* which we don't have... */
- BKE_effectors_apply(effectors, NULL, effector_weights, &epoint, eff_force, NULL);
+ BKE_effectors_apply(effectors, NULL, effector_weights, &epoint, eff_force, NULL, NULL);
if (G.f & G_DEBUG) {
printf("\tapplying force (%f,%f,%f) to '%s'\n",
eff_force[0],
diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c
index 7cf424f53e0..b0faa555f29 100644
--- a/source/blender/blenkernel/intern/scene.c
+++ b/source/blender/blenkernel/intern/scene.c
@@ -822,48 +822,70 @@ Scene *BKE_scene_duplicate(Main *bmain, Scene *sce, eSceneCopyMethod type)
return sce_copy;
}
else {
- BKE_id_copy_ex(bmain, (ID *)sce, (ID **)&sce_copy, LIB_ID_COPY_ACTIONS);
+ const eDupli_ID_Flags duplicate_flags = U.dupflag | USER_DUP_OBJECT;
+
+ BKE_id_copy(bmain, (ID *)sce, (ID **)&sce_copy);
id_us_min(&sce_copy->id);
id_us_ensure_real(&sce_copy->id);
+ if (duplicate_flags & USER_DUP_ACT) {
+ BKE_animdata_copy_id_action(bmain, &sce_copy->id, true);
+ }
+
/* Extra actions, most notably SCE_FULL_COPY also duplicates several 'children' datablocks. */
if (type == SCE_COPY_FULL) {
+ /* Scene duplication is always root of duplication currently. */
+ const bool is_subprocess = false;
+
+ if (!is_subprocess) {
+ BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false);
+ BKE_main_id_clear_newpoins(bmain);
+ }
+
/* Copy Freestyle LineStyle datablocks. */
LISTBASE_FOREACH (ViewLayer *, view_layer_dst, &sce_copy->view_layers) {
LISTBASE_FOREACH (
FreestyleLineSet *, lineset, &view_layer_dst->freestyle_config.linesets) {
- if (lineset->linestyle) {
- if (is_scene_liboverride && ID_IS_LINKED(lineset->linestyle)) {
- continue;
- }
- id_us_min(&lineset->linestyle->id);
- BKE_id_copy_ex(
- bmain, (ID *)lineset->linestyle, (ID **)&lineset->linestyle, LIB_ID_COPY_ACTIONS);
- }
+ BKE_id_copy_for_duplicate(
+ bmain, &lineset->linestyle->id, is_scene_liboverride, duplicate_flags);
}
}
/* Full copy of world (included animations) */
- if (sce_copy->world) {
- if (!is_scene_liboverride || !ID_IS_LINKED(sce_copy->world)) {
- id_us_min(&sce_copy->world->id);
- BKE_id_copy_ex(
- bmain, (ID *)sce_copy->world, (ID **)&sce_copy->world, LIB_ID_COPY_ACTIONS);
- }
- }
+ BKE_id_copy_for_duplicate(bmain, &sce->world->id, is_scene_liboverride, duplicate_flags);
/* Full copy of GreasePencil. */
- if (sce_copy->gpd) {
- if (!is_scene_liboverride || !ID_IS_LINKED(sce_copy->gpd)) {
- id_us_min(&sce_copy->gpd->id);
- BKE_id_copy_ex(bmain, (ID *)sce_copy->gpd, (ID **)&sce_copy->gpd, LIB_ID_COPY_ACTIONS);
- }
- }
+ BKE_id_copy_for_duplicate(bmain, &sce->gpd->id, is_scene_liboverride, duplicate_flags);
/* Deep-duplicate collections and objects (using preferences' settings for which sub-data to
* duplicate along the object itself). */
- BKE_collection_duplicate(bmain, NULL, sce_copy->master_collection, true, true, true);
+ BKE_collection_duplicate(bmain,
+ NULL,
+ sce_copy->master_collection,
+ duplicate_flags,
+ LIB_ID_DUPLICATE_IS_SUBPROCESS);
+
+ if (!is_subprocess) {
+ /* This code will follow into all ID links using an ID tagged with LIB_TAG_NEW.*/
+ BKE_libblock_relink_to_newid(&sce_copy->id);
+
+#ifndef NDEBUG
+ /* Call to `BKE_libblock_relink_to_newid` above is supposed to have cleared all those
+ * flags. */
+ ID *id_iter;
+ FOREACH_MAIN_ID_BEGIN (bmain, id_iter) {
+ BLI_assert((id_iter->tag & LIB_TAG_NEW) == 0);
+ }
+ FOREACH_MAIN_ID_END;
+#endif
+
+ /* Cleanup. */
+ BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false);
+ BKE_main_id_clear_newpoins(bmain);
+
+ BKE_main_collection_sync(bmain);
+ }
}
else {
/* Remove sequencer if not full copy */
@@ -2271,7 +2293,8 @@ static char *scene_undo_depsgraph_gen_key(Scene *scene, ViewLayer *view_layer, c
size_t key_full_offset = BLI_strncpy_rlen(key_full, scene->id.name, MAX_ID_NAME);
if (scene->id.lib != NULL) {
- key_full_offset += BLI_strncpy_rlen(key_full + key_full_offset, scene->id.lib->name, FILE_MAX);
+ key_full_offset += BLI_strncpy_rlen(
+ key_full + key_full_offset, scene->id.lib->filepath, FILE_MAX);
}
key_full_offset += BLI_strncpy_rlen(key_full + key_full_offset, view_layer->name, MAX_NAME);
BLI_assert(key_full_offset < MAX_ID_NAME + FILE_MAX + MAX_NAME);
diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c
index bfc0d437994..c510b3a2dfb 100644
--- a/source/blender/blenkernel/intern/screen.c
+++ b/source/blender/blenkernel/intern/screen.c
@@ -393,6 +393,7 @@ static void panel_list_copy(ListBase *newlb, const ListBase *lb)
Panel *panel = lb->first;
for (; new_panel; new_panel = new_panel->next, panel = panel->next) {
new_panel->activedata = NULL;
+ new_panel->runtime.custom_data_ptr = NULL;
panel_list_copy(&new_panel->children, &panel->children);
}
}
@@ -575,18 +576,25 @@ void BKE_region_callback_free_gizmomap_set(void (*callback)(struct wmGizmoMap *)
region_free_gizmomap_callback = callback;
}
-void BKE_area_region_panels_free(ListBase *lb)
+static void area_region_panels_free_recursive(Panel *panel)
{
- Panel *panel, *panel_next;
- for (panel = lb->first; panel; panel = panel_next) {
- panel_next = panel->next;
- if (panel->activedata) {
- MEM_freeN(panel->activedata);
- }
- BKE_area_region_panels_free(&panel->children);
+ MEM_SAFE_FREE(panel->activedata);
+
+ LISTBASE_FOREACH_MUTABLE (Panel *, child_panel, &panel->children) {
+ area_region_panels_free_recursive(child_panel);
}
- BLI_freelistN(lb);
+ MEM_freeN(panel);
+}
+
+void BKE_area_region_panels_free(ListBase *lb)
+{
+ LISTBASE_FOREACH_MUTABLE (Panel *, panel, lb) {
+ /* Free custom data just for parent panels to avoid a double free. */
+ MEM_SAFE_FREE(panel->runtime.custom_data_ptr);
+ area_region_panels_free_recursive(panel);
+ }
+ BLI_listbase_clear(lb);
}
/* not region itself */
diff --git a/source/blender/blenkernel/intern/seqeffects.c b/source/blender/blenkernel/intern/seqeffects.c
index 9fa43ed0a5f..de233a8d473 100644
--- a/source/blender/blenkernel/intern/seqeffects.c
+++ b/source/blender/blenkernel/intern/seqeffects.c
@@ -2810,7 +2810,7 @@ static ImBuf *do_glow_effect(const SeqRenderData *context,
context->rectx,
context->recty,
ibuf1->rect_float,
- ibuf2->rect_float,
+ NULL,
out->rect_float);
}
else {
@@ -2821,7 +2821,7 @@ static ImBuf *do_glow_effect(const SeqRenderData *context,
context->rectx,
context->recty,
(unsigned char *)ibuf1->rect,
- (unsigned char *)ibuf2->rect,
+ NULL,
(unsigned char *)out->rect);
}
@@ -3821,7 +3821,7 @@ void BKE_sequencer_text_font_load(TextVars *data, const bool do_id_user)
}
char path[FILE_MAX];
- STRNCPY(path, data->text_font->name);
+ STRNCPY(path, data->text_font->filepath);
BLI_assert(BLI_thread_is_main());
BLI_path_abs(path, ID_BLEND_PATH_FROM_GLOBAL(&data->text_font->id));
@@ -3895,7 +3895,7 @@ static ImBuf *do_text_effect(const SeqRenderData *context,
data->text_blf_id = -1;
if (data->text_font) {
- data->text_blf_id = BLF_load(data->text_font->name);
+ data->text_blf_id = BLF_load(data->text_font->filepath);
}
}
diff --git a/source/blender/blenkernel/intern/seqmodifier.c b/source/blender/blenkernel/intern/seqmodifier.c
index 75f7ed82165..604cbf476a8 100644
--- a/source/blender/blenkernel/intern/seqmodifier.c
+++ b/source/blender/blenkernel/intern/seqmodifier.c
@@ -1126,3 +1126,5 @@ int BKE_sequence_supports_modifiers(Sequence *seq)
{
return !ELEM(seq->type, SEQ_TYPE_SOUND_RAM, SEQ_TYPE_SOUND_HD);
}
+
+/** \} */
diff --git a/source/blender/blenkernel/intern/sequencer.c b/source/blender/blenkernel/intern/sequencer.c
index 90edebfaa97..297d60e5976 100644
--- a/source/blender/blenkernel/intern/sequencer.c
+++ b/source/blender/blenkernel/intern/sequencer.c
@@ -406,17 +406,18 @@ static void seqclipboard_ptr_restore(Main *bmain, ID **id_pt)
/* check for a data with the same filename */
switch (GS(ID_PT->name)) {
case ID_SO: {
- id_restore = BLI_findstring(lb, ((bSound *)ID_PT)->name, offsetof(bSound, name));
+ id_restore = BLI_findstring(lb, ((bSound *)ID_PT)->filepath, offsetof(bSound, filepath));
if (id_restore == NULL) {
- id_restore = BKE_sound_new_file(bmain, ((bSound *)ID_PT)->name);
+ id_restore = BKE_sound_new_file(bmain, ((bSound *)ID_PT)->filepath);
(ID_PT)->newid = id_restore; /* reuse next time */
}
break;
}
case ID_MC: {
- id_restore = BLI_findstring(lb, ((MovieClip *)ID_PT)->name, offsetof(MovieClip, name));
+ id_restore = BLI_findstring(
+ lb, ((MovieClip *)ID_PT)->filepath, offsetof(MovieClip, filepath));
if (id_restore == NULL) {
- id_restore = BKE_movieclip_file_add(bmain, ((MovieClip *)ID_PT)->name);
+ id_restore = BKE_movieclip_file_add(bmain, ((MovieClip *)ID_PT)->filepath);
(ID_PT)->newid = id_restore; /* reuse next time */
}
break;
@@ -842,30 +843,22 @@ void BKE_sequence_calc(Scene *scene, Sequence *seq)
}
/* effects and meta: automatic start and end */
-
if (seq->type & SEQ_TYPE_EFFECT) {
- /* pointers */
- if (seq->seq2 == NULL) {
- seq->seq2 = seq->seq1;
- }
- if (seq->seq3 == NULL) {
- seq->seq3 = seq->seq1;
- }
-
- /* effecten go from seq1 -> seq2: test */
-
- /* we take the largest start and smallest end */
-
- // seq->start = seq->startdisp = MAX2(seq->seq1->startdisp, seq->seq2->startdisp);
- // seq->enddisp = MIN2(seq->seq1->enddisp, seq->seq2->enddisp);
-
if (seq->seq1) {
- /* XXX These resets should not be necessary, but users used to be able to
- * edit effect's length, leading to strange results. See [#29190] */
seq->startofs = seq->endofs = seq->startstill = seq->endstill = 0;
- seq->start = seq->startdisp = max_iii(
- seq->seq1->startdisp, seq->seq2->startdisp, seq->seq3->startdisp);
- seq->enddisp = min_iii(seq->seq1->enddisp, seq->seq2->enddisp, seq->seq3->enddisp);
+ if (seq->seq3) {
+ seq->start = seq->startdisp = max_iii(
+ seq->seq1->startdisp, seq->seq2->startdisp, seq->seq3->startdisp);
+ seq->enddisp = min_iii(seq->seq1->enddisp, seq->seq2->enddisp, seq->seq3->enddisp);
+ }
+ else if (seq->seq2) {
+ seq->start = seq->startdisp = max_ii(seq->seq1->startdisp, seq->seq2->startdisp);
+ seq->enddisp = min_ii(seq->seq1->enddisp, seq->seq2->enddisp);
+ }
+ else {
+ seq->start = seq->startdisp = seq->seq1->startdisp;
+ seq->enddisp = seq->seq1->enddisp;
+ }
/* we cant help if strips don't overlap, it wont give useful results.
* but at least ensure 'len' is never negative which causes bad bugs elsewhere. */
if (seq->enddisp < seq->startdisp) {
@@ -919,6 +912,7 @@ static void seq_multiview_name(Scene *scene,
size_t r_size)
{
const char *suffix = BKE_scene_multiview_view_id_suffix_get(&scene->r, view_id);
+ BLI_assert(ext != NULL && suffix != NULL && prefix != NULL);
BLI_snprintf(r_path, r_size, "%s%s%s", prefix, suffix, ext);
}
@@ -1188,7 +1182,7 @@ static void seqbase_unique_name(ListBase *seqbasep, SeqUniqueInfo *sui)
Sequence *seq;
for (seq = seqbasep->first; seq; seq = seq->next) {
if ((sui->seq != seq) && STREQ(sui->name_dest, seq->name + 2)) {
- /* SEQ_NAME_MAXSTR -4 for the number, -1 for \0, - 2 for prefix */
+ /* SEQ_NAME_MAXSTR -4 for the number, -1 for \0, - 2 for r_prefix */
BLI_snprintf(sui->name_dest,
sizeof(sui->name_dest),
"%.*s.%03d",
@@ -1783,6 +1777,33 @@ static void seq_open_anim_file(Scene *scene, Sequence *seq, bool openfile)
}
}
+static bool seq_proxy_get_custom_file_fname(Sequence *seq, char *name, const int view_id)
+{
+ char fname[FILE_MAXFILE];
+ char suffix[24];
+ StripProxy *proxy = seq->strip->proxy;
+
+ if (proxy == NULL) {
+ return false;
+ }
+
+ BLI_join_dirfile(fname, PROXY_MAXFILE, proxy->dir, proxy->file);
+ BLI_path_abs(fname, BKE_main_blendfile_path_from_global());
+
+ if (view_id > 0) {
+ BLI_snprintf(suffix, sizeof(suffix), "_%d", view_id);
+ /* TODO(sergey): This will actually append suffix after extension
+ * which is weird but how was originally coded in multiview branch.
+ */
+ BLI_snprintf(name, PROXY_MAXFILE, "%s_%s", fname, suffix);
+ }
+ else {
+ BLI_strncpy(name, fname, PROXY_MAXFILE);
+ }
+
+ return true;
+}
+
static bool seq_proxy_get_fname(Editing *ed,
Sequence *seq,
int cfra,
@@ -1790,141 +1811,82 @@ static bool seq_proxy_get_fname(Editing *ed,
char *name,
const int view_id)
{
- int frameno;
char dir[PROXY_MAXFILE];
- StripAnim *sanim;
char suffix[24] = {'\0'};
-
StripProxy *proxy = seq->strip->proxy;
- if (!proxy) {
+
+ if (proxy == NULL) {
return false;
}
- /* MOVIE tracks (only exception: custom files) are now handled
- * internally by ImBuf module for various reasons: proper time code
- * support, quicker index build, using one file instead
- * of a full directory of jpeg files, etc. Trying to support old
- * and new method at once could lead to funny effects, if people
- * have both, a directory full of jpeg files and proxy avis, so
- * sorry folks, please rebuild your proxies... */
+ /* Multiview suffix. */
+ if (view_id > 0) {
+ BLI_snprintf(suffix, sizeof(suffix), "_%d", view_id);
+ }
- sanim = BLI_findlink(&seq->anims, view_id);
+ /* Per strip with Custom file situation is handled separately. */
+ if (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_FILE &&
+ ed->proxy_storage != SEQ_EDIT_PROXY_DIR_STORAGE) {
+ if (seq_proxy_get_custom_file_fname(seq, name, view_id)) {
+ return true;
+ }
+ }
if (ed->proxy_storage == SEQ_EDIT_PROXY_DIR_STORAGE) {
- char fname[FILE_MAXFILE];
+ /* Per project default. */
if (ed->proxy_dir[0] == 0) {
BLI_strncpy(dir, "//BL_proxy", sizeof(dir));
}
- else {
+ else { /* Per project with custom dir. */
BLI_strncpy(dir, ed->proxy_dir, sizeof(dir));
}
-
- if (sanim && sanim->anim) {
- IMB_anim_get_fname(sanim->anim, fname, FILE_MAXFILE);
- }
- else if (seq->type == SEQ_TYPE_IMAGE) {
- fname[0] = 0;
- }
- else {
- /* We could make a name here, except non-movie's don't generate proxies,
- * cancel until other types of sequence strips are supported. */
- return false;
- }
- BLI_path_append(dir, sizeof(dir), fname);
BLI_path_abs(name, BKE_main_blendfile_path_from_global());
}
- else if ((proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_DIR) &&
- (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_FILE)) {
- BLI_strncpy(dir, seq->strip->proxy->dir, sizeof(dir));
- }
- else if (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_FILE) {
- BLI_strncpy(dir, seq->strip->proxy->dir, sizeof(dir));
- }
- else if (sanim && sanim->anim && (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_DIR)) {
- char fname[FILE_MAXFILE];
- BLI_strncpy(dir, seq->strip->proxy->dir, sizeof(dir));
- IMB_anim_get_fname(sanim->anim, fname, FILE_MAXFILE);
- BLI_path_append(dir, sizeof(dir), fname);
- }
- else if (seq->type == SEQ_TYPE_IMAGE) {
+ else {
+ /* Pre strip with custom dir. */
if (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_DIR) {
BLI_strncpy(dir, seq->strip->proxy->dir, sizeof(dir));
}
- else {
+ else { /* Per strip default. */
BLI_snprintf(dir, PROXY_MAXFILE, "%s/BL_proxy", seq->strip->dir);
}
}
- else {
- return false;
- }
-
- if (view_id > 0) {
- BLI_snprintf(suffix, sizeof(suffix), "_%d", view_id);
- }
-
- if (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_FILE &&
- ed->proxy_storage != SEQ_EDIT_PROXY_DIR_STORAGE) {
- char fname[FILE_MAXFILE];
- BLI_join_dirfile(fname, PROXY_MAXFILE, dir, proxy->file);
- BLI_path_abs(fname, BKE_main_blendfile_path_from_global());
- if (suffix[0] != '\0') {
- /* TODO(sergey): This will actually append suffix after extension
- * which is weird but how was originally coded in multiview branch.
- */
- BLI_snprintf(name, PROXY_MAXFILE, "%s_%s", fname, suffix);
- }
- else {
- BLI_strncpy(name, fname, PROXY_MAXFILE);
- }
-
- return true;
- }
-
- /* generate a separate proxy directory for each preview size */
+ /* Proxy size number to be used in path. */
int proxy_size_number = BKE_sequencer_rendersize_to_scale_factor(render_size) * 100;
- if (seq->type == SEQ_TYPE_IMAGE) {
- BLI_snprintf(name,
- PROXY_MAXFILE,
- "%s/images/%d/%s_proxy%s",
- dir,
- proxy_size_number,
- BKE_sequencer_give_stripelem(seq, cfra)->name,
- suffix);
- frameno = 1;
- }
- else {
- frameno = (int)BKE_sequencer_give_stripelem_index(seq, cfra) + seq->anim_startofs;
- BLI_snprintf(name, PROXY_MAXFILE, "%s/proxy_misc/%d/####%s", dir, proxy_size_number, suffix);
- }
-
+ BLI_snprintf(name,
+ PROXY_MAXFILE,
+ "%s/images/%d/%s_proxy%s",
+ dir,
+ proxy_size_number,
+ BKE_sequencer_give_stripelem(seq, cfra)->name,
+ suffix);
BLI_path_abs(name, BKE_main_blendfile_path_from_global());
- BLI_path_frame(name, frameno, 0);
-
strcat(name, ".jpg");
return true;
}
+static bool seq_can_use_proxy(Sequence *seq, IMB_Proxy_Size psize)
+{
+ if (seq->strip->proxy == NULL) {
+ return false;
+ }
+ short size_flags = seq->strip->proxy->build_size_flags;
+ return (seq->flag & SEQ_USE_PROXY) != 0 && psize != IMB_PROXY_NONE && (size_flags & psize) != 0;
+}
+
static ImBuf *seq_proxy_fetch(const SeqRenderData *context, Sequence *seq, int cfra)
{
char name[PROXY_MAXFILE];
StripProxy *proxy = seq->strip->proxy;
const eSpaceSeq_Proxy_RenderSize psize = context->preview_render_size;
- const IMB_Proxy_Size psize_flag = seq_rendersize_to_proxysize(psize);
- int size_flags;
Editing *ed = context->scene->ed;
StripAnim *sanim;
- if (!(seq->flag & SEQ_USE_PROXY)) {
- return NULL;
- }
-
- size_flags = proxy->build_size_flags;
-
/* only use proxies, if they are enabled (even if present!) */
- if (psize_flag == IMB_PROXY_NONE || (size_flags & psize_flag) == 0) {
+ if (!seq_can_use_proxy(seq, seq_rendersize_to_proxysize(psize))) {
return NULL;
}
@@ -2342,7 +2304,9 @@ MINLINE float color_balance_fl(
x = 0.f;
}
- return powf(x, gamma) * mul;
+ x = powf(x, gamma) * mul;
+ CLAMP(x, FLT_MIN, FLT_MAX);
+ return x;
}
static void make_cb_table_float(float lift, float gain, float gamma, float *table, float mul)
@@ -2977,9 +2941,9 @@ static ImBuf *seq_render_effect_strip_impl(const SeqRenderData *context,
case EARLY_DO_EFFECT:
for (i = 0; i < 3; i++) {
/* Speed effect requires time remapping of cfra for input(s). */
- if (input[1] && seq->type == SEQ_TYPE_SPEED) {
+ if (input[0] && seq->type == SEQ_TYPE_SPEED) {
float target_frame = BKE_sequencer_speed_effect_target_frame_get(context, seq, cfra, i);
- ibuf[i] = seq_render_strip(context, state, input[i], target_frame);
+ ibuf[i] = seq_render_strip(context, state, input[0], target_frame);
}
else { /* Other effects. */
if (input[i]) {
@@ -2988,7 +2952,7 @@ static ImBuf *seq_render_effect_strip_impl(const SeqRenderData *context,
}
}
- if (ibuf[0] && ibuf[1]) {
+ if (ibuf[0] && (ibuf[1] || BKE_sequence_effect_get_num_inputs(seq->type) == 1)) {
if (sh.multithreaded) {
out = BKE_sequencer_effect_execute_threaded(
&sh, context, seq, cfra, fac, facf, ibuf[0], ibuf[1], ibuf[2]);
@@ -3021,97 +2985,126 @@ static ImBuf *seq_render_effect_strip_impl(const SeqRenderData *context,
return out;
}
+/* Render individual view for multiview or single (default view) for monoview. */
+static ImBuf *seq_render_image_strip_view(const SeqRenderData *context,
+ Sequence *seq,
+ char *name,
+ char *prefix,
+ const char *ext,
+ int view_id)
+{
+
+ ImBuf *ibuf = NULL;
+
+ int flag = IB_rect | IB_metadata;
+ if (seq->alpha_mode == SEQ_ALPHA_PREMUL) {
+ flag |= IB_alphamode_premul;
+ }
+
+ if (prefix[0] == '\0') {
+ ibuf = IMB_loadiffname(name, flag, seq->strip->colorspace_settings.name);
+ }
+ else {
+ char str[FILE_MAX];
+ BKE_scene_multiview_view_prefix_get(context->scene, name, prefix, &ext);
+ seq_multiview_name(context->scene, view_id, prefix, ext, str, FILE_MAX);
+ ibuf = IMB_loadiffname(str, flag, seq->strip->colorspace_settings.name);
+ }
+
+ if (ibuf == NULL) {
+ return NULL;
+ }
+
+ /* We don't need both (speed reasons)! */
+ if (ibuf->rect_float != NULL && ibuf->rect != NULL) {
+ imb_freerectImBuf(ibuf);
+ }
+
+ /* All sequencer color is done in SRGB space, linear gives odd crossfades. */
+ BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibuf, false);
+
+ return ibuf;
+}
+
+static bool seq_image_strip_is_multiview_render(
+ Scene *scene, Sequence *seq, int totfiles, char *name, char *r_prefix, const char *r_ext)
+{
+ if (totfiles > 1) {
+ BKE_scene_multiview_view_prefix_get(scene, name, r_prefix, &r_ext);
+ if (r_prefix[0] == '\0') {
+ return false;
+ }
+ }
+ else {
+ r_prefix[0] = '\0';
+ }
+
+ return (seq->flag & SEQ_USE_VIEWS) != 0 && (scene->r.scemode & R_MULTIVIEW) != 0;
+}
+
static ImBuf *seq_render_image_strip(const SeqRenderData *context,
Sequence *seq,
float UNUSED(nr),
- float cfra)
+ float cfra,
+ bool *r_is_proxy_image)
{
- ImBuf *ibuf = NULL;
char name[FILE_MAX];
- bool is_multiview = (seq->flag & SEQ_USE_VIEWS) != 0 &&
- (context->scene->r.scemode & R_MULTIVIEW) != 0;
- StripElem *s_elem = BKE_sequencer_give_stripelem(seq, cfra);
- int flag;
+ const char *ext = NULL;
+ char prefix[FILE_MAX];
+ ImBuf *ibuf = NULL;
- if (s_elem) {
- BLI_join_dirfile(name, sizeof(name), seq->strip->dir, s_elem->name);
- BLI_path_abs(name, BKE_main_blendfile_path_from_global());
+ StripElem *s_elem = BKE_sequencer_give_stripelem(seq, cfra);
+ if (s_elem == NULL) {
+ return NULL;
}
- flag = IB_rect | IB_metadata;
- if (seq->alpha_mode == SEQ_ALPHA_PREMUL) {
- flag |= IB_alphamode_premul;
- }
+ BLI_join_dirfile(name, sizeof(name), seq->strip->dir, s_elem->name);
+ BLI_path_abs(name, BKE_main_blendfile_path_from_global());
- if (!s_elem) {
- /* don't do anything */
+ /* Try to get a proxy image. */
+ ibuf = seq_proxy_fetch(context, seq, cfra);
+ if (ibuf != NULL) {
+ s_elem->orig_width = ibuf->x;
+ s_elem->orig_height = ibuf->y;
+ *r_is_proxy_image = true;
+ return ibuf;
}
- else if (is_multiview) {
- const int totfiles = seq_num_files(context->scene, seq->views_format, true);
- int totviews;
- struct ImBuf **ibufs_arr;
- char prefix[FILE_MAX];
- const char *ext = NULL;
- if (totfiles > 1) {
- BKE_scene_multiview_view_prefix_get(context->scene, name, prefix, &ext);
- if (prefix[0] == '\0') {
- goto monoview_image;
- }
- }
- else {
- prefix[0] = '\0';
- }
+ /* Proxy not found, render original. */
+ const int totfiles = seq_num_files(context->scene, seq->views_format, true);
+ bool is_multiview_render = seq_image_strip_is_multiview_render(
+ context->scene, seq, totfiles, name, prefix, ext);
- totviews = BKE_scene_multiview_num_views_get(&context->scene->r);
- ibufs_arr = MEM_callocN(sizeof(ImBuf *) * totviews, "Sequence Image Views Imbufs");
+ if (is_multiview_render) {
+ int totviews = BKE_scene_multiview_num_views_get(&context->scene->r);
+ ImBuf **ibufs_arr = MEM_callocN(sizeof(ImBuf *) * totviews, "Sequence Image Views Imbufs");
for (int view_id = 0; view_id < totfiles; view_id++) {
+ ibufs_arr[view_id] = seq_render_image_strip_view(context, seq, name, prefix, ext, view_id);
+ }
- if (prefix[0] == '\0') {
- ibufs_arr[view_id] = IMB_loadiffname(name, flag, seq->strip->colorspace_settings.name);
- }
- else {
- char str[FILE_MAX];
- seq_multiview_name(context->scene, view_id, prefix, ext, str, FILE_MAX);
- ibufs_arr[view_id] = IMB_loadiffname(str, flag, seq->strip->colorspace_settings.name);
- }
-
- if (ibufs_arr[view_id]) {
- /* we don't need both (speed reasons)! */
- if (ibufs_arr[view_id]->rect_float && ibufs_arr[view_id]->rect) {
- imb_freerectImBuf(ibufs_arr[view_id]);
- }
- }
+ if (ibufs_arr[0] == NULL) {
+ return NULL;
}
- if (seq->views_format == R_IMF_VIEWS_STEREO_3D && ibufs_arr[0]) {
+ if (seq->views_format == R_IMF_VIEWS_STEREO_3D) {
IMB_ImBufFromStereo3d(seq->stereo3d_format, ibufs_arr[0], &ibufs_arr[0], &ibufs_arr[1]);
}
for (int view_id = 0; view_id < totviews; view_id++) {
- if (ibufs_arr[view_id]) {
- SeqRenderData localcontext = *context;
- localcontext.view_id = view_id;
-
- /* all sequencer color is done in SRGB space, linear gives odd crossfades */
- BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibufs_arr[view_id], false);
+ SeqRenderData localcontext = *context;
+ localcontext.view_id = view_id;
- if (view_id != context->view_id) {
- ibufs_arr[view_id] = seq_render_preprocess_ibuf(
- &localcontext, seq, ibufs_arr[view_id], cfra, clock(), true, false, false);
- }
+ if (view_id != context->view_id) {
+ ibufs_arr[view_id] = seq_render_preprocess_ibuf(
+ &localcontext, seq, ibufs_arr[view_id], cfra, clock(), true, false, false);
}
}
- /* return the original requested ImBuf */
+ /* Return the original requested ImBuf. */
ibuf = ibufs_arr[context->view_id];
- if (ibuf) {
- s_elem->orig_width = ibufs_arr[0]->x;
- s_elem->orig_height = ibufs_arr[0]->y;
- }
- /* "remove" the others (decrease their refcount) */
+ /* Remove the others (decrease their refcount). */
for (int view_id = 0; view_id < totviews; view_id++) {
if (ibufs_arr[view_id] != ibuf) {
IMB_freeImBuf(ibufs_arr[view_id]);
@@ -3121,116 +3114,114 @@ static ImBuf *seq_render_image_strip(const SeqRenderData *context,
MEM_freeN(ibufs_arr);
}
else {
- monoview_image:
- if ((ibuf = IMB_loadiffname(name, flag, seq->strip->colorspace_settings.name))) {
- /* we don't need both (speed reasons)! */
- if (ibuf->rect_float && ibuf->rect) {
- imb_freerectImBuf(ibuf);
- }
-
- /* all sequencer color is done in SRGB space, linear gives odd crossfades */
- BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibuf, false);
+ ibuf = seq_render_image_strip_view(context, seq, name, prefix, ext, context->view_id);
+ }
- s_elem->orig_width = ibuf->x;
- s_elem->orig_height = ibuf->y;
- }
+ if (ibuf == NULL) {
+ return NULL;
}
+ s_elem->orig_width = ibuf->x;
+ s_elem->orig_height = ibuf->y;
+
return ibuf;
}
-static ImBuf *seq_render_movie_strip(const SeqRenderData *context,
- Sequence *seq,
- float nr,
- float cfra)
+/**
+ * Render individual view for multi-view or single (default view) for mono-view.
+ */
+static ImBuf *seq_render_movie_strip_view(const SeqRenderData *context,
+ Sequence *seq,
+ float nr,
+ StripAnim *sanim,
+ bool *r_is_proxy_image)
{
ImBuf *ibuf = NULL;
- StripAnim *sanim;
+ IMB_Proxy_Size psize = seq_rendersize_to_proxysize(context->preview_render_size);
- bool is_multiview = (seq->flag & SEQ_USE_VIEWS) != 0 &&
- (context->scene->r.scemode & R_MULTIVIEW) != 0;
+ IMB_anim_set_preseek(sanim->anim, seq->anim_preseek);
- IMB_Proxy_Size psize = seq_rendersize_to_proxysize(context->preview_render_size);
+ if (seq_can_use_proxy(seq, psize)) {
+ ibuf = IMB_anim_absolute(sanim->anim,
+ nr + seq->anim_startofs,
+ seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN,
+ psize);
+ if (ibuf != NULL) {
+ *r_is_proxy_image = true;
+ }
+ }
- if ((seq->flag & SEQ_USE_PROXY) == 0) {
- psize = IMB_PROXY_NONE;
+ /* Fetching for requested proxy size failed, try fetching the original instead. */
+ if (ibuf == NULL) {
+ ibuf = IMB_anim_absolute(sanim->anim,
+ nr + seq->anim_startofs,
+ seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN,
+ IMB_PROXY_NONE);
+ }
+ if (ibuf == NULL) {
+ return NULL;
}
- /* load all the videos */
- seq_open_anim_file(context->scene, seq, false);
- if (is_multiview) {
- ImBuf **ibuf_arr;
- const int totfiles = seq_num_files(context->scene, seq->views_format, true);
- int totviews;
- int ibuf_view_id;
+ BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibuf, false);
- if (totfiles != BLI_listbase_count_at_most(&seq->anims, totfiles + 1)) {
- goto monoview_movie;
- }
+ /* We don't need both (speed reasons)! */
+ if (ibuf->rect_float != NULL && ibuf->rect != NULL) {
+ imb_freerectImBuf(ibuf);
+ }
+
+ return ibuf;
+}
+
+static ImBuf *seq_render_movie_strip(
+ const SeqRenderData *context, Sequence *seq, float nr, float cfra, bool *r_is_proxy_image)
+{
+ /* Load all the videos. */
+ seq_open_anim_file(context->scene, seq, false);
+
+ ImBuf *ibuf = NULL;
+ StripAnim *sanim = seq->anims.first;
+ const int totfiles = seq_num_files(context->scene, seq->views_format, true);
+ bool is_multiview_render = (seq->flag & SEQ_USE_VIEWS) != 0 &&
+ (context->scene->r.scemode & R_MULTIVIEW) != 0 &&
+ BLI_listbase_count_at_most(&seq->anims, totfiles + 1) == totfiles;
- totviews = BKE_scene_multiview_num_views_get(&context->scene->r);
+ if (is_multiview_render) {
+ ImBuf **ibuf_arr;
+ int totviews = BKE_scene_multiview_num_views_get(&context->scene->r);
ibuf_arr = MEM_callocN(sizeof(ImBuf *) * totviews, "Sequence Image Views Imbufs");
+ int ibuf_view_id;
for (ibuf_view_id = 0, sanim = seq->anims.first; sanim; sanim = sanim->next, ibuf_view_id++) {
if (sanim->anim) {
- IMB_anim_set_preseek(sanim->anim, seq->anim_preseek);
-
- ibuf_arr[ibuf_view_id] = IMB_anim_absolute(sanim->anim,
- nr + seq->anim_startofs,
- seq->strip->proxy ? seq->strip->proxy->tc :
- IMB_TC_RECORD_RUN,
- psize);
-
- /* fetching for requested proxy size failed, try fetching the original instead */
- if (!ibuf_arr[ibuf_view_id] && psize != IMB_PROXY_NONE) {
- ibuf_arr[ibuf_view_id] = IMB_anim_absolute(sanim->anim,
- nr + seq->anim_startofs,
- seq->strip->proxy ? seq->strip->proxy->tc :
- IMB_TC_RECORD_RUN,
- IMB_PROXY_NONE);
- }
- if (ibuf_arr[ibuf_view_id]) {
- /* we don't need both (speed reasons)! */
- if (ibuf_arr[ibuf_view_id]->rect_float && ibuf_arr[ibuf_view_id]->rect) {
- imb_freerectImBuf(ibuf_arr[ibuf_view_id]);
- }
- }
+ ibuf_arr[ibuf_view_id] = seq_render_movie_strip_view(
+ context, seq, nr, sanim, r_is_proxy_image);
}
}
if (seq->views_format == R_IMF_VIEWS_STEREO_3D) {
- if (ibuf_arr[0]) {
- IMB_ImBufFromStereo3d(seq->stereo3d_format, ibuf_arr[0], &ibuf_arr[0], &ibuf_arr[1]);
- }
- else {
- /* probably proxy hasn't been created yet */
+ if (ibuf_arr[0] == NULL) {
+ /* Probably proxy hasn't been created yet. */
MEM_freeN(ibuf_arr);
return NULL;
}
+
+ IMB_ImBufFromStereo3d(seq->stereo3d_format, ibuf_arr[0], &ibuf_arr[0], &ibuf_arr[1]);
}
for (int view_id = 0; view_id < totviews; view_id++) {
SeqRenderData localcontext = *context;
localcontext.view_id = view_id;
- if (ibuf_arr[view_id]) {
- /* all sequencer color is done in SRGB space, linear gives odd crossfades */
- BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibuf_arr[view_id], false);
- }
if (view_id != context->view_id) {
ibuf_arr[view_id] = seq_render_preprocess_ibuf(
&localcontext, seq, ibuf_arr[view_id], cfra, clock(), true, false, false);
}
}
- /* return the original requested ImBuf */
+ /* Return the original requested ImBuf. */
ibuf = ibuf_arr[context->view_id];
- if (ibuf) {
- seq->strip->stripdata->orig_width = ibuf->x;
- seq->strip->stripdata->orig_height = ibuf->y;
- }
- /* "remove" the others (decrease their refcount) */
+ /* Remove the others (decrease their refcount). */
for (int view_id = 0; view_id < totviews; view_id++) {
if (ibuf_arr[view_id] != ibuf) {
IMB_freeImBuf(ibuf_arr[view_id]);
@@ -3240,44 +3231,39 @@ static ImBuf *seq_render_movie_strip(const SeqRenderData *context,
MEM_freeN(ibuf_arr);
}
else {
- monoview_movie:
- sanim = seq->anims.first;
- if (sanim && sanim->anim) {
- IMB_anim_set_preseek(sanim->anim, seq->anim_preseek);
-
- ibuf = IMB_anim_absolute(sanim->anim,
- nr + seq->anim_startofs,
- seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN,
- psize);
-
- /* fetching for requested proxy size failed, try fetching the original instead */
- if (!ibuf && psize != IMB_PROXY_NONE) {
- ibuf = IMB_anim_absolute(sanim->anim,
- nr + seq->anim_startofs,
- seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN,
- IMB_PROXY_NONE);
- }
- if (ibuf) {
- BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibuf, false);
+ ibuf = seq_render_movie_strip_view(context, seq, nr, sanim, r_is_proxy_image);
+ }
- /* we don't need both (speed reasons)! */
- if (ibuf->rect_float && ibuf->rect) {
- imb_freerectImBuf(ibuf);
- }
+ if (ibuf == NULL) {
+ return NULL;
+ }
- seq->strip->stripdata->orig_width = ibuf->x;
- seq->strip->stripdata->orig_height = ibuf->y;
- }
- }
+ seq->strip->stripdata->orig_width = ibuf->x;
+ seq->strip->stripdata->orig_height = ibuf->y;
+
+ return ibuf;
+}
+
+static ImBuf *seq_get_movieclip_ibuf(Sequence *seq, MovieClipUser user)
+{
+ ImBuf *ibuf = NULL;
+ float tloc[2], tscale, tangle;
+ if (seq->clip_flag & SEQ_MOVIECLIP_RENDER_STABILIZED) {
+ ibuf = BKE_movieclip_get_stable_ibuf(seq->clip, &user, tloc, &tscale, &tangle, 0);
+ }
+ else {
+ ibuf = BKE_movieclip_get_ibuf_flag(seq->clip, &user, seq->clip->flag, MOVIECLIP_CACHE_SKIP);
}
return ibuf;
}
-static ImBuf *seq_render_movieclip_strip(const SeqRenderData *context, Sequence *seq, float nr)
+static ImBuf *seq_render_movieclip_strip(const SeqRenderData *context,
+ Sequence *seq,
+ float nr,
+ bool *r_is_proxy_image)
{
ImBuf *ibuf = NULL;
MovieClipUser user;
- float tloc[2], tscale, tangle;
IMB_Proxy_Size psize = seq_rendersize_to_proxysize(context->preview_render_size);
if (!seq->clip) {
@@ -3288,8 +3274,6 @@ static ImBuf *seq_render_movieclip_strip(const SeqRenderData *context, Sequence
BKE_movieclip_user_set_frame(&user, nr + seq->anim_startofs + seq->clip->start_frame);
- user.render_flag |= MCLIP_PROXY_RENDER_USE_FALLBACK_RENDER;
-
user.render_size = MCLIP_PROXY_RENDER_SIZE_FULL;
switch (psize) {
case IMB_PROXY_NONE:
@@ -3313,11 +3297,17 @@ static ImBuf *seq_render_movieclip_strip(const SeqRenderData *context, Sequence
user.render_flag |= MCLIP_PROXY_RENDER_UNDISTORT;
}
- if (seq->clip_flag & SEQ_MOVIECLIP_RENDER_STABILIZED) {
- ibuf = BKE_movieclip_get_stable_ibuf(seq->clip, &user, tloc, &tscale, &tangle, 0);
+ /* Try to get a proxy image. */
+ ibuf = seq_get_movieclip_ibuf(seq, user);
+
+ if (ibuf != NULL && psize != IMB_PROXY_NONE) {
+ *r_is_proxy_image = true;
}
- else {
- ibuf = BKE_movieclip_get_ibuf_flag(seq->clip, &user, seq->clip->flag, MOVIECLIP_CACHE_SKIP);
+
+ /* If proxy is not found, grab full-size frame. */
+ if (ibuf == NULL) {
+ user.render_flag |= MCLIP_PROXY_RENDER_USE_FALLBACK_RENDER;
+ ibuf = seq_get_movieclip_ibuf(seq, user);
}
return ibuf;
@@ -3695,7 +3685,8 @@ static ImBuf *do_render_strip_seqbase(const SeqRenderData *context,
static ImBuf *do_render_strip_uncached(const SeqRenderData *context,
SeqRenderState *state,
Sequence *seq,
- float cfra)
+ float cfra,
+ bool *r_is_proxy_image)
{
ImBuf *ibuf = NULL;
float nr = BKE_sequencer_give_stripelem_index(seq, cfra);
@@ -3747,17 +3738,17 @@ static ImBuf *do_render_strip_uncached(const SeqRenderData *context,
}
case SEQ_TYPE_IMAGE: {
- ibuf = seq_render_image_strip(context, seq, nr, cfra);
+ ibuf = seq_render_image_strip(context, seq, nr, cfra, r_is_proxy_image);
break;
}
case SEQ_TYPE_MOVIE: {
- ibuf = seq_render_movie_strip(context, seq, nr, cfra);
+ ibuf = seq_render_movie_strip(context, seq, nr, cfra, r_is_proxy_image);
break;
}
case SEQ_TYPE_MOVIECLIP: {
- ibuf = seq_render_movieclip_strip(context, seq, nr);
+ ibuf = seq_render_movieclip_strip(context, seq, nr, r_is_proxy_image);
if (ibuf) {
/* duplicate frame so movie cache wouldn't be confused by sequencer's stuff */
@@ -3852,40 +3843,26 @@ static ImBuf *seq_render_strip(const SeqRenderData *context,
clock_t begin = seq_estimate_render_cost_begin();
ibuf = BKE_sequencer_cache_get(context, seq, cfra, SEQ_CACHE_STORE_PREPROCESSED, false);
+ if (ibuf != NULL) {
+ return ibuf;
+ }
+ ibuf = BKE_sequencer_cache_get(context, seq, cfra, SEQ_CACHE_STORE_RAW, false);
if (ibuf == NULL) {
- ibuf = BKE_sequencer_cache_get(context, seq, cfra, SEQ_CACHE_STORE_RAW, false);
- if (ibuf == NULL) {
- /* MOVIECLIPs have their own proxy management */
- if (seq->type != SEQ_TYPE_MOVIECLIP) {
- ibuf = seq_proxy_fetch(context, seq, cfra);
- is_proxy_image = (ibuf != NULL);
- }
-
- if (ibuf == NULL) {
- ibuf = do_render_strip_uncached(context, state, seq, cfra);
- }
-
- if (ibuf) {
- if (ELEM(seq->type, SEQ_TYPE_MOVIE, SEQ_TYPE_MOVIECLIP)) {
- is_proxy_image = seq_rendersize_to_proxysize(context->preview_render_size) !=
- IMB_PROXY_NONE;
- }
- }
- }
-
- if (ibuf) {
- use_preprocess = BKE_sequencer_input_have_to_preprocess(context, seq, cfra);
- }
-
- if (ibuf == NULL) {
- ibuf = IMB_allocImBuf(context->rectx, context->recty, 32, IB_rect);
- sequencer_imbuf_assign_spaces(context->scene, ibuf);
- }
+ ibuf = do_render_strip_uncached(context, state, seq, cfra, &is_proxy_image);
+ }
+ if (ibuf) {
+ use_preprocess = BKE_sequencer_input_have_to_preprocess(context, seq, cfra);
ibuf = seq_render_preprocess_ibuf(
context, seq, ibuf, cfra, begin, use_preprocess, is_proxy_image, is_preprocessed);
}
+
+ if (ibuf == NULL) {
+ ibuf = IMB_allocImBuf(context->rectx, context->recty, 32, IB_rect);
+ sequencer_imbuf_assign_spaces(context->scene, ibuf);
+ }
+
return ibuf;
}
@@ -5052,7 +5029,7 @@ int BKE_sequence_swap(Sequence *seq_a, Sequence *seq_b, const char **error_str)
return 1;
}
-/* prefix + [" + escaped_name + "] + \0 */
+/* r_prefix + [" + escaped_name + "] + \0 */
#define SEQ_RNAPATH_MAXSTR ((30 + 2 + (SEQ_NAME_MAXSTR * 2) + 2) + 1)
static size_t sequencer_rna_path_prefix(char str[SEQ_RNAPATH_MAXSTR], const char *name)
diff --git a/source/blender/blenkernel/intern/shader_fx.c b/source/blender/blenkernel/intern/shader_fx.c
index 0ad61de1ff2..2923298c5d5 100644
--- a/source/blender/blenkernel/intern/shader_fx.c
+++ b/source/blender/blenkernel/intern/shader_fx.c
@@ -82,8 +82,9 @@ ShaderFxData *BKE_shaderfx_new(int type)
BLI_strncpy(fx->name, DATA_(fxi->name), sizeof(fx->name));
fx->type = type;
- fx->mode = eShaderFxMode_Realtime | eShaderFxMode_Render | eShaderFxMode_Expanded;
+ fx->mode = eShaderFxMode_Realtime | eShaderFxMode_Render;
fx->flag = eShaderFxFlag_OverrideLibrary_Local;
+ fx->ui_expand_flag = 1; /* Expand only the parent panel by default. */
if (fxi->flags & eShaderFxTypeFlag_EnableInEditmode) {
fx->mode |= eShaderFxMode_Editmode;
@@ -156,7 +157,7 @@ bool BKE_shaderfx_depends_ontime(ShaderFxData *fx)
const ShaderFxTypeInfo *BKE_shaderfx_get_info(ShaderFxType type)
{
/* type unsigned, no need to check < 0 */
- if (type < NUM_SHADER_FX_TYPES && shader_fx_types[type]->name[0] != '\0') {
+ if (type < NUM_SHADER_FX_TYPES && type > 0 && shader_fx_types[type]->name[0] != '\0') {
return shader_fx_types[type];
}
else {
@@ -164,6 +165,20 @@ const ShaderFxTypeInfo *BKE_shaderfx_get_info(ShaderFxType type)
}
}
+/**
+ * Get an effect's panel type, which was defined in the #panelRegister callback.
+ *
+ * \note ShaderFx panel types are assumed to be named with the struct name field concatenated to
+ * the defined prefix.
+ */
+void BKE_shaderfxType_panel_id(ShaderFxType type, char *r_idname)
+{
+ const ShaderFxTypeInfo *fxi = BKE_shaderfx_get_info(type);
+
+ strcpy(r_idname, SHADERFX_TYPE_PANEL_PREFIX);
+ strcat(r_idname, fxi->name);
+}
+
void BKE_shaderfx_copydata_generic(const ShaderFxData *fx_src, ShaderFxData *fx_dst)
{
const ShaderFxTypeInfo *fxi = BKE_shaderfx_get_info(fx_src->type);
@@ -198,6 +213,7 @@ void BKE_shaderfx_copydata_ex(ShaderFxData *fx, ShaderFxData *target, const int
target->mode = fx->mode;
target->flag = fx->flag;
+ target->ui_expand_flag = fx->ui_expand_flag;
if (fxi->copyData) {
fxi->copyData(fx, target);
diff --git a/source/blender/blenkernel/intern/simulation.cc b/source/blender/blenkernel/intern/simulation.cc
index 20a23ab8b38..eef848aff72 100644
--- a/source/blender/blenkernel/intern/simulation.cc
+++ b/source/blender/blenkernel/intern/simulation.cc
@@ -54,10 +54,6 @@
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
-using blender::float3;
-using blender::MutableSpan;
-using blender::Span;
-
static void simulation_init_data(ID *id)
{
Simulation *simulation = (Simulation *)id;
@@ -168,6 +164,9 @@ void *BKE_simulation_add(Main *bmain, const char *name)
return simulation;
}
+namespace blender {
+namespace bke {
+
static MutableSpan<float3> get_particle_positions(ParticleSimulationState *state)
{
return MutableSpan<float3>(
@@ -197,7 +196,7 @@ static void copy_particle_state_to_cow(ParticleSimulationState *state_orig,
state_cow->tot_particles = state_orig->tot_particles;
}
-void BKE_simulation_data_update(Depsgraph *depsgraph, Scene *scene, Simulation *simulation)
+static void simulation_data_update(Depsgraph *depsgraph, Scene *scene, Simulation *simulation)
{
int current_frame = scene->r.cfra;
@@ -259,3 +258,11 @@ void BKE_simulation_data_update(Depsgraph *depsgraph, Scene *scene, Simulation *
copy_particle_state_to_cow(state_orig, state_cow);
}
}
+
+} // namespace bke
+} // namespace blender
+
+void BKE_simulation_data_update(Depsgraph *depsgraph, Scene *scene, Simulation *simulation)
+{
+ blender::bke::simulation_data_update(depsgraph, scene, simulation);
+}
diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c
index 68d0822a223..9c7abbdf876 100644
--- a/source/blender/blenkernel/intern/softbody.c
+++ b/source/blender/blenkernel/intern/softbody.c
@@ -1479,7 +1479,8 @@ static void _scan_for_ext_spring_forces(
mid_v3_v3v3(pos, sb->bpoint[bs->v1].pos, sb->bpoint[bs->v2].pos);
mid_v3_v3v3(vel, sb->bpoint[bs->v1].vec, sb->bpoint[bs->v2].vec);
pd_point_from_soft(scene, pos, vel, -1, &epoint);
- BKE_effectors_apply(effectors, NULL, sb->effector_weights, &epoint, force, speed);
+ BKE_effectors_apply(
+ effectors, NULL, sb->effector_weights, &epoint, force, NULL, speed);
mul_v3_fl(speed, windfactor);
add_v3_v3(vel, speed);
@@ -2107,7 +2108,7 @@ static int _softbody_calc_forces_slice_in_a_thread(Scene *scene,
float eval_sb_fric_force_scale = sb_fric_force_scale(ob);
pd_point_from_soft(scene, bp->pos, bp->vec, sb->bpoint - bp, &epoint);
- BKE_effectors_apply(effectors, NULL, sb->effector_weights, &epoint, force, speed);
+ BKE_effectors_apply(effectors, NULL, sb->effector_weights, &epoint, force, NULL, speed);
/* apply forcefield*/
mul_v3_fl(force, fieldfactor * eval_sb_fric_force_scale);
diff --git a/source/blender/blenkernel/intern/sound.c b/source/blender/blenkernel/intern/sound.c
index e8f31594cc0..a293bc55073 100644
--- a/source/blender/blenkernel/intern/sound.c
+++ b/source/blender/blenkernel/intern/sound.c
@@ -172,7 +172,7 @@ bSound *BKE_sound_new_file(Main *bmain, const char *filepath)
BLI_path_abs(str, path);
sound = BKE_libblock_alloc(bmain, ID_SO, BLI_path_basename(filepath), 0);
- BLI_strncpy(sound->name, filepath, FILE_MAX);
+ BLI_strncpy(sound->filepath, filepath, FILE_MAX);
/* sound->type = SOUND_TYPE_FILE; */ /* XXX unused currently */
sound->spinlock = MEM_mallocN(sizeof(SpinLock), "sound_spinlock");
@@ -193,7 +193,7 @@ bSound *BKE_sound_new_file_exists_ex(Main *bmain, const char *filepath, bool *r_
/* first search an identical filepath */
for (sound = bmain->sounds.first; sound; sound = sound->id.next) {
- BLI_strncpy(strtest, sound->name, sizeof(sound->name));
+ BLI_strncpy(strtest, sound->filepath, sizeof(sound->filepath));
BLI_path_abs(strtest, ID_BLEND_PATH(bmain, &sound->id));
if (BLI_path_cmp(strtest, str) == 0) {
@@ -452,8 +452,8 @@ static void sound_load_audio(Main *bmain, bSound *sound, bool free_waveform)
/* load sound */
PackedFile *pf = sound->packedfile;
- /* don't modify soundact->sound->name, only change a copy */
- BLI_strncpy(fullpath, sound->name, sizeof(fullpath));
+ /* don't modify soundact->sound->filepath, only change a copy */
+ BLI_strncpy(fullpath, sound->filepath, sizeof(fullpath));
BLI_path_abs(fullpath, ID_BLEND_PATH(bmain, &sound->id));
/* but we need a packed file then */
diff --git a/source/blender/blenkernel/intern/studiolight.c b/source/blender/blenkernel/intern/studiolight.c
index 4892e8d6ede..8455b60c894 100644
--- a/source/blender/blenkernel/intern/studiolight.c
+++ b/source/blender/blenkernel/intern/studiolight.c
@@ -1426,9 +1426,9 @@ void BKE_studiolight_init(void)
BLI_addtail(&studiolights, sl);
- /* go over the preset folder and add a studiolight for every image with its path */
- /* for portable installs (where USER and SYSTEM paths are the same),
- * only go over LOCAL datafiles once */
+ /* Go over the preset folder and add a studio-light for every image with its path. */
+ /* For portable installs (where USER and SYSTEM paths are the same),
+ * only go over LOCAL data-files once. */
/* Also reserve icon space for it. */
if (!BKE_appdir_app_is_portable_install()) {
studiolight_add_files_from_datafolder(BLENDER_USER_DATAFILES,
diff --git a/source/blender/blenkernel/intern/subdiv_ccg.c b/source/blender/blenkernel/intern/subdiv_ccg.c
index d5d5530c1ce..a1e218390c3 100644
--- a/source/blender/blenkernel/intern/subdiv_ccg.c
+++ b/source/blender/blenkernel/intern/subdiv_ccg.c
@@ -655,6 +655,7 @@ void BKE_subdiv_ccg_destroy(SubdivCCG *subdiv_ccg)
MEM_SAFE_FREE(adjacent_vertex->corner_coords);
}
MEM_SAFE_FREE(subdiv_ccg->adjacent_vertices);
+ MEM_SAFE_FREE(subdiv_ccg->cache_.start_face_grid_index);
MEM_freeN(subdiv_ccg);
}
@@ -1797,13 +1798,40 @@ void BKE_subdiv_ccg_neighbor_coords_get(const SubdivCCG *subdiv_ccg,
int BKE_subdiv_ccg_grid_to_face_index(const SubdivCCG *subdiv_ccg, const int grid_index)
{
- // Subdiv *subdiv = subdiv_ccg->subdiv; /* UNUSED */
- // OpenSubdiv_TopologyRefiner *topology_refiner = subdiv->topology_refiner; /* UNUSED */
- SubdivCCGFace *face = subdiv_ccg->grid_faces[grid_index];
-
- // const int face_grid_index = grid_index - face->start_grid_index; /* UNUSED */
+ const SubdivCCGFace *face = subdiv_ccg->grid_faces[grid_index];
const int face_index = face - subdiv_ccg->faces;
return face_index;
}
+const int *BKE_subdiv_ccg_start_face_grid_index_ensure(SubdivCCG *subdiv_ccg)
+{
+ if (subdiv_ccg->cache_.start_face_grid_index == NULL) {
+ const Subdiv *subdiv = subdiv_ccg->subdiv;
+ OpenSubdiv_TopologyRefiner *topology_refiner = subdiv->topology_refiner;
+ if (topology_refiner == NULL) {
+ return NULL;
+ }
+
+ const int num_coarse_faces = topology_refiner->getNumFaces(topology_refiner);
+
+ subdiv_ccg->cache_.start_face_grid_index = MEM_malloc_arrayN(
+ sizeof(int), num_coarse_faces, "start_face_grid_index");
+
+ int start_grid_index = 0;
+ for (int face_index = 0; face_index < num_coarse_faces; face_index++) {
+ const int num_face_grids = topology_refiner->getNumFaceVertices(topology_refiner,
+ face_index);
+ subdiv_ccg->cache_.start_face_grid_index[face_index] = start_grid_index;
+ start_grid_index += num_face_grids;
+ }
+ }
+
+ return subdiv_ccg->cache_.start_face_grid_index;
+}
+
+const int *BKE_subdiv_ccg_start_face_grid_index_get(const SubdivCCG *subdiv_ccg)
+{
+ return subdiv_ccg->cache_.start_face_grid_index;
+}
+
/** \} */
diff --git a/source/blender/blenkernel/intern/text.c b/source/blender/blenkernel/intern/text.c
index bce2266f3e9..5f85e1a1664 100644
--- a/source/blender/blenkernel/intern/text.c
+++ b/source/blender/blenkernel/intern/text.c
@@ -59,34 +59,6 @@
# include "BPY_extern.h"
#endif
-/*
- * How Texts should work
- * --
- * A text should relate to a file as follows -
- * (Text *)->name should be the place where the
- * file will or has been saved.
- *
- * (Text *)->flags has the following bits
- * TXT_ISDIRTY - should always be set if the file in mem. differs from
- * the file on disk, or if there is no file on disk.
- * TXT_ISMEM - should always be set if the Text has not been mapped to
- * a file, in which case (Text *)->name may be NULL or garbage.
- * TXT_ISEXT - should always be set if the Text is not to be written into
- * the .blend
- * TXT_ISSCRIPT - should be set if the user has designated the text
- * as a script. (NEW: this was unused, but now it is needed by
- * space handler script links (see header_view3d.c, for example)
- *
- * ->>> see also: /makesdna/DNA_text_types.h
- *
- * Display
- * --
- *
- * The st->top determines at what line the top of the text is displayed.
- * If the user moves the cursor the st containing that cursor should
- * be popped ... other st's retain their own top location.
- */
-
/* -------------------------------------------------------------------- */
/** \name Prototypes
* \{ */
@@ -110,9 +82,8 @@ static void text_init_data(ID *id)
BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(text, id));
- text->name = NULL;
+ text->filepath = NULL;
- text->nlines = 1;
text->flags = TXT_ISDIRTY | TXT_ISMEM;
if ((U.flag & USER_TXT_TABSTOSPACES_DISABLE) == 0) {
text->flags |= TXT_TABSTOSPACES;
@@ -157,8 +128,8 @@ static void text_copy_data(Main *UNUSED(bmain),
const Text *text_src = (Text *)id_src;
/* File name can be NULL. */
- if (text_src->name) {
- text_dst->name = BLI_strdup(text_src->name);
+ if (text_src->filepath) {
+ text_dst->filepath = BLI_strdup(text_src->filepath);
}
text_dst->flags |= TXT_ISDIRTY;
@@ -190,7 +161,7 @@ static void text_free_data(ID *id)
BKE_text_free_lines(text);
- MEM_SAFE_FREE(text->name);
+ MEM_SAFE_FREE(text->filepath);
#ifdef WITH_PYTHON
BPY_text_free_code(text);
#endif
@@ -316,12 +287,12 @@ static void cleanup_textline(TextLine *tl)
*/
static void text_from_buf(Text *text, const unsigned char *buffer, const int len)
{
- int i, llen;
+ int i, llen, lines_count;
BLI_assert(BLI_listbase_is_empty(&text->lines));
- text->nlines = 0;
llen = 0;
+ lines_count = 0;
for (i = 0; i < len; i++) {
if (buffer[i] == '\n') {
TextLine *tmp;
@@ -339,7 +310,7 @@ static void text_from_buf(Text *text, const unsigned char *buffer, const int len
cleanup_textline(tmp);
BLI_addtail(&text->lines, tmp);
- text->nlines++;
+ lines_count += 1;
llen = 0;
continue;
@@ -353,7 +324,7 @@ static void text_from_buf(Text *text, const unsigned char *buffer, const int len
* - file is empty. in this case new line is needed to start editing from.
* - last character in buffer is \n. in this case new line is needed to
* deal with newline at end of file. (see [#28087]) (sergey) */
- if (llen != 0 || text->nlines == 0 || buffer[len - 1] == '\n') {
+ if (llen != 0 || lines_count == 0 || buffer[len - 1] == '\n') {
TextLine *tmp;
tmp = (TextLine *)MEM_mallocN(sizeof(TextLine), "textline");
@@ -370,7 +341,7 @@ static void text_from_buf(Text *text, const unsigned char *buffer, const int len
cleanup_textline(tmp);
BLI_addtail(&text->lines, tmp);
- text->nlines++;
+ /* lines_count += 1; */ /* UNUSED */
}
text->curl = text->sell = text->lines.first;
@@ -384,11 +355,11 @@ bool BKE_text_reload(Text *text)
char filepath_abs[FILE_MAX];
BLI_stat_t st;
- if (!text->name) {
+ if (!text->filepath) {
return false;
}
- BLI_strncpy(filepath_abs, text->name, FILE_MAX);
+ BLI_strncpy(filepath_abs, text->filepath, FILE_MAX);
BLI_path_abs(filepath_abs, ID_BLEND_PATH_FROM_GLOBAL(&text->id));
buffer = BLI_file_read_text_as_mem(filepath_abs, 0, &buffer_len);
@@ -444,8 +415,8 @@ Text *BKE_text_load_ex(Main *bmain, const char *file, const char *relpath, const
}
if (is_internal == false) {
- ta->name = MEM_mallocN(strlen(file) + 1, "text_name");
- strcpy(ta->name, file);
+ ta->filepath = MEM_mallocN(strlen(file) + 1, "text_name");
+ strcpy(ta->filepath, file);
}
else {
ta->flags |= TXT_ISMEM | TXT_ISDIRTY;
@@ -503,11 +474,11 @@ int BKE_text_file_modified_check(Text *text)
int result;
char file[FILE_MAX];
- if (!text->name) {
+ if (!text->filepath) {
return 0;
}
- BLI_strncpy(file, text->name, FILE_MAX);
+ BLI_strncpy(file, text->filepath, FILE_MAX);
BLI_path_abs(file, ID_BLEND_PATH_FROM_GLOBAL(&text->id));
if (!BLI_exists(file)) {
@@ -537,11 +508,11 @@ void BKE_text_file_modified_ignore(Text *text)
int result;
char file[FILE_MAX];
- if (!text->name) {
+ if (!text->filepath) {
return;
}
- BLI_strncpy(file, text->name, FILE_MAX);
+ BLI_strncpy(file, text->filepath, FILE_MAX);
BLI_path_abs(file, ID_BLEND_PATH_FROM_GLOBAL(&text->id));
if (!BLI_exists(file)) {
@@ -738,6 +709,10 @@ bool txt_cursor_is_line_end(Text *text)
/* -------------------------------------------------------------------- */
/** \name Cursor Movement Functions
+ *
+ * \note If the user moves the cursor the space containing that cursor should be popped
+ * See #txt_pop_first, #txt_pop_last
+ * Other space-types retain their own top location.
* \{ */
void txt_move_up(Text *text, const bool sel)
@@ -1309,6 +1284,8 @@ void txt_sel_set(Text *text, int startl, int startc, int endl, int endc)
text->selc = BLI_str_utf8_offset_from_index(tol->line, endc);
}
+/** \} */
+
/* -------------------------------------------------------------------- */
/** \name Buffer Conversion for Undo/Redo
*
@@ -2105,8 +2082,6 @@ static void txt_select_prefix(Text *text, const char *add, bool skip_blank_lines
/**
* Generic un-prefix operation, use for comment & indent.
*
- * \param r_line_index_mask: List of lines that are already at indent level 0,
- * to store them later into the undo buffer.
* \param require_all: When true, all non-empty lines must have this prefix.
* Needed for comments where we might want to un-comment a block which contains some comments.
*
diff --git a/source/blender/blenlib/BLI_array.hh b/source/blender/blenlib/BLI_array.hh
index 07155439170..25267bc65d6 100644
--- a/source/blender/blenlib/BLI_array.hh
+++ b/source/blender/blenlib/BLI_array.hh
@@ -27,8 +27,7 @@
* blender::Array should usually be used instead of blender::Vector whenever the number of elements
* is known at construction time. Note however, that blender::Array will default construct all
* elements when initialized with the size-constructor. For trivial types, this does nothing. In
- * all other cases, this adds overhead. If this becomes a problem, a different constructor which
- * does not do default construction can be added.
+ * all other cases, this adds overhead.
*
* A main benefit of using Array over Vector is that it expresses the intent of the developer
* better. It indicates that the size of the data structure is not expected to change. Furthermore,
@@ -130,6 +129,24 @@ class Array {
uninitialized_fill_n(m_data, m_size, value);
}
+ /**
+ * Create a new array with uninitialized elements. The caller is responsible for constructing the
+ * elements. Moving, copying or destructing an Array with uninitialized elements invokes
+ * undefined behavior.
+ *
+ * This should be used very rarely. Note, that the normal size-constructor also does not
+ * initialize the elements when T is trivially constructible. Therefore, it only makes sense to
+ * use this with non trivially constructible types.
+ *
+ * Usage:
+ * Array<std::string> my_strings(10, NoInitialization());
+ */
+ Array(uint size, NoInitialization)
+ {
+ m_size = size;
+ m_data = this->get_buffer_for_size(size);
+ }
+
Array(const Array &other) : m_allocator(other.m_allocator)
{
m_size = other.size();
diff --git a/source/blender/blenlib/BLI_color.hh b/source/blender/blenlib/BLI_color.hh
index 432459c9998..37f74edcf4c 100644
--- a/source/blender/blenlib/BLI_color.hh
+++ b/source/blender/blenlib/BLI_color.hh
@@ -28,6 +28,10 @@ struct Color4f {
Color4f() = default;
+ Color4f(const float *rgba) : r(rgba[0]), g(rgba[1]), b(rgba[2]), a(rgba[3])
+ {
+ }
+
Color4f(float r, float g, float b, float a) : r(r), g(g), b(b), a(a)
{
}
diff --git a/source/blender/blenlib/BLI_dot_export.hh b/source/blender/blenlib/BLI_dot_export.hh
index 67af4391a55..b496c00a75d 100644
--- a/source/blender/blenlib/BLI_dot_export.hh
+++ b/source/blender/blenlib/BLI_dot_export.hh
@@ -25,17 +25,17 @@
*/
#include "BLI_map.hh"
-#include "BLI_optional.hh"
#include "BLI_set.hh"
#include "BLI_utility_mixins.hh"
#include "BLI_vector.hh"
#include "BLI_dot_export_attribute_enums.hh"
+#include <optional>
#include <sstream>
namespace blender {
-namespace DotExport {
+namespace dot {
class Graph;
class DirectedGraph;
@@ -197,10 +197,10 @@ class DirectedGraph final : public Graph {
class NodePort {
private:
Node *m_node;
- Optional<std::string> m_port_name;
+ std::optional<std::string> m_port_name;
public:
- NodePort(Node &node, Optional<std::string> port_name = {})
+ NodePort(Node &node, std::optional<std::string> port_name = {})
: m_node(&node), m_port_name(std::move(port_name))
{
}
@@ -283,7 +283,7 @@ class NodeWithSocketsRef {
}
};
-} // namespace DotExport
+} // namespace dot
} // namespace blender
#endif /* __BLI_DOT_EXPORT_HH__ */
diff --git a/source/blender/blenlib/BLI_dot_export_attribute_enums.hh b/source/blender/blenlib/BLI_dot_export_attribute_enums.hh
index 8fe1cda05f3..12019a49194 100644
--- a/source/blender/blenlib/BLI_dot_export_attribute_enums.hh
+++ b/source/blender/blenlib/BLI_dot_export_attribute_enums.hh
@@ -20,7 +20,7 @@
#include "BLI_string_ref.hh"
namespace blender {
-namespace DotExport {
+namespace dot {
enum class Attr_rankdir {
LeftToRight,
@@ -119,7 +119,7 @@ inline StringRef dirType_to_string(Attr_dirType value)
return "";
}
-} // namespace DotExport
+} // namespace dot
} // namespace blender
#endif /* __BLI_DOT_EXPORT_ATTRIBUTE_ENUMS_HH__ */
diff --git a/source/blender/blenlib/BLI_hash.hh b/source/blender/blenlib/BLI_hash.hh
index 5490c953756..49e619ff1bc 100644
--- a/source/blender/blenlib/BLI_hash.hh
+++ b/source/blender/blenlib/BLI_hash.hh
@@ -23,7 +23,7 @@
* A specialization of `blender::DefaultHash<T>` provides a hash function for values of type T.
* This hash function is used by default in hash table implementations in blenlib.
*
- * The actual hash function is in the `operator()` method of DefaultHash<T>. The following code
+ * The actual hash function is in the `operator()` method of `DefaultHash<T>`. The following code
* computes the hash of some value using DefaultHash.
*
* T value = ...;
@@ -32,17 +32,17 @@
*
* Hash table implementations like blender::Set support heterogeneous key lookups. That means that
* one can do a lookup with a key of type A in a hash table that stores keys of type B. This is
- * commonly done when B is std::string, because the conversion from e.g. a StringRef to std::string
- * can be costly and is unnecessary. To make this work, values of type A and B that compare equal
- * have to have the same hash value. This is achieved by defining potentially multiple `operator()`
- * in a specialization of DefaultHash. All those methods have to compute the same hash for values
- * that compare equal.
+ * commonly done when B is std::string, because the conversion from e.g. a #StringRef to
+ * std::string can be costly and is unnecessary. To make this work, values of type A and B that
+ * compare equal have to have the same hash value. This is achieved by defining potentially
+ * multiple `operator()` in a specialization of #DefaultHash. All those methods have to compute the
+ * same hash for values that compare equal.
*
* The computed hash is an unsigned 32 bit integer. Ideally, the hash function would generate
* uniformly random hash values for a set of keys. However, in many cases trivial hash functions
* are faster and produce a good enough distribution. In general it is better when more information
* is in the lower bits of the hash. By choosing a good probing strategy, the effects of a bad hash
- * function are less noticable though. In this context a good probing strategy is one that takes
+ * function are less noticeable though. In this context a good probing strategy is one that takes
* all bits of the hash into account eventually. One has to check on a case by case basis to see if
* a better but more expensive or trivial hash function works better.
*
@@ -50,11 +50,11 @@
*
* - When you want to provide a default hash function for your own custom type: Add a `hash`
* member function to it. The function should return `uint32_t` and take no arguments. This
- * method will be called by the default implementation of DefaultHash. It will automatically be
+ * method will be called by the default implementation of #DefaultHash. It will automatically be
* used by hash table implementations.
*
* - When you want to provide a default hash function for a type that you cannot modify: Add a new
- * specialization to the DefaultHash struct. This can be done by writing code like below in
+ * specialization to the #DefaultHash struct. This can be done by writing code like below in
* either global or BLI namespace.
*
* template<> struct blender::DefaultHash<TheType> {
@@ -86,9 +86,9 @@
namespace blender {
/**
- * If there is no other specialization of DefaultHash for a given type, try to call `hash()` on the
- * value. If there is no such method, this will result in a compiler error. Usually that means that
- * you have to implement a hash function using one of three strategies listed above.
+ * If there is no other specialization of #DefaultHash for a given type, try to call `hash()` on
+ * the value. If there is no such method, this will result in a compiler error. Usually that means
+ * that you have to implement a hash function using one of three strategies listed above.
*/
template<typename T> struct DefaultHash {
uint32_t operator()(const T &value) const
@@ -165,7 +165,7 @@ inline uint32_t hash_string(StringRef str)
template<> struct DefaultHash<std::string> {
/**
- * Take a StringRef as parameter to support heterogeneous lookups in hash table implementations
+ * Take a #StringRef as parameter to support heterogeneous lookups in hash table implementations
* when std::string is used as key.
*/
uint32_t operator()(StringRef value) const
diff --git a/source/blender/blenlib/BLI_hash_tables.hh b/source/blender/blenlib/BLI_hash_tables.hh
index c3b0b1f90e0..195811ebd71 100644
--- a/source/blender/blenlib/BLI_hash_tables.hh
+++ b/source/blender/blenlib/BLI_hash_tables.hh
@@ -39,7 +39,7 @@ namespace blender {
/* -------------------------------------------------------------------- */
/** \name Constexpr Utility Functions
*
- * Those should eventually be deduplicated with functions in BLI_math_base.h.
+ * Those should eventually be de-duplicated with functions in BLI_math_base.h.
* \{ */
inline constexpr int is_power_of_2_i_constexpr(int n)
@@ -158,10 +158,10 @@ class LoadFactor {
* two values of the key type are selected to indicate whether the slot is empty or removed.
*
* The classes below tell a slot implementation which special key values it can use. They can be
- * used as KeyInfo in slot types like IntrusiveSetSlot and IntrusiveMapSlot.
+ * used as #KeyInfo in slot types like #IntrusiveSetSlot and #IntrusiveMapSlot.
*
- * A KeyInfo type has to implement a couple of static methods that are descriped in
- * TemplatedKeyInfo.
+ * A #KeyInfo type has to implement a couple of static methods that are descried in
+ * #TemplatedKeyInfo.
*
* \{ */
diff --git a/source/blender/blenlib/BLI_index_range.hh b/source/blender/blenlib/BLI_index_range.hh
index 25192429a5d..e2b74931537 100644
--- a/source/blender/blenlib/BLI_index_range.hh
+++ b/source/blender/blenlib/BLI_index_range.hh
@@ -211,7 +211,7 @@ class IndexRange {
}
/**
- * Returns a new range, that contains a subinterval of the current one.
+ * Returns a new range, that contains a sub-interval of the current one.
*/
IndexRange slice(uint start, uint size) const
{
diff --git a/source/blender/blenlib/BLI_listbase_wrapper.hh b/source/blender/blenlib/BLI_listbase_wrapper.hh
index a77e2d66458..9cf7fc5c0e0 100644
--- a/source/blender/blenlib/BLI_listbase_wrapper.hh
+++ b/source/blender/blenlib/BLI_listbase_wrapper.hh
@@ -20,10 +20,10 @@
/** \file
* \ingroup bli
*
- * `blender::ListBaseWrapper` is a typed wrapper for the ListBase struct. That makes it safer and
+ * `blender::ListBaseWrapper` is a typed wrapper for the #ListBase struct. That makes it safer and
* more convenient to use in C++ in some cases. However, if you find yourself iterating over a
* linked list a lot, consider to convert it into a vector for further processing. This improves
- * performance and debugability.
+ * performance and debug-ability.
*/
#include "BLI_listbase.h"
diff --git a/source/blender/blenlib/BLI_map.hh b/source/blender/blenlib/BLI_map.hh
index a4c3ee76ca1..688f334001f 100644
--- a/source/blender/blenlib/BLI_map.hh
+++ b/source/blender/blenlib/BLI_map.hh
@@ -67,13 +67,13 @@
* interface as blender::Map. This is useful for benchmarking.
*/
+#include <optional>
#include <unordered_map>
#include "BLI_array.hh"
#include "BLI_hash.hh"
#include "BLI_hash_tables.hh"
#include "BLI_map_slots.hh"
-#include "BLI_optional.hh"
#include "BLI_probing_strategies.hh"
namespace blender {
@@ -98,7 +98,7 @@ template<
*/
uint32_t InlineBufferCapacity = (sizeof(Key) + sizeof(Value) < 100) ? 4 : 0,
/**
- * The strategy used to deal with collistions. They are defined in BLI_probing_strategies.hh.
+ * The strategy used to deal with collisions. They are defined in BLI_probing_strategies.hh.
*/
typename ProbingStrategy = DefaultProbingStrategy,
/**
@@ -392,7 +392,7 @@ class Map {
* Get the value that is stored for the given key and remove it from the map. If the key is not
* in the map, a value-less optional is returned.
*/
- Optional<Value> pop_try(const Key &key)
+ std::optional<Value> pop_try(const Key &key)
{
return this->pop_try_as(key);
}
@@ -400,7 +400,7 @@ class Map {
/**
* Same as `pop_try`, but accepts other key types that are supported by the hash function.
*/
- template<typename ForwardKey> Optional<Value> pop_try_as(const ForwardKey &key)
+ template<typename ForwardKey> std::optional<Value> pop_try_as(const ForwardKey &key)
{
return this->pop_try__impl(key, m_hash(key));
}
@@ -641,7 +641,7 @@ class Map {
*/
template<typename FuncT> void foreach_item(const FuncT &func) const
{
- uint32_t size = this->size();
+ uint32_t size = m_slots.size();
for (uint32_t i = 0; i < size; i++) {
const Slot &slot = m_slots[i];
if (slot.is_occupied()) {
@@ -1074,11 +1074,12 @@ class Map {
MAP_SLOT_PROBING_END();
}
- template<typename ForwardKey> Optional<Value> pop_try__impl(const ForwardKey &key, uint32_t hash)
+ template<typename ForwardKey>
+ std::optional<Value> pop_try__impl(const ForwardKey &key, uint32_t hash)
{
MAP_SLOT_PROBING_BEGIN (hash, slot) {
if (slot.contains(key, m_is_equal, hash)) {
- Optional<Value> value = std::move(*slot.value());
+ std::optional<Value> value = std::move(*slot.value());
slot.remove();
m_removed_slots++;
return value;
diff --git a/source/blender/blenlib/BLI_math_geom.h b/source/blender/blenlib/BLI_math_geom.h
index 563bcad5d14..f51486c5e7b 100644
--- a/source/blender/blenlib/BLI_math_geom.h
+++ b/source/blender/blenlib/BLI_math_geom.h
@@ -190,6 +190,10 @@ float dist_squared_to_projected_aabb_simple(const float projmat[4][4],
const float bbmin[3],
const float bbmax[3]);
+float closest_to_ray_v3(float r_close[3],
+ const float p[3],
+ const float ray_orig[3],
+ const float ray_dir[3]);
float closest_to_line_v2(float r_close[2], const float p[2], const float l1[2], const float l2[2]);
double closest_to_line_v2_db(double r_close[2],
const double p[2],
diff --git a/source/blender/blenlib/BLI_memory_utils.hh b/source/blender/blenlib/BLI_memory_utils.hh
index 81792e2101c..0c2cae6c606 100644
--- a/source/blender/blenlib/BLI_memory_utils.hh
+++ b/source/blender/blenlib/BLI_memory_utils.hh
@@ -204,14 +204,6 @@ template<typename T> void uninitialized_fill_n(T *dst, uint n, const T &value)
}
}
-/**
- * The same as std::unique_ptr. This can be removed when we start using C++14.
- */
-template<typename T, typename... Args> std::unique_ptr<T> make_unique(Args &&... args)
-{
- return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
-}
-
template<typename T> struct DestructValueAtAddress {
void operator()(T *ptr)
{
@@ -250,6 +242,13 @@ template<size_t Size, size_t Alignment> class alignas(Alignment) AlignedBuffer {
}
};
+/**
+ * This can be used by container constructors. A parameter of this type should be used to indicate
+ * that the constructor does not construct the elements.
+ */
+class NoInitialization {
+};
+
} // namespace blender
#endif /* __BLI_MEMORY_UTILS_HH__ */
diff --git a/source/blender/blenlib/BLI_optional.hh b/source/blender/blenlib/BLI_optional.hh
deleted file mode 100644
index 2e6b66d0eac..00000000000
--- a/source/blender/blenlib/BLI_optional.hh
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-/** \file
- * \ingroup bli
- *
- * Simple version of std::optional, which is only available since C++17.
- */
-
-#ifndef __BLI_OPTIONAL_HH__
-#define __BLI_OPTIONAL_HH__
-
-#include "BLI_memory_utils.hh"
-#include "BLI_utildefines.h"
-
-#include <algorithm>
-#include <memory>
-
-namespace blender {
-
-template<typename T> class Optional {
- private:
- AlignedBuffer<sizeof(T), alignof(T)> m_storage;
- bool m_set;
-
- public:
- Optional() : m_set(false)
- {
- }
-
- ~Optional()
- {
- this->reset();
- }
-
- Optional(const T &value) : Optional()
- {
- this->set(value);
- }
-
- Optional(T &&value) : Optional()
- {
- this->set(std::forward<T>(value));
- }
-
- Optional(const Optional &other) : Optional()
- {
- if (other.has_value()) {
- this->set(other.value());
- }
- }
-
- Optional(Optional &&other) : Optional()
- {
- if (other.has_value()) {
- this->set(std::move(other.value()));
- }
- }
-
- Optional &operator=(const Optional &other)
- {
- if (this == &other) {
- return *this;
- }
- if (other.has_value()) {
- this->set(other.value());
- }
- else {
- this->reset();
- }
- return *this;
- }
-
- Optional &operator=(Optional &&other)
- {
- if (this == &other) {
- return *this;
- }
- if (other.has_value()) {
- this->set(std::move(other.value()));
- }
- else {
- this->reset();
- }
- return *this;
- }
-
- bool has_value() const
- {
- return m_set;
- }
-
- const T &value() const
- {
- BLI_assert(m_set);
- return *this->value_ptr();
- }
-
- T &value()
- {
- BLI_assert(m_set);
- return *this->value_ptr();
- }
-
- void set(const T &value)
- {
- if (m_set) {
- this->value() = value;
- }
- else {
- new ((void *)this->value_ptr()) T(value);
- m_set = true;
- }
- }
-
- void set(T &&value)
- {
- if (m_set) {
- this->value() = std::move(value);
- }
- else {
- new ((void *)this->value_ptr()) T(std::move(value));
- m_set = true;
- }
- }
-
- void set_new(const T &value)
- {
- BLI_assert(!m_set);
- new ((void *)this->value_ptr()) T(value);
- m_set = true;
- }
-
- void set_new(T &&value)
- {
- BLI_assert(!m_set);
- new ((void *)this->value_ptr()) T(std::move(value));
- m_set = true;
- }
-
- void reset()
- {
- if (m_set) {
- this->value_ptr()->~T();
- m_set = false;
- }
- }
-
- T extract()
- {
- BLI_assert(m_set);
- T value = std::move(this->value());
- this->reset();
- return value;
- }
-
- T *operator->()
- {
- return this->value_ptr();
- }
-
- T &operator*()
- {
- return *this->value_ptr();
- }
-
- private:
- T *value_ptr() const
- {
- return (T *)m_storage.ptr();
- }
-};
-
-} /* namespace blender */
-
-#endif /* __BLI_OPTIONAL_HH__ */
diff --git a/source/blender/blenlib/BLI_set.hh b/source/blender/blenlib/BLI_set.hh
index d23aa96e434..c3dec147ac6 100644
--- a/source/blender/blenlib/BLI_set.hh
+++ b/source/blender/blenlib/BLI_set.hh
@@ -30,7 +30,7 @@
* Every slot is in one of three states: empty, occupied or removed. If a slot is occupied, it
* contains an instance of the key type.
*
- * Benchmarking and comparing hash tables is hard, because many factors influence the result. The
+ * Bench-marking and comparing hash tables is hard, because many factors influence the result. The
* performance of a hash table depends on the combination of the hash function, probing strategy,
* max load factor, key type, slot type and the data distribution. This implementation is designed
* to be relatively fast by default in all cases. However, it also offers many customization
@@ -49,21 +49,21 @@
* - Small buffer optimization is enabled by default, if the key is not too large.
* - The methods `add_new` and `remove_contained` should be used instead of `add` and `remove`
* whenever appropriate. Assumptions and intention are described better this way.
- * - Lookups can be performed using types other than Key without conversion. For that use the
- * methods ending with `_as`. The template parameters Hash and IsEqual have to support the other
+ * - Look-ups can be performed using types other than Key without conversion. For that use the
+ * methods ending with `_as`. The template parameters Hash and #IsEqual have to support the other
* key type. This can greatly improve performance when the set contains strings.
- * - The default constructor is cheap, even when a large InlineBufferCapacity is used. A large
+ * - The default constructor is cheap, even when a large #InlineBufferCapacity is used. A large
* slot array will only be initialized when the first key is added.
* - The `print_stats` method can be used to get information about the distribution of keys and
* memory usage of the set.
* - The method names don't follow the std::unordered_set names in many cases. Searching for such
* names in this file will usually let you discover the new name.
- * - There is a StdUnorderedSetWrapper class, that wraps std::unordered_set and gives it the same
- * interface as blender::Set. This is useful for benchmarking.
+ * - There is a #StdUnorderedSetWrapper class, that wraps std::unordered_set and gives it the same
+ * interface as blender::Set. This is useful for bench-marking.
*
* Possible Improvements:
- * - Use a branchless loop over slots in grow function (measured ~10% performance improvement when
- * the distribution of occupied slots is suffiently random).
+ * - Use a branch-less loop over slots in grow function (measured ~10% performance improvement when
+ * the distribution of occupied slots is sufficiently random).
* - Support max load factor customization.
* - Improve performance with large data sets through software prefetching. I got fairly
* significant improvements in simple tests (~30% faster). It still needs to be investigated how
diff --git a/source/blender/blenlib/BLI_string_ref.hh b/source/blender/blenlib/BLI_string_ref.hh
index bb8479efe95..62755de19af 100644
--- a/source/blender/blenlib/BLI_string_ref.hh
+++ b/source/blender/blenlib/BLI_string_ref.hh
@@ -89,7 +89,7 @@ class StringRefBase {
}
/**
- * Implicitely convert to std::string. This is convenient in most cases, but you have to be a bit
+ * Implicitly convert to std::string. This is convenient in most cases, but you have to be a bit
* careful not to convert to std::string accidentally.
*/
operator std::string() const
diff --git a/source/blender/blenlib/BLI_vector.hh b/source/blender/blenlib/BLI_vector.hh
index 25e3d2f969b..c5f65d54288 100644
--- a/source/blender/blenlib/BLI_vector.hh
+++ b/source/blender/blenlib/BLI_vector.hh
@@ -107,7 +107,7 @@ class Vector {
#endif
/**
- * Be a friend with other vector instanciations. This is necessary to implement some memory
+ * Be a friend with other vector instantiations. This is necessary to implement some memory
* management logic.
*/
template<typename OtherT, uint OtherInlineBufferCapacity, typename OtherAllocator>
@@ -147,7 +147,7 @@ class Vector {
}
/**
- * Create a vector that contains copys of the values in the initialized list.
+ * Create a vector that contains copies of the values in the initialized list.
*
* This allows you to write code like:
* Vector<int> vec = {3, 4, 5};
@@ -611,7 +611,7 @@ class Vector {
}
/**
- * Finds the first occurence of the value, removes it and copies the last element to the hole in
+ * Finds the first occurrence of the value, removes it and copies the last element to the hole in
* the vector. This takes O(n) time.
*/
void remove_first_occurrence_and_reorder(const T &value)
diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt
index 69df0505dfe..2630eb6bbd0 100644
--- a/source/blender/blenlib/CMakeLists.txt
+++ b/source/blender/blenlib/CMakeLists.txt
@@ -225,7 +225,6 @@ set(SRC
BLI_memory_utils.hh
BLI_mempool.h
BLI_noise.h
- BLI_optional.hh
BLI_path_util.h
BLI_polyfill_2d.h
BLI_polyfill_2d_beautify.h
diff --git a/source/blender/blenlib/intern/BLI_kdopbvh.c b/source/blender/blenlib/intern/BLI_kdopbvh.c
index da67baf0ead..9d1981b3392 100644
--- a/source/blender/blenlib/intern/BLI_kdopbvh.c
+++ b/source/blender/blenlib/intern/BLI_kdopbvh.c
@@ -769,7 +769,7 @@ static void non_recursive_bvh_div_nodes_task_cb(void *__restrict userdata,
* This functions builds an optimal implicit tree from the given leafs.
* Where optimal stands for:
* - The resulting tree will have the smallest number of branches;
- * - At most only one branch will have NULL childs;
+ * - At most only one branch will have NULL children;
* - All leafs will be stored at level N or N+1.
*
* This function creates an implicit tree on branches_array,
@@ -777,7 +777,7 @@ static void non_recursive_bvh_div_nodes_task_cb(void *__restrict userdata,
*
* The tree is built per depth levels. First branches at depth 1.. then branches at depth 2.. etc..
* The reason is that we can build level N+1 from level N without any data dependencies..
- * thus it allows to use multithread building.
+ * thus it allows to use multi-thread building.
*
* To archive this is necessary to find how much leafs are accessible from a certain branch,
* #BVHBuildHelper, #implicit_needed_branches and #implicit_leafs_index
diff --git a/source/blender/blenlib/intern/delaunay_2d.c b/source/blender/blenlib/intern/delaunay_2d.c
index 4e0cd3a78dc..08ccff695c1 100644
--- a/source/blender/blenlib/intern/delaunay_2d.c
+++ b/source/blender/blenlib/intern/delaunay_2d.c
@@ -405,7 +405,8 @@ static CDTEdge *add_vert_to_symedge_edge(CDT_state *cdt, CDTVert *v, SymEdge *se
return e;
}
-/* Connect the verts of se1 and se2, assuming that currently those two SymEdges are on
+/**
+ * Connect the verts of se1 and se2, assuming that currently those two #SymEdges are on
* the outer boundary (have face == outer_face) of two components that are isolated from
* each other.
*/
@@ -479,7 +480,7 @@ static CDTEdge *split_edge(CDT_state *cdt, SymEdge *se, double lambda)
* the deleted edge will be the one that was e's face.
* There will be now an unused face, marked by setting its deleted flag,
* and an unused #CDTEdge, marked by setting the next and rot pointers of
- * its SymEdges to NULL.
+ * its #SymEdge(s) to NULL.
* <pre>
* . v2 .
* / \ / \
@@ -1017,7 +1018,9 @@ static void initial_triangulation(CDT_state *cdt)
MEM_freeN(sites);
}
-/** Use LinkNode linked list as stack of SymEdges, allocating from cdt->listpool. */
+/**
+ * Use #LinkNode linked list as stack of #SymEdges, allocating from `cdt->listpool` .
+ */
typedef LinkNode *Stack;
BLI_INLINE void push(Stack *stack, SymEdge *se, CDT_state *cdt)
@@ -1153,22 +1156,22 @@ static double tri_orient(const SymEdge *t)
* in the path we will take to insert an edge constraint.
* Each such point will either be
* (a) a vertex or
- * (b) a fraction lambda (0 < lambda < 1) along some SymEdge.]
+ * (b) a fraction lambda (0 < lambda < 1) along some #SymEdge.]
*
* In general, lambda=0 indicates case a and lambda != 0 indicates case be.
* The 'in' edge gives the destination attachment point of a diagonal from the previous crossing,
* and the 'out' edge gives the origin attachment point of a diagonal to the next crossing.
* But in some cases, 'in' and 'out' are undefined or not needed, and will be NULL.
*
- * For case (a), 'vert' will be the vertex, and lambda will be 0, and 'in' will be the SymEdge from
- * 'vert' that has as face the one that you go through to get to this vertex. If you go exactly
- * along an edge then we set 'in' to NULL, since it won't be needed. The first crossing will have
- * 'in' = NULL. We set 'out' to the SymEdge that has the face we go though to get to the next
- * crossing, or, if the next crossing is a case (a), then it is the edge that goes to that next
- * vertex. 'out' wlll be NULL for the last one.
+ * For case (a), 'vert' will be the vertex, and lambda will be 0, and 'in' will be the #SymEdge
+ * from 'vert' that has as face the one that you go through to get to this vertex. If you go
+ * exactly along an edge then we set 'in' to NULL, since it won't be needed. The first crossing
+ * will have 'in' = NULL. We set 'out' to the #SymEdge that has the face we go though to get to the
+ * next crossing, or, if the next crossing is a case (a), then it is the edge that goes to that
+ * next vertex. 'out' wlll be NULL for the last one.
*
* For case (b), vert will be NULL at first, and later filled in with the created split vertex,
- * and 'in' will be the SymEdge that we go through, and lambda will be between 0 and 1,
+ * and 'in' will be the #SymEdge that we go through, and lambda will be between 0 and 1,
* the fraction from in's vert to in->next's vert to put the split vertex.
* 'out' is not needed in this case, since the attachment point will be the sym of the first
* half of the split edge.
@@ -1231,8 +1234,8 @@ static void fill_crossdata_for_through_vert(CDTVert *v,
/**
* As part of finding crossings, we found a case where orient tests say that the next crossing
- * is on the SymEdge t, while intersecting with the ray from curco to v2.
- * Find the intersection point and fill in the CrossData for that point.
+ * is on the #SymEdge t, while intersecting with the ray from \a curco to \a v2.
+ * Find the intersection point and fill in the #CrossData for that point.
* It may turn out that when doing the intersection, we get an answer that says that
* this case is better handled as through-vertex case instead, so we may do that.
* In the latter case, we want to avoid a situation where the current crossing is on an edge
@@ -1442,12 +1445,12 @@ static bool get_next_crossing_from_vert(CDT_state *cdt,
}
/**
- * As part of finding the crossings of a ray to v2, find the next crossing after 'cd', assuming
+ * As part of finding the crossings of a ray to 'v2', find the next crossing after 'cd', assuming
* 'cd' represents a crossing that goes through a an edge, not at either end of that edge.
*
- * We have the triangle vb-va-vc, where va and vb are the split edge and vc is the third vertex on
- * that new side of the edge (should be closer to v2). The next crossing should be through vc or
- * intersecting vb-vc or va-vc.
+ * We have the triangle 'vb-va-vc', where va and vb are the split edge and 'vc' is the third vertex
+ * on that new side of the edge (should be closer to v2). The next crossing should be through 'vc'
+ * or intersecting 'vb-vc' or 'va-vc'.
*/
static void get_next_crossing_from_edge(CDT_state *cdt,
CrossData *cd,
diff --git a/source/blender/blenlib/intern/dot_export.cc b/source/blender/blenlib/intern/dot_export.cc
index a2cf843c473..ff39265d681 100644
--- a/source/blender/blenlib/intern/dot_export.cc
+++ b/source/blender/blenlib/intern/dot_export.cc
@@ -19,7 +19,7 @@
#include "BLI_dot_export.hh"
namespace blender {
-namespace DotExport {
+namespace dot {
/* Graph Building
************************************************/
@@ -237,7 +237,7 @@ void NodePort::to_dot_string(std::stringstream &ss) const
{
m_node->export__as_id(ss);
if (m_port_name.has_value()) {
- ss << ":" << m_port_name.value();
+ ss << ":" << *m_port_name;
}
}
@@ -301,5 +301,5 @@ NodeWithSocketsRef::NodeWithSocketsRef(Node &node,
m_node->set_shape(Attr_shape::Rectangle);
}
-} // namespace DotExport
+} // namespace dot
} // namespace blender
diff --git a/source/blender/blenlib/intern/edgehash.c b/source/blender/blenlib/intern/edgehash.c
index 556c0a65fc5..56529581dd3 100644
--- a/source/blender/blenlib/intern/edgehash.c
+++ b/source/blender/blenlib/intern/edgehash.c
@@ -376,7 +376,7 @@ bool BLI_edgehash_ensure_p(EdgeHash *eh, uint v0, uint v1, void ***r_value)
* Remove \a key (v0, v1) from \a eh, or return false if the key wasn't found.
*
* \param v0, v1: The key to remove.
- * \param valfreefp: Optional callback to free the value.
+ * \param free_value: Optional callback to free the value.
* \return true if \a key was removed from \a eh.
*/
bool BLI_edgehash_remove(EdgeHash *eh, uint v0, uint v1, EdgeHashFreeFP free_value)
diff --git a/source/blender/blenlib/intern/math_bits_inline.c b/source/blender/blenlib/intern/math_bits_inline.c
index 8f8f257f1e7..e7a7b17e1e4 100644
--- a/source/blender/blenlib/intern/math_bits_inline.c
+++ b/source/blender/blenlib/intern/math_bits_inline.c
@@ -63,7 +63,7 @@ MINLINE unsigned int bitscan_reverse_uint(unsigned int a)
#ifdef _MSC_VER
unsigned long clz;
_BitScanReverse(&clz, a);
- return clz;
+ return 31 - clz;
#else
return (unsigned int)__builtin_clz(a);
#endif
diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c
index e7c1fc8c2d9..d3dc4729617 100644
--- a/source/blender/blenlib/intern/math_geom.c
+++ b/source/blender/blenlib/intern/math_geom.c
@@ -3248,19 +3248,27 @@ bool isect_ray_aabb_v3_simple(const float orig[3],
}
}
-/* find closest point to p on line through (l1, l2) and return lambda,
- * where (0 <= lambda <= 1) when cp is in the line segment (l1, l2)
+float closest_to_ray_v3(float r_close[3],
+ const float p[3],
+ const float ray_orig[3],
+ const float ray_dir[3])
+{
+ float h[3], lambda;
+ sub_v3_v3v3(h, p, ray_orig);
+ lambda = dot_v3v3(ray_dir, h) / dot_v3v3(ray_dir, ray_dir);
+ madd_v3_v3v3fl(r_close, ray_orig, ray_dir, lambda);
+ return lambda;
+}
+
+/**
+ * Find closest point to p on line through (l1, l2) and return lambda,
+ * where (0 <= lambda <= 1) when cp is in the line segment (l1, l2).
*/
float closest_to_line_v3(float r_close[3], const float p[3], const float l1[3], const float l2[3])
{
- float h[3], u[3], lambda;
+ float u[3];
sub_v3_v3v3(u, l2, l1);
- sub_v3_v3v3(h, p, l1);
- lambda = dot_v3v3(u, h) / dot_v3v3(u, u);
- r_close[0] = l1[0] + u[0] * lambda;
- r_close[1] = l1[1] + u[1] * lambda;
- r_close[2] = l1[2] + u[2] * lambda;
- return lambda;
+ return closest_to_ray_v3(r_close, p, l1, u);
}
float closest_to_line_v2(float r_close[2], const float p[2], const float l1[2], const float l2[2])
diff --git a/source/blender/blenlib/intern/math_matrix.c b/source/blender/blenlib/intern/math_matrix.c
index 9e398239bc7..92cfd09f191 100644
--- a/source/blender/blenlib/intern/math_matrix.c
+++ b/source/blender/blenlib/intern/math_matrix.c
@@ -2388,6 +2388,22 @@ void interp_m3_m3m3(float R[3][3], const float A[3][3], const float B[3][3], con
mat3_polar_decompose(A, U_A, P_A);
mat3_polar_decompose(B, U_B, P_B);
+ /* Quaternions cannot represent an axis flip. If such a singularity is detected, choose a
+ * different decomposition of the matrix that still satisfies A = U_A * P_A but which has a
+ * positive determinant and thus no axis flips. This resolves T77154.
+ *
+ * Note that a flip of two axes is just a rotation of 180 degrees around the third axis, and
+ * three flipped axes are just an 180 degree rotation + a single axis flip. It is thus sufficient
+ * to solve this problem for single axis flips. */
+ if (determinant_m3_array(U_A) < 0) {
+ mul_m3_fl(U_A, -1.0f);
+ mul_m3_fl(P_A, -1.0f);
+ }
+ if (determinant_m3_array(U_B) < 0) {
+ mul_m3_fl(U_B, -1.0f);
+ mul_m3_fl(P_B, -1.0f);
+ }
+
mat3_to_quat(quat_A, U_A);
mat3_to_quat(quat_B, U_B);
interp_qt_qtqt(quat, quat_A, quat_B, t);
diff --git a/source/blender/blenlib/intern/math_rotation.c b/source/blender/blenlib/intern/math_rotation.c
index a91cdabe3ab..a2f7cc24dd3 100644
--- a/source/blender/blenlib/intern/math_rotation.c
+++ b/source/blender/blenlib/intern/math_rotation.c
@@ -543,8 +543,8 @@ void rotation_between_quats_to_quat(float q[4], const float q1[4], const float q
*
* \param q: input quaternion.
* \param axis: twist axis in [0,1,2]
- * \param r_swing[out]: if not NULL, receives the swing quaternion.
- * \param r_twist[out]: if not NULL, receives the twist quaternion.
+ * \param r_swing: if not NULL, receives the swing quaternion.
+ * \param r_twist: if not NULL, receives the twist quaternion.
* \returns twist angle.
*/
float quat_split_swing_and_twist(const float q[4], int axis, float r_swing[4], float r_twist[4])
@@ -2127,7 +2127,7 @@ void mul_v3m3_dq(float co[3], float mat[3][3], DualQuat *dq)
co[1] = (co[1] + t[1]) * len2;
co[2] = (co[2] + t[2]) * len2;
- /* compute crazyspace correction mat */
+ /* Compute crazy-space correction matrix. */
if (mat) {
if (dq->scale_weight) {
copy_m3_m4(scalemat, dq->scale);
diff --git a/source/blender/blenlib/intern/storage.c b/source/blender/blenlib/intern/storage.c
index fbfb258693b..f2217b1cd1a 100644
--- a/source/blender/blenlib/intern/storage.c
+++ b/source/blender/blenlib/intern/storage.c
@@ -529,7 +529,7 @@ void *BLI_file_read_binary_as_mem(const char *filepath, size_t pad_bytes, size_t
* Return the text file data with:
* - Newlines replaced with '\0'.
- * - Optionally trim whitespace, replacing trailing ' ' & '\t' with '\0'.
+ * - Optionally trim white-space, replacing trailing <space> & <tab> with '\0'.
*
* This is an alternative to using #BLI_file_read_as_lines,
* allowing us to loop over lines without converting it into a linked list
diff --git a/source/blender/blenloader/BLO_readfile.h b/source/blender/blenloader/BLO_readfile.h
index 5e123fe69d8..73d5729675a 100644
--- a/source/blender/blenloader/BLO_readfile.h
+++ b/source/blender/blenloader/BLO_readfile.h
@@ -50,6 +50,18 @@ struct wmWindowManager;
typedef struct BlendHandle BlendHandle;
+typedef struct WorkspaceConfigFileData {
+ struct Main *main; /* has to be freed when done reading file data */
+
+ struct ListBase workspaces;
+} WorkspaceConfigFileData;
+
+/* -------------------------------------------------------------------- */
+/** \name BLO Read File API
+ *
+ * \see #BLO_write_file for file writing.
+ * \{ */
+
typedef enum eBlenFileType {
BLENFILETYPE_BLEND = 1,
/* BLENFILETYPE_PUB = 2, */ /* UNUSED */
@@ -71,12 +83,6 @@ typedef struct BlendFileData {
eBlenFileType type;
} BlendFileData;
-typedef struct WorkspaceConfigFileData {
- struct Main *main; /* has to be freed when done reading file data */
-
- struct ListBase workspaces;
-} WorkspaceConfigFileData;
-
struct BlendFileReadParams {
uint skip_flags : 3; /* eBLOReadSkip */
uint is_startup : 1;
@@ -110,6 +116,12 @@ BlendFileData *BLO_read_from_memfile(struct Main *oldmain,
void BLO_blendfiledata_free(BlendFileData *bfd);
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name BLO Blend File Handle API
+ * \{ */
+
BlendHandle *BLO_blendhandle_from_file(const char *filepath, struct ReportList *reports);
BlendHandle *BLO_blendhandle_from_memory(const void *mem, int memsize);
@@ -121,7 +133,7 @@ struct LinkNode *BLO_blendhandle_get_linkable_groups(BlendHandle *bh);
void BLO_blendhandle_close(BlendHandle *bh);
-/***/
+/** \} */
#define BLO_GROUP_MAX 32
#define BLO_EMBEDDED_STARTUP_BLEND "<startup.blend>"
diff --git a/source/blender/blenloader/BLO_writefile.h b/source/blender/blenloader/BLO_writefile.h
index d83abf7f9ed..8fe04e764f9 100644
--- a/source/blender/blenloader/BLO_writefile.h
+++ b/source/blender/blenloader/BLO_writefile.h
@@ -30,14 +30,48 @@ struct Main;
struct MemFile;
struct ReportList;
+/* -------------------------------------------------------------------- */
+/** \name BLO Write File API
+ *
+ * \see #BLO_read_from_file for file reading.
+ * \{ */
+
+/**
+ * Adjust paths when saving (kept unless #BlendFileWriteParams.use_save_as_copy is set).
+ */
+typedef enum eBLO_WritePathRemap {
+ /** No path manipulation. */
+ BLO_WRITE_PATH_REMAP_NONE = 0,
+ /** Remap existing relative paths (default). */
+ BLO_WRITE_PATH_REMAP_RELATIVE = 1,
+ /** Remap paths making all paths relative to the new location. */
+ BLO_WRITE_PATH_REMAP_RELATIVE_ALL = 2,
+ /** Make all paths absolute. */
+ BLO_WRITE_PATH_REMAP_ABSOLUTE = 3,
+} eBLO_WritePathRemap;
+
+/** Similar to #BlendFileReadParams. */
+struct BlendFileWriteParams {
+ eBLO_WritePathRemap remap_mode;
+ /** Save `.blend1`, `.blend2`... etc. */
+ uint use_save_versions : 1;
+ /** On write, restore paths after editing them (see #BLO_WRITE_PATH_REMAP_RELATIVE). */
+ uint use_save_as_copy : 1;
+ uint use_userdef : 1;
+ const struct BlendThumbnail *thumb;
+};
+
extern bool BLO_write_file(struct Main *mainvar,
const char *filepath,
- int write_flags,
- struct ReportList *reports,
- const struct BlendThumbnail *thumb);
+ const int write_flags,
+ const struct BlendFileWriteParams *params,
+ struct ReportList *reports);
+
extern bool BLO_write_file_mem(struct Main *mainvar,
struct MemFile *compare,
struct MemFile *current,
int write_flags);
+/** \} */
+
#endif
diff --git a/source/blender/blenloader/intern/blend_validate.c b/source/blender/blenloader/intern/blend_validate.c
index 33c5e3ac197..0a5d8d332aa 100644
--- a/source/blender/blenloader/intern/blend_validate.c
+++ b/source/blender/blenloader/intern/blend_validate.c
@@ -70,7 +70,7 @@ bool BLO_main_validate_libraries(Main *bmain, ReportList *reports)
RPT_ERROR,
"ID %s is in local database while being linked from library %s!",
id->name,
- id->lib->name);
+ id->lib->filepath);
}
}
}
@@ -82,15 +82,15 @@ bool BLO_main_validate_libraries(Main *bmain, ReportList *reports)
continue;
}
- BKE_library_filepath_set(bmain, curlib, curlib->name);
- BlendHandle *bh = BLO_blendhandle_from_file(curlib->filepath, reports);
+ BKE_library_filepath_set(bmain, curlib, curlib->filepath);
+ BlendHandle *bh = BLO_blendhandle_from_file(curlib->filepath_abs, reports);
if (bh == NULL) {
BKE_reportf(reports,
RPT_ERROR,
"Library ID %s not found at expected path %s!",
curlib->id.name,
- curlib->filepath);
+ curlib->filepath_abs);
continue;
}
@@ -107,7 +107,7 @@ bool BLO_main_validate_libraries(Main *bmain, ReportList *reports)
RPT_ERROR,
"Library ID %s in library %s, this should not happen!",
id->name,
- curlib->name);
+ curlib->filepath);
continue;
}
@@ -120,7 +120,7 @@ bool BLO_main_validate_libraries(Main *bmain, ReportList *reports)
RPT_ERROR,
"ID %s has NULL lib pointer while being in library %s!",
id->name,
- curlib->name);
+ curlib->filepath);
continue;
}
if (id->lib != curlib) {
@@ -143,7 +143,7 @@ bool BLO_main_validate_libraries(Main *bmain, ReportList *reports)
RPT_ERROR,
"ID %s not found in library %s anymore!",
id->name,
- id->lib->name);
+ id->lib->filepath);
continue;
}
}
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index ee3f52a9414..5bd42cecd11 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -121,6 +121,7 @@
#include "BKE_colortools.h"
#include "BKE_constraint.h"
#include "BKE_curve.h"
+#include "BKE_curveprofile.h"
#include "BKE_effect.h"
#include "BKE_fcurve_driver.h"
#include "BKE_fluid.h"
@@ -262,10 +263,10 @@ static BHead *find_bhead_from_code_name(FileData *fd, const short idcode, const
static BHead *find_bhead_from_idname(FileData *fd, const char *idname);
#ifdef USE_COLLECTION_COMPAT_28
-static void expand_scene_collection(FileData *fd, Main *mainvar, SceneCollection *sc);
+static void expand_scene_collection(BlendExpander *expander, SceneCollection *sc);
#endif
static void direct_link_animdata(BlendDataReader *reader, AnimData *adt);
-static void lib_link_animdata(FileData *fd, ID *id, AnimData *adt);
+static void lib_link_animdata(BlendLibReader *reader, ID *id, AnimData *adt);
typedef struct BHeadN {
struct BHeadN *next, *prev;
@@ -314,7 +315,7 @@ void blo_reportf_wrap(ReportList *reports, ReportType type, const char *format,
/* for reporting linking messages */
static const char *library_parent_filepath(Library *lib)
{
- return lib->parent ? lib->parent->filepath : "<direct>";
+ return lib->parent ? lib->parent->filepath_abs : "<direct>";
}
/* -------------------------------------------------------------------- */
@@ -677,7 +678,7 @@ static Main *blo_find_main(FileData *fd, const char *filepath, const char *relab
// printf("blo_find_main: converted to %s\n", name1);
for (m = mainlist->first; m; m = m->next) {
- const char *libname = (m->curlib) ? m->curlib->filepath : m->name;
+ const char *libname = (m->curlib) ? m->curlib->filepath_abs : m->name;
if (BLI_path_cmp(name1, libname) == 0) {
if (G.debug & G_DEBUG) {
@@ -700,8 +701,8 @@ static Main *blo_find_main(FileData *fd, const char *filepath, const char *relab
/* Matches direct_link_library(). */
id_us_ensure_real(&lib->id);
- BLI_strncpy(lib->name, filepath, sizeof(lib->name));
- BLI_strncpy(lib->filepath, name1, sizeof(lib->filepath));
+ BLI_strncpy(lib->filepath, filepath, sizeof(lib->filepath));
+ BLI_strncpy(lib->filepath_abs, name1, sizeof(lib->filepath_abs));
m->curlib = lib;
@@ -2455,7 +2456,7 @@ static void link_glob_list(FileData *fd, ListBase *lb) /* for glob data */
* \{ */
static void IDP_DirectLinkProperty(IDProperty *prop, BlendDataReader *reader);
-static void IDP_LibLinkProperty(IDProperty *prop, FileData *fd);
+static void IDP_LibLinkProperty(IDProperty *prop, BlendLibReader *reader);
static void IDP_DirectLinkIDPArray(IDProperty *prop, BlendDataReader *reader)
{
@@ -2591,7 +2592,7 @@ static void _IDP_DirectLinkGroup_OrFree(IDProperty **prop,
}
}
-static void IDP_LibLinkProperty(IDProperty *prop, FileData *fd)
+static void IDP_LibLinkProperty(IDProperty *prop, BlendLibReader *reader)
{
if (!prop) {
return;
@@ -2600,7 +2601,7 @@ static void IDP_LibLinkProperty(IDProperty *prop, FileData *fd)
switch (prop->type) {
case IDP_ID: /* PointerProperty */
{
- void *newaddr = newlibadr(fd, NULL, IDP_Id(prop));
+ void *newaddr = BLO_read_get_new_id_address(reader, NULL, IDP_Id(prop));
if (IDP_Id(prop) && !newaddr && G.debug) {
printf("Error while loading \"%s\". Data not found in file!\n", prop->name);
}
@@ -2611,14 +2612,14 @@ static void IDP_LibLinkProperty(IDProperty *prop, FileData *fd)
{
IDProperty *idp_array = IDP_IDPArray(prop);
for (int i = 0; i < prop->len; i++) {
- IDP_LibLinkProperty(&(idp_array[i]), fd);
+ IDP_LibLinkProperty(&(idp_array[i]), reader);
}
break;
}
case IDP_GROUP: /* PointerProperty */
{
LISTBASE_FOREACH (IDProperty *, loop, &prop->data.group) {
- IDP_LibLinkProperty(loop, fd);
+ IDP_LibLinkProperty(loop, reader);
}
break;
}
@@ -2658,45 +2659,46 @@ static PreviewImage *direct_link_preview_image(BlendDataReader *reader, PreviewI
/** \name Read ID
* \{ */
-static void lib_link_id(FileData *fd, Main *bmain, ID *id);
-static void lib_link_nodetree(FileData *fd, Main *bmain, bNodeTree *ntree);
-static void lib_link_collection(FileData *fd, Main *bmain, Collection *collection);
+static void lib_link_id(BlendLibReader *reader, ID *id);
+static void lib_link_nodetree(BlendLibReader *reader, bNodeTree *ntree);
+static void lib_link_collection(BlendLibReader *reader, Collection *collection);
-static void lib_link_id_embedded_id(FileData *fd, Main *bmain, ID *id)
+static void lib_link_id_embedded_id(BlendLibReader *reader, ID *id)
{
+
/* Handle 'private IDs'. */
bNodeTree *nodetree = ntreeFromID(id);
if (nodetree != NULL) {
- lib_link_id(fd, bmain, &nodetree->id);
- lib_link_nodetree(fd, bmain, nodetree);
+ lib_link_id(reader, &nodetree->id);
+ lib_link_nodetree(reader, nodetree);
}
if (GS(id->name) == ID_SCE) {
Scene *scene = (Scene *)id;
if (scene->master_collection != NULL) {
- lib_link_id(fd, bmain, &scene->master_collection->id);
- lib_link_collection(fd, bmain, scene->master_collection);
+ lib_link_id(reader, &scene->master_collection->id);
+ lib_link_collection(reader, scene->master_collection);
}
}
}
-static void lib_link_id(FileData *fd, Main *bmain, ID *id)
+static void lib_link_id(BlendLibReader *reader, ID *id)
{
/* Note: WM IDProperties are never written to file, hence they should always be NULL here. */
BLI_assert((GS(id->name) != ID_WM) || id->properties == NULL);
- IDP_LibLinkProperty(id->properties, fd);
+ IDP_LibLinkProperty(id->properties, reader);
AnimData *adt = BKE_animdata_from_id(id);
if (adt != NULL) {
- lib_link_animdata(fd, id, adt);
+ lib_link_animdata(reader, id, adt);
}
if (id->override_library) {
- id->override_library->reference = newlibadr(fd, id->lib, id->override_library->reference);
- id->override_library->storage = newlibadr(fd, id->lib, id->override_library->storage);
+ BLO_read_id_address(reader, id->lib, &id->override_library->reference);
+ BLO_read_id_address(reader, id->lib, &id->override_library->storage);
}
- lib_link_id_embedded_id(fd, bmain, id);
+ lib_link_id_embedded_id(reader, id);
}
static void direct_link_id_override_property_operation_cb(BlendDataReader *reader, void *data)
@@ -2891,58 +2893,23 @@ static void direct_link_id_common(
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Read CurveMapping
- * \{ */
-
-/* cuma itself has been read! */
-static void direct_link_curvemapping(BlendDataReader *reader, CurveMapping *cumap)
-{
- int a;
-
- /* flag seems to be able to hang? Maybe old files... not bad to clear anyway */
- cumap->flag &= ~CUMA_PREMULLED;
-
- for (a = 0; a < CM_TOT; a++) {
- BLO_read_data_address(reader, &cumap->cm[a].curve);
- cumap->cm[a].table = NULL;
- cumap->cm[a].premultable = NULL;
- }
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Read CurveProfile
- * \{ */
-
-static void direct_link_curveprofile(BlendDataReader *reader, CurveProfile *profile)
-{
- BLO_read_data_address(reader, &profile->path);
- profile->table = NULL;
- profile->segments = NULL;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
/** \name Read ID: Brush
* \{ */
/* library brush linking after fileread */
-static void lib_link_brush(FileData *fd, Main *UNUSED(bmain), Brush *brush)
+static void lib_link_brush(BlendLibReader *reader, Brush *brush)
{
/* brush->(mask_)mtex.obj is ignored on purpose? */
- brush->mtex.tex = newlibadr(fd, brush->id.lib, brush->mtex.tex);
- brush->mask_mtex.tex = newlibadr(fd, brush->id.lib, brush->mask_mtex.tex);
- brush->clone.image = newlibadr(fd, brush->id.lib, brush->clone.image);
- brush->toggle_brush = newlibadr(fd, brush->id.lib, brush->toggle_brush);
- brush->paint_curve = newlibadr(fd, brush->id.lib, brush->paint_curve);
+ BLO_read_id_address(reader, brush->id.lib, &brush->mtex.tex);
+ BLO_read_id_address(reader, brush->id.lib, &brush->mask_mtex.tex);
+ BLO_read_id_address(reader, brush->id.lib, &brush->clone.image);
+ BLO_read_id_address(reader, brush->id.lib, &brush->toggle_brush);
+ BLO_read_id_address(reader, brush->id.lib, &brush->paint_curve);
/* link default grease pencil palette */
if (brush->gpencil_settings != NULL) {
if (brush->gpencil_settings->flag & GP_BRUSH_MATERIAL_PINNED) {
- brush->gpencil_settings->material = newlibadr(
- fd, brush->id.lib, brush->gpencil_settings->material);
+ BLO_read_id_address(reader, brush->id.lib, &brush->gpencil_settings->material);
if (!brush->gpencil_settings->material) {
brush->gpencil_settings->flag &= ~GP_BRUSH_MATERIAL_PINNED;
@@ -2964,7 +2931,7 @@ static void direct_link_brush(BlendDataReader *reader, Brush *brush)
BLO_read_data_address(reader, &brush->gradient);
if (brush->curve) {
- direct_link_curvemapping(reader, brush->curve);
+ BKE_curvemapping_blend_read(reader, brush->curve);
}
else {
BKE_brush_curve_preset(brush, CURVE_PRESET_SHARP);
@@ -2985,39 +2952,39 @@ static void direct_link_brush(BlendDataReader *reader, Brush *brush)
BLO_read_data_address(reader, &brush->gpencil_settings->curve_rand_value);
if (brush->gpencil_settings->curve_sensitivity) {
- direct_link_curvemapping(reader, brush->gpencil_settings->curve_sensitivity);
+ BKE_curvemapping_blend_read(reader, brush->gpencil_settings->curve_sensitivity);
}
if (brush->gpencil_settings->curve_strength) {
- direct_link_curvemapping(reader, brush->gpencil_settings->curve_strength);
+ BKE_curvemapping_blend_read(reader, brush->gpencil_settings->curve_strength);
}
if (brush->gpencil_settings->curve_jitter) {
- direct_link_curvemapping(reader, brush->gpencil_settings->curve_jitter);
+ BKE_curvemapping_blend_read(reader, brush->gpencil_settings->curve_jitter);
}
if (brush->gpencil_settings->curve_rand_pressure) {
- direct_link_curvemapping(reader, brush->gpencil_settings->curve_rand_pressure);
+ BKE_curvemapping_blend_read(reader, brush->gpencil_settings->curve_rand_pressure);
}
if (brush->gpencil_settings->curve_rand_strength) {
- direct_link_curvemapping(reader, brush->gpencil_settings->curve_rand_strength);
+ BKE_curvemapping_blend_read(reader, brush->gpencil_settings->curve_rand_strength);
}
if (brush->gpencil_settings->curve_rand_uv) {
- direct_link_curvemapping(reader, brush->gpencil_settings->curve_rand_uv);
+ BKE_curvemapping_blend_read(reader, brush->gpencil_settings->curve_rand_uv);
}
if (brush->gpencil_settings->curve_rand_hue) {
- direct_link_curvemapping(reader, brush->gpencil_settings->curve_rand_hue);
+ BKE_curvemapping_blend_read(reader, brush->gpencil_settings->curve_rand_hue);
}
if (brush->gpencil_settings->curve_rand_saturation) {
- direct_link_curvemapping(reader, brush->gpencil_settings->curve_rand_saturation);
+ BKE_curvemapping_blend_read(reader, brush->gpencil_settings->curve_rand_saturation);
}
if (brush->gpencil_settings->curve_rand_value) {
- direct_link_curvemapping(reader, brush->gpencil_settings->curve_rand_value);
+ BKE_curvemapping_blend_read(reader, brush->gpencil_settings->curve_rand_value);
}
}
@@ -3031,7 +2998,7 @@ static void direct_link_brush(BlendDataReader *reader, Brush *brush)
/** \name Read ID: Palette
* \{ */
-static void lib_link_palette(FileData *UNUSED(fd), Main *UNUSED(bmain), Palette *UNUSED(palette))
+static void lib_link_palette(BlendLibReader *UNUSED(reader), Palette *UNUSED(palette))
{
}
@@ -3042,7 +3009,7 @@ static void direct_link_palette(BlendDataReader *reader, Palette *palette)
BLO_read_list(reader, &palette->colors);
}
-static void lib_link_paint_curve(FileData *UNUSED(fd), Main *UNUSED(bmain), PaintCurve *UNUSED(pc))
+static void lib_link_paint_curve(BlendLibReader *UNUSED(reader), PaintCurve *UNUSED(pc))
{
}
@@ -3081,11 +3048,11 @@ static PackedFile *direct_link_packedfile(BlendDataReader *reader, PackedFile *o
* \{ */
// XXX deprecated - old animation system
-static void lib_link_ipo(FileData *fd, Main *UNUSED(bmain), Ipo *ipo)
+static void lib_link_ipo(BlendLibReader *reader, Ipo *ipo)
{
LISTBASE_FOREACH (IpoCurve *, icu, &ipo->curve) {
if (icu->driver) {
- icu->driver->ob = newlibadr(fd, ipo->id.lib, icu->driver->ob);
+ BLO_read_id_address(reader, ipo->id.lib, &icu->driver->ob);
}
}
}
@@ -3105,17 +3072,17 @@ static void direct_link_ipo(BlendDataReader *reader, Ipo *ipo)
}
// XXX deprecated - old animation system
-static void lib_link_nlastrips(FileData *fd, ID *id, ListBase *striplist)
+static void lib_link_nlastrips(BlendLibReader *reader, ID *id, ListBase *striplist)
{
bActionStrip *strip;
bActionModifier *amod;
for (strip = striplist->first; strip; strip = strip->next) {
- strip->object = newlibadr(fd, id->lib, strip->object);
- strip->act = newlibadr(fd, id->lib, strip->act);
- strip->ipo = newlibadr(fd, id->lib, strip->ipo);
+ BLO_read_id_address(reader, id->lib, &strip->object);
+ BLO_read_id_address(reader, id->lib, &strip->act);
+ BLO_read_id_address(reader, id->lib, &strip->ipo);
for (amod = strip->modifiers.first; amod; amod = amod->next) {
- amod->ob = newlibadr(fd, id->lib, amod->ob);
+ BLO_read_id_address(reader, id->lib, &amod->ob);
}
}
}
@@ -3133,12 +3100,12 @@ static void direct_link_nlastrips(BlendDataReader *reader, ListBase *strips)
}
// XXX deprecated - old animation system
-static void lib_link_constraint_channels(FileData *fd, ID *id, ListBase *chanbase)
+static void lib_link_constraint_channels(BlendLibReader *reader, ID *id, ListBase *chanbase)
{
bConstraintChannel *chan;
for (chan = chanbase->first; chan; chan = chan->next) {
- chan->ipo = newlibadr(fd, id->lib, chan->ipo);
+ BLO_read_id_address(reader, id->lib, &chan->ipo);
}
}
@@ -3148,7 +3115,7 @@ static void lib_link_constraint_channels(FileData *fd, ID *id, ListBase *chanbas
/** \name Read ID: Action
* \{ */
-static void lib_link_fmodifiers(FileData *fd, ID *id, ListBase *list)
+static void lib_link_fmodifiers(BlendLibReader *reader, ID *id, ListBase *list)
{
FModifier *fcm;
@@ -3157,7 +3124,7 @@ static void lib_link_fmodifiers(FileData *fd, ID *id, ListBase *list)
switch (fcm->type) {
case FMODIFIER_TYPE_PYTHON: {
FMod_Python *data = (FMod_Python *)fcm->data;
- data->script = newlibadr(fd, id->lib, data->script);
+ BLO_read_id_address(reader, id->lib, &data->script);
break;
}
@@ -3165,7 +3132,7 @@ static void lib_link_fmodifiers(FileData *fd, ID *id, ListBase *list)
}
}
-static void lib_link_fcurves(FileData *fd, ID *id, ListBase *list)
+static void lib_link_fcurves(BlendLibReader *reader, ID *id, ListBase *list)
{
FCurve *fcu;
@@ -3184,7 +3151,7 @@ static void lib_link_fcurves(FileData *fd, ID *id, ListBase *list)
DRIVER_TARGETS_LOOPER_BEGIN (dvar) {
/* only relink if still used */
if (tarIndex < dvar->num_targets) {
- dtar->id = newlibadr(fd, id->lib, dtar->id);
+ BLO_read_id_address(reader, id->lib, &dtar->id);
}
else {
dtar->id = NULL;
@@ -3195,7 +3162,7 @@ static void lib_link_fcurves(FileData *fd, ID *id, ListBase *list)
}
/* modifiers */
- lib_link_fmodifiers(fd, id, &fcu->modifiers);
+ lib_link_fmodifiers(reader, id, &fcu->modifiers);
}
}
@@ -3295,20 +3262,20 @@ static void direct_link_fcurves(BlendDataReader *reader, ListBase *list)
}
}
-static void lib_link_action(FileData *fd, Main *UNUSED(bmain), bAction *act)
+static void lib_link_action(BlendLibReader *reader, bAction *act)
{
// XXX deprecated - old animation system <<<
LISTBASE_FOREACH (bActionChannel *, chan, &act->chanbase) {
- chan->ipo = newlibadr(fd, act->id.lib, chan->ipo);
- lib_link_constraint_channels(fd, &act->id, &chan->constraintChannels);
+ BLO_read_id_address(reader, act->id.lib, &chan->ipo);
+ lib_link_constraint_channels(reader, &act->id, &chan->constraintChannels);
}
// >>> XXX deprecated - old animation system
- lib_link_fcurves(fd, &act->id, &act->curves);
+ lib_link_fcurves(reader, &act->id, &act->curves);
LISTBASE_FOREACH (TimeMarker *, marker, &act->markers) {
if (marker->camera) {
- marker->camera = newlibadr(fd, act->id.lib, marker->camera);
+ BLO_read_id_address(reader, act->id.lib, &marker->camera);
}
}
}
@@ -3339,29 +3306,29 @@ static void direct_link_action(BlendDataReader *reader, bAction *act)
}
}
-static void lib_link_nladata_strips(FileData *fd, ID *id, ListBase *list)
+static void lib_link_nladata_strips(BlendLibReader *reader, ID *id, ListBase *list)
{
NlaStrip *strip;
for (strip = list->first; strip; strip = strip->next) {
/* check strip's children */
- lib_link_nladata_strips(fd, id, &strip->strips);
+ lib_link_nladata_strips(reader, id, &strip->strips);
/* check strip's F-Curves */
- lib_link_fcurves(fd, id, &strip->fcurves);
+ lib_link_fcurves(reader, id, &strip->fcurves);
/* reassign the counted-reference to action */
- strip->act = newlibadr(fd, id->lib, strip->act);
+ BLO_read_id_address(reader, id->lib, &strip->act);
}
}
-static void lib_link_nladata(FileData *fd, ID *id, ListBase *list)
+static void lib_link_nladata(BlendLibReader *reader, ID *id, ListBase *list)
{
NlaTrack *nlt;
/* we only care about the NLA strips inside the tracks */
for (nlt = list->first; nlt; nlt = nlt->next) {
- lib_link_nladata_strips(fd, id, &nlt->strips);
+ lib_link_nladata_strips(reader, id, &nlt->strips);
}
}
@@ -3403,7 +3370,7 @@ static void direct_link_nladata(BlendDataReader *reader, ListBase *list)
/* ------- */
-static void lib_link_keyingsets(FileData *fd, ID *id, ListBase *list)
+static void lib_link_keyingsets(BlendLibReader *reader, ID *id, ListBase *list)
{
KeyingSet *ks;
KS_Path *ksp;
@@ -3411,7 +3378,7 @@ static void lib_link_keyingsets(FileData *fd, ID *id, ListBase *list)
/* here, we're only interested in the ID pointer stored in some of the paths */
for (ks = list->first; ks; ks = ks->next) {
for (ksp = ks->paths.first; ksp; ksp = ksp->next) {
- ksp->id = newlibadr(fd, id->lib, ksp->id);
+ BLO_read_id_address(reader, id->lib, &ksp->id);
}
}
}
@@ -3436,23 +3403,23 @@ static void direct_link_keyingsets(BlendDataReader *reader, ListBase *list)
/* ------- */
-static void lib_link_animdata(FileData *fd, ID *id, AnimData *adt)
+static void lib_link_animdata(BlendLibReader *reader, ID *id, AnimData *adt)
{
if (adt == NULL) {
return;
}
/* link action data */
- adt->action = newlibadr(fd, id->lib, adt->action);
- adt->tmpact = newlibadr(fd, id->lib, adt->tmpact);
+ BLO_read_id_address(reader, id->lib, &adt->action);
+ BLO_read_id_address(reader, id->lib, &adt->tmpact);
/* link drivers */
- lib_link_fcurves(fd, id, &adt->drivers);
+ lib_link_fcurves(reader, id, &adt->drivers);
/* overrides don't have lib-link for now, so no need to do anything */
/* link NLA-data */
- lib_link_nladata(fd, id, &adt->nla_tracks);
+ lib_link_nladata(reader, id, &adt->nla_tracks);
}
static void direct_link_animdata(BlendDataReader *reader, AnimData *adt)
@@ -3490,9 +3457,7 @@ static void direct_link_animdata(BlendDataReader *reader, AnimData *adt)
/** \name Read ID: CacheFiles
* \{ */
-static void lib_link_cachefiles(FileData *UNUSED(fd),
- Main *UNUSED(bmain),
- CacheFile *UNUSED(cache_file))
+static void lib_link_cachefiles(BlendLibReader *UNUSED(reader), CacheFile *UNUSED(cache_file))
{
}
@@ -3514,26 +3479,26 @@ static void direct_link_cachefile(BlendDataReader *reader, CacheFile *cache_file
/** \name Read ID: WorkSpace
* \{ */
-static void lib_link_workspaces(FileData *fd, Main *bmain, WorkSpace *workspace)
+static void lib_link_workspaces(BlendLibReader *reader, WorkSpace *workspace)
{
ID *id = (ID *)workspace;
LISTBASE_FOREACH_MUTABLE (WorkSpaceLayout *, layout, &workspace->layouts) {
- layout->screen = newlibadr(fd, id->lib, layout->screen);
+ BLO_read_id_address(reader, id->lib, &layout->screen);
if (layout->screen) {
if (ID_IS_LINKED(id)) {
layout->screen->winid = 0;
if (layout->screen->temp) {
/* delete temp layouts when appending */
- BKE_workspace_layout_remove(bmain, workspace, layout);
+ BKE_workspace_layout_remove(reader->main, workspace, layout);
}
}
}
else {
/* If we're reading a layout without screen stored, it's useless and we shouldn't keep it
* around. */
- BKE_workspace_layout_remove(bmain, workspace, layout);
+ BKE_workspace_layout_remove(reader->main, workspace, layout);
}
}
}
@@ -3570,10 +3535,13 @@ static void direct_link_workspace(BlendDataReader *reader, WorkSpace *workspace,
id_us_ensure_real(&workspace->id);
}
-static void lib_link_workspace_instance_hook(FileData *fd, WorkSpaceInstanceHook *hook, ID *id)
+static void lib_link_workspace_instance_hook(BlendLibReader *reader,
+ WorkSpaceInstanceHook *hook,
+ ID *id)
{
WorkSpace *workspace = BKE_workspace_active_get(hook);
- BKE_workspace_active_set(hook, newlibadr(fd, id->lib, workspace));
+ BLO_read_id_address(reader, id->lib, &workspace);
+ BKE_workspace_active_set(hook, workspace);
}
/** \} */
@@ -3582,19 +3550,19 @@ static void lib_link_workspace_instance_hook(FileData *fd, WorkSpaceInstanceHook
/** \name Read ID: Node Tree
* \{ */
-static void lib_link_node_socket(FileData *fd, Library *lib, bNodeSocket *sock)
+static void lib_link_node_socket(BlendLibReader *reader, Library *lib, bNodeSocket *sock)
{
- IDP_LibLinkProperty(sock->prop, fd);
+ IDP_LibLinkProperty(sock->prop, reader);
switch ((eNodeSocketDatatype)sock->type) {
case SOCK_OBJECT: {
bNodeSocketValueObject *default_value = sock->default_value;
- default_value->value = newlibadr(fd, lib, default_value->value);
+ BLO_read_id_address(reader, lib, &default_value->value);
break;
}
case SOCK_IMAGE: {
bNodeSocketValueImage *default_value = sock->default_value;
- default_value->value = newlibadr(fd, lib, default_value->value);
+ BLO_read_id_address(reader, lib, &default_value->value);
break;
}
case SOCK_FLOAT:
@@ -3614,33 +3582,33 @@ static void lib_link_node_socket(FileData *fd, Library *lib, bNodeSocket *sock)
}
}
-static void lib_link_node_sockets(FileData *fd, Library *lib, ListBase *sockets)
+static void lib_link_node_sockets(BlendLibReader *reader, Library *lib, ListBase *sockets)
{
LISTBASE_FOREACH (bNodeSocket *, sock, sockets) {
- lib_link_node_socket(fd, lib, sock);
+ lib_link_node_socket(reader, lib, sock);
}
}
/* Single node tree (also used for material/scene trees), ntree is not NULL */
-static void lib_link_ntree(FileData *fd, Library *lib, bNodeTree *ntree)
+static void lib_link_ntree(BlendLibReader *reader, Library *lib, bNodeTree *ntree)
{
ntree->id.lib = lib;
- ntree->gpd = newlibadr(fd, lib, ntree->gpd);
+ BLO_read_id_address(reader, lib, &ntree->gpd);
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
/* Link ID Properties -- and copy this comment EXACTLY for easy finding
* of library blocks that implement this.*/
- IDP_LibLinkProperty(node->prop, fd);
+ IDP_LibLinkProperty(node->prop, reader);
- node->id = newlibadr(fd, lib, node->id);
+ BLO_read_id_address(reader, lib, &node->id);
- lib_link_node_sockets(fd, lib, &node->inputs);
- lib_link_node_sockets(fd, lib, &node->outputs);
+ lib_link_node_sockets(reader, lib, &node->inputs);
+ lib_link_node_sockets(reader, lib, &node->outputs);
}
- lib_link_node_sockets(fd, lib, &ntree->inputs);
- lib_link_node_sockets(fd, lib, &ntree->outputs);
+ lib_link_node_sockets(reader, lib, &ntree->inputs);
+ lib_link_node_sockets(reader, lib, &ntree->outputs);
/* Set node->typeinfo pointers. This is done in lib linking, after the
* first versioning that can change types still without functions that
@@ -3650,7 +3618,7 @@ static void lib_link_ntree(FileData *fd, Library *lib, bNodeTree *ntree)
/* For nodes with static socket layout, add/remove sockets as needed
* to match the static layout. */
- if (fd->memfile == NULL) {
+ if (reader->fd->memfile == NULL) {
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
node_verify_socket_templates(ntree, node);
}
@@ -3658,9 +3626,9 @@ static void lib_link_ntree(FileData *fd, Library *lib, bNodeTree *ntree)
}
/* library ntree linking after fileread */
-static void lib_link_nodetree(FileData *fd, Main *UNUSED(bmain), bNodeTree *ntree)
+static void lib_link_nodetree(BlendLibReader *reader, bNodeTree *ntree)
{
- lib_link_ntree(fd, ntree->id.lib, ntree);
+ lib_link_ntree(reader, ntree->id.lib, ntree);
}
static void direct_link_node_socket(BlendDataReader *reader, bNodeSocket *sock)
@@ -3730,7 +3698,7 @@ static void direct_link_nodetree(BlendDataReader *reader, bNodeTree *ntree)
case CMP_NODE_HUECORRECT:
case TEX_NODE_CURVE_RGB:
case TEX_NODE_CURVE_TIME: {
- direct_link_curvemapping(reader, node->storage);
+ BKE_curvemapping_blend_read(reader, node->storage);
break;
}
case SH_NODE_SCRIPT: {
@@ -3846,7 +3814,7 @@ static void direct_link_nodetree(BlendDataReader *reader, bNodeTree *ntree)
/* temp struct used to transport needed info to lib_link_constraint_cb() */
typedef struct tConstraintLinkData {
- FileData *fd;
+ BlendLibReader *reader;
ID *id;
} tConstraintLinkData;
/* callback function used to relink constraint ID-links */
@@ -3856,10 +3824,10 @@ static void lib_link_constraint_cb(bConstraint *UNUSED(con),
void *userdata)
{
tConstraintLinkData *cld = (tConstraintLinkData *)userdata;
- *idpoin = newlibadr(cld->fd, cld->id->lib, *idpoin);
+ BLO_read_id_address(cld->reader, cld->id->lib, idpoin);
}
-static void lib_link_constraints(FileData *fd, ID *id, ListBase *conlist)
+static void lib_link_constraints(BlendLibReader *reader, ID *id, ListBase *conlist)
{
tConstraintLinkData cld;
bConstraint *con;
@@ -3872,7 +3840,7 @@ static void lib_link_constraints(FileData *fd, ID *id, ListBase *conlist)
con->type = CONSTRAINT_TYPE_NULL;
}
/* own ipo, all constraints have it */
- con->ipo = newlibadr(fd, id->lib, con->ipo); // XXX deprecated - old animation system
+ BLO_read_id_address(reader, id->lib, &con->ipo); // XXX deprecated - old animation system
/* If linking from a library, clear 'local' library override flag. */
if (id->lib != NULL) {
@@ -3881,7 +3849,7 @@ static void lib_link_constraints(FileData *fd, ID *id, ListBase *conlist)
}
/* relink all ID-blocks used by the constraints */
- cld.fd = fd;
+ cld.reader = reader;
cld.id = id;
BKE_constraints_id_loop(conlist, lib_link_constraint_cb, &cld);
@@ -3944,7 +3912,7 @@ static void direct_link_constraints(BlendDataReader *reader, ListBase *lb)
}
}
-static void lib_link_pose(FileData *fd, Main *bmain, Object *ob, bPose *pose)
+static void lib_link_pose(BlendLibReader *reader, Object *ob, bPose *pose)
{
bArmature *arm = ob->data;
@@ -3955,7 +3923,7 @@ static void lib_link_pose(FileData *fd, Main *bmain, Object *ob, bPose *pose)
/* always rebuild to match proxy or lib changes, but on Undo */
bool rebuild = false;
- if (fd->memfile == NULL) {
+ if (reader->fd->memfile == NULL) {
if (ob->proxy || ob->id.lib != arm->id.lib) {
rebuild = true;
}
@@ -3977,13 +3945,13 @@ static void lib_link_pose(FileData *fd, Main *bmain, Object *ob, bPose *pose)
}
LISTBASE_FOREACH (bPoseChannel *, pchan, &pose->chanbase) {
- lib_link_constraints(fd, (ID *)ob, &pchan->constraints);
+ lib_link_constraints(reader, (ID *)ob, &pchan->constraints);
pchan->bone = BKE_armature_find_bone_name(arm, pchan->name);
- IDP_LibLinkProperty(pchan->prop, fd);
+ IDP_LibLinkProperty(pchan->prop, reader);
- pchan->custom = newlibadr(fd, arm->id.lib, pchan->custom);
+ BLO_read_id_address(reader, arm->id.lib, &pchan->custom);
if (UNLIKELY(pchan->bone == NULL)) {
rebuild = true;
}
@@ -3996,24 +3964,24 @@ static void lib_link_pose(FileData *fd, Main *bmain, Object *ob, bPose *pose)
if (rebuild) {
DEG_id_tag_update_ex(
- bmain, &ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION);
- BKE_pose_tag_recalc(bmain, pose);
+ reader->main, &ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION);
+ BKE_pose_tag_recalc(reader->main, pose);
}
}
-static void lib_link_bones(FileData *fd, Bone *bone)
+static void lib_link_bones(BlendLibReader *reader, Bone *bone)
{
- IDP_LibLinkProperty(bone->prop, fd);
+ IDP_LibLinkProperty(bone->prop, reader);
LISTBASE_FOREACH (Bone *, curbone, &bone->childbase) {
- lib_link_bones(fd, curbone);
+ lib_link_bones(reader, curbone);
}
}
-static void lib_link_armature(FileData *fd, Main *UNUSED(bmain), bArmature *arm)
+static void lib_link_armature(BlendLibReader *reader, bArmature *arm)
{
LISTBASE_FOREACH (Bone *, curbone, &arm->bonebase) {
- lib_link_bones(fd, curbone);
+ lib_link_bones(reader, curbone);
}
}
@@ -4066,16 +4034,16 @@ static void direct_link_armature(BlendDataReader *reader, bArmature *arm)
/** \name Read ID: Camera
* \{ */
-static void lib_link_camera(FileData *fd, Main *UNUSED(bmain), Camera *ca)
+static void lib_link_camera(BlendLibReader *reader, Camera *ca)
{
- ca->ipo = newlibadr(fd, ca->id.lib, ca->ipo); /* deprecated, for versioning */
+ BLO_read_id_address(reader, ca->id.lib, &ca->ipo); /* deprecated, for versioning */
- ca->dof_ob = newlibadr(fd, ca->id.lib, ca->dof_ob); /* deprecated, for versioning */
- ca->dof.focus_object = newlibadr(fd, ca->id.lib, ca->dof.focus_object);
+ BLO_read_id_address(reader, ca->id.lib, &ca->dof_ob); /* deprecated, for versioning */
+ BLO_read_id_address(reader, ca->id.lib, &ca->dof.focus_object);
LISTBASE_FOREACH (CameraBGImage *, bgpic, &ca->bg_images) {
- bgpic->ima = newlibadr(fd, ca->id.lib, bgpic->ima);
- bgpic->clip = newlibadr(fd, ca->id.lib, bgpic->clip);
+ BLO_read_id_address(reader, ca->id.lib, &bgpic->ima);
+ BLO_read_id_address(reader, ca->id.lib, &bgpic->clip);
}
}
@@ -4098,9 +4066,9 @@ static void direct_link_camera(BlendDataReader *reader, Camera *ca)
/** \name Read ID: Light
* \{ */
-static void lib_link_light(FileData *fd, Main *UNUSED(bmain), Light *la)
+static void lib_link_light(BlendLibReader *reader, Light *la)
{
- la->ipo = newlibadr(fd, la->id.lib, la->ipo); // XXX deprecated - old animation system
+ BLO_read_id_address(reader, la->id.lib, &la->ipo); // XXX deprecated - old animation system
}
static void direct_link_light(BlendDataReader *reader, Light *la)
@@ -4110,7 +4078,7 @@ static void direct_link_light(BlendDataReader *reader, Light *la)
BLO_read_data_address(reader, &la->curfalloff);
if (la->curfalloff) {
- direct_link_curvemapping(reader, la->curfalloff);
+ BKE_curvemapping_blend_read(reader, la->curfalloff);
}
la->preview = direct_link_preview_image(reader, la->preview);
@@ -4132,12 +4100,12 @@ void blo_do_versions_key_uidgen(Key *key)
}
}
-static void lib_link_key(FileData *fd, Main *UNUSED(bmain), Key *key)
+static void lib_link_key(BlendLibReader *reader, Key *key)
{
BLI_assert((key->id.tag & LIB_TAG_EXTERN) == 0);
- key->ipo = newlibadr(fd, key->id.lib, key->ipo); // XXX deprecated - old animation system
- key->from = newlibadr(fd, key->id.lib, key->from);
+ BLO_read_id_address(reader, key->id.lib, &key->ipo); // XXX deprecated - old animation system
+ BLO_read_id_address(reader, key->id.lib, &key->from);
}
static void switch_endian_keyblock(Key *key, KeyBlock *kb)
@@ -4195,13 +4163,13 @@ static void direct_link_key(BlendDataReader *reader, Key *key)
/** \name Read ID: Meta Ball
* \{ */
-static void lib_link_mball(FileData *fd, Main *UNUSED(bmain), MetaBall *mb)
+static void lib_link_mball(BlendLibReader *reader, MetaBall *mb)
{
for (int a = 0; a < mb->totcol; a++) {
- mb->mat[a] = newlibadr(fd, mb->id.lib, mb->mat[a]);
+ BLO_read_id_address(reader, mb->id.lib, &mb->mat[a]);
}
- mb->ipo = newlibadr(fd, mb->id.lib, mb->ipo); // XXX deprecated - old animation system
+ BLO_read_id_address(reader, mb->id.lib, &mb->ipo); // XXX deprecated - old animation system
}
static void direct_link_mball(BlendDataReader *reader, MetaBall *mb)
@@ -4228,9 +4196,9 @@ static void direct_link_mball(BlendDataReader *reader, MetaBall *mb)
/** \name Read ID: World
* \{ */
-static void lib_link_world(FileData *fd, Main *UNUSED(bmain), World *wrld)
+static void lib_link_world(BlendLibReader *reader, World *wrld)
{
- wrld->ipo = newlibadr(fd, wrld->id.lib, wrld->ipo); // XXX deprecated - old animation system
+ BLO_read_id_address(reader, wrld->id.lib, &wrld->ipo); // XXX deprecated - old animation system
}
static void direct_link_world(BlendDataReader *reader, World *wrld)
@@ -4248,7 +4216,7 @@ static void direct_link_world(BlendDataReader *reader, World *wrld)
/** \name Read ID: VFont
* \{ */
-static void lib_link_vfont(FileData *UNUSED(fd), Main *UNUSED(bmain), VFont *UNUSED(vf))
+static void lib_link_vfont(BlendLibReader *UNUSED(reader), VFont *UNUSED(vf))
{
}
@@ -4265,7 +4233,7 @@ static void direct_link_vfont(BlendDataReader *reader, VFont *vf)
/** \name Read ID: Text
* \{ */
-static void lib_link_text(FileData *UNUSED(fd), Main *UNUSED(bmain), Text *UNUSED(text))
+static void lib_link_text(BlendLibReader *UNUSED(reader), Text *UNUSED(text))
{
}
@@ -4273,7 +4241,7 @@ static void direct_link_text(BlendDataReader *reader, Text *text)
{
TextLine *ln;
- BLO_read_data_address(reader, &text->name);
+ BLO_read_data_address(reader, &text->filepath);
text->compiled = NULL;
@@ -4310,7 +4278,7 @@ static void direct_link_text(BlendDataReader *reader, Text *text)
/** \name Read ID: Image
* \{ */
-static void lib_link_image(FileData *UNUSED(fd), Main *UNUSED(bmain), Image *UNUSED(ima))
+static void lib_link_image(BlendLibReader *UNUSED(reader), Image *UNUSED(ima))
{
}
@@ -4389,22 +4357,22 @@ static void direct_link_image(BlendDataReader *reader, Image *ima)
/** \name Read ID: Curve
* \{ */
-static void lib_link_curve(FileData *fd, Main *UNUSED(bmain), Curve *cu)
+static void lib_link_curve(BlendLibReader *reader, Curve *cu)
{
for (int a = 0; a < cu->totcol; a++) {
- cu->mat[a] = newlibadr(fd, cu->id.lib, cu->mat[a]);
+ BLO_read_id_address(reader, cu->id.lib, &cu->mat[a]);
}
- cu->bevobj = newlibadr(fd, cu->id.lib, cu->bevobj);
- cu->taperobj = newlibadr(fd, cu->id.lib, cu->taperobj);
- cu->textoncurve = newlibadr(fd, cu->id.lib, cu->textoncurve);
- cu->vfont = newlibadr(fd, cu->id.lib, cu->vfont);
- cu->vfontb = newlibadr(fd, cu->id.lib, cu->vfontb);
- cu->vfonti = newlibadr(fd, cu->id.lib, cu->vfonti);
- cu->vfontbi = newlibadr(fd, cu->id.lib, cu->vfontbi);
+ BLO_read_id_address(reader, cu->id.lib, &cu->bevobj);
+ BLO_read_id_address(reader, cu->id.lib, &cu->taperobj);
+ BLO_read_id_address(reader, cu->id.lib, &cu->textoncurve);
+ BLO_read_id_address(reader, cu->id.lib, &cu->vfont);
+ BLO_read_id_address(reader, cu->id.lib, &cu->vfontb);
+ BLO_read_id_address(reader, cu->id.lib, &cu->vfonti);
+ BLO_read_id_address(reader, cu->id.lib, &cu->vfontbi);
- cu->ipo = newlibadr(fd, cu->id.lib, cu->ipo); // XXX deprecated - old animation system
- cu->key = newlibadr(fd, cu->id.lib, cu->key);
+ BLO_read_id_address(reader, cu->id.lib, &cu->ipo); // XXX deprecated - old animation system
+ BLO_read_id_address(reader, cu->id.lib, &cu->key);
}
static void switch_endian_knots(Nurb *nu)
@@ -4483,10 +4451,10 @@ static void direct_link_curve(BlendDataReader *reader, Curve *cu)
/** \name Read ID: Texture
* \{ */
-static void lib_link_texture(FileData *fd, Main *UNUSED(bmain), Tex *tex)
+static void lib_link_texture(BlendLibReader *reader, Tex *tex)
{
- tex->ima = newlibadr(fd, tex->id.lib, tex->ima);
- tex->ipo = newlibadr(fd, tex->id.lib, tex->ipo); // XXX deprecated - old animation system
+ BLO_read_id_address(reader, tex->id.lib, &tex->ima);
+ BLO_read_id_address(reader, tex->id.lib, &tex->ipo); // XXX deprecated - old animation system
}
static void direct_link_texture(BlendDataReader *reader, Tex *tex)
@@ -4508,18 +4476,18 @@ static void direct_link_texture(BlendDataReader *reader, Tex *tex)
/** \name Read ID: Material
* \{ */
-static void lib_link_material(FileData *fd, Main *UNUSED(bmain), Material *ma)
+static void lib_link_material(BlendLibReader *reader, Material *ma)
{
- ma->ipo = newlibadr(fd, ma->id.lib, ma->ipo); // XXX deprecated - old animation system
+ BLO_read_id_address(reader, ma->id.lib, &ma->ipo); // XXX deprecated - old animation system
/* relink grease pencil settings */
if (ma->gp_style != NULL) {
MaterialGPencilStyle *gp_style = ma->gp_style;
if (gp_style->sima != NULL) {
- gp_style->sima = newlibadr(fd, ma->id.lib, gp_style->sima);
+ BLO_read_id_address(reader, ma->id.lib, &gp_style->sima);
}
if (gp_style->ima != NULL) {
- gp_style->ima = newlibadr(fd, ma->id.lib, gp_style->ima);
+ BLO_read_id_address(reader, ma->id.lib, &gp_style->ima);
}
}
}
@@ -4630,31 +4598,31 @@ static void direct_link_pointcache_list(BlendDataReader *reader,
}
}
-static void lib_link_partdeflect(FileData *fd, ID *id, PartDeflect *pd)
+static void lib_link_partdeflect(BlendLibReader *reader, ID *id, PartDeflect *pd)
{
if (pd && pd->tex) {
- pd->tex = newlibadr(fd, id->lib, pd->tex);
+ BLO_read_id_address(reader, id->lib, &pd->tex);
}
if (pd && pd->f_source) {
- pd->f_source = newlibadr(fd, id->lib, pd->f_source);
+ BLO_read_id_address(reader, id->lib, &pd->f_source);
}
}
-static void lib_link_particlesettings(FileData *fd, Main *UNUSED(bmain), ParticleSettings *part)
+static void lib_link_particlesettings(BlendLibReader *reader, ParticleSettings *part)
{
- part->ipo = newlibadr(fd, part->id.lib, part->ipo); // XXX deprecated - old animation system
+ BLO_read_id_address(reader, part->id.lib, &part->ipo); // XXX deprecated - old animation system
- part->instance_object = newlibadr(fd, part->id.lib, part->instance_object);
- part->instance_collection = newlibadr(fd, part->id.lib, part->instance_collection);
- part->force_group = newlibadr(fd, part->id.lib, part->force_group);
- part->bb_ob = newlibadr(fd, part->id.lib, part->bb_ob);
- part->collision_group = newlibadr(fd, part->id.lib, part->collision_group);
+ BLO_read_id_address(reader, part->id.lib, &part->instance_object);
+ BLO_read_id_address(reader, part->id.lib, &part->instance_collection);
+ BLO_read_id_address(reader, part->id.lib, &part->force_group);
+ BLO_read_id_address(reader, part->id.lib, &part->bb_ob);
+ BLO_read_id_address(reader, part->id.lib, &part->collision_group);
- lib_link_partdeflect(fd, &part->id, part->pd);
- lib_link_partdeflect(fd, &part->id, part->pd2);
+ lib_link_partdeflect(reader, &part->id, part->pd);
+ lib_link_partdeflect(reader, &part->id, part->pd2);
if (part->effector_weights) {
- part->effector_weights->group = newlibadr(fd, part->id.lib, part->effector_weights->group);
+ BLO_read_id_address(reader, part->id.lib, &part->effector_weights->group);
}
else {
part->effector_weights = BKE_effector_add_weights(part->force_group);
@@ -4662,7 +4630,7 @@ static void lib_link_particlesettings(FileData *fd, Main *UNUSED(bmain), Particl
if (part->instance_weights.first && part->instance_collection) {
LISTBASE_FOREACH (ParticleDupliWeight *, dw, &part->instance_weights) {
- dw->ob = newlibadr(fd, part->id.lib, dw->ob);
+ BLO_read_id_address(reader, part->id.lib, &dw->ob);
}
}
else {
@@ -4679,12 +4647,12 @@ static void lib_link_particlesettings(FileData *fd, Main *UNUSED(bmain), Particl
case eBoidRuleType_Goal:
case eBoidRuleType_Avoid: {
BoidRuleGoalAvoid *brga = (BoidRuleGoalAvoid *)rule;
- brga->ob = newlibadr(fd, part->id.lib, brga->ob);
+ BLO_read_id_address(reader, part->id.lib, &brga->ob);
break;
}
case eBoidRuleType_FollowLeader: {
BoidRuleFollowLeader *brfl = (BoidRuleFollowLeader *)rule;
- brfl->ob = newlibadr(fd, part->id.lib, brfl->ob);
+ BLO_read_id_address(reader, part->id.lib, &brfl->ob);
break;
}
}
@@ -4695,8 +4663,8 @@ static void lib_link_particlesettings(FileData *fd, Main *UNUSED(bmain), Particl
for (int a = 0; a < MAX_MTEX; a++) {
MTex *mtex = part->mtex[a];
if (mtex) {
- mtex->tex = newlibadr(fd, part->id.lib, mtex->tex);
- mtex->object = newlibadr(fd, part->id.lib, mtex->object);
+ BLO_read_id_address(reader, part->id.lib, &mtex->tex);
+ BLO_read_id_address(reader, part->id.lib, &mtex->object);
}
}
}
@@ -4722,15 +4690,15 @@ static void direct_link_particlesettings(BlendDataReader *reader, ParticleSettin
BLO_read_data_address(reader, &part->clumpcurve);
if (part->clumpcurve) {
- direct_link_curvemapping(reader, part->clumpcurve);
+ BKE_curvemapping_blend_read(reader, part->clumpcurve);
}
BLO_read_data_address(reader, &part->roughcurve);
if (part->roughcurve) {
- direct_link_curvemapping(reader, part->roughcurve);
+ BKE_curvemapping_blend_read(reader, part->roughcurve);
}
BLO_read_data_address(reader, &part->twistcurve);
if (part->twistcurve) {
- direct_link_curvemapping(reader, part->twistcurve);
+ BKE_curvemapping_blend_read(reader, part->twistcurve);
}
BLO_read_data_address(reader, &part->effector_weights);
@@ -4761,30 +4729,33 @@ static void direct_link_particlesettings(BlendDataReader *reader, ParticleSettin
CLAMP(part->trail_count, 1, 100000);
}
-static void lib_link_particlesystems(FileData *fd, Object *ob, ID *id, ListBase *particles)
+static void lib_link_particlesystems(BlendLibReader *reader,
+ Object *ob,
+ ID *id,
+ ListBase *particles)
{
ParticleSystem *psys, *psysnext;
for (psys = particles->first; psys; psys = psysnext) {
psysnext = psys->next;
- psys->part = newlibadr(fd, id->lib, psys->part);
+ BLO_read_id_address(reader, id->lib, &psys->part);
if (psys->part) {
ParticleTarget *pt = psys->targets.first;
for (; pt; pt = pt->next) {
- pt->ob = newlibadr(fd, id->lib, pt->ob);
+ BLO_read_id_address(reader, id->lib, &pt->ob);
}
- psys->parent = newlibadr(fd, id->lib, psys->parent);
- psys->target_ob = newlibadr(fd, id->lib, psys->target_ob);
+ BLO_read_id_address(reader, id->lib, &psys->parent);
+ BLO_read_id_address(reader, id->lib, &psys->target_ob);
if (psys->clmd) {
/* XXX - from reading existing code this seems correct but intended usage of
* pointcache /w cloth should be added in 'ParticleSystem' - campbell */
psys->clmd->point_cache = psys->pointcache;
psys->clmd->ptcaches.first = psys->clmd->ptcaches.last = NULL;
- psys->clmd->coll_parms->group = newlibadr(fd, id->lib, psys->clmd->coll_parms->group);
+ BLO_read_id_address(reader, id->lib, &psys->clmd->coll_parms->group);
psys->clmd->modifier.error = NULL;
}
}
@@ -4895,21 +4866,21 @@ static void direct_link_particlesystems(BlendDataReader *reader, ListBase *parti
/** \name Read ID: Mesh
* \{ */
-static void lib_link_mesh(FileData *fd, Main *UNUSED(bmain), Mesh *me)
+static void lib_link_mesh(BlendLibReader *reader, Mesh *me)
{
/* this check added for python created meshes */
if (me->mat) {
for (int i = 0; i < me->totcol; i++) {
- me->mat[i] = newlibadr(fd, me->id.lib, me->mat[i]);
+ BLO_read_id_address(reader, me->id.lib, &me->mat[i]);
}
}
else {
me->totcol = 0;
}
- me->ipo = newlibadr(fd, me->id.lib, me->ipo); // XXX: deprecated: old anim sys
- me->key = newlibadr(fd, me->id.lib, me->key);
- me->texcomesh = newlibadr(fd, me->id.lib, me->texcomesh);
+ BLO_read_id_address(reader, me->id.lib, &me->ipo); // XXX: deprecated: old anim sys
+ BLO_read_id_address(reader, me->id.lib, &me->key);
+ BLO_read_id_address(reader, me->id.lib, &me->texcomesh);
}
static void direct_link_dverts(BlendDataReader *reader, int count, MDeformVert *mdverts)
@@ -5124,10 +5095,10 @@ static void direct_link_mesh(BlendDataReader *reader, Mesh *mesh)
/** \name Read ID: Lattice
* \{ */
-static void lib_link_latt(FileData *fd, Main *UNUSED(bmain), Lattice *lt)
+static void lib_link_latt(BlendLibReader *reader, Lattice *lt)
{
- lt->ipo = newlibadr(fd, lt->id.lib, lt->ipo); // XXX deprecated - old animation system
- lt->key = newlibadr(fd, lt->id.lib, lt->key);
+ BLO_read_id_address(reader, lt->id.lib, &lt->ipo); // XXX deprecated - old animation system
+ BLO_read_id_address(reader, lt->id.lib, &lt->key);
}
static void direct_link_latt(BlendDataReader *reader, Lattice *lt)
@@ -5152,17 +5123,17 @@ static void direct_link_latt(BlendDataReader *reader, Lattice *lt)
static void lib_link_modifiers_common(void *userData, Object *ob, ID **idpoin, int cb_flag)
{
- FileData *fd = userData;
+ BlendLibReader *reader = userData;
- *idpoin = newlibadr(fd, ob->id.lib, *idpoin);
+ BLO_read_id_address(reader, ob->id.lib, idpoin);
if (*idpoin != NULL && (cb_flag & IDWALK_CB_USER) != 0) {
id_us_plus_no_lib(*idpoin);
}
}
-static void lib_link_modifiers(FileData *fd, Object *ob)
+static void lib_link_modifiers(BlendLibReader *reader, Object *ob)
{
- BKE_modifiers_foreach_ID_link(ob, lib_link_modifiers_common, fd);
+ BKE_modifiers_foreach_ID_link(ob, lib_link_modifiers_common, reader);
/* If linking from a library, clear 'local' library override flag. */
if (ob->id.lib != NULL) {
@@ -5172,9 +5143,9 @@ static void lib_link_modifiers(FileData *fd, Object *ob)
}
}
-static void lib_link_gpencil_modifiers(FileData *fd, Object *ob)
+static void lib_link_gpencil_modifiers(BlendLibReader *reader, Object *ob)
{
- BKE_gpencil_modifiers_foreach_ID_link(ob, lib_link_modifiers_common, fd);
+ BKE_gpencil_modifiers_foreach_ID_link(ob, lib_link_modifiers_common, reader);
/* If linking from a library, clear 'local' library override flag. */
if (ob->id.lib != NULL) {
@@ -5185,9 +5156,9 @@ static void lib_link_gpencil_modifiers(FileData *fd, Object *ob)
}
}
-static void lib_link_shaderfxs(FileData *fd, Object *ob)
+static void lib_link_shaderfxs(BlendLibReader *reader, Object *ob)
{
- BKE_shaderfx_foreach_ID_link(ob, lib_link_modifiers_common, fd);
+ BKE_shaderfx_foreach_ID_link(ob, lib_link_modifiers_common, reader);
/* If linking from a library, clear 'local' library override flag. */
if (ob->id.lib != NULL) {
@@ -5197,28 +5168,28 @@ static void lib_link_shaderfxs(FileData *fd, Object *ob)
}
}
-static void lib_link_object(FileData *fd, Main *bmain, Object *ob)
+static void lib_link_object(BlendLibReader *reader, Object *ob)
{
bool warn = false;
int a;
// XXX deprecated - old animation system <<<
- ob->ipo = newlibadr(fd, ob->id.lib, ob->ipo);
- ob->action = newlibadr(fd, ob->id.lib, ob->action);
+ BLO_read_id_address(reader, ob->id.lib, &ob->ipo);
+ BLO_read_id_address(reader, ob->id.lib, &ob->action);
// >>> XXX deprecated - old animation system
- ob->parent = newlibadr(fd, ob->id.lib, ob->parent);
- ob->track = newlibadr(fd, ob->id.lib, ob->track);
- ob->poselib = newlibadr(fd, ob->id.lib, ob->poselib);
+ BLO_read_id_address(reader, ob->id.lib, &ob->parent);
+ BLO_read_id_address(reader, ob->id.lib, &ob->track);
+ BLO_read_id_address(reader, ob->id.lib, &ob->poselib);
/* 2.8x drops support for non-empty dupli instances. */
if (ob->type == OB_EMPTY) {
- ob->instance_collection = newlibadr(fd, ob->id.lib, ob->instance_collection);
+ BLO_read_id_address(reader, ob->id.lib, &ob->instance_collection);
}
else {
if (ob->instance_collection != NULL) {
- ID *id = newlibadr(fd, ob->id.lib, ob->instance_collection);
- blo_reportf_wrap(fd->reports,
+ ID *id = BLO_read_get_new_id_address(reader, ob->id.lib, &ob->instance_collection->id);
+ blo_reportf_wrap(reader->fd->reports,
RPT_WARNING,
TIP_("Non-Empty object '%s' cannot duplicate collection '%s' "
"anymore in Blender 2.80, removed instancing"),
@@ -5229,7 +5200,7 @@ static void lib_link_object(FileData *fd, Main *bmain, Object *ob)
ob->transflag &= ~OB_DUPLICOLLECTION;
}
- ob->proxy = newlibadr(fd, ob->id.lib, ob->proxy);
+ BLO_read_id_address(reader, ob->id.lib, &ob->proxy);
if (ob->proxy) {
/* paranoia check, actually a proxy_from pointer should never be written... */
if (ob->proxy->id.lib == NULL) {
@@ -5237,7 +5208,7 @@ static void lib_link_object(FileData *fd, Main *bmain, Object *ob)
ob->proxy = NULL;
if (ob->id.lib) {
- printf("Proxy lost from object %s lib %s\n", ob->id.name + 2, ob->id.lib->name);
+ printf("Proxy lost from object %s lib %s\n", ob->id.name + 2, ob->id.lib->filepath);
}
else {
printf("Proxy lost from object %s lib <NONE>\n", ob->id.name + 2);
@@ -5248,14 +5219,14 @@ static void lib_link_object(FileData *fd, Main *bmain, Object *ob)
ob->proxy->proxy_from = ob;
}
}
- ob->proxy_group = newlibadr(fd, ob->id.lib, ob->proxy_group);
+ BLO_read_id_address(reader, ob->id.lib, &ob->proxy_group);
void *poin = ob->data;
- ob->data = newlibadr(fd, ob->id.lib, ob->data);
+ BLO_read_id_address(reader, ob->id.lib, &ob->data);
if (ob->data == NULL && poin != NULL) {
if (ob->id.lib) {
- printf("Can't find obdata of %s lib %s\n", ob->id.name + 2, ob->id.lib->name);
+ printf("Can't find obdata of %s lib %s\n", ob->id.name + 2, ob->id.lib->filepath);
}
else {
printf("Object %s lost data.\n", ob->id.name + 2);
@@ -5280,7 +5251,7 @@ static void lib_link_object(FileData *fd, Main *bmain, Object *ob)
}
}
for (a = 0; a < ob->totcol; a++) {
- ob->mat[a] = newlibadr(fd, ob->id.lib, ob->mat[a]);
+ BLO_read_id_address(reader, ob->id.lib, &ob->mat[a]);
}
/* When the object is local and the data is library its possible
@@ -5290,26 +5261,26 @@ static void lib_link_object(FileData *fd, Main *bmain, Object *ob)
/* Only expand so as not to loose any object materials that might be set. */
if (totcol_data && (*totcol_data > ob->totcol)) {
/* printf("'%s' %d -> %d\n", ob->id.name, ob->totcol, *totcol_data); */
- BKE_object_material_resize(bmain, ob, *totcol_data, false);
+ BKE_object_material_resize(reader->main, ob, *totcol_data, false);
}
}
- ob->gpd = newlibadr(fd, ob->id.lib, ob->gpd);
+ BLO_read_id_address(reader, ob->id.lib, &ob->gpd);
/* if id.us==0 a new base will be created later on */
/* WARNING! Also check expand_object(), should reflect the stuff below. */
- lib_link_pose(fd, bmain, ob, ob->pose);
- lib_link_constraints(fd, &ob->id, &ob->constraints);
+ lib_link_pose(reader, ob, ob->pose);
+ lib_link_constraints(reader, &ob->id, &ob->constraints);
// XXX deprecated - old animation system <<<
- lib_link_constraint_channels(fd, &ob->id, &ob->constraintChannels);
- lib_link_nlastrips(fd, &ob->id, &ob->nlastrips);
+ lib_link_constraint_channels(reader, &ob->id, &ob->constraintChannels);
+ lib_link_nlastrips(reader, &ob->id, &ob->nlastrips);
// >>> XXX deprecated - old animation system
LISTBASE_FOREACH (PartEff *, paf, &ob->effect) {
if (paf->type == EFF_PARTICLE) {
- paf->group = newlibadr(fd, ob->id.lib, paf->group);
+ BLO_read_id_address(reader, ob->id.lib, &paf->group);
}
}
@@ -5318,8 +5289,8 @@ static void lib_link_object(FileData *fd, Main *bmain, Object *ob)
ob, eModifierType_Fluidsim);
if (fluidmd && fluidmd->fss) {
- fluidmd->fss->ipo = newlibadr(
- fd, ob->id.lib, fluidmd->fss->ipo); // XXX deprecated - old animation system
+ BLO_read_id_address(
+ reader, ob->id.lib, &fluidmd->fss->ipo); // XXX deprecated - old animation system
}
}
@@ -5341,30 +5312,29 @@ static void lib_link_object(FileData *fd, Main *bmain, Object *ob)
/* texture field */
if (ob->pd) {
- lib_link_partdeflect(fd, &ob->id, ob->pd);
+ lib_link_partdeflect(reader, &ob->id, ob->pd);
}
if (ob->soft) {
- ob->soft->collision_group = newlibadr(fd, ob->id.lib, ob->soft->collision_group);
+ BLO_read_id_address(reader, ob->id.lib, &ob->soft->collision_group);
- ob->soft->effector_weights->group = newlibadr(
- fd, ob->id.lib, ob->soft->effector_weights->group);
+ BLO_read_id_address(reader, ob->id.lib, &ob->soft->effector_weights->group);
}
- lib_link_particlesystems(fd, ob, &ob->id, &ob->particlesystem);
- lib_link_modifiers(fd, ob);
- lib_link_gpencil_modifiers(fd, ob);
- lib_link_shaderfxs(fd, ob);
+ lib_link_particlesystems(reader, ob, &ob->id, &ob->particlesystem);
+ lib_link_modifiers(reader, ob);
+ lib_link_gpencil_modifiers(reader, ob);
+ lib_link_shaderfxs(reader, ob);
if (ob->rigidbody_constraint) {
- ob->rigidbody_constraint->ob1 = newlibadr(fd, ob->id.lib, ob->rigidbody_constraint->ob1);
- ob->rigidbody_constraint->ob2 = newlibadr(fd, ob->id.lib, ob->rigidbody_constraint->ob2);
+ BLO_read_id_address(reader, ob->id.lib, &ob->rigidbody_constraint->ob1);
+ BLO_read_id_address(reader, ob->id.lib, &ob->rigidbody_constraint->ob2);
}
{
LodLevel *level;
for (level = ob->lodlevels.first; level; level = level->next) {
- level->source = newlibadr(fd, ob->id.lib, level->source);
+ BLO_read_id_address(reader, ob->id.lib, &level->source);
if (!level->source && level == ob->lodlevels.first) {
level->source = ob;
@@ -5373,7 +5343,7 @@ static void lib_link_object(FileData *fd, Main *bmain, Object *ob)
}
if (warn) {
- BKE_report(fd->reports, RPT_WARNING, "Warning in console");
+ BKE_report(reader->fd->reports, RPT_WARNING, "Warning in console");
}
}
@@ -5600,16 +5570,6 @@ static void direct_link_modifiers(BlendDataReader *reader, ListBase *lb, Object
if (is_allocated) {
/* All the fields has been properly allocated. */
}
- else if (md->type == eModifierType_Subsurf) {
- SubsurfModifierData *smd = (SubsurfModifierData *)md;
-
- smd->emCache = smd->mCache = NULL;
- }
- else if (md->type == eModifierType_Armature) {
- ArmatureModifierData *amd = (ArmatureModifierData *)md;
-
- amd->vert_coords_prev = NULL;
- }
else if (md->type == eModifierType_Cloth) {
ClothModifierData *clmd = (ClothModifierData *)md;
@@ -5749,148 +5709,6 @@ static void direct_link_modifiers(BlendDataReader *reader, ListBase *lb, Object
BLO_read_data_address(reader, &pmd->brush->vel_ramp);
}
}
- else if (md->type == eModifierType_Collision) {
- CollisionModifierData *collmd = (CollisionModifierData *)md;
-#if 0
- // TODO: CollisionModifier should use pointcache
- // + have proper reset events before enabling this
- collmd->x = newdataadr(fd, collmd->x);
- collmd->xnew = newdataadr(fd, collmd->xnew);
- collmd->mfaces = newdataadr(fd, collmd->mfaces);
-
- collmd->current_x = MEM_calloc_arrayN(collmd->numverts, sizeof(MVert), "current_x");
- collmd->current_xnew = MEM_calloc_arrayN(collmd->numverts, sizeof(MVert), "current_xnew");
- collmd->current_v = MEM_calloc_arrayN(collmd->numverts, sizeof(MVert), "current_v");
-#endif
-
- collmd->x = NULL;
- collmd->xnew = NULL;
- collmd->current_x = NULL;
- collmd->current_xnew = NULL;
- collmd->current_v = NULL;
- collmd->time_x = collmd->time_xnew = -1000;
- collmd->mvert_num = 0;
- collmd->tri_num = 0;
- collmd->is_static = false;
- collmd->bvhtree = NULL;
- collmd->tri = NULL;
- }
- else if (md->type == eModifierType_Surface) {
- SurfaceModifierData *surmd = (SurfaceModifierData *)md;
-
- surmd->mesh = NULL;
- surmd->bvhtree = NULL;
- surmd->x = NULL;
- surmd->v = NULL;
- surmd->numverts = 0;
- }
- else if (md->type == eModifierType_Hook) {
- HookModifierData *hmd = (HookModifierData *)md;
- BLO_read_int32_array(reader, hmd->totindex, &hmd->indexar);
-
- BLO_read_data_address(reader, &hmd->curfalloff);
- if (hmd->curfalloff) {
- direct_link_curvemapping(reader, hmd->curfalloff);
- }
- }
- else if (md->type == eModifierType_ParticleSystem) {
- ParticleSystemModifierData *psmd = (ParticleSystemModifierData *)md;
-
- psmd->mesh_final = NULL;
- psmd->mesh_original = NULL;
- BLO_read_data_address(reader, &psmd->psys);
- psmd->flag &= ~eParticleSystemFlag_psys_updated;
- psmd->flag |= eParticleSystemFlag_file_loaded;
- }
- else if (md->type == eModifierType_Explode) {
- ExplodeModifierData *psmd = (ExplodeModifierData *)md;
-
- psmd->facepa = NULL;
- }
- else if (md->type == eModifierType_MeshDeform) {
- MeshDeformModifierData *mmd = (MeshDeformModifierData *)md;
-
- BLO_read_data_address(reader, &mmd->bindinfluences);
- BLO_read_int32_array(reader, mmd->totvert + 1, &mmd->bindoffsets);
- BLO_read_float3_array(reader, mmd->totcagevert, &mmd->bindcagecos);
- BLO_read_data_address(reader, &mmd->dyngrid);
- BLO_read_data_address(reader, &mmd->dyninfluences);
- BLO_read_int32_array(reader, mmd->totvert, &mmd->dynverts);
-
- BLO_read_float_array(reader, mmd->totvert, &mmd->bindweights);
- BLO_read_float3_array(reader, mmd->totcagevert, &mmd->bindcos);
- }
- else if (md->type == eModifierType_Ocean) {
- OceanModifierData *omd = (OceanModifierData *)md;
- omd->oceancache = NULL;
- omd->ocean = NULL;
- }
- else if (md->type == eModifierType_Warp) {
- WarpModifierData *tmd = (WarpModifierData *)md;
-
- BLO_read_data_address(reader, &tmd->curfalloff);
- if (tmd->curfalloff) {
- direct_link_curvemapping(reader, tmd->curfalloff);
- }
- }
- else if (md->type == eModifierType_WeightVGEdit) {
- WeightVGEditModifierData *wmd = (WeightVGEditModifierData *)md;
-
- BLO_read_data_address(reader, &wmd->cmap_curve);
- if (wmd->cmap_curve) {
- direct_link_curvemapping(reader, wmd->cmap_curve);
- }
- }
- else if (md->type == eModifierType_CorrectiveSmooth) {
- CorrectiveSmoothModifierData *csmd = (CorrectiveSmoothModifierData *)md;
-
- if (csmd->bind_coords) {
- BLO_read_float3_array(reader, csmd->bind_coords_num, (float **)&csmd->bind_coords);
- }
-
- /* runtime only */
- csmd->delta_cache.deltas = NULL;
- csmd->delta_cache.totverts = 0;
- }
- else if (md->type == eModifierType_MeshSequenceCache) {
- MeshSeqCacheModifierData *msmcd = (MeshSeqCacheModifierData *)md;
- msmcd->reader = NULL;
- msmcd->reader_object_path[0] = '\0';
- }
- else if (md->type == eModifierType_SurfaceDeform) {
- SurfaceDeformModifierData *smd = (SurfaceDeformModifierData *)md;
-
- BLO_read_data_address(reader, &smd->verts);
-
- if (smd->verts) {
- for (int i = 0; i < smd->numverts; i++) {
- BLO_read_data_address(reader, &smd->verts[i].binds);
-
- if (smd->verts[i].binds) {
- for (int j = 0; j < smd->verts[i].numbinds; j++) {
- BLO_read_uint32_array(
- reader, smd->verts[i].binds[j].numverts, &smd->verts[i].binds[j].vert_inds);
-
- if (smd->verts[i].binds[j].mode == MOD_SDEF_MODE_CENTROID ||
- smd->verts[i].binds[j].mode == MOD_SDEF_MODE_LOOPTRI) {
- BLO_read_float3_array(reader, 1, &smd->verts[i].binds[j].vert_weights);
- }
- else {
- BLO_read_float_array(
- reader, smd->verts[i].binds[j].numverts, &smd->verts[i].binds[j].vert_weights);
- }
- }
- }
- }
- }
- }
- else if (md->type == eModifierType_Bevel) {
- BevelModifierData *bmd = (BevelModifierData *)md;
- BLO_read_data_address(reader, &bmd->custom_profile);
- if (bmd->custom_profile) {
- direct_link_curveprofile(reader, bmd->custom_profile);
- }
- }
if (mti->blendRead != NULL) {
mti->blendRead(reader, md);
@@ -5921,7 +5739,7 @@ static void direct_link_gpencil_modifiers(BlendDataReader *reader, ListBase *lb)
BLO_read_data_address(reader, &hmd->curfalloff);
if (hmd->curfalloff) {
- direct_link_curvemapping(reader, hmd->curfalloff);
+ BKE_curvemapping_blend_read(reader, hmd->curfalloff);
}
}
else if (md->type == eGpencilModifierType_Noise) {
@@ -5929,7 +5747,7 @@ static void direct_link_gpencil_modifiers(BlendDataReader *reader, ListBase *lb)
BLO_read_data_address(reader, &gpmd->curve_intensity);
if (gpmd->curve_intensity) {
- direct_link_curvemapping(reader, gpmd->curve_intensity);
+ BKE_curvemapping_blend_read(reader, gpmd->curve_intensity);
/* initialize the curve. Maybe this could be moved to modififer logic */
BKE_curvemapping_initialize(gpmd->curve_intensity);
}
@@ -5939,7 +5757,7 @@ static void direct_link_gpencil_modifiers(BlendDataReader *reader, ListBase *lb)
BLO_read_data_address(reader, &gpmd->curve_thickness);
if (gpmd->curve_thickness) {
- direct_link_curvemapping(reader, gpmd->curve_thickness);
+ BKE_curvemapping_blend_read(reader, gpmd->curve_thickness);
BKE_curvemapping_initialize(gpmd->curve_thickness);
}
}
@@ -5948,7 +5766,7 @@ static void direct_link_gpencil_modifiers(BlendDataReader *reader, ListBase *lb)
BLO_read_data_address(reader, &gpmd->colorband);
BLO_read_data_address(reader, &gpmd->curve_intensity);
if (gpmd->curve_intensity) {
- direct_link_curvemapping(reader, gpmd->curve_intensity);
+ BKE_curvemapping_blend_read(reader, gpmd->curve_intensity);
BKE_curvemapping_initialize(gpmd->curve_intensity);
}
}
@@ -5956,7 +5774,7 @@ static void direct_link_gpencil_modifiers(BlendDataReader *reader, ListBase *lb)
SmoothGpencilModifierData *gpmd = (SmoothGpencilModifierData *)md;
BLO_read_data_address(reader, &gpmd->curve_intensity);
if (gpmd->curve_intensity) {
- direct_link_curvemapping(reader, gpmd->curve_intensity);
+ BKE_curvemapping_blend_read(reader, gpmd->curve_intensity);
BKE_curvemapping_initialize(gpmd->curve_intensity);
}
}
@@ -5964,7 +5782,7 @@ static void direct_link_gpencil_modifiers(BlendDataReader *reader, ListBase *lb)
ColorGpencilModifierData *gpmd = (ColorGpencilModifierData *)md;
BLO_read_data_address(reader, &gpmd->curve_intensity);
if (gpmd->curve_intensity) {
- direct_link_curvemapping(reader, gpmd->curve_intensity);
+ BKE_curvemapping_blend_read(reader, gpmd->curve_intensity);
BKE_curvemapping_initialize(gpmd->curve_intensity);
}
}
@@ -5972,7 +5790,7 @@ static void direct_link_gpencil_modifiers(BlendDataReader *reader, ListBase *lb)
OpacityGpencilModifierData *gpmd = (OpacityGpencilModifierData *)md;
BLO_read_data_address(reader, &gpmd->curve_intensity);
if (gpmd->curve_intensity) {
- direct_link_curvemapping(reader, gpmd->curve_intensity);
+ BKE_curvemapping_blend_read(reader, gpmd->curve_intensity);
BKE_curvemapping_initialize(gpmd->curve_intensity);
}
}
@@ -6206,7 +6024,7 @@ static void direct_link_view_settings(BlendDataReader *reader,
BLO_read_data_address(reader, &view_settings->curve_mapping);
if (view_settings->curve_mapping) {
- direct_link_curvemapping(reader, view_settings->curve_mapping);
+ BKE_curvemapping_blend_read(reader, view_settings->curve_mapping);
}
}
@@ -6253,39 +6071,39 @@ static void direct_link_view_layer(BlendDataReader *reader, ViewLayer *view_laye
view_layer->object_bases_hash = NULL;
}
-static void lib_link_layer_collection(FileData *fd,
+static void lib_link_layer_collection(BlendLibReader *reader,
Library *lib,
LayerCollection *layer_collection,
bool master)
{
/* Master collection is not a real data-lock. */
if (!master) {
- layer_collection->collection = newlibadr(fd, lib, layer_collection->collection);
+ BLO_read_id_address(reader, lib, &layer_collection->collection);
}
for (LayerCollection *layer_collection_nested = layer_collection->layer_collections.first;
layer_collection_nested != NULL;
layer_collection_nested = layer_collection_nested->next) {
- lib_link_layer_collection(fd, lib, layer_collection_nested, false);
+ lib_link_layer_collection(reader, lib, layer_collection_nested, false);
}
}
-static void lib_link_view_layer(FileData *fd, Library *lib, ViewLayer *view_layer)
+static void lib_link_view_layer(BlendLibReader *reader, Library *lib, ViewLayer *view_layer)
{
LISTBASE_FOREACH (FreestyleModuleConfig *, fmc, &view_layer->freestyle_config.modules) {
- fmc->script = newlibadr(fd, lib, fmc->script);
+ BLO_read_id_address(reader, lib, &fmc->script);
}
LISTBASE_FOREACH (FreestyleLineSet *, fls, &view_layer->freestyle_config.linesets) {
- fls->linestyle = newlibadr(fd, lib, fls->linestyle);
- fls->group = newlibadr(fd, lib, fls->group);
+ BLO_read_id_address(reader, lib, &fls->linestyle);
+ BLO_read_id_address(reader, lib, &fls->group);
}
for (Base *base = view_layer->object_bases.first, *base_next = NULL; base; base = base_next) {
base_next = base->next;
/* we only bump the use count for the collection objects */
- base->object = newlibadr(fd, lib, base->object);
+ BLO_read_id_address(reader, lib, &base->object);
if (base->object == NULL) {
/* Free in case linked object got lost. */
@@ -6299,12 +6117,12 @@ static void lib_link_view_layer(FileData *fd, Library *lib, ViewLayer *view_laye
for (LayerCollection *layer_collection = view_layer->layer_collections.first;
layer_collection != NULL;
layer_collection = layer_collection->next) {
- lib_link_layer_collection(fd, lib, layer_collection, true);
+ lib_link_layer_collection(reader, lib, layer_collection, true);
}
- view_layer->mat_override = newlibadr(fd, lib, view_layer->mat_override);
+ BLO_read_id_address(reader, lib, &view_layer->mat_override);
- IDP_LibLinkProperty(view_layer->id_properties, fd);
+ IDP_LibLinkProperty(view_layer->id_properties, reader);
}
/** \} */
@@ -6324,15 +6142,15 @@ static void direct_link_scene_collection(BlendDataReader *reader, SceneCollectio
}
}
-static void lib_link_scene_collection(FileData *fd, Library *lib, SceneCollection *sc)
+static void lib_link_scene_collection(BlendLibReader *reader, Library *lib, SceneCollection *sc)
{
LISTBASE_FOREACH (LinkData *, link, &sc->objects) {
- link->data = newlibadr(fd, lib, link->data);
+ BLO_read_id_address(reader, lib, &link->data);
BLI_assert(link->data);
}
LISTBASE_FOREACH (SceneCollection *, nsc, &sc->scene_collections) {
- lib_link_scene_collection(fd, lib, nsc);
+ lib_link_scene_collection(reader, lib, nsc);
}
}
#endif
@@ -6363,11 +6181,11 @@ static void direct_link_collection(BlendDataReader *reader, Collection *collecti
#endif
}
-static void lib_link_collection_data(FileData *fd, Library *lib, Collection *collection)
+static void lib_link_collection_data(BlendLibReader *reader, Library *lib, Collection *collection)
{
for (CollectionObject *cob = collection->gobject.first, *cob_next = NULL; cob; cob = cob_next) {
cob_next = cob->next;
- cob->ob = newlibadr(fd, lib, cob->ob);
+ BLO_read_id_address(reader, lib, &cob->ob);
if (cob->ob == NULL) {
BLI_freelinkN(&collection->gobject, cob);
@@ -6375,23 +6193,23 @@ static void lib_link_collection_data(FileData *fd, Library *lib, Collection *col
}
for (CollectionChild *child = collection->children.first; child != NULL; child = child->next) {
- child->collection = newlibadr(fd, lib, child->collection);
+ BLO_read_id_address(reader, lib, &child->collection);
}
}
-static void lib_link_collection(FileData *fd, Main *UNUSED(bmain), Collection *collection)
+static void lib_link_collection(BlendLibReader *reader, Collection *collection)
{
#ifdef USE_COLLECTION_COMPAT_28
if (collection->collection) {
- lib_link_scene_collection(fd, collection->id.lib, collection->collection);
+ lib_link_scene_collection(reader, collection->id.lib, collection->collection);
}
if (collection->view_layer) {
- lib_link_view_layer(fd, collection->id.lib, collection->view_layer);
+ lib_link_view_layer(reader, collection->id.lib, collection->view_layer);
}
#endif
- lib_link_collection_data(fd, collection->id.lib, collection);
+ lib_link_collection_data(reader, collection->id.lib, collection);
}
/** \} */
@@ -6412,29 +6230,29 @@ static void composite_patch(bNodeTree *ntree, Scene *scene)
}
}
-static void link_paint(FileData *fd, Scene *sce, Paint *p)
+static void link_paint(BlendLibReader *reader, Scene *sce, Paint *p)
{
if (p) {
- p->brush = newlibadr(fd, sce->id.lib, p->brush);
+ BLO_read_id_address(reader, sce->id.lib, &p->brush);
for (int i = 0; i < p->tool_slots_len; i++) {
if (p->tool_slots[i].brush != NULL) {
- p->tool_slots[i].brush = newlibadr(fd, sce->id.lib, p->tool_slots[i].brush);
+ BLO_read_id_address(reader, sce->id.lib, &p->tool_slots[i].brush);
}
}
- p->palette = newlibadr(fd, sce->id.lib, p->palette);
+ BLO_read_id_address(reader, sce->id.lib, &p->palette);
p->paint_cursor = NULL;
BKE_paint_runtime_init(sce->toolsettings, p);
}
}
-static void lib_link_sequence_modifiers(FileData *fd, Scene *scene, ListBase *lb)
+static void lib_link_sequence_modifiers(BlendLibReader *reader, Scene *scene, ListBase *lb)
{
SequenceModifierData *smd;
for (smd = lb->first; smd; smd = smd->next) {
if (smd->mask_id) {
- smd->mask_id = newlibadr(fd, scene->id.lib, smd->mask_id);
+ BLO_read_id_address(reader, scene->id.lib, &smd->mask_id);
}
}
}
@@ -6515,76 +6333,72 @@ static bool scene_validate_setscene__liblink(Scene *sce, const int totscene)
}
#endif
-static void lib_link_scene(FileData *fd, Main *UNUSED(bmain), Scene *sce)
+static void lib_link_scene(BlendLibReader *reader, Scene *sce)
{
- lib_link_keyingsets(fd, &sce->id, &sce->keyingsets);
+ lib_link_keyingsets(reader, &sce->id, &sce->keyingsets);
- sce->camera = newlibadr(fd, sce->id.lib, sce->camera);
- sce->world = newlibadr(fd, sce->id.lib, sce->world);
- sce->set = newlibadr(fd, sce->id.lib, sce->set);
- sce->gpd = newlibadr(fd, sce->id.lib, sce->gpd);
+ BLO_read_id_address(reader, sce->id.lib, &sce->camera);
+ BLO_read_id_address(reader, sce->id.lib, &sce->world);
+ BLO_read_id_address(reader, sce->id.lib, &sce->set);
+ BLO_read_id_address(reader, sce->id.lib, &sce->gpd);
- link_paint(fd, sce, &sce->toolsettings->imapaint.paint);
+ link_paint(reader, sce, &sce->toolsettings->imapaint.paint);
if (sce->toolsettings->sculpt) {
- link_paint(fd, sce, &sce->toolsettings->sculpt->paint);
+ link_paint(reader, sce, &sce->toolsettings->sculpt->paint);
}
if (sce->toolsettings->vpaint) {
- link_paint(fd, sce, &sce->toolsettings->vpaint->paint);
+ link_paint(reader, sce, &sce->toolsettings->vpaint->paint);
}
if (sce->toolsettings->wpaint) {
- link_paint(fd, sce, &sce->toolsettings->wpaint->paint);
+ link_paint(reader, sce, &sce->toolsettings->wpaint->paint);
}
if (sce->toolsettings->uvsculpt) {
- link_paint(fd, sce, &sce->toolsettings->uvsculpt->paint);
+ link_paint(reader, sce, &sce->toolsettings->uvsculpt->paint);
}
if (sce->toolsettings->gp_paint) {
- link_paint(fd, sce, &sce->toolsettings->gp_paint->paint);
+ link_paint(reader, sce, &sce->toolsettings->gp_paint->paint);
}
if (sce->toolsettings->gp_vertexpaint) {
- link_paint(fd, sce, &sce->toolsettings->gp_vertexpaint->paint);
+ link_paint(reader, sce, &sce->toolsettings->gp_vertexpaint->paint);
}
if (sce->toolsettings->gp_sculptpaint) {
- link_paint(fd, sce, &sce->toolsettings->gp_sculptpaint->paint);
+ link_paint(reader, sce, &sce->toolsettings->gp_sculptpaint->paint);
}
if (sce->toolsettings->gp_weightpaint) {
- link_paint(fd, sce, &sce->toolsettings->gp_weightpaint->paint);
+ link_paint(reader, sce, &sce->toolsettings->gp_weightpaint->paint);
}
if (sce->toolsettings->sculpt) {
- sce->toolsettings->sculpt->gravity_object = newlibadr(
- fd, sce->id.lib, sce->toolsettings->sculpt->gravity_object);
+ BLO_read_id_address(reader, sce->id.lib, &sce->toolsettings->sculpt->gravity_object);
}
if (sce->toolsettings->imapaint.stencil) {
- sce->toolsettings->imapaint.stencil = newlibadr(
- fd, sce->id.lib, sce->toolsettings->imapaint.stencil);
+ BLO_read_id_address(reader, sce->id.lib, &sce->toolsettings->imapaint.stencil);
}
if (sce->toolsettings->imapaint.clone) {
- sce->toolsettings->imapaint.clone = newlibadr(
- fd, sce->id.lib, sce->toolsettings->imapaint.clone);
+ BLO_read_id_address(reader, sce->id.lib, &sce->toolsettings->imapaint.clone);
}
if (sce->toolsettings->imapaint.canvas) {
- sce->toolsettings->imapaint.canvas = newlibadr(
- fd, sce->id.lib, sce->toolsettings->imapaint.canvas);
+ BLO_read_id_address(reader, sce->id.lib, &sce->toolsettings->imapaint.canvas);
}
- sce->toolsettings->particle.shape_object = newlibadr(
- fd, sce->id.lib, sce->toolsettings->particle.shape_object);
+ BLO_read_id_address(reader, sce->id.lib, &sce->toolsettings->particle.shape_object);
- sce->toolsettings->gp_sculpt.guide.reference_object = newlibadr(
- fd, sce->id.lib, sce->toolsettings->gp_sculpt.guide.reference_object);
+ BLO_read_id_address(reader, sce->id.lib, &sce->toolsettings->gp_sculpt.guide.reference_object);
for (Base *base_legacy_next, *base_legacy = sce->base.first; base_legacy;
base_legacy = base_legacy_next) {
base_legacy_next = base_legacy->next;
- base_legacy->object = newlibadr(fd, sce->id.lib, base_legacy->object);
+ BLO_read_id_address(reader, sce->id.lib, &base_legacy->object);
if (base_legacy->object == NULL) {
- blo_reportf_wrap(
- fd->reports, RPT_WARNING, TIP_("LIB: object lost from scene: '%s'"), sce->id.name + 2);
+ blo_reportf_wrap(reader->fd->reports,
+ RPT_WARNING,
+ TIP_("LIB: object lost from scene: '%s'"),
+ sce->id.name + 2);
BLI_remlink(&sce->base, base_legacy);
if (base_legacy == sce->basact) {
sce->basact = NULL;
@@ -6595,24 +6409,25 @@ static void lib_link_scene(FileData *fd, Main *UNUSED(bmain), Scene *sce)
Sequence *seq;
SEQ_BEGIN (sce->ed, seq) {
- IDP_LibLinkProperty(seq->prop, fd);
+ IDP_LibLinkProperty(seq->prop, reader);
if (seq->ipo) {
- seq->ipo = newlibadr(fd, sce->id.lib, seq->ipo); // XXX deprecated - old animation system
+ BLO_read_id_address(
+ reader, sce->id.lib, &seq->ipo); // XXX deprecated - old animation system
}
seq->scene_sound = NULL;
if (seq->scene) {
- seq->scene = newlibadr(fd, sce->id.lib, seq->scene);
+ BLO_read_id_address(reader, sce->id.lib, &seq->scene);
seq->scene_sound = NULL;
}
if (seq->clip) {
- seq->clip = newlibadr(fd, sce->id.lib, seq->clip);
+ BLO_read_id_address(reader, sce->id.lib, &seq->clip);
}
if (seq->mask) {
- seq->mask = newlibadr(fd, sce->id.lib, seq->mask);
+ BLO_read_id_address(reader, sce->id.lib, &seq->mask);
}
if (seq->scene_camera) {
- seq->scene_camera = newlibadr(fd, sce->id.lib, seq->scene_camera);
+ BLO_read_id_address(reader, sce->id.lib, &seq->scene_camera);
}
if (seq->sound) {
seq->scene_sound = NULL;
@@ -6620,7 +6435,7 @@ static void lib_link_scene(FileData *fd, Main *UNUSED(bmain), Scene *sce)
seq->type = SEQ_TYPE_SOUND_RAM;
}
else {
- seq->sound = newlibadr(fd, sce->id.lib, seq->sound);
+ BLO_read_id_address(reader, sce->id.lib, &seq->sound);
}
if (seq->sound) {
id_us_plus_no_lib((ID *)seq->sound);
@@ -6629,17 +6444,17 @@ static void lib_link_scene(FileData *fd, Main *UNUSED(bmain), Scene *sce)
}
if (seq->type == SEQ_TYPE_TEXT) {
TextVars *t = seq->effectdata;
- t->text_font = newlibadr(fd, sce->id.lib, t->text_font);
+ BLO_read_id_address(reader, sce->id.lib, &t->text_font);
}
BLI_listbase_clear(&seq->anims);
- lib_link_sequence_modifiers(fd, sce, &seq->modifiers);
+ lib_link_sequence_modifiers(reader, sce, &seq->modifiers);
}
SEQ_END;
LISTBASE_FOREACH (TimeMarker *, marker, &sce->markers) {
if (marker->camera) {
- marker->camera = newlibadr(fd, sce->id.lib, marker->camera);
+ BLO_read_id_address(reader, sce->id.lib, &marker->camera);
}
}
@@ -6647,13 +6462,13 @@ static void lib_link_scene(FileData *fd, Main *UNUSED(bmain), Scene *sce)
if (sce->rigidbody_world) {
RigidBodyWorld *rbw = sce->rigidbody_world;
if (rbw->group) {
- rbw->group = newlibadr(fd, sce->id.lib, rbw->group);
+ BLO_read_id_address(reader, sce->id.lib, &rbw->group);
}
if (rbw->constraints) {
- rbw->constraints = newlibadr(fd, sce->id.lib, rbw->constraints);
+ BLO_read_id_address(reader, sce->id.lib, &rbw->constraints);
}
if (rbw->effector_weights) {
- rbw->effector_weights->group = newlibadr(fd, sce->id.lib, rbw->effector_weights->group);
+ BLO_read_id_address(reader, sce->id.lib, &rbw->effector_weights->group);
}
}
@@ -6662,30 +6477,30 @@ static void lib_link_scene(FileData *fd, Main *UNUSED(bmain), Scene *sce)
}
LISTBASE_FOREACH (SceneRenderLayer *, srl, &sce->r.layers) {
- srl->mat_override = newlibadr(fd, sce->id.lib, srl->mat_override);
+ BLO_read_id_address(reader, sce->id.lib, &srl->mat_override);
LISTBASE_FOREACH (FreestyleModuleConfig *, fmc, &srl->freestyleConfig.modules) {
- fmc->script = newlibadr(fd, sce->id.lib, fmc->script);
+ BLO_read_id_address(reader, sce->id.lib, &fmc->script);
}
LISTBASE_FOREACH (FreestyleLineSet *, fls, &srl->freestyleConfig.linesets) {
- fls->linestyle = newlibadr(fd, sce->id.lib, fls->linestyle);
- fls->group = newlibadr(fd, sce->id.lib, fls->group);
+ BLO_read_id_address(reader, sce->id.lib, &fls->linestyle);
+ BLO_read_id_address(reader, sce->id.lib, &fls->group);
}
}
/* Motion Tracking */
- sce->clip = newlibadr(fd, sce->id.lib, sce->clip);
+ BLO_read_id_address(reader, sce->id.lib, &sce->clip);
#ifdef USE_COLLECTION_COMPAT_28
if (sce->collection) {
- lib_link_scene_collection(fd, sce->id.lib, sce->collection);
+ lib_link_scene_collection(reader, sce->id.lib, sce->collection);
}
#endif
LISTBASE_FOREACH (ViewLayer *, view_layer, &sce->view_layers) {
- lib_link_view_layer(fd, sce->id.lib, view_layer);
+ lib_link_view_layer(reader, sce->id.lib, view_layer);
}
if (sce->r.bake.cage_object) {
- sce->r.bake.cage_object = newlibadr(fd, sce->id.lib, sce->r.bake.cage_object);
+ BLO_read_id_address(reader, sce->id.lib, &sce->r.bake.cage_object);
}
#ifdef USE_SETSCENE_CHECK
@@ -6735,7 +6550,7 @@ static void direct_link_paint(BlendDataReader *reader, const Scene *scene, Paint
BLO_read_data_address(reader, &p->cavity_curve);
if (p->cavity_curve) {
- direct_link_curvemapping(reader, p->cavity_curve);
+ BKE_curvemapping_blend_read(reader, p->cavity_curve);
}
else {
BKE_paint_cavity_curve_preset(p, CURVE_PRESET_LINE);
@@ -6777,12 +6592,12 @@ static void direct_link_sequence_modifiers(BlendDataReader *reader, ListBase *lb
if (smd->type == seqModifierType_Curves) {
CurvesModifierData *cmd = (CurvesModifierData *)smd;
- direct_link_curvemapping(reader, &cmd->curve_mapping);
+ BKE_curvemapping_blend_read(reader, &cmd->curve_mapping);
}
else if (smd->type == seqModifierType_HueCorrect) {
HueCorrectModifierData *hcmd = (HueCorrectModifierData *)smd;
- direct_link_curvemapping(reader, &hcmd->curve_mapping);
+ BKE_curvemapping_blend_read(reader, &hcmd->curve_mapping);
}
}
}
@@ -6845,23 +6660,23 @@ static void direct_link_scene(BlendDataReader *reader, Scene *sce)
/* relink grease pencil interpolation curves */
BLO_read_data_address(reader, &sce->toolsettings->gp_interpolate.custom_ipo);
if (sce->toolsettings->gp_interpolate.custom_ipo) {
- direct_link_curvemapping(reader, sce->toolsettings->gp_interpolate.custom_ipo);
+ BKE_curvemapping_blend_read(reader, sce->toolsettings->gp_interpolate.custom_ipo);
}
/* relink grease pencil multiframe falloff curve */
BLO_read_data_address(reader, &sce->toolsettings->gp_sculpt.cur_falloff);
if (sce->toolsettings->gp_sculpt.cur_falloff) {
- direct_link_curvemapping(reader, sce->toolsettings->gp_sculpt.cur_falloff);
+ BKE_curvemapping_blend_read(reader, sce->toolsettings->gp_sculpt.cur_falloff);
}
/* relink grease pencil primitive curve */
BLO_read_data_address(reader, &sce->toolsettings->gp_sculpt.cur_primitive);
if (sce->toolsettings->gp_sculpt.cur_primitive) {
- direct_link_curvemapping(reader, sce->toolsettings->gp_sculpt.cur_primitive);
+ BKE_curvemapping_blend_read(reader, sce->toolsettings->gp_sculpt.cur_primitive);
}
/* Relink toolsettings curve profile */
BLO_read_data_address(reader, &sce->toolsettings->custom_bevel_profile_preset);
if (sce->toolsettings->custom_bevel_profile_preset) {
- direct_link_curveprofile(reader, sce->toolsettings->custom_bevel_profile_preset);
+ BKE_curveprofile_blend_read(reader, sce->toolsettings->custom_bevel_profile_preset);
}
}
@@ -7060,7 +6875,7 @@ static void direct_link_scene(BlendDataReader *reader, Scene *sce)
sce->preview = direct_link_preview_image(reader, sce->preview);
- direct_link_curvemapping(reader, &sce->r.mblur_shutter_curve);
+ BKE_curvemapping_blend_read(reader, &sce->r.mblur_shutter_curve);
#ifdef USE_COLLECTION_COMPAT_28
/* this runs before the very first doversion */
@@ -7107,18 +6922,18 @@ static void direct_link_scene(BlendDataReader *reader, Scene *sce)
* \{ */
/* relink's grease pencil data's refs */
-static void lib_link_gpencil(FileData *fd, Main *UNUSED(bmain), bGPdata *gpd)
+static void lib_link_gpencil(BlendLibReader *reader, bGPdata *gpd)
{
/* Relink all data-lock linked by GP data-lock */
/* Layers */
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
/* Layer -> Parent References */
- gpl->parent = newlibadr(fd, gpd->id.lib, gpl->parent);
+ BLO_read_id_address(reader, gpd->id.lib, &gpl->parent);
}
/* materials */
for (int a = 0; a < gpd->totcol; a++) {
- gpd->mat[a] = newlibadr(fd, gpd->id.lib, gpd->mat[a]);
+ BLO_read_id_address(reader, gpd->id.lib, &gpd->mat[a]);
}
}
@@ -7210,6 +7025,7 @@ static void direct_link_panel_list(BlendDataReader *reader, ListBase *lb)
panel->runtime_flag = 0;
panel->activedata = NULL;
panel->type = NULL;
+ panel->runtime.custom_data_ptr = NULL;
direct_link_panel_list(reader, &panel->children);
}
}
@@ -7525,9 +7341,9 @@ static void direct_link_area(BlendDataReader *reader, ScrArea *area)
BLO_read_data_address(reader, &area->v4);
}
-static void lib_link_area(FileData *fd, ID *parent_id, ScrArea *area)
+static void lib_link_area(BlendLibReader *reader, ID *parent_id, ScrArea *area)
{
- area->full = newlibadr(fd, parent_id->lib, area->full);
+ BLO_read_id_address(reader, parent_id->lib, &area->full);
memset(&area->runtime, 0x0, sizeof(area->runtime));
@@ -7536,11 +7352,11 @@ static void lib_link_area(FileData *fd, ID *parent_id, ScrArea *area)
case SPACE_VIEW3D: {
View3D *v3d = (View3D *)sl;
- v3d->camera = newlibadr(fd, parent_id->lib, v3d->camera);
- v3d->ob_center = newlibadr(fd, parent_id->lib, v3d->ob_center);
+ BLO_read_id_address(reader, parent_id->lib, &v3d->camera);
+ BLO_read_id_address(reader, parent_id->lib, &v3d->ob_center);
if (v3d->localvd) {
- v3d->localvd->camera = newlibadr(fd, parent_id->lib, v3d->localvd->camera);
+ BLO_read_id_address(reader, parent_id->lib, &v3d->localvd->camera);
}
break;
}
@@ -7549,14 +7365,14 @@ static void lib_link_area(FileData *fd, ID *parent_id, ScrArea *area)
bDopeSheet *ads = sipo->ads;
if (ads) {
- ads->source = newlibadr(fd, parent_id->lib, ads->source);
- ads->filter_grp = newlibadr(fd, parent_id->lib, ads->filter_grp);
+ BLO_read_id_address(reader, parent_id->lib, &ads->source);
+ BLO_read_id_address(reader, parent_id->lib, &ads->filter_grp);
}
break;
}
case SPACE_PROPERTIES: {
SpaceProperties *sbuts = (SpaceProperties *)sl;
- sbuts->pinid = newlibadr(fd, parent_id->lib, sbuts->pinid);
+ BLO_read_id_address(reader, parent_id->lib, &sbuts->pinid);
if (sbuts->pinid == NULL) {
sbuts->flag &= ~SB_PIN_CONTEXT;
}
@@ -7569,23 +7385,23 @@ static void lib_link_area(FileData *fd, ID *parent_id, ScrArea *area)
bDopeSheet *ads = &saction->ads;
if (ads) {
- ads->source = newlibadr(fd, parent_id->lib, ads->source);
- ads->filter_grp = newlibadr(fd, parent_id->lib, ads->filter_grp);
+ BLO_read_id_address(reader, parent_id->lib, &ads->source);
+ BLO_read_id_address(reader, parent_id->lib, &ads->filter_grp);
}
- saction->action = newlibadr(fd, parent_id->lib, saction->action);
+ BLO_read_id_address(reader, parent_id->lib, &saction->action);
break;
}
case SPACE_IMAGE: {
SpaceImage *sima = (SpaceImage *)sl;
- sima->image = newlibadr(fd, parent_id->lib, sima->image);
- sima->mask_info.mask = newlibadr(fd, parent_id->lib, sima->mask_info.mask);
+ BLO_read_id_address(reader, parent_id->lib, &sima->image);
+ BLO_read_id_address(reader, parent_id->lib, &sima->mask_info.mask);
/* NOTE: pre-2.5, this was local data not lib data, but now we need this as lib data
* so fingers crossed this works fine!
*/
- sima->gpd = newlibadr(fd, parent_id->lib, sima->gpd);
+ BLO_read_id_address(reader, parent_id->lib, &sima->gpd);
break;
}
case SPACE_SEQ: {
@@ -7594,7 +7410,7 @@ static void lib_link_area(FileData *fd, ID *parent_id, ScrArea *area)
/* NOTE: pre-2.5, this was local data not lib data, but now we need this as lib data
* so fingers crossed this works fine!
*/
- sseq->gpd = newlibadr(fd, parent_id->lib, sseq->gpd);
+ BLO_read_id_address(reader, parent_id->lib, &sseq->gpd);
break;
}
case SPACE_NLA: {
@@ -7602,22 +7418,22 @@ static void lib_link_area(FileData *fd, ID *parent_id, ScrArea *area)
bDopeSheet *ads = snla->ads;
if (ads) {
- ads->source = newlibadr(fd, parent_id->lib, ads->source);
- ads->filter_grp = newlibadr(fd, parent_id->lib, ads->filter_grp);
+ BLO_read_id_address(reader, parent_id->lib, &ads->source);
+ BLO_read_id_address(reader, parent_id->lib, &ads->filter_grp);
}
break;
}
case SPACE_TEXT: {
SpaceText *st = (SpaceText *)sl;
- st->text = newlibadr(fd, parent_id->lib, st->text);
+ BLO_read_id_address(reader, parent_id->lib, &st->text);
break;
}
case SPACE_SCRIPT: {
SpaceScript *scpt = (SpaceScript *)sl;
/*scpt->script = NULL; - 2.45 set to null, better re-run the script */
if (scpt->script) {
- scpt->script = newlibadr(fd, parent_id->lib, scpt->script);
+ BLO_read_id_address(reader, parent_id->lib, &scpt->script);
if (scpt->script) {
SCRIPT_SET_NULL(scpt->script);
}
@@ -7626,7 +7442,7 @@ static void lib_link_area(FileData *fd, ID *parent_id, ScrArea *area)
}
case SPACE_OUTLINER: {
SpaceOutliner *so = (SpaceOutliner *)sl;
- so->search_tse.id = newlibadr(fd, NULL, so->search_tse.id);
+ BLO_read_id_address(reader, NULL, &so->search_tse.id);
if (so->treestore) {
TreeStoreElem *tselem;
@@ -7634,7 +7450,7 @@ static void lib_link_area(FileData *fd, ID *parent_id, ScrArea *area)
BLI_mempool_iternew(so->treestore, &iter);
while ((tselem = BLI_mempool_iterstep(&iter))) {
- tselem->id = newlibadr(fd, NULL, tselem->id);
+ BLO_read_id_address(reader, NULL, &tselem->id);
}
if (so->treehash) {
/* rebuild hash table, because it depends on ids too */
@@ -7646,14 +7462,18 @@ static void lib_link_area(FileData *fd, ID *parent_id, ScrArea *area)
case SPACE_NODE: {
SpaceNode *snode = (SpaceNode *)sl;
bNodeTreePath *path, *path_next;
- bNodeTree *ntree;
/* node tree can be stored locally in id too, link this first */
- snode->id = newlibadr(fd, parent_id->lib, snode->id);
- snode->from = newlibadr(fd, parent_id->lib, snode->from);
+ BLO_read_id_address(reader, parent_id->lib, &snode->id);
+ BLO_read_id_address(reader, parent_id->lib, &snode->from);
- ntree = snode->id ? ntreeFromID(snode->id) : NULL;
- snode->nodetree = ntree ? ntree : newlibadr(fd, parent_id->lib, snode->nodetree);
+ bNodeTree *ntree = snode->id ? ntreeFromID(snode->id) : NULL;
+ if (ntree) {
+ snode->nodetree = ntree;
+ }
+ else {
+ BLO_read_id_address(reader, parent_id->lib, &snode->nodetree);
+ }
for (path = snode->treepath.first; path; path = path->next) {
if (path == snode->treepath.first) {
@@ -7661,7 +7481,7 @@ static void lib_link_area(FileData *fd, ID *parent_id, ScrArea *area)
path->nodetree = snode->nodetree;
}
else {
- path->nodetree = newlibadr(fd, parent_id->lib, path->nodetree);
+ BLO_read_id_address(reader, parent_id->lib, &path->nodetree);
}
if (!path->nodetree) {
@@ -7690,8 +7510,8 @@ static void lib_link_area(FileData *fd, ID *parent_id, ScrArea *area)
}
case SPACE_CLIP: {
SpaceClip *sclip = (SpaceClip *)sl;
- sclip->clip = newlibadr(fd, parent_id->lib, sclip->clip);
- sclip->mask_info.mask = newlibadr(fd, parent_id->lib, sclip->mask_info.mask);
+ BLO_read_id_address(reader, parent_id->lib, &sclip->clip);
+ BLO_read_id_address(reader, parent_id->lib, &sclip->mask_info.mask);
break;
}
default:
@@ -7739,10 +7559,9 @@ static void direct_link_wm_xr_data(BlendDataReader *reader, wmXrData *xr_data)
direct_link_view3dshading(reader, &xr_data->session_settings.shading);
}
-static void lib_link_wm_xr_data(FileData *fd, ID *parent_id, wmXrData *xr_data)
+static void lib_link_wm_xr_data(BlendLibReader *reader, ID *parent_id, wmXrData *xr_data)
{
- xr_data->session_settings.base_pose_object = newlibadr(
- fd, parent_id->lib, xr_data->session_settings.base_pose_object);
+ BLO_read_id_address(reader, parent_id->lib, &xr_data->session_settings.base_pose_object);
}
/** \} */
@@ -7828,21 +7647,21 @@ static void direct_link_windowmanager(BlendDataReader *reader, wmWindowManager *
wm->is_interface_locked = 0;
}
-static void lib_link_windowmanager(FileData *fd, Main *UNUSED(bmain), wmWindowManager *wm)
+static void lib_link_windowmanager(BlendLibReader *reader, wmWindowManager *wm)
{
LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
if (win->workspace_hook) { /* NULL for old files */
- lib_link_workspace_instance_hook(fd, win->workspace_hook, &wm->id);
+ lib_link_workspace_instance_hook(reader, win->workspace_hook, &wm->id);
}
- win->scene = newlibadr(fd, wm->id.lib, win->scene);
+ BLO_read_id_address(reader, wm->id.lib, &win->scene);
/* deprecated, but needed for versioning (will be NULL'ed then) */
- win->screen = newlibadr(fd, NULL, win->screen);
+ BLO_read_id_address(reader, NULL, &win->screen);
LISTBASE_FOREACH (ScrArea *, area, &win->global_areas.areabase) {
- lib_link_area(fd, &wm->id, area);
+ lib_link_area(reader, &wm->id, area);
}
- lib_link_wm_xr_data(fd, &wm->id, &wm->xr);
+ lib_link_wm_xr_data(reader, &wm->id, &wm->xr);
}
}
@@ -7854,17 +7673,17 @@ static void lib_link_windowmanager(FileData *fd, Main *UNUSED(bmain), wmWindowMa
/* note: file read without screens option G_FILE_NO_UI;
* check lib pointers in call below */
-static void lib_link_screen(FileData *fd, Main *UNUSED(bmain), bScreen *screen)
+static void lib_link_screen(BlendLibReader *reader, bScreen *screen)
{
/* deprecated, but needed for versioning (will be NULL'ed then) */
- screen->scene = newlibadr(fd, screen->id.lib, screen->scene);
+ BLO_read_id_address(reader, screen->id.lib, &screen->scene);
screen->animtimer = NULL; /* saved in rare cases */
screen->tool_tip = NULL;
screen->scrubbing = false;
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
- lib_link_area(fd, &screen->id, area);
+ lib_link_area(reader, &screen->id, area);
}
}
@@ -8404,12 +8223,12 @@ static void direct_link_library(FileData *fd, Library *lib, Main *main)
/* check if the library was already read */
for (newmain = fd->mainlist->first; newmain; newmain = newmain->next) {
if (newmain->curlib) {
- if (BLI_path_cmp(newmain->curlib->filepath, lib->filepath) == 0) {
+ if (BLI_path_cmp(newmain->curlib->filepath_abs, lib->filepath_abs) == 0) {
blo_reportf_wrap(fd->reports,
RPT_WARNING,
TIP_("Library '%s', '%s' had multiple instances, save and reload!"),
- lib->name,
- lib->filepath);
+ lib->filepath,
+ lib->filepath_abs);
change_link_placeholder_to_real_ID_pointer(fd->mainlist, fd, lib, newmain->curlib);
/* change_link_placeholder_to_real_ID_pointer_fd(fd, lib, newmain->curlib); */
@@ -8431,12 +8250,12 @@ static void direct_link_library(FileData *fd, Library *lib, Main *main)
}
}
- /* make sure we have full path in lib->filepath */
- BLI_strncpy(lib->filepath, lib->name, sizeof(lib->name));
- BLI_path_normalize(fd->relabase, lib->filepath);
+ /* Make sure we have full path in lib->filepath_abs */
+ BLI_strncpy(lib->filepath_abs, lib->filepath, sizeof(lib->filepath));
+ BLI_path_normalize(fd->relabase, lib->filepath_abs);
- // printf("direct_link_library: name %s\n", lib->name);
// printf("direct_link_library: filepath %s\n", lib->filepath);
+ // printf("direct_link_library: filepath_abs %s\n", lib->filepath_abs);
BlendDataReader reader = {fd};
lib->packedfile = direct_link_packedfile(&reader, lib->packedfile);
@@ -8457,7 +8276,7 @@ static void direct_link_library(FileData *fd, Library *lib, Main *main)
id_us_ensure_real(&lib->id);
}
-static void lib_link_library(FileData *UNUSED(fd), Main *UNUSED(bmain), Library *UNUSED(lib))
+static void lib_link_library(BlendLibReader *UNUSED(reader), Library *UNUSED(lib))
{
}
@@ -8474,8 +8293,8 @@ static void fix_relpaths_library(const char *basepath, Main *main)
* it absolute. This can happen when appending an object with a relative
* link into an unsaved blend file. See [#27405].
* The remap relative option will make it relative again on save - campbell */
- if (BLI_path_is_rel(lib->name)) {
- BLI_strncpy(lib->name, lib->filepath, sizeof(lib->name));
+ if (BLI_path_is_rel(lib->filepath)) {
+ BLI_strncpy(lib->filepath, lib->filepath_abs, sizeof(lib->filepath));
}
}
}
@@ -8484,9 +8303,9 @@ static void fix_relpaths_library(const char *basepath, Main *main)
/* Libraries store both relative and abs paths, recreate relative paths,
* relative to the blend file since indirectly linked libs will be
* relative to their direct linked library. */
- if (BLI_path_is_rel(lib->name)) { /* if this is relative to begin with? */
- BLI_strncpy(lib->name, lib->filepath, sizeof(lib->name));
- BLI_path_rel(lib->name, basepath);
+ if (BLI_path_is_rel(lib->filepath)) { /* if this is relative to begin with? */
+ BLI_strncpy(lib->filepath, lib->filepath_abs, sizeof(lib->filepath));
+ BLI_path_rel(lib->filepath, basepath);
}
}
}
@@ -8498,9 +8317,9 @@ static void fix_relpaths_library(const char *basepath, Main *main)
/** \name Read ID: Light Probe
* \{ */
-static void lib_link_lightprobe(FileData *fd, Main *UNUSED(bmain), LightProbe *prb)
+static void lib_link_lightprobe(BlendLibReader *reader, LightProbe *prb)
{
- prb->visibility_grp = newlibadr(fd, prb->id.lib, prb->visibility_grp);
+ BLO_read_id_address(reader, prb->id.lib, &prb->visibility_grp);
}
static void direct_link_lightprobe(BlendDataReader *reader, LightProbe *prb)
@@ -8515,9 +8334,9 @@ static void direct_link_lightprobe(BlendDataReader *reader, LightProbe *prb)
/** \name Read ID: Speaker
* \{ */
-static void lib_link_speaker(FileData *fd, Main *UNUSED(bmain), Speaker *spk)
+static void lib_link_speaker(BlendLibReader *reader, Speaker *spk)
{
- spk->sound = newlibadr(fd, spk->id.lib, spk->sound);
+ BLO_read_id_address(reader, spk->id.lib, &spk->sound);
}
static void direct_link_speaker(BlendDataReader *reader, Speaker *spk)
@@ -8567,9 +8386,10 @@ static void direct_link_sound(BlendDataReader *reader, bSound *sound)
sound->newpackedfile = direct_link_packedfile(reader, sound->newpackedfile);
}
-static void lib_link_sound(FileData *fd, Main *UNUSED(bmain), bSound *sound)
+static void lib_link_sound(BlendLibReader *reader, bSound *sound)
{
- sound->ipo = newlibadr(fd, sound->id.lib, sound->ipo); // XXX deprecated - old animation system
+ BLO_read_id_address(
+ reader, sound->id.lib, &sound->ipo); // XXX deprecated - old animation system
}
/** \} */
@@ -8661,36 +8481,38 @@ static void direct_link_movieclip(BlendDataReader *reader, MovieClip *clip)
}
}
-static void lib_link_movieTracks(FileData *fd, MovieClip *clip, ListBase *tracksbase)
+static void lib_link_movieTracks(BlendLibReader *reader, MovieClip *clip, ListBase *tracksbase)
{
MovieTrackingTrack *track;
for (track = tracksbase->first; track; track = track->next) {
- track->gpd = newlibadr(fd, clip->id.lib, track->gpd);
+ BLO_read_id_address(reader, clip->id.lib, &track->gpd);
}
}
-static void lib_link_moviePlaneTracks(FileData *fd, MovieClip *clip, ListBase *tracksbase)
+static void lib_link_moviePlaneTracks(BlendLibReader *reader,
+ MovieClip *clip,
+ ListBase *tracksbase)
{
MovieTrackingPlaneTrack *plane_track;
for (plane_track = tracksbase->first; plane_track; plane_track = plane_track->next) {
- plane_track->image = newlibadr(fd, clip->id.lib, plane_track->image);
+ BLO_read_id_address(reader, clip->id.lib, &plane_track->image);
}
}
-static void lib_link_movieclip(FileData *fd, Main *UNUSED(bmain), MovieClip *clip)
+static void lib_link_movieclip(BlendLibReader *reader, MovieClip *clip)
{
MovieTracking *tracking = &clip->tracking;
- clip->gpd = newlibadr(fd, clip->id.lib, clip->gpd);
+ BLO_read_id_address(reader, clip->id.lib, &clip->gpd);
- lib_link_movieTracks(fd, clip, &tracking->tracks);
- lib_link_moviePlaneTracks(fd, clip, &tracking->plane_tracks);
+ lib_link_movieTracks(reader, clip, &tracking->tracks);
+ lib_link_moviePlaneTracks(reader, clip, &tracking->plane_tracks);
LISTBASE_FOREACH (MovieTrackingObject *, object, &tracking->objects) {
- lib_link_movieTracks(fd, clip, &object->tracks);
- lib_link_moviePlaneTracks(fd, clip, &object->plane_tracks);
+ lib_link_movieTracks(reader, clip, &object->tracks);
+ lib_link_moviePlaneTracks(reader, clip, &object->plane_tracks);
}
}
@@ -8758,12 +8580,12 @@ static void direct_link_mask(BlendDataReader *reader, Mask *mask)
}
}
-static void lib_link_mask_parent(FileData *fd, Mask *mask, MaskParent *parent)
+static void lib_link_mask_parent(BlendLibReader *reader, Mask *mask, MaskParent *parent)
{
- parent->id = newlibadr(fd, mask->id.lib, parent->id);
+ BLO_read_id_address(reader, mask->id.lib, &parent->id);
}
-static void lib_link_mask(FileData *fd, Main *UNUSED(bmain), Mask *mask)
+static void lib_link_mask(BlendLibReader *reader, Mask *mask)
{
LISTBASE_FOREACH (MaskLayer *, masklay, &mask->masklayers) {
MaskSpline *spline;
@@ -8775,10 +8597,10 @@ static void lib_link_mask(FileData *fd, Main *UNUSED(bmain), Mask *mask)
for (i = 0; i < spline->tot_point; i++) {
MaskSplinePoint *point = &spline->points[i];
- lib_link_mask_parent(fd, mask, &point->parent);
+ lib_link_mask_parent(reader, mask, &point->parent);
}
- lib_link_mask_parent(fd, mask, &spline->parent);
+ lib_link_mask_parent(reader, mask, &spline->parent);
spline = spline->next;
}
@@ -8791,7 +8613,7 @@ static void lib_link_mask(FileData *fd, Main *UNUSED(bmain), Mask *mask)
/** \name Read ID: Line Style
* \{ */
-static void lib_link_linestyle(FileData *fd, Main *UNUSED(bmain), FreestyleLineStyle *linestyle)
+static void lib_link_linestyle(BlendLibReader *reader, FreestyleLineStyle *linestyle)
{
LineStyleModifier *m;
@@ -8800,7 +8622,7 @@ static void lib_link_linestyle(FileData *fd, Main *UNUSED(bmain), FreestyleLineS
case LS_MODIFIER_DISTANCE_FROM_OBJECT: {
LineStyleColorModifier_DistanceFromObject *cm =
(LineStyleColorModifier_DistanceFromObject *)m;
- cm->target = newlibadr(fd, linestyle->id.lib, cm->target);
+ BLO_read_id_address(reader, linestyle->id.lib, &cm->target);
break;
}
}
@@ -8810,7 +8632,7 @@ static void lib_link_linestyle(FileData *fd, Main *UNUSED(bmain), FreestyleLineS
case LS_MODIFIER_DISTANCE_FROM_OBJECT: {
LineStyleAlphaModifier_DistanceFromObject *am =
(LineStyleAlphaModifier_DistanceFromObject *)m;
- am->target = newlibadr(fd, linestyle->id.lib, am->target);
+ BLO_read_id_address(reader, linestyle->id.lib, &am->target);
break;
}
}
@@ -8820,7 +8642,7 @@ static void lib_link_linestyle(FileData *fd, Main *UNUSED(bmain), FreestyleLineS
case LS_MODIFIER_DISTANCE_FROM_OBJECT: {
LineStyleThicknessModifier_DistanceFromObject *tm =
(LineStyleThicknessModifier_DistanceFromObject *)m;
- tm->target = newlibadr(fd, linestyle->id.lib, tm->target);
+ BLO_read_id_address(reader, linestyle->id.lib, &tm->target);
break;
}
}
@@ -8828,8 +8650,8 @@ static void lib_link_linestyle(FileData *fd, Main *UNUSED(bmain), FreestyleLineS
for (int a = 0; a < MAX_MTEX; a++) {
MTex *mtex = linestyle->mtex[a];
if (mtex) {
- mtex->tex = newlibadr(fd, linestyle->id.lib, mtex->tex);
- mtex->object = newlibadr(fd, linestyle->id.lib, mtex->object);
+ BLO_read_id_address(reader, linestyle->id.lib, &mtex->tex);
+ BLO_read_id_address(reader, linestyle->id.lib, &mtex->object);
}
}
}
@@ -8890,51 +8712,51 @@ static void direct_link_linestyle_alpha_modifier(BlendDataReader *reader,
case LS_MODIFIER_ALONG_STROKE: {
LineStyleAlphaModifier_AlongStroke *m = (LineStyleAlphaModifier_AlongStroke *)modifier;
BLO_read_data_address(reader, &m->curve);
- direct_link_curvemapping(reader, m->curve);
+ BKE_curvemapping_blend_read(reader, m->curve);
break;
}
case LS_MODIFIER_DISTANCE_FROM_CAMERA: {
LineStyleAlphaModifier_DistanceFromCamera *m = (LineStyleAlphaModifier_DistanceFromCamera *)
modifier;
BLO_read_data_address(reader, &m->curve);
- direct_link_curvemapping(reader, m->curve);
+ BKE_curvemapping_blend_read(reader, m->curve);
break;
}
case LS_MODIFIER_DISTANCE_FROM_OBJECT: {
LineStyleAlphaModifier_DistanceFromObject *m = (LineStyleAlphaModifier_DistanceFromObject *)
modifier;
BLO_read_data_address(reader, &m->curve);
- direct_link_curvemapping(reader, m->curve);
+ BKE_curvemapping_blend_read(reader, m->curve);
break;
}
case LS_MODIFIER_MATERIAL: {
LineStyleAlphaModifier_Material *m = (LineStyleAlphaModifier_Material *)modifier;
BLO_read_data_address(reader, &m->curve);
- direct_link_curvemapping(reader, m->curve);
+ BKE_curvemapping_blend_read(reader, m->curve);
break;
}
case LS_MODIFIER_TANGENT: {
LineStyleAlphaModifier_Tangent *m = (LineStyleAlphaModifier_Tangent *)modifier;
BLO_read_data_address(reader, &m->curve);
- direct_link_curvemapping(reader, m->curve);
+ BKE_curvemapping_blend_read(reader, m->curve);
break;
}
case LS_MODIFIER_NOISE: {
LineStyleAlphaModifier_Noise *m = (LineStyleAlphaModifier_Noise *)modifier;
BLO_read_data_address(reader, &m->curve);
- direct_link_curvemapping(reader, m->curve);
+ BKE_curvemapping_blend_read(reader, m->curve);
break;
}
case LS_MODIFIER_CREASE_ANGLE: {
LineStyleAlphaModifier_CreaseAngle *m = (LineStyleAlphaModifier_CreaseAngle *)modifier;
BLO_read_data_address(reader, &m->curve);
- direct_link_curvemapping(reader, m->curve);
+ BKE_curvemapping_blend_read(reader, m->curve);
break;
}
case LS_MODIFIER_CURVATURE_3D: {
LineStyleAlphaModifier_Curvature_3D *m = (LineStyleAlphaModifier_Curvature_3D *)modifier;
BLO_read_data_address(reader, &m->curve);
- direct_link_curvemapping(reader, m->curve);
+ BKE_curvemapping_blend_read(reader, m->curve);
break;
}
}
@@ -8948,47 +8770,47 @@ static void direct_link_linestyle_thickness_modifier(BlendDataReader *reader,
LineStyleThicknessModifier_AlongStroke *m = (LineStyleThicknessModifier_AlongStroke *)
modifier;
BLO_read_data_address(reader, &m->curve);
- direct_link_curvemapping(reader, m->curve);
+ BKE_curvemapping_blend_read(reader, m->curve);
break;
}
case LS_MODIFIER_DISTANCE_FROM_CAMERA: {
LineStyleThicknessModifier_DistanceFromCamera *m =
(LineStyleThicknessModifier_DistanceFromCamera *)modifier;
BLO_read_data_address(reader, &m->curve);
- direct_link_curvemapping(reader, m->curve);
+ BKE_curvemapping_blend_read(reader, m->curve);
break;
}
case LS_MODIFIER_DISTANCE_FROM_OBJECT: {
LineStyleThicknessModifier_DistanceFromObject *m =
(LineStyleThicknessModifier_DistanceFromObject *)modifier;
BLO_read_data_address(reader, &m->curve);
- direct_link_curvemapping(reader, m->curve);
+ BKE_curvemapping_blend_read(reader, m->curve);
break;
}
case LS_MODIFIER_MATERIAL: {
LineStyleThicknessModifier_Material *m = (LineStyleThicknessModifier_Material *)modifier;
BLO_read_data_address(reader, &m->curve);
- direct_link_curvemapping(reader, m->curve);
+ BKE_curvemapping_blend_read(reader, m->curve);
break;
}
case LS_MODIFIER_TANGENT: {
LineStyleThicknessModifier_Tangent *m = (LineStyleThicknessModifier_Tangent *)modifier;
BLO_read_data_address(reader, &m->curve);
- direct_link_curvemapping(reader, m->curve);
+ BKE_curvemapping_blend_read(reader, m->curve);
break;
}
case LS_MODIFIER_CREASE_ANGLE: {
LineStyleThicknessModifier_CreaseAngle *m = (LineStyleThicknessModifier_CreaseAngle *)
modifier;
BLO_read_data_address(reader, &m->curve);
- direct_link_curvemapping(reader, m->curve);
+ BKE_curvemapping_blend_read(reader, m->curve);
break;
}
case LS_MODIFIER_CURVATURE_3D: {
LineStyleThicknessModifier_Curvature_3D *m = (LineStyleThicknessModifier_Curvature_3D *)
modifier;
BLO_read_data_address(reader, &m->curve);
- direct_link_curvemapping(reader, m->curve);
+ BKE_curvemapping_blend_read(reader, m->curve);
break;
}
}
@@ -9033,10 +8855,10 @@ static void direct_link_linestyle(BlendDataReader *reader, FreestyleLineStyle *l
/** \name Read ID: Hair
* \{ */
-static void lib_link_hair(FileData *fd, Main *UNUSED(main), Hair *hair)
+static void lib_link_hair(BlendLibReader *reader, Hair *hair)
{
for (int a = 0; a < hair->totcol; a++) {
- hair->mat[a] = newlibadr(fd, hair->id.lib, hair->mat[a]);
+ BLO_read_id_address(reader, hair->id.lib, &hair->mat[a]);
}
}
@@ -9060,10 +8882,10 @@ static void direct_link_hair(BlendDataReader *reader, Hair *hair)
/** \name Read ID: Point Cloud
* \{ */
-static void lib_link_pointcloud(FileData *fd, Main *UNUSED(main), PointCloud *pointcloud)
+static void lib_link_pointcloud(BlendLibReader *reader, PointCloud *pointcloud)
{
for (int a = 0; a < pointcloud->totcol; a++) {
- pointcloud->mat[a] = newlibadr(fd, pointcloud->id.lib, pointcloud->mat[a]);
+ BLO_read_id_address(reader, pointcloud->id.lib, &pointcloud->mat[a]);
}
}
@@ -9086,10 +8908,10 @@ static void direct_link_pointcloud(BlendDataReader *reader, PointCloud *pointclo
/** \name Read ID: Volume
* \{ */
-static void lib_link_volume(FileData *fd, Main *UNUSED(main), Volume *volume)
+static void lib_link_volume(BlendLibReader *reader, Volume *volume)
{
for (int a = 0; a < volume->totcol; a++) {
- volume->mat[a] = newlibadr(fd, volume->id.lib, volume->mat[a]);
+ BLO_read_id_address(reader, volume->id.lib, &volume->mat[a]);
}
}
@@ -9115,9 +8937,7 @@ static void direct_link_volume(BlendDataReader *reader, Volume *volume)
/** \name Read ID: Simulation
* \{ */
-static void lib_link_simulation(FileData *UNUSED(fd),
- Main *UNUSED(main),
- Simulation *UNUSED(simulation))
+static void lib_link_simulation(BlendLibReader *UNUSED(reader), Simulation *UNUSED(simulation))
{
}
@@ -9502,7 +9322,7 @@ static bool read_libblock_undo_restore_linked(FileData *fd, Main *main, const ID
DEBUG_PRINTF("UNDO: restore linked datablock %s\n", id->name);
DEBUG_PRINTF(" from %s (%s): ",
main->curlib ? main->curlib->id.name : "<NULL>",
- main->curlib ? main->curlib->name : "<NULL>");
+ main->curlib ? main->curlib->filepath : "<NULL>");
ID *id_old = BKE_libblock_find_name(main, GS(id->name), id->name + 2);
if (id_old != NULL) {
@@ -9951,8 +9771,8 @@ static void do_versions(FileData *fd, Library *lib, Main *main)
static void do_versions_after_linking(Main *main, ReportList *reports)
{
- // printf("%s for %s (%s), %d.%d\n", __func__, main->curlib ? main->curlib->name : main->name,
- // main->curlib ? "LIB" : "MAIN", main->versionfile, main->subversionfile);
+ // printf("%s for %s (%s), %d.%d\n", __func__, main->curlib ? main->curlib->filepath :
+ // main->name, main->curlib ? "LIB" : "MAIN", main->versionfile, main->subversionfile);
/* Don't allow versioning to create new data-blocks. */
main->is_locked_for_linking = true;
@@ -9977,6 +9797,8 @@ static void lib_link_all(FileData *fd, Main *bmain)
{
const bool do_partial_undo = (fd->skip_flags & BLO_READ_SKIP_UNDO_OLD_MAIN) == 0;
+ BlendLibReader reader = {fd, bmain};
+
ID *id;
FOREACH_MAIN_ID_BEGIN (bmain, id) {
if ((id->tag & LIB_TAG_NEED_LINK) == 0) {
@@ -9997,7 +9819,7 @@ static void lib_link_all(FileData *fd, Main *bmain)
continue;
}
- lib_link_id(fd, bmain, id);
+ lib_link_id(&reader, id);
/* Note: ID types are processed in reverse order as defined by INDEX_ID_XXX enums in DNA_ID.h.
* This ensures handling of most dependencies in proper order, as elsewhere in code.
@@ -10005,129 +9827,129 @@ static void lib_link_all(FileData *fd, Main *bmain)
* whether something is wrong then. */
switch (GS(id->name)) {
case ID_MSK:
- lib_link_mask(fd, bmain, (Mask *)id);
+ lib_link_mask(&reader, (Mask *)id);
break;
case ID_WM:
- lib_link_windowmanager(fd, bmain, (wmWindowManager *)id);
+ lib_link_windowmanager(&reader, (wmWindowManager *)id);
break;
case ID_WS:
/* Could we skip WS in undo case? */
- lib_link_workspaces(fd, bmain, (WorkSpace *)id);
+ lib_link_workspaces(&reader, (WorkSpace *)id);
break;
case ID_SCE:
- lib_link_scene(fd, bmain, (Scene *)id);
+ lib_link_scene(&reader, (Scene *)id);
break;
case ID_LS:
- lib_link_linestyle(fd, bmain, (FreestyleLineStyle *)id);
+ lib_link_linestyle(&reader, (FreestyleLineStyle *)id);
break;
case ID_OB:
- lib_link_object(fd, bmain, (Object *)id);
+ lib_link_object(&reader, (Object *)id);
break;
case ID_SCR:
/* DO NOT skip screens here,
* 3D viewport may contains pointers to other ID data (like bgpic)! See T41411. */
- lib_link_screen(fd, bmain, (bScreen *)id);
+ lib_link_screen(&reader, (bScreen *)id);
break;
case ID_MC:
- lib_link_movieclip(fd, bmain, (MovieClip *)id);
+ lib_link_movieclip(&reader, (MovieClip *)id);
break;
case ID_WO:
- lib_link_world(fd, bmain, (World *)id);
+ lib_link_world(&reader, (World *)id);
break;
case ID_LP:
- lib_link_lightprobe(fd, bmain, (LightProbe *)id);
+ lib_link_lightprobe(&reader, (LightProbe *)id);
break;
case ID_SPK:
- lib_link_speaker(fd, bmain, (Speaker *)id);
+ lib_link_speaker(&reader, (Speaker *)id);
break;
case ID_PA:
- lib_link_particlesettings(fd, bmain, (ParticleSettings *)id);
+ lib_link_particlesettings(&reader, (ParticleSettings *)id);
break;
case ID_PC:
- lib_link_paint_curve(fd, bmain, (PaintCurve *)id);
+ lib_link_paint_curve(&reader, (PaintCurve *)id);
break;
case ID_BR:
- lib_link_brush(fd, bmain, (Brush *)id);
+ lib_link_brush(&reader, (Brush *)id);
break;
case ID_GR:
- lib_link_collection(fd, bmain, (Collection *)id);
+ lib_link_collection(&reader, (Collection *)id);
break;
case ID_SO:
- lib_link_sound(fd, bmain, (bSound *)id);
+ lib_link_sound(&reader, (bSound *)id);
break;
case ID_TXT:
- lib_link_text(fd, bmain, (Text *)id);
+ lib_link_text(&reader, (Text *)id);
break;
case ID_CA:
- lib_link_camera(fd, bmain, (Camera *)id);
+ lib_link_camera(&reader, (Camera *)id);
break;
case ID_LA:
- lib_link_light(fd, bmain, (Light *)id);
+ lib_link_light(&reader, (Light *)id);
break;
case ID_LT:
- lib_link_latt(fd, bmain, (Lattice *)id);
+ lib_link_latt(&reader, (Lattice *)id);
break;
case ID_MB:
- lib_link_mball(fd, bmain, (MetaBall *)id);
+ lib_link_mball(&reader, (MetaBall *)id);
break;
case ID_CU:
- lib_link_curve(fd, bmain, (Curve *)id);
+ lib_link_curve(&reader, (Curve *)id);
break;
case ID_ME:
- lib_link_mesh(fd, bmain, (Mesh *)id);
+ lib_link_mesh(&reader, (Mesh *)id);
break;
case ID_CF:
- lib_link_cachefiles(fd, bmain, (CacheFile *)id);
+ lib_link_cachefiles(&reader, (CacheFile *)id);
break;
case ID_AR:
- lib_link_armature(fd, bmain, (bArmature *)id);
+ lib_link_armature(&reader, (bArmature *)id);
break;
case ID_VF:
- lib_link_vfont(fd, bmain, (VFont *)id);
+ lib_link_vfont(&reader, (VFont *)id);
break;
case ID_HA:
- lib_link_hair(fd, bmain, (Hair *)id);
+ lib_link_hair(&reader, (Hair *)id);
break;
case ID_PT:
- lib_link_pointcloud(fd, bmain, (PointCloud *)id);
+ lib_link_pointcloud(&reader, (PointCloud *)id);
break;
case ID_VO:
- lib_link_volume(fd, bmain, (Volume *)id);
+ lib_link_volume(&reader, (Volume *)id);
break;
case ID_MA:
- lib_link_material(fd, bmain, (Material *)id);
+ lib_link_material(&reader, (Material *)id);
break;
case ID_TE:
- lib_link_texture(fd, bmain, (Tex *)id);
+ lib_link_texture(&reader, (Tex *)id);
break;
case ID_IM:
- lib_link_image(fd, bmain, (Image *)id);
+ lib_link_image(&reader, (Image *)id);
break;
case ID_NT:
/* Has to be done after node users (scene/materials/...), this will verify group nodes. */
- lib_link_nodetree(fd, bmain, (bNodeTree *)id);
+ lib_link_nodetree(&reader, (bNodeTree *)id);
break;
case ID_GD:
- lib_link_gpencil(fd, bmain, (bGPdata *)id);
+ lib_link_gpencil(&reader, (bGPdata *)id);
break;
case ID_PAL:
- lib_link_palette(fd, bmain, (Palette *)id);
+ lib_link_palette(&reader, (Palette *)id);
break;
case ID_KE:
- lib_link_key(fd, bmain, (Key *)id);
+ lib_link_key(&reader, (Key *)id);
break;
case ID_AC:
- lib_link_action(fd, bmain, (bAction *)id);
+ lib_link_action(&reader, (bAction *)id);
break;
case ID_SIM:
- lib_link_simulation(fd, bmain, (Simulation *)id);
+ lib_link_simulation(&reader, (Simulation *)id);
break;
case ID_IP:
/* XXX deprecated... still needs to be maintained for version patches still. */
- lib_link_ipo(fd, bmain, (Ipo *)id);
+ lib_link_ipo(&reader, (Ipo *)id);
break;
case ID_LI:
- lib_link_library(fd, bmain, (Library *)id); /* Only init users. */
+ lib_link_library(&reader, (Library *)id); /* Only init users. */
break;
}
@@ -10604,7 +10426,7 @@ static void expand_doit_library(void *fdhandle, Main *mainvar, void *old)
}
Library *lib = read_struct(fd, bheadlib, "Library");
- Main *libmain = blo_find_main(fd, lib->name, fd->relabase);
+ Main *libmain = blo_find_main(fd, lib->filepath, fd->relabase);
if (libmain->curlib == NULL) {
const char *idname = blo_bhead_id_name(fd, bhead);
@@ -10613,7 +10435,7 @@ static void expand_doit_library(void *fdhandle, Main *mainvar, void *old)
RPT_WARNING,
TIP_("LIB: Data refers to main .blend file: '%s' from %s"),
idname,
- mainvar->curlib->filepath);
+ mainvar->curlib->filepath_abs);
return;
}
@@ -10624,7 +10446,7 @@ static void expand_doit_library(void *fdhandle, Main *mainvar, void *old)
* library it belongs to, so that it will be read later. */
read_libblock(fd, libmain, bhead, LIB_TAG_INDIRECT, false, NULL);
// commented because this can print way too much
- // if (G.debug & G_DEBUG) printf("expand_doit: other lib %s\n", lib->name);
+ // if (G.debug & G_DEBUG) printf("expand_doit: other lib %s\n", lib->filepath);
/* for outliner dependency only */
libmain->curlib->parent = mainvar->curlib;
@@ -10661,7 +10483,7 @@ static void expand_doit_library(void *fdhandle, Main *mainvar, void *old)
/* Commented because this can print way too much. */
#if 0
if (G.debug & G_DEBUG) {
- printf("expand_doit: already linked: %s lib: %s\n", id->name, lib->name);
+ printf("expand_doit: already linked: %s lib: %s\n", id->name, lib->filepath);
}
#endif
}
@@ -10699,26 +10521,26 @@ static void expand_doit_library(void *fdhandle, Main *mainvar, void *old)
static BLOExpandDoitCallback expand_doit;
// XXX deprecated - old animation system
-static void expand_ipo(FileData *fd, Main *mainvar, Ipo *ipo)
+static void expand_ipo(BlendExpander *expander, Ipo *ipo)
{
IpoCurve *icu;
for (icu = ipo->curve.first; icu; icu = icu->next) {
if (icu->driver) {
- expand_doit(fd, mainvar, icu->driver->ob);
+ BLO_expand(expander, icu->driver->ob);
}
}
}
// XXX deprecated - old animation system
-static void expand_constraint_channels(FileData *fd, Main *mainvar, ListBase *chanbase)
+static void expand_constraint_channels(BlendExpander *expander, ListBase *chanbase)
{
bConstraintChannel *chan;
for (chan = chanbase->first; chan; chan = chan->next) {
- expand_doit(fd, mainvar, chan->ipo);
+ BLO_expand(expander, chan->ipo);
}
}
-static void expand_fmodifiers(FileData *fd, Main *mainvar, ListBase *list)
+static void expand_fmodifiers(BlendExpander *expander, ListBase *list)
{
FModifier *fcm;
@@ -10728,7 +10550,7 @@ static void expand_fmodifiers(FileData *fd, Main *mainvar, ListBase *list)
case FMODIFIER_TYPE_PYTHON: {
FMod_Python *data = (FMod_Python *)fcm->data;
- expand_doit(fd, mainvar, data->script);
+ BLO_expand(expander, data->script);
break;
}
@@ -10736,7 +10558,7 @@ static void expand_fmodifiers(FileData *fd, Main *mainvar, ListBase *list)
}
}
-static void expand_fcurves(FileData *fd, Main *mainvar, ListBase *list)
+static void expand_fcurves(BlendExpander *expander, ListBase *list)
{
FCurve *fcu;
@@ -10749,54 +10571,54 @@ static void expand_fcurves(FileData *fd, Main *mainvar, ListBase *list)
for (dvar = driver->variables.first; dvar; dvar = dvar->next) {
DRIVER_TARGETS_LOOPER_BEGIN (dvar) {
// TODO: only expand those that are going to get used?
- expand_doit(fd, mainvar, dtar->id);
+ BLO_expand(expander, dtar->id);
}
DRIVER_TARGETS_LOOPER_END;
}
}
/* F-Curve Modifiers */
- expand_fmodifiers(fd, mainvar, &fcu->modifiers);
+ expand_fmodifiers(expander, &fcu->modifiers);
}
}
-static void expand_animdata_nlastrips(FileData *fd, Main *mainvar, ListBase *list)
+static void expand_animdata_nlastrips(BlendExpander *expander, ListBase *list)
{
NlaStrip *strip;
for (strip = list->first; strip; strip = strip->next) {
/* check child strips */
- expand_animdata_nlastrips(fd, mainvar, &strip->strips);
+ expand_animdata_nlastrips(expander, &strip->strips);
/* check F-Curves */
- expand_fcurves(fd, mainvar, &strip->fcurves);
+ expand_fcurves(expander, &strip->fcurves);
/* check F-Modifiers */
- expand_fmodifiers(fd, mainvar, &strip->modifiers);
+ expand_fmodifiers(expander, &strip->modifiers);
/* relink referenced action */
- expand_doit(fd, mainvar, strip->act);
+ BLO_expand(expander, strip->act);
}
}
-static void expand_animdata(FileData *fd, Main *mainvar, AnimData *adt)
+static void expand_animdata(BlendExpander *expander, AnimData *adt)
{
NlaTrack *nlt;
/* own action */
- expand_doit(fd, mainvar, adt->action);
- expand_doit(fd, mainvar, adt->tmpact);
+ BLO_expand(expander, adt->action);
+ BLO_expand(expander, adt->tmpact);
/* drivers - assume that these F-Curves have driver data to be in this list... */
- expand_fcurves(fd, mainvar, &adt->drivers);
+ expand_fcurves(expander, &adt->drivers);
/* nla-data - referenced actions */
for (nlt = adt->nla_tracks.first; nlt; nlt = nlt->next) {
- expand_animdata_nlastrips(fd, mainvar, &nlt->strips);
+ expand_animdata_nlastrips(expander, &nlt->strips);
}
}
-static void expand_idprops(FileData *fd, Main *mainvar, IDProperty *prop)
+static void expand_idprops(BlendExpander *expander, IDProperty *prop)
{
if (!prop) {
return;
@@ -10804,84 +10626,84 @@ static void expand_idprops(FileData *fd, Main *mainvar, IDProperty *prop)
switch (prop->type) {
case IDP_ID:
- expand_doit(fd, mainvar, IDP_Id(prop));
+ BLO_expand(expander, IDP_Id(prop));
break;
case IDP_IDPARRAY: {
IDProperty *idp_array = IDP_IDPArray(prop);
for (int i = 0; i < prop->len; i++) {
- expand_idprops(fd, mainvar, &idp_array[i]);
+ expand_idprops(expander, &idp_array[i]);
}
break;
}
case IDP_GROUP:
LISTBASE_FOREACH (IDProperty *, loop, &prop->data.group) {
- expand_idprops(fd, mainvar, loop);
+ expand_idprops(expander, loop);
}
break;
}
}
-static void expand_id(FileData *fd, Main *mainvar, ID *id);
-static void expand_nodetree(FileData *fd, Main *mainvar, bNodeTree *ntree);
-static void expand_collection(FileData *fd, Main *mainvar, Collection *collection);
+static void expand_id(BlendExpander *expander, ID *id);
+static void expand_nodetree(BlendExpander *expander, bNodeTree *ntree);
+static void expand_collection(BlendExpander *expander, Collection *collection);
-static void expand_id_embedded_id(FileData *fd, Main *mainvar, ID *id)
+static void expand_id_embedded_id(BlendExpander *expander, ID *id)
{
/* Handle 'private IDs'. */
bNodeTree *nodetree = ntreeFromID(id);
if (nodetree != NULL) {
- expand_id(fd, mainvar, &nodetree->id);
- expand_nodetree(fd, mainvar, nodetree);
+ expand_id(expander, &nodetree->id);
+ expand_nodetree(expander, nodetree);
}
if (GS(id->name) == ID_SCE) {
Scene *scene = (Scene *)id;
if (scene->master_collection != NULL) {
- expand_id(fd, mainvar, &scene->master_collection->id);
- expand_collection(fd, mainvar, scene->master_collection);
+ expand_id(expander, &scene->master_collection->id);
+ expand_collection(expander, scene->master_collection);
}
}
}
-static void expand_id(FileData *fd, Main *mainvar, ID *id)
+static void expand_id(BlendExpander *expander, ID *id)
{
- expand_idprops(fd, mainvar, id->properties);
+ expand_idprops(expander, id->properties);
if (id->override_library) {
- expand_doit(fd, mainvar, id->override_library->reference);
- expand_doit(fd, mainvar, id->override_library->storage);
+ BLO_expand(expander, id->override_library->reference);
+ BLO_expand(expander, id->override_library->storage);
}
AnimData *adt = BKE_animdata_from_id(id);
if (adt != NULL) {
- expand_animdata(fd, mainvar, adt);
+ expand_animdata(expander, adt);
}
- expand_id_embedded_id(fd, mainvar, id);
+ expand_id_embedded_id(expander, id);
}
-static void expand_action(FileData *fd, Main *mainvar, bAction *act)
+static void expand_action(BlendExpander *expander, bAction *act)
{
bActionChannel *chan;
// XXX deprecated - old animation system --------------
for (chan = act->chanbase.first; chan; chan = chan->next) {
- expand_doit(fd, mainvar, chan->ipo);
- expand_constraint_channels(fd, mainvar, &chan->constraintChannels);
+ BLO_expand(expander, chan->ipo);
+ expand_constraint_channels(expander, &chan->constraintChannels);
}
// ---------------------------------------------------
/* F-Curves in Action */
- expand_fcurves(fd, mainvar, &act->curves);
+ expand_fcurves(expander, &act->curves);
LISTBASE_FOREACH (TimeMarker *, marker, &act->markers) {
if (marker->camera) {
- expand_doit(fd, mainvar, marker->camera);
+ BLO_expand(expander, marker->camera);
}
}
}
-static void expand_keyingsets(FileData *fd, Main *mainvar, ListBase *list)
+static void expand_keyingsets(BlendExpander *expander, ListBase *list)
{
KeyingSet *ks;
KS_Path *ksp;
@@ -10889,39 +10711,39 @@ static void expand_keyingsets(FileData *fd, Main *mainvar, ListBase *list)
/* expand the ID-pointers in KeyingSets's paths */
for (ks = list->first; ks; ks = ks->next) {
for (ksp = ks->paths.first; ksp; ksp = ksp->next) {
- expand_doit(fd, mainvar, ksp->id);
+ BLO_expand(expander, ksp->id);
}
}
}
-static void expand_particlesettings(FileData *fd, Main *mainvar, ParticleSettings *part)
+static void expand_particlesettings(BlendExpander *expander, ParticleSettings *part)
{
int a;
- expand_doit(fd, mainvar, part->instance_object);
- expand_doit(fd, mainvar, part->instance_collection);
- expand_doit(fd, mainvar, part->force_group);
- expand_doit(fd, mainvar, part->bb_ob);
- expand_doit(fd, mainvar, part->collision_group);
+ BLO_expand(expander, part->instance_object);
+ BLO_expand(expander, part->instance_collection);
+ BLO_expand(expander, part->force_group);
+ BLO_expand(expander, part->bb_ob);
+ BLO_expand(expander, part->collision_group);
for (a = 0; a < MAX_MTEX; a++) {
if (part->mtex[a]) {
- expand_doit(fd, mainvar, part->mtex[a]->tex);
- expand_doit(fd, mainvar, part->mtex[a]->object);
+ BLO_expand(expander, part->mtex[a]->tex);
+ BLO_expand(expander, part->mtex[a]->object);
}
}
if (part->effector_weights) {
- expand_doit(fd, mainvar, part->effector_weights->group);
+ BLO_expand(expander, part->effector_weights->group);
}
if (part->pd) {
- expand_doit(fd, mainvar, part->pd->tex);
- expand_doit(fd, mainvar, part->pd->f_source);
+ BLO_expand(expander, part->pd->tex);
+ BLO_expand(expander, part->pd->f_source);
}
if (part->pd2) {
- expand_doit(fd, mainvar, part->pd2->tex);
- expand_doit(fd, mainvar, part->pd2->f_source);
+ BLO_expand(expander, part->pd2->tex);
+ BLO_expand(expander, part->pd2->f_source);
}
if (part->boids) {
@@ -10932,58 +10754,58 @@ static void expand_particlesettings(FileData *fd, Main *mainvar, ParticleSetting
for (rule = state->rules.first; rule; rule = rule->next) {
if (rule->type == eBoidRuleType_Avoid) {
BoidRuleGoalAvoid *gabr = (BoidRuleGoalAvoid *)rule;
- expand_doit(fd, mainvar, gabr->ob);
+ BLO_expand(expander, gabr->ob);
}
else if (rule->type == eBoidRuleType_FollowLeader) {
BoidRuleFollowLeader *flbr = (BoidRuleFollowLeader *)rule;
- expand_doit(fd, mainvar, flbr->ob);
+ BLO_expand(expander, flbr->ob);
}
}
}
}
LISTBASE_FOREACH (ParticleDupliWeight *, dw, &part->instance_weights) {
- expand_doit(fd, mainvar, dw->ob);
+ BLO_expand(expander, dw->ob);
}
}
-static void expand_collection(FileData *fd, Main *mainvar, Collection *collection)
+static void expand_collection(BlendExpander *expander, Collection *collection)
{
LISTBASE_FOREACH (CollectionObject *, cob, &collection->gobject) {
- expand_doit(fd, mainvar, cob->ob);
+ BLO_expand(expander, cob->ob);
}
LISTBASE_FOREACH (CollectionChild *, child, &collection->children) {
- expand_doit(fd, mainvar, child->collection);
+ BLO_expand(expander, child->collection);
}
#ifdef USE_COLLECTION_COMPAT_28
if (collection->collection != NULL) {
- expand_scene_collection(fd, mainvar, collection->collection);
+ expand_scene_collection(expander, collection->collection);
}
#endif
}
-static void expand_key(FileData *fd, Main *mainvar, Key *key)
+static void expand_key(BlendExpander *expander, Key *key)
{
- expand_doit(fd, mainvar, key->ipo); // XXX deprecated - old animation system
+ BLO_expand(expander, key->ipo); // XXX deprecated - old animation system
}
-static void expand_node_socket(FileData *fd, Main *mainvar, bNodeSocket *sock)
+static void expand_node_socket(BlendExpander *expander, bNodeSocket *sock)
{
- expand_idprops(fd, mainvar, sock->prop);
+ expand_idprops(expander, sock->prop);
if (sock->default_value != NULL) {
switch ((eNodeSocketDatatype)sock->type) {
case SOCK_OBJECT: {
bNodeSocketValueObject *default_value = sock->default_value;
- expand_doit(fd, mainvar, default_value->value);
+ BLO_expand(expander, default_value->value);
break;
}
case SOCK_IMAGE: {
bNodeSocketValueImage *default_value = sock->default_value;
- expand_doit(fd, mainvar, default_value->value);
+ BLO_expand(expander, default_value->value);
break;
}
case SOCK_FLOAT:
@@ -11004,155 +10826,145 @@ static void expand_node_socket(FileData *fd, Main *mainvar, bNodeSocket *sock)
}
}
-static void expand_node_sockets(FileData *fd, Main *mainvar, ListBase *sockets)
+static void expand_node_sockets(BlendExpander *expander, ListBase *sockets)
{
LISTBASE_FOREACH (bNodeSocket *, sock, sockets) {
- expand_node_socket(fd, mainvar, sock);
+ expand_node_socket(expander, sock);
}
}
-static void expand_nodetree(FileData *fd, Main *mainvar, bNodeTree *ntree)
+static void expand_nodetree(BlendExpander *expander, bNodeTree *ntree)
{
bNode *node;
if (ntree->gpd) {
- expand_doit(fd, mainvar, ntree->gpd);
+ BLO_expand(expander, ntree->gpd);
}
for (node = ntree->nodes.first; node; node = node->next) {
if (node->id && node->type != CMP_NODE_R_LAYERS) {
- expand_doit(fd, mainvar, node->id);
+ BLO_expand(expander, node->id);
}
- expand_idprops(fd, mainvar, node->prop);
+ expand_idprops(expander, node->prop);
- expand_node_sockets(fd, mainvar, &node->inputs);
- expand_node_sockets(fd, mainvar, &node->outputs);
+ expand_node_sockets(expander, &node->inputs);
+ expand_node_sockets(expander, &node->outputs);
}
- expand_node_sockets(fd, mainvar, &ntree->inputs);
- expand_node_sockets(fd, mainvar, &ntree->outputs);
+ expand_node_sockets(expander, &ntree->inputs);
+ expand_node_sockets(expander, &ntree->outputs);
}
-static void expand_texture(FileData *fd, Main *mainvar, Tex *tex)
+static void expand_texture(BlendExpander *expander, Tex *tex)
{
- expand_doit(fd, mainvar, tex->ima);
- expand_doit(fd, mainvar, tex->ipo); // XXX deprecated - old animation system
+ BLO_expand(expander, tex->ima);
+ BLO_expand(expander, tex->ipo); // XXX deprecated - old animation system
}
-static void expand_brush(FileData *fd, Main *mainvar, Brush *brush)
+static void expand_brush(BlendExpander *expander, Brush *brush)
{
- expand_doit(fd, mainvar, brush->mtex.tex);
- expand_doit(fd, mainvar, brush->mask_mtex.tex);
- expand_doit(fd, mainvar, brush->clone.image);
- expand_doit(fd, mainvar, brush->paint_curve);
+ BLO_expand(expander, brush->mtex.tex);
+ BLO_expand(expander, brush->mask_mtex.tex);
+ BLO_expand(expander, brush->clone.image);
+ BLO_expand(expander, brush->paint_curve);
if (brush->gpencil_settings != NULL) {
- expand_doit(fd, mainvar, brush->gpencil_settings->material);
+ BLO_expand(expander, brush->gpencil_settings->material);
}
}
-static void expand_material(FileData *fd, Main *mainvar, Material *ma)
+static void expand_material(BlendExpander *expander, Material *ma)
{
- expand_doit(fd, mainvar, ma->ipo); // XXX deprecated - old animation system
+ BLO_expand(expander, ma->ipo); // XXX deprecated - old animation system
if (ma->gp_style) {
MaterialGPencilStyle *gp_style = ma->gp_style;
- expand_doit(fd, mainvar, gp_style->sima);
- expand_doit(fd, mainvar, gp_style->ima);
+ BLO_expand(expander, gp_style->sima);
+ BLO_expand(expander, gp_style->ima);
}
}
-static void expand_light(FileData *fd, Main *mainvar, Light *la)
+static void expand_light(BlendExpander *expander, Light *la)
{
- expand_doit(fd, mainvar, la->ipo); // XXX deprecated - old animation system
+ BLO_expand(expander, la->ipo); // XXX deprecated - old animation system
}
-static void expand_lattice(FileData *fd, Main *mainvar, Lattice *lt)
+static void expand_lattice(BlendExpander *expander, Lattice *lt)
{
- expand_doit(fd, mainvar, lt->ipo); // XXX deprecated - old animation system
- expand_doit(fd, mainvar, lt->key);
+ BLO_expand(expander, lt->ipo); // XXX deprecated - old animation system
+ BLO_expand(expander, lt->key);
}
-static void expand_world(FileData *fd, Main *mainvar, World *wrld)
+static void expand_world(BlendExpander *expander, World *wrld)
{
- expand_doit(fd, mainvar, wrld->ipo); // XXX deprecated - old animation system
+ BLO_expand(expander, wrld->ipo); // XXX deprecated - old animation system
}
-static void expand_mball(FileData *fd, Main *mainvar, MetaBall *mb)
+static void expand_mball(BlendExpander *expander, MetaBall *mb)
{
int a;
for (a = 0; a < mb->totcol; a++) {
- expand_doit(fd, mainvar, mb->mat[a]);
+ BLO_expand(expander, mb->mat[a]);
}
}
-static void expand_curve(FileData *fd, Main *mainvar, Curve *cu)
+static void expand_curve(BlendExpander *expander, Curve *cu)
{
int a;
for (a = 0; a < cu->totcol; a++) {
- expand_doit(fd, mainvar, cu->mat[a]);
+ BLO_expand(expander, cu->mat[a]);
}
- expand_doit(fd, mainvar, cu->vfont);
- expand_doit(fd, mainvar, cu->vfontb);
- expand_doit(fd, mainvar, cu->vfonti);
- expand_doit(fd, mainvar, cu->vfontbi);
- expand_doit(fd, mainvar, cu->key);
- expand_doit(fd, mainvar, cu->ipo); // XXX deprecated - old animation system
- expand_doit(fd, mainvar, cu->bevobj);
- expand_doit(fd, mainvar, cu->taperobj);
- expand_doit(fd, mainvar, cu->textoncurve);
+ BLO_expand(expander, cu->vfont);
+ BLO_expand(expander, cu->vfontb);
+ BLO_expand(expander, cu->vfonti);
+ BLO_expand(expander, cu->vfontbi);
+ BLO_expand(expander, cu->key);
+ BLO_expand(expander, cu->ipo); // XXX deprecated - old animation system
+ BLO_expand(expander, cu->bevobj);
+ BLO_expand(expander, cu->taperobj);
+ BLO_expand(expander, cu->textoncurve);
}
-static void expand_mesh(FileData *fd, Main *mainvar, Mesh *me)
+static void expand_mesh(BlendExpander *expander, Mesh *me)
{
int a;
for (a = 0; a < me->totcol; a++) {
- expand_doit(fd, mainvar, me->mat[a]);
+ BLO_expand(expander, me->mat[a]);
}
- expand_doit(fd, mainvar, me->key);
- expand_doit(fd, mainvar, me->texcomesh);
+ BLO_expand(expander, me->key);
+ BLO_expand(expander, me->texcomesh);
}
-/* temp struct used to transport needed info to expand_constraint_cb() */
-typedef struct tConstraintExpandData {
- FileData *fd;
- Main *mainvar;
-} tConstraintExpandData;
/* callback function used to expand constraint ID-links */
static void expand_constraint_cb(bConstraint *UNUSED(con),
ID **idpoin,
bool UNUSED(is_reference),
void *userdata)
{
- tConstraintExpandData *ced = (tConstraintExpandData *)userdata;
- expand_doit(ced->fd, ced->mainvar, *idpoin);
+ BlendExpander *expander = userdata;
+ BLO_expand(expander, *idpoin);
}
-static void expand_constraints(FileData *fd, Main *mainvar, ListBase *lb)
+static void expand_constraints(BlendExpander *expander, ListBase *lb)
{
- tConstraintExpandData ced;
bConstraint *curcon;
- /* relink all ID-blocks used by the constraints */
- ced.fd = fd;
- ced.mainvar = mainvar;
-
- BKE_constraints_id_loop(lb, expand_constraint_cb, &ced);
+ BKE_constraints_id_loop(lb, expand_constraint_cb, expander);
/* deprecated manual expansion stuff */
for (curcon = lb->first; curcon; curcon = curcon->next) {
if (curcon->ipo) {
- expand_doit(fd, mainvar, curcon->ipo); // XXX deprecated - old animation system
+ BLO_expand(expander, curcon->ipo); // XXX deprecated - old animation system
}
}
}
-static void expand_pose(FileData *fd, Main *mainvar, bPose *pose)
+static void expand_pose(BlendExpander *expander, bPose *pose)
{
bPoseChannel *chan;
@@ -11161,25 +10973,25 @@ static void expand_pose(FileData *fd, Main *mainvar, bPose *pose)
}
for (chan = pose->chanbase.first; chan; chan = chan->next) {
- expand_constraints(fd, mainvar, &chan->constraints);
- expand_idprops(fd, mainvar, chan->prop);
- expand_doit(fd, mainvar, chan->custom);
+ expand_constraints(expander, &chan->constraints);
+ expand_idprops(expander, chan->prop);
+ BLO_expand(expander, chan->custom);
}
}
-static void expand_bones(FileData *fd, Main *mainvar, Bone *bone)
+static void expand_bones(BlendExpander *expander, Bone *bone)
{
- expand_idprops(fd, mainvar, bone->prop);
+ expand_idprops(expander, bone->prop);
LISTBASE_FOREACH (Bone *, curBone, &bone->childbase) {
- expand_bones(fd, mainvar, curBone);
+ expand_bones(expander, curBone);
}
}
-static void expand_armature(FileData *fd, Main *mainvar, bArmature *arm)
+static void expand_armature(BlendExpander *expander, bArmature *arm)
{
LISTBASE_FOREACH (Bone *, curBone, &arm->bonebase) {
- expand_bones(fd, mainvar, curBone);
+ expand_bones(expander, curBone);
}
}
@@ -11188,298 +11000,268 @@ static void expand_object_expandModifiers(void *userData,
ID **idpoin,
int UNUSED(cb_flag))
{
- struct {
- FileData *fd;
- Main *mainvar;
- } *data = userData;
-
- FileData *fd = data->fd;
- Main *mainvar = data->mainvar;
-
- expand_doit(fd, mainvar, *idpoin);
+ BlendExpander *expander = userData;
+ BLO_expand(expander, *idpoin);
}
-static void expand_object(FileData *fd, Main *mainvar, Object *ob)
+static void expand_object(BlendExpander *expander, Object *ob)
{
ParticleSystem *psys;
bActionStrip *strip;
PartEff *paf;
int a;
- expand_doit(fd, mainvar, ob->data);
+ BLO_expand(expander, ob->data);
/* expand_object_expandModifier() */
if (ob->modifiers.first) {
- struct {
- FileData *fd;
- Main *mainvar;
- } data;
- data.fd = fd;
- data.mainvar = mainvar;
-
- BKE_modifiers_foreach_ID_link(ob, expand_object_expandModifiers, (void *)&data);
+ BKE_modifiers_foreach_ID_link(ob, expand_object_expandModifiers, expander);
}
/* expand_object_expandModifier() */
if (ob->greasepencil_modifiers.first) {
- struct {
- FileData *fd;
- Main *mainvar;
- } data;
- data.fd = fd;
- data.mainvar = mainvar;
-
- BKE_gpencil_modifiers_foreach_ID_link(ob, expand_object_expandModifiers, (void *)&data);
+ BKE_gpencil_modifiers_foreach_ID_link(ob, expand_object_expandModifiers, expander);
}
/* expand_object_expandShaderFx() */
if (ob->shader_fx.first) {
- struct {
- FileData *fd;
- Main *mainvar;
- } data;
- data.fd = fd;
- data.mainvar = mainvar;
-
- BKE_shaderfx_foreach_ID_link(ob, expand_object_expandModifiers, (void *)&data);
+ BKE_shaderfx_foreach_ID_link(ob, expand_object_expandModifiers, expander);
}
- expand_pose(fd, mainvar, ob->pose);
- expand_doit(fd, mainvar, ob->poselib);
- expand_constraints(fd, mainvar, &ob->constraints);
+ expand_pose(expander, ob->pose);
+ BLO_expand(expander, ob->poselib);
+ expand_constraints(expander, &ob->constraints);
- expand_doit(fd, mainvar, ob->gpd);
+ BLO_expand(expander, ob->gpd);
// XXX deprecated - old animation system (for version patching only)
- expand_doit(fd, mainvar, ob->ipo);
- expand_doit(fd, mainvar, ob->action);
+ BLO_expand(expander, ob->ipo);
+ BLO_expand(expander, ob->action);
- expand_constraint_channels(fd, mainvar, &ob->constraintChannels);
+ expand_constraint_channels(expander, &ob->constraintChannels);
for (strip = ob->nlastrips.first; strip; strip = strip->next) {
- expand_doit(fd, mainvar, strip->object);
- expand_doit(fd, mainvar, strip->act);
- expand_doit(fd, mainvar, strip->ipo);
+ BLO_expand(expander, strip->object);
+ BLO_expand(expander, strip->act);
+ BLO_expand(expander, strip->ipo);
}
// XXX deprecated - old animation system (for version patching only)
for (a = 0; a < ob->totcol; a++) {
- expand_doit(fd, mainvar, ob->mat[a]);
+ BLO_expand(expander, ob->mat[a]);
}
paf = blo_do_version_give_parteff_245(ob);
if (paf && paf->group) {
- expand_doit(fd, mainvar, paf->group);
+ BLO_expand(expander, paf->group);
}
if (ob->instance_collection) {
- expand_doit(fd, mainvar, ob->instance_collection);
+ BLO_expand(expander, ob->instance_collection);
}
if (ob->proxy) {
- expand_doit(fd, mainvar, ob->proxy);
+ BLO_expand(expander, ob->proxy);
}
if (ob->proxy_group) {
- expand_doit(fd, mainvar, ob->proxy_group);
+ BLO_expand(expander, ob->proxy_group);
}
for (psys = ob->particlesystem.first; psys; psys = psys->next) {
- expand_doit(fd, mainvar, psys->part);
+ BLO_expand(expander, psys->part);
}
if (ob->pd) {
- expand_doit(fd, mainvar, ob->pd->tex);
- expand_doit(fd, mainvar, ob->pd->f_source);
+ BLO_expand(expander, ob->pd->tex);
+ BLO_expand(expander, ob->pd->f_source);
}
if (ob->soft) {
- expand_doit(fd, mainvar, ob->soft->collision_group);
+ BLO_expand(expander, ob->soft->collision_group);
if (ob->soft->effector_weights) {
- expand_doit(fd, mainvar, ob->soft->effector_weights->group);
+ BLO_expand(expander, ob->soft->effector_weights->group);
}
}
if (ob->rigidbody_constraint) {
- expand_doit(fd, mainvar, ob->rigidbody_constraint->ob1);
- expand_doit(fd, mainvar, ob->rigidbody_constraint->ob2);
+ BLO_expand(expander, ob->rigidbody_constraint->ob1);
+ BLO_expand(expander, ob->rigidbody_constraint->ob2);
}
if (ob->currentlod) {
LodLevel *level;
for (level = ob->lodlevels.first; level; level = level->next) {
- expand_doit(fd, mainvar, level->source);
+ BLO_expand(expander, level->source);
}
}
}
#ifdef USE_COLLECTION_COMPAT_28
-static void expand_scene_collection(FileData *fd, Main *mainvar, SceneCollection *sc)
+static void expand_scene_collection(BlendExpander *expander, SceneCollection *sc)
{
LISTBASE_FOREACH (LinkData *, link, &sc->objects) {
- expand_doit(fd, mainvar, link->data);
+ BLO_expand(expander, link->data);
}
LISTBASE_FOREACH (SceneCollection *, nsc, &sc->scene_collections) {
- expand_scene_collection(fd, mainvar, nsc);
+ expand_scene_collection(expander, nsc);
}
}
#endif
-static void expand_scene(FileData *fd, Main *mainvar, Scene *sce)
+static void expand_scene(BlendExpander *expander, Scene *sce)
{
SceneRenderLayer *srl;
FreestyleModuleConfig *module;
FreestyleLineSet *lineset;
LISTBASE_FOREACH (Base *, base_legacy, &sce->base) {
- expand_doit(fd, mainvar, base_legacy->object);
+ BLO_expand(expander, base_legacy->object);
}
- expand_doit(fd, mainvar, sce->camera);
- expand_doit(fd, mainvar, sce->world);
+ BLO_expand(expander, sce->camera);
+ BLO_expand(expander, sce->world);
- expand_keyingsets(fd, mainvar, &sce->keyingsets);
+ expand_keyingsets(expander, &sce->keyingsets);
if (sce->set) {
- expand_doit(fd, mainvar, sce->set);
+ BLO_expand(expander, sce->set);
}
for (srl = sce->r.layers.first; srl; srl = srl->next) {
- expand_doit(fd, mainvar, srl->mat_override);
+ BLO_expand(expander, srl->mat_override);
for (module = srl->freestyleConfig.modules.first; module; module = module->next) {
if (module->script) {
- expand_doit(fd, mainvar, module->script);
+ BLO_expand(expander, module->script);
}
}
for (lineset = srl->freestyleConfig.linesets.first; lineset; lineset = lineset->next) {
if (lineset->group) {
- expand_doit(fd, mainvar, lineset->group);
+ BLO_expand(expander, lineset->group);
}
- expand_doit(fd, mainvar, lineset->linestyle);
+ BLO_expand(expander, lineset->linestyle);
}
}
LISTBASE_FOREACH (ViewLayer *, view_layer, &sce->view_layers) {
- expand_idprops(fd, mainvar, view_layer->id_properties);
+ expand_idprops(expander, view_layer->id_properties);
for (module = view_layer->freestyle_config.modules.first; module; module = module->next) {
if (module->script) {
- expand_doit(fd, mainvar, module->script);
+ BLO_expand(expander, module->script);
}
}
for (lineset = view_layer->freestyle_config.linesets.first; lineset; lineset = lineset->next) {
if (lineset->group) {
- expand_doit(fd, mainvar, lineset->group);
+ BLO_expand(expander, lineset->group);
}
- expand_doit(fd, mainvar, lineset->linestyle);
+ BLO_expand(expander, lineset->linestyle);
}
}
if (sce->gpd) {
- expand_doit(fd, mainvar, sce->gpd);
+ BLO_expand(expander, sce->gpd);
}
if (sce->ed) {
Sequence *seq;
SEQ_BEGIN (sce->ed, seq) {
- expand_idprops(fd, mainvar, seq->prop);
+ expand_idprops(expander, seq->prop);
if (seq->scene) {
- expand_doit(fd, mainvar, seq->scene);
+ BLO_expand(expander, seq->scene);
}
if (seq->scene_camera) {
- expand_doit(fd, mainvar, seq->scene_camera);
+ BLO_expand(expander, seq->scene_camera);
}
if (seq->clip) {
- expand_doit(fd, mainvar, seq->clip);
+ BLO_expand(expander, seq->clip);
}
if (seq->mask) {
- expand_doit(fd, mainvar, seq->mask);
+ BLO_expand(expander, seq->mask);
}
if (seq->sound) {
- expand_doit(fd, mainvar, seq->sound);
+ BLO_expand(expander, seq->sound);
}
if (seq->type == SEQ_TYPE_TEXT && seq->effectdata) {
TextVars *data = seq->effectdata;
- expand_doit(fd, mainvar, data->text_font);
+ BLO_expand(expander, data->text_font);
}
}
SEQ_END;
}
if (sce->rigidbody_world) {
- expand_doit(fd, mainvar, sce->rigidbody_world->group);
- expand_doit(fd, mainvar, sce->rigidbody_world->constraints);
+ BLO_expand(expander, sce->rigidbody_world->group);
+ BLO_expand(expander, sce->rigidbody_world->constraints);
}
LISTBASE_FOREACH (TimeMarker *, marker, &sce->markers) {
if (marker->camera) {
- expand_doit(fd, mainvar, marker->camera);
+ BLO_expand(expander, marker->camera);
}
}
- expand_doit(fd, mainvar, sce->clip);
+ BLO_expand(expander, sce->clip);
#ifdef USE_COLLECTION_COMPAT_28
if (sce->collection) {
- expand_scene_collection(fd, mainvar, sce->collection);
+ expand_scene_collection(expander, sce->collection);
}
#endif
if (sce->r.bake.cage_object) {
- expand_doit(fd, mainvar, sce->r.bake.cage_object);
+ BLO_expand(expander, sce->r.bake.cage_object);
}
}
-static void expand_camera(FileData *fd, Main *mainvar, Camera *ca)
+static void expand_camera(BlendExpander *expander, Camera *ca)
{
- expand_doit(fd, mainvar, ca->ipo); // XXX deprecated - old animation system
+ BLO_expand(expander, ca->ipo); // XXX deprecated - old animation system
LISTBASE_FOREACH (CameraBGImage *, bgpic, &ca->bg_images) {
if (bgpic->source == CAM_BGIMG_SOURCE_IMAGE) {
- expand_doit(fd, mainvar, bgpic->ima);
+ BLO_expand(expander, bgpic->ima);
}
else if (bgpic->source == CAM_BGIMG_SOURCE_MOVIE) {
- expand_doit(fd, mainvar, bgpic->ima);
+ BLO_expand(expander, bgpic->ima);
}
}
}
-static void expand_cachefile(FileData *UNUSED(fd),
- Main *UNUSED(mainvar),
- CacheFile *UNUSED(cache_file))
+static void expand_cachefile(BlendExpander *UNUSED(expander), CacheFile *UNUSED(cache_file))
{
}
-static void expand_speaker(FileData *fd, Main *mainvar, Speaker *spk)
+static void expand_speaker(BlendExpander *expander, Speaker *spk)
{
- expand_doit(fd, mainvar, spk->sound);
+ BLO_expand(expander, spk->sound);
}
-static void expand_sound(FileData *fd, Main *mainvar, bSound *snd)
+static void expand_sound(BlendExpander *expander, bSound *snd)
{
- expand_doit(fd, mainvar, snd->ipo); // XXX deprecated - old animation system
+ BLO_expand(expander, snd->ipo); // XXX deprecated - old animation system
}
-static void expand_lightprobe(FileData *UNUSED(fd), Main *UNUSED(mainvar), LightProbe *UNUSED(prb))
+static void expand_lightprobe(BlendExpander *UNUSED(expander), LightProbe *UNUSED(prb))
{
}
-static void expand_movieclip(FileData *UNUSED(fd), Main *UNUSED(mainvar), MovieClip *UNUSED(clip))
+static void expand_movieclip(BlendExpander *UNUSED(expander), MovieClip *UNUSED(clip))
{
}
-static void expand_mask_parent(FileData *fd, Main *mainvar, MaskParent *parent)
+static void expand_mask_parent(BlendExpander *expander, MaskParent *parent)
{
if (parent->id) {
- expand_doit(fd, mainvar, parent->id);
+ BLO_expand(expander, parent->id);
}
}
-static void expand_mask(FileData *fd, Main *mainvar, Mask *mask)
+static void expand_mask(BlendExpander *expander, Mask *mask)
{
MaskLayer *mask_layer;
@@ -11492,98 +11274,98 @@ static void expand_mask(FileData *fd, Main *mainvar, Mask *mask)
for (i = 0; i < spline->tot_point; i++) {
MaskSplinePoint *point = &spline->points[i];
- expand_mask_parent(fd, mainvar, &point->parent);
+ expand_mask_parent(expander, &point->parent);
}
- expand_mask_parent(fd, mainvar, &spline->parent);
+ expand_mask_parent(expander, &spline->parent);
}
}
}
-static void expand_linestyle(FileData *fd, Main *mainvar, FreestyleLineStyle *linestyle)
+static void expand_linestyle(BlendExpander *expander, FreestyleLineStyle *linestyle)
{
int a;
LineStyleModifier *m;
for (a = 0; a < MAX_MTEX; a++) {
if (linestyle->mtex[a]) {
- expand_doit(fd, mainvar, linestyle->mtex[a]->tex);
- expand_doit(fd, mainvar, linestyle->mtex[a]->object);
+ BLO_expand(expander, linestyle->mtex[a]->tex);
+ BLO_expand(expander, linestyle->mtex[a]->object);
}
}
for (m = linestyle->color_modifiers.first; m; m = m->next) {
if (m->type == LS_MODIFIER_DISTANCE_FROM_OBJECT) {
- expand_doit(fd, mainvar, ((LineStyleColorModifier_DistanceFromObject *)m)->target);
+ BLO_expand(expander, ((LineStyleColorModifier_DistanceFromObject *)m)->target);
}
}
for (m = linestyle->alpha_modifiers.first; m; m = m->next) {
if (m->type == LS_MODIFIER_DISTANCE_FROM_OBJECT) {
- expand_doit(fd, mainvar, ((LineStyleAlphaModifier_DistanceFromObject *)m)->target);
+ BLO_expand(expander, ((LineStyleAlphaModifier_DistanceFromObject *)m)->target);
}
}
for (m = linestyle->thickness_modifiers.first; m; m = m->next) {
if (m->type == LS_MODIFIER_DISTANCE_FROM_OBJECT) {
- expand_doit(fd, mainvar, ((LineStyleThicknessModifier_DistanceFromObject *)m)->target);
+ BLO_expand(expander, ((LineStyleThicknessModifier_DistanceFromObject *)m)->target);
}
}
}
-static void expand_gpencil(FileData *fd, Main *mainvar, bGPdata *gpd)
+static void expand_gpencil(BlendExpander *expander, bGPdata *gpd)
{
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
- expand_doit(fd, mainvar, gpl->parent);
+ BLO_expand(expander, gpl->parent);
}
for (int a = 0; a < gpd->totcol; a++) {
- expand_doit(fd, mainvar, gpd->mat[a]);
+ BLO_expand(expander, gpd->mat[a]);
}
}
-static void expand_workspace(FileData *fd, Main *mainvar, WorkSpace *workspace)
+static void expand_workspace(BlendExpander *expander, WorkSpace *workspace)
{
LISTBASE_FOREACH (WorkSpaceLayout *, layout, &workspace->layouts) {
- expand_doit(fd, mainvar, BKE_workspace_layout_screen_get(layout));
+ BLO_expand(expander, BKE_workspace_layout_screen_get(layout));
}
}
-static void expand_hair(FileData *fd, Main *mainvar, Hair *hair)
+static void expand_hair(BlendExpander *expander, Hair *hair)
{
for (int a = 0; a < hair->totcol; a++) {
- expand_doit(fd, mainvar, hair->mat[a]);
+ BLO_expand(expander, hair->mat[a]);
}
if (hair->adt) {
- expand_animdata(fd, mainvar, hair->adt);
+ expand_animdata(expander, hair->adt);
}
}
-static void expand_pointcloud(FileData *fd, Main *mainvar, PointCloud *pointcloud)
+static void expand_pointcloud(BlendExpander *expander, PointCloud *pointcloud)
{
for (int a = 0; a < pointcloud->totcol; a++) {
- expand_doit(fd, mainvar, pointcloud->mat[a]);
+ BLO_expand(expander, pointcloud->mat[a]);
}
if (pointcloud->adt) {
- expand_animdata(fd, mainvar, pointcloud->adt);
+ expand_animdata(expander, pointcloud->adt);
}
}
-static void expand_volume(FileData *fd, Main *mainvar, Volume *volume)
+static void expand_volume(BlendExpander *expander, Volume *volume)
{
for (int a = 0; a < volume->totcol; a++) {
- expand_doit(fd, mainvar, volume->mat[a]);
+ BLO_expand(expander, volume->mat[a]);
}
if (volume->adt) {
- expand_animdata(fd, mainvar, volume->adt);
+ expand_animdata(expander, volume->adt);
}
}
-static void expand_simulation(FileData *fd, Main *mainvar, Simulation *simulation)
+static void expand_simulation(BlendExpander *expander, Simulation *simulation)
{
if (simulation->adt) {
- expand_animdata(fd, mainvar, simulation->adt);
+ expand_animdata(expander, simulation->adt);
}
}
@@ -11612,6 +11394,8 @@ void BLO_expand_main(void *fdhandle, Main *mainvar)
int a;
bool do_it = true;
+ BlendExpander expander = {fd, mainvar};
+
while (do_it) {
do_it = false;
@@ -11620,104 +11404,104 @@ void BLO_expand_main(void *fdhandle, Main *mainvar)
id = lbarray[a]->first;
while (id) {
if (id->tag & LIB_TAG_NEED_EXPAND) {
- expand_id(fd, mainvar, id);
+ expand_id(&expander, id);
switch (GS(id->name)) {
case ID_OB:
- expand_object(fd, mainvar, (Object *)id);
+ expand_object(&expander, (Object *)id);
break;
case ID_ME:
- expand_mesh(fd, mainvar, (Mesh *)id);
+ expand_mesh(&expander, (Mesh *)id);
break;
case ID_CU:
- expand_curve(fd, mainvar, (Curve *)id);
+ expand_curve(&expander, (Curve *)id);
break;
case ID_MB:
- expand_mball(fd, mainvar, (MetaBall *)id);
+ expand_mball(&expander, (MetaBall *)id);
break;
case ID_SCE:
- expand_scene(fd, mainvar, (Scene *)id);
+ expand_scene(&expander, (Scene *)id);
break;
case ID_MA:
- expand_material(fd, mainvar, (Material *)id);
+ expand_material(&expander, (Material *)id);
break;
case ID_TE:
- expand_texture(fd, mainvar, (Tex *)id);
+ expand_texture(&expander, (Tex *)id);
break;
case ID_WO:
- expand_world(fd, mainvar, (World *)id);
+ expand_world(&expander, (World *)id);
break;
case ID_LT:
- expand_lattice(fd, mainvar, (Lattice *)id);
+ expand_lattice(&expander, (Lattice *)id);
break;
case ID_LA:
- expand_light(fd, mainvar, (Light *)id);
+ expand_light(&expander, (Light *)id);
break;
case ID_KE:
- expand_key(fd, mainvar, (Key *)id);
+ expand_key(&expander, (Key *)id);
break;
case ID_CA:
- expand_camera(fd, mainvar, (Camera *)id);
+ expand_camera(&expander, (Camera *)id);
break;
case ID_SPK:
- expand_speaker(fd, mainvar, (Speaker *)id);
+ expand_speaker(&expander, (Speaker *)id);
break;
case ID_SO:
- expand_sound(fd, mainvar, (bSound *)id);
+ expand_sound(&expander, (bSound *)id);
break;
case ID_LP:
- expand_lightprobe(fd, mainvar, (LightProbe *)id);
+ expand_lightprobe(&expander, (LightProbe *)id);
break;
case ID_AR:
- expand_armature(fd, mainvar, (bArmature *)id);
+ expand_armature(&expander, (bArmature *)id);
break;
case ID_AC:
- expand_action(fd, mainvar, (bAction *)id); // XXX deprecated - old animation system
+ expand_action(&expander, (bAction *)id); // XXX deprecated - old animation system
break;
case ID_GR:
- expand_collection(fd, mainvar, (Collection *)id);
+ expand_collection(&expander, (Collection *)id);
break;
case ID_NT:
- expand_nodetree(fd, mainvar, (bNodeTree *)id);
+ expand_nodetree(&expander, (bNodeTree *)id);
break;
case ID_BR:
- expand_brush(fd, mainvar, (Brush *)id);
+ expand_brush(&expander, (Brush *)id);
break;
case ID_IP:
- expand_ipo(fd, mainvar, (Ipo *)id); // XXX deprecated - old animation system
+ expand_ipo(&expander, (Ipo *)id); // XXX deprecated - old animation system
break;
case ID_PA:
- expand_particlesettings(fd, mainvar, (ParticleSettings *)id);
+ expand_particlesettings(&expander, (ParticleSettings *)id);
break;
case ID_MC:
- expand_movieclip(fd, mainvar, (MovieClip *)id);
+ expand_movieclip(&expander, (MovieClip *)id);
break;
case ID_MSK:
- expand_mask(fd, mainvar, (Mask *)id);
+ expand_mask(&expander, (Mask *)id);
break;
case ID_LS:
- expand_linestyle(fd, mainvar, (FreestyleLineStyle *)id);
+ expand_linestyle(&expander, (FreestyleLineStyle *)id);
break;
case ID_GD:
- expand_gpencil(fd, mainvar, (bGPdata *)id);
+ expand_gpencil(&expander, (bGPdata *)id);
break;
case ID_CF:
- expand_cachefile(fd, mainvar, (CacheFile *)id);
+ expand_cachefile(&expander, (CacheFile *)id);
break;
case ID_WS:
- expand_workspace(fd, mainvar, (WorkSpace *)id);
+ expand_workspace(&expander, (WorkSpace *)id);
break;
case ID_HA:
- expand_hair(fd, mainvar, (Hair *)id);
+ expand_hair(&expander, (Hair *)id);
break;
case ID_PT:
- expand_pointcloud(fd, mainvar, (PointCloud *)id);
+ expand_pointcloud(&expander, (PointCloud *)id);
break;
case ID_VO:
- expand_volume(fd, mainvar, (Volume *)id);
+ expand_volume(&expander, (Volume *)id);
break;
case ID_SIM:
- expand_simulation(fd, mainvar, (Simulation *)id);
+ expand_simulation(&expander, (Simulation *)id);
break;
default:
break;
@@ -12084,10 +11868,6 @@ ID *BLO_library_link_named_part(Main *mainl,
* \param idcode: The kind of data-block to link.
* \param name: The name of the data-block (without the 2 char ID prefix).
* \param flag: Options for linking, used for instantiating.
- * \param scene: The scene in which to instantiate objects/collections
- * (if NULL, no instantiation is done).
- * \param v3d: The active 3D viewport.
- * (only to define active layers for instantiated objects & collections, can be NULL).
* \return the linked ID when found.
*/
ID *BLO_library_link_named_part_ex(
@@ -12153,13 +11933,13 @@ static Main *library_link_begin(Main *mainvar, FileData **fd, const char *filepa
}
/**
- * Initialize the BlendHandle for linking library data.
+ * Initialize the #BlendHandle for linking library data.
*
* \param mainvar: The current main database, e.g. #G_MAIN or #CTX_data_main(C).
* \param bh: A blender file handle as returned by
* #BLO_blendhandle_from_file or #BLO_blendhandle_from_memory.
- * \param filepath: Used for relative linking, copied to the \a lib->name.
- * \return the library Main, to be passed to #BLO_library_append_named_part as \a mainl.
+ * \param filepath: Used for relative linking, copied to the `lib->filepath`.
+ * \return the library #Main, to be passed to #BLO_library_link_named_part_ex as \a mainl.
*/
Main *BLO_library_link_begin(Main *mainvar, BlendHandle **bh, const char *filepath)
{
@@ -12193,7 +11973,12 @@ static void split_main_newid(Main *mainptr, Main *main_newid)
}
}
-/* scene and v3d may be NULL. */
+/**
+ * \param scene: The scene in which to instantiate objects/collections
+ * (if NULL, no instantiation is done).
+ * \param v3d: The active 3D viewport.
+ * (only to define active layers for instantiated objects & collections, can be NULL).
+ */
static void library_link_end(Main *mainl,
FileData **fd,
const short flag,
@@ -12219,10 +12004,10 @@ static void library_link_end(Main *mainl,
/* make the lib path relative if required */
if (flag & FILE_RELPATH) {
/* use the full path, this could have been read by other library even */
- BLI_strncpy(curlib->name, curlib->filepath, sizeof(curlib->name));
+ BLI_strncpy(curlib->filepath, curlib->filepath_abs, sizeof(curlib->filepath));
/* uses current .blend file as reference */
- BLI_path_rel(curlib->name, BKE_main_blendfile_path_from_global());
+ BLI_path_rel(curlib->filepath, BKE_main_blendfile_path_from_global());
}
blo_join_main((*fd)->mainlist);
@@ -12274,7 +12059,7 @@ static void library_link_end(Main *mainl,
/* Give a base to loose objects and collections.
* Only directly linked objects & collections are instantiated by
- * `BLO_library_link_named_part_ex()` & co,
+ * #BLO_library_link_named_part_ex & co,
* here we handle indirect ones and other possible edge-cases. */
if (scene) {
add_collections_to_scene(mainvar, bmain, scene, view_layer, v3d, curlib, flag);
@@ -12371,7 +12156,7 @@ static void read_library_linked_id(
"non-linkable data type"),
BKE_idtype_idcode_to_name(GS(id->name)),
id->name + 2,
- mainvar->curlib->filepath,
+ mainvar->curlib->filepath_abs,
library_parent_filepath(mainvar->curlib));
}
@@ -12389,7 +12174,7 @@ static void read_library_linked_id(
TIP_("LIB: %s: '%s' missing from '%s', parent '%s'"),
BKE_idtype_idcode_to_name(GS(id->name)),
id->name + 2,
- mainvar->curlib->filepath,
+ mainvar->curlib->filepath_abs,
library_parent_filepath(mainvar->curlib));
/* Generate a placeholder for this ID (simplified version of read_libblock actually...). */
@@ -12509,22 +12294,22 @@ static FileData *read_library_file_data(FileData *basefd,
blo_reportf_wrap(basefd->reports,
RPT_INFO,
TIP_("Read packed library: '%s', parent '%s'"),
- mainptr->curlib->name,
+ mainptr->curlib->filepath,
library_parent_filepath(mainptr->curlib));
fd = blo_filedata_from_memory(pf->data, pf->size, basefd->reports);
/* Needed for library_append and read_libraries. */
- BLI_strncpy(fd->relabase, mainptr->curlib->filepath, sizeof(fd->relabase));
+ BLI_strncpy(fd->relabase, mainptr->curlib->filepath_abs, sizeof(fd->relabase));
}
else {
/* Read file on disk. */
blo_reportf_wrap(basefd->reports,
RPT_INFO,
TIP_("Read library: '%s', '%s', parent '%s'"),
+ mainptr->curlib->filepath_abs,
mainptr->curlib->filepath,
- mainptr->curlib->name,
library_parent_filepath(mainptr->curlib));
- fd = blo_filedata_from_file(mainptr->curlib->filepath, basefd->reports);
+ fd = blo_filedata_from_file(mainptr->curlib->filepath_abs, basefd->reports);
}
if (fd) {
@@ -12561,7 +12346,7 @@ static FileData *read_library_file_data(FileData *basefd,
if (fd == NULL) {
blo_reportf_wrap(
- basefd->reports, RPT_WARNING, TIP_("Cannot find lib '%s'"), mainptr->curlib->filepath);
+ basefd->reports, RPT_WARNING, TIP_("Cannot find lib '%s'"), mainptr->curlib->filepath_abs);
}
return fd;
@@ -12593,7 +12378,7 @@ static void read_libraries(FileData *basefd, ListBase *mainlist)
#if 0
printf("Reading linked data-blocks from %s (%s)\n",
mainptr->curlib->id.name,
- mainptr->curlib->name);
+ mainptr->curlib->filepath);
#endif
/* Open file if it has not been done yet. */
@@ -12643,10 +12428,10 @@ static void read_libraries(FileData *basefd, ListBase *mainlist)
lib_link_all(mainptr->curlib->filedata, mainptr);
}
- /* Note: No need to call `do_versions_after_linking()` or `BKE_main_id_refcount_recompute()`
+ /* Note: No need to call #do_versions_after_linking() or #BKE_main_id_refcount_recompute()
* here, as this function is only called for library 'subset' data handling, as part of either
- * full blendfile reading (`blo_read_file_internal()`), or libdata linking
- * (`library_link_end()`). */
+ * full blendfile reading (#blo_read_file_internal()), or library-data linking
+ * (#library_link_end()). */
/* Free file data we no longer need. */
if (mainptr->curlib->filedata) {
@@ -12787,7 +12572,7 @@ void BLO_read_pointer_array(BlendDataReader *reader, void **ptr_p)
int file_pointer_size = fd->filesdna->pointer_size;
int current_pointer_size = fd->memsdna->pointer_size;
- /* Overallocation is fine, but might be better to pass the length as parameter. */
+ /* Over-allocation is fine, but might be better to pass the length as parameter. */
int array_size = MEM_allocN_len(orig_array) / file_pointer_size;
void *final_array = NULL;
diff --git a/source/blender/blenloader/intern/versioning_250.c b/source/blender/blenloader/intern/versioning_250.c
index eaeef0d52c1..2c3b047af46 100644
--- a/source/blender/blenloader/intern/versioning_250.c
+++ b/source/blender/blenloader/intern/versioning_250.c
@@ -308,7 +308,7 @@ static void area_add_window_regions(ScrArea *area, SpaceLink *sl, ListBase *lb)
region->v2d.tot.ymax = 0.0f;
region->v2d.scroll |= (V2D_SCROLL_BOTTOM | V2D_SCROLL_HORIZONTAL_HANDLES);
- region->v2d.scroll |= (V2D_SCROLL_RIGHT);
+ region->v2d.scroll |= V2D_SCROLL_RIGHT;
region->v2d.align = V2D_ALIGN_NO_POS_Y;
region->v2d.flag |= V2D_VIEWSYNC_AREA_VERTICAL;
break;
@@ -334,7 +334,7 @@ static void area_add_window_regions(ScrArea *area, SpaceLink *sl, ListBase *lb)
region->v2d.minzoom = 0.01f;
region->v2d.maxzoom = 50;
region->v2d.scroll = (V2D_SCROLL_BOTTOM | V2D_SCROLL_HORIZONTAL_HANDLES);
- region->v2d.scroll |= (V2D_SCROLL_RIGHT);
+ region->v2d.scroll |= V2D_SCROLL_RIGHT;
region->v2d.keepzoom = V2D_LOCKZOOM_Y;
region->v2d.align = V2D_ALIGN_NO_POS_Y;
region->v2d.flag = V2D_VIEWSYNC_AREA_VERTICAL;
diff --git a/source/blender/blenloader/intern/versioning_270.c b/source/blender/blenloader/intern/versioning_270.c
index 2a468026ac5..496c8353f85 100644
--- a/source/blender/blenloader/intern/versioning_270.c
+++ b/source/blender/blenloader/intern/versioning_270.c
@@ -981,7 +981,7 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *bmain)
BLI_addtail(&ima->packedfiles, imapf);
imapf->packedfile = ima->packedfile;
- BLI_strncpy(imapf->filepath, ima->name, FILE_MAX);
+ BLI_strncpy(imapf->filepath, ima->filepath, FILE_MAX);
ima->packedfile = NULL;
}
}
diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c
index 79d14f28f9c..c13331b9346 100644
--- a/source/blender/blenloader/intern/versioning_280.c
+++ b/source/blender/blenloader/intern/versioning_280.c
@@ -1749,6 +1749,24 @@ void do_versions_after_linking_280(Main *bmain, ReportList *UNUSED(reports))
*/
{
/* Keep this block, even when empty. */
+ /* Paint Brush. This ensure that the brush paints by default. Used during the development and
+ * patch review of the initial Sculpt Vertex Colors implementation (D5975) */
+ LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) {
+ if (brush->ob_mode & OB_MODE_SCULPT && brush->sculpt_tool == SCULPT_TOOL_PAINT) {
+ brush->tip_roundness = 1.0f;
+ brush->flow = 1.0f;
+ brush->density = 1.0f;
+ brush->tip_scale_x = 1.0f;
+ }
+ }
+
+ /* Pose Brush with support for loose parts. */
+ LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) {
+ if (brush->sculpt_tool == SCULPT_TOOL_POSE && brush->disconnected_distance_max == 0.0f) {
+ brush->flag2 |= BRUSH_USE_CONNECTED_ONLY;
+ brush->disconnected_distance_max = 0.1f;
+ }
+ }
}
}
@@ -3475,7 +3493,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
for (Mesh *me = bmain->meshes.first; me; me = me->id.next) {
me->flag &= ~(ME_FLAG_UNUSED_0 | ME_FLAG_UNUSED_1 | ME_FLAG_UNUSED_3 | ME_FLAG_UNUSED_4 |
- ME_FLAG_UNUSED_6 | ME_FLAG_UNUSED_7 | ME_FLAG_UNUSED_8);
+ ME_FLAG_UNUSED_6 | ME_FLAG_UNUSED_7 | ME_REMESH_REPROJECT_VERTEX_COLORS);
}
for (Material *mat = bmain->materials.first; mat; mat = mat->id.next) {
@@ -5076,6 +5094,23 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
* \note Keep this message at the bottom of the function.
*/
{
+ /* Set the cloth wind factor to 1 for old forces. */
+ if (!DNA_struct_elem_find(fd->filesdna, "PartDeflect", "float", "f_wind_factor")) {
+ LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
+ if (ob->pd) {
+ ob->pd->f_wind_factor = 1.0f;
+ }
+ }
+ LISTBASE_FOREACH (ParticleSettings *, part, &bmain->particles) {
+ if (part->pd) {
+ part->pd->f_wind_factor = 1.0f;
+ }
+ if (part->pd2) {
+ part->pd2->f_wind_factor = 1.0f;
+ }
+ }
+ }
+
/* Keep this block, even when empty. */
}
diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c
index c5628b43960..2e93df09e1e 100644
--- a/source/blender/blenloader/intern/versioning_290.c
+++ b/source/blender/blenloader/intern/versioning_290.c
@@ -24,11 +24,13 @@
#include "BLI_utildefines.h"
#include "DNA_brush_types.h"
+#include "DNA_constraint_types.h"
#include "DNA_genfile.h"
#include "DNA_gpencil_modifier_types.h"
#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
#include "DNA_screen_types.h"
+#include "DNA_shader_fx_types.h"
#include "BKE_collection.h"
#include "BKE_colortools.h"
@@ -267,6 +269,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
{
/* Keep this block, even when empty. */
+ /* Transition to saving expansion for all of a modifier's sub-panels. */
if (!DNA_struct_elem_find(fd->filesdna, "ModifierData", "short", "ui_expand_flag")) {
for (Object *object = bmain->objects.first; object != NULL; object = object->id.next) {
LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
@@ -279,5 +282,75 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
}
+
+ /* EEVEE Motion blur new parameters. */
+ if (!DNA_struct_elem_find(fd->filesdna, "SceneEEVEE", "float", "motion_blur_depth_scale")) {
+ LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
+ scene->eevee.motion_blur_depth_scale = 100.0f;
+ scene->eevee.motion_blur_max = 32;
+ }
+ }
+
+ if (!DNA_struct_elem_find(fd->filesdna, "SceneEEVEE", "int", "motion_blur_steps")) {
+ LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
+ scene->eevee.motion_blur_steps = 1;
+ }
+ }
+
+ /* Transition to saving expansion for all of a constraint's sub-panels. */
+ if (!DNA_struct_elem_find(fd->filesdna, "bConstraint", "short", "ui_expand_flag")) {
+ for (Object *object = bmain->objects.first; object != NULL; object = object->id.next) {
+ LISTBASE_FOREACH (bConstraint *, con, &object->constraints) {
+ if (con->flag & CONSTRAINT_EXPAND_DEPRECATED) {
+ con->ui_expand_flag = 1;
+ }
+ else {
+ con->ui_expand_flag = 0;
+ }
+ }
+ }
+ }
+
+ /* Transition to saving expansion for all of grease pencil modifier's sub-panels. */
+ if (!DNA_struct_elem_find(fd->filesdna, "GpencilModifierData", "short", "ui_expand_flag")) {
+ for (Object *object = bmain->objects.first; object != NULL; object = object->id.next) {
+ LISTBASE_FOREACH (GpencilModifierData *, md, &object->greasepencil_modifiers) {
+ if (md->mode & eGpencilModifierMode_Expanded_DEPRECATED) {
+ md->ui_expand_flag = 1;
+ }
+ else {
+ md->ui_expand_flag = 0;
+ }
+ }
+ }
+ }
+
+ /* Transition to saving expansion for all of an effect's sub-panels. */
+ if (!DNA_struct_elem_find(fd->filesdna, "ShaderFxData", "short", "ui_expand_flag")) {
+ for (Object *object = bmain->objects.first; object != NULL; object = object->id.next) {
+ LISTBASE_FOREACH (ShaderFxData *, fx, &object->shader_fx) {
+ if (fx->mode & eShaderFxMode_Expanded_DEPRECATED) {
+ fx->ui_expand_flag = 1;
+ }
+ else {
+ fx->ui_expand_flag = 0;
+ }
+ }
+ }
+ }
+ }
+
+ /* Refactor bevel profile type to use an enum. */
+ if (!DNA_struct_elem_find(fd->filesdna, "BevelModifierData", "short", "profile_type")) {
+ for (Object *object = bmain->objects.first; object != NULL; object = object->id.next) {
+ LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
+ if (md->type == eModifierType_Bevel) {
+ BevelModifierData *bmd = (BevelModifierData *)md;
+ bool use_custom_profile = bmd->flags & MOD_BEVEL_CUSTOM_PROFILE_DEPRECATED;
+ bmd->profile_type = use_custom_profile ? MOD_BEVEL_PROFILE_CUSTOM :
+ MOD_BEVEL_PROFILE_SUPERELLIPSE;
+ }
+ }
+ }
}
}
diff --git a/source/blender/blenloader/intern/versioning_cycles.c b/source/blender/blenloader/intern/versioning_cycles.c
index ff3d4574561..46faddf6e5a 100644
--- a/source/blender/blenloader/intern/versioning_cycles.c
+++ b/source/blender/blenloader/intern/versioning_cycles.c
@@ -78,22 +78,45 @@ static IDProperty *cycles_properties_from_ID(ID *id)
return (idprop) ? IDP_GetPropertyTypeFromGroup(idprop, "cycles", IDP_GROUP) : NULL;
}
+static IDProperty *cycles_properties_from_view_layer(ViewLayer *view_layer)
+{
+ IDProperty *idprop = view_layer->id_properties;
+ return (idprop) ? IDP_GetPropertyTypeFromGroup(idprop, "cycles", IDP_GROUP) : NULL;
+}
+
static float cycles_property_float(IDProperty *idprop, const char *name, float default_value)
{
IDProperty *prop = IDP_GetPropertyTypeFromGroup(idprop, name, IDP_FLOAT);
return (prop) ? IDP_Float(prop) : default_value;
}
-static float cycles_property_int(IDProperty *idprop, const char *name, int default_value)
+static int cycles_property_int(IDProperty *idprop, const char *name, int default_value)
{
IDProperty *prop = IDP_GetPropertyTypeFromGroup(idprop, name, IDP_INT);
return (prop) ? IDP_Int(prop) : default_value;
}
-static bool cycles_property_boolean(IDProperty *idprop, const char *name, bool default_value)
+static void cycles_property_int_set(IDProperty *idprop, const char *name, int value)
{
IDProperty *prop = IDP_GetPropertyTypeFromGroup(idprop, name, IDP_INT);
- return (prop) ? IDP_Int(prop) : default_value;
+ if (prop) {
+ IDP_Int(prop) = value;
+ }
+ else {
+ IDPropertyTemplate val = {0};
+ val.i = value;
+ IDP_AddToGroup(idprop, IDP_New(IDP_INT, &val, name));
+ }
+}
+
+static bool cycles_property_boolean(IDProperty *idprop, const char *name, bool default_value)
+{
+ return cycles_property_int(idprop, name, default_value);
+}
+
+static void cycles_property_boolean_set(IDProperty *idprop, const char *name, bool value)
+{
+ cycles_property_int_set(idprop, name, value);
}
static void displacement_node_insert(bNodeTree *ntree)
@@ -1524,4 +1547,53 @@ void do_versions_after_linking_cycles(Main *bmain)
}
FOREACH_NODETREE_END;
}
+
+ if (!MAIN_VERSION_ATLEAST(bmain, 290, 5)) {
+ /* New denoiser settings. */
+ for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) {
+ IDProperty *cscene = cycles_properties_from_ID(&scene->id);
+
+ /* Check if any view layers had (optix) denoising enabled. */
+ bool use_optix = false;
+ bool use_denoising = false;
+ for (ViewLayer *view_layer = scene->view_layers.first; view_layer;
+ view_layer = view_layer->next) {
+ IDProperty *cview_layer = cycles_properties_from_view_layer(view_layer);
+ if (cview_layer) {
+ use_denoising = use_denoising ||
+ cycles_property_boolean(cview_layer, "use_denoising", false);
+ use_optix = use_optix ||
+ cycles_property_boolean(cview_layer, "use_optix_denoising", false);
+ }
+ }
+
+ if (cscene) {
+ const int DENOISER_AUTO = 0;
+ const int DENOISER_NLM = 1;
+ const int DENOISER_OPTIX = 2;
+
+ /* Enable denoiser if it was enabled for one view layer before. */
+ cycles_property_int_set(cscene, "denoiser", (use_optix) ? DENOISER_OPTIX : DENOISER_NLM);
+ cycles_property_boolean_set(cscene, "use_denoising", use_denoising);
+
+ /* Migrate Optix denoiser to new settings. */
+ if (cycles_property_int(cscene, "preview_denoising", 0)) {
+ cycles_property_boolean_set(cscene, "use_preview_denoising", true);
+ cycles_property_int_set(cscene, "preview_denoiser", DENOISER_AUTO);
+ }
+ }
+
+ /* Enable denoising in all view layer if there was no denoising before,
+ * so that enabling the scene settings auto enables it for all view layers. */
+ if (!use_denoising) {
+ for (ViewLayer *view_layer = scene->view_layers.first; view_layer;
+ view_layer = view_layer->next) {
+ IDProperty *cview_layer = cycles_properties_from_view_layer(view_layer);
+ if (cview_layer) {
+ cycles_property_boolean_set(cview_layer, "use_denoising", true);
+ }
+ }
+ }
+ }
+ }
}
diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c
index 91d89254c90..1217b69f1b5 100644
--- a/source/blender/blenloader/intern/versioning_defaults.c
+++ b/source/blender/blenloader/intern/versioning_defaults.c
@@ -682,6 +682,22 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template)
brush->sculpt_tool = SCULPT_TOOL_SLIDE_RELAX;
}
+ brush_name = "Paint";
+ brush = BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2);
+ if (!brush) {
+ brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
+ id_us_min(&brush->id);
+ brush->sculpt_tool = SCULPT_TOOL_PAINT;
+ }
+
+ brush_name = "Smear";
+ brush = BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2);
+ if (!brush) {
+ brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
+ id_us_min(&brush->id);
+ brush->sculpt_tool = SCULPT_TOOL_SMEAR;
+ }
+
brush_name = "Simplify";
brush = BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2);
if (!brush) {
diff --git a/source/blender/blenloader/intern/versioning_legacy.c b/source/blender/blenloader/intern/versioning_legacy.c
index 2cc811e213f..ce472a97337 100644
--- a/source/blender/blenloader/intern/versioning_legacy.c
+++ b/source/blender/blenloader/intern/versioning_legacy.c
@@ -856,8 +856,8 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *bmain)
if (bmain->versionfile <= 223) {
VFont *vf;
for (vf = bmain->fonts.first; vf; vf = vf->id.next) {
- if (STREQ(vf->name + strlen(vf->name) - 6, ".Bfont")) {
- strcpy(vf->name, FO_BUILTIN_NAME);
+ if (STREQ(vf->filepath + strlen(vf->filepath) - 6, ".Bfont")) {
+ strcpy(vf->filepath, FO_BUILTIN_NAME);
}
}
}
@@ -1643,9 +1643,9 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *bmain)
if (bmain->versionfile == 241) {
Image *ima;
for (ima = bmain->images.first; ima; ima = ima->id.next) {
- if (STREQ(ima->name, "Compositor")) {
+ if (STREQ(ima->filepath, "Compositor")) {
strcpy(ima->id.name + 2, "Viewer Node");
- strcpy(ima->name, "Viewer Node");
+ strcpy(ima->filepath, "Viewer Node");
}
}
}
diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c
index 3677f6c4130..68206ed47e2 100644
--- a/source/blender/blenloader/intern/writefile.c
+++ b/source/blender/blenloader/intern/writefile.c
@@ -155,8 +155,10 @@
#include "BKE_blender_version.h"
#include "BKE_bpath.h"
#include "BKE_collection.h"
+#include "BKE_colortools.h"
#include "BKE_constraint.h"
#include "BKE_curve.h"
+#include "BKE_curveprofile.h"
#include "BKE_fcurve.h"
#include "BKE_fcurve_driver.h"
#include "BKE_global.h" // for G
@@ -974,26 +976,6 @@ static void write_animdata(BlendWriter *writer, AnimData *adt)
write_nladata(writer, &adt->nla_tracks);
}
-static void write_curvemapping_curves(BlendWriter *writer, CurveMapping *cumap)
-{
- for (int a = 0; a < CM_TOT; a++) {
- BLO_write_struct_array(writer, CurveMapPoint, cumap->cm[a].totpoint, cumap->cm[a].curve);
- }
-}
-
-static void write_curvemapping(BlendWriter *writer, CurveMapping *cumap)
-{
- BLO_write_struct(writer, CurveMapping, cumap);
-
- write_curvemapping_curves(writer, cumap);
-}
-
-static void write_CurveProfile(BlendWriter *writer, CurveProfile *profile)
-{
- BLO_write_struct(writer, CurveProfile, profile);
- BLO_write_struct_array(writer, CurveProfilePoint, profile->path_len, profile->path);
-}
-
static void write_node_socket_default_value(BlendWriter *writer, bNodeSocket *sock)
{
if (sock->default_value == NULL) {
@@ -1094,7 +1076,7 @@ static void write_nodetree_nolib(BlendWriter *writer, bNodeTree *ntree)
/* could be handlerized at some point, now only 1 exception still */
if ((ntree->type == NTREE_SHADER) &&
ELEM(node->type, SH_NODE_CURVE_VEC, SH_NODE_CURVE_RGB)) {
- write_curvemapping(writer, node->storage);
+ BKE_curvemapping_blend_write(writer, node->storage);
}
else if (ntree->type == NTREE_SHADER && (node->type == SH_NODE_SCRIPT)) {
NodeShaderScript *nss = (NodeShaderScript *)node->storage;
@@ -1108,11 +1090,11 @@ static void write_nodetree_nolib(BlendWriter *writer, bNodeTree *ntree)
CMP_NODE_CURVE_VEC,
CMP_NODE_CURVE_RGB,
CMP_NODE_HUECORRECT)) {
- write_curvemapping(writer, node->storage);
+ BKE_curvemapping_blend_write(writer, node->storage);
}
else if ((ntree->type == NTREE_TEXTURE) &&
(node->type == TEX_NODE_CURVE_RGB || node->type == TEX_NODE_CURVE_TIME)) {
- write_curvemapping(writer, node->storage);
+ BKE_curvemapping_blend_write(writer, node->storage);
}
else if ((ntree->type == NTREE_COMPOSIT) && (node->type == CMP_NODE_MOVIEDISTORTION)) {
/* pass */
@@ -1443,13 +1425,13 @@ static void write_particlesettings(BlendWriter *writer,
BLO_write_struct(writer, EffectorWeights, part->effector_weights);
if (part->clumpcurve) {
- write_curvemapping(writer, part->clumpcurve);
+ BKE_curvemapping_blend_write(writer, part->clumpcurve);
}
if (part->roughcurve) {
- write_curvemapping(writer, part->roughcurve);
+ BKE_curvemapping_blend_write(writer, part->roughcurve);
}
if (part->twistcurve) {
- write_curvemapping(writer, part->twistcurve);
+ BKE_curvemapping_blend_write(writer, part->twistcurve);
}
LISTBASE_FOREACH (ParticleDupliWeight *, dw, &part->instance_weights) {
@@ -1686,16 +1668,7 @@ static void write_modifiers(BlendWriter *writer, ListBase *modbase)
BLO_write_struct_by_name(writer, mti->structName, md);
- if (md->type == eModifierType_Hook) {
- HookModifierData *hmd = (HookModifierData *)md;
-
- if (hmd->curfalloff) {
- write_curvemapping(writer, hmd->curfalloff);
- }
-
- BLO_write_int32_array(writer, hmd->totindex, hmd->indexar);
- }
- else if (md->type == eModifierType_Cloth) {
+ if (md->type == eModifierType_Cloth) {
ClothModifierData *clmd = (ClothModifierData *)md;
BLO_write_struct(writer, ClothSimSettings, clmd->sim_parms);
@@ -1777,70 +1750,6 @@ static void write_modifiers(BlendWriter *writer, ListBase *modbase)
writestruct(wd, DATA, MFace, collmd->numfaces, collmd->mfaces);
#endif
}
- else if (md->type == eModifierType_MeshDeform) {
- MeshDeformModifierData *mmd = (MeshDeformModifierData *)md;
- int size = mmd->dyngridsize;
-
- BLO_write_struct_array(writer, MDefInfluence, mmd->totinfluence, mmd->bindinfluences);
- BLO_write_int32_array(writer, mmd->totvert + 1, mmd->bindoffsets);
- BLO_write_float3_array(writer, mmd->totcagevert, mmd->bindcagecos);
- BLO_write_struct_array(writer, MDefCell, size * size * size, mmd->dyngrid);
- BLO_write_struct_array(writer, MDefInfluence, mmd->totinfluence, mmd->dyninfluences);
- BLO_write_int32_array(writer, mmd->totvert, mmd->dynverts);
- }
- else if (md->type == eModifierType_Warp) {
- WarpModifierData *tmd = (WarpModifierData *)md;
- if (tmd->curfalloff) {
- write_curvemapping(writer, tmd->curfalloff);
- }
- }
- else if (md->type == eModifierType_WeightVGEdit) {
- WeightVGEditModifierData *wmd = (WeightVGEditModifierData *)md;
-
- if (wmd->cmap_curve) {
- write_curvemapping(writer, wmd->cmap_curve);
- }
- }
- else if (md->type == eModifierType_CorrectiveSmooth) {
- CorrectiveSmoothModifierData *csmd = (CorrectiveSmoothModifierData *)md;
-
- if (csmd->bind_coords) {
- BLO_write_float3_array(writer, csmd->bind_coords_num, (float *)csmd->bind_coords);
- }
- }
- else if (md->type == eModifierType_SurfaceDeform) {
- SurfaceDeformModifierData *smd = (SurfaceDeformModifierData *)md;
-
- BLO_write_struct_array(writer, SDefVert, smd->numverts, smd->verts);
-
- if (smd->verts) {
- for (int i = 0; i < smd->numverts; i++) {
- BLO_write_struct_array(writer, SDefBind, smd->verts[i].numbinds, smd->verts[i].binds);
-
- if (smd->verts[i].binds) {
- for (int j = 0; j < smd->verts[i].numbinds; j++) {
- BLO_write_uint32_array(
- writer, smd->verts[i].binds[j].numverts, smd->verts[i].binds[j].vert_inds);
-
- if (smd->verts[i].binds[j].mode == MOD_SDEF_MODE_CENTROID ||
- smd->verts[i].binds[j].mode == MOD_SDEF_MODE_LOOPTRI) {
- BLO_write_float3_array(writer, 1, smd->verts[i].binds[j].vert_weights);
- }
- else {
- BLO_write_float_array(
- writer, smd->verts[i].binds[j].numverts, smd->verts[i].binds[j].vert_weights);
- }
- }
- }
- }
- }
- }
- else if (md->type == eModifierType_Bevel) {
- BevelModifierData *bmd = (BevelModifierData *)md;
- if (bmd->custom_profile) {
- write_CurveProfile(writer, bmd->custom_profile);
- }
- }
if (mti->blendWrite != NULL) {
mti->blendWrite(writer, md);
@@ -1868,21 +1777,21 @@ static void write_gpencil_modifiers(BlendWriter *writer, ListBase *modbase)
ThickGpencilModifierData *gpmd = (ThickGpencilModifierData *)md;
if (gpmd->curve_thickness) {
- write_curvemapping(writer, gpmd->curve_thickness);
+ BKE_curvemapping_blend_write(writer, gpmd->curve_thickness);
}
}
else if (md->type == eGpencilModifierType_Noise) {
NoiseGpencilModifierData *gpmd = (NoiseGpencilModifierData *)md;
if (gpmd->curve_intensity) {
- write_curvemapping(writer, gpmd->curve_intensity);
+ BKE_curvemapping_blend_write(writer, gpmd->curve_intensity);
}
}
else if (md->type == eGpencilModifierType_Hook) {
HookGpencilModifierData *gpmd = (HookGpencilModifierData *)md;
if (gpmd->curfalloff) {
- write_curvemapping(writer, gpmd->curfalloff);
+ BKE_curvemapping_blend_write(writer, gpmd->curfalloff);
}
}
else if (md->type == eGpencilModifierType_Tint) {
@@ -1891,25 +1800,25 @@ static void write_gpencil_modifiers(BlendWriter *writer, ListBase *modbase)
BLO_write_struct(writer, ColorBand, gpmd->colorband);
}
if (gpmd->curve_intensity) {
- write_curvemapping(writer, gpmd->curve_intensity);
+ BKE_curvemapping_blend_write(writer, gpmd->curve_intensity);
}
}
else if (md->type == eGpencilModifierType_Smooth) {
SmoothGpencilModifierData *gpmd = (SmoothGpencilModifierData *)md;
if (gpmd->curve_intensity) {
- write_curvemapping(writer, gpmd->curve_intensity);
+ BKE_curvemapping_blend_write(writer, gpmd->curve_intensity);
}
}
else if (md->type == eGpencilModifierType_Color) {
ColorGpencilModifierData *gpmd = (ColorGpencilModifierData *)md;
if (gpmd->curve_intensity) {
- write_curvemapping(writer, gpmd->curve_intensity);
+ BKE_curvemapping_blend_write(writer, gpmd->curve_intensity);
}
}
else if (md->type == eGpencilModifierType_Opacity) {
OpacityGpencilModifierData *gpmd = (OpacityGpencilModifierData *)md;
if (gpmd->curve_intensity) {
- write_curvemapping(writer, gpmd->curve_intensity);
+ BKE_curvemapping_blend_write(writer, gpmd->curve_intensity);
}
}
}
@@ -2464,7 +2373,7 @@ static void write_light(BlendWriter *writer, Light *la, const void *id_address)
}
if (la->curfalloff) {
- write_curvemapping(writer, la->curfalloff);
+ BKE_curvemapping_blend_write(writer, la->curfalloff);
}
/* Node-tree is integral part of lights, no libdata. */
@@ -2521,12 +2430,12 @@ static void write_sequence_modifiers(BlendWriter *writer, ListBase *modbase)
if (smd->type == seqModifierType_Curves) {
CurvesModifierData *cmd = (CurvesModifierData *)smd;
- write_curvemapping(writer, &cmd->curve_mapping);
+ BKE_curvemapping_blend_write(writer, &cmd->curve_mapping);
}
else if (smd->type == seqModifierType_HueCorrect) {
HueCorrectModifierData *hcmd = (HueCorrectModifierData *)smd;
- write_curvemapping(writer, &hcmd->curve_mapping);
+ BKE_curvemapping_blend_write(writer, &hcmd->curve_mapping);
}
}
else {
@@ -2538,7 +2447,7 @@ static void write_sequence_modifiers(BlendWriter *writer, ListBase *modbase)
static void write_view_settings(BlendWriter *writer, ColorManagedViewSettings *view_settings)
{
if (view_settings->curve_mapping) {
- write_curvemapping(writer, view_settings->curve_mapping);
+ BKE_curvemapping_blend_write(writer, view_settings->curve_mapping);
}
}
@@ -2552,7 +2461,7 @@ static void write_view3dshading(BlendWriter *writer, View3DShading *shading)
static void write_paint(BlendWriter *writer, Paint *p)
{
if (p->cavity_curve) {
- write_curvemapping(writer, p->cavity_curve);
+ BKE_curvemapping_blend_write(writer, p->cavity_curve);
}
BLO_write_struct_array(writer, PaintToolSlot, p->tool_slots_len, p->tool_slots);
}
@@ -2669,19 +2578,19 @@ static void write_scene(BlendWriter *writer, Scene *sce, const void *id_address)
}
/* write grease-pencil custom ipo curve to file */
if (tos->gp_interpolate.custom_ipo) {
- write_curvemapping(writer, tos->gp_interpolate.custom_ipo);
+ BKE_curvemapping_blend_write(writer, tos->gp_interpolate.custom_ipo);
}
/* write grease-pencil multiframe falloff curve to file */
if (tos->gp_sculpt.cur_falloff) {
- write_curvemapping(writer, tos->gp_sculpt.cur_falloff);
+ BKE_curvemapping_blend_write(writer, tos->gp_sculpt.cur_falloff);
}
/* write grease-pencil primitive curve to file */
if (tos->gp_sculpt.cur_primitive) {
- write_curvemapping(writer, tos->gp_sculpt.cur_primitive);
+ BKE_curvemapping_blend_write(writer, tos->gp_sculpt.cur_primitive);
}
/* Write the curve profile to the file. */
if (tos->custom_bevel_profile_preset) {
- write_CurveProfile(writer, tos->custom_bevel_profile_preset);
+ BKE_curveprofile_blend_write(writer, tos->custom_bevel_profile_preset);
}
write_paint(writer, &tos->imapaint.paint);
@@ -2823,7 +2732,7 @@ static void write_scene(BlendWriter *writer, Scene *sce, const void *id_address)
}
write_previews(writer, sce->preview);
- write_curvemapping_curves(writer, &sce->r.mblur_shutter_curve);
+ BKE_curvemapping_curves_blend_write(writer, &sce->r.mblur_shutter_curve);
LISTBASE_FOREACH (ViewLayer *, view_layer, &sce->view_layers) {
write_view_layer(writer, view_layer);
@@ -3237,8 +3146,8 @@ static void write_text(BlendWriter *writer, Text *text, const void *id_address)
BLO_write_id_struct(writer, Text, id_address, &text->id);
write_iddata(writer, &text->id);
- if (text->name) {
- BLO_write_string(writer, text->name);
+ if (text->filepath) {
+ BLO_write_string(writer, text->filepath);
}
if (!(text->flags & TXT_ISEXT)) {
@@ -3327,38 +3236,38 @@ static void write_brush(BlendWriter *writer, Brush *brush, const void *id_addres
write_iddata(writer, &brush->id);
if (brush->curve) {
- write_curvemapping(writer, brush->curve);
+ BKE_curvemapping_blend_write(writer, brush->curve);
}
if (brush->gpencil_settings) {
BLO_write_struct(writer, BrushGpencilSettings, brush->gpencil_settings);
if (brush->gpencil_settings->curve_sensitivity) {
- write_curvemapping(writer, brush->gpencil_settings->curve_sensitivity);
+ BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_sensitivity);
}
if (brush->gpencil_settings->curve_strength) {
- write_curvemapping(writer, brush->gpencil_settings->curve_strength);
+ BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_strength);
}
if (brush->gpencil_settings->curve_jitter) {
- write_curvemapping(writer, brush->gpencil_settings->curve_jitter);
+ BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_jitter);
}
if (brush->gpencil_settings->curve_rand_pressure) {
- write_curvemapping(writer, brush->gpencil_settings->curve_rand_pressure);
+ BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_pressure);
}
if (brush->gpencil_settings->curve_rand_strength) {
- write_curvemapping(writer, brush->gpencil_settings->curve_rand_strength);
+ BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_strength);
}
if (brush->gpencil_settings->curve_rand_uv) {
- write_curvemapping(writer, brush->gpencil_settings->curve_rand_uv);
+ BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_uv);
}
if (brush->gpencil_settings->curve_rand_hue) {
- write_curvemapping(writer, brush->gpencil_settings->curve_rand_hue);
+ BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_hue);
}
if (brush->gpencil_settings->curve_rand_saturation) {
- write_curvemapping(writer, brush->gpencil_settings->curve_rand_saturation);
+ BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_saturation);
}
if (brush->gpencil_settings->curve_rand_value) {
- write_curvemapping(writer, brush->gpencil_settings->curve_rand_value);
+ BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_value);
}
}
if (brush->gradient) {
@@ -3619,28 +3528,30 @@ static void write_linestyle_alpha_modifiers(BlendWriter *writer, ListBase *modif
for (m = modifiers->first; m; m = m->next) {
switch (m->type) {
case LS_MODIFIER_ALONG_STROKE:
- write_curvemapping(writer, ((LineStyleAlphaModifier_AlongStroke *)m)->curve);
+ BKE_curvemapping_blend_write(writer, ((LineStyleAlphaModifier_AlongStroke *)m)->curve);
break;
case LS_MODIFIER_DISTANCE_FROM_CAMERA:
- write_curvemapping(writer, ((LineStyleAlphaModifier_DistanceFromCamera *)m)->curve);
+ BKE_curvemapping_blend_write(writer,
+ ((LineStyleAlphaModifier_DistanceFromCamera *)m)->curve);
break;
case LS_MODIFIER_DISTANCE_FROM_OBJECT:
- write_curvemapping(writer, ((LineStyleAlphaModifier_DistanceFromObject *)m)->curve);
+ BKE_curvemapping_blend_write(writer,
+ ((LineStyleAlphaModifier_DistanceFromObject *)m)->curve);
break;
case LS_MODIFIER_MATERIAL:
- write_curvemapping(writer, ((LineStyleAlphaModifier_Material *)m)->curve);
+ BKE_curvemapping_blend_write(writer, ((LineStyleAlphaModifier_Material *)m)->curve);
break;
case LS_MODIFIER_TANGENT:
- write_curvemapping(writer, ((LineStyleAlphaModifier_Tangent *)m)->curve);
+ BKE_curvemapping_blend_write(writer, ((LineStyleAlphaModifier_Tangent *)m)->curve);
break;
case LS_MODIFIER_NOISE:
- write_curvemapping(writer, ((LineStyleAlphaModifier_Noise *)m)->curve);
+ BKE_curvemapping_blend_write(writer, ((LineStyleAlphaModifier_Noise *)m)->curve);
break;
case LS_MODIFIER_CREASE_ANGLE:
- write_curvemapping(writer, ((LineStyleAlphaModifier_CreaseAngle *)m)->curve);
+ BKE_curvemapping_blend_write(writer, ((LineStyleAlphaModifier_CreaseAngle *)m)->curve);
break;
case LS_MODIFIER_CURVATURE_3D:
- write_curvemapping(writer, ((LineStyleAlphaModifier_Curvature_3D *)m)->curve);
+ BKE_curvemapping_blend_write(writer, ((LineStyleAlphaModifier_Curvature_3D *)m)->curve);
break;
}
}
@@ -3688,25 +3599,28 @@ static void write_linestyle_thickness_modifiers(BlendWriter *writer, ListBase *m
for (m = modifiers->first; m; m = m->next) {
switch (m->type) {
case LS_MODIFIER_ALONG_STROKE:
- write_curvemapping(writer, ((LineStyleThicknessModifier_AlongStroke *)m)->curve);
+ BKE_curvemapping_blend_write(writer, ((LineStyleThicknessModifier_AlongStroke *)m)->curve);
break;
case LS_MODIFIER_DISTANCE_FROM_CAMERA:
- write_curvemapping(writer, ((LineStyleThicknessModifier_DistanceFromCamera *)m)->curve);
+ BKE_curvemapping_blend_write(writer,
+ ((LineStyleThicknessModifier_DistanceFromCamera *)m)->curve);
break;
case LS_MODIFIER_DISTANCE_FROM_OBJECT:
- write_curvemapping(writer, ((LineStyleThicknessModifier_DistanceFromObject *)m)->curve);
+ BKE_curvemapping_blend_write(writer,
+ ((LineStyleThicknessModifier_DistanceFromObject *)m)->curve);
break;
case LS_MODIFIER_MATERIAL:
- write_curvemapping(writer, ((LineStyleThicknessModifier_Material *)m)->curve);
+ BKE_curvemapping_blend_write(writer, ((LineStyleThicknessModifier_Material *)m)->curve);
break;
case LS_MODIFIER_TANGENT:
- write_curvemapping(writer, ((LineStyleThicknessModifier_Tangent *)m)->curve);
+ BKE_curvemapping_blend_write(writer, ((LineStyleThicknessModifier_Tangent *)m)->curve);
break;
case LS_MODIFIER_CREASE_ANGLE:
- write_curvemapping(writer, ((LineStyleThicknessModifier_CreaseAngle *)m)->curve);
+ BKE_curvemapping_blend_write(writer, ((LineStyleThicknessModifier_CreaseAngle *)m)->curve);
break;
case LS_MODIFIER_CURVATURE_3D:
- write_curvemapping(writer, ((LineStyleThicknessModifier_Curvature_3D *)m)->curve);
+ BKE_curvemapping_blend_write(writer,
+ ((LineStyleThicknessModifier_Curvature_3D *)m)->curve);
break;
}
}
@@ -4005,7 +3919,7 @@ static void write_libraries(WriteData *wd, Main *main)
writestruct(wd, DATA, PackedFile, 1, pf);
writedata(wd, DATA, pf->size, pf->data);
if (wd->use_memfile == false) {
- printf("write packed .blend: %s\n", main->curlib->name);
+ printf("write packed .blend: %s\n", main->curlib->filepath);
}
}
if (main->curlib->asset_repository) {
@@ -4024,7 +3938,7 @@ static void write_libraries(WriteData *wd, Main *main)
"ERROR: write file: data-block '%s' from lib '%s' is not linkable "
"but is flagged as directly linked",
id->name,
- main->curlib->filepath);
+ main->curlib->filepath_abs);
BLI_assert(0);
}
}
@@ -4042,7 +3956,7 @@ static void write_libraries(WriteData *wd, Main *main)
"ERROR: write file: data-block '%s' from lib '%s' is not linkable "
"but is flagged as directly linked",
id->name,
- main->curlib->filepath);
+ main->curlib->filepath_abs);
BLI_assert(0);
}
writestruct(wd, ID_LINK_PLACEHOLDER, ID, 1, id);
@@ -4136,6 +4050,7 @@ static bool write_file_handle(Main *mainvar,
MemFile *compare,
MemFile *current,
int write_flags,
+ bool use_userdef,
const BlendThumbnail *thumb)
{
BHead bhead;
@@ -4196,7 +4111,8 @@ static bool write_file_handle(Main *mainvar,
BLI_assert(
(id->tag & (LIB_TAG_NO_MAIN | LIB_TAG_NO_USER_REFCOUNT | LIB_TAG_NOT_ALLOCATED)) == 0);
- const bool do_override = !ELEM(override_storage, NULL, bmain) && id->override_library;
+ const bool do_override = !ELEM(override_storage, NULL, bmain) &&
+ ID_IS_OVERRIDE_LIBRARY_REAL(id);
if (do_override) {
BKE_lib_override_library_operations_store_start(bmain, override_storage, id);
@@ -4389,7 +4305,7 @@ static bool write_file_handle(Main *mainvar,
/* So changes above don't cause a 'DNA1' to be detected as changed on undo. */
mywrite_flush(wd);
- if (write_flags & G_FILE_USERPREFS) {
+ if (use_userdef) {
write_userdef(&writer, &U);
}
@@ -4462,14 +4378,20 @@ static bool do_history(const char *name, ReportList *reports)
*/
bool BLO_write_file(Main *mainvar,
const char *filepath,
- int write_flags,
- ReportList *reports,
- const BlendThumbnail *thumb)
+ const int write_flags,
+ const struct BlendFileWriteParams *params,
+ ReportList *reports)
{
char tempname[FILE_MAX + 1];
eWriteWrapType ww_type;
WriteWrap ww;
+ eBLO_WritePathRemap remap_mode = params->remap_mode;
+ const bool use_save_versions = params->use_save_versions;
+ const bool use_save_as_copy = params->use_save_as_copy;
+ const bool use_userdef = params->use_userdef;
+ const BlendThumbnail *thumb = params->thumb;
+
/* path backup/restore */
void *path_list_backup = NULL;
const int path_list_flag = (BKE_BPATH_TRAVERSE_SKIP_LIBRARY | BKE_BPATH_TRAVERSE_SKIP_MULTIFILE);
@@ -4499,7 +4421,15 @@ bool BLO_write_file(Main *mainvar,
}
/* Remapping of relative paths to new file location. */
- if (write_flags & G_FILE_RELATIVE_REMAP) {
+ if (remap_mode != BLO_WRITE_PATH_REMAP_NONE) {
+
+ if (remap_mode == BLO_WRITE_PATH_REMAP_RELATIVE) {
+ /* Make all relative as none of the existing paths can be relative in an unsaved document. */
+ if (G.relbase_valid == false) {
+ remap_mode = BLO_WRITE_PATH_REMAP_RELATIVE_ALL;
+ }
+ }
+
char dir_src[FILE_MAX];
char dir_dst[FILE_MAX];
BLI_split_dir_part(mainvar->name, dir_src, sizeof(dir_src));
@@ -4509,29 +4439,49 @@ bool BLO_write_file(Main *mainvar,
BLI_path_normalize(mainvar->name, dir_dst);
BLI_path_normalize(mainvar->name, dir_src);
- if (G.relbase_valid && (BLI_path_cmp(dir_dst, dir_src) == 0)) {
- /* Saved to same path. Nothing to do. */
- write_flags &= ~G_FILE_RELATIVE_REMAP;
+ /* Only for relative, not relative-all, as this means making existing paths relative. */
+ if (remap_mode == BLO_WRITE_PATH_REMAP_RELATIVE) {
+ if (G.relbase_valid && (BLI_path_cmp(dir_dst, dir_src) == 0)) {
+ /* Saved to same path. Nothing to do. */
+ remap_mode = BLO_WRITE_PATH_REMAP_NONE;
+ }
}
- else {
+ else if (remap_mode == BLO_WRITE_PATH_REMAP_ABSOLUTE) {
+ if (G.relbase_valid == false) {
+ /* Unsaved, all paths are absolute.Even if the user manages to set a relative path,
+ * there is no base-path that can be used to make it absolute. */
+ remap_mode = BLO_WRITE_PATH_REMAP_NONE;
+ }
+ }
+
+ if (remap_mode != BLO_WRITE_PATH_REMAP_NONE) {
/* Check if we need to backup and restore paths. */
- if (UNLIKELY(G_FILE_SAVE_COPY & write_flags)) {
+ if (UNLIKELY(use_save_as_copy)) {
path_list_backup = BKE_bpath_list_backup(mainvar, path_list_flag);
}
- if (G.relbase_valid) {
- /* Saved, make relative paths relative to new location (if possible). */
- BKE_bpath_relative_rebase(mainvar, dir_src, dir_dst, NULL);
- }
- else {
- /* Unsaved, make all relative. */
- BKE_bpath_relative_convert(mainvar, dir_dst, NULL);
+ switch (remap_mode) {
+ case BLO_WRITE_PATH_REMAP_RELATIVE:
+ /* Saved, make relative paths relative to new location (if possible). */
+ BKE_bpath_relative_rebase(mainvar, dir_src, dir_dst, NULL);
+ break;
+ case BLO_WRITE_PATH_REMAP_RELATIVE_ALL:
+ /* Make all relative (when requested or unsaved). */
+ BKE_bpath_relative_convert(mainvar, dir_dst, NULL);
+ break;
+ case BLO_WRITE_PATH_REMAP_ABSOLUTE:
+ /* Make all absolute (when requested or unsaved). */
+ BKE_bpath_absolute_convert(mainvar, dir_src, NULL);
+ break;
+ case BLO_WRITE_PATH_REMAP_NONE:
+ BLI_assert(0); /* Unreachable. */
+ break;
}
}
}
/* actual file writing */
- const bool err = write_file_handle(mainvar, &ww, NULL, NULL, write_flags, thumb);
+ const bool err = write_file_handle(mainvar, &ww, NULL, NULL, write_flags, use_userdef, thumb);
ww.close(&ww);
@@ -4549,7 +4499,7 @@ bool BLO_write_file(Main *mainvar,
/* file save to temporary file was successful */
/* now do reverse file history (move .blend1 -> .blend2, .blend -> .blend1) */
- if (write_flags & G_FILE_HISTORY) {
+ if (use_save_versions) {
const bool err_hist = do_history(filepath, reports);
if (err_hist) {
BKE_report(reports, RPT_ERROR, "Version backup failed (file saved with @)");
@@ -4575,9 +4525,10 @@ bool BLO_write_file(Main *mainvar,
*/
bool BLO_write_file_mem(Main *mainvar, MemFile *compare, MemFile *current, int write_flags)
{
- write_flags &= ~G_FILE_USERPREFS;
+ bool use_userdef = false;
- const bool err = write_file_handle(mainvar, NULL, compare, current, write_flags, NULL);
+ const bool err = write_file_handle(
+ mainvar, NULL, compare, current, write_flags, use_userdef, NULL);
return (err == 0);
}
diff --git a/source/blender/bmesh/intern/bmesh_interp.c b/source/blender/bmesh/intern/bmesh_interp.c
index 27c03f0a84f..82502227a9a 100644
--- a/source/blender/bmesh/intern/bmesh_interp.c
+++ b/source/blender/bmesh/intern/bmesh_interp.c
@@ -744,9 +744,21 @@ void BM_loop_interp_from_face(
float co[2];
int i;
- /* convert the 3d coords into 2d for projection */
- BLI_assert(BM_face_is_normal_valid(f_src));
- axis_dominant_v3_to_m3(axis_mat, f_src->no);
+ /* Convert the 3d coords into 2d for projection. */
+ float axis_dominant[3];
+ if (!is_zero_v3(f_src->no)) {
+ BLI_assert(BM_face_is_normal_valid(f_src));
+ copy_v3_v3(axis_dominant, f_src->no);
+ }
+ else {
+ /* Rare case in which all the vertices of the face are aligned.
+ * Get a random axis that is orthogonal to the tangent. */
+ float vec[3];
+ BM_face_calc_tangent_auto(f_src, vec);
+ ortho_v3_v3(axis_dominant, vec);
+ normalize_v3(axis_dominant);
+ }
+ axis_dominant_v3_to_m3(axis_mat, axis_dominant);
i = 0;
l_iter = l_first = BM_FACE_FIRST_LOOP(f_src);
diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.c b/source/blender/bmesh/intern/bmesh_mesh_convert.c
index b8508f7e12c..65bc4da49bc 100644
--- a/source/blender/bmesh/intern/bmesh_mesh_convert.c
+++ b/source/blender/bmesh/intern/bmesh_mesh_convert.c
@@ -925,7 +925,7 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
else if ((oldkey != NULL) && (cd_shape_keyindex_offset != -1) &&
((keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset)) != ORIGINDEX_NONE) &&
(keyi < currkey->totelem)) {
- /* Old method of reconstructing keys via vertice's original key indices,
+ /* Old method of reconstructing keys via vertices original key indices,
* currently used if the new method above fails
* (which is theoretically possible in certain cases of undo). */
copy_v3_v3(fp, oldkey[keyi]);
@@ -938,9 +938,9 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
/* Propagate edited basis offsets to other shapes. */
if (apply_offset) {
add_v3_v3(fp, *ofs_pt++);
- /* Apply back new coordinates of offsetted shape-keys into BMesh.
- * Otherwise, in case we call again BM_mesh_bm_to_me on same BMesh,
- * we'll apply diff from previous call to BM_mesh_bm_to_me,
+ /* Apply back new coordinates shape-keys that have offset into BMesh.
+ * Otherwise, in case we call again #BM_mesh_bm_to_me on same BMesh,
+ * we'll apply diff from previous call to #BM_mesh_bm_to_me,
* to shape-key values from *original creation of the BMesh*. See T50524. */
copy_v3_v3(BM_ELEM_CD_GET_VOID_P(eve, cd_shape_offset), fp);
}
@@ -976,7 +976,7 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
MEM_freeN(oldverts);
}
- /* Topology could be changed, ensure mdisps are ok. */
+ /* Topology could be changed, ensure #CD_MDISPS are ok. */
multires_topology_changed(me);
/* To be removed as soon as COW is enabled by default.. */
diff --git a/source/blender/bmesh/intern/bmesh_opdefines.c b/source/blender/bmesh/intern/bmesh_opdefines.c
index 04cdc0020d9..67c0fdba12b 100644
--- a/source/blender/bmesh/intern/bmesh_opdefines.c
+++ b/source/blender/bmesh/intern/bmesh_opdefines.c
@@ -1724,6 +1724,13 @@ static BMO_FlagSet bmo_enum_bevel_offset_type[] = {
{BEVEL_AMT_WIDTH, "WIDTH"},
{BEVEL_AMT_DEPTH, "DEPTH"},
{BEVEL_AMT_PERCENT, "PERCENT"},
+ {BEVEL_AMT_ABSOLUTE, "ABSOLUTE"},
+ {0, NULL},
+};
+
+static BMO_FlagSet bmo_enum_bevel_profile_type[] = {
+ {BEVEL_PROFILE_SUPERELLIPSE, "SUPERELLIPSE"},
+ {BEVEL_PROFILE_CUSTOM, "CUSTOM"},
{0, NULL},
};
@@ -1759,7 +1766,9 @@ static BMOpDefine bmo_bevel_def = {
{{"geom", BMO_OP_SLOT_ELEMENT_BUF, {BM_VERT | BM_EDGE | BM_FACE}}, /* input edges and vertices */
{"offset", BMO_OP_SLOT_FLT}, /* amount to offset beveled edge */
{"offset_type", BMO_OP_SLOT_INT, {(int)BMO_OP_SLOT_SUBTYPE_INT_ENUM},
- bmo_enum_bevel_offset_type}, /* how to measure the offset */
+ bmo_enum_bevel_offset_type}, /* how to measure the offset */
+ {"profile_type", BMO_OP_SLOT_INT, {(int)BMO_OP_SLOT_SUBTYPE_INT_ENUM},
+ bmo_enum_bevel_profile_type}, /* The profile type to use for bevel. */
{"segments", BMO_OP_SLOT_INT}, /* number of segments in bevel */
{"profile", BMO_OP_SLOT_FLT}, /* profile shape, 0->1 (.5=>round) */
{"vertex_only", BMO_OP_SLOT_BOOL}, /* only bevel vertices, not edges */
@@ -1777,9 +1786,7 @@ static BMOpDefine bmo_bevel_def = {
bmo_enum_bevel_miter_type}, /* outer miter kind */
{"spread", BMO_OP_SLOT_FLT}, /* amount to offset beveled edge */
{"smoothresh", BMO_OP_SLOT_FLT}, /* for passing mesh's smoothresh, used in hardening */
- {"use_custom_profile", BMO_OP_SLOT_BOOL}, /* Whether to use custom profile feature */
- /* the ProfileWiget struct for the custom profile shape */
- {"custom_profile", BMO_OP_SLOT_PTR, {(int)BMO_OP_SLOT_SUBTYPE_PTR_STRUCT}},
+ {"custom_profile", BMO_OP_SLOT_PTR, {(int)BMO_OP_SLOT_SUBTYPE_PTR_STRUCT}}, /* CurveProfile */
{"vmesh_method", BMO_OP_SLOT_INT, {(int)BMO_OP_SLOT_SUBTYPE_INT_ENUM},
bmo_enum_bevel_vmesh_method},
{{'\0'}},
diff --git a/source/blender/bmesh/intern/bmesh_operators.h b/source/blender/bmesh/intern/bmesh_operators.h
index 9f0107db693..29fcf7ca0ca 100644
--- a/source/blender/bmesh/intern/bmesh_operators.h
+++ b/source/blender/bmesh/intern/bmesh_operators.h
@@ -109,9 +109,16 @@ enum {
BEVEL_AMT_WIDTH,
BEVEL_AMT_DEPTH,
BEVEL_AMT_PERCENT,
+ BEVEL_AMT_ABSOLUTE,
};
-/* Bevel face_strength_mode values: should match face_str mode enum in DNA_modifer_types.h */
+/* Bevel profile type */
+enum {
+ BEVEL_PROFILE_SUPERELLIPSE,
+ BEVEL_PROFILE_CUSTOM,
+};
+
+/* Bevel face_strength_mode values: should match face_str mode enum in DNA_modifier_types.h */
enum {
BEVEL_FACE_STRENGTH_NONE,
BEVEL_FACE_STRENGTH_NEW,
diff --git a/source/blender/bmesh/intern/bmesh_polygon_edgenet.c b/source/blender/bmesh/intern/bmesh_polygon_edgenet.c
index 284f5265b90..a738b2139c4 100644
--- a/source/blender/bmesh/intern/bmesh_polygon_edgenet.c
+++ b/source/blender/bmesh/intern/bmesh_polygon_edgenet.c
@@ -1242,7 +1242,8 @@ bool BM_face_split_edgenet_connect_islands(BMesh *bm,
uint *r_edge_net_new_len)
{
/* -------------------------------------------------------------------- */
- /* This function has 2 main parts.
+ /**
+ * This function has 2 main parts.
*
* - Check if there are any holes.
* - Connect the holes with edges (if any are found).
diff --git a/source/blender/bmesh/operators/bmo_bevel.c b/source/blender/bmesh/operators/bmo_bevel.c
index 18193beff58..67f875ac262 100644
--- a/source/blender/bmesh/operators/bmo_bevel.c
+++ b/source/blender/bmesh/operators/bmo_bevel.c
@@ -33,6 +33,7 @@ void bmo_bevel_exec(BMesh *bm, BMOperator *op)
{
const float offset = BMO_slot_float_get(op->slots_in, "offset");
const int offset_type = BMO_slot_int_get(op->slots_in, "offset_type");
+ const int profile_type = BMO_slot_int_get(op->slots_in, "profile_type");
const int seg = BMO_slot_int_get(op->slots_in, "segments");
const bool vonly = BMO_slot_bool_get(op->slots_in, "vertex_only");
const float profile = BMO_slot_float_get(op->slots_in, "profile");
@@ -47,7 +48,6 @@ void bmo_bevel_exec(BMesh *bm, BMOperator *op)
const int miter_inner = BMO_slot_int_get(op->slots_in, "miter_inner");
const float spread = BMO_slot_float_get(op->slots_in, "spread");
const float smoothresh = BMO_slot_float_get(op->slots_in, "smoothresh");
- const bool use_custom_profile = BMO_slot_bool_get(op->slots_in, "use_custom_profile");
const CurveProfile *custom_profile = BMO_slot_ptr_get(op->slots_in, "custom_profile");
const int vmesh_method = BMO_slot_int_get(op->slots_in, "vmesh_method");
@@ -76,6 +76,7 @@ void bmo_bevel_exec(BMesh *bm, BMOperator *op)
BM_mesh_bevel(bm,
offset,
offset_type,
+ profile_type,
seg,
profile,
vonly,
@@ -93,7 +94,6 @@ void bmo_bevel_exec(BMesh *bm, BMOperator *op)
miter_inner,
spread,
smoothresh,
- use_custom_profile,
custom_profile,
vmesh_method);
diff --git a/source/blender/bmesh/operators/bmo_connect.c b/source/blender/bmesh/operators/bmo_connect.c
index 9969d31a4a5..b52c26a65f0 100644
--- a/source/blender/bmesh/operators/bmo_connect.c
+++ b/source/blender/bmesh/operators/bmo_connect.c
@@ -50,6 +50,7 @@ static int bm_face_connect_verts(BMesh *bm, BMFace *f, const bool check_degenera
BMLoop *l_tag_prev = NULL, *l_tag_first = NULL;
BMLoop *l_iter, *l_first;
uint i;
+ int result = 1;
STACK_INIT(loops_split, pair_split_max);
STACK_INIT(verts_pair, pair_split_max);
@@ -109,30 +110,60 @@ static int bm_face_connect_verts(BMesh *bm, BMFace *f, const bool check_degenera
v_pair[1] = loops_split[i][1]->v;
}
+ /* Clear and re-use to store duplicate faces, to remove after splitting is finished. */
+ STACK_CLEAR(loops_split);
+
for (i = 0; i < STACK_SIZE(verts_pair); i++) {
BMFace *f_new;
BMLoop *l_new;
- BMLoop *l_a, *l_b;
-
- if ((l_a = BM_face_vert_share_loop(f, verts_pair[i][0])) &&
- (l_b = BM_face_vert_share_loop(f, verts_pair[i][1]))) {
- f_new = BM_face_split(bm, f, l_a, l_b, &l_new, NULL, false);
+ BMLoop *l_pair[2];
+
+ /* Note that duplicate edges in this case is very unlikely but it can happen, see T70287. */
+ bool edge_exists = (BM_edge_exists(verts_pair[i][0], verts_pair[i][1]) != NULL);
+ if ((l_pair[0] = BM_face_vert_share_loop(f, verts_pair[i][0])) &&
+ (l_pair[1] = BM_face_vert_share_loop(f, verts_pair[i][1]))) {
+ f_new = BM_face_split(bm, f, l_pair[0], l_pair[1], &l_new, NULL, edge_exists);
+
+ /* Check if duplicate faces have been created, store the loops for removal in this case.
+ * Note that this matches how triangulate works (newly created duplicates get removed). */
+ if (UNLIKELY(edge_exists)) {
+ BMLoop **l_pair_deferred_remove = NULL;
+ for (int j = 0; j < 2; j++) {
+ if (BM_face_find_double(l_pair[j]->f)) {
+ if (l_pair_deferred_remove == NULL) {
+ l_pair_deferred_remove = STACK_PUSH_RET(loops_split);
+ l_pair_deferred_remove[0] = NULL;
+ l_pair_deferred_remove[1] = NULL;
+ }
+ l_pair_deferred_remove[j] = l_pair[j];
+ }
+ }
+ }
}
else {
f_new = NULL;
l_new = NULL;
}
- f = f_new;
-
if (!l_new || !f_new) {
- return -1;
+ result = -1;
+ break;
}
+
+ f = f_new;
// BMO_face_flag_enable(bm, f_new, FACE_NEW);
BMO_edge_flag_enable(bm, l_new->e, EDGE_OUT);
}
- return 1;
+ for (i = 0; i < STACK_SIZE(loops_split); i++) {
+ for (int j = 0; j < 2; j++) {
+ if (loops_split[i][j] != NULL) {
+ BM_face_kill(bm, loops_split[i][j]->f);
+ }
+ }
+ }
+
+ return result;
}
void bmo_connect_verts_exec(BMesh *bm, BMOperator *op)
diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c
index 00b647555cf..f4c08f3153b 100644
--- a/source/blender/bmesh/tools/bmesh_bevel.c
+++ b/source/blender/bmesh/tools/bmesh_bevel.c
@@ -22,6 +22,7 @@
#include "MEM_guardedalloc.h"
+#include "DNA_curveprofile_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_modifier_types.h"
#include "DNA_scene_types.h"
@@ -32,15 +33,13 @@
#include "BLI_memarena.h"
#include "BLI_utildefines.h"
+#include "BKE_curveprofile.h"
#include "BKE_customdata.h"
#include "BKE_deform.h"
#include "BKE_mesh.h"
#include "eigen_capi.h"
-#include "BKE_curveprofile.h"
-#include "DNA_curveprofile_types.h"
-
#include "bmesh.h"
#include "bmesh_bevel.h" /* own include */
@@ -176,6 +175,26 @@ typedef struct ProfileSpacing {
} ProfileSpacing;
/**
+ * If the mesh has custom data Loop layers that 'have math' we use this
+ * data to help decide which face to use as representative when there
+ * is an ambiguous choice as to which face to use, which happens
+ * when there is an odd number of segments.
+ *
+ * The face_compent field of the following will only be set if there are an odd
+ * number of segments. The it uses BMFace indices to index into it, so will
+ * only be valid as long BMFaces are not added or deleted in the BMesh.
+ * "Connected Component" here means connected in UV space:
+ * i.e., one face is directly connected to another if they share an edge and
+ * all of Loop UV custom layers are contiguous across that edge.
+ */
+typedef struct MathLayerInfo {
+ /** A connected-component id for each BMFace in the mesh. */
+ int *face_component;
+ /** Does the mesh have any custom loop uv layers? */
+ bool has_math_layers;
+} MathLayerInfo;
+
+/**
* An element in a cyclic boundary of a Vertex Mesh (VMesh), placed on each side of beveled edges
* where each profile starts, or on each side of a miter.
*/
@@ -298,10 +317,14 @@ typedef struct BevelParams {
ProfileSpacing pro_spacing;
/** Parameter values for evenly spaced profile points for the miter profiles. */
ProfileSpacing pro_spacing_miter;
+ /** Information about 'math' loop layers, like UV layers. */
+ MathLayerInfo math_layer_info;
/** Blender units to offset each side of a beveled edge. */
float offset;
/** How offset is measured; enum defined in bmesh_operators.h. */
int offset_type;
+ /** Profile type: radius, superellipse, or custom */
+ int profile_type;
/** Number of segments in beveled edge profile. */
int seg;
/** User profile setting. */
@@ -324,9 +347,7 @@ typedef struct BevelParams {
bool mark_sharp;
/** Should we harden normals? */
bool harden_normals;
- /** Should we use the custom profiles feature? */
- bool use_custom_profile;
- char _pad[3];
+ char _pad[1];
/** The struct used to store the custom profile input. */
const struct CurveProfile *custom_profile;
/** Vertex group array, maybe set if vertex_only. */
@@ -734,12 +755,13 @@ static BMFace *bev_create_quad_ex(BMesh *bm,
BMEdge *e2,
BMEdge *e3,
BMEdge *e4,
+ BMFace *frep,
int mat_nr)
{
BMVert *varr[4] = {v1, v2, v3, v4};
BMFace *farr[4] = {f1, f2, f3, f4};
BMEdge *earr[4] = {e1, e2, e3, e4};
- return bev_create_ngon(bm, varr, 4, farr, f1, earr, mat_nr, true);
+ return bev_create_ngon(bm, varr, 4, farr, frep, earr, mat_nr, true);
}
/* Is Loop layer layer_index contiguous across shared vertex of l1 and l2? */
@@ -753,7 +775,8 @@ static bool contig_ldata_across_loops(BMesh *bm, BMLoop *l1, BMLoop *l2, int lay
}
/* Are all loop layers with have math (e.g., UVs)
- * contiguous from face f1 to face f2 across edge e? */
+ * contiguous from face f1 to face f2 across edge e?
+ */
static bool contig_ldata_across_edge(BMesh *bm, BMEdge *e, BMFace *f1, BMFace *f2)
{
BMLoop *lef1, *lef2;
@@ -765,41 +788,202 @@ static bool contig_ldata_across_edge(BMesh *bm, BMEdge *e, BMFace *f1, BMFace *f
return true;
}
- v1 = e->v1;
- v2 = e->v2;
if (!BM_edge_loop_pair(e, &lef1, &lef2)) {
return false;
}
+ /* If faces are oriented consistently around e,
+ * should now have lef1 and lef2 being f1 and f2 in either order.
+ */
if (lef1->f == f2) {
SWAP(BMLoop *, lef1, lef2);
}
-
- if (lef1->v == v1) {
- lv1f1 = lef1;
- lv2f1 = BM_face_other_edge_loop(f1, e, v2);
+ if (lef1->f != f1 || lef2->f != f2) {
+ return false;
}
- else {
- lv2f1 = lef1;
- lv1f1 = BM_face_other_edge_loop(f1, e, v1);
+ v1 = lef1->v;
+ v2 = lef2->v;
+ BLI_assert((v1 == e->v1 && v2 == e->v2) || (v1 == e->v2 && v2 == e->v1));
+ lv1f1 = lef1;
+ lv2f1 = lef1->next;
+ lv1f2 = lef2->next;
+ lv2f2 = lef2;
+ BLI_assert(lv1f1->v == v1 && lv1f1->f == f1 && lv2f1->v == v2 && lv2f1->f == f1 &&
+ lv1f2->v == v1 && lv1f2->f == f2 && lv2f2->v == v2 && lv2f2->f == f2);
+ for (i = 0; i < bm->ldata.totlayer; i++) {
+ if (CustomData_layer_has_math(&bm->ldata, i)) {
+ if (!contig_ldata_across_loops(bm, lv1f1, lv1f2, i) ||
+ !contig_ldata_across_loops(bm, lv2f1, lv2f2, i)) {
+ return false;
+ }
+ }
}
+ return true;
+}
+
+/*
+ * Set up the fields of bp->math_layer_info.
+ * We always set has_math_layers to the correct value.
+ * Only if there are UV layers and the number of segments is odd,
+ * we need to calculate connected face components in UV space.
+ */
+static void math_layer_info_init(BevelParams *bp, BMesh *bm)
+{
+ int i, f, stack_top, totface, current_component;
+ int bmf_index, bmf_other_index;
+ int *face_component;
+ BMFace *bmf, *bmf_other;
+ BMEdge *bme;
+ BMFace **stack;
+ BMIter eiter, fiter;
- if (lef2->v == v1) {
- lv1f2 = lef2;
- lv2f2 = BM_face_other_edge_loop(f2, e, v2);
+ bp->math_layer_info.has_math_layers = false;
+ bp->math_layer_info.face_component = NULL;
+ for (i = 0; i < bm->ldata.totlayer; i++) {
+ if (CustomData_has_layer(&bm->ldata, CD_MLOOPUV)) {
+ bp->math_layer_info.has_math_layers = true;
+ break;
+ }
}
- else {
- lv2f2 = lef2;
- lv1f2 = BM_face_other_edge_loop(f2, e, v1);
+ if (!bp->math_layer_info.has_math_layers || (bp->seg % 2) == 0) {
+ return;
}
- for (i = 0; i < bm->ldata.totlayer; i++) {
- if (CustomData_layer_has_math(&bm->ldata, i) &&
- (!contig_ldata_across_loops(bm, lv1f1, lv1f2, i) ||
- !contig_ldata_across_loops(bm, lv2f1, lv2f2, i))) {
- return false;
+ BM_mesh_elem_index_ensure(bm, BM_FACE);
+ BM_mesh_elem_table_ensure(bm, BM_FACE);
+ totface = bm->totface;
+ face_component = BLI_memarena_alloc(bp->mem_arena, totface * sizeof(int));
+ bp->math_layer_info.face_component = face_component;
+
+ /* Set all component ids by DFS from faces with unassigned components. */
+ for (f = 0; f < totface; f++) {
+ face_component[f] = -1;
+ }
+ current_component = -1;
+
+ /* Use an array as a stack. Stack size can't exceed double total faces. */
+ stack = MEM_malloc_arrayN(2 * totface, sizeof(BMFace *), __func__);
+ for (f = 0; f < totface; f++) {
+ if (face_component[f] == -1) {
+ stack_top = 0;
+ current_component++;
+ BLI_assert(stack_top < 2 * totface);
+ stack[stack_top] = BM_face_at_index(bm, f);
+ while (stack_top >= 0) {
+ bmf = stack[stack_top];
+ stack_top--;
+ bmf_index = BM_elem_index_get(bmf);
+ if (face_component[bmf_index] != -1) {
+ continue;
+ }
+ face_component[bmf_index] = current_component;
+ /* Neighbors are faces that share an edge with bmf and
+ * are where contig_ldata_across_edge(...) is true for the
+ * shared edge and two faces.
+ */
+ BM_ITER_ELEM (bme, &eiter, bmf, BM_EDGES_OF_FACE) {
+ BM_ITER_ELEM (bmf_other, &fiter, bme, BM_FACES_OF_EDGE) {
+ if (bmf_other != bmf) {
+ bmf_other_index = BM_elem_index_get(bmf_other);
+ if (face_component[bmf_other_index] != -1) {
+ continue;
+ }
+ if (contig_ldata_across_edge(bm, bme, bmf, bmf_other)) {
+ stack_top++;
+ BLI_assert(stack_top < 2 * totface);
+ stack[stack_top] = bmf_other;
+ }
+ }
+ }
+ }
+ }
}
}
- return true;
+ MEM_freeN(stack);
+}
+
+/* Use a tie-breaking rule to choose a representative face when
+ * there are number of choices, face[0], face[1], ..., face[nfaces].
+ * This is needed when there are an odd number of segments, and the center
+ * segmment (and its continuation into vmesh) can usually arbitrarily be
+ * the previous face or the next face.
+ * Or, for the center polygon of a corner, all of the faces around
+ * the vertex are possible choices.
+ * If we just choose randomly, the resulting UV maps or material
+ * assignment can look ugly/inconsistent.
+ * Allow for the case when args are null.
+ */
+static BMFace *choose_rep_face(BevelParams *bp, BMFace **face, int nfaces)
+{
+ int bmf_index, value_index, best_f, i;
+ BMFace *bmf;
+ float cent[3];
+#define VEC_VALUE_LEN 6
+ float(*value_vecs)[VEC_VALUE_LEN] = NULL;
+ bool *still_viable = NULL;
+ int num_viable = 0;
+
+ value_vecs = BLI_array_alloca(value_vecs, nfaces);
+ still_viable = BLI_array_alloca(still_viable, nfaces);
+ for (int f = 0; f < nfaces; f++) {
+ bmf = face[f];
+ if (bmf == NULL) {
+ still_viable[f] = false;
+ continue;
+ }
+ still_viable[f] = true;
+ num_viable++;
+ bmf_index = BM_elem_index_get(bmf);
+ value_index = 0;
+ /* First tie-breaker: lower math-layer connected component id. */
+ value_vecs[f][value_index++] = bp->math_layer_info.face_component ?
+ (float)bp->math_layer_info.face_component[bmf_index] :
+ 0.0f;
+ /* Next tie-breaker: selected face beats unselected one. */
+ value_vecs[f][value_index++] = BM_elem_flag_test(bmf, BM_ELEM_SELECT) ? 0.0f : 1.0f;
+ /* Next tie-breaker: lower material index. */
+ value_vecs[f][value_index++] = bmf->mat_nr >= 0 ? (float)bmf->mat_nr : 0.0f;
+ /* Next three tie-breakers: z, x, y components of face center. */
+ BM_face_calc_center_bounds(bmf, cent);
+ value_vecs[f][value_index++] = cent[2];
+ value_vecs[f][value_index++] = cent[0];
+ value_vecs[f][value_index++] = cent[1];
+ BLI_assert(value_index == VEC_VALUE_LEN);
+ }
+
+ /* Look for a face that has a unique minimum value for in a value_index,
+ * trying each value_index in turn until find a unique minimum.
+ */
+ best_f = -1;
+ for (value_index = 0; num_viable > 1 && value_index < VEC_VALUE_LEN; value_index++) {
+ for (int f = 0; f < nfaces; f++) {
+ if (!still_viable[f] || f == best_f) {
+ continue;
+ }
+ if (best_f == -1) {
+ best_f = f;
+ continue;
+ }
+ if (value_vecs[f][value_index] < value_vecs[best_f][value_index]) {
+ best_f = f;
+ /* Previous f's are now not viable any more. */
+ for (i = f - 1; i >= 0; i--) {
+ if (still_viable[i]) {
+ still_viable[i] = false;
+ num_viable--;
+ }
+ }
+ }
+ else if (value_vecs[f][value_index] > value_vecs[best_f][value_index]) {
+ still_viable[f] = false;
+ num_viable--;
+ }
+ }
+ }
+ if (best_f == -1) {
+ best_f = 0;
+ }
+ return face[best_f];
+#undef VEC_VALUE_LEN
}
/* Merge (using average) all the UV values for loops of v's faces.
@@ -1737,7 +1921,7 @@ static void calculate_profile(BevelParams *bp, BoundVert *bndv, bool reversed, b
}
}
r = pro->super_r;
- if (!bp->use_custom_profile && r == PRO_LINE_R) {
+ if (bp->profile_type == BEVEL_PROFILE_SUPERELLIPSE && r == PRO_LINE_R) {
map_ok = false;
}
else {
@@ -1746,8 +1930,7 @@ static void calculate_profile(BevelParams *bp, BoundVert *bndv, bool reversed, b
if (bp->vmesh_method == BEVEL_VMESH_CUTOFF && map_ok) {
/* Calculate the "height" of the profile by putting the (0,0) and (1,1) corners of the
- * un-transformed profile throughout the 2D->3D map and calculating the distance between them.
- */
+ * un-transformed profile through the 2D->3D map and calculating the distance between them. */
zero_v3(p);
mul_v3_m4v3(bottom_corner, map, p);
p[0] = 1.0f;
@@ -1784,14 +1967,8 @@ static void calculate_profile(BevelParams *bp, BoundVert *bndv, bool reversed, b
}
else {
if (map_ok) {
- if (reversed) {
- p[0] = (float)yvals[ns - k];
- p[1] = (float)xvals[ns - k];
- }
- else {
- p[0] = (float)xvals[k];
- p[1] = (float)yvals[k];
- }
+ p[0] = reversed ? (float)yvals[ns - k] : (float)xvals[k];
+ p[1] = reversed ? (float)xvals[ns - k] : (float)yvals[k];
p[2] = 0.0f;
/* Do the 2D->3D transformation of the profile coordinates. */
mul_v3_m4v3(co, map, p);
@@ -2334,7 +2511,7 @@ static void calculate_vm_profiles(BevelParams *bp, BevVert *bv, VMesh *vm)
}
bool miter_profile = false;
bool reverse_profile = false;
- if (bp->use_custom_profile) {
+ if (bp->profile_type == BEVEL_PROFILE_CUSTOM) {
/* Use the miter profile spacing struct if the default is filled with the custom profile. */
miter_profile = (bndv->is_arc_start || bndv->is_patch_start);
/* Don't bother reversing the profile if it's a miter profile */
@@ -2464,7 +2641,7 @@ static void build_boundary_terminal_edge(BevelParams *bp,
}
/* For the edges not adjacent to the beveled edge, slide the bevel amount along. */
d = efirst->offset_l_spec;
- if (bp->use_custom_profile || bp->profile < 0.25f) {
+ if (bp->profile_type == BEVEL_PROFILE_CUSTOM || bp->profile < 0.25f) {
d *= sqrtf(2.0f); /* Need to go further along the edge to make room for full profile area. */
}
for (e = e->next; e->next != efirst; e = e->next) {
@@ -2496,7 +2673,7 @@ static void build_boundary_terminal_edge(BevelParams *bp,
}
else if (vm->count == 3) {
use_tri_fan = true;
- if (bp->use_custom_profile) {
+ if (bp->profile_type == BEVEL_PROFILE_CUSTOM) {
/* Prevent overhanging edges: use M_POLY if the extra point is planar with the profile. */
bndv = efirst->leftv;
float profile_plane[4];
@@ -3831,7 +4008,7 @@ static VMesh *cubic_subdiv(BevelParams *bp, VMesh *vm_in)
copy_v3_v3(co, mesh_vert(vm_in, i, 0, k)->co);
/* Smooth boundary rule. Custom profiles shouldn't be smoothed. */
- if (!bp->use_custom_profile) {
+ if (bp->profile_type != BEVEL_PROFILE_CUSTOM) {
copy_v3_v3(co1, mesh_vert(vm_in, i, 0, k - 1)->co);
copy_v3_v3(co2, mesh_vert(vm_in, i, 0, k + 1)->co);
@@ -3850,7 +4027,7 @@ static VMesh *cubic_subdiv(BevelParams *bp, VMesh *vm_in)
get_profile_point(bp, &bndv->profile, k, ns_out, co);
/* Smooth if using a non-custom profile. */
- if (!bp->use_custom_profile) {
+ if (bp->profile_type != BEVEL_PROFILE_CUSTOM) {
copy_v3_v3(co1, mesh_vert_canon(vm_out, i, 0, k - 1)->co);
copy_v3_v3(co2, mesh_vert_canon(vm_out, i, 0, k + 1)->co);
@@ -4078,7 +4255,7 @@ static VMesh *make_cube_corner_adj_vmesh(BevelParams *bp)
int i, j, k, ns2;
float co[3], coc[3];
- if (!bp->use_custom_profile) {
+ if (bp->profile_type != BEVEL_PROFILE_CUSTOM) {
if (r == PRO_SQUARE_R) {
return make_cube_corner_square(mem_arena, nseg);
}
@@ -4162,7 +4339,7 @@ static int tri_corner_test(BevelParams *bp, BevVert *bv)
int in_plane_e = 0;
/* The superellipse snapping of this case isn't helpful with custom profiles enabled. */
- if (bp->vertex_only || bp->use_custom_profile) {
+ if (bp->vertex_only || bp->profile_type == BEVEL_PROFILE_CUSTOM) {
return -1;
}
if (bv->vmesh->count != 3) {
@@ -4281,7 +4458,7 @@ static VMesh *adj_vmesh(BevelParams *bp, BevVert *bv)
fullness = bp->pro_spacing.fullness;
sub_v3_v3v3(center_direction, original_vertex, boundverts_center);
if (len_squared_v3(center_direction) > BEVEL_EPSILON_SQ) {
- if (bp->use_custom_profile) {
+ if (bp->profile_type == BEVEL_PROFILE_CUSTOM) {
fullness *= 2.0f;
madd_v3_v3v3fl(mesh_vert(vm0, 0, 1, 1)->co, negative_fullest, center_direction, fullness);
}
@@ -4359,7 +4536,7 @@ static VMesh *pipe_adj_vmesh(BevelParams *bp, BevVert *bv, BoundVert *vpipe)
bool even, midline;
float *profile_point_pipe1, *profile_point_pipe2, f;
- /* Some unecessary overhead running this subdivision with custom profile snapping later on. */
+ /* Some unnecessary overhead running this subdivision with custom profile snapping later on. */
vm = adj_vmesh(bp, bv);
/* Now snap all interior coordinates to be on the epipe profile. */
@@ -4377,7 +4554,7 @@ static VMesh *pipe_adj_vmesh(BevelParams *bp, BevVert *bv, BoundVert *vpipe)
continue;
}
/* With a custom profile just copy the shape of the profile at each ring. */
- if (bp->use_custom_profile) {
+ if (bp->profile_type == BEVEL_PROFILE_CUSTOM) {
/* Find both profile vertices that correspond to this point. */
if (i == ipipe1 || i == ipipe2) {
if (n_bndv == 3 && i == ipipe1) {
@@ -4480,6 +4657,105 @@ static float snap_face_dist_squared(float *co, BMFace *f, BMEdge **r_snap_e, flo
return beste_d2;
}
+/* What would be the area of the polygon around bv if interpolated in face frep?
+ */
+static float interp_poly_area(BevVert *bv, BMFace *frep)
+{
+ BoundVert *v;
+ VMesh *vm = bv->vmesh;
+ BMEdge *snape;
+ int n;
+ float(*uv_co)[3] = NULL;
+ float area;
+
+ BLI_assert(vm != NULL);
+ uv_co = BLI_array_alloca(uv_co, vm->count);
+ v = vm->boundstart;
+ n = 0;
+ do {
+ BLI_assert(n < vm->count);
+ snap_face_dist_squared(v->nv.v->co, frep, &snape, uv_co[n]);
+ n++;
+ } while ((v = v->next) != vm->boundstart);
+ area = fabsf(area_poly_v3(uv_co, n));
+ return area;
+}
+
+/**
+ * If we make a poly out of verts around bv, snapping to rep frep, will uv poly have zero area?
+ * The uv poly is made by snapping all outside-of-frep vertices to the closest edge in frep.
+ * Sometimes this results in a zero or very small area polygon, which translates to a zero
+ * or very small area polygong in UV space -- not good for interpolating textures.
+ */
+static bool is_bad_uv_poly(BevVert *bv, BMFace *frep)
+{
+ float area = interp_poly_area(bv, frep);
+ return area < BEVEL_EPSILON_BIG;
+}
+
+/*
+ * Pick a good face from all the faces around bv to use for
+ * a representative face, using choose_rep_face.
+ * We want to choose from among the faces that would be
+ * chosen for a single-segment edge polygon between two successive
+ * Boundverts.
+ * But the single beveled edge is a special case,
+ * where we also want to consider the third face (else can get
+ * zero-area UV interpolated face).
+ *
+ * If there are math-having custom loop layers, like UV, then
+ * don't include faces that would result in zero-area UV polygons
+ * if chosen as the rep.
+ */
+static BMFace *frep_for_center_poly(BevelParams *bp, BevVert *bv)
+{
+ int i, j, fcount;
+ BMFace **fchoices, *bmf, *bmf1, *bmf2, *any_bmf;
+ BMFace *ftwo[2];
+ bool already_there;
+ bool consider_all_faces;
+
+ fcount = 0;
+ any_bmf = NULL;
+ consider_all_faces = bv->selcount == 1;
+ /* Make an array that can hold maximum possible number of choices. */
+ fchoices = BLI_array_alloca(fchoices, bv->edgecount);
+ for (i = 0; i < bv->edgecount; i++) {
+ if (!bv->edges[i].is_bev && !consider_all_faces) {
+ continue;
+ }
+ bmf1 = bv->edges[i].fprev;
+ bmf2 = bv->edges[i].fnext;
+ ftwo[0] = bmf1;
+ ftwo[1] = bmf2;
+ bmf = choose_rep_face(bp, ftwo, 2);
+ if (bmf != NULL) {
+ if (any_bmf == NULL) {
+ any_bmf = bmf;
+ }
+ already_there = false;
+ for (j = fcount - 1; j >= 0; j--) {
+ if (fchoices[j] == bmf) {
+ already_there = true;
+ break;
+ }
+ }
+ if (!already_there) {
+ if (bp->math_layer_info.has_math_layers) {
+ if (is_bad_uv_poly(bv, bmf)) {
+ continue;
+ }
+ }
+ fchoices[fcount++] = bmf;
+ }
+ }
+ }
+ if (fcount == 0) {
+ return any_bmf;
+ }
+ return choose_rep_face(bp, fchoices, fcount);
+}
+
static void build_center_ngon(BevelParams *bp, BMesh *bm, BevVert *bv, int mat_nr)
{
VMesh *vm = bv->vmesh;
@@ -4496,7 +4772,7 @@ static void build_center_ngon(BevelParams *bp, BMesh *bm, BevVert *bv, int mat_n
ns2 = vm->seg / 2;
if (bv->any_seam) {
- frep = boundvert_rep_face(vm->boundstart, NULL);
+ frep = frep_for_center_poly(bp, bv);
get_incident_edges(frep, bv->v, &frep_e1, &frep_e2);
}
else {
@@ -4821,7 +5097,8 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv, BoundVert
VMesh *vm1, *vm;
BoundVert *bndv;
BMVert *bmv1, *bmv2, *bmv3, *bmv4;
- BMFace *f, *f2, *r_f;
+ BMFace *f, *f2, *r_f, *fc;
+ BMFace *fchoices[2];
BMEdge *bme, *bme1, *bme2, *bme3;
EdgeHalf *e;
int mat_nr = bp->mat_nr;
@@ -4832,7 +5109,8 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv, BoundVert
odd = ns % 2;
BLI_assert(n_bndv >= 3 && ns > 1);
- if (bp->pro_super_r == PRO_SQUARE_R && bv->selcount >= 3 && !odd && !bp->use_custom_profile) {
+ if (bp->pro_super_r == PRO_SQUARE_R && bv->selcount >= 3 && !odd &&
+ bp->profile_type != BEVEL_PROFILE_CUSTOM) {
vm1 = square_out_adj_vmesh(bp, bv);
}
else if (vpipe) {
@@ -4842,7 +5120,7 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv, BoundVert
vm1 = tri_corner_adj_vmesh(bp, bv);
/* The PRO_SQUARE_IN_R profile has boundary edges that merge
* and no internal ring polys except possibly center ngon. */
- if (bp->pro_super_r == PRO_SQUARE_IN_R && !bp->use_custom_profile) {
+ if (bp->pro_super_r == PRO_SQUARE_IN_R && bp->profile_type != BEVEL_PROFILE_CUSTOM) {
build_square_in_vmesh(bp, bm, bv, vm1);
return;
}
@@ -4874,6 +5152,14 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv, BoundVert
i = bndv->index;
f = boundvert_rep_face(bndv, NULL);
f2 = boundvert_rep_face(bndv->next, NULL);
+ fchoices[0] = f;
+ fchoices[1] = f2;
+ if (odd) {
+ fc = choose_rep_face(bp, fchoices, 2);
+ }
+ else {
+ fc = NULL;
+ }
if (bp->vertex_only) {
e = bndv->efirst;
}
@@ -4884,7 +5170,10 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv, BoundVert
/* For odd ns, make polys with lower left corner at (i,j,k) for
* j in [0, ns2-1], k in [0, ns2]. And then the center ngon.
* For even ns,
- * j in [0, ns2-1], k in [0, ns2-1]. */
+ * j in [0, ns2-1], k in [0, ns2-1].
+ *
+ * Recall: j is ring index, k is segment index.
+ */
for (j = 0; j < ns2; j++) {
for (k = 0; k < ns2 + odd; k++) {
bmv1 = mesh_vert(vm, i, j, k)->v;
@@ -4908,6 +5197,7 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv, BoundVert
NULL,
bndv->next->efirst->e,
bme,
+ f2,
mat_nr);
}
else {
@@ -4921,11 +5211,11 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv, BoundVert
/* Only one edge attached to v, since vertex_only. */
if (e->is_seam) {
r_f = bev_create_quad_ex(
- bm, bmv1, bmv2, bmv3, bmv4, f2, f2, f2, f2, bme, NULL, bme, NULL, mat_nr);
+ bm, bmv1, bmv2, bmv3, bmv4, f2, f2, f2, f2, bme, NULL, bme, NULL, f2, mat_nr);
}
else {
r_f = bev_create_quad_ex(
- bm, bmv1, bmv2, bmv3, bmv4, f2, f2, f2, f, bme, NULL, bme, NULL, mat_nr);
+ bm, bmv1, bmv2, bmv3, bmv4, f2, f2, f2, f, bme, NULL, bme, NULL, f2, mat_nr);
}
}
}
@@ -4934,10 +5224,11 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv, BoundVert
if (k == ns2) {
if (e && e->is_seam) {
r_f = bev_create_quad_ex(
- bm, bmv1, bmv2, bmv3, bmv4, f, f, f, f, NULL, bme, bme, NULL, mat_nr);
+ bm, bmv1, bmv2, bmv3, bmv4, fc, fc, fc, fc, NULL, bme, bme, NULL, fc, mat_nr);
}
else {
- r_f = bev_create_quad(bm, bmv1, bmv2, bmv3, bmv4, f, f2, f2, f, mat_nr);
+ r_f = bev_create_quad_ex(
+ bm, bmv1, bmv2, bmv3, bmv4, f, f2, f2, f, NULL, bme, bme, NULL, fc, mat_nr);
}
}
else {
@@ -4952,7 +5243,7 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv, BoundVert
}
bme2 = bme1 != NULL ? bme1 : bme3;
r_f = bev_create_quad_ex(
- bm, bmv1, bmv2, bmv3, bmv4, f, f, f, f, NULL, bme1, bme2, bme3, mat_nr);
+ bm, bmv1, bmv2, bmv3, bmv4, f, f, f, f, NULL, bme1, bme2, bme3, f, mat_nr);
}
}
record_face_kind(bp, r_f, F_VERT);
@@ -5150,43 +5441,9 @@ static void bevel_build_cutoff(BevelParams *bp, BMesh *bm, BevVert *bv)
}
}
-/**
- * If we make a poly out of verts around bv, snapping to rep frep, will uv poly have zero area?
- * The uv poly is made by snapping all outside-of-frep vertices to the closest edge in frep.
- * Assume that this function is called when the only inside-of-frep vertex is vm->boundstart.
- * The poly will have zero area if the distance of that first vertex to some edge e is zero,
- * and all the other vertices snap to e or snap to an edge
- * at a point that is essentially on e too.
- */
-static bool is_bad_uv_poly(BevVert *bv, BMFace *frep)
-{
- BoundVert *v;
- BMEdge *snape, *firste;
- float co[3];
- VMesh *vm = bv->vmesh;
- float d2;
-
- v = vm->boundstart;
- d2 = snap_face_dist_squared(v->nv.v->co, frep, &firste, co);
- if (d2 > BEVEL_EPSILON_BIG_SQ || firste == NULL) {
- return false;
- }
-
- for (v = v->next; v != vm->boundstart; v = v->next) {
- snap_face_dist_squared(v->nv.v->co, frep, &snape, co);
- if (snape != firste) {
- d2 = dist_to_line_v3(co, firste->v1->co, firste->v2->co);
- if (d2 > BEVEL_EPSILON_BIG_SQ) {
- return false;
- }
- }
- }
- return true;
-}
-
static BMFace *bevel_build_poly(BevelParams *bp, BMesh *bm, BevVert *bv)
{
- BMFace *f, *repface, *frep2;
+ BMFace *f, *repface;
int n, k;
VMesh *vm = bv->vmesh;
BoundVert *bndv;
@@ -5199,10 +5456,7 @@ static BMFace *bevel_build_poly(BevelParams *bp, BMesh *bm, BevVert *bv)
BLI_array_staticdeclare(bmfaces, BM_DEFAULT_NGON_STACK_SIZE);
if (bv->any_seam) {
- repface = boundvert_rep_face(vm->boundstart, &frep2);
- if (frep2 && repface && is_bad_uv_poly(bv, repface)) {
- repface = frep2;
- }
+ repface = frep_for_center_poly(bp, bv);
get_incident_edges(repface, bv->v, &repface_e1, &repface_e2);
}
else {
@@ -5446,7 +5700,7 @@ static void build_vmesh(BevelParams *bp, BMesh *bm, BevVert *bv)
for (k = 1; k < ns; k++) {
v_weld1 = mesh_vert(bv->vmesh, weld1->index, 0, k)->co;
v_weld2 = mesh_vert(bv->vmesh, weld2->index, 0, ns - k)->co;
- if (bp->use_custom_profile) {
+ if (bp->profile_type == BEVEL_PROFILE_CUSTOM) {
/* Don't bother with special case profile check from below. */
mid_v3_v3v3(co, v_weld1, v_weld2);
}
@@ -5771,9 +6025,8 @@ static BevVert *bevel_vert_construct(BMesh *bm, BevelParams *bp, BMVert *v)
float vert_axis[3] = {0, 0, 0};
int i, ccw_test_sum;
int nsel = 0;
- int ntot = 0;
- int nwire = 0;
- int fcnt;
+ int tot_edges = 0;
+ int tot_wire = 0;
/* Gather input selected edges.
* Only bevel selected edges that have exactly two incident faces.
@@ -5785,24 +6038,24 @@ static BevVert *bevel_vert_construct(BMesh *bm, BevelParams *bp, BMVert *v)
first_bme = NULL;
BM_ITER_ELEM (bme, &iter, v, BM_EDGES_OF_VERT) {
- fcnt = BM_edge_face_count(bme);
+ int face_count = BM_edge_face_count(bme);
BM_BEVEL_EDGE_TAG_DISABLE(bme);
if (BM_elem_flag_test(bme, BM_ELEM_TAG) && !bp->vertex_only) {
- BLI_assert(fcnt == 2);
+ BLI_assert(face_count == 2);
nsel++;
if (!first_bme) {
first_bme = bme;
}
}
- if (fcnt == 1) {
+ if (face_count == 1) {
/* Good to start face chain from this edge. */
first_bme = bme;
}
- if (fcnt > 0 || bp->vertex_only) {
- ntot++;
+ if (face_count > 0 || bp->vertex_only) {
+ tot_edges++;
}
if (BM_edge_is_wire(bme)) {
- nwire++;
+ tot_wire++;
/* If edge beveling, exclude wire edges from edges array.
* Mark this edge as "chosen" so loop below won't choose it. */
if (!bp->vertex_only) {
@@ -5814,7 +6067,7 @@ static BevVert *bevel_vert_construct(BMesh *bm, BevelParams *bp, BMVert *v)
first_bme = v->e;
}
- if ((nsel == 0 && !bp->vertex_only) || (ntot < 2 && bp->vertex_only)) {
+ if ((nsel == 0 && !bp->vertex_only) || (tot_edges < 2 && bp->vertex_only)) {
/* Signal this vert isn't being beveled. */
BM_elem_flag_disable(v, BM_ELEM_TAG);
return NULL;
@@ -5822,13 +6075,13 @@ static BevVert *bevel_vert_construct(BMesh *bm, BevelParams *bp, BMVert *v)
bv = (BevVert *)BLI_memarena_alloc(bp->mem_arena, (sizeof(BevVert)));
bv->v = v;
- bv->edgecount = ntot;
+ bv->edgecount = tot_edges;
bv->selcount = nsel;
- bv->wirecount = nwire;
+ bv->wirecount = tot_wire;
bv->offset = bp->offset;
- bv->edges = (EdgeHalf *)BLI_memarena_alloc(bp->mem_arena, ntot * sizeof(EdgeHalf));
- if (nwire) {
- bv->wire_edges = (BMEdge **)BLI_memarena_alloc(bp->mem_arena, nwire * sizeof(BMEdge *));
+ bv->edges = (EdgeHalf *)BLI_memarena_alloc(bp->mem_arena, tot_edges * sizeof(EdgeHalf));
+ if (tot_wire) {
+ bv->wire_edges = (BMEdge **)BLI_memarena_alloc(bp->mem_arena, tot_wire * sizeof(BMEdge *));
}
else {
bv->wire_edges = NULL;
@@ -5841,7 +6094,7 @@ static BevVert *bevel_vert_construct(BMesh *bm, BevelParams *bp, BMVert *v)
find_bevel_edge_order(bm, bv, first_bme);
/* Fill in other attributes of EdgeHalfs. */
- for (i = 0; i < ntot; i++) {
+ for (i = 0; i < tot_edges; i++) {
e = &bv->edges[i];
bme = e->e;
if (BM_elem_flag_test(bme, BM_ELEM_TAG) && !bp->vertex_only) {
@@ -5864,27 +6117,27 @@ static BevVert *bevel_vert_construct(BMesh *bm, BevelParams *bp, BMVert *v)
/* If edge array doesn't go CCW around vertex from average normal side,
* reverse the array, being careful to reverse face pointers too. */
- if (ntot > 1) {
+ if (tot_edges > 1) {
ccw_test_sum = 0;
- for (i = 0; i < ntot; i++) {
+ for (i = 0; i < tot_edges; i++) {
ccw_test_sum += bev_ccw_test(
- bv->edges[i].e, bv->edges[(i + 1) % ntot].e, bv->edges[i].fnext);
+ bv->edges[i].e, bv->edges[(i + 1) % tot_edges].e, bv->edges[i].fnext);
}
if (ccw_test_sum < 0) {
- for (i = 0; i <= (ntot / 2) - 1; i++) {
- SWAP(EdgeHalf, bv->edges[i], bv->edges[ntot - i - 1]);
+ for (i = 0; i <= (tot_edges / 2) - 1; i++) {
+ SWAP(EdgeHalf, bv->edges[i], bv->edges[tot_edges - i - 1]);
SWAP(BMFace *, bv->edges[i].fprev, bv->edges[i].fnext);
- SWAP(BMFace *, bv->edges[ntot - i - 1].fprev, bv->edges[ntot - i - 1].fnext);
+ SWAP(BMFace *, bv->edges[tot_edges - i - 1].fprev, bv->edges[tot_edges - i - 1].fnext);
}
- if (ntot % 2 == 1) {
- i = ntot / 2;
+ if (tot_edges % 2 == 1) {
+ i = tot_edges / 2;
SWAP(BMFace *, bv->edges[i].fprev, bv->edges[i].fnext);
}
}
}
if (bp->vertex_only) {
- /* If weighted, modify offset by weight. */
+ /* Modify the offset by the vertex group or bevel weight if they are specified. */
if (bp->dvert != NULL && bp->vertex_group != -1) {
weight = BKE_defvert_find_weight(bp->dvert + BM_elem_index_get(v), bp->vertex_group);
bv->offset *= weight;
@@ -5896,7 +6149,7 @@ static BevVert *bevel_vert_construct(BMesh *bm, BevelParams *bp, BMVert *v)
/* Find center axis. Note: Don't use vert normal, can give unwanted results. */
if (ELEM(bp->offset_type, BEVEL_AMT_WIDTH, BEVEL_AMT_DEPTH)) {
float edge_dir[3];
- for (i = 0, e = bv->edges; i < ntot; i++, e++) {
+ for (i = 0, e = bv->edges; i < tot_edges; i++, e++) {
v2 = BM_edge_other_vert(e->e, bv->v);
sub_v3_v3v3(edge_dir, bv->v->co, v2->co);
normalize_v3(edge_dir);
@@ -5905,14 +6158,14 @@ static BevVert *bevel_vert_construct(BMesh *bm, BevelParams *bp, BMVert *v)
}
}
- for (i = 0, e = bv->edges; i < ntot; i++, e++) {
- e->next = &bv->edges[(i + 1) % ntot];
- e->prev = &bv->edges[(i + ntot - 1) % ntot];
+ /* Set offsets for each beveled edge. */
+ for (i = 0, e = bv->edges; i < tot_edges; i++, e++) {
+ e->next = &bv->edges[(i + 1) % tot_edges];
+ e->prev = &bv->edges[(i + tot_edges - 1) % tot_edges];
- /* Set offsets. */
if (e->is_bev) {
/* Convert distance as specified by user into offsets along
- * faces on left side and right side of this edgehalf.
+ * faces on the left side and right sides of this edgehalf.
* Except for percent method, offset will be same on each side. */
switch (bp->offset_type) {
@@ -5938,8 +6191,7 @@ static BevVert *bevel_vert_construct(BMesh *bm, BevelParams *bp, BMVert *v)
}
break;
case BEVEL_AMT_PERCENT:
- /* Offset needs to be such that it meets adjacent edges at percentage of their lengths.
- */
+ /* Offset needs to meet adjacent edges at percentage of their lengths. */
v1 = BM_edge_other_vert(e->prev->e, v);
v2 = BM_edge_other_vert(e->e, v);
z = sinf(angle_v3v3v3(v1->co, v->co, v2->co));
@@ -5949,12 +6201,23 @@ static BevVert *bevel_vert_construct(BMesh *bm, BevelParams *bp, BMVert *v)
z = sinf(angle_v3v3v3(v1->co, v->co, v2->co));
e->offset_r_spec = BM_edge_calc_length(e->next->e) * bp->offset * z / 100.0f;
break;
+ case BEVEL_AMT_ABSOLUTE:
+ /* Like Percent, but the amount gives the absolute distance along adjacent edges. */
+ v1 = BM_edge_other_vert(e->prev->e, v);
+ v2 = BM_edge_other_vert(e->e, v);
+ z = sinf(angle_v3v3v3(v1->co, v->co, v2->co));
+ e->offset_l_spec = bp->offset * z;
+ v1 = BM_edge_other_vert(e->e, v);
+ v2 = BM_edge_other_vert(e->next->e, v);
+ z = sinf(angle_v3v3v3(v1->co, v->co, v2->co));
+ e->offset_r_spec = bp->offset * z;
+ break;
default:
BLI_assert(!"bad bevel offset kind");
e->offset_l_spec = bp->offset;
break;
}
- if (bp->offset_type != BEVEL_AMT_PERCENT) {
+ if (bp->offset_type != BEVEL_AMT_PERCENT && bp->offset_type != BEVEL_AMT_ABSOLUTE) {
e->offset_r_spec = e->offset_l_spec;
}
if (bp->use_weights) {
@@ -6000,6 +6263,10 @@ static BevVert *bevel_vert_construct(BMesh *bm, BevelParams *bp, BMVert *v)
e->offset_l_spec = BM_edge_calc_length(e->e) * bv->offset / 100.0f;
break;
}
+ case BEVEL_AMT_ABSOLUTE: {
+ e->offset_l_spec = bv->offset;
+ break;
+ }
}
e->offset_r_spec = e->offset_l_spec;
}
@@ -6017,7 +6284,8 @@ static BevVert *bevel_vert_construct(BMesh *bm, BevelParams *bp, BMVert *v)
}
}
- if (nwire) {
+ /* Collect wire edges if we found any earlier. */
+ if (tot_wire) {
i = 0;
BM_ITER_ELEM (bme, &iter, v, BM_EDGES_OF_VERT) {
if (BM_edge_is_wire(bme)) {
@@ -6406,7 +6674,7 @@ static void bevel_build_edge_polygons(BMesh *bm, BevelParams *bp, BMEdge *bme)
VMesh *vm1, *vm2;
EdgeHalf *e1, *e2;
BMEdge *bme1, *bme2, *center_bme;
- BMFace *f1, *f2, *f, *r_f;
+ BMFace *f1, *f2, *f, *r_f, *f_choice;
BMVert *verts[4];
BMFace *faces[4];
BMEdge *edges[4];
@@ -6467,15 +6735,17 @@ static void bevel_build_edge_polygons(BMesh *bm, BevelParams *bp, BMEdge *bme)
verts[3] = mesh_vert(vm1, i1, 0, k)->v;
verts[2] = mesh_vert(vm2, i2, 0, nseg - k)->v;
if (odd && k == mid + 1) {
+ BMFace *fchoices[2] = {f1, f2};
+ edges[0] = edges[1] = NULL;
+ edges[2] = edges[3] = bme;
+ f_choice = choose_rep_face(bp, fchoices, 2);
if (e1->is_seam) {
- /* Straddles a seam: choose to interpolate in f1 and snap right edge to bme. */
- edges[0] = edges[1] = NULL;
- edges[2] = edges[3] = bme;
- r_f = bev_create_ngon(bm, verts, 4, NULL, f1, edges, mat_nr, true);
+ /* Straddles a seam: choose to interpolate in f_choice and snap right edge to bme. */
+ r_f = bev_create_ngon(bm, verts, 4, NULL, f_choice, edges, mat_nr, true);
}
else {
/* Straddles but not a seam: interpolate left half in f1, right half in f2. */
- r_f = bev_create_ngon(bm, verts, 4, faces, NULL, NULL, mat_nr, true);
+ r_f = bev_create_ngon(bm, verts, 4, faces, f_choice, NULL, mat_nr, true);
}
}
else if (!odd && k == mid) {
@@ -6820,7 +7090,7 @@ static float find_profile_fullness(BevelParams *bp)
0.647f, /* 11 */
};
- if (bp->use_custom_profile) {
+ if (bp->profile_type == BEVEL_PROFILE_CUSTOM) {
/* Set fullness to the average "height" of the profile's sampled points. */
fullness = 0.0f;
for (int i = 0; i < nseg; i++) { /* Don't use the end points. */
@@ -7156,6 +7426,7 @@ static void bevel_limit_offset(BevelParams *bp, BMesh *bm)
void BM_mesh_bevel(BMesh *bm,
const float offset,
const int offset_type,
+ const int profile_type,
const int segments,
const float profile,
const bool vertex_only,
@@ -7173,7 +7444,6 @@ void BM_mesh_bevel(BMesh *bm,
const int miter_inner,
const float spread,
const float smoothresh,
- const bool use_custom_profile,
const struct CurveProfile *custom_profile,
const int vmesh_method)
{
@@ -7194,7 +7464,7 @@ void BM_mesh_bevel(BMesh *bm,
bp.use_weights = use_weights;
bp.loop_slide = loop_slide;
bp.limit_offset = limit_offset;
- bp.offset_adjust = true;
+ bp.offset_adjust = !vertex_only && !ELEM(offset_type, BEVEL_AMT_PERCENT, BEVEL_AMT_ABSOLUTE);
bp.dvert = dvert;
bp.vertex_group = vertex_group;
bp.mat_nr = mat;
@@ -7207,12 +7477,11 @@ void BM_mesh_bevel(BMesh *bm,
bp.spread = spread;
bp.smoothresh = smoothresh;
bp.face_hash = NULL;
- bp.use_custom_profile = use_custom_profile;
+ bp.profile_type = profile_type;
bp.custom_profile = custom_profile;
bp.vmesh_method = vmesh_method;
- /* Disable the miters with the cutoff vertex mesh method, this combination isn't useful anyway.
- */
+ /* Disable the miters with the cutoff vertex mesh method, the combination isn't useful anyway. */
if (bp.vmesh_method == BEVEL_VMESH_CUTOFF) {
bp.miter_outer = BEVEL_MITER_SHARP;
bp.miter_inner = BEVEL_MITER_SHARP;
@@ -7242,13 +7511,15 @@ void BM_mesh_bevel(BMesh *bm,
BLI_memarena_use_calloc(bp.mem_arena);
/* Get the 2D profile point locations from either the superellipse or the custom profile. */
- set_profile_spacing(&bp, &bp.pro_spacing, bp.use_custom_profile);
+ set_profile_spacing(&bp, &bp.pro_spacing, bp.profile_type == BEVEL_PROFILE_CUSTOM);
+
+ /* Get the 'fullness' of the profile for the ADJ vertex mesh method. */
if (bp.seg > 1) {
bp.pro_spacing.fullness = find_profile_fullness(&bp);
}
- /* Get separate non-custom profile samples for the miter profiles if they are needed. */
- if (bp.use_custom_profile &&
+ /* Get separate non-custom profile samples for the miter profiles if they are needed */
+ if (bp.profile_type == BEVEL_PROFILE_CUSTOM &&
(bp.miter_inner != BEVEL_MITER_SHARP || bp.miter_outer != BEVEL_MITER_SHARP)) {
set_profile_spacing(&bp, &bp.pro_spacing_miter, false);
}
@@ -7256,6 +7527,8 @@ void BM_mesh_bevel(BMesh *bm,
bp.face_hash = BLI_ghash_ptr_new(__func__);
BLI_ghash_flag_set(bp.face_hash, GHASH_FLAG_ALLOW_DUPES);
+ math_layer_info_init(&bp, bm);
+
/* Analyze input vertices, sorting edges and assigning initial new vertex positions. */
BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
if (BM_elem_flag_test(v, BM_ELEM_TAG)) {
@@ -7282,12 +7555,12 @@ void BM_mesh_bevel(BMesh *bm,
}
/* Perhaps do a pass to try to even out widths. */
- if (!bp.vertex_only && bp.offset_adjust && bp.offset_type != BEVEL_AMT_PERCENT) {
+ if (bp.offset_adjust) {
adjust_offsets(&bp, bm);
}
/* Maintain consistent orientations for the asymmetrical custom profiles. */
- if (bp.use_custom_profile) {
+ if (bp.profile_type == BEVEL_PROFILE_CUSTOM) {
BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
if (BM_elem_flag_test(e, BM_ELEM_TAG)) {
regularize_profile_orientation(&bp, e);
diff --git a/source/blender/bmesh/tools/bmesh_bevel.h b/source/blender/bmesh/tools/bmesh_bevel.h
index 8562e584ec9..667482960d3 100644
--- a/source/blender/bmesh/tools/bmesh_bevel.h
+++ b/source/blender/bmesh/tools/bmesh_bevel.h
@@ -27,6 +27,7 @@ struct MDeformVert;
void BM_mesh_bevel(BMesh *bm,
const float offset,
const int offset_type,
+ const int profile_type,
const int segments,
const float profile,
const bool vertex_only,
@@ -44,7 +45,6 @@ void BM_mesh_bevel(BMesh *bm,
const int miter_inner,
const float spread,
const float smoothresh,
- const bool use_custom_profile,
const struct CurveProfile *custom_profile,
const int vmesh_method);
diff --git a/source/blender/compositor/intern/COM_NodeOperation.h b/source/blender/compositor/intern/COM_NodeOperation.h
index 07f482f9e5f..d2c896a2e56 100644
--- a/source/blender/compositor/intern/COM_NodeOperation.h
+++ b/source/blender/compositor/intern/COM_NodeOperation.h
@@ -167,7 +167,7 @@ class NodeOperation : public SocketReader {
* the ExecutionSystem.
*
* \see ExecutionSystem
- * \group check
+ * \ingroup check
* \param rendering: [true false]
* true: rendering
* false: editing
diff --git a/source/blender/compositor/intern/COM_NodeOperationBuilder.cpp b/source/blender/compositor/intern/COM_NodeOperationBuilder.cpp
index 5ce6ca34b34..53a6d115e97 100644
--- a/source/blender/compositor/intern/COM_NodeOperationBuilder.cpp
+++ b/source/blender/compositor/intern/COM_NodeOperationBuilder.cpp
@@ -98,10 +98,10 @@ void NodeOperationBuilder::convertToOperations(ExecutionSystem *system)
add_operation_input_constants();
- add_datatype_conversions();
-
resolve_proxies();
+ add_datatype_conversions();
+
determineResolutions();
/* surround complex ops with read/write buffer */
diff --git a/source/blender/compositor/nodes/COM_FlipNode.cpp b/source/blender/compositor/nodes/COM_FlipNode.cpp
index 47148ceb1a9..d89e6b7b844 100644
--- a/source/blender/compositor/nodes/COM_FlipNode.cpp
+++ b/source/blender/compositor/nodes/COM_FlipNode.cpp
@@ -33,8 +33,8 @@ void FlipNode::convertToOperations(NodeConverter &converter,
NodeOutput *outputSocket = this->getOutputSocket(0);
FlipOperation *operation = new FlipOperation();
switch (this->getbNode()->custom1) {
- case 0: /// \TODO: I didn't find any constants in the old implementation, should I introduce
- /// them.
+ case 0: /* TODO: I didn't find any constants in the old implementation,
+ * should I introduce them. */
operation->setFlipX(true);
operation->setFlipY(false);
break;
diff --git a/source/blender/depsgraph/intern/builder/deg_builder.cc b/source/blender/depsgraph/intern/builder/deg_builder.cc
index 82f3ea7d182..39de6054615 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder.cc
@@ -50,7 +50,8 @@
#include "DEG_depsgraph.h"
-namespace DEG {
+namespace blender {
+namespace deg {
bool deg_check_id_in_depsgraph(const Depsgraph *graph, ID *id_orig)
{
@@ -240,4 +241,5 @@ void deg_graph_build_finalize(Main *bmain, Depsgraph *graph)
}
}
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/builder/deg_builder.h b/source/blender/depsgraph/intern/builder/deg_builder.h
index 2db861b6fca..36b6b1bf17d 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder.h
@@ -29,7 +29,8 @@ struct Main;
struct Object;
struct bPoseChannel;
-namespace DEG {
+namespace blender {
+namespace deg {
struct Depsgraph;
class DepsgraphBuilderCache;
@@ -58,4 +59,5 @@ bool deg_check_id_in_depsgraph(const Depsgraph *graph, ID *id_orig);
bool deg_check_base_in_depsgraph(const Depsgraph *graph, Base *base);
void deg_graph_build_finalize(Main *bmain, Depsgraph *graph);
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_cache.cc b/source/blender/depsgraph/intern/builder/deg_builder_cache.cc
index 104676f7ab6..2faedc4fcd6 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_cache.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_cache.cc
@@ -31,7 +31,8 @@
#include "BKE_animsys.h"
-namespace DEG {
+namespace blender {
+namespace deg {
/* Animated property storage. */
@@ -155,21 +156,16 @@ DepsgraphBuilderCache::DepsgraphBuilderCache()
DepsgraphBuilderCache::~DepsgraphBuilderCache()
{
- for (AnimatedPropertyStorageMap::value_type &iter : animated_property_storage_map_) {
- AnimatedPropertyStorage *animated_property_storage = iter.second;
+ for (AnimatedPropertyStorage *animated_property_storage :
+ animated_property_storage_map_.values()) {
OBJECT_GUARDED_DELETE(animated_property_storage, AnimatedPropertyStorage);
}
}
AnimatedPropertyStorage *DepsgraphBuilderCache::ensureAnimatedPropertyStorage(ID *id)
{
- AnimatedPropertyStorageMap::iterator it = animated_property_storage_map_.find(id);
- if (it != animated_property_storage_map_.end()) {
- return it->second;
- }
- AnimatedPropertyStorage *animated_property_storage = OBJECT_GUARDED_NEW(AnimatedPropertyStorage);
- animated_property_storage_map_.insert(make_pair(id, animated_property_storage));
- return animated_property_storage;
+ return animated_property_storage_map_.lookup_or_add_cb(
+ id, []() { return OBJECT_GUARDED_NEW(AnimatedPropertyStorage); });
}
AnimatedPropertyStorage *DepsgraphBuilderCache::ensureInitializedAnimatedPropertyStorage(ID *id)
@@ -182,4 +178,5 @@ AnimatedPropertyStorage *DepsgraphBuilderCache::ensureInitializedAnimatedPropert
return animated_property_storage;
}
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_cache.h b/source/blender/depsgraph/intern/builder/deg_builder_cache.h
index bb4e1f5c96a..6e159f55c94 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_cache.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_cache.h
@@ -31,7 +31,8 @@ struct ID;
struct PointerRNA;
struct PropertyRNA;
-namespace DEG {
+namespace blender {
+namespace deg {
class DepsgraphBuilderCache;
@@ -45,8 +46,6 @@ class AnimatedPropertyID {
AnimatedPropertyID(ID *id, StructRNA *type, void *data, const char *property_name);
uint32_t hash() const;
-
- bool operator<(const AnimatedPropertyID &other) const;
friend bool operator==(const AnimatedPropertyID &a, const AnimatedPropertyID &b);
/* Corresponds to PointerRNA.data. */
@@ -73,8 +72,6 @@ class AnimatedPropertyStorage {
Set<AnimatedPropertyID> animated_properties_set;
};
-typedef map<ID *, AnimatedPropertyStorage *> AnimatedPropertyStorageMap;
-
/* Cached data which can be re-used by multiple builders. */
class DepsgraphBuilderCache {
public:
@@ -100,7 +97,8 @@ class DepsgraphBuilderCache {
return animated_property_storage->isPropertyAnimated(args...);
}
- AnimatedPropertyStorageMap animated_property_storage_map_;
+ Map<ID *, AnimatedPropertyStorage *> animated_property_storage_map_;
};
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_cycle.cc b/source/blender/depsgraph/intern/builder/deg_builder_cycle.cc
index 6deecfde9e1..f8c7737c843 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_cycle.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_cycle.cc
@@ -37,7 +37,8 @@
#include "intern/depsgraph.h"
#include "intern/depsgraph_relation.h"
-namespace DEG {
+namespace blender {
+namespace deg {
namespace {
@@ -234,4 +235,5 @@ void deg_graph_detect_cycles(Depsgraph *graph)
}
}
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_cycle.h b/source/blender/depsgraph/intern/builder/deg_builder_cycle.h
index aafdcdbea04..f9674e0e60d 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_cycle.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_cycle.h
@@ -23,11 +23,13 @@
#pragma once
-namespace DEG {
+namespace blender {
+namespace deg {
struct Depsgraph;
/* Detect and solve dependency cycles. */
void deg_graph_detect_cycles(Depsgraph *graph);
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_map.cc b/source/blender/depsgraph/intern/builder/deg_builder_map.cc
index 4bca4f037b0..e605e83a862 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_map.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_map.cc
@@ -25,7 +25,8 @@
#include "DNA_ID.h"
-namespace DEG {
+namespace blender {
+namespace deg {
BuilderMap::BuilderMap()
{
@@ -42,33 +43,21 @@ bool BuilderMap::checkIsBuilt(ID *id, int tag) const
void BuilderMap::tagBuild(ID *id, int tag)
{
- IDTagMap::iterator it = id_tags_.find(id);
- if (it == id_tags_.end()) {
- id_tags_.insert(make_pair(id, tag));
- return;
- }
- it->second |= tag;
+ id_tags_.lookup_or_add(id, 0) |= tag;
}
bool BuilderMap::checkIsBuiltAndTag(ID *id, int tag)
{
- IDTagMap::iterator it = id_tags_.find(id);
- if (it == id_tags_.end()) {
- id_tags_.insert(make_pair(id, tag));
- return false;
- }
- const bool result = (it->second & tag) == tag;
- it->second |= tag;
+ int &id_tag = id_tags_.lookup_or_add(id, 0);
+ const bool result = (id_tag & tag) == tag;
+ id_tag |= tag;
return result;
}
int BuilderMap::getIDTag(ID *id) const
{
- IDTagMap::const_iterator it = id_tags_.find(id);
- if (it == id_tags_.end()) {
- return 0;
- }
- return it->second;
+ return id_tags_.lookup_default(id, 0);
}
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_map.h b/source/blender/depsgraph/intern/builder/deg_builder_map.h
index 65b493e2467..5dfc3297b3e 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_map.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_map.h
@@ -27,7 +27,8 @@
struct ID;
-namespace DEG {
+namespace blender {
+namespace deg {
class BuilderMap {
public:
@@ -75,8 +76,8 @@ class BuilderMap {
protected:
int getIDTag(ID *id) const;
- typedef map<ID *, int> IDTagMap;
- IDTagMap id_tags_;
+ Map<ID *, int> id_tags_;
};
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
index 513472f6ec9..f5cc5963253 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
@@ -119,7 +119,8 @@
#include "intern/node/deg_node_id.h"
#include "intern/node/deg_node_operation.h"
-namespace DEG {
+namespace blender {
+namespace deg {
/* ************ */
/* Node Builder */
@@ -594,7 +595,7 @@ void DepsgraphNodeBuilder::build_object(int base_index,
}
id_node->has_base |= (base_index != -1);
/* Various flags, flushing from bases/collections. */
- build_object_flags(base_index, object, linked_state);
+ build_object_from_layer(base_index, object, linked_state);
/* Transform. */
build_object_transform(object);
/* Parent. */
@@ -662,6 +663,21 @@ void DepsgraphNodeBuilder::build_object(int base_index,
function_bind(BKE_object_sync_to_original, _1, object_cow));
}
+void DepsgraphNodeBuilder::build_object_from_layer(int base_index,
+ Object *object,
+ eDepsNode_LinkedState_Type linked_state)
+{
+
+ OperationNode *entry_node = add_operation_node(
+ &object->id, NodeType::OBJECT_FROM_LAYER, OperationCode::OBJECT_FROM_LAYER_ENTRY);
+ entry_node->set_as_entry();
+ OperationNode *exit_node = add_operation_node(
+ &object->id, NodeType::OBJECT_FROM_LAYER, OperationCode::OBJECT_FROM_LAYER_EXIT);
+ exit_node->set_as_exit();
+
+ build_object_flags(base_index, object, linked_state);
+}
+
void DepsgraphNodeBuilder::build_object_flags(int base_index,
Object *object,
eDepsNode_LinkedState_Type linked_state)
@@ -733,7 +749,7 @@ void DepsgraphNodeBuilder::build_object_data(Object *object, bool is_object_visi
break;
case OB_ARMATURE:
if (ID_IS_LINKED(object) && object->proxy_from != nullptr) {
- build_proxy_rig(object);
+ build_proxy_rig(object, is_object_visible);
}
else {
build_rig(object, is_object_visible);
@@ -1884,4 +1900,5 @@ void DepsgraphNodeBuilder::constraint_walk(bConstraint * /*con*/,
}
}
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h
index 8c0e486ec04..256fa3450a6 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h
@@ -64,7 +64,8 @@ struct bNodeTree;
struct bPoseChannel;
struct bSound;
-namespace DEG {
+namespace blender {
+namespace deg {
struct ComponentNode;
struct Depsgraph;
@@ -168,6 +169,9 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder {
virtual void build_object_proxy_from(Object *object, bool is_object_visible);
virtual void build_object_proxy_group(Object *object, bool is_object_visible);
virtual void build_object_instance_collection(Object *object, bool is_object_visible);
+ virtual void build_object_from_layer(int base_index,
+ Object *object,
+ eDepsNode_LinkedState_Type linked_state);
virtual void build_object_flags(int base_index,
Object *object,
eDepsNode_LinkedState_Type linked_state);
@@ -200,7 +204,7 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder {
virtual void build_ik_pose(Object *object, bPoseChannel *pchan, bConstraint *con);
virtual void build_splineik_pose(Object *object, bPoseChannel *pchan, bConstraint *con);
virtual void build_rig(Object *object, bool is_object_visible);
- virtual void build_proxy_rig(Object *object);
+ virtual void build_proxy_rig(Object *object, bool is_object_visible);
virtual void build_armature(bArmature *armature);
virtual void build_armature_bones(ListBase *bones);
virtual void build_shapekeys(Key *key);
@@ -287,4 +291,5 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder {
BuilderMap built_map_;
};
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc
index ab0a5c13321..62282ac4d37 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc
@@ -54,7 +54,8 @@
#include "intern/node/deg_node_component.h"
#include "intern/node/deg_node_operation.h"
-namespace DEG {
+namespace blender {
+namespace deg {
void DepsgraphNodeBuilder::build_pose_constraints(Object *object,
bPoseChannel *pchan,
@@ -283,7 +284,7 @@ void DepsgraphNodeBuilder::build_rig(Object *object, bool is_object_visible)
}
}
-void DepsgraphNodeBuilder::build_proxy_rig(Object *object)
+void DepsgraphNodeBuilder::build_proxy_rig(Object *object, bool is_object_visible)
{
bArmature *armature = (bArmature *)object->data;
OperationNode *op_node;
@@ -326,6 +327,11 @@ void DepsgraphNodeBuilder::build_proxy_rig(Object *object)
&object->id, NodeType::PARAMETERS, OperationCode::PARAMETERS_EVAL, nullptr, pchan->name);
}
+ /* Custom shape. */
+ if (pchan->custom != nullptr) {
+ build_object(-1, pchan->custom, DEG_ID_LINKED_INDIRECTLY, is_object_visible);
+ }
+
pchan_index++;
}
op_node = add_operation_node(&object->id,
@@ -339,4 +345,5 @@ void DepsgraphNodeBuilder::build_proxy_rig(Object *object)
op_node->set_as_exit();
}
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes_scene.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes_scene.cc
index 60e843f9124..ee40fa3f5c8 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes_scene.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes_scene.cc
@@ -25,7 +25,8 @@
#include "DNA_scene_types.h"
-namespace DEG {
+namespace blender {
+namespace deg {
void DepsgraphNodeBuilder::build_scene_render(Scene *scene, ViewLayer *view_layer)
{
@@ -83,4 +84,5 @@ void DepsgraphNodeBuilder::build_scene_compositor(Scene *scene)
build_nodetree(scene->nodetree);
}
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc
index 58cfb36b4ab..1d6712d16ef 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc
@@ -55,7 +55,8 @@
#include "intern/node/deg_node_component.h"
#include "intern/node/deg_node_operation.h"
-namespace DEG {
+namespace blender {
+namespace deg {
void DepsgraphNodeBuilder::build_layer_collections(ListBase *lb)
{
@@ -174,4 +175,5 @@ void DepsgraphNodeBuilder::build_view_layer(Scene *scene,
}
}
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.cc b/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.cc
index a0179181866..924088cbc13 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.cc
@@ -28,7 +28,8 @@
#include "BLI_utildefines.h"
-namespace DEG {
+namespace blender {
+namespace deg {
RootPChanMap::RootPChanMap()
{
@@ -77,4 +78,5 @@ bool RootPChanMap::has_common_root(const char *bone1, const char *bone2) const
return Set<StringRefNull>::Intersects(*bone1_roots, *bone2_roots);
}
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.h b/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.h
index df8b295f5bb..7a6ea38a0f0 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.h
@@ -25,7 +25,8 @@
#include "intern/depsgraph_type.h"
-namespace DEG {
+namespace blender {
+namespace deg {
struct RootPChanMap {
/* Constructor and destructor - Create and free the internal map respectively. */
@@ -49,4 +50,5 @@ struct RootPChanMap {
Map<StringRefNull, Set<StringRefNull>> map_;
};
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
index 92dff751b8a..50c52a519b4 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
@@ -118,7 +118,8 @@
#include "intern/depsgraph_relation.h"
#include "intern/depsgraph_type.h"
-namespace DEG {
+namespace blender {
+namespace deg {
/* ***************** */
/* Relations Builder */
@@ -321,7 +322,7 @@ void DepsgraphRelationBuilder::add_customdata_mask(Object *object,
{
if (customdata_masks != DEGCustomDataMeshMasks() && object != nullptr &&
object->type == OB_MESH) {
- DEG::IDNode *id_node = graph_->find_id_node(&object->id);
+ IDNode *id_node = graph_->find_id_node(&object->id);
if (id_node == nullptr) {
BLI_assert(!"ID should always be valid");
@@ -334,7 +335,7 @@ void DepsgraphRelationBuilder::add_customdata_mask(Object *object,
void DepsgraphRelationBuilder::add_special_eval_flag(ID *id, uint32_t flag)
{
- DEG::IDNode *id_node = graph_->find_id_node(id);
+ IDNode *id_node = graph_->find_id_node(id);
if (id_node == nullptr) {
BLI_assert(!"ID should always be valid");
}
@@ -496,7 +497,7 @@ void DepsgraphRelationBuilder::build_id(ID *id)
build_collection(nullptr, nullptr, (Collection *)id);
break;
case ID_OB:
- build_object(nullptr, (Object *)id);
+ build_object((Object *)id);
break;
case ID_KE:
build_shapekeys((Key *)id);
@@ -598,7 +599,7 @@ void DepsgraphRelationBuilder::build_collection(LayerCollection *from_layer_coll
ComponentKey duplicator_key(object != nullptr ? &object->id : nullptr, NodeType::DUPLI);
if (!group_done) {
LISTBASE_FOREACH (CollectionObject *, cob, &collection->gobject) {
- build_object(nullptr, cob->ob);
+ build_object(cob->ob);
}
LISTBASE_FOREACH (CollectionChild *, child, &collection->children) {
build_collection(nullptr, nullptr, child->collection);
@@ -621,12 +622,9 @@ void DepsgraphRelationBuilder::build_collection(LayerCollection *from_layer_coll
}
}
-void DepsgraphRelationBuilder::build_object(Base *base, Object *object)
+void DepsgraphRelationBuilder::build_object(Object *object)
{
if (built_map_.checkIsBuiltAndTag(object)) {
- if (base != nullptr) {
- build_object_flags(base, object);
- }
return;
}
/* Object Transforms */
@@ -644,11 +642,11 @@ void DepsgraphRelationBuilder::build_object(Base *base, Object *object)
OperationKey ob_eval_key(&object->id, NodeType::TRANSFORM, OperationCode::TRANSFORM_EVAL);
add_relation(init_transform_key, local_transform_key, "Transform Init");
/* Various flags, flushing from bases/collections. */
- build_object_flags(base, object);
+ build_object_from_layer_relations(object);
/* Parenting. */
if (object->parent != nullptr) {
/* Make sure parent object's relations are built. */
- build_object(nullptr, object->parent);
+ build_object(object->parent);
/* Parent relationship. */
build_object_parent(object);
/* Local -> parent. */
@@ -736,7 +734,7 @@ void DepsgraphRelationBuilder::build_object_proxy_from(Object *object)
return;
}
/* Object is linked here (comes from the library). */
- build_object(nullptr, object->proxy_from);
+ build_object(object->proxy_from);
ComponentKey ob_transform_key(&object->proxy_from->id, NodeType::TRANSFORM);
ComponentKey proxy_transform_key(&object->id, NodeType::TRANSFORM);
add_relation(ob_transform_key, proxy_transform_key, "Proxy Transform");
@@ -748,27 +746,40 @@ void DepsgraphRelationBuilder::build_object_proxy_group(Object *object)
return;
}
/* Object is local here (local in .blend file, users interacts with it). */
- build_object(nullptr, object->proxy_group);
+ build_object(object->proxy_group);
OperationKey proxy_group_eval_key(
&object->proxy_group->id, NodeType::TRANSFORM, OperationCode::TRANSFORM_EVAL);
OperationKey transform_eval_key(&object->id, NodeType::TRANSFORM, OperationCode::TRANSFORM_EVAL);
add_relation(proxy_group_eval_key, transform_eval_key, "Proxy Group Transform");
}
-void DepsgraphRelationBuilder::build_object_flags(Base *base, Object *object)
+void DepsgraphRelationBuilder::build_object_from_layer_relations(Object *object)
{
- if (base == nullptr) {
- return;
- }
- OperationKey view_layer_done_key(
- &scene_->id, NodeType::LAYER_COLLECTIONS, OperationCode::VIEW_LAYER_EVAL);
+ OperationKey object_from_layer_entry_key(
+ &object->id, NodeType::OBJECT_FROM_LAYER, OperationCode::OBJECT_FROM_LAYER_ENTRY);
+ OperationKey object_from_layer_exit_key(
+ &object->id, NodeType::OBJECT_FROM_LAYER, OperationCode::OBJECT_FROM_LAYER_EXIT);
OperationKey object_flags_key(
&object->id, NodeType::OBJECT_FROM_LAYER, OperationCode::OBJECT_BASE_FLAGS);
- add_relation(view_layer_done_key, object_flags_key, "Base flags flush");
+
+ if (!has_node(object_flags_key)) {
+ /* Just connect Entry -> Exit if there is no OBJECT_BASE_FLAGS node. */
+ add_relation(object_from_layer_entry_key, object_from_layer_exit_key, "Object from Layer");
+ return;
+ }
+
+ /* Entry -> OBJECT_BASE_FLAGS -> Exit */
+ add_relation(object_from_layer_entry_key, object_flags_key, "Base flags flush Entry");
+ add_relation(object_flags_key, object_from_layer_exit_key, "Base flags flush Exit");
+
/* Synchronization back to original object. */
OperationKey synchronize_key(
&object->id, NodeType::SYNCHRONIZATION, OperationCode::SYNCHRONIZE_TO_ORIGINAL);
- add_relation(object_flags_key, synchronize_key, "Synchronize to Original");
+ add_relation(object_from_layer_exit_key, synchronize_key, "Synchronize to Original");
+
+ OperationKey view_layer_done_key(
+ &scene_->id, NodeType::LAYER_COLLECTIONS, OperationCode::VIEW_LAYER_EVAL);
+ add_relation(view_layer_done_key, object_from_layer_entry_key, "View Layer flags to Object");
}
void DepsgraphRelationBuilder::build_object_data(Object *object)
@@ -1729,9 +1740,9 @@ void DepsgraphRelationBuilder::build_rigidbody(Scene *scene)
continue;
}
/* Make sure indirectly linked objects are fully built. */
- build_object(nullptr, object);
- build_object(nullptr, rbc->ob1);
- build_object(nullptr, rbc->ob2);
+ build_object(object);
+ build_object(rbc->ob1);
+ build_object(rbc->ob2);
/* final result of the constraint object's transform controls how
* the constraint affects the physics sim for these objects. */
ComponentKey trans_key(&object->id, NodeType::TRANSFORM);
@@ -1820,7 +1831,7 @@ void DepsgraphRelationBuilder::build_particle_systems(Object *object)
continue;
}
/* Make sure target object is pulled into the graph. */
- build_object(nullptr, particle_target->ob);
+ build_object(particle_target->ob);
/* Use geometry component, since that's where particles are
* actually evaluated. */
ComponentKey target_key(&particle_target->ob->id, NodeType::GEOMETRY);
@@ -1832,7 +1843,7 @@ void DepsgraphRelationBuilder::build_particle_systems(Object *object)
case PART_DRAW_OB:
if (part->instance_object != nullptr) {
/* Make sure object's relations are all built. */
- build_object(nullptr, part->instance_object);
+ build_object(part->instance_object);
/* Build relation for the particle visualization. */
build_particle_system_visualization_object(object, psys, part->instance_object);
}
@@ -2123,19 +2134,19 @@ void DepsgraphRelationBuilder::build_object_data_geometry_datablock(ID *obdata)
add_relation(bevob_geom_key, obdata_geom_eval_key, "Curve Bevel Geometry");
ComponentKey bevob_key(&cu->bevobj->id, NodeType::TRANSFORM);
add_relation(bevob_key, obdata_geom_eval_key, "Curve Bevel Transform");
- build_object(nullptr, cu->bevobj);
+ build_object(cu->bevobj);
}
if (cu->taperobj != nullptr) {
ComponentKey taperob_key(&cu->taperobj->id, NodeType::GEOMETRY);
add_relation(taperob_key, obdata_geom_eval_key, "Curve Taper");
- build_object(nullptr, cu->taperobj);
+ build_object(cu->taperobj);
}
if (cu->textoncurve != nullptr) {
ComponentKey textoncurve_geom_key(&cu->textoncurve->id, NodeType::GEOMETRY);
add_relation(textoncurve_geom_key, obdata_geom_eval_key, "Text on Curve Geometry");
ComponentKey textoncurve_key(&cu->textoncurve->id, NodeType::TRANSFORM);
add_relation(textoncurve_key, obdata_geom_eval_key, "Text on Curve Transform");
- build_object(nullptr, cu->textoncurve);
+ build_object(cu->textoncurve);
}
break;
}
@@ -2232,7 +2243,7 @@ void DepsgraphRelationBuilder::build_camera(Camera *camera)
build_animdata(&camera->id);
build_parameters(&camera->id);
if (camera->dof.focus_object != nullptr) {
- build_object(nullptr, camera->dof.focus_object);
+ build_object(camera->dof.focus_object);
ComponentKey camera_parameters_key(&camera->id, NodeType::PARAMETERS);
ComponentKey dof_ob_key(&camera->dof.focus_object->id, NodeType::TRANSFORM);
add_relation(dof_ob_key, camera_parameters_key, "Camera DOF");
@@ -2307,7 +2318,7 @@ void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree)
add_relation(image_key, shading_key, "Image -> Node");
}
else if (id_type == ID_OB) {
- build_object(nullptr, (Object *)id);
+ build_object((Object *)id);
ComponentKey object_transform_key(id, NodeType::TRANSFORM);
add_relation(object_transform_key, shading_key, "Object Transform -> Node");
if (object_have_geometry_component(reinterpret_cast<Object *>(id))) {
@@ -2323,7 +2334,7 @@ void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree)
* On the one hand it's annoying to always pull it in, but on another hand it's also annoying
* to have hardcoded node-type exception here. */
if (node_scene->camera != nullptr) {
- build_object(nullptr, node_scene->camera);
+ build_object(node_scene->camera);
}
}
else if (id_type == ID_TXT) {
@@ -2661,7 +2672,7 @@ void DepsgraphRelationBuilder::build_scene_speakers(Scene * /*scene*/, ViewLayer
if (object->type != OB_SPEAKER || !need_pull_base_into_graph(base)) {
continue;
}
- build_object(nullptr, base->object);
+ build_object(base->object);
}
}
@@ -2887,9 +2898,7 @@ void DepsgraphRelationBuilder::build_driver_relations(IDNode *id_node)
}
// Mapping from RNA prefix -> set of driver evaluation nodes:
- typedef Vector<Node *> DriverGroup;
- typedef map<string, DriverGroup> DriverGroupMap;
- DriverGroupMap driver_groups;
+ Map<string, Vector<Node *>> driver_groups;
LISTBASE_FOREACH (FCurve *, fcu, &adt->drivers) {
if (fcu->rna_path == nullptr) {
@@ -2897,33 +2906,33 @@ void DepsgraphRelationBuilder::build_driver_relations(IDNode *id_node)
}
// Get the RNA path except the part after the last dot.
char *last_dot = strrchr(fcu->rna_path, '.');
- string rna_prefix;
+ StringRef rna_prefix;
if (last_dot != nullptr) {
- rna_prefix = string(fcu->rna_path, last_dot);
+ rna_prefix = StringRef(fcu->rna_path, last_dot);
}
// Insert this driver node into the group belonging to the RNA prefix.
OperationKey driver_key(
id_orig, NodeType::PARAMETERS, OperationCode::DRIVER, fcu->rna_path, fcu->array_index);
Node *node_driver = get_node(driver_key);
- driver_groups[rna_prefix].append(node_driver);
+ driver_groups.lookup_or_add_default_as(rna_prefix).append(node_driver);
}
- for (pair<string, DriverGroup> prefix_group : driver_groups) {
+ for (Span<Node *> prefix_group : driver_groups.values()) {
// For each node in the driver group, try to connect it to another node
// in the same group without creating any cycles.
- int num_drivers = prefix_group.second.size();
+ int num_drivers = prefix_group.size();
if (num_drivers < 2) {
// A relation requires two drivers.
continue;
}
for (int from_index = 0; from_index < num_drivers; ++from_index) {
- Node *op_from = prefix_group.second[from_index];
+ Node *op_from = prefix_group[from_index];
// Start by trying the next node in the group.
for (int to_offset = 1; to_offset < num_drivers; ++to_offset) {
int to_index = (from_index + to_offset) % num_drivers;
- Node *op_to = prefix_group.second[to_index];
+ Node *op_to = prefix_group[to_index];
// Investigate whether this relation would create a dependency cycle.
// Example graph:
@@ -2975,4 +2984,5 @@ void DepsgraphRelationBuilder::constraint_walk(bConstraint * /*con*/,
data->builder->build_id(id);
}
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.h b/source/blender/depsgraph/intern/builder/deg_builder_relations.h
index aa6d8ababd3..b4b0dc71f85 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.h
@@ -84,7 +84,8 @@ struct bSound;
struct PropertyRNA;
-namespace DEG {
+namespace blender {
+namespace deg {
struct ComponentNode;
struct DepsNodeHandle;
@@ -210,10 +211,10 @@ class DepsgraphRelationBuilder : public DepsgraphBuilder {
virtual void build_collection(LayerCollection *from_layer_collection,
Object *object,
Collection *collection);
- virtual void build_object(Base *base, Object *object);
+ virtual void build_object(Object *object);
virtual void build_object_proxy_from(Object *object);
virtual void build_object_proxy_group(Object *object);
- virtual void build_object_flags(Base *base, Object *object);
+ virtual void build_object_from_layer_relations(Object *object);
virtual void build_object_data(Object *object);
virtual void build_object_data_camera(Object *object);
virtual void build_object_data_geometry(Object *object);
@@ -385,6 +386,7 @@ struct DepsNodeHandle {
const char *default_name;
};
-} // namespace DEG
+} // namespace deg
+} // namespace blender
#include "intern/builder/deg_builder_relations_impl.h"
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_impl.h b/source/blender/depsgraph/intern/builder/deg_builder_relations_impl.h
index 5983627fafc..d4c28060878 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations_impl.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_impl.h
@@ -28,7 +28,8 @@
#include "DNA_ID.h"
#include "DNA_object_types.h"
-namespace DEG {
+namespace blender {
+namespace deg {
template<typename KeyType>
OperationNode *DepsgraphRelationBuilder::find_operation_node(const KeyType &key)
@@ -215,4 +216,5 @@ bool DepsgraphRelationBuilder::is_same_nodetree_node_dependency(const KeyFrom &k
return true;
}
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_keys.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations_keys.cc
index 6c449900f03..2220ba6ee13 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations_keys.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_keys.cc
@@ -25,7 +25,8 @@
#include "intern/builder/deg_builder_relations.h"
-namespace DEG {
+namespace blender {
+namespace deg {
////////////////////////////////////////////////////////////////////////////////
// Time source.
@@ -192,4 +193,5 @@ string RNAPathKey::identifier() const
return string("RnaPathKey(") + "id: " + id_name + ", prop: '" + prop_name + "')";
}
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc
index fe9adecbf0a..b695c43402f 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc
@@ -58,7 +58,8 @@
#include "intern/depsgraph_relation.h"
#include "intern/depsgraph_type.h"
-namespace DEG {
+namespace blender {
+namespace deg {
/* IK Solver Eval Steps */
void DepsgraphRelationBuilder::build_ik_pose(Object *object,
@@ -449,7 +450,7 @@ void DepsgraphRelationBuilder::build_rig(Object *object)
}
/* Custom shape. */
if (pchan->custom != nullptr) {
- build_object(nullptr, pchan->custom);
+ build_object(pchan->custom);
}
}
}
@@ -509,4 +510,5 @@ void DepsgraphRelationBuilder::build_proxy_rig(Object *object)
}
}
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_scene.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations_scene.cc
index 410df839875..6ebf5210b63 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations_scene.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_scene.cc
@@ -25,7 +25,8 @@
#include "DNA_scene_types.h"
-namespace DEG {
+namespace blender {
+namespace deg {
void DepsgraphRelationBuilder::build_scene_render(Scene *scene, ViewLayer *view_layer)
{
@@ -43,7 +44,7 @@ void DepsgraphRelationBuilder::build_scene_render(Scene *scene, ViewLayer *view_
build_scene_speakers(scene, view_layer);
}
if (scene->camera != nullptr) {
- build_object(nullptr, scene->camera);
+ build_object(scene->camera);
}
}
@@ -71,4 +72,5 @@ void DepsgraphRelationBuilder::build_scene_compositor(Scene *scene)
build_nodetree(scene->nodetree);
}
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc
index e132ba30e67..6e64eba60dc 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc
@@ -56,7 +56,8 @@
#include "intern/depsgraph_type.h"
-namespace DEG {
+namespace blender {
+namespace deg {
void DepsgraphRelationBuilder::build_layer_collections(ListBase *lb)
{
@@ -96,14 +97,14 @@ void DepsgraphRelationBuilder::build_view_layer(Scene *scene,
* do nullptr-pointer check of the base, so it's fine to pass original one. */
LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
if (need_pull_base_into_graph(base)) {
- build_object(base, base->object);
+ build_object(base->object);
}
}
build_layer_collections(&view_layer->layer_collections);
if (scene->camera != nullptr) {
- build_object(nullptr, scene->camera);
+ build_object(scene->camera);
}
/* Rigidbody. */
if (scene->rigidbody_world != nullptr) {
@@ -153,4 +154,5 @@ void DepsgraphRelationBuilder::build_view_layer(Scene *scene,
}
}
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_remove_noop.cc b/source/blender/depsgraph/intern/builder/deg_builder_remove_noop.cc
index c6545362bb1..a9b405cfb27 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_remove_noop.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_remove_noop.cc
@@ -33,7 +33,8 @@
#include "intern/depsgraph_relation.h"
#include "intern/depsgraph_type.h"
-namespace DEG {
+namespace blender {
+namespace deg {
static inline bool is_unused_noop(OperationNode *op_node)
{
@@ -84,4 +85,5 @@ void deg_graph_remove_unused_noops(Depsgraph *graph)
(::Depsgraph *)graph, BUILD, "Removed %d relations to no-op nodes\n", num_removed_relations);
}
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_remove_noop.h b/source/blender/depsgraph/intern/builder/deg_builder_remove_noop.h
index e7a2e90cf7c..e30e90af9c0 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_remove_noop.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_remove_noop.h
@@ -23,11 +23,13 @@
#pragma once
-namespace DEG {
+namespace blender {
+namespace deg {
struct Depsgraph;
/* Remove all no-op nodes that have zero outgoing relations. */
void deg_graph_remove_unused_noops(Depsgraph *graph);
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_rna.cc b/source/blender/depsgraph/intern/builder/deg_builder_rna.cc
index fbd53bf46f8..98ae653d294 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_rna.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_rna.cc
@@ -48,7 +48,8 @@
#include "intern/node/deg_node_id.h"
#include "intern/node/deg_node_operation.h"
-namespace DEG {
+namespace blender {
+namespace deg {
/* ********************************* ID Data ******************************** */
@@ -365,8 +366,9 @@ RNANodeIdentifier RNANodeQuery::construct_node_identifier(const PointerRNA *ptr,
RNANodeQueryIDData *RNANodeQuery::ensure_id_data(const ID *id)
{
unique_ptr<RNANodeQueryIDData> &id_data = id_data_map_.lookup_or_add_cb(
- id, [&]() { return blender::make_unique<RNANodeQueryIDData>(id); });
+ id, [&]() { return std::make_unique<RNANodeQueryIDData>(id); });
return id_data.get();
}
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_rna.h b/source/blender/depsgraph/intern/builder/deg_builder_rna.h
index bd806ce058a..52d0e5f6b12 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_rna.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_rna.h
@@ -30,7 +30,8 @@ struct ID;
struct PointerRNA;
struct PropertyRNA;
-namespace DEG {
+namespace blender {
+namespace deg {
struct Depsgraph;
struct Node;
@@ -94,4 +95,5 @@ class RNANodeQuery {
RNANodeQueryIDData *ensure_id_data(const ID *id);
};
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_transitive.cc b/source/blender/depsgraph/intern/builder/deg_builder_transitive.cc
index 22ceac899b8..8ffb20a9999 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_transitive.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_transitive.cc
@@ -33,7 +33,8 @@
#include "intern/depsgraph.h"
#include "intern/depsgraph_relation.h"
-namespace DEG {
+namespace blender {
+namespace deg {
/* -------------------------------------------------- */
@@ -109,4 +110,5 @@ void deg_graph_transitive_reduction(Depsgraph *graph)
DEG_DEBUG_PRINTF((::Depsgraph *)graph, BUILD, "Removed %d relations\n", num_removed_relations);
}
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_transitive.h b/source/blender/depsgraph/intern/builder/deg_builder_transitive.h
index 43068886809..3f083381f83 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_transitive.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_transitive.h
@@ -23,11 +23,13 @@
#pragma once
-namespace DEG {
+namespace blender {
+namespace deg {
struct Depsgraph;
/* Performs a transitive reduction to remove redundant relations. */
void deg_graph_transitive_reduction(Depsgraph *graph);
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/debug/deg_debug.cc b/source/blender/depsgraph/intern/debug/deg_debug.cc
index ca27cef2c56..ab6adea6416 100644
--- a/source/blender/depsgraph/intern/debug/deg_debug.cc
+++ b/source/blender/depsgraph/intern/debug/deg_debug.cc
@@ -32,7 +32,8 @@
#include "BKE_global.h"
-namespace DEG {
+namespace blender {
+namespace deg {
DepsgraphDebug::DepsgraphDebug()
: flags(G.debug), is_ever_evaluated(false), graph_evaluation_start_time_(0)
@@ -97,4 +98,5 @@ string color_end(void)
return string(TRUECOLOR_ANSI_COLOR_FINISH);
}
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/debug/deg_debug.h b/source/blender/depsgraph/intern/debug/deg_debug.h
index f5e2f2c4b51..043cdc85333 100644
--- a/source/blender/depsgraph/intern/debug/deg_debug.h
+++ b/source/blender/depsgraph/intern/debug/deg_debug.h
@@ -30,7 +30,8 @@
#include "DEG_depsgraph_debug.h"
-namespace DEG {
+namespace blender {
+namespace deg {
class DepsgraphDebug {
public:
@@ -89,4 +90,5 @@ bool terminal_do_color(void);
string color_for_pointer(const void *pointer);
string color_end(void);
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc b/source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc
index 1f33bdefb79..458baf4fb1e 100644
--- a/source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc
+++ b/source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc
@@ -40,10 +40,13 @@
#include "intern/node/deg_node_operation.h"
#include "intern/node/deg_node_time.h"
+namespace deg = blender::deg;
+
/* ****************** */
/* Graphviz Debugging */
-namespace DEG {
+namespace blender {
+namespace deg {
#define NL "\r\n"
@@ -578,7 +581,8 @@ static void deg_debug_graphviz_graph_relations(const DebugContext &ctx, const De
}
}
-} // namespace DEG
+} // namespace deg
+} // namespace blender
void DEG_debug_relations_graphviz(const Depsgraph *graph, FILE *f, const char *label)
{
@@ -586,29 +590,29 @@ void DEG_debug_relations_graphviz(const Depsgraph *graph, FILE *f, const char *l
return;
}
- const DEG::Depsgraph *deg_graph = reinterpret_cast<const DEG::Depsgraph *>(graph);
+ const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(graph);
- DEG::DebugContext ctx;
+ deg::DebugContext ctx;
ctx.file = f;
- DEG::deg_debug_fprintf(ctx, "digraph depgraph {" NL);
- DEG::deg_debug_fprintf(ctx, "rankdir=LR;" NL);
- DEG::deg_debug_fprintf(ctx, "graph [");
- DEG::deg_debug_fprintf(ctx, "compound=true");
- DEG::deg_debug_fprintf(ctx, ",labelloc=\"t\"");
- DEG::deg_debug_fprintf(ctx, ",fontsize=%f", DEG::deg_debug_graphviz_graph_label_size);
- DEG::deg_debug_fprintf(ctx, ",fontname=\"%s\"", DEG::deg_debug_graphviz_fontname);
- DEG::deg_debug_fprintf(ctx, ",label=\"%s\"", label);
- DEG::deg_debug_fprintf(ctx, ",splines=ortho");
- DEG::deg_debug_fprintf(ctx, ",overlap=scalexy"); // XXX: only when using neato
- DEG::deg_debug_fprintf(ctx, "];" NL);
+ deg::deg_debug_fprintf(ctx, "digraph depgraph {" NL);
+ deg::deg_debug_fprintf(ctx, "rankdir=LR;" NL);
+ deg::deg_debug_fprintf(ctx, "graph [");
+ deg::deg_debug_fprintf(ctx, "compound=true");
+ deg::deg_debug_fprintf(ctx, ",labelloc=\"t\"");
+ deg::deg_debug_fprintf(ctx, ",fontsize=%f", deg::deg_debug_graphviz_graph_label_size);
+ deg::deg_debug_fprintf(ctx, ",fontname=\"%s\"", deg::deg_debug_graphviz_fontname);
+ deg::deg_debug_fprintf(ctx, ",label=\"%s\"", label);
+ deg::deg_debug_fprintf(ctx, ",splines=ortho");
+ deg::deg_debug_fprintf(ctx, ",overlap=scalexy"); // XXX: only when using neato
+ deg::deg_debug_fprintf(ctx, "];" NL);
- DEG::deg_debug_graphviz_graph_nodes(ctx, deg_graph);
- DEG::deg_debug_graphviz_graph_relations(ctx, deg_graph);
+ deg::deg_debug_graphviz_graph_nodes(ctx, deg_graph);
+ deg::deg_debug_graphviz_graph_relations(ctx, deg_graph);
- DEG::deg_debug_graphviz_legend(ctx);
+ deg::deg_debug_graphviz_legend(ctx);
- DEG::deg_debug_fprintf(ctx, "}" NL);
+ deg::deg_debug_fprintf(ctx, "}" NL);
}
#undef NL
diff --git a/source/blender/depsgraph/intern/debug/deg_debug_stats_gnuplot.cc b/source/blender/depsgraph/intern/debug/deg_debug_stats_gnuplot.cc
index 9e751093ae2..78525ba6b69 100644
--- a/source/blender/depsgraph/intern/debug/deg_debug_stats_gnuplot.cc
+++ b/source/blender/depsgraph/intern/debug/deg_debug_stats_gnuplot.cc
@@ -36,7 +36,10 @@
#define NL "\r\n"
-namespace DEG {
+namespace deg = blender::deg;
+
+namespace blender {
+namespace deg {
namespace {
struct DebugContext {
@@ -147,7 +150,8 @@ void deg_debug_stats_gnuplot(const DebugContext &ctx)
}
} // namespace
-} // namespace DEG
+} // namespace deg
+} // namespace blender
void DEG_debug_stats_gnuplot(const Depsgraph *depsgraph,
FILE *f,
@@ -157,10 +161,10 @@ void DEG_debug_stats_gnuplot(const Depsgraph *depsgraph,
if (depsgraph == nullptr) {
return;
}
- DEG::DebugContext ctx;
+ deg::DebugContext ctx;
ctx.file = f;
- ctx.graph = (DEG::Depsgraph *)depsgraph;
+ ctx.graph = (deg::Depsgraph *)depsgraph;
ctx.label = label;
ctx.output_filename = output_filename;
- DEG::deg_debug_stats_gnuplot(ctx);
+ deg::deg_debug_stats_gnuplot(ctx);
}
diff --git a/source/blender/depsgraph/intern/debug/deg_time_average.h b/source/blender/depsgraph/intern/debug/deg_time_average.h
index 9794e9a88c3..838ceff8d96 100644
--- a/source/blender/depsgraph/intern/debug/deg_time_average.h
+++ b/source/blender/depsgraph/intern/debug/deg_time_average.h
@@ -23,7 +23,8 @@
#pragma once
-namespace DEG {
+namespace blender {
+namespace deg {
// Utility class which takes care of calculating average of time series, such as
// FPS counters.
@@ -68,4 +69,5 @@ template<int MaxSamples> class AveragedTimeSampler {
int next_sample_index_;
};
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/depsgraph.cc b/source/blender/depsgraph/intern/depsgraph.cc
index 51ae56ac613..ce2c7110aee 100644
--- a/source/blender/depsgraph/intern/depsgraph.cc
+++ b/source/blender/depsgraph/intern/depsgraph.cc
@@ -55,7 +55,10 @@
#include "intern/node/deg_node_operation.h"
#include "intern/node/deg_node_time.h"
-namespace DEG {
+namespace deg = blender::deg;
+
+namespace blender {
+namespace deg {
Depsgraph::Depsgraph(Main *bmain, Scene *scene, ViewLayer *view_layer, eEvaluationMode mode)
: time_source(nullptr),
@@ -258,7 +261,8 @@ ID *Depsgraph::get_cow_id(const ID *id_orig) const
return id_node->id_cow;
}
-} // namespace DEG
+} // namespace deg
+} // namespace blender
/* **************** */
/* Public Graph API */
@@ -266,9 +270,9 @@ ID *Depsgraph::get_cow_id(const ID *id_orig) const
/* Initialize a new Depsgraph */
Depsgraph *DEG_graph_new(Main *bmain, Scene *scene, ViewLayer *view_layer, eEvaluationMode mode)
{
- DEG::Depsgraph *deg_depsgraph = OBJECT_GUARDED_NEW(
- DEG::Depsgraph, bmain, scene, view_layer, mode);
- DEG::register_graph(deg_depsgraph);
+ deg::Depsgraph *deg_depsgraph = OBJECT_GUARDED_NEW(
+ deg::Depsgraph, bmain, scene, view_layer, mode);
+ deg::register_graph(deg_depsgraph);
return reinterpret_cast<Depsgraph *>(deg_depsgraph);
}
@@ -279,11 +283,11 @@ void DEG_graph_replace_owners(struct Depsgraph *depsgraph,
Scene *scene,
ViewLayer *view_layer)
{
- DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(depsgraph);
+ deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(depsgraph);
const bool do_update_register = deg_graph->bmain != bmain;
if (do_update_register && deg_graph->bmain != NULL) {
- DEG::unregister_graph(deg_graph);
+ deg::unregister_graph(deg_graph);
}
deg_graph->bmain = bmain;
@@ -291,7 +295,7 @@ void DEG_graph_replace_owners(struct Depsgraph *depsgraph,
deg_graph->view_layer = view_layer;
if (do_update_register) {
- DEG::register_graph(deg_graph);
+ deg::register_graph(deg_graph);
}
}
@@ -301,15 +305,15 @@ void DEG_graph_free(Depsgraph *graph)
if (graph == nullptr) {
return;
}
- using DEG::Depsgraph;
- DEG::Depsgraph *deg_depsgraph = reinterpret_cast<DEG::Depsgraph *>(graph);
- DEG::unregister_graph(deg_depsgraph);
+ using deg::Depsgraph;
+ deg::Depsgraph *deg_depsgraph = reinterpret_cast<deg::Depsgraph *>(graph);
+ deg::unregister_graph(deg_depsgraph);
OBJECT_GUARDED_DELETE(deg_depsgraph, Depsgraph);
}
bool DEG_is_evaluating(const struct Depsgraph *depsgraph)
{
- const DEG::Depsgraph *deg_graph = reinterpret_cast<const DEG::Depsgraph *>(depsgraph);
+ const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(depsgraph);
return deg_graph->is_evaluating;
}
@@ -322,19 +326,19 @@ bool DEG_is_active(const struct Depsgraph *depsgraph)
* cases. */
return false;
}
- const DEG::Depsgraph *deg_graph = reinterpret_cast<const DEG::Depsgraph *>(depsgraph);
+ const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(depsgraph);
return deg_graph->is_active;
}
void DEG_make_active(struct Depsgraph *depsgraph)
{
- DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(depsgraph);
+ deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(depsgraph);
deg_graph->is_active = true;
/* TODO(sergey): Copy data from evaluated state to original. */
}
void DEG_make_inactive(struct Depsgraph *depsgraph)
{
- DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(depsgraph);
+ deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(depsgraph);
deg_graph->is_active = false;
}
diff --git a/source/blender/depsgraph/intern/depsgraph.h b/source/blender/depsgraph/intern/depsgraph.h
index e0686bd04aa..9dea518b89c 100644
--- a/source/blender/depsgraph/intern/depsgraph.h
+++ b/source/blender/depsgraph/intern/depsgraph.h
@@ -47,7 +47,8 @@ struct ID;
struct Scene;
struct ViewLayer;
-namespace DEG {
+namespace blender {
+namespace deg {
struct IDNode;
struct Node;
@@ -169,4 +170,5 @@ struct Depsgraph {
Map<const ID *, ListBase *> *physics_relations[DEG_PHYSICS_RELATIONS_NUM];
};
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/depsgraph_build.cc b/source/blender/depsgraph/intern/depsgraph_build.cc
index cdaf68cb826..c11d051e663 100644
--- a/source/blender/depsgraph/intern/depsgraph_build.cc
+++ b/source/blender/depsgraph/intern/depsgraph_build.cc
@@ -64,22 +64,24 @@
/* ****************** */
/* External Build API */
-static DEG::NodeType deg_build_scene_component_type(eDepsSceneComponentType component)
+namespace deg = blender::deg;
+
+static deg::NodeType deg_build_scene_component_type(eDepsSceneComponentType component)
{
switch (component) {
case DEG_SCENE_COMP_PARAMETERS:
- return DEG::NodeType::PARAMETERS;
+ return deg::NodeType::PARAMETERS;
case DEG_SCENE_COMP_ANIMATION:
- return DEG::NodeType::ANIMATION;
+ return deg::NodeType::ANIMATION;
case DEG_SCENE_COMP_SEQUENCER:
- return DEG::NodeType::SEQUENCER;
+ return deg::NodeType::SEQUENCER;
}
- return DEG::NodeType::UNDEFINED;
+ return deg::NodeType::UNDEFINED;
}
-static DEG::DepsNodeHandle *get_node_handle(DepsNodeHandle *node_handle)
+static deg::DepsNodeHandle *get_node_handle(DepsNodeHandle *node_handle)
{
- return reinterpret_cast<DEG::DepsNodeHandle *>(node_handle);
+ return reinterpret_cast<deg::DepsNodeHandle *>(node_handle);
}
void DEG_add_scene_relation(DepsNodeHandle *node_handle,
@@ -87,9 +89,9 @@ void DEG_add_scene_relation(DepsNodeHandle *node_handle,
eDepsSceneComponentType component,
const char *description)
{
- DEG::NodeType type = deg_build_scene_component_type(component);
- DEG::ComponentKey comp_key(&scene->id, type);
- DEG::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle);
+ deg::NodeType type = deg_build_scene_component_type(component);
+ deg::ComponentKey comp_key(&scene->id, type);
+ deg::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle);
deg_node_handle->builder->add_node_handle_relation(comp_key, deg_node_handle, description);
}
@@ -98,9 +100,9 @@ void DEG_add_object_relation(DepsNodeHandle *node_handle,
eDepsObjectComponentType component,
const char *description)
{
- DEG::NodeType type = DEG::nodeTypeFromObjectComponent(component);
- DEG::ComponentKey comp_key(&object->id, type);
- DEG::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle);
+ deg::NodeType type = deg::nodeTypeFromObjectComponent(component);
+ deg::ComponentKey comp_key(&object->id, type);
+ deg::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle);
deg_node_handle->builder->add_node_handle_relation(comp_key, deg_node_handle, description);
}
@@ -108,9 +110,9 @@ void DEG_add_simulation_relation(DepsNodeHandle *node_handle,
Simulation *simulation,
const char *description)
{
- DEG::OperationKey operation_key(
- &simulation->id, DEG::NodeType::SIMULATION, DEG::OperationCode::SIMULATION_EVAL);
- DEG::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle);
+ deg::OperationKey operation_key(
+ &simulation->id, deg::NodeType::SIMULATION, deg::OperationCode::SIMULATION_EVAL);
+ deg::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle);
deg_node_handle->builder->add_node_handle_relation(operation_key, deg_node_handle, description);
}
@@ -119,9 +121,9 @@ void DEG_add_object_cache_relation(DepsNodeHandle *node_handle,
eDepsObjectComponentType component,
const char *description)
{
- DEG::NodeType type = DEG::nodeTypeFromObjectComponent(component);
- DEG::ComponentKey comp_key(&cache_file->id, type);
- DEG::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle);
+ deg::NodeType type = deg::nodeTypeFromObjectComponent(component);
+ deg::ComponentKey comp_key(&cache_file->id, type);
+ deg::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle);
deg_node_handle->builder->add_node_handle_relation(comp_key, deg_node_handle, description);
}
@@ -131,9 +133,9 @@ void DEG_add_bone_relation(DepsNodeHandle *node_handle,
eDepsObjectComponentType component,
const char *description)
{
- DEG::NodeType type = DEG::nodeTypeFromObjectComponent(component);
- DEG::ComponentKey comp_key(&object->id, type, bone_name);
- DEG::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle);
+ deg::NodeType type = deg::nodeTypeFromObjectComponent(component);
+ deg::ComponentKey comp_key(&object->id, type, bone_name);
+ deg::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle);
deg_node_handle->builder->add_node_handle_relation(comp_key, deg_node_handle, description);
}
@@ -142,18 +144,18 @@ void DEG_add_object_pointcache_relation(struct DepsNodeHandle *node_handle,
eDepsObjectComponentType component,
const char *description)
{
- DEG::NodeType type = DEG::nodeTypeFromObjectComponent(component);
- DEG::ComponentKey comp_key(&object->id, type);
- DEG::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle);
- DEG::DepsgraphRelationBuilder *relation_builder = deg_node_handle->builder;
+ deg::NodeType type = deg::nodeTypeFromObjectComponent(component);
+ deg::ComponentKey comp_key(&object->id, type);
+ deg::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle);
+ deg::DepsgraphRelationBuilder *relation_builder = deg_node_handle->builder;
/* Add relation from source to the node handle. */
relation_builder->add_node_handle_relation(comp_key, deg_node_handle, description);
/* Node deduct point cache component and connect source to it. */
ID *id = DEG_get_id_from_handle(node_handle);
- DEG::ComponentKey point_cache_key(id, DEG::NodeType::POINT_CACHE);
- DEG::Relation *rel = relation_builder->add_relation(comp_key, point_cache_key, "Point Cache");
+ deg::ComponentKey point_cache_key(id, deg::NodeType::POINT_CACHE);
+ deg::Relation *rel = relation_builder->add_relation(comp_key, point_cache_key, "Point Cache");
if (rel != nullptr) {
- rel->flag |= DEG::RELATION_FLAG_FLUSH_USER_EDIT_ONLY;
+ rel->flag |= deg::RELATION_FLAG_FLUSH_USER_EDIT_ONLY;
}
else {
fprintf(stderr, "Error in point cache relation from %s to ^%s.\n", object->id.name, id->name);
@@ -164,22 +166,22 @@ void DEG_add_generic_id_relation(struct DepsNodeHandle *node_handle,
struct ID *id,
const char *description)
{
- DEG::OperationKey operation_key(
- id, DEG::NodeType::GENERIC_DATABLOCK, DEG::OperationCode::GENERIC_DATABLOCK_UPDATE);
- DEG::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle);
+ deg::OperationKey operation_key(
+ id, deg::NodeType::GENERIC_DATABLOCK, deg::OperationCode::GENERIC_DATABLOCK_UPDATE);
+ deg::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle);
deg_node_handle->builder->add_node_handle_relation(operation_key, deg_node_handle, description);
}
void DEG_add_modifier_to_transform_relation(struct DepsNodeHandle *node_handle,
const char *description)
{
- DEG::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle);
+ deg::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle);
deg_node_handle->builder->add_modifier_to_transform_relation(deg_node_handle, description);
}
void DEG_add_special_eval_flag(struct DepsNodeHandle *node_handle, ID *id, uint32_t flag)
{
- DEG::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle);
+ deg::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle);
deg_node_handle->builder->add_special_eval_flag(id, flag);
}
@@ -187,41 +189,41 @@ void DEG_add_customdata_mask(struct DepsNodeHandle *node_handle,
struct Object *object,
const CustomData_MeshMasks *masks)
{
- DEG::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle);
- deg_node_handle->builder->add_customdata_mask(object, DEG::DEGCustomDataMeshMasks(masks));
+ deg::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle);
+ deg_node_handle->builder->add_customdata_mask(object, deg::DEGCustomDataMeshMasks(masks));
}
struct ID *DEG_get_id_from_handle(struct DepsNodeHandle *node_handle)
{
- DEG::DepsNodeHandle *deg_handle = get_node_handle(node_handle);
+ deg::DepsNodeHandle *deg_handle = get_node_handle(node_handle);
return deg_handle->node->owner->owner->id_orig;
}
struct Depsgraph *DEG_get_graph_from_handle(struct DepsNodeHandle *node_handle)
{
- DEG::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle);
- DEG::DepsgraphRelationBuilder *relation_builder = deg_node_handle->builder;
+ deg::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle);
+ deg::DepsgraphRelationBuilder *relation_builder = deg_node_handle->builder;
return reinterpret_cast<Depsgraph *>(relation_builder->getGraph());
}
/* ******************** */
/* Graph Building API's */
-static void graph_build_finalize_common(DEG::Depsgraph *deg_graph, Main *bmain)
+static void graph_build_finalize_common(deg::Depsgraph *deg_graph, Main *bmain)
{
/* Detect and solve cycles. */
- DEG::deg_graph_detect_cycles(deg_graph);
+ deg::deg_graph_detect_cycles(deg_graph);
/* Simplify the graph by removing redundant relations (to optimize
* traversal later). */
/* TODO: it would be useful to have an option to disable this in cases where
* it is causing trouble. */
if (G.debug_value == 799) {
- DEG::deg_graph_transitive_reduction(deg_graph);
+ deg::deg_graph_transitive_reduction(deg_graph);
}
/* Store pointers to commonly used valuated datablocks. */
deg_graph->scene_cow = (Scene *)deg_graph->get_cow_id(&deg_graph->scene->id);
/* Flush visibility layer and re-schedule nodes for update. */
- DEG::deg_graph_build_finalize(bmain, deg_graph);
+ deg::deg_graph_build_finalize(bmain, deg_graph);
DEG_graph_on_visible_update(bmain, reinterpret_cast<::Depsgraph *>(deg_graph), false);
#if 0
if (!DEG_debug_consistency_check(deg_graph)) {
@@ -243,21 +245,21 @@ void DEG_graph_build_from_view_layer(Depsgraph *graph,
if (G.debug & (G_DEBUG_DEPSGRAPH_BUILD | G_DEBUG_DEPSGRAPH_TIME)) {
start_time = PIL_check_seconds_timer();
}
- DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(graph);
+ deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(graph);
/* Perform sanity checks. */
BLI_assert(BLI_findindex(&scene->view_layers, view_layer) != -1);
BLI_assert(deg_graph->scene == scene);
BLI_assert(deg_graph->view_layer == view_layer);
- DEG::DepsgraphBuilderCache builder_cache;
+ deg::DepsgraphBuilderCache builder_cache;
/* Generate all the nodes in the graph first */
- DEG::DepsgraphNodeBuilder node_builder(bmain, deg_graph, &builder_cache);
+ deg::DepsgraphNodeBuilder node_builder(bmain, deg_graph, &builder_cache);
node_builder.begin_build();
- node_builder.build_view_layer(scene, view_layer, DEG::DEG_ID_LINKED_DIRECTLY);
+ node_builder.build_view_layer(scene, view_layer, deg::DEG_ID_LINKED_DIRECTLY);
node_builder.end_build();
/* Hook up relationships between operations - to determine evaluation order. */
- DEG::DepsgraphRelationBuilder relation_builder(bmain, deg_graph, &builder_cache);
+ deg::DepsgraphRelationBuilder relation_builder(bmain, deg_graph, &builder_cache);
relation_builder.begin_build();
- relation_builder.build_view_layer(scene, view_layer, DEG::DEG_ID_LINKED_DIRECTLY);
+ relation_builder.build_view_layer(scene, view_layer, deg::DEG_ID_LINKED_DIRECTLY);
relation_builder.build_copy_on_write_relations();
relation_builder.build_driver_relations();
/* Finalize building. */
@@ -277,19 +279,19 @@ void DEG_graph_build_for_render_pipeline(Depsgraph *graph,
if (G.debug & (G_DEBUG_DEPSGRAPH_BUILD | G_DEBUG_DEPSGRAPH_TIME)) {
start_time = PIL_check_seconds_timer();
}
- DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(graph);
+ deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(graph);
/* Perform sanity checks. */
BLI_assert(deg_graph->scene == scene);
deg_graph->is_render_pipeline_depsgraph = true;
- DEG::DepsgraphBuilderCache builder_cache;
+ deg::DepsgraphBuilderCache builder_cache;
/* Generate all the nodes in the graph first */
- DEG::DepsgraphNodeBuilder node_builder(bmain, deg_graph, &builder_cache);
+ deg::DepsgraphNodeBuilder node_builder(bmain, deg_graph, &builder_cache);
node_builder.begin_build();
node_builder.build_scene_render(scene, view_layer);
node_builder.end_build();
/* Hook up relationships between operations - to determine evaluation
* order. */
- DEG::DepsgraphRelationBuilder relation_builder(bmain, deg_graph, &builder_cache);
+ deg::DepsgraphRelationBuilder relation_builder(bmain, deg_graph, &builder_cache);
relation_builder.begin_build();
relation_builder.build_scene_render(scene, view_layer);
relation_builder.build_copy_on_write_relations();
@@ -309,20 +311,20 @@ void DEG_graph_build_for_compositor_preview(
if (G.debug & (G_DEBUG_DEPSGRAPH_BUILD | G_DEBUG_DEPSGRAPH_TIME)) {
start_time = PIL_check_seconds_timer();
}
- DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(graph);
+ deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(graph);
/* Perform sanity checks. */
BLI_assert(deg_graph->scene == scene);
deg_graph->is_render_pipeline_depsgraph = true;
- DEG::DepsgraphBuilderCache builder_cache;
+ deg::DepsgraphBuilderCache builder_cache;
/* Generate all the nodes in the graph first */
- DEG::DepsgraphNodeBuilder node_builder(bmain, deg_graph, &builder_cache);
+ deg::DepsgraphNodeBuilder node_builder(bmain, deg_graph, &builder_cache);
node_builder.begin_build();
node_builder.build_scene_render(scene, view_layer);
node_builder.build_nodetree(nodetree);
node_builder.end_build();
/* Hook up relationships between operations - to determine evaluation
* order. */
- DEG::DepsgraphRelationBuilder relation_builder(bmain, deg_graph, &builder_cache);
+ deg::DepsgraphRelationBuilder relation_builder(bmain, deg_graph, &builder_cache);
relation_builder.begin_build();
relation_builder.build_scene_render(scene, view_layer);
relation_builder.build_nodetree(nodetree);
@@ -349,7 +351,8 @@ void DEG_graph_build_for_compositor_preview(
* This way we avoid high-poly character body pulled into the dependency graph when it's coming
* from a library into an animation file and the dependency graph constructed for a proxy rig. */
-namespace DEG {
+namespace blender {
+namespace deg {
namespace {
class DepsgraphFromIDsFilter {
@@ -433,7 +436,8 @@ class DepsgraphFromIDsRelationBuilder : public DepsgraphRelationBuilder {
};
} // namespace
-} // namespace DEG
+} // namespace deg
+} // namespace blender
void DEG_graph_build_from_ids(Depsgraph *graph,
Main *bmain,
@@ -446,25 +450,25 @@ void DEG_graph_build_from_ids(Depsgraph *graph,
if (G.debug & (G_DEBUG_DEPSGRAPH_BUILD | G_DEBUG_DEPSGRAPH_TIME)) {
start_time = PIL_check_seconds_timer();
}
- DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(graph);
+ deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(graph);
/* Perform sanity checks. */
BLI_assert(BLI_findindex(&scene->view_layers, view_layer) != -1);
BLI_assert(deg_graph->scene == scene);
BLI_assert(deg_graph->view_layer == view_layer);
- DEG::DepsgraphBuilderCache builder_cache;
+ deg::DepsgraphBuilderCache builder_cache;
/* Generate all the nodes in the graph first */
- DEG::DepsgraphFromIDsNodeBuilder node_builder(bmain, deg_graph, &builder_cache, ids, num_ids);
+ deg::DepsgraphFromIDsNodeBuilder node_builder(bmain, deg_graph, &builder_cache, ids, num_ids);
node_builder.begin_build();
- node_builder.build_view_layer(scene, view_layer, DEG::DEG_ID_LINKED_DIRECTLY);
+ node_builder.build_view_layer(scene, view_layer, deg::DEG_ID_LINKED_DIRECTLY);
for (int i = 0; i < num_ids; ++i) {
node_builder.build_id(ids[i]);
}
node_builder.end_build();
/* Hook up relationships between operations - to determine evaluation order. */
- DEG::DepsgraphFromIDsRelationBuilder relation_builder(
+ deg::DepsgraphFromIDsRelationBuilder relation_builder(
bmain, deg_graph, &builder_cache, ids, num_ids);
relation_builder.begin_build();
- relation_builder.build_view_layer(scene, view_layer, DEG::DEG_ID_LINKED_DIRECTLY);
+ relation_builder.build_view_layer(scene, view_layer, deg::DEG_ID_LINKED_DIRECTLY);
for (int i = 0; i < num_ids; ++i) {
relation_builder.build_id(ids[i]);
}
@@ -482,7 +486,7 @@ void DEG_graph_build_from_ids(Depsgraph *graph,
void DEG_graph_tag_relations_update(Depsgraph *graph)
{
DEG_DEBUG_PRINTF(graph, TAG, "%s: Tagging relations for update.\n", __func__);
- DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(graph);
+ deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(graph);
deg_graph->need_update = true;
/* NOTE: When relations are updated, it's quite possible that
* we've got new bases in the scene. This means, we need to
@@ -490,16 +494,16 @@ void DEG_graph_tag_relations_update(Depsgraph *graph)
*
* TODO(sergey): Try to make it so we don't flush updates
* to the whole depsgraph. */
- DEG::IDNode *id_node = deg_graph->find_id_node(&deg_graph->scene->id);
+ deg::IDNode *id_node = deg_graph->find_id_node(&deg_graph->scene->id);
if (id_node != nullptr) {
- id_node->tag_update(deg_graph, DEG::DEG_UPDATE_SOURCE_RELATIONS);
+ id_node->tag_update(deg_graph, deg::DEG_UPDATE_SOURCE_RELATIONS);
}
}
/* Create or update relations in the specified graph. */
void DEG_graph_relations_update(Depsgraph *graph, Main *bmain, Scene *scene, ViewLayer *view_layer)
{
- DEG::Depsgraph *deg_graph = (DEG::Depsgraph *)graph;
+ deg::Depsgraph *deg_graph = (deg::Depsgraph *)graph;
if (!deg_graph->need_update) {
/* Graph is up to date, nothing to do. */
return;
@@ -511,7 +515,7 @@ void DEG_graph_relations_update(Depsgraph *graph, Main *bmain, Scene *scene, Vie
void DEG_relations_tag_update(Main *bmain)
{
DEG_GLOBAL_DEBUG_PRINTF(TAG, "%s: Tagging relations for update.\n", __func__);
- for (DEG::Depsgraph *depsgraph : DEG::get_all_registered_graphs(bmain)) {
+ for (deg::Depsgraph *depsgraph : deg::get_all_registered_graphs(bmain)) {
DEG_graph_tag_relations_update(reinterpret_cast<Depsgraph *>(depsgraph));
}
}
diff --git a/source/blender/depsgraph/intern/depsgraph_debug.cc b/source/blender/depsgraph/intern/depsgraph_debug.cc
index 8f5117ec0f6..0763738ff59 100644
--- a/source/blender/depsgraph/intern/depsgraph_debug.cc
+++ b/source/blender/depsgraph/intern/depsgraph_debug.cc
@@ -42,27 +42,29 @@
#include "intern/node/deg_node_id.h"
#include "intern/node/deg_node_time.h"
+namespace deg = blender::deg;
+
void DEG_debug_flags_set(Depsgraph *depsgraph, int flags)
{
- DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(depsgraph);
+ deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(depsgraph);
deg_graph->debug.flags = flags;
}
int DEG_debug_flags_get(const Depsgraph *depsgraph)
{
- const DEG::Depsgraph *deg_graph = reinterpret_cast<const DEG::Depsgraph *>(depsgraph);
+ const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(depsgraph);
return deg_graph->debug.flags;
}
void DEG_debug_name_set(struct Depsgraph *depsgraph, const char *name)
{
- DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(depsgraph);
+ deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(depsgraph);
deg_graph->debug.name = name;
}
const char *DEG_debug_name_get(struct Depsgraph *depsgraph)
{
- const DEG::Depsgraph *deg_graph = reinterpret_cast<const DEG::Depsgraph *>(depsgraph);
+ const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(depsgraph);
return deg_graph->debug.name.c_str();
}
@@ -70,8 +72,8 @@ bool DEG_debug_compare(const struct Depsgraph *graph1, const struct Depsgraph *g
{
BLI_assert(graph1 != nullptr);
BLI_assert(graph2 != nullptr);
- const DEG::Depsgraph *deg_graph1 = reinterpret_cast<const DEG::Depsgraph *>(graph1);
- const DEG::Depsgraph *deg_graph2 = reinterpret_cast<const DEG::Depsgraph *>(graph2);
+ const deg::Depsgraph *deg_graph1 = reinterpret_cast<const deg::Depsgraph *>(graph1);
+ const deg::Depsgraph *deg_graph2 = reinterpret_cast<const deg::Depsgraph *>(graph2);
if (deg_graph1->operations.size() != deg_graph2->operations.size()) {
return false;
}
@@ -103,18 +105,18 @@ bool DEG_debug_graph_relations_validate(Depsgraph *graph,
bool DEG_debug_consistency_check(Depsgraph *graph)
{
- const DEG::Depsgraph *deg_graph = reinterpret_cast<const DEG::Depsgraph *>(graph);
+ const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(graph);
/* Validate links exists in both directions. */
- for (DEG::OperationNode *node : deg_graph->operations) {
- for (DEG::Relation *rel : node->outlinks) {
+ for (deg::OperationNode *node : deg_graph->operations) {
+ for (deg::Relation *rel : node->outlinks) {
int counter1 = 0;
- for (DEG::Relation *tmp_rel : node->outlinks) {
+ for (deg::Relation *tmp_rel : node->outlinks) {
if (tmp_rel == rel) {
counter1++;
}
}
int counter2 = 0;
- for (DEG::Relation *tmp_rel : rel->to->inlinks) {
+ for (deg::Relation *tmp_rel : rel->to->inlinks) {
if (tmp_rel == rel) {
counter2++;
}
@@ -130,16 +132,16 @@ bool DEG_debug_consistency_check(Depsgraph *graph)
}
}
- for (DEG::OperationNode *node : deg_graph->operations) {
- for (DEG::Relation *rel : node->inlinks) {
+ for (deg::OperationNode *node : deg_graph->operations) {
+ for (deg::Relation *rel : node->inlinks) {
int counter1 = 0;
- for (DEG::Relation *tmp_rel : node->inlinks) {
+ for (deg::Relation *tmp_rel : node->inlinks) {
if (tmp_rel == rel) {
counter1++;
}
}
int counter2 = 0;
- for (DEG::Relation *tmp_rel : rel->from->outlinks) {
+ for (deg::Relation *tmp_rel : rel->from->outlinks) {
if (tmp_rel == rel) {
counter2++;
}
@@ -153,19 +155,19 @@ bool DEG_debug_consistency_check(Depsgraph *graph)
}
/* Validate node valency calculated in both directions. */
- for (DEG::OperationNode *node : deg_graph->operations) {
+ for (deg::OperationNode *node : deg_graph->operations) {
node->num_links_pending = 0;
node->custom_flags = 0;
}
- for (DEG::OperationNode *node : deg_graph->operations) {
+ for (deg::OperationNode *node : deg_graph->operations) {
if (node->custom_flags) {
printf("Node %s is twice in the operations!\n", node->identifier().c_str());
return false;
}
- for (DEG::Relation *rel : node->outlinks) {
- if (rel->to->type == DEG::NodeType::OPERATION) {
- DEG::OperationNode *to = (DEG::OperationNode *)rel->to;
+ for (deg::Relation *rel : node->outlinks) {
+ if (rel->to->type == deg::NodeType::OPERATION) {
+ deg::OperationNode *to = (deg::OperationNode *)rel->to;
BLI_assert(to->num_links_pending < to->inlinks.size());
++to->num_links_pending;
}
@@ -173,10 +175,10 @@ bool DEG_debug_consistency_check(Depsgraph *graph)
node->custom_flags = 1;
}
- for (DEG::OperationNode *node : deg_graph->operations) {
+ for (deg::OperationNode *node : deg_graph->operations) {
int num_links_pending = 0;
- for (DEG::Relation *rel : node->inlinks) {
- if (rel->from->type == DEG::NodeType::OPERATION) {
+ for (deg::Relation *rel : node->inlinks) {
+ if (rel->from->type == deg::NodeType::OPERATION) {
num_links_pending++;
}
}
@@ -205,7 +207,7 @@ void DEG_stats_simple(const Depsgraph *graph,
size_t *r_operations,
size_t *r_relations)
{
- const DEG::Depsgraph *deg_graph = reinterpret_cast<const DEG::Depsgraph *>(graph);
+ const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(graph);
/* number of operations */
if (r_operations) {
@@ -219,17 +221,17 @@ void DEG_stats_simple(const Depsgraph *graph,
size_t tot_outer = 0;
size_t tot_rels = 0;
- for (DEG::IDNode *id_node : deg_graph->id_nodes) {
+ for (deg::IDNode *id_node : deg_graph->id_nodes) {
tot_outer++;
- for (DEG::ComponentNode *comp_node : id_node->components.values()) {
+ for (deg::ComponentNode *comp_node : id_node->components.values()) {
tot_outer++;
- for (DEG::OperationNode *op_node : comp_node->operations) {
+ for (deg::OperationNode *op_node : comp_node->operations) {
tot_rels += op_node->inlinks.size();
}
}
}
- DEG::TimeSourceNode *time_source = deg_graph->find_time_source();
+ deg::TimeSourceNode *time_source = deg_graph->find_time_source();
if (time_source != nullptr) {
tot_rels += time_source->inlinks.size();
}
@@ -243,13 +245,13 @@ void DEG_stats_simple(const Depsgraph *graph,
}
}
-static DEG::string depsgraph_name_for_logging(struct Depsgraph *depsgraph)
+static deg::string depsgraph_name_for_logging(struct Depsgraph *depsgraph)
{
const char *name = DEG_debug_name_get(depsgraph);
if (name[0] == '\0') {
return "";
}
- return "[" + DEG::string(name) + "]: ";
+ return "[" + deg::string(name) + "]: ";
}
void DEG_debug_print_begin(struct Depsgraph *depsgraph)
@@ -270,9 +272,9 @@ void DEG_debug_print_eval(struct Depsgraph *depsgraph,
depsgraph_name_for_logging(depsgraph).c_str(),
function_name,
object_name,
- DEG::color_for_pointer(object_address).c_str(),
+ deg::color_for_pointer(object_address).c_str(),
object_address,
- DEG::color_end().c_str());
+ deg::color_end().c_str());
fflush(stdout);
}
@@ -292,14 +294,14 @@ void DEG_debug_print_eval_subdata(struct Depsgraph *depsgraph,
depsgraph_name_for_logging(depsgraph).c_str(),
function_name,
object_name,
- DEG::color_for_pointer(object_address).c_str(),
+ deg::color_for_pointer(object_address).c_str(),
object_address,
- DEG::color_end().c_str(),
+ deg::color_end().c_str(),
subdata_comment,
subdata_name,
- DEG::color_for_pointer(subdata_address).c_str(),
+ deg::color_for_pointer(subdata_address).c_str(),
subdata_address,
- DEG::color_end().c_str());
+ deg::color_end().c_str());
fflush(stdout);
}
@@ -320,15 +322,15 @@ void DEG_debug_print_eval_subdata_index(struct Depsgraph *depsgraph,
depsgraph_name_for_logging(depsgraph).c_str(),
function_name,
object_name,
- DEG::color_for_pointer(object_address).c_str(),
+ deg::color_for_pointer(object_address).c_str(),
object_address,
- DEG::color_end().c_str(),
+ deg::color_end().c_str(),
subdata_comment,
subdata_name,
subdata_index,
- DEG::color_for_pointer(subdata_address).c_str(),
+ deg::color_for_pointer(subdata_address).c_str(),
subdata_address,
- DEG::color_end().c_str());
+ deg::color_end().c_str());
fflush(stdout);
}
@@ -348,14 +350,14 @@ void DEG_debug_print_eval_parent_typed(struct Depsgraph *depsgraph,
depsgraph_name_for_logging(depsgraph).c_str(),
function_name,
object_name,
- DEG::color_for_pointer(object_address).c_str(),
+ deg::color_for_pointer(object_address).c_str(),
object_address,
- DEG::color_end().c_str(),
+ deg::color_end().c_str(),
parent_comment,
parent_name,
- DEG::color_for_pointer(parent_address).c_str(),
+ deg::color_for_pointer(parent_address).c_str(),
parent_address,
- DEG::color_end().c_str());
+ deg::color_end().c_str());
fflush(stdout);
}
@@ -373,9 +375,9 @@ void DEG_debug_print_eval_time(struct Depsgraph *depsgraph,
depsgraph_name_for_logging(depsgraph).c_str(),
function_name,
object_name,
- DEG::color_for_pointer(object_address).c_str(),
+ deg::color_for_pointer(object_address).c_str(),
object_address,
- DEG::color_end().c_str(),
+ deg::color_end().c_str(),
time);
fflush(stdout);
}
diff --git a/source/blender/depsgraph/intern/depsgraph_eval.cc b/source/blender/depsgraph/intern/depsgraph_eval.cc
index b6c6129e9ba..8a641f23a42 100644
--- a/source/blender/depsgraph/intern/depsgraph_eval.cc
+++ b/source/blender/depsgraph/intern/depsgraph_eval.cc
@@ -45,44 +45,46 @@
#include "intern/depsgraph.h"
+namespace deg = blender::deg;
+
/* Evaluate all nodes tagged for updating. */
void DEG_evaluate_on_refresh(Main *bmain, Depsgraph *graph)
{
- DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(graph);
+ deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(graph);
deg_graph->ctime = BKE_scene_frame_get(deg_graph->scene);
/* Update time on primary timesource. */
- DEG::TimeSourceNode *tsrc = deg_graph->find_time_source();
+ deg::TimeSourceNode *tsrc = deg_graph->find_time_source();
tsrc->cfra = deg_graph->ctime;
/* Update time in scene. */
if (deg_graph->scene_cow) {
BKE_scene_frame_set(deg_graph->scene_cow, deg_graph->ctime);
}
- DEG::deg_graph_flush_updates(bmain, deg_graph);
- DEG::deg_evaluate_on_refresh(deg_graph);
+ deg::deg_graph_flush_updates(bmain, deg_graph);
+ deg::deg_evaluate_on_refresh(deg_graph);
deg_graph->need_update_time = false;
}
/* Frame-change happened for root scene that graph belongs to. */
void DEG_evaluate_on_framechange(Main *bmain, Depsgraph *graph, float ctime)
{
- DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(graph);
+ deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(graph);
deg_graph->ctime = ctime;
/* Update time on primary timesource. */
- DEG::TimeSourceNode *tsrc = deg_graph->find_time_source();
+ deg::TimeSourceNode *tsrc = deg_graph->find_time_source();
tsrc->cfra = ctime;
deg_graph->need_update_time = true;
- DEG::deg_graph_flush_updates(bmain, deg_graph);
+ deg::deg_graph_flush_updates(bmain, deg_graph);
/* Update time in scene. */
if (deg_graph->scene_cow) {
BKE_scene_frame_set(deg_graph->scene_cow, deg_graph->ctime);
}
/* Perform recalculation updates. */
- DEG::deg_evaluate_on_refresh(deg_graph);
+ deg::deg_evaluate_on_refresh(deg_graph);
deg_graph->need_update_time = false;
}
bool DEG_needs_eval(Depsgraph *graph)
{
- DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(graph);
+ deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(graph);
return !deg_graph->entry_tags.is_empty() || deg_graph->need_update_time;
}
diff --git a/source/blender/depsgraph/intern/depsgraph_physics.cc b/source/blender/depsgraph/intern/depsgraph_physics.cc
index ad67117fda3..76f3a2e8ff4 100644
--- a/source/blender/depsgraph/intern/depsgraph_physics.cc
+++ b/source/blender/depsgraph/intern/depsgraph_physics.cc
@@ -44,6 +44,8 @@
#include "depsgraph.h"
+namespace deg = blender::deg;
+
/*************************** Evaluation Query API *****************************/
static ePhysicsRelationType modifier_to_relation_type(unsigned int modifier_type)
@@ -63,7 +65,7 @@ static ePhysicsRelationType modifier_to_relation_type(unsigned int modifier_type
ListBase *DEG_get_effector_relations(const Depsgraph *graph, Collection *collection)
{
- const DEG::Depsgraph *deg_graph = reinterpret_cast<const DEG::Depsgraph *>(graph);
+ const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(graph);
if (deg_graph->physics_relations[DEG_PHYSICS_EFFECTOR] == nullptr) {
return nullptr;
}
@@ -77,7 +79,7 @@ ListBase *DEG_get_collision_relations(const Depsgraph *graph,
Collection *collection,
unsigned int modifier_type)
{
- const DEG::Depsgraph *deg_graph = reinterpret_cast<const DEG::Depsgraph *>(graph);
+ const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(graph);
const ePhysicsRelationType type = modifier_to_relation_type(modifier_type);
if (deg_graph->physics_relations[type] == nullptr) {
return nullptr;
@@ -96,7 +98,7 @@ void DEG_add_collision_relations(DepsNodeHandle *handle,
const char *name)
{
Depsgraph *depsgraph = DEG_get_graph_from_handle(handle);
- DEG::Depsgraph *deg_graph = (DEG::Depsgraph *)depsgraph;
+ deg::Depsgraph *deg_graph = (deg::Depsgraph *)depsgraph;
ListBase *relations = build_collision_relations(deg_graph, collection, modifier_type);
LISTBASE_FOREACH (CollisionRelation *, relation, relations) {
Object *ob1 = relation->ob;
@@ -119,7 +121,7 @@ void DEG_add_forcefield_relations(DepsNodeHandle *handle,
const char *name)
{
Depsgraph *depsgraph = DEG_get_graph_from_handle(handle);
- DEG::Depsgraph *deg_graph = (DEG::Depsgraph *)depsgraph;
+ deg::Depsgraph *deg_graph = (deg::Depsgraph *)depsgraph;
ListBase *relations = build_effector_relations(deg_graph, effector_weights->group);
LISTBASE_FOREACH (EffectorRelation *, relation, relations) {
if (relation->ob == object) {
@@ -158,7 +160,8 @@ void DEG_add_forcefield_relations(DepsNodeHandle *handle,
/******************************** Internal API ********************************/
-namespace DEG {
+namespace blender {
+namespace deg {
ListBase *build_effector_relations(Depsgraph *graph, Collection *collection)
{
@@ -218,4 +221,5 @@ void clear_physics_relations(Depsgraph *graph)
}
}
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/depsgraph_physics.h b/source/blender/depsgraph/intern/depsgraph_physics.h
index f5d7b9817b5..dbcb577b0b2 100644
--- a/source/blender/depsgraph/intern/depsgraph_physics.h
+++ b/source/blender/depsgraph/intern/depsgraph_physics.h
@@ -26,7 +26,8 @@
struct Collection;
struct ListBase;
-namespace DEG {
+namespace blender {
+namespace deg {
struct Depsgraph;
@@ -36,4 +37,5 @@ ListBase *build_collision_relations(Depsgraph *graph,
unsigned int modifier_type);
void clear_physics_relations(Depsgraph *graph);
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/depsgraph_query.cc b/source/blender/depsgraph/intern/depsgraph_query.cc
index 3c760e71197..0b6014c18f1 100644
--- a/source/blender/depsgraph/intern/depsgraph_query.cc
+++ b/source/blender/depsgraph/intern/depsgraph_query.cc
@@ -47,39 +47,41 @@
#include "intern/eval/deg_eval_copy_on_write.h"
#include "intern/node/deg_node_id.h"
+namespace deg = blender::deg;
+
struct Scene *DEG_get_input_scene(const Depsgraph *graph)
{
- const DEG::Depsgraph *deg_graph = reinterpret_cast<const DEG::Depsgraph *>(graph);
+ const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(graph);
return deg_graph->scene;
}
struct ViewLayer *DEG_get_input_view_layer(const Depsgraph *graph)
{
- const DEG::Depsgraph *deg_graph = reinterpret_cast<const DEG::Depsgraph *>(graph);
+ const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(graph);
return deg_graph->view_layer;
}
eEvaluationMode DEG_get_mode(const Depsgraph *graph)
{
- const DEG::Depsgraph *deg_graph = reinterpret_cast<const DEG::Depsgraph *>(graph);
+ const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(graph);
return deg_graph->mode;
}
float DEG_get_ctime(const Depsgraph *graph)
{
- const DEG::Depsgraph *deg_graph = reinterpret_cast<const DEG::Depsgraph *>(graph);
+ const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(graph);
return deg_graph->ctime;
}
bool DEG_id_type_updated(const Depsgraph *graph, short id_type)
{
- const DEG::Depsgraph *deg_graph = reinterpret_cast<const DEG::Depsgraph *>(graph);
+ const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(graph);
return deg_graph->id_type_updated[BKE_idtype_idcode_to_index(id_type)] != 0;
}
bool DEG_id_type_any_updated(const Depsgraph *graph)
{
- const DEG::Depsgraph *deg_graph = reinterpret_cast<const DEG::Depsgraph *>(graph);
+ const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(graph);
/* Loop over all ID types. */
for (int id_type_index = 0; id_type_index < MAX_LIBARRAY; id_type_index++) {
@@ -93,7 +95,7 @@ bool DEG_id_type_any_updated(const Depsgraph *graph)
bool DEG_id_type_any_exists(const Depsgraph *depsgraph, short id_type)
{
- const DEG::Depsgraph *deg_graph = reinterpret_cast<const DEG::Depsgraph *>(depsgraph);
+ const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(depsgraph);
return deg_graph->id_type_exist[BKE_idtype_idcode_to_index(id_type)] != 0;
}
@@ -108,8 +110,8 @@ uint32_t DEG_get_eval_flags_for_id(const Depsgraph *graph, ID *id)
return 0;
}
- 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::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(graph);
+ 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;
@@ -131,8 +133,8 @@ void DEG_get_customdata_mask_for_object(const Depsgraph *graph,
return;
}
- 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(&ob->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(&ob->id));
if (id_node == nullptr) {
/* TODO(sergey): Does it mean we need to check set scene? */
return;
@@ -147,17 +149,17 @@ void DEG_get_customdata_mask_for_object(const Depsgraph *graph,
Scene *DEG_get_evaluated_scene(const Depsgraph *graph)
{
- const DEG::Depsgraph *deg_graph = reinterpret_cast<const DEG::Depsgraph *>(graph);
+ const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(graph);
Scene *scene_cow = deg_graph->scene_cow;
/* TODO(sergey): Shall we expand data-block here? Or is it OK to assume
* that caller is OK with just a pointer in case scene is not updated yet? */
- BLI_assert(scene_cow != nullptr && DEG::deg_copy_on_write_is_expanded(&scene_cow->id));
+ BLI_assert(scene_cow != nullptr && deg::deg_copy_on_write_is_expanded(&scene_cow->id));
return scene_cow;
}
ViewLayer *DEG_get_evaluated_view_layer(const Depsgraph *graph)
{
- const DEG::Depsgraph *deg_graph = reinterpret_cast<const DEG::Depsgraph *>(graph);
+ const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(graph);
Scene *scene_cow = DEG_get_evaluated_scene(graph);
if (scene_cow == nullptr) {
return nullptr; /* Happens with new, not-yet-built/evaluated graphes. */
@@ -184,8 +186,8 @@ ID *DEG_get_evaluated_id(const Depsgraph *depsgraph, ID *id)
/* 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);
+ 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;
}
@@ -309,7 +311,7 @@ bool DEG_is_evaluated_object(const Object *object)
bool DEG_is_fully_evaluated(const struct Depsgraph *depsgraph)
{
- const DEG::Depsgraph *deg_graph = (const DEG::Depsgraph *)depsgraph;
+ const deg::Depsgraph *deg_graph = (const deg::Depsgraph *)depsgraph;
/* Check whether relations are up to date. */
if (deg_graph->need_update) {
return false;
diff --git a/source/blender/depsgraph/intern/depsgraph_query_foreach.cc b/source/blender/depsgraph/intern/depsgraph_query_foreach.cc
index b68c4b91fcc..ebdd08ce519 100644
--- a/source/blender/depsgraph/intern/depsgraph_query_foreach.cc
+++ b/source/blender/depsgraph/intern/depsgraph_query_foreach.cc
@@ -40,9 +40,12 @@
#include "intern/node/deg_node_id.h"
#include "intern/node/deg_node_operation.h"
+namespace deg = blender::deg;
+
/* ************************ DEG TRAVERSAL ********************* */
-namespace DEG {
+namespace blender {
+namespace deg {
namespace {
typedef deque<OperationNode *> TraversalQueue;
@@ -262,14 +265,15 @@ void deg_foreach_id(const Depsgraph *depsgraph, DEGForeachIDCallback callback, v
}
} // namespace
-} // namespace DEG
+} // namespace deg
+} // namespace blender
void DEG_foreach_dependent_ID(const Depsgraph *depsgraph,
const ID *id,
DEGForeachIDCallback callback,
void *user_data)
{
- DEG::deg_foreach_dependent_ID((const DEG::Depsgraph *)depsgraph, id, callback, user_data);
+ deg::deg_foreach_dependent_ID((const deg::Depsgraph *)depsgraph, id, callback, user_data);
}
void DEG_foreach_dependent_ID_component(const Depsgraph *depsgraph,
@@ -279,8 +283,8 @@ void DEG_foreach_dependent_ID_component(const Depsgraph *depsgraph,
DEGForeachIDComponentCallback callback,
void *user_data)
{
- DEG::deg_foreach_dependent_ID_component(
- (const DEG::Depsgraph *)depsgraph, id, source_component_type, flags, callback, user_data);
+ deg::deg_foreach_dependent_ID_component(
+ (const deg::Depsgraph *)depsgraph, id, source_component_type, flags, callback, user_data);
}
void DEG_foreach_ancestor_ID(const Depsgraph *depsgraph,
@@ -288,10 +292,10 @@ void DEG_foreach_ancestor_ID(const Depsgraph *depsgraph,
DEGForeachIDCallback callback,
void *user_data)
{
- DEG::deg_foreach_ancestor_ID((const DEG::Depsgraph *)depsgraph, id, callback, user_data);
+ deg::deg_foreach_ancestor_ID((const deg::Depsgraph *)depsgraph, id, callback, user_data);
}
void DEG_foreach_ID(const Depsgraph *depsgraph, DEGForeachIDCallback callback, void *user_data)
{
- DEG::deg_foreach_id((const DEG::Depsgraph *)depsgraph, callback, user_data);
+ deg::deg_foreach_id((const deg::Depsgraph *)depsgraph, callback, user_data);
}
diff --git a/source/blender/depsgraph/intern/depsgraph_query_iter.cc b/source/blender/depsgraph/intern/depsgraph_query_iter.cc
index 1eb07206465..c27a7cc3b93 100644
--- a/source/blender/depsgraph/intern/depsgraph_query_iter.cc
+++ b/source/blender/depsgraph/intern/depsgraph_query_iter.cc
@@ -59,6 +59,8 @@
# define INVALIDATE_WORK_DATA
#endif
+namespace deg = blender::deg;
+
/* ************************ DEG ITERATORS ********************* */
namespace {
@@ -169,14 +171,14 @@ bool deg_objects_dupli_iterator_next(BLI_Iterator *iter)
copy_m4_m4(data->temp_dupli_object.obmat, dob->mat);
invert_m4_m4(data->temp_dupli_object.imat, data->temp_dupli_object.obmat);
iter->current = &data->temp_dupli_object;
- BLI_assert(DEG::deg_validate_copy_on_write_datablock(&data->temp_dupli_object.id));
+ BLI_assert(deg::deg_validate_copy_on_write_datablock(&data->temp_dupli_object.id));
return true;
}
return false;
}
-void deg_iterator_objects_step(BLI_Iterator *iter, DEG::IDNode *id_node)
+void deg_iterator_objects_step(BLI_Iterator *iter, deg::IDNode *id_node)
{
/* Set it early in case we need to exit and we are running from within a loop. */
iter->skip = true;
@@ -193,17 +195,17 @@ void deg_iterator_objects_step(BLI_Iterator *iter, DEG::IDNode *id_node)
}
switch (id_node->linked_state) {
- case DEG::DEG_ID_LINKED_DIRECTLY:
+ case deg::DEG_ID_LINKED_DIRECTLY:
if ((data->flag & DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY) == 0) {
return;
}
break;
- case DEG::DEG_ID_LINKED_VIA_SET:
+ case deg::DEG_ID_LINKED_VIA_SET:
if ((data->flag & DEG_ITER_OBJECT_FLAG_LINKED_VIA_SET) == 0) {
return;
}
break;
- case DEG::DEG_ID_LINKED_INDIRECTLY:
+ case deg::DEG_ID_LINKED_INDIRECTLY:
if ((data->flag & DEG_ITER_OBJECT_FLAG_LINKED_INDIRECTLY) == 0) {
return;
}
@@ -211,7 +213,7 @@ void deg_iterator_objects_step(BLI_Iterator *iter, DEG::IDNode *id_node)
}
Object *object = (Object *)id_node->id_cow;
- BLI_assert(DEG::deg_validate_copy_on_write_datablock(&object->id));
+ BLI_assert(deg::deg_validate_copy_on_write_datablock(&object->id));
int ob_visibility = OB_VISIBLE_ALL;
if (data->flag & DEG_ITER_OBJECT_FLAG_VISIBLE) {
@@ -241,7 +243,7 @@ void deg_iterator_objects_step(BLI_Iterator *iter, DEG::IDNode *id_node)
void DEG_iterator_objects_begin(BLI_Iterator *iter, DEGObjectIterData *data)
{
Depsgraph *depsgraph = data->graph;
- DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(depsgraph);
+ deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(depsgraph);
const size_t num_id_nodes = deg_graph->id_nodes.size();
iter->data = data;
@@ -261,7 +263,7 @@ void DEG_iterator_objects_begin(BLI_Iterator *iter, DEGObjectIterData *data)
data->eval_mode = DEG_get_mode(depsgraph);
deg_invalidate_iterator_work_data(data);
- DEG::IDNode *id_node = deg_graph->id_nodes[data->id_node_index];
+ deg::IDNode *id_node = deg_graph->id_nodes[data->id_node_index];
deg_iterator_objects_step(iter, id_node);
if (iter->skip) {
@@ -273,7 +275,7 @@ void DEG_iterator_objects_next(BLI_Iterator *iter)
{
DEGObjectIterData *data = (DEGObjectIterData *)iter->data;
Depsgraph *depsgraph = data->graph;
- DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(depsgraph);
+ deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(depsgraph);
do {
iter->skip = false;
if (data->dupli_list) {
@@ -297,7 +299,7 @@ void DEG_iterator_objects_next(BLI_Iterator *iter)
return;
}
- DEG::IDNode *id_node = deg_graph->id_nodes[data->id_node_index];
+ deg::IDNode *id_node = deg_graph->id_nodes[data->id_node_index];
deg_iterator_objects_step(iter, id_node);
} while (iter->skip);
}
@@ -314,7 +316,7 @@ void DEG_iterator_objects_end(BLI_Iterator *iter)
/* ************************ DEG ID ITERATOR ********************* */
-static void DEG_iterator_ids_step(BLI_Iterator *iter, DEG::IDNode *id_node, bool only_updated)
+static void DEG_iterator_ids_step(BLI_Iterator *iter, deg::IDNode *id_node, bool only_updated)
{
ID *id_cow = id_node->id_cow;
@@ -339,7 +341,7 @@ static void DEG_iterator_ids_step(BLI_Iterator *iter, DEG::IDNode *id_node, bool
void DEG_iterator_ids_begin(BLI_Iterator *iter, DEGIDIterData *data)
{
Depsgraph *depsgraph = data->graph;
- DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(depsgraph);
+ deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(depsgraph);
const size_t num_id_nodes = deg_graph->id_nodes.size();
iter->data = data;
@@ -352,7 +354,7 @@ void DEG_iterator_ids_begin(BLI_Iterator *iter, DEGIDIterData *data)
data->id_node_index = 0;
data->num_id_nodes = num_id_nodes;
- DEG::IDNode *id_node = deg_graph->id_nodes[data->id_node_index];
+ deg::IDNode *id_node = deg_graph->id_nodes[data->id_node_index];
DEG_iterator_ids_step(iter, id_node, data->only_updated);
if (iter->skip) {
@@ -364,7 +366,7 @@ void DEG_iterator_ids_next(BLI_Iterator *iter)
{
DEGIDIterData *data = (DEGIDIterData *)iter->data;
Depsgraph *depsgraph = data->graph;
- DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(depsgraph);
+ deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(depsgraph);
do {
iter->skip = false;
@@ -375,7 +377,7 @@ void DEG_iterator_ids_next(BLI_Iterator *iter)
return;
}
- DEG::IDNode *id_node = deg_graph->id_nodes[data->id_node_index];
+ deg::IDNode *id_node = deg_graph->id_nodes[data->id_node_index];
DEG_iterator_ids_step(iter, id_node, data->only_updated);
} while (iter->skip);
}
diff --git a/source/blender/depsgraph/intern/depsgraph_registry.cc b/source/blender/depsgraph/intern/depsgraph_registry.cc
index 7eac7b45069..6bfd2e881cc 100644
--- a/source/blender/depsgraph/intern/depsgraph_registry.cc
+++ b/source/blender/depsgraph/intern/depsgraph_registry.cc
@@ -27,7 +27,8 @@
#include "intern/depsgraph.h"
-namespace DEG {
+namespace blender {
+namespace deg {
static Map<Main *, VectorSet<Depsgraph *>> g_graph_registry;
@@ -58,4 +59,5 @@ Span<Depsgraph *> get_all_registered_graphs(Main *bmain)
return {};
}
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/depsgraph_registry.h b/source/blender/depsgraph/intern/depsgraph_registry.h
index 967791d2fbf..dc29f9d8edf 100644
--- a/source/blender/depsgraph/intern/depsgraph_registry.h
+++ b/source/blender/depsgraph/intern/depsgraph_registry.h
@@ -27,7 +27,8 @@
struct Main;
-namespace DEG {
+namespace blender {
+namespace deg {
struct Depsgraph;
@@ -35,4 +36,5 @@ void register_graph(Depsgraph *depsgraph);
void unregister_graph(Depsgraph *depsgraph);
Span<Depsgraph *> get_all_registered_graphs(Main *bmain);
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/depsgraph_relation.cc b/source/blender/depsgraph/intern/depsgraph_relation.cc
index a4ec48658f5..d09247b03d9 100644
--- a/source/blender/depsgraph/intern/depsgraph_relation.cc
+++ b/source/blender/depsgraph/intern/depsgraph_relation.cc
@@ -28,7 +28,8 @@
#include "intern/depsgraph_type.h"
#include "intern/node/deg_node.h"
-namespace DEG {
+namespace blender {
+namespace deg {
Relation::Relation(Node *from, Node *to, const char *description)
: from(from), to(to), name(description), flag(0)
@@ -64,4 +65,5 @@ void Relation::unlink()
to->inlinks.remove_first_occurrence_and_reorder(this);
}
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/depsgraph_relation.h b/source/blender/depsgraph/intern/depsgraph_relation.h
index 2f9f0249b1f..a5d69c08d1f 100644
--- a/source/blender/depsgraph/intern/depsgraph_relation.h
+++ b/source/blender/depsgraph/intern/depsgraph_relation.h
@@ -23,7 +23,8 @@
#pragma once
-namespace DEG {
+namespace blender {
+namespace deg {
struct Node;
@@ -60,4 +61,5 @@ struct Relation {
int flag; /* Bitmask of RelationFlag) */
};
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc
index b757a4fc477..f13153545fd 100644
--- a/source/blender/depsgraph/intern/depsgraph_tag.cc
+++ b/source/blender/depsgraph/intern/depsgraph_tag.cc
@@ -70,10 +70,13 @@
#include "intern/node/deg_node_id.h"
#include "intern/node/deg_node_operation.h"
+namespace deg = blender::deg;
+
/* *********************** */
/* Update Tagging/Flushing */
-namespace DEG {
+namespace blender {
+namespace deg {
namespace {
@@ -109,7 +112,7 @@ void depsgraph_select_tag_to_component_opcode(const ID *id,
}
else if (id_type == ID_OB) {
*component_type = NodeType::OBJECT_FROM_LAYER;
- *operation_code = OperationCode::OBJECT_BASE_FLAGS;
+ *operation_code = OperationCode::OBJECT_FROM_LAYER_ENTRY;
}
else if (id_type == ID_MC) {
*component_type = NodeType::BATCH_CACHE;
@@ -510,7 +513,7 @@ void deg_graph_on_visible_update(Main *bmain, Depsgraph *graph, const bool do_ti
/* NOTE: It is possible to have this function called with `do_time=false` first and later (prior
* to evaluation though) with `do_time=true`. This means early output checks should be aware of
* this. */
- for (DEG::IDNode *id_node : graph->id_nodes) {
+ for (deg::IDNode *id_node : graph->id_nodes) {
const ID_Type id_type = GS(id_node->id_orig->name);
if (id_type == ID_OB) {
Object *object_orig = reinterpret_cast<Object *>(id_node->id_orig);
@@ -525,7 +528,7 @@ void deg_graph_on_visible_update(Main *bmain, Depsgraph *graph, const bool do_ti
continue;
}
int flag = 0;
- if (!DEG::deg_copy_on_write_is_expanded(id_node->id_cow)) {
+ if (!deg::deg_copy_on_write_is_expanded(id_node->id_cow)) {
flag |= ID_RECALC_COPY_ON_WRITE;
if (do_time) {
if (BKE_animdata_from_id(id_node->id_orig) != nullptr) {
@@ -619,7 +622,7 @@ NodeType geometry_tag_to_component(const ID *id)
void id_tag_update(Main *bmain, ID *id, int flag, eUpdateSource update_source)
{
graph_id_tag_update(bmain, nullptr, id, flag, update_source);
- for (DEG::Depsgraph *depsgraph : DEG::get_all_registered_graphs(bmain)) {
+ for (deg::Depsgraph *depsgraph : deg::get_all_registered_graphs(bmain)) {
graph_id_tag_update(bmain, depsgraph, id, flag, update_source);
}
@@ -684,7 +687,8 @@ void graph_id_tag_update(
}
}
-} // namespace DEG
+} // namespace deg
+} // namespace blender
const char *DEG_update_tag_as_string(IDRecalcFlag flag)
{
@@ -759,7 +763,7 @@ void DEG_id_tag_update_ex(Main *bmain, ID *id, int flag)
/* Ideally should not happen, but old depsgraph allowed this. */
return;
}
- DEG::id_tag_update(bmain, id, flag, DEG::DEG_UPDATE_SOURCE_USER_EDIT);
+ deg::id_tag_update(bmain, id, flag, deg::DEG_UPDATE_SOURCE_USER_EDIT);
}
void DEG_graph_id_tag_update(struct Main *bmain,
@@ -767,8 +771,8 @@ void DEG_graph_id_tag_update(struct Main *bmain,
struct ID *id,
int flag)
{
- DEG::Depsgraph *graph = (DEG::Depsgraph *)depsgraph;
- DEG::graph_id_tag_update(bmain, graph, id, flag, DEG::DEG_UPDATE_SOURCE_USER_EDIT);
+ deg::Depsgraph *graph = (deg::Depsgraph *)depsgraph;
+ deg::graph_id_tag_update(bmain, graph, id, flag, deg::DEG_UPDATE_SOURCE_USER_EDIT);
}
/* Mark a particular datablock type as having changing. */
@@ -784,13 +788,13 @@ void DEG_graph_id_type_tag(Depsgraph *depsgraph, short id_type)
DEG_graph_id_type_tag(depsgraph, ID_SCE);
}
const int id_type_index = BKE_idtype_idcode_to_index(id_type);
- DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(depsgraph);
+ deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(depsgraph);
deg_graph->id_type_updated[id_type_index] = 1;
}
void DEG_id_type_tag(Main *bmain, short id_type)
{
- for (DEG::Depsgraph *depsgraph : DEG::get_all_registered_graphs(bmain)) {
+ for (deg::Depsgraph *depsgraph : deg::get_all_registered_graphs(bmain)) {
DEG_graph_id_type_tag(reinterpret_cast<::Depsgraph *>(depsgraph), id_type);
}
}
@@ -798,13 +802,13 @@ void DEG_id_type_tag(Main *bmain, short id_type)
/* Update dependency graph when visible scenes/layers changes. */
void DEG_graph_on_visible_update(Main *bmain, Depsgraph *depsgraph, const bool do_time)
{
- DEG::Depsgraph *graph = (DEG::Depsgraph *)depsgraph;
- DEG::deg_graph_on_visible_update(bmain, graph, do_time);
+ deg::Depsgraph *graph = (deg::Depsgraph *)depsgraph;
+ deg::deg_graph_on_visible_update(bmain, graph, do_time);
}
void DEG_on_visible_update(Main *bmain, const bool do_time)
{
- for (DEG::Depsgraph *depsgraph : DEG::get_all_registered_graphs(bmain)) {
+ for (deg::Depsgraph *depsgraph : deg::get_all_registered_graphs(bmain)) {
DEG_graph_on_visible_update(bmain, reinterpret_cast<::Depsgraph *>(depsgraph), do_time);
}
}
@@ -821,7 +825,7 @@ void DEG_ids_check_recalc(
update_ctx.depsgraph = depsgraph;
update_ctx.scene = scene;
update_ctx.view_layer = view_layer;
- DEG::deg_editors_scene_update(&update_ctx, updated);
+ deg::deg_editors_scene_update(&update_ctx, updated);
}
static void deg_graph_clear_id_recalc_flags(ID *id)
@@ -837,14 +841,14 @@ static void deg_graph_clear_id_recalc_flags(ID *id)
void DEG_ids_clear_recalc(Main *UNUSED(bmain), Depsgraph *depsgraph)
{
- DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(depsgraph);
+ deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(depsgraph);
/* TODO(sergey): Re-implement POST_UPDATE_HANDLER_WORKAROUND using entry_tags
* and id_tags storage from the new dependency graph. */
if (!DEG_id_type_any_updated(depsgraph)) {
return;
}
/* Go over all ID nodes nodes, clearing tags. */
- for (DEG::IDNode *id_node : deg_graph->id_nodes) {
+ for (deg::IDNode *id_node : deg_graph->id_nodes) {
/* TODO: we clear original ID recalc flags here, but this may not work
* correctly when there are multiple depsgraph with others still using
* the recalc flag. */
diff --git a/source/blender/depsgraph/intern/depsgraph_tag.h b/source/blender/depsgraph/intern/depsgraph_tag.h
index e79372f2459..68b6a164be4 100644
--- a/source/blender/depsgraph/intern/depsgraph_tag.h
+++ b/source/blender/depsgraph/intern/depsgraph_tag.h
@@ -26,7 +26,8 @@
struct ID;
struct Main;
-namespace DEG {
+namespace blender {
+namespace deg {
struct Depsgraph;
@@ -40,4 +41,5 @@ void id_tag_update(Main *bmain, ID *id, int flag, eUpdateSource update_source);
void graph_id_tag_update(
Main *bmain, Depsgraph *graph, ID *id, int flag, eUpdateSource update_source);
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/depsgraph_type.cc b/source/blender/depsgraph/intern/depsgraph_type.cc
index 92d775b0ae4..25e9a4643b3 100644
--- a/source/blender/depsgraph/intern/depsgraph_type.cc
+++ b/source/blender/depsgraph/intern/depsgraph_type.cc
@@ -37,13 +37,15 @@
#include "intern/node/deg_node_factory.h"
#include "intern/node/deg_node_operation.h"
+namespace deg = blender::deg;
+
/* Register all node types */
void DEG_register_node_types(void)
{
/* register node types */
- DEG::deg_register_base_depsnodes();
- DEG::deg_register_component_depsnodes();
- DEG::deg_register_operation_depsnodes();
+ deg::deg_register_base_depsnodes();
+ deg::deg_register_component_depsnodes();
+ deg::deg_register_operation_depsnodes();
}
/* Free registry on exit */
@@ -51,7 +53,7 @@ void DEG_free_node_types(void)
{
}
-DEG::DEGCustomDataMeshMasks::DEGCustomDataMeshMasks(const CustomData_MeshMasks *other)
+deg::DEGCustomDataMeshMasks::DEGCustomDataMeshMasks(const CustomData_MeshMasks *other)
: vert_mask(other->vmask),
edge_mask(other->emask),
face_mask(other->fmask),
diff --git a/source/blender/depsgraph/intern/depsgraph_type.h b/source/blender/depsgraph/intern/depsgraph_type.h
index 3d386695e6c..50f697323c4 100644
--- a/source/blender/depsgraph/intern/depsgraph_type.h
+++ b/source/blender/depsgraph/intern/depsgraph_type.h
@@ -50,20 +50,13 @@ struct Depsgraph;
struct CustomData_MeshMasks;
-namespace DEG {
+namespace blender {
+namespace deg {
/* Commonly used types. */
-using blender::Map;
-using blender::Set;
-using blender::Span;
-using blender::StringRef;
-using blender::StringRefNull;
-using blender::Vector;
-using blender::VectorSet;
using std::deque;
-using std::map;
+using std::optional;
using std::pair;
-using std::set;
using std::string;
using std::unique_ptr;
@@ -176,4 +169,5 @@ struct DEGCustomDataMeshMasks {
}
};
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/depsgraph_update.cc b/source/blender/depsgraph/intern/depsgraph_update.cc
index d10bfaaace8..98ff136f7bc 100644
--- a/source/blender/depsgraph/intern/depsgraph_update.cc
+++ b/source/blender/depsgraph/intern/depsgraph_update.cc
@@ -27,7 +27,10 @@
#include "intern/depsgraph_type.h"
-namespace DEG {
+namespace deg = blender::deg;
+
+namespace blender {
+namespace deg {
static DEG_EditorUpdateIDCb deg_editor_update_id_cb = nullptr;
static DEG_EditorUpdateSceneCb deg_editor_update_scene_cb = nullptr;
@@ -46,11 +49,12 @@ void deg_editors_scene_update(const DEGEditorUpdateContext *update_ctx, bool upd
}
}
-} // namespace DEG
+} // namespace deg
+} // namespace blender
/* Set callbacks which are being called when depsgraph changes. */
void DEG_editors_set_update_cb(DEG_EditorUpdateIDCb id_func, DEG_EditorUpdateSceneCb scene_func)
{
- DEG::deg_editor_update_id_cb = id_func;
- DEG::deg_editor_update_scene_cb = scene_func;
+ deg::deg_editor_update_id_cb = id_func;
+ deg::deg_editor_update_scene_cb = scene_func;
}
diff --git a/source/blender/depsgraph/intern/depsgraph_update.h b/source/blender/depsgraph/intern/depsgraph_update.h
index 1723658ced3..6f40a4366a5 100644
--- a/source/blender/depsgraph/intern/depsgraph_update.h
+++ b/source/blender/depsgraph/intern/depsgraph_update.h
@@ -26,10 +26,12 @@
struct DEGEditorUpdateContext;
struct ID;
-namespace DEG {
+namespace blender {
+namespace deg {
void deg_editors_id_update(const DEGEditorUpdateContext *update_ctx, struct ID *id);
void deg_editors_scene_update(const DEGEditorUpdateContext *update_ctx, bool updated);
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/eval/deg_eval.cc b/source/blender/depsgraph/intern/eval/deg_eval.cc
index 189beb506b3..6ca30a67f1f 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval.cc
@@ -54,7 +54,8 @@
#include "intern/node/deg_node_operation.h"
#include "intern/node/deg_node_time.h"
-namespace DEG {
+namespace blender {
+namespace deg {
namespace {
@@ -416,4 +417,5 @@ void deg_evaluate_on_refresh(Depsgraph *graph)
graph->debug.end_graph_evaluation();
}
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/eval/deg_eval.h b/source/blender/depsgraph/intern/eval/deg_eval.h
index 5baf13653ca..49690f15412 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval.h
+++ b/source/blender/depsgraph/intern/eval/deg_eval.h
@@ -25,7 +25,8 @@
#pragma once
-namespace DEG {
+namespace blender {
+namespace deg {
struct Depsgraph;
@@ -38,4 +39,5 @@ struct Depsgraph;
*/
void deg_evaluate_on_refresh(Depsgraph *graph);
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc
index 70a6875f1c0..2290c37fc2a 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc
@@ -100,7 +100,8 @@
#include "intern/node/deg_node.h"
#include "intern/node/deg_node_id.h"
-namespace DEG {
+namespace blender {
+namespace deg {
#define DEBUG_PRINT \
if (G.debug & G_DEBUG_DEPSGRAPH_EVAL) \
@@ -945,7 +946,7 @@ ID *deg_expand_copy_on_write_datablock(const Depsgraph *depsgraph,
DepsgraphNodeBuilder *node_builder,
bool create_placeholders)
{
- DEG::IDNode *id_node = depsgraph->find_id_node(id_orig);
+ IDNode *id_node = depsgraph->find_id_node(id_orig);
BLI_assert(id_node != nullptr);
return deg_expand_copy_on_write_datablock(depsgraph, id_node, node_builder, create_placeholders);
}
@@ -969,7 +970,7 @@ ID *deg_update_copy_on_write_datablock(const Depsgraph *depsgraph, const IDNode
/* NOTE: Depsgraph is supposed to have ID node already. */
ID *deg_update_copy_on_write_datablock(const Depsgraph *depsgraph, ID *id_orig)
{
- DEG::IDNode *id_node = depsgraph->find_id_node(id_orig);
+ IDNode *id_node = depsgraph->find_id_node(id_orig);
BLI_assert(id_node != nullptr);
return deg_update_copy_on_write_datablock(depsgraph, id_node);
}
@@ -1089,7 +1090,7 @@ void deg_free_copy_on_write_datablock(ID *id_cow)
void deg_evaluate_copy_on_write(struct ::Depsgraph *graph, const IDNode *id_node)
{
- const DEG::Depsgraph *depsgraph = reinterpret_cast<const DEG::Depsgraph *>(graph);
+ const Depsgraph *depsgraph = reinterpret_cast<const Depsgraph *>(graph);
DEG_debug_print_eval(graph, __func__, id_node->id_orig->name, id_node->id_cow);
if (id_node->id_orig == &depsgraph->scene->id) {
/* NOTE: This is handled by eval_ctx setup routines, which
@@ -1137,4 +1138,5 @@ bool deg_copy_on_write_is_needed(const ID_Type id_type)
return ID_TYPE_IS_COW(id_type);
}
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h
index 05464d11f13..255ea840088 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h
+++ b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h
@@ -43,7 +43,8 @@ struct ID;
struct Depsgraph;
-namespace DEG {
+namespace blender {
+namespace deg {
struct Depsgraph;
class DepsgraphNodeBuilder;
@@ -98,4 +99,5 @@ bool deg_copy_on_write_is_expanded(const struct ID *id_cow);
bool deg_copy_on_write_is_needed(const ID *id_orig);
bool deg_copy_on_write_is_needed(const ID_Type id_type);
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_flush.cc b/source/blender/depsgraph/intern/eval/deg_eval_flush.cc
index df6c139e916..a0a9ff7eca3 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_flush.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval_flush.cc
@@ -32,9 +32,11 @@
#include "BLI_task.h"
#include "BLI_utildefines.h"
+#include "BKE_key.h"
#include "BKE_object.h"
#include "BKE_scene.h"
+#include "DNA_key_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
@@ -67,7 +69,8 @@
// catch usage of invalid state.
#undef INVALIDATE_ON_FLUSH
-namespace DEG {
+namespace blender {
+namespace deg {
enum {
ID_STATE_NONE = 0,
@@ -227,7 +230,7 @@ void flush_editors_id_update(Depsgraph *graph, const DEGEditorUpdateContext *upd
ID *id_orig = id_node->id_orig;
ID *id_cow = id_node->id_cow;
/* Gather recalc flags from all changed components. */
- for (DEG::ComponentNode *comp_node : id_node->components.values()) {
+ for (ComponentNode *comp_node : id_node->components.values()) {
if (comp_node->custom_flags != COMPONENT_STATE_DONE) {
continue;
}
@@ -250,9 +253,30 @@ void flush_editors_id_update(Depsgraph *graph, const DEGEditorUpdateContext *upd
if (deg_copy_on_write_is_expanded(id_cow)) {
if (graph->is_active && id_node->is_user_modified) {
deg_editors_id_update(update_ctx, id_orig);
- }
- if (ID_IS_OVERRIDE_LIBRARY(id_orig)) {
- id_orig->tag |= LIB_TAG_OVERRIDE_LIBRARY_AUTOREFRESH;
+
+ /* We only want to tag an ID for lib-override auto-refresh if it was actually tagged as
+ * changed. CoW IDs indirectly modified because of changes in other IDs should never
+ * require a lib-override diffing. */
+ if (ID_IS_OVERRIDE_LIBRARY_REAL(id_orig)) {
+ id_orig->tag |= LIB_TAG_OVERRIDE_LIBRARY_AUTOREFRESH;
+ }
+ else if (ID_IS_OVERRIDE_LIBRARY_VIRTUAL(id_orig)) {
+ switch (GS(id_orig->name)) {
+ case ID_KE:
+ ((Key *)id_orig)->from->tag |= LIB_TAG_OVERRIDE_LIBRARY_AUTOREFRESH;
+ break;
+ case ID_GR:
+ BLI_assert(id_orig->flag & LIB_EMBEDDED_DATA);
+ /* TODO. */
+ break;
+ case ID_NT:
+ BLI_assert(id_orig->flag & LIB_EMBEDDED_DATA);
+ /* TODO. */
+ break;
+ default:
+ BLI_assert(0);
+ }
+ }
}
/* Inform draw engines that something was changed. */
flush_engine_data_update(id_cow);
@@ -336,9 +360,9 @@ void deg_graph_flush_updates(Main *bmain, Depsgraph *graph)
if (graph->need_update_time) {
const Scene *scene_orig = graph->scene;
const float ctime = BKE_scene_frame_get(scene_orig);
- DEG::TimeSourceNode *time_source = graph->find_time_source();
+ TimeSourceNode *time_source = graph->find_time_source();
graph->ctime = ctime;
- time_source->tag_update(graph, DEG::DEG_UPDATE_SOURCE_TIME);
+ time_source->tag_update(graph, DEG_UPDATE_SOURCE_TIME);
}
if (graph->entry_tags.is_empty()) {
return;
@@ -389,4 +413,5 @@ void deg_graph_clear_tags(Depsgraph *graph)
graph->entry_tags.clear();
}
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_flush.h b/source/blender/depsgraph/intern/eval/deg_eval_flush.h
index a4550dd4851..c76dc9fe01d 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_flush.h
+++ b/source/blender/depsgraph/intern/eval/deg_eval_flush.h
@@ -27,7 +27,8 @@
struct Main;
-namespace DEG {
+namespace blender {
+namespace deg {
struct Depsgraph;
@@ -39,4 +40,5 @@ void deg_graph_flush_updates(struct Main *bmain, struct Depsgraph *graph);
/* Clear tags from all operation nodes. */
void deg_graph_clear_tags(struct Depsgraph *graph);
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc
index c3733cb235c..f3d9422a88b 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc
@@ -29,7 +29,8 @@
#include "DRW_engine.h"
-namespace DEG {
+namespace blender {
+namespace deg {
RuntimeBackup::RuntimeBackup(const Depsgraph *depsgraph)
: have_backup(false),
@@ -116,4 +117,5 @@ void RuntimeBackup::restore_to_id(ID *id)
}
}
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.h
index dde7d0b2782..c6249c83daa 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.h
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.h
@@ -32,7 +32,8 @@
#include "intern/eval/deg_eval_runtime_backup_sound.h"
#include "intern/eval/deg_eval_runtime_backup_volume.h"
-namespace DEG {
+namespace blender {
+namespace deg {
struct Depsgraph;
@@ -67,4 +68,5 @@ class RuntimeBackup {
VolumeBackup volume_backup;
};
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.cc
index 29f70e8548e..6c4eb8a91ee 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.cc
@@ -32,7 +32,8 @@
#include "intern/depsgraph.h"
-namespace DEG {
+namespace blender {
+namespace deg {
namespace {
@@ -141,4 +142,5 @@ void AnimationBackup::restore_to_id(ID *id)
}
}
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.h
index d021354e6f2..6b5d5eab75f 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.h
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.h
@@ -27,7 +27,8 @@
#include "intern/depsgraph_type.h"
-namespace DEG {
+namespace blender {
+namespace deg {
struct Depsgraph;
@@ -62,4 +63,5 @@ class AnimationBackup {
Vector<AnimationValueBackup> values_backup;
};
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_modifier.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_modifier.cc
index 3361c26a077..f2d9a87ca9d 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_modifier.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_modifier.cc
@@ -23,7 +23,8 @@
#include "intern/eval/deg_eval_runtime_backup_modifier.h"
-namespace DEG {
+namespace blender {
+namespace deg {
ModifierDataBackupID::ModifierDataBackupID(const Depsgraph * /*depsgraph*/)
: ModifierDataBackupID(nullptr, eModifierType_None)
@@ -35,15 +36,16 @@ ModifierDataBackupID::ModifierDataBackupID(ModifierData *modifier_data, Modifier
{
}
-bool ModifierDataBackupID::operator<(const ModifierDataBackupID &other) const
+bool operator==(const ModifierDataBackupID &a, const ModifierDataBackupID &b)
{
- if (modifier_data < other.modifier_data) {
- return true;
- }
- if (modifier_data == other.modifier_data) {
- return static_cast<int>(type) < static_cast<int>(other.type);
- }
- return false;
+ return a.modifier_data == b.modifier_data && a.type == b.type;
}
-} // namespace DEG
+uint32_t ModifierDataBackupID::hash() const
+{
+ uintptr_t ptr = (uintptr_t)modifier_data;
+ return (ptr >> 4) ^ (uintptr_t)type;
+}
+
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_modifier.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_modifier.h
index 4b3d46126f3..dc16bdcc1b8 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_modifier.h
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_modifier.h
@@ -29,7 +29,8 @@
struct ModifierData;
-namespace DEG {
+namespace blender {
+namespace deg {
struct Depsgraph;
@@ -46,13 +47,16 @@ class ModifierDataBackupID {
ModifierDataBackupID(const Depsgraph *depsgraph);
ModifierDataBackupID(ModifierData *modifier_data, ModifierType type);
- bool operator<(const ModifierDataBackupID &other) const;
+ friend bool operator==(const ModifierDataBackupID &a, const ModifierDataBackupID &b);
+
+ uint32_t hash() const;
ModifierData *modifier_data;
ModifierType type;
};
/* Storage for backed up runtime modifier data. */
-typedef map<ModifierDataBackupID, void *> ModifierRuntimeDataBackup;
+typedef Map<ModifierDataBackupID, void *> ModifierRuntimeDataBackup;
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_movieclip.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_movieclip.cc
index d552c8da99a..9d3740258cc 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_movieclip.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_movieclip.cc
@@ -27,7 +27,8 @@
#include "BLI_utildefines.h"
-namespace DEG {
+namespace blender {
+namespace deg {
MovieClipBackup::MovieClipBackup(const Depsgraph * /*depsgraph*/)
{
@@ -58,4 +59,5 @@ void MovieClipBackup::restore_to_movieclip(MovieClip *movieclip)
reset();
}
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_movieclip.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_movieclip.h
index 427e9c7b483..0b1de633696 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_movieclip.h
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_movieclip.h
@@ -27,7 +27,8 @@ struct MovieClip;
struct MovieClipCache;
struct anim;
-namespace DEG {
+namespace blender {
+namespace deg {
struct Depsgraph;
@@ -45,4 +46,5 @@ class MovieClipBackup {
struct MovieClipCache *cache;
};
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc
index 2b172f824b6..e0957a10cb1 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc
@@ -32,7 +32,8 @@
#include "BKE_action.h"
#include "BKE_object.h"
-namespace DEG {
+namespace blender {
+namespace deg {
ObjectRuntimeBackup::ObjectRuntimeBackup(const Depsgraph * /*depsgraph*/)
: base_flag(0), base_local_view_bits(0)
@@ -75,7 +76,7 @@ void ObjectRuntimeBackup::backup_modifier_runtime_data(Object *object)
}
BLI_assert(modifier_data->orig_modifier_data != nullptr);
ModifierDataBackupID modifier_data_id = create_modifier_data_id(modifier_data);
- modifier_runtime_data.insert(make_pair(modifier_data_id, modifier_data->runtime));
+ modifier_runtime_data.add(modifier_data_id, modifier_data->runtime);
modifier_data->runtime = nullptr;
}
}
@@ -86,7 +87,7 @@ void ObjectRuntimeBackup::backup_pose_channel_runtime_data(Object *object)
LISTBASE_FOREACH (bPoseChannel *, pchan, &object->pose->chanbase) {
/* This is nullptr in Edit mode. */
if (pchan->orig_pchan != nullptr) {
- pose_channel_runtime_data[pchan->orig_pchan] = pchan->runtime;
+ pose_channel_runtime_data.add(pchan->orig_pchan, pchan->runtime);
BKE_pose_channel_runtime_reset(&pchan->runtime);
}
}
@@ -153,22 +154,16 @@ void ObjectRuntimeBackup::restore_modifier_runtime_data(Object *object)
LISTBASE_FOREACH (ModifierData *, modifier_data, &object->modifiers) {
BLI_assert(modifier_data->orig_modifier_data != nullptr);
ModifierDataBackupID modifier_data_id = create_modifier_data_id(modifier_data);
- ModifierRuntimeDataBackup::iterator runtime_data_iterator = modifier_runtime_data.find(
- modifier_data_id);
- if (runtime_data_iterator != modifier_runtime_data.end()) {
- modifier_data->runtime = runtime_data_iterator->second;
- runtime_data_iterator->second = nullptr;
+ void *runtime = modifier_runtime_data.pop_default(modifier_data_id, nullptr);
+ if (runtime != nullptr) {
+ modifier_data->runtime = runtime;
}
}
- for (ModifierRuntimeDataBackup::value_type value : modifier_runtime_data) {
- const ModifierDataBackupID modifier_data_id = value.first;
- void *runtime = value.second;
- if (value.second == nullptr) {
- continue;
- }
- const ModifierTypeInfo *modifier_type_info = BKE_modifier_get_info(modifier_data_id.type);
+
+ for (ModifierRuntimeDataBackup::Item item : modifier_runtime_data.items()) {
+ const ModifierTypeInfo *modifier_type_info = BKE_modifier_get_info(item.key.type);
BLI_assert(modifier_type_info != nullptr);
- modifier_type_info->freeRuntimeData(runtime);
+ modifier_type_info->freeRuntimeData(item.value);
}
}
@@ -178,18 +173,18 @@ void ObjectRuntimeBackup::restore_pose_channel_runtime_data(Object *object)
LISTBASE_FOREACH (bPoseChannel *, pchan, &object->pose->chanbase) {
/* This is nullptr in Edit mode. */
if (pchan->orig_pchan != nullptr) {
- PoseChannelRuntimeDataBackup::iterator runtime_data_iterator =
- pose_channel_runtime_data.find(pchan->orig_pchan);
- if (runtime_data_iterator != pose_channel_runtime_data.end()) {
- pchan->runtime = runtime_data_iterator->second;
- pose_channel_runtime_data.erase(runtime_data_iterator);
+ optional<bPoseChannel_Runtime> runtime = pose_channel_runtime_data.pop_try(
+ pchan->orig_pchan);
+ if (runtime.has_value()) {
+ pchan->runtime = *runtime;
}
}
}
}
- for (PoseChannelRuntimeDataBackup::value_type &value : pose_channel_runtime_data) {
- BKE_pose_channel_runtime_free(&value.second);
+ for (bPoseChannel_Runtime &runtime : pose_channel_runtime_data.values()) {
+ BKE_pose_channel_runtime_free(&runtime);
}
}
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.h
index e5c3d6a967a..04d7fb1bc22 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.h
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.h
@@ -30,7 +30,8 @@
struct Object;
-namespace DEG {
+namespace blender {
+namespace deg {
class ObjectRuntimeBackup {
public:
@@ -53,7 +54,8 @@ class ObjectRuntimeBackup {
short base_flag;
unsigned short base_local_view_bits;
ModifierRuntimeDataBackup modifier_runtime_data;
- PoseChannelRuntimeDataBackup pose_channel_runtime_data;
+ Map<bPoseChannel *, bPoseChannel_Runtime> pose_channel_runtime_data;
};
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_pose.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_pose.cc
index 821cc21f359..45663378f67 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_pose.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_pose.cc
@@ -23,6 +23,8 @@
#include "intern/eval/deg_eval_runtime_backup_pose.h"
-namespace DEG {
+namespace blender {
+namespace deg {
-} // namespace DEG
+}
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_pose.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_pose.h
index 53a2c4c0784..a6ce97529b7 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_pose.h
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_pose.h
@@ -27,11 +27,8 @@
#include "DNA_action_types.h"
-struct bPoseChannel;
+namespace blender {
+namespace deg {
-namespace DEG {
-
-/* Storage for backed up pose channel runtime data. */
-typedef map<bPoseChannel *, bPoseChannel_Runtime> PoseChannelRuntimeDataBackup;
-
-} // namespace DEG
+}
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_scene.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_scene.cc
index 32b2d0b93c1..f000c8b5d4d 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_scene.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_scene.cc
@@ -28,7 +28,8 @@
#include "DNA_rigidbody_types.h"
#include "DNA_scene_types.h"
-namespace DEG {
+namespace blender {
+namespace deg {
SceneBackup::SceneBackup(const Depsgraph *depsgraph) : sequencer_backup(depsgraph)
{
@@ -85,4 +86,5 @@ void SceneBackup::restore_to_scene(Scene *scene)
reset();
}
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_scene.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_scene.h
index 751bc4208d2..007236e7890 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_scene.h
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_scene.h
@@ -27,7 +27,8 @@
struct Scene;
-namespace DEG {
+namespace blender {
+namespace deg {
struct Depsgraph;
@@ -55,4 +56,5 @@ class SceneBackup {
SequencerBackup sequencer_backup;
};
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequence.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequence.cc
index f26d78d3138..3866a89cc17 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequence.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequence.cc
@@ -25,7 +25,8 @@
#include "DNA_sequence_types.h"
-namespace DEG {
+namespace blender {
+namespace deg {
SequenceBackup::SequenceBackup(const Depsgraph * /*depsgraph*/)
{
@@ -55,4 +56,5 @@ bool SequenceBackup::isEmpty() const
return (scene_sound == nullptr);
}
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequence.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequence.h
index 8a762a2785e..eb38dc3dc5b 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequence.h
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequence.h
@@ -25,7 +25,8 @@
struct Sequence;
-namespace DEG {
+namespace blender {
+namespace deg {
struct Depsgraph;
@@ -44,4 +45,5 @@ class SequenceBackup {
void *scene_sound;
};
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequencer.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequencer.cc
index adc7fd570e8..2780938fe05 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequencer.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequencer.cc
@@ -29,7 +29,8 @@
#include "BKE_sequencer.h"
#include "BKE_sound.h"
-namespace DEG {
+namespace blender {
+namespace deg {
SequencerBackup::SequencerBackup(const Depsgraph *depsgraph) : depsgraph(depsgraph)
{
@@ -42,7 +43,7 @@ void SequencerBackup::init_from_scene(Scene *scene)
SequenceBackup sequence_backup(depsgraph);
sequence_backup.init_from_sequence(sequence);
if (!sequence_backup.isEmpty()) {
- sequences_backup.insert(make_pair(sequence->orig_sequence, sequence_backup));
+ sequences_backup.add(sequence->orig_sequence, sequence_backup);
}
}
SEQ_END;
@@ -52,21 +53,19 @@ void SequencerBackup::restore_to_scene(Scene *scene)
{
Sequence *sequence;
SEQ_BEGIN (scene->ed, sequence) {
- SequencesBackupMap::iterator it = sequences_backup.find(sequence->orig_sequence);
- if (it == sequences_backup.end()) {
- continue;
+ SequenceBackup *sequence_backup = sequences_backup.lookup_ptr(sequence->orig_sequence);
+ if (sequence_backup != nullptr) {
+ sequence_backup->restore_to_sequence(sequence);
}
- SequenceBackup &sequence_backup = it->second;
- sequence_backup.restore_to_sequence(sequence);
}
SEQ_END;
/* Cleanup audio while the scene is still known. */
- for (SequencesBackupMap::value_type &it : sequences_backup) {
- SequenceBackup &sequence_backup = it.second;
+ for (SequenceBackup &sequence_backup : sequences_backup.values()) {
if (sequence_backup.scene_sound != nullptr) {
BKE_sound_remove_scene_sound(scene, sequence_backup.scene_sound);
}
}
}
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequencer.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequencer.h
index 05f37b45dc4..9fe38ec270c 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequencer.h
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequencer.h
@@ -28,7 +28,8 @@
struct Scene;
-namespace DEG {
+namespace blender {
+namespace deg {
struct Depsgraph;
@@ -42,8 +43,8 @@ class SequencerBackup {
const Depsgraph *depsgraph;
- typedef map<Sequence *, SequenceBackup> SequencesBackupMap;
- SequencesBackupMap sequences_backup;
+ Map<Sequence *, SequenceBackup> sequences_backup;
};
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sound.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sound.cc
index f427d57a8ef..4b63ada8bde 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sound.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sound.cc
@@ -27,7 +27,8 @@
#include "DNA_sound_types.h"
-namespace DEG {
+namespace blender {
+namespace deg {
SoundBackup::SoundBackup(const Depsgraph * /*depsgraph*/)
{
@@ -61,4 +62,5 @@ void SoundBackup::restore_to_sound(bSound *sound)
reset();
}
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sound.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sound.h
index 87783146701..754deb57556 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sound.h
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sound.h
@@ -25,7 +25,8 @@
struct bSound;
-namespace DEG {
+namespace blender {
+namespace deg {
struct Depsgraph;
@@ -44,4 +45,5 @@ class SoundBackup {
void *playback_handle;
};
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_volume.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_volume.cc
index 09e13ec131d..8808673ce6a 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_volume.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_volume.cc
@@ -33,7 +33,8 @@
#include <stdio.h>
-namespace DEG {
+namespace blender {
+namespace deg {
VolumeBackup::VolumeBackup(const Depsgraph * /*depsgraph*/) : grids(nullptr)
{
@@ -57,4 +58,5 @@ void VolumeBackup::restore_to_volume(Volume *volume)
}
}
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_volume.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_volume.h
index cf57c702c8f..c0ef5204653 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_volume.h
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_volume.h
@@ -26,7 +26,8 @@
struct Volume;
struct VolumeGridVector;
-namespace DEG {
+namespace blender {
+namespace deg {
struct Depsgraph;
@@ -42,4 +43,5 @@ class VolumeBackup {
char filepath[1024]; /* FILE_MAX */
};
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_stats.cc b/source/blender/depsgraph/intern/eval/deg_eval_stats.cc
index 9d3b1356570..3c84c781cbb 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_stats.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval_stats.cc
@@ -32,7 +32,8 @@
#include "intern/node/deg_node_id.h"
#include "intern/node/deg_node_operation.h"
-namespace DEG {
+namespace blender {
+namespace deg {
void deg_eval_stats_aggregate(Depsgraph *graph)
{
@@ -54,4 +55,5 @@ void deg_eval_stats_aggregate(Depsgraph *graph)
}
}
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_stats.h b/source/blender/depsgraph/intern/eval/deg_eval_stats.h
index 988b42e15ae..8f0d3b1b938 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_stats.h
+++ b/source/blender/depsgraph/intern/eval/deg_eval_stats.h
@@ -23,11 +23,13 @@
#pragma once
-namespace DEG {
+namespace blender {
+namespace deg {
struct Depsgraph;
/* Aggregate operation timings to overall component and ID nodes timing. */
void deg_eval_stats_aggregate(Depsgraph *graph);
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/node/deg_node.cc b/source/blender/depsgraph/intern/node/deg_node.cc
index 10760d3170b..54a22a07ee4 100644
--- a/source/blender/depsgraph/intern/node/deg_node.cc
+++ b/source/blender/depsgraph/intern/node/deg_node.cc
@@ -36,7 +36,8 @@
#include "intern/node/deg_node_operation.h"
#include "intern/node/deg_node_time.h"
-namespace DEG {
+namespace blender {
+namespace deg {
const char *nodeClassAsString(NodeClass node_class)
{
@@ -340,4 +341,5 @@ void deg_register_base_depsnodes()
register_node_typeinfo(&DNTI_ID_REF);
}
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/node/deg_node.h b/source/blender/depsgraph/intern/node/deg_node.h
index f0ce38ddeae..94953c4ec5d 100644
--- a/source/blender/depsgraph/intern/node/deg_node.h
+++ b/source/blender/depsgraph/intern/node/deg_node.h
@@ -32,7 +32,8 @@
struct ID;
struct Scene;
-namespace DEG {
+namespace blender {
+namespace deg {
struct Depsgraph;
struct OperationNode;
@@ -212,4 +213,5 @@ struct Node {
void deg_register_base_depsnodes();
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/node/deg_node_component.cc b/source/blender/depsgraph/intern/node/deg_node_component.cc
index 87d704bb0a0..c99b3aba312 100644
--- a/source/blender/depsgraph/intern/node/deg_node_component.cc
+++ b/source/blender/depsgraph/intern/node/deg_node_component.cc
@@ -38,7 +38,8 @@
#include "intern/node/deg_node_id.h"
#include "intern/node/deg_node_operation.h"
-namespace DEG {
+namespace blender {
+namespace deg {
/* *********** */
/* Outer Nodes */
@@ -377,4 +378,5 @@ void deg_register_component_depsnodes()
register_node_typeinfo(&DNTI_SIMULATION);
}
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/node/deg_node_component.h b/source/blender/depsgraph/intern/node/deg_node_component.h
index 036baa9d46c..3757a1dea5b 100644
--- a/source/blender/depsgraph/intern/node/deg_node_component.h
+++ b/source/blender/depsgraph/intern/node/deg_node_component.h
@@ -32,7 +32,8 @@
struct ID;
struct bPoseChannel;
-namespace DEG {
+namespace blender {
+namespace deg {
struct BoneComponentNode;
struct Depsgraph;
@@ -83,12 +84,9 @@ struct ComponentNode : public Node {
* when node may have been partially created earlier (e.g. parent ref before
* parent item is added)
*
- * \param type: Operation node type (corresponding to context/component that
- * it operates in)
- * \param optype: Role that operation plays within component
- * (i.e. where in eval process)
- * \param op: The operation to perform
- * \param name: Identifier for operation - used to find/locate it again */
+ * \param opcode: The operation to perform.
+ * \param name: Identifier for operation - used to find/locate it again.
+ */
OperationNode *add_operation(const DepsEvalOperationCb &op,
OperationCode opcode,
const char *name,
@@ -203,4 +201,5 @@ struct BoneComponentNode : public ComponentNode {
void deg_register_component_depsnodes();
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/node/deg_node_factory.cc b/source/blender/depsgraph/intern/node/deg_node_factory.cc
index 9dfd018b4fd..9bb093139a1 100644
--- a/source/blender/depsgraph/intern/node/deg_node_factory.cc
+++ b/source/blender/depsgraph/intern/node/deg_node_factory.cc
@@ -23,7 +23,8 @@
#include "intern/node/deg_node_factory.h"
-namespace DEG {
+namespace blender {
+namespace deg {
/* Global type registry */
static DepsNodeFactory *node_typeinfo_registry[static_cast<int>(NodeType::NUM_TYPES)] = {nullptr};
@@ -42,4 +43,5 @@ DepsNodeFactory *type_get_factory(const NodeType type)
return node_typeinfo_registry[type_as_int];
}
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/node/deg_node_factory.h b/source/blender/depsgraph/intern/node/deg_node_factory.h
index db2c3bb2f44..125f340a0fa 100644
--- a/source/blender/depsgraph/intern/node/deg_node_factory.h
+++ b/source/blender/depsgraph/intern/node/deg_node_factory.h
@@ -30,7 +30,8 @@
struct ID;
-namespace DEG {
+namespace blender {
+namespace deg {
struct DepsNodeFactory {
virtual NodeType type() const = 0;
@@ -56,6 +57,7 @@ void register_node_typeinfo(DepsNodeFactory *factory);
/* Get typeinfo for specified type */
DepsNodeFactory *type_get_factory(const NodeType type);
-} // namespace DEG
+} // namespace deg
+} // namespace blender
#include "intern/node/deg_node_factory_impl.h"
diff --git a/source/blender/depsgraph/intern/node/deg_node_factory_impl.h b/source/blender/depsgraph/intern/node/deg_node_factory_impl.h
index ad25ffdf26c..f5dd7122fca 100644
--- a/source/blender/depsgraph/intern/node/deg_node_factory_impl.h
+++ b/source/blender/depsgraph/intern/node/deg_node_factory_impl.h
@@ -27,7 +27,8 @@
struct ID;
-namespace DEG {
+namespace blender {
+namespace deg {
template<class ModeObjectType> NodeType DepsNodeFactoryImpl<ModeObjectType>::type() const
{
@@ -63,4 +64,5 @@ Node *DepsNodeFactoryImpl<ModeObjectType>::create_node(const ID *id,
return node;
}
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/node/deg_node_id.cc b/source/blender/depsgraph/intern/node/deg_node_id.cc
index 984873fbcac..843f59b446a 100644
--- a/source/blender/depsgraph/intern/node/deg_node_id.cc
+++ b/source/blender/depsgraph/intern/node/deg_node_id.cc
@@ -41,7 +41,8 @@
#include "intern/node/deg_node_factory.h"
#include "intern/node/deg_node_time.h"
-namespace DEG {
+namespace blender {
+namespace deg {
const char *linkedStateAsString(eDepsNode_LinkedState_Type linked_state)
{
@@ -212,4 +213,5 @@ IDComponentsMask IDNode::get_visible_components_mask() const
return result;
}
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/node/deg_node_id.h b/source/blender/depsgraph/intern/node/deg_node_id.h
index 1e315195c1a..9bd6130bbdc 100644
--- a/source/blender/depsgraph/intern/node/deg_node_id.h
+++ b/source/blender/depsgraph/intern/node/deg_node_id.h
@@ -28,7 +28,8 @@
#include "DNA_ID.h"
#include "intern/node/deg_node.h"
-namespace DEG {
+namespace blender {
+namespace deg {
struct ComponentNode;
@@ -115,4 +116,5 @@ struct IDNode : public Node {
DEG_DEPSNODE_DECLARE;
};
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/node/deg_node_operation.cc b/source/blender/depsgraph/intern/node/deg_node_operation.cc
index 91bd0117f6c..680e7757ebb 100644
--- a/source/blender/depsgraph/intern/node/deg_node_operation.cc
+++ b/source/blender/depsgraph/intern/node/deg_node_operation.cc
@@ -32,7 +32,8 @@
#include "intern/node/deg_node_factory.h"
#include "intern/node/deg_node_id.h"
-namespace DEG {
+namespace blender {
+namespace deg {
const char *operationCodeAsString(OperationCode opcode)
{
@@ -63,8 +64,12 @@ const char *operationCodeAsString(OperationCode opcode)
case OperationCode::AUDIO_VOLUME:
return "AUDIO_VOLUME";
/* Object related. */
+ case OperationCode::OBJECT_FROM_LAYER_ENTRY:
+ return "OBJECT_FROM_LAYER_ENTRY";
case OperationCode::OBJECT_BASE_FLAGS:
return "OBJECT_BASE_FLAGS";
+ case OperationCode::OBJECT_FROM_LAYER_EXIT:
+ return "OBJECT_FROM_LAYER_EXIT";
case OperationCode::DIMENSIONS:
return "DIMENSIONS";
/* Transform. */
@@ -266,4 +271,5 @@ void deg_register_operation_depsnodes()
register_node_typeinfo(&DNTI_OPERATION);
}
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/node/deg_node_operation.h b/source/blender/depsgraph/intern/node/deg_node_operation.h
index 6b14e6af02f..87168fc3659 100644
--- a/source/blender/depsgraph/intern/node/deg_node_operation.h
+++ b/source/blender/depsgraph/intern/node/deg_node_operation.h
@@ -29,7 +29,8 @@
struct Depsgraph;
-namespace DEG {
+namespace blender {
+namespace deg {
struct ComponentNode;
@@ -63,7 +64,9 @@ enum class OperationCode {
AUDIO_VOLUME,
/* Object related. ------------------------------------------------------ */
+ OBJECT_FROM_LAYER_ENTRY,
OBJECT_BASE_FLAGS,
+ OBJECT_FROM_LAYER_EXIT,
DIMENSIONS,
/* Transform. ----------------------------------------------------------- */
@@ -274,4 +277,5 @@ struct OperationNode : public Node {
void deg_register_operation_depsnodes();
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/node/deg_node_time.cc b/source/blender/depsgraph/intern/node/deg_node_time.cc
index ff3e950bb44..af931fbae34 100644
--- a/source/blender/depsgraph/intern/node/deg_node_time.cc
+++ b/source/blender/depsgraph/intern/node/deg_node_time.cc
@@ -28,7 +28,8 @@
#include "intern/depsgraph.h"
#include "intern/depsgraph_relation.h"
-namespace DEG {
+namespace blender {
+namespace deg {
void TimeSourceNode::tag_update(Depsgraph *graph, eUpdateSource /*source*/)
{
@@ -38,4 +39,5 @@ void TimeSourceNode::tag_update(Depsgraph *graph, eUpdateSource /*source*/)
}
}
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/node/deg_node_time.h b/source/blender/depsgraph/intern/node/deg_node_time.h
index 684414f7780..364c214b014 100644
--- a/source/blender/depsgraph/intern/node/deg_node_time.h
+++ b/source/blender/depsgraph/intern/node/deg_node_time.h
@@ -25,7 +25,8 @@
#include "intern/node/deg_node.h"
-namespace DEG {
+namespace blender {
+namespace deg {
/* Time Source Node. */
struct TimeSourceNode : public Node {
@@ -42,4 +43,5 @@ struct TimeSourceNode : public Node {
DEG_DEPSNODE_DECLARE;
};
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt
index a26c150cb51..1ddae11999b 100644
--- a/source/blender/draw/CMakeLists.txt
+++ b/source/blender/draw/CMakeLists.txt
@@ -214,6 +214,7 @@ data_to_c_simple(engines/eevee/shaders/effect_downsample_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/effect_downsample_cube_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/effect_gtao_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/effect_velocity_resolve_frag.glsl SRC)
+data_to_c_simple(engines/eevee/shaders/effect_velocity_tile_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/effect_minmaxz_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/effect_mist_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/effect_motion_blur_frag.glsl SRC)
@@ -224,6 +225,8 @@ data_to_c_simple(engines/eevee/shaders/effect_temporal_aa.glsl SRC)
data_to_c_simple(engines/eevee/shaders/lightprobe_planar_downsample_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/lightprobe_planar_downsample_geom.glsl SRC)
data_to_c_simple(engines/eevee/shaders/lightprobe_planar_downsample_vert.glsl SRC)
+data_to_c_simple(engines/eevee/shaders/object_motion_frag.glsl SRC)
+data_to_c_simple(engines/eevee/shaders/object_motion_vert.glsl SRC)
data_to_c_simple(engines/eevee/shaders/prepass_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/prepass_vert.glsl SRC)
data_to_c_simple(engines/eevee/shaders/shadow_accum_frag.glsl SRC)
@@ -310,6 +313,7 @@ data_to_c_simple(engines/basic/shaders/depth_frag.glsl SRC)
data_to_c_simple(engines/overlay/shaders/antialiasing_frag.glsl SRC)
data_to_c_simple(engines/overlay/shaders/antialiasing_vert.glsl SRC)
data_to_c_simple(engines/overlay/shaders/armature_dof_vert.glsl SRC)
+data_to_c_simple(engines/overlay/shaders/armature_dof_solid_frag.glsl SRC)
data_to_c_simple(engines/overlay/shaders/armature_envelope_outline_vert.glsl SRC)
data_to_c_simple(engines/overlay/shaders/armature_envelope_solid_frag.glsl SRC)
data_to_c_simple(engines/overlay/shaders/armature_envelope_solid_vert.glsl SRC)
diff --git a/source/blender/draw/DRW_select_buffer.h b/source/blender/draw/DRW_select_buffer.h
index 6ebc30d0382..66dee3a9aa9 100644
--- a/source/blender/draw/DRW_select_buffer.h
+++ b/source/blender/draw/DRW_select_buffer.h
@@ -56,16 +56,16 @@ struct ObjectOffsets {
uint vert;
};
-struct SELECTID_Context {
+typedef struct SELECTID_Context {
/* All context objects */
struct Object **objects;
- uint objects_len;
/* Array with only drawn objects. When a new object is found within the rect,
* it is added to the end of the list.
* The list is reset to any viewport or context update. */
- struct ObjectOffsets *index_offsets;
struct Object **objects_drawn;
+ struct ObjectOffsets *index_offsets;
+ uint objects_len;
uint objects_drawn_len;
/** Total number of element indices `index_offsets[object_drawn_len - 1].vert`. */
@@ -73,13 +73,13 @@ struct SELECTID_Context {
short select_mode;
+ /* rect is used to check which objects whose indexes need to be drawn. */
+ rcti last_rect;
+
/* To check for updates. */
float persmat[4][4];
bool is_dirty;
-
- /* rect is used to check which objects whose indexes need to be drawn. */
- rcti last_rect;
-};
+} SELECTID_Context;
/* draw_select_buffer.c */
bool DRW_select_buffer_elem_get(const uint sel_id,
diff --git a/source/blender/draw/engines/basic/basic_engine.c b/source/blender/draw/engines/basic/basic_engine.c
index bbc3c407f14..0dd3cc14234 100644
--- a/source/blender/draw/engines/basic/basic_engine.c
+++ b/source/blender/draw/engines/basic/basic_engine.c
@@ -131,7 +131,7 @@ static void basic_cache_init(void *vedata)
stl->g_data = MEM_callocN(sizeof(*stl->g_data), __func__);
}
- /* Twice for normal and infront objects. */
+ /* Twice for normal and in front objects. */
for (int i = 0; i < 2; i++) {
DRWState clip_state = (draw_ctx->sh_cfg == GPU_SHADER_CFG_CLIPPED) ? DRW_STATE_CLIP_PLANES : 0;
DRWState infront_state = (DRW_state_is_select() && (i == 1)) ? DRW_STATE_IN_FRONT_SELECT : 0;
diff --git a/source/blender/draw/engines/eevee/eevee_data.c b/source/blender/draw/engines/eevee/eevee_data.c
index a19af77124f..a4aa0e10198 100644
--- a/source/blender/draw/engines/eevee/eevee_data.c
+++ b/source/blender/draw/engines/eevee/eevee_data.c
@@ -24,11 +24,153 @@
#include "DRW_render.h"
+#include "BLI_ghash.h"
#include "BLI_memblock.h"
+#include "BKE_duplilist.h"
+
+#include "DEG_depsgraph_query.h"
+
+#include "GPU_vertex_buffer.h"
+
#include "eevee_lightcache.h"
#include "eevee_private.h"
+/* Motion Blur data. */
+
+static void eevee_motion_blur_mesh_data_free(void *val)
+{
+ EEVEE_GeometryMotionData *geom_mb = (EEVEE_GeometryMotionData *)val;
+ switch (geom_mb->type) {
+ case EEVEE_HAIR_GEOM_MOTION_DATA:
+ for (int i = 0; i < ARRAY_SIZE(geom_mb->vbo); i++) {
+ GPU_VERTBUF_DISCARD_SAFE(geom_mb->hair_pos[i]);
+ DRW_TEXTURE_FREE_SAFE(geom_mb->hair_pos_tx[i]);
+ }
+ break;
+
+ case EEVEE_MESH_GEOM_MOTION_DATA:
+ for (int i = 0; i < ARRAY_SIZE(geom_mb->vbo); i++) {
+ GPU_VERTBUF_DISCARD_SAFE(geom_mb->vbo[i]);
+ }
+ break;
+ }
+ MEM_freeN(val);
+}
+
+static uint eevee_object_key_hash(const void *key)
+{
+ EEVEE_ObjectKey *ob_key = (EEVEE_ObjectKey *)key;
+ uint hash = BLI_ghashutil_ptrhash(ob_key->ob);
+ hash = BLI_ghashutil_combine_hash(hash, BLI_ghashutil_ptrhash(ob_key->parent));
+ for (int i = 0; i < 16; i++) {
+ if (ob_key->id[i] != 0) {
+ hash = BLI_ghashutil_combine_hash(hash, BLI_ghashutil_inthash(ob_key->id[i]));
+ }
+ else {
+ break;
+ }
+ }
+ return hash;
+}
+
+/* Return false if equal. */
+static bool eevee_object_key_cmp(const void *a, const void *b)
+{
+ EEVEE_ObjectKey *key_a = (EEVEE_ObjectKey *)a;
+ EEVEE_ObjectKey *key_b = (EEVEE_ObjectKey *)b;
+
+ if (key_a->ob != key_b->ob) {
+ return true;
+ }
+ if (key_a->parent != key_b->parent) {
+ return true;
+ }
+ if (memcmp(key_a->id, key_b->id, sizeof(key_a->id)) != 0) {
+ return true;
+ }
+ return false;
+}
+
+void EEVEE_motion_blur_data_init(EEVEE_MotionBlurData *mb)
+{
+ if (mb->object == NULL) {
+ mb->object = BLI_ghash_new(eevee_object_key_hash, eevee_object_key_cmp, "EEVEE Object Motion");
+ }
+ if (mb->geom == NULL) {
+ mb->geom = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "EEVEE Mesh Motion");
+ }
+}
+
+void EEVEE_motion_blur_data_free(EEVEE_MotionBlurData *mb)
+{
+ if (mb->object) {
+ BLI_ghash_free(mb->object, MEM_freeN, MEM_freeN);
+ mb->object = NULL;
+ }
+ if (mb->geom) {
+ BLI_ghash_free(mb->geom, NULL, eevee_motion_blur_mesh_data_free);
+ mb->geom = NULL;
+ }
+}
+
+EEVEE_ObjectMotionData *EEVEE_motion_blur_object_data_get(EEVEE_MotionBlurData *mb,
+ Object *ob,
+ bool hair)
+{
+ if (mb->object == NULL) {
+ return NULL;
+ }
+
+ EEVEE_ObjectKey key, *key_p;
+ /* Small hack to avoid another comparisson. */
+ key.ob = (Object *)((char *)ob + hair);
+ DupliObject *dup = DRW_object_get_dupli(ob);
+ if (dup) {
+ key.parent = DRW_object_get_dupli_parent(ob);
+ memcpy(key.id, dup->persistent_id, sizeof(key.id));
+ }
+ else {
+ key.parent = key.ob;
+ memset(key.id, 0, sizeof(key.id));
+ }
+
+ EEVEE_ObjectMotionData *ob_step = BLI_ghash_lookup(mb->object, &key);
+ if (ob_step == NULL) {
+ key_p = MEM_mallocN(sizeof(*key_p), __func__);
+ memcpy(key_p, &key, sizeof(*key_p));
+
+ ob_step = MEM_callocN(sizeof(EEVEE_ObjectMotionData), __func__);
+
+ BLI_ghash_insert(mb->object, key_p, ob_step);
+ }
+ return ob_step;
+}
+
+EEVEE_GeometryMotionData *EEVEE_motion_blur_geometry_data_get(EEVEE_MotionBlurData *mb,
+ Object *ob,
+ bool hair)
+{
+ if (mb->geom == NULL) {
+ return NULL;
+ }
+
+ /* Use original data as key to ensure matching accross update. */
+ Object *ob_orig = DEG_get_original_object(ob);
+
+ void *key = (char *)ob_orig->data + hair;
+ EEVEE_GeometryMotionData *geom_step = BLI_ghash_lookup(mb->geom, key);
+ if (geom_step == NULL) {
+ geom_step = MEM_callocN(sizeof(EEVEE_GeometryMotionData), __func__);
+ geom_step->type = (hair) ? EEVEE_HAIR_GEOM_MOTION_DATA : EEVEE_MESH_GEOM_MOTION_DATA;
+ BLI_ghash_insert(mb->geom, key, geom_step);
+ }
+
+ return geom_step;
+}
+
+/* View Layer data. */
+
void EEVEE_view_layer_data_free(void *storage)
{
EEVEE_ViewLayerData *sldata = (EEVEE_ViewLayerData *)storage;
@@ -105,6 +247,8 @@ static void eevee_object_data_init(DrawData *dd)
{
EEVEE_ObjectEngineData *eevee_data = (EEVEE_ObjectEngineData *)dd;
eevee_data->shadow_caster_id = -1;
+ eevee_data->need_update = false;
+ eevee_data->geom_update = false;
}
EEVEE_ObjectEngineData *EEVEE_object_data_get(Object *ob)
diff --git a/source/blender/draw/engines/eevee/eevee_effects.c b/source/blender/draw/engines/eevee/eevee_effects.c
index ab846fe0f11..8c48ae65d9b 100644
--- a/source/blender/draw/engines/eevee/eevee_effects.c
+++ b/source/blender/draw/engines/eevee/eevee_effects.c
@@ -153,7 +153,7 @@ void EEVEE_effects_init(EEVEE_ViewLayerData *sldata,
effects->enabled_effects = 0;
effects->enabled_effects |= (G.debug_value == 9) ? EFFECT_VELOCITY_BUFFER : 0;
- effects->enabled_effects |= EEVEE_motion_blur_init(sldata, vedata, camera);
+ effects->enabled_effects |= EEVEE_motion_blur_init(sldata, vedata);
effects->enabled_effects |= EEVEE_bloom_init(sldata, vedata);
effects->enabled_effects |= EEVEE_depth_of_field_init(sldata, vedata, camera);
effects->enabled_effects |= EEVEE_temporal_sampling_init(sldata, vedata);
@@ -225,10 +225,13 @@ void EEVEE_effects_init(EEVEE_ViewLayerData *sldata,
*/
if ((effects->enabled_effects & EFFECT_VELOCITY_BUFFER) != 0) {
effects->velocity_tx = DRW_texture_pool_query_2d(
- size_fs[0], size_fs[1], GPU_RG16, &draw_engine_eevee_type);
+ size_fs[0], size_fs[1], GPU_RGBA16, &draw_engine_eevee_type);
- /* TODO output objects velocity during the mainpass. */
- // GPU_framebuffer_texture_attach(fbl->main_fb, effects->velocity_tx, 1, 0);
+ GPU_framebuffer_ensure_config(&fbl->velocity_fb,
+ {
+ GPU_ATTACHMENT_TEXTURE(dtxl->depth),
+ GPU_ATTACHMENT_TEXTURE(effects->velocity_tx),
+ });
GPU_framebuffer_ensure_config(
&fbl->velocity_resolve_fb,
@@ -328,14 +331,18 @@ void EEVEE_effects_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
}
if ((effects->enabled_effects & EFFECT_VELOCITY_BUFFER) != 0) {
+ EEVEE_MotionBlurData *mb_data = &effects->motion_blur;
+
/* This pass compute camera motions to the non moving objects. */
DRW_PASS_CREATE(psl->velocity_resolve, DRW_STATE_WRITE_COLOR);
grp = DRW_shgroup_create(EEVEE_shaders_velocity_resolve_sh_get(), psl->velocity_resolve);
DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &e_data.depth_src);
DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
- DRW_shgroup_uniform_mat4(grp, "currPersinv", effects->velocity_curr_persinv);
- DRW_shgroup_uniform_mat4(grp, "pastPersmat", effects->velocity_past_persmat);
+
+ DRW_shgroup_uniform_mat4(grp, "prevViewProjMatrix", mb_data->camera[MB_PREV].persmat);
+ DRW_shgroup_uniform_mat4(grp, "currViewProjMatrixInv", mb_data->camera[MB_CURR].persinv);
+ DRW_shgroup_uniform_mat4(grp, "nextViewProjMatrix", mb_data->camera[MB_NEXT].persmat);
DRW_shgroup_call(grp, quad, NULL);
}
}
@@ -501,17 +508,19 @@ static void EEVEE_velocity_resolve(EEVEE_Data *vedata)
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
- struct DRWView *view = effects->taa_view;
if ((effects->enabled_effects & EFFECT_VELOCITY_BUFFER) != 0) {
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
e_data.depth_src = dtxl->depth;
- DRW_view_persmat_get(view, effects->velocity_curr_persinv, true);
GPU_framebuffer_bind(fbl->velocity_resolve_fb);
DRW_draw_pass(psl->velocity_resolve);
+
+ if (psl->velocity_object) {
+ GPU_framebuffer_bind(fbl->velocity_fb);
+ DRW_draw_pass(psl->velocity_object);
+ }
}
- DRW_view_persmat_get(view, effects->velocity_past_persmat, false);
}
void EEVEE_draw_effects(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
@@ -529,6 +538,7 @@ void EEVEE_draw_effects(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
effects->target_buffer = fbl->effect_color_fb; /* next target to render to */
/* Post process stack (order matters) */
+ EEVEE_velocity_resolve(vedata);
EEVEE_motion_blur_draw(vedata);
EEVEE_depth_of_field_draw(vedata);
@@ -537,7 +547,6 @@ void EEVEE_draw_effects(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
* Velocity resolve use a hack to exclude lookdev
* spheres from creating shimmering re-projection vectors. */
EEVEE_lookdev_draw(vedata);
- EEVEE_velocity_resolve(vedata);
EEVEE_temporal_sampling_draw(vedata);
EEVEE_bloom_draw(vedata);
diff --git a/source/blender/draw/engines/eevee/eevee_engine.c b/source/blender/draw/engines/eevee/eevee_engine.c
index b698574f9d7..bac96ab1079 100644
--- a/source/blender/draw/engines/eevee/eevee_engine.c
+++ b/source/blender/draw/engines/eevee/eevee_engine.c
@@ -166,7 +166,9 @@ static void eevee_cache_finish(void *vedata)
EEVEE_materials_cache_finish(sldata, vedata);
EEVEE_lights_cache_finish(sldata, vedata);
EEVEE_lightprobes_cache_finish(sldata, vedata);
+ EEVEE_renderpasses_cache_finish(sldata, vedata);
+ EEVEE_subsurface_draw_init(sldata, vedata);
EEVEE_effects_draw_init(sldata, vedata);
EEVEE_volumes_draw_init(sldata, vedata);
@@ -213,6 +215,10 @@ static void eevee_draw_scene(void *vedata)
loop_len = MAX2(1, scene->eevee.taa_samples);
}
+ if (stl->effects->bypass_drawing) {
+ loop_len = 0;
+ }
+
while (loop_len--) {
float clear_col[4] = {0.0f, 0.0f, 0.0f, 0.0f};
float clear_depth = 1.0f;
@@ -351,11 +357,18 @@ static void eevee_draw_scene(void *vedata)
EEVEE_renderpasses_draw(sldata, vedata);
}
+ if (stl->effects->bypass_drawing) {
+ /* Restore the depth from sample 1. */
+ GPU_framebuffer_blit(fbl->double_buffer_depth_fb, 0, dfbl->default_fb, 0, GPU_DEPTH_BIT);
+ }
+
EEVEE_renderpasses_draw_debug(vedata);
EEVEE_volumes_free_smoke_textures();
stl->g_data->view_updated = false;
+
+ DRW_view_set_active(NULL);
}
static void eevee_view_update(void *vedata)
@@ -370,7 +383,7 @@ static void eevee_id_object_update(void *UNUSED(vedata), Object *object)
{
EEVEE_LightProbeEngineData *ped = EEVEE_lightprobe_data_get(object);
if (ped != NULL && ped->dd.recalc != 0) {
- ped->need_update = (ped->dd.recalc & (ID_RECALC_TRANSFORM)) != 0;
+ ped->need_update = (ped->dd.recalc & ID_RECALC_TRANSFORM) != 0;
ped->dd.recalc = 0;
}
EEVEE_LightEngineData *led = EEVEE_light_data_get(object);
@@ -381,6 +394,7 @@ static void eevee_id_object_update(void *UNUSED(vedata), Object *object)
EEVEE_ObjectEngineData *oedata = EEVEE_object_data_get(object);
if (oedata != NULL && oedata->dd.recalc != 0) {
oedata->need_update = true;
+ oedata->geom_update = (oedata->dd.recalc & (ID_RECALC_GEOMETRY)) != 0;
oedata->dd.recalc = 0;
}
}
@@ -390,6 +404,11 @@ static void eevee_id_world_update(void *vedata, World *wo)
EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
LightCache *lcache = stl->g_data->light_cache;
+ if (lcache == NULL || lcache == stl->lookdev_lightcache) {
+ /* Avoid Lookdev viewport clearing the update flag (see T67741). */
+ return;
+ }
+
EEVEE_WorldEngineData *wedata = EEVEE_world_data_ensure(wo);
if (wedata != NULL && wedata->dd.recalc != 0) {
@@ -400,7 +419,7 @@ static void eevee_id_world_update(void *vedata, World *wo)
}
}
-static void eevee_id_update(void *vedata, ID *id)
+void eevee_id_update(void *vedata, ID *id)
{
/* Handle updates based on ID type. */
switch (GS(id->name)) {
@@ -416,23 +435,129 @@ static void eevee_id_update(void *vedata, ID *id)
}
}
+static void eevee_render_reset_passes(EEVEE_Data *vedata)
+{
+ /* Reset passlist. This is safe as they are stored into managed memory chunks. */
+ memset(vedata->psl, 0, sizeof(*vedata->psl));
+}
+
static void eevee_render_to_image(void *vedata,
RenderEngine *engine,
struct RenderLayer *render_layer,
const rcti *rect)
{
+ EEVEE_Data *ved = (EEVEE_Data *)vedata;
const DRWContextState *draw_ctx = DRW_context_state_get();
+ Depsgraph *depsgraph = draw_ctx->depsgraph;
+ Scene *scene = DEG_get_evaluated_scene(depsgraph);
+ EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
+ const bool do_motion_blur = (scene->eevee.flag & SCE_EEVEE_MOTION_BLUR_ENABLED) != 0;
+ const bool do_motion_blur_fx = do_motion_blur && (scene->eevee.motion_blur_max > 0);
- if (!EEVEE_render_init(vedata, engine, draw_ctx->depsgraph)) {
+ if (!EEVEE_render_init(vedata, engine, depsgraph)) {
return;
}
+ EEVEE_PrivateData *g_data = ved->stl->g_data;
+
+ int steps = max_ii(1, scene->eevee.motion_blur_steps);
+ int time_steps_tot = (do_motion_blur) ? steps : 1;
+ g_data->render_tot_samples = divide_ceil_u(scene->eevee.taa_render_samples, time_steps_tot);
+ /* Centered on frame for now. */
+ float time = CFRA - scene->eevee.motion_blur_shutter / 2.0f;
+ float time_step = scene->eevee.motion_blur_shutter / time_steps_tot;
+ for (int i = 0; i < time_steps_tot && !RE_engine_test_break(engine); i++) {
+ float time_prev = time;
+ float time_curr = time + time_step * 0.5f;
+ float time_next = time + time_step;
+ time += time_step;
+
+ /* Previous motion step. */
+ if (do_motion_blur_fx) {
+ if (i > 0) {
+ /* The previous step of this iteration N is exactly the next step of iteration N - 1.
+ * So we just swap the resources to avoid too much re-evaluation. */
+ EEVEE_motion_blur_swap_data(vedata);
+ }
+ else {
+ EEVEE_motion_blur_step_set(ved, MB_PREV);
+ RE_engine_frame_set(engine, floorf(time_prev), fractf(time_prev));
+
+ EEVEE_render_view_sync(vedata, engine, depsgraph);
+ EEVEE_render_cache_init(sldata, vedata);
+
+ DRW_render_object_iter(vedata, engine, depsgraph, EEVEE_render_cache);
+
+ EEVEE_motion_blur_cache_finish(vedata);
+ EEVEE_materials_cache_finish(sldata, vedata);
+ eevee_render_reset_passes(vedata);
+ }
+ }
+
+ /* Next motion step. */
+ if (do_motion_blur_fx) {
+ EEVEE_motion_blur_step_set(ved, MB_NEXT);
+ RE_engine_frame_set(engine, floorf(time_next), fractf(time_next));
- DRW_render_object_iter(vedata, engine, draw_ctx->depsgraph, EEVEE_render_cache);
+ EEVEE_render_view_sync(vedata, engine, depsgraph);
+ EEVEE_render_cache_init(sldata, vedata);
- /* Actually do the rendering. */
- EEVEE_render_draw(vedata, engine, render_layer, rect);
+ DRW_render_object_iter(vedata, engine, depsgraph, EEVEE_render_cache);
+
+ EEVEE_motion_blur_cache_finish(vedata);
+ EEVEE_materials_cache_finish(sldata, vedata);
+ eevee_render_reset_passes(vedata);
+ }
+
+ /* Current motion step. */
+ {
+ if (do_motion_blur) {
+ EEVEE_motion_blur_step_set(ved, MB_CURR);
+ RE_engine_frame_set(engine, floorf(time_curr), fractf(time_curr));
+ }
+
+ EEVEE_render_view_sync(vedata, engine, depsgraph);
+ EEVEE_render_cache_init(sldata, vedata);
+
+ DRW_render_object_iter(vedata, engine, depsgraph, EEVEE_render_cache);
+
+ EEVEE_motion_blur_cache_finish(vedata);
+ EEVEE_volumes_cache_finish(sldata, vedata);
+ EEVEE_materials_cache_finish(sldata, vedata);
+ EEVEE_lights_cache_finish(sldata, vedata);
+ EEVEE_lightprobes_cache_finish(sldata, vedata);
+ EEVEE_renderpasses_cache_finish(sldata, vedata);
+
+ EEVEE_subsurface_draw_init(sldata, vedata);
+ EEVEE_effects_draw_init(sldata, vedata);
+ EEVEE_volumes_draw_init(sldata, vedata);
+ }
+
+ /* Actual drawing. */
+ {
+ EEVEE_renderpasses_output_init(sldata, vedata, g_data->render_tot_samples * time_steps_tot);
+
+ EEVEE_temporal_sampling_create_view(vedata);
+ EEVEE_render_draw(vedata, engine, render_layer, rect);
+
+ if (i < time_steps_tot - 1) {
+ /* Don't reset after the last loop. Since EEVEE_render_read_result
+ * might need some DRWPasses. */
+ DRW_cache_restart();
+ }
+ }
+ }
EEVEE_volumes_free_smoke_textures();
+ EEVEE_motion_blur_data_free(&ved->stl->effects->motion_blur);
+
+ if (RE_engine_test_break(engine)) {
+ return;
+ }
+
+ EEVEE_render_read_result(vedata, engine, render_layer, rect);
+
+ /* Restore original viewport size. */
+ DRW_render_viewport_size_set((int[2]){g_data->size_orig[0], g_data->size_orig[1]});
}
static void eevee_engine_free(void)
diff --git a/source/blender/draw/engines/eevee/eevee_lightcache.c b/source/blender/draw/engines/eevee/eevee_lightcache.c
index 4cdd166f09c..78d50a02fc7 100644
--- a/source/blender/draw/engines/eevee/eevee_lightcache.c
+++ b/source/blender/draw/engines/eevee/eevee_lightcache.c
@@ -849,6 +849,7 @@ static void eevee_lightbake_cache_create(EEVEE_Data *vedata, EEVEE_LightBake *lb
/* Disable volumetrics when baking. */
stl->effects->enabled_effects &= ~EFFECT_VOLUMETRIC;
+ EEVEE_subsurface_draw_init(sldata, vedata);
EEVEE_effects_draw_init(sldata, vedata);
EEVEE_volumes_draw_init(sldata, vedata);
diff --git a/source/blender/draw/engines/eevee/eevee_lightprobes.c b/source/blender/draw/engines/eevee/eevee_lightprobes.c
index 83b2a9bb6d4..71c8294d123 100644
--- a/source/blender/draw/engines/eevee/eevee_lightprobes.c
+++ b/source/blender/draw/engines/eevee/eevee_lightprobes.c
@@ -957,8 +957,8 @@ static void lightbake_render_scene_reflected(int layer, EEVEE_BakeRenderData *us
/* Slight modification: we handle refraction as normal
* shading and don't do SSRefraction. */
- DRW_draw_pass(psl->depth_ps);
- DRW_draw_pass(psl->depth_refract_ps);
+ DRW_draw_pass(psl->depth_clip_ps);
+ DRW_draw_pass(psl->depth_refract_clip_ps);
DRW_draw_pass(psl->probe_background);
EEVEE_create_minmax_buffer(vedata, tmp_planar_depth, layer);
diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c
index cfc70baaf01..8537ad0e532 100644
--- a/source/blender/draw/engines/eevee/eevee_materials.c
+++ b/source/blender/draw/engines/eevee/eevee_materials.c
@@ -801,6 +801,8 @@ static void eevee_hair_cache_populate(EEVEE_Data *vedata,
*matcache.shadow_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.shadow_grp);
*cast_shadow = true;
}
+
+ EEVEE_motion_blur_hair_cache_populate(sldata, vedata, ob, psys, md);
}
#define ADD_SHGROUP_CALL(shgrp, ob, geom, oedata) \
@@ -851,8 +853,7 @@ void EEVEE_materials_cache_populate(EEVEE_Data *vedata,
*/
bool use_volume_material = (matcache[0].shading_gpumat &&
GPU_material_has_volume_output(matcache[0].shading_gpumat));
-
- if ((ob->dt >= OB_SOLID) || DRW_state_is_image_render()) {
+ if ((ob->dt >= OB_SOLID) || DRW_state_is_scene_render()) {
if (use_sculpt_pbvh) {
struct DRWShadingGroup **shgrps_array = BLI_array_alloca(shgrps_array, materials_len);
@@ -901,6 +902,9 @@ void EEVEE_materials_cache_populate(EEVEE_Data *vedata,
}
}
}
+
+ /* Motion Blur Vectors. */
+ EEVEE_motion_blur_cache_populate(sldata, vedata, ob);
}
/* Volumetrics */
@@ -946,17 +950,15 @@ void EEVEE_object_hair_cache_populate(EEVEE_Data *vedata,
eevee_hair_cache_populate(vedata, sldata, ob, NULL, NULL, HAIR_MATERIAL_NR, cast_shadow);
}
-void EEVEE_materials_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
+void EEVEE_materials_cache_finish(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
{
EEVEE_PrivateData *pd = vedata->stl->g_data;
EEVEE_EffectsInfo *effects = vedata->stl->effects;
BLI_ghash_free(pd->material_hash, NULL, NULL);
+ pd->material_hash = NULL;
SET_FLAG_FROM_TEST(effects->enabled_effects, effects->sss_surface_count > 0, EFFECT_SSS);
-
- /* TODO(fclem) this is not really clean. Init should not be done in cache finish. */
- EEVEE_subsurface_draw_init(sldata, vedata);
}
void EEVEE_materials_free(void)
@@ -1015,7 +1017,7 @@ void EEVEE_material_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata,
/* Should be enough precision for many samples. */
const eGPUTextureFormat texture_format = (tot_samples > 128) ? GPU_RGBA32F : GPU_RGBA16F;
- const bool do_clear = DRW_state_is_image_render() || (effects->taa_current_sample == 1);
+ const bool do_clear = (effects->taa_current_sample == 1);
/* Create FrameBuffer. */
GPU_framebuffer_ensure_config(&fbl->material_accum_fb,
{GPU_ATTACHMENT_TEXTURE(dtxl->depth), GPU_ATTACHMENT_LEAVE});
diff --git a/source/blender/draw/engines/eevee/eevee_mist.c b/source/blender/draw/engines/eevee/eevee_mist.c
index 7b942784ee9..1cedd334d67 100644
--- a/source/blender/draw/engines/eevee/eevee_mist.c
+++ b/source/blender/draw/engines/eevee/eevee_mist.c
@@ -75,7 +75,7 @@ void EEVEE_mist_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->mist_accum)});
/* Clear texture. */
- if (DRW_state_is_image_render() || effects->taa_current_sample == 1) {
+ if (effects->taa_current_sample == 1) {
GPU_framebuffer_bind(fbl->mist_accum_fb);
GPU_framebuffer_clear_color(fbl->mist_accum_fb, clear);
}
diff --git a/source/blender/draw/engines/eevee/eevee_motion_blur.c b/source/blender/draw/engines/eevee/eevee_motion_blur.c
index a6e6b30a6b1..586ee780f1d 100644
--- a/source/blender/draw/engines/eevee/eevee_motion_blur.c
+++ b/source/blender/draw/engines/eevee/eevee_motion_blur.c
@@ -24,12 +24,19 @@
#include "DRW_render.h"
+#include "BLI_rand.h"
+#include "BLI_string_utils.h"
+
#include "BKE_animsys.h"
#include "BKE_camera.h"
#include "BKE_object.h"
+#include "BKE_screen.h"
#include "DNA_anim_types.h"
#include "DNA_camera_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_particle_types.h"
#include "DNA_screen_types.h"
#include "ED_screen.h"
@@ -37,172 +44,517 @@
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
+#include "GPU_batch.h"
#include "GPU_texture.h"
#include "eevee_private.h"
static struct {
/* Motion Blur */
struct GPUShader *motion_blur_sh;
+ struct GPUShader *motion_blur_object_sh;
+ struct GPUShader *motion_blur_hair_sh;
+ struct GPUShader *velocity_tiles_sh;
+ struct GPUShader *velocity_tiles_expand_sh;
} e_data = {NULL}; /* Engine data */
+extern char datatoc_effect_velocity_tile_frag_glsl[];
extern char datatoc_effect_motion_blur_frag_glsl[];
+extern char datatoc_object_motion_frag_glsl[];
+extern char datatoc_object_motion_vert_glsl[];
+extern char datatoc_common_hair_lib_glsl[];
+extern char datatoc_common_view_lib_glsl[];
-static void eevee_motion_blur_camera_get_matrix_at_time(Scene *scene,
- ARegion *region,
- RegionView3D *rv3d,
- View3D *v3d,
- Object *camera,
- float time,
- float r_mat[4][4])
-{
- float obmat[4][4];
+#define EEVEE_VELOCITY_TILE_SIZE 32
- /* HACK */
- Object cam_cpy = *camera;
- Camera camdata_cpy = *(Camera *)(camera->data);
- cam_cpy.data = &camdata_cpy;
+static void eevee_create_shader_motion_blur(void)
+{
+ e_data.motion_blur_sh = DRW_shader_create_fullscreen(
+ datatoc_effect_motion_blur_frag_glsl,
+ "#define EEVEE_VELOCITY_TILE_SIZE " STRINGIFY(EEVEE_VELOCITY_TILE_SIZE) "\n");
+ e_data.motion_blur_object_sh = DRW_shader_create_with_lib(datatoc_object_motion_vert_glsl,
+ NULL,
+ datatoc_object_motion_frag_glsl,
+ datatoc_common_view_lib_glsl,
+ NULL);
+ e_data.velocity_tiles_sh = DRW_shader_create_fullscreen(
+ datatoc_effect_velocity_tile_frag_glsl,
+ "#define TILE_GATHER\n"
+ "#define EEVEE_VELOCITY_TILE_SIZE " STRINGIFY(EEVEE_VELOCITY_TILE_SIZE) "\n");
+ e_data.velocity_tiles_expand_sh = DRW_shader_create_fullscreen(
+ datatoc_effect_velocity_tile_frag_glsl,
+ "#define TILE_EXPANSION\n"
+ "#define EEVEE_VELOCITY_TILE_SIZE " STRINGIFY(EEVEE_VELOCITY_TILE_SIZE) "\n");
+
+ char *vert = BLI_string_joinN(datatoc_common_hair_lib_glsl, datatoc_object_motion_vert_glsl);
+ e_data.motion_blur_hair_sh = DRW_shader_create_with_lib(
+ vert, NULL, datatoc_object_motion_frag_glsl, datatoc_common_view_lib_glsl, "#define HAIR\n");
+ MEM_freeN(vert);
+}
- /* Reset original pointers, so direct evaluation does not attempt to flush
- * animation back to the original object: otherwise viewport with motion
- * blur enabled will always loose non-keyed changes. */
- cam_cpy.id.orig_id = NULL;
- camdata_cpy.id.orig_id = NULL;
+int EEVEE_motion_blur_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
+{
+ EEVEE_StorageList *stl = vedata->stl;
+ EEVEE_FramebufferList *fbl = vedata->fbl;
+ EEVEE_EffectsInfo *effects = stl->effects;
const DRWContextState *draw_ctx = DRW_context_state_get();
+ Scene *scene = draw_ctx->scene;
- /* Past matrix */
- /* FIXME : This is a temporal solution that does not take care of parent animations */
- /* Recalc Anim manually */
- BKE_animsys_evaluate_animdata(&camdata_cpy.id, camdata_cpy.adt, time, ADT_RECALC_ALL, false);
- BKE_object_where_is_calc_time(draw_ctx->depsgraph, scene, &cam_cpy, time);
+ /* Viewport not supported for now. */
+ if (!DRW_state_is_scene_render()) {
+ return 0;
+ }
+
+ effects->motion_blur_max = max_ii(0, scene->eevee.motion_blur_max);
+
+ if ((effects->motion_blur_max > 0) && (scene->eevee.flag & SCE_EEVEE_MOTION_BLUR_ENABLED)) {
+ if (!e_data.motion_blur_sh) {
+ eevee_create_shader_motion_blur();
+ }
- /* Compute winmat */
- CameraParams params;
- BKE_camera_params_init(&params);
+ if (DRW_state_is_scene_render()) {
+ int mb_step = effects->motion_blur_step;
+ DRW_view_viewmat_get(NULL, effects->motion_blur.camera[mb_step].viewmat, false);
+ DRW_view_persmat_get(NULL, effects->motion_blur.camera[mb_step].persmat, false);
+ DRW_view_persmat_get(NULL, effects->motion_blur.camera[mb_step].persinv, true);
+ }
- if (v3d != NULL) {
- BKE_camera_params_from_view3d(&params, draw_ctx->depsgraph, v3d, rv3d);
- BKE_camera_params_compute_viewplane(&params, region->winx, region->winy, 1.0f, 1.0f);
+ const float *fs_size = DRW_viewport_size_get();
+ int tx_size[2] = {1 + ((int)fs_size[0] / EEVEE_VELOCITY_TILE_SIZE),
+ 1 + ((int)fs_size[1] / EEVEE_VELOCITY_TILE_SIZE)};
+
+ effects->velocity_tiles_x_tx = DRW_texture_pool_query_2d(
+ tx_size[0], fs_size[1], GPU_RGBA16, &draw_engine_eevee_type);
+ GPU_framebuffer_ensure_config(&fbl->velocity_tiles_fb[0],
+ {
+ GPU_ATTACHMENT_NONE,
+ GPU_ATTACHMENT_TEXTURE(effects->velocity_tiles_x_tx),
+ });
+
+ effects->velocity_tiles_tx = DRW_texture_pool_query_2d(
+ tx_size[0], tx_size[1], GPU_RGBA16, &draw_engine_eevee_type);
+ GPU_framebuffer_ensure_config(&fbl->velocity_tiles_fb[1],
+ {
+ GPU_ATTACHMENT_NONE,
+ GPU_ATTACHMENT_TEXTURE(effects->velocity_tiles_tx),
+ });
+
+ return EFFECT_MOTION_BLUR | EFFECT_POST_BUFFER | EFFECT_VELOCITY_BUFFER;
}
- else {
- BKE_camera_params_from_object(&params, &cam_cpy);
- BKE_camera_params_compute_viewplane(
- &params, scene->r.xsch, scene->r.ysch, scene->r.xasp, scene->r.yasp);
+ return 0;
+}
+
+void EEVEE_motion_blur_step_set(EEVEE_Data *vedata, int step)
+{
+ BLI_assert(step < 3);
+ vedata->stl->effects->motion_blur_step = step;
+}
+
+static void eevee_motion_blur_sync_camera(EEVEE_Data *vedata)
+{
+ EEVEE_EffectsInfo *effects = vedata->stl->effects;
+ if (DRW_state_is_scene_render()) {
+ int mb_step = effects->motion_blur_step;
+ DRW_view_viewmat_get(NULL, effects->motion_blur.camera[mb_step].viewmat, false);
+ DRW_view_persmat_get(NULL, effects->motion_blur.camera[mb_step].persmat, false);
+ DRW_view_persmat_get(NULL, effects->motion_blur.camera[mb_step].persinv, true);
}
- BKE_camera_params_compute_matrix(&params);
+ effects->motion_blur_near_far[0] = fabsf(DRW_view_near_distance_get(NULL));
+ effects->motion_blur_near_far[1] = fabsf(DRW_view_far_distance_get(NULL));
+}
+
+void EEVEE_motion_blur_cache_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
+{
+ EEVEE_PassList *psl = vedata->psl;
+ EEVEE_StorageList *stl = vedata->stl;
+ EEVEE_EffectsInfo *effects = stl->effects;
+ EEVEE_MotionBlurData *mb_data = &effects->motion_blur;
+ DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ Scene *scene = draw_ctx->scene;
+
+ if ((effects->enabled_effects & EFFECT_MOTION_BLUR) != 0) {
+ const float *fs_size = DRW_viewport_size_get();
+ int tx_size[2] = {GPU_texture_width(effects->velocity_tiles_tx),
+ GPU_texture_height(effects->velocity_tiles_tx)};
+
+ eevee_motion_blur_sync_camera(vedata);
+
+ DRWShadingGroup *grp;
+ {
+ DRW_PASS_CREATE(psl->velocity_tiles_x, DRW_STATE_WRITE_COLOR);
+ DRW_PASS_CREATE(psl->velocity_tiles, DRW_STATE_WRITE_COLOR);
+
+ /* Create max velocity tiles in 2 passes. One for X and one for Y */
+ GPUShader *sh = e_data.velocity_tiles_sh;
+ grp = DRW_shgroup_create(sh, psl->velocity_tiles_x);
+ DRW_shgroup_uniform_texture(grp, "velocityBuffer", effects->velocity_tx);
+ DRW_shgroup_uniform_ivec2_copy(grp, "velocityBufferSize", (int[2]){fs_size[0], fs_size[1]});
+ DRW_shgroup_uniform_vec2(grp, "viewportSize", DRW_viewport_size_get(), 1);
+ DRW_shgroup_uniform_vec2(grp, "viewportSizeInv", DRW_viewport_invert_size_get(), 1);
+ DRW_shgroup_uniform_ivec2_copy(grp, "gatherStep", (int[2]){1, 0});
+ DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
+
+ grp = DRW_shgroup_create(sh, psl->velocity_tiles);
+ DRW_shgroup_uniform_texture(grp, "velocityBuffer", effects->velocity_tiles_x_tx);
+ DRW_shgroup_uniform_ivec2_copy(grp, "velocityBufferSize", (int[2]){tx_size[0], fs_size[1]});
+ DRW_shgroup_uniform_ivec2_copy(grp, "gatherStep", (int[2]){0, 1});
+ DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
+
+ /* Expand max tiles by keeping the max tile in each tile neighborhood. */
+ DRW_PASS_CREATE(psl->velocity_tiles_expand[0], DRW_STATE_WRITE_COLOR);
+ DRW_PASS_CREATE(psl->velocity_tiles_expand[1], DRW_STATE_WRITE_COLOR);
+ for (int i = 0; i < 2; i++) {
+ GPUTexture *tile_tx = (i == 0) ? effects->velocity_tiles_tx : effects->velocity_tiles_x_tx;
+ GPUShader *sh_expand = e_data.velocity_tiles_expand_sh;
+ grp = DRW_shgroup_create(sh_expand, psl->velocity_tiles_expand[i]);
+ DRW_shgroup_uniform_ivec2_copy(grp, "velocityBufferSize", tx_size);
+ DRW_shgroup_uniform_texture(grp, "velocityBuffer", tile_tx);
+ DRW_shgroup_uniform_vec2(grp, "viewportSize", DRW_viewport_size_get(), 1);
+ DRW_shgroup_uniform_vec2(grp, "viewportSizeInv", DRW_viewport_invert_size_get(), 1);
+ DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
+ }
+ }
+ {
+ DRW_PASS_CREATE(psl->motion_blur, DRW_STATE_WRITE_COLOR);
+ eGPUSamplerState state = 0;
+ int expand_steps = 1 + (max_ii(0, effects->motion_blur_max - 1) / EEVEE_VELOCITY_TILE_SIZE);
+ GPUTexture *tile_tx = (expand_steps & 1) ? effects->velocity_tiles_x_tx :
+ effects->velocity_tiles_tx;
+
+ grp = DRW_shgroup_create(e_data.motion_blur_sh, psl->motion_blur);
+ DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex());
+ DRW_shgroup_uniform_texture_ref_ex(grp, "colorBuffer", &effects->source_buffer, state);
+ DRW_shgroup_uniform_texture_ref_ex(grp, "depthBuffer", &dtxl->depth, state);
+ DRW_shgroup_uniform_texture_ref_ex(grp, "velocityBuffer", &effects->velocity_tx, state);
+ DRW_shgroup_uniform_texture(grp, "tileMaxBuffer", tile_tx);
+ DRW_shgroup_uniform_float_copy(grp, "depthScale", scene->eevee.motion_blur_depth_scale);
+ DRW_shgroup_uniform_vec2(grp, "nearFar", effects->motion_blur_near_far, 1);
+ DRW_shgroup_uniform_bool_copy(grp, "isPerspective", DRW_view_is_persp_get(NULL));
+ DRW_shgroup_uniform_vec2(grp, "viewportSize", DRW_viewport_size_get(), 1);
+ DRW_shgroup_uniform_vec2(grp, "viewportSizeInv", DRW_viewport_invert_size_get(), 1);
+ DRW_shgroup_uniform_ivec2_copy(grp, "tileBufferSize", tx_size);
+ DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
+ }
+ {
+ DRW_PASS_CREATE(psl->velocity_object, DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL);
+
+ grp = DRW_shgroup_create(e_data.motion_blur_object_sh, psl->velocity_object);
+ DRW_shgroup_uniform_mat4(grp, "prevViewProjMatrix", mb_data->camera[MB_PREV].persmat);
+ DRW_shgroup_uniform_mat4(grp, "currViewProjMatrix", mb_data->camera[MB_CURR].persmat);
+ DRW_shgroup_uniform_mat4(grp, "nextViewProjMatrix", mb_data->camera[MB_NEXT].persmat);
+
+ DRW_PASS_CREATE(psl->velocity_hair, DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL);
+
+ mb_data->hair_grp = grp = DRW_shgroup_create(e_data.motion_blur_hair_sh, psl->velocity_hair);
+ DRW_shgroup_uniform_mat4(grp, "prevViewProjMatrix", mb_data->camera[MB_PREV].persmat);
+ DRW_shgroup_uniform_mat4(grp, "currViewProjMatrix", mb_data->camera[MB_CURR].persmat);
+ DRW_shgroup_uniform_mat4(grp, "nextViewProjMatrix", mb_data->camera[MB_NEXT].persmat);
+
+ DRW_pass_link(psl->velocity_object, psl->velocity_hair);
+ }
- /* FIXME Should be done per view (MULTIVIEW) */
- normalize_m4_m4(obmat, cam_cpy.obmat);
- invert_m4(obmat);
- mul_m4_m4m4(r_mat, params.winmat, obmat);
+ EEVEE_motion_blur_data_init(mb_data);
+ }
+ else {
+ psl->motion_blur = NULL;
+ psl->velocity_object = NULL;
+ psl->velocity_hair = NULL;
+ }
}
-static void eevee_create_shader_motion_blur(void)
+void EEVEE_motion_blur_hair_cache_populate(EEVEE_ViewLayerData *UNUSED(sldata),
+ EEVEE_Data *vedata,
+ Object *ob,
+ ParticleSystem *psys,
+ ModifierData *md)
{
- e_data.motion_blur_sh = DRW_shader_create_fullscreen(datatoc_effect_motion_blur_frag_glsl, NULL);
+ EEVEE_PassList *psl = vedata->psl;
+ EEVEE_StorageList *stl = vedata->stl;
+ EEVEE_EffectsInfo *effects = stl->effects;
+ DRWShadingGroup *grp = NULL;
+
+ if (!DRW_state_is_scene_render() || psl->velocity_hair == NULL) {
+ return;
+ }
+
+ /* For now we assume hair objects are always moving. */
+ EEVEE_ObjectMotionData *mb_data = EEVEE_motion_blur_object_data_get(
+ &effects->motion_blur, ob, true);
+
+ if (mb_data) {
+ int mb_step = effects->motion_blur_step;
+ /* Store transform */
+ DRW_hair_duplimat_get(ob, psys, md, mb_data->obmat[mb_step]);
+
+ EEVEE_GeometryMotionData *mb_geom = EEVEE_motion_blur_geometry_data_get(
+ &effects->motion_blur, ob, true);
+
+ if (mb_step == MB_CURR) {
+ /* Fill missing matrices if the object was hidden in previous or next frame. */
+ if (is_zero_m4(mb_data->obmat[MB_PREV])) {
+ copy_m4_m4(mb_data->obmat[MB_PREV], mb_data->obmat[MB_CURR]);
+ }
+ if (is_zero_m4(mb_data->obmat[MB_NEXT])) {
+ copy_m4_m4(mb_data->obmat[MB_NEXT], mb_data->obmat[MB_CURR]);
+ }
+
+ grp = DRW_shgroup_hair_create_sub(ob, psys, md, effects->motion_blur.hair_grp);
+ DRW_shgroup_uniform_mat4(grp, "prevModelMatrix", mb_data->obmat[MB_PREV]);
+ DRW_shgroup_uniform_mat4(grp, "currModelMatrix", mb_data->obmat[MB_CURR]);
+ DRW_shgroup_uniform_mat4(grp, "nextModelMatrix", mb_data->obmat[MB_NEXT]);
+ DRW_shgroup_uniform_texture(grp, "prvBuffer", mb_geom->hair_pos_tx[MB_PREV]);
+ DRW_shgroup_uniform_texture(grp, "nxtBuffer", mb_geom->hair_pos_tx[MB_NEXT]);
+ DRW_shgroup_uniform_bool(grp, "useDeform", &mb_geom->use_deform, 1);
+ }
+ else {
+ /* Store vertex position buffer. */
+ mb_geom->hair_pos[mb_step] = DRW_hair_pos_buffer_get(ob, psys, md);
+ mb_geom->use_deform = true;
+ }
+ }
}
-int EEVEE_motion_blur_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata, Object *camera)
+void EEVEE_motion_blur_cache_populate(EEVEE_ViewLayerData *UNUSED(sldata),
+ EEVEE_Data *vedata,
+ Object *ob)
{
+ EEVEE_PassList *psl = vedata->psl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
+ DRWShadingGroup *grp = NULL;
- const DRWContextState *draw_ctx = DRW_context_state_get();
- const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
- Scene *scene = draw_ctx->scene;
+ if (!DRW_state_is_scene_render() || psl->velocity_object == NULL) {
+ return;
+ }
- View3D *v3d = draw_ctx->v3d;
- RegionView3D *rv3d = draw_ctx->rv3d;
- ARegion *region = draw_ctx->region;
+ const bool is_dupli = (ob->base_flag & BASE_FROM_DUPLI) != 0;
+ /* For now we assume dupli objects are moving. */
+ const bool object_moves = is_dupli || BKE_object_moves_in_time(ob, true);
+ const bool is_deform = BKE_object_is_deform_modified(DRW_context_state_get()->scene, ob);
- if (scene_eval->eevee.flag & SCE_EEVEE_MOTION_BLUR_ENABLED) {
- /* Update Motion Blur Matrices */
- if (camera && (camera->type == OB_CAMERA) && (camera->data != NULL)) {
- float persmat[4][4];
- float ctime = DEG_get_ctime(draw_ctx->depsgraph);
- float delta = scene_eval->eevee.motion_blur_shutter;
- Object *ob_camera_eval = DEG_get_evaluated_object(draw_ctx->depsgraph, camera);
+ if (!(object_moves || is_deform)) {
+ return;
+ }
- /* Viewport Matrix */
- /* Note: This does not have TAA jitter applied. */
- DRW_view_persmat_get(NULL, persmat, false);
+ EEVEE_ObjectMotionData *mb_data = EEVEE_motion_blur_object_data_get(
+ &effects->motion_blur, ob, false);
- bool view_is_valid = (stl->g_data->view_updated == false);
+ if (mb_data) {
+ int mb_step = effects->motion_blur_step;
+ /* Store transform */
+ copy_m4_m4(mb_data->obmat[mb_step], ob->obmat);
+
+ EEVEE_GeometryMotionData *mb_geom = EEVEE_motion_blur_geometry_data_get(
+ &effects->motion_blur, ob, false);
+
+ if (mb_step == MB_CURR) {
+ GPUBatch *batch = DRW_cache_object_surface_get(ob);
+ if (batch == NULL) {
+ return;
+ }
- if (draw_ctx->evil_C != NULL) {
- struct wmWindowManager *wm = CTX_wm_manager(draw_ctx->evil_C);
- view_is_valid = view_is_valid && (ED_screen_animation_no_scrub(wm) == NULL);
+ /* Fill missing matrices if the object was hidden in previous or next frame. */
+ if (is_zero_m4(mb_data->obmat[MB_PREV])) {
+ copy_m4_m4(mb_data->obmat[MB_PREV], mb_data->obmat[MB_CURR]);
+ }
+ if (is_zero_m4(mb_data->obmat[MB_NEXT])) {
+ copy_m4_m4(mb_data->obmat[MB_NEXT], mb_data->obmat[MB_CURR]);
}
- /* The view is jittered by the oglrenderer. So avoid testing in this case. */
- if (!DRW_state_is_image_render()) {
- view_is_valid = view_is_valid && compare_m4m4(persmat, effects->prev_drw_persmat, FLT_MIN);
- /* WATCH: assume TAA init code runs last. */
- if (scene_eval->eevee.taa_samples == 1) {
- /* Only if TAA is disabled. If not, TAA will update prev_drw_persmat itself. */
- copy_m4_m4(effects->prev_drw_persmat, persmat);
+ grp = DRW_shgroup_create(e_data.motion_blur_object_sh, psl->velocity_object);
+ DRW_shgroup_uniform_mat4(grp, "prevModelMatrix", mb_data->obmat[MB_PREV]);
+ DRW_shgroup_uniform_mat4(grp, "currModelMatrix", mb_data->obmat[MB_CURR]);
+ DRW_shgroup_uniform_mat4(grp, "nextModelMatrix", mb_data->obmat[MB_NEXT]);
+ DRW_shgroup_uniform_bool(grp, "useDeform", &mb_geom->use_deform, 1);
+
+ DRW_shgroup_call(grp, batch, ob);
+
+ if (mb_geom->use_deform) {
+ EEVEE_ObjectEngineData *oedata = EEVEE_object_data_ensure(ob);
+ if (!oedata->geom_update) {
+ /* FIXME(fclem) There can be false positive where the actual mesh is not updated.
+ * This avoids a crash but removes the motion blur from some object.
+ * Maybe an issue with depsgraph tagging. */
+ mb_geom->use_deform = false;
+ oedata->geom_update = false;
+
+ GPU_VERTBUF_DISCARD_SAFE(mb_geom->vbo[MB_PREV]);
+ GPU_VERTBUF_DISCARD_SAFE(mb_geom->vbo[MB_NEXT]);
}
+ /* Keep to modify later (after init). */
+ mb_geom->batch = batch;
}
+ }
+ else if (is_deform) {
+ /* Store vertex position buffer. */
+ mb_geom->vbo[mb_step] = DRW_cache_object_pos_vertbuf_get(ob);
+ mb_geom->use_deform = (mb_geom->vbo[mb_step] != NULL);
+ }
+ else {
+ mb_geom->vbo[mb_step] = NULL;
+ mb_geom->use_deform = false;
+ }
+ }
+}
+
+void EEVEE_motion_blur_cache_finish(EEVEE_Data *vedata)
+{
+ EEVEE_StorageList *stl = vedata->stl;
+ EEVEE_EffectsInfo *effects = stl->effects;
+ GHashIterator ghi;
- effects->motion_blur_mat_cached = view_is_valid && !DRW_state_is_image_render();
+ if ((effects->enabled_effects & EFFECT_MOTION_BLUR) == 0) {
+ return;
+ }
- /* Current matrix */
- if (effects->motion_blur_mat_cached == false) {
- eevee_motion_blur_camera_get_matrix_at_time(
- scene, region, rv3d, v3d, ob_camera_eval, ctime, effects->current_world_to_ndc);
- }
+ int mb_step = effects->motion_blur_step;
+
+ if (mb_step != MB_CURR) {
+ /* Push instances attributes to the GPU. */
+ DRW_render_instance_buffer_finish();
+
+ /* Need to be called after DRW_render_instance_buffer_finish() */
+ /* Also we weed to have a correct fbo bound for DRW_hair_update */
+ GPU_framebuffer_bind(vedata->fbl->main_fb);
+ DRW_hair_update();
- /* Only continue if camera is not being keyed */
- if (DRW_state_is_image_render() ||
- compare_m4m4(persmat, effects->current_world_to_ndc, 0.0001f)) {
- /* Past matrix */
- if (effects->motion_blur_mat_cached == false) {
- eevee_motion_blur_camera_get_matrix_at_time(
- scene, region, rv3d, v3d, ob_camera_eval, ctime - delta, effects->past_world_to_ndc);
-
-#if 0 /* for future high quality blur */
- /* Future matrix */
- eevee_motion_blur_camera_get_matrix_at_time(
- scene, region, rv3d, v3d, ob_camera_eval, ctime + delta, effects->future_world_to_ndc);
-#endif
- invert_m4_m4(effects->current_ndc_to_world, effects->current_world_to_ndc);
+ DRW_cache_restart();
+ }
+
+ for (BLI_ghashIterator_init(&ghi, effects->motion_blur.geom);
+ BLI_ghashIterator_done(&ghi) == false;
+ BLI_ghashIterator_step(&ghi)) {
+ EEVEE_GeometryMotionData *mb_geom = BLI_ghashIterator_getValue(&ghi);
+
+ if (!mb_geom->use_deform) {
+ continue;
+ }
+
+ switch (mb_geom->type) {
+ case EEVEE_HAIR_GEOM_MOTION_DATA:
+ if (mb_step == MB_CURR) {
+ /* TODO(fclem) Check if vertex count mismatch. */
+ mb_geom->use_deform = true;
}
+ else {
+ mb_geom->hair_pos[mb_step] = GPU_vertbuf_duplicate(mb_geom->hair_pos[mb_step]);
- effects->motion_blur_mat_cached = true;
- effects->motion_blur_samples = scene_eval->eevee.motion_blur_samples;
+ /* Create vbo immediately to bind to texture buffer. */
+ GPU_vertbuf_use(mb_geom->hair_pos[mb_step]);
- if (!e_data.motion_blur_sh) {
- eevee_create_shader_motion_blur();
+ mb_geom->hair_pos_tx[mb_step] = GPU_texture_create_from_vertbuf(
+ mb_geom->hair_pos[mb_step]);
+ }
+ break;
+
+ case EEVEE_MESH_GEOM_MOTION_DATA:
+ if (mb_step == MB_CURR) {
+ /* Modify batch to have data from adjacent frames. */
+ GPUBatch *batch = mb_geom->batch;
+ for (int i = 0; i < MB_CURR; i++) {
+ GPUVertBuf *vbo = mb_geom->vbo[i];
+ if (vbo && batch) {
+ if (vbo->vertex_len != batch->verts[0]->vertex_len) {
+ /* Vertex count mismatch, disable deform motion blur. */
+ mb_geom->use_deform = false;
+ }
+
+ if (mb_geom->use_deform == false) {
+ GPU_VERTBUF_DISCARD_SAFE(mb_geom->vbo[MB_PREV]);
+ GPU_VERTBUF_DISCARD_SAFE(mb_geom->vbo[MB_NEXT]);
+ break;
+ }
+ else {
+ /* Modify the batch to include the previous & next position. */
+ if (i == MB_PREV) {
+ GPU_batch_vertbuf_add_ex(batch, vbo, true);
+ mb_geom->vbo[i] = NULL;
+ }
+ else {
+ /* This VBO can be reuse by next time step. Don't pass ownership. */
+ GPU_batch_vertbuf_add_ex(batch, vbo, false);
+ }
+ }
+ }
+ }
+ }
+ else {
+ GPUVertBuf *vbo = mb_geom->vbo[mb_step];
+ /* If this assert fails, it means that different EEVEE_GeometryMotionDatas
+ * has been used for each motion blur step. */
+ BLI_assert(vbo);
+ if (vbo) {
+ /* Use the vbo to perform the copy on the GPU. */
+ GPU_vertbuf_use(vbo);
+ /* Perform a copy to avoid loosing it after RE_engine_frame_set(). */
+ mb_geom->vbo[mb_step] = vbo = GPU_vertbuf_duplicate(vbo);
+ /* Find and replace "pos" attrib name. */
+ int attrib_id = GPU_vertformat_attr_id_get(&vbo->format, "pos");
+ GPU_vertformat_attr_rename(
+ &vbo->format, attrib_id, (mb_step == MB_PREV) ? "prv" : "nxt");
+ }
}
+ break;
- return EFFECT_MOTION_BLUR | EFFECT_POST_BUFFER;
- }
+ default:
+ BLI_assert(0);
+ break;
}
}
-
- return 0;
}
-void EEVEE_motion_blur_cache_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
+void EEVEE_motion_blur_swap_data(EEVEE_Data *vedata)
{
- EEVEE_PassList *psl = vedata->psl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
- DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
- struct GPUBatch *quad = DRW_cache_fullscreen_quad_get();
+ GHashIterator ghi;
- if ((effects->enabled_effects & EFFECT_MOTION_BLUR) != 0) {
- DRW_PASS_CREATE(psl->motion_blur, DRW_STATE_WRITE_COLOR);
+ BLI_assert((effects->enabled_effects & EFFECT_MOTION_BLUR) != 0);
- DRWShadingGroup *grp = DRW_shgroup_create(e_data.motion_blur_sh, psl->motion_blur);
- DRW_shgroup_uniform_int(grp, "samples", &effects->motion_blur_samples, 1);
- DRW_shgroup_uniform_mat4(grp, "currInvViewProjMatrix", effects->current_ndc_to_world);
- DRW_shgroup_uniform_mat4(grp, "pastViewProjMatrix", effects->past_world_to_ndc);
- DRW_shgroup_uniform_texture_ref(grp, "colorBuffer", &effects->source_buffer);
- DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth);
- DRW_shgroup_call(grp, quad, NULL);
+ /* Camera Data. */
+ effects->motion_blur.camera[MB_PREV] = effects->motion_blur.camera[MB_CURR];
+
+ /* Object Data. */
+ for (BLI_ghashIterator_init(&ghi, effects->motion_blur.object);
+ BLI_ghashIterator_done(&ghi) == false;
+ BLI_ghashIterator_step(&ghi)) {
+ EEVEE_ObjectMotionData *mb_data = BLI_ghashIterator_getValue(&ghi);
+
+ copy_m4_m4(mb_data->obmat[MB_PREV], mb_data->obmat[MB_NEXT]);
+ }
+
+ /* Deformation Data. */
+ for (BLI_ghashIterator_init(&ghi, effects->motion_blur.geom);
+ BLI_ghashIterator_done(&ghi) == false;
+ BLI_ghashIterator_step(&ghi)) {
+ EEVEE_GeometryMotionData *mb_geom = BLI_ghashIterator_getValue(&ghi);
+
+ switch (mb_geom->type) {
+ case EEVEE_HAIR_GEOM_MOTION_DATA:
+ GPU_VERTBUF_DISCARD_SAFE(mb_geom->hair_pos[MB_PREV]);
+ DRW_TEXTURE_FREE_SAFE(mb_geom->hair_pos_tx[MB_PREV]);
+ mb_geom->hair_pos[MB_PREV] = mb_geom->hair_pos[MB_NEXT];
+ mb_geom->hair_pos_tx[MB_PREV] = mb_geom->hair_pos_tx[MB_NEXT];
+ break;
+
+ case EEVEE_MESH_GEOM_MOTION_DATA:
+ GPU_VERTBUF_DISCARD_SAFE(mb_geom->vbo[MB_PREV]);
+ mb_geom->vbo[MB_PREV] = mb_geom->vbo[MB_NEXT];
+
+ if (mb_geom->vbo[MB_NEXT]) {
+ GPUVertBuf *vbo = mb_geom->vbo[MB_NEXT];
+ int attrib_id = GPU_vertformat_attr_id_get(&vbo->format, "nxt");
+ GPU_vertformat_attr_rename(&vbo->format, attrib_id, "prv");
+ }
+ break;
+
+ default:
+ BLI_assert(0);
+ break;
+ }
}
}
@@ -216,6 +568,30 @@ void EEVEE_motion_blur_draw(EEVEE_Data *vedata)
/* Motion Blur */
if ((effects->enabled_effects & EFFECT_MOTION_BLUR) != 0) {
+ /* Create velocity max tiles in 2 passes. One for each dimension. */
+ GPU_framebuffer_bind(fbl->velocity_tiles_fb[0]);
+ DRW_draw_pass(psl->velocity_tiles_x);
+
+ GPU_framebuffer_bind(fbl->velocity_tiles_fb[1]);
+ DRW_draw_pass(psl->velocity_tiles);
+
+ /* Expand the tiles by reading the neighborhood. Do as many passes as required. */
+ int buf = 0;
+ for (int i = effects->motion_blur_max; i > 0; i -= EEVEE_VELOCITY_TILE_SIZE) {
+ GPU_framebuffer_bind(fbl->velocity_tiles_fb[buf]);
+
+ /* Change viewport to avoid invoking more pixel shaders than necessary since in one of the
+ * buffer the texture is way bigger in height. This avoid creating another texture and
+ * reduce VRAM usage. */
+ int w = GPU_texture_width(effects->velocity_tiles_tx);
+ int h = GPU_texture_height(effects->velocity_tiles_tx);
+ GPU_framebuffer_viewport_set(fbl->velocity_tiles_fb[buf], 0, 0, w, h);
+
+ DRW_draw_pass(psl->velocity_tiles_expand[buf]);
+
+ buf = buf ? 0 : 1;
+ }
+
GPU_framebuffer_bind(effects->target_buffer);
DRW_draw_pass(psl->motion_blur);
SWAP_BUFFERS();
@@ -225,4 +601,8 @@ void EEVEE_motion_blur_draw(EEVEE_Data *vedata)
void EEVEE_motion_blur_free(void)
{
DRW_SHADER_FREE_SAFE(e_data.motion_blur_sh);
+ DRW_SHADER_FREE_SAFE(e_data.motion_blur_object_sh);
+ DRW_SHADER_FREE_SAFE(e_data.motion_blur_hair_sh);
+ DRW_SHADER_FREE_SAFE(e_data.velocity_tiles_sh);
+ DRW_SHADER_FREE_SAFE(e_data.velocity_tiles_expand_sh);
}
diff --git a/source/blender/draw/engines/eevee/eevee_occlusion.c b/source/blender/draw/engines/eevee/eevee_occlusion.c
index f5ebbe08dd1..a075210967c 100644
--- a/source/blender/draw/engines/eevee/eevee_occlusion.c
+++ b/source/blender/draw/engines/eevee/eevee_occlusion.c
@@ -155,7 +155,7 @@ void EEVEE_occlusion_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->ao_accum)});
/* Clear texture. */
- if (DRW_state_is_image_render() || effects->taa_current_sample == 1) {
+ if (effects->taa_current_sample == 1) {
GPU_framebuffer_bind(fbl->ao_accum_fb);
GPU_framebuffer_clear_color(fbl->ao_accum_fb, clear);
}
diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h
index 40008c5c364..5005c5a8ba9 100644
--- a/source/blender/draw/engines/eevee/eevee_private.h
+++ b/source/blender/draw/engines/eevee/eevee_private.h
@@ -29,6 +29,8 @@
#include "DNA_lightprobe_types.h"
+#include "BKE_camera.h"
+
struct EEVEE_ShadowCasterBuffer;
struct GPUFrameBuffer;
struct Object;
@@ -256,7 +258,12 @@ typedef struct EEVEE_PassList {
struct DRWPass *sss_translucency_ps;
struct DRWPass *color_downsample_ps;
struct DRWPass *color_downsample_cube_ps;
+ struct DRWPass *velocity_object;
+ struct DRWPass *velocity_hair;
struct DRWPass *velocity_resolve;
+ struct DRWPass *velocity_tiles_x;
+ struct DRWPass *velocity_tiles;
+ struct DRWPass *velocity_tiles_expand[2];
struct DRWPass *taa_resolve;
struct DRWPass *alpha_checker;
@@ -327,6 +334,8 @@ typedef struct EEVEE_FramebufferList {
struct GPUFrameBuffer *renderpass_fb;
struct GPUFrameBuffer *ao_accum_fb;
struct GPUFrameBuffer *velocity_resolve_fb;
+ struct GPUFrameBuffer *velocity_fb;
+ struct GPUFrameBuffer *velocity_tiles_fb[2];
struct GPUFrameBuffer *update_noise_fb;
@@ -556,6 +565,58 @@ enum {
PROBE_UPDATE_ALL = 0xFFFFFF,
};
+/* ************** MOTION BLUR ************ */
+
+#define MB_PREV 0
+#define MB_NEXT 1
+#define MB_CURR 2
+
+typedef struct EEVEE_MotionBlurData {
+ struct GHash *object;
+ struct GHash *geom;
+ struct {
+ float viewmat[4][4];
+ float persmat[4][4];
+ float persinv[4][4];
+ } camera[3];
+ DRWShadingGroup *hair_grp;
+} EEVEE_MotionBlurData;
+
+typedef struct EEVEE_ObjectKey {
+ /** Object or source object for duplis */
+ struct Object *ob;
+ /** Parent object for duplis */
+ struct Object *parent;
+ /** Dupli objects recursive unique identifier */
+ int id[16]; /* 2*MAX_DUPLI_RECUR */
+} EEVEE_ObjectKey;
+
+typedef struct EEVEE_ObjectMotionData {
+ float obmat[3][4][4];
+} EEVEE_ObjectMotionData;
+
+typedef enum eEEVEEMotionData {
+ EEVEE_MESH_GEOM_MOTION_DATA = 0,
+ EEVEE_HAIR_GEOM_MOTION_DATA,
+} eEEVEEMotionData;
+
+typedef struct EEVEE_GeometryMotionData {
+ eEEVEEMotionData type;
+ int use_deform; /* To disable deform mb if vertcount mismatch. */
+ union {
+ struct {
+ /* Mesh */
+ struct GPUBatch *batch; /* Batch for time = t. */
+ struct GPUVertBuf *vbo[2]; /* Vbo for time = t +/- step. */
+ };
+ struct {
+ /* Hair */
+ struct GPUVertBuf *hair_pos[2]; /* Position buffer for time = t +/- step. */
+ struct GPUTexture *hair_pos_tx[2]; /* Buffer Texture of the corresponding VBO. */
+ };
+ };
+} EEVEE_GeometryMotionData;
+
/* ************ EFFECTS DATA ************* */
typedef enum EEVEE_EffectsFlag {
@@ -607,9 +668,10 @@ typedef struct EEVEE_EffectsInfo {
int taa_render_sample;
int taa_total_sample;
float taa_alpha;
+ bool bypass_drawing;
bool prev_drw_support;
bool prev_is_navigating;
- float prev_drw_persmat[4][4];
+ float prev_drw_persmat[4][4]; /* Used for checking view validity and reprojection. */
struct DRWView *taa_view;
/* Ambient Occlusion */
int ao_depth_layer;
@@ -617,15 +679,24 @@ typedef struct EEVEE_EffectsInfo {
struct GPUTexture *gtao_horizons; /* Textures from pool */
struct GPUTexture *gtao_horizons_debug;
/* Motion Blur */
- float current_world_to_ndc[4][4];
float current_ndc_to_world[4][4];
+ float current_world_to_ndc[4][4];
+ float current_world_to_view[4][4];
float past_world_to_ndc[4][4];
- int motion_blur_samples;
- bool motion_blur_mat_cached;
+ float past_world_to_view[4][4];
+ CameraParams past_cam_params;
+ CameraParams current_cam_params;
+ char motion_blur_step; /* Which step we are evaluating. */
+ int motion_blur_max; /* Maximum distance in pixels a motion blured pixel can cover. */
+ float motion_blur_near_far[2]; /* Camera near/far clip distances (positive). */
+ bool cam_params_init;
+ /* TODO(fclem) Only used in render mode for now.
+ * This is because we are missing a per scene persistent place to hold this. */
+ struct EEVEE_MotionBlurData motion_blur;
/* Velocity Pass */
- float velocity_curr_persinv[4][4];
- float velocity_past_persmat[4][4];
struct GPUTexture *velocity_tx; /* Texture from pool */
+ struct GPUTexture *velocity_tiles_x_tx;
+ struct GPUTexture *velocity_tiles_tx;
/* Depth Of Field */
float dof_near_far[2];
float dof_params[2];
@@ -805,6 +876,7 @@ typedef struct EEVEE_ObjectEngineData {
bool ob_vis, ob_vis_dirty;
bool need_update;
+ bool geom_update;
uint shadow_caster_id;
} EEVEE_ObjectEngineData;
@@ -880,15 +952,25 @@ typedef struct EEVEE_PrivateData {
struct DRWView *world_views[6];
/** For rendering planar reflections. */
struct DRWView *planar_views[MAX_PLANAR];
+
+ int render_tot_samples;
} EEVEE_PrivateData; /* Transient data */
/* eevee_data.c */
+void EEVEE_motion_blur_data_init(EEVEE_MotionBlurData *mb);
+void EEVEE_motion_blur_data_free(EEVEE_MotionBlurData *mb);
void EEVEE_view_layer_data_free(void *sldata);
EEVEE_ViewLayerData *EEVEE_view_layer_data_get(void);
EEVEE_ViewLayerData *EEVEE_view_layer_data_ensure_ex(struct ViewLayer *view_layer);
EEVEE_ViewLayerData *EEVEE_view_layer_data_ensure(void);
EEVEE_ObjectEngineData *EEVEE_object_data_get(Object *ob);
EEVEE_ObjectEngineData *EEVEE_object_data_ensure(Object *ob);
+EEVEE_ObjectMotionData *EEVEE_motion_blur_object_data_get(EEVEE_MotionBlurData *mb,
+ Object *ob,
+ bool hair);
+EEVEE_GeometryMotionData *EEVEE_motion_blur_geometry_data_get(EEVEE_MotionBlurData *mb,
+ Object *ob,
+ bool hair);
EEVEE_LightProbeEngineData *EEVEE_lightprobe_data_get(Object *ob);
EEVEE_LightProbeEngineData *EEVEE_lightprobe_data_ensure(Object *ob);
EEVEE_LightEngineData *EEVEE_light_data_get(Object *ob);
@@ -896,6 +978,8 @@ EEVEE_LightEngineData *EEVEE_light_data_ensure(Object *ob);
EEVEE_WorldEngineData *EEVEE_world_data_get(World *wo);
EEVEE_WorldEngineData *EEVEE_world_data_ensure(World *wo);
+void eevee_id_update(void *vedata, ID *id);
+
/* eevee_materials.c */
struct GPUTexture *EEVEE_materials_get_util_tex(void); /* XXX */
void EEVEE_materials_init(EEVEE_ViewLayerData *sldata,
@@ -1112,8 +1196,17 @@ void EEVEE_subsurface_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data
void EEVEE_subsurface_free(void);
/* eevee_motion_blur.c */
-int EEVEE_motion_blur_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, Object *camera);
+int EEVEE_motion_blur_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
+void EEVEE_motion_blur_step_set(EEVEE_Data *vedata, int step);
void EEVEE_motion_blur_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
+void EEVEE_motion_blur_cache_populate(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, Object *ob);
+void EEVEE_motion_blur_hair_cache_populate(EEVEE_ViewLayerData *sldata,
+ EEVEE_Data *vedata,
+ Object *ob,
+ struct ParticleSystem *psys,
+ struct ModifierData *md);
+void EEVEE_motion_blur_swap_data(EEVEE_Data *vedata);
+void EEVEE_motion_blur_cache_finish(EEVEE_Data *vedata);
void EEVEE_motion_blur_draw(EEVEE_Data *vedata);
void EEVEE_motion_blur_free(void);
@@ -1127,6 +1220,7 @@ void EEVEE_renderpasses_init(EEVEE_Data *vedata);
void EEVEE_renderpasses_output_init(EEVEE_ViewLayerData *sldata,
EEVEE_Data *vedata,
uint tot_samples);
+void EEVEE_renderpasses_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
void EEVEE_renderpasses_output_accumulate(EEVEE_ViewLayerData *sldata,
EEVEE_Data *vedata,
bool post_effect);
@@ -1140,6 +1234,7 @@ bool EEVEE_renderpasses_only_first_sample_pass_active(EEVEE_Data *vedata);
/* eevee_temporal_sampling.c */
void EEVEE_temporal_sampling_reset(EEVEE_Data *vedata);
+void EEVEE_temporal_sampling_create_view(EEVEE_Data *vedata);
int EEVEE_temporal_sampling_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
void EEVEE_temporal_sampling_offset_calc(const double ht_point[2],
const float filter_size,
@@ -1183,6 +1278,10 @@ void EEVEE_effects_free(void);
bool EEVEE_render_init(EEVEE_Data *vedata,
struct RenderEngine *engine,
struct Depsgraph *depsgraph);
+void EEVEE_render_view_sync(EEVEE_Data *vedata,
+ struct RenderEngine *engine,
+ struct Depsgraph *depsgraph);
+void EEVEE_render_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
void EEVEE_render_cache(void *vedata,
struct Object *ob,
struct RenderEngine *engine,
@@ -1191,6 +1290,10 @@ void EEVEE_render_draw(EEVEE_Data *vedata,
struct RenderEngine *engine,
struct RenderLayer *render_layer,
const struct rcti *rect);
+void EEVEE_render_read_result(EEVEE_Data *vedata,
+ struct RenderEngine *engine,
+ struct RenderLayer *rl,
+ const rcti *rect);
void EEVEE_render_update_passes(struct RenderEngine *engine,
struct Scene *scene,
struct ViewLayer *view_layer);
diff --git a/source/blender/draw/engines/eevee/eevee_render.c b/source/blender/draw/engines/eevee/eevee_render.c
index 89a5ad2198a..f903fa905e8 100644
--- a/source/blender/draw/engines/eevee/eevee_render.c
+++ b/source/blender/draw/engines/eevee/eevee_render.c
@@ -131,6 +131,29 @@ bool EEVEE_render_init(EEVEE_Data *ved, RenderEngine *engine, struct Depsgraph *
&sldata->common_data);
}
+ EEVEE_render_view_sync(vedata, engine, depsgraph);
+
+ /* TODO(sergey): Shall render hold pointer to an evaluated camera instead? */
+ struct Object *ob_camera_eval = DEG_get_evaluated_object(depsgraph, RE_GetCamera(engine->re));
+
+ DRWView *view = (DRWView *)DRW_view_default_get();
+ DRW_view_camtexco_set(view, camtexcofac);
+
+ /* `EEVEE_renderpasses_init` will set the active render passes used by `EEVEE_effects_init`.
+ * `EEVEE_effects_init` needs to go second for TAA. */
+ EEVEE_renderpasses_init(vedata);
+ EEVEE_effects_init(sldata, vedata, ob_camera_eval, false);
+ EEVEE_materials_init(sldata, vedata, stl, fbl);
+ EEVEE_shadows_init(sldata);
+ EEVEE_lightprobes_init(sldata, vedata);
+
+ return true;
+}
+
+void EEVEE_render_view_sync(EEVEE_Data *vedata, RenderEngine *engine, struct Depsgraph *depsgraph)
+{
+ EEVEE_PrivateData *g_data = vedata->stl->g_data;
+
/* Set the pers & view matrix. */
float winmat[4][4], viewmat[4][4], viewinv[4][4];
/* TODO(sergey): Shall render hold pointer to an evaluated camera instead? */
@@ -143,19 +166,13 @@ bool EEVEE_render_init(EEVEE_Data *ved, RenderEngine *engine, struct Depsgraph *
invert_m4_m4(viewmat, viewinv);
DRWView *view = DRW_view_create(viewmat, winmat, NULL, NULL, NULL);
- DRW_view_camtexco_set(view, camtexcofac);
+ DRW_view_reset();
DRW_view_default_set(view);
DRW_view_set_active(view);
+}
- /* `EEVEE_renderpasses_init` will set the active render passes used by `EEVEE_effects_init`.
- * `EEVEE_effects_init` needs to go second for TAA. */
- EEVEE_renderpasses_init(vedata);
- EEVEE_effects_init(sldata, vedata, ob_camera_eval, false);
- EEVEE_materials_init(sldata, vedata, stl, fbl);
- EEVEE_shadows_init(sldata);
- EEVEE_lightprobes_init(sldata, vedata);
-
- /* INIT CACHE */
+void EEVEE_render_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
+{
EEVEE_bloom_cache_init(sldata, vedata);
EEVEE_depth_of_field_cache_init(sldata, vedata);
EEVEE_effects_cache_init(sldata, vedata);
@@ -168,8 +185,6 @@ bool EEVEE_render_init(EEVEE_Data *ved, RenderEngine *engine, struct Depsgraph *
EEVEE_subsurface_cache_init(sldata, vedata);
EEVEE_temporal_sampling_cache_init(sldata, vedata);
EEVEE_volumes_cache_init(sldata, vedata);
-
- return true;
}
/* Used by light cache. in this case engine is NULL. */
@@ -182,6 +197,8 @@ void EEVEE_render_cache(void *vedata,
EEVEE_LightProbesInfo *pinfo = sldata->probes;
bool cast_shadow = false;
+ eevee_id_update(vedata, &ob->id);
+
if (pinfo->vis_data.collection) {
/* Used for rendering probe with visibility groups. */
bool ob_vis = BKE_collection_has_object_recursive(pinfo->vis_data.collection, ob);
@@ -478,27 +495,12 @@ static void eevee_render_draw_background(EEVEE_Data *vedata)
void EEVEE_render_draw(EEVEE_Data *vedata, RenderEngine *engine, RenderLayer *rl, const rcti *rect)
{
- const DRWContextState *draw_ctx = DRW_context_state_get();
- const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
const char *viewname = RE_GetActiveRenderView(engine->re);
EEVEE_PassList *psl = vedata->psl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_FramebufferList *fbl = vedata->fbl;
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
- EEVEE_PrivateData *g_data = stl->g_data;
-
- /* FINISH CACHE */
- EEVEE_volumes_cache_finish(sldata, vedata);
- EEVEE_materials_cache_finish(sldata, vedata);
- EEVEE_lights_cache_finish(sldata, vedata);
- EEVEE_lightprobes_cache_finish(sldata, vedata);
-
- EEVEE_effects_draw_init(sldata, vedata);
- EEVEE_volumes_draw_init(sldata, vedata);
-
- /* Sort transparents before the loop. */
- DRW_pass_sort_shgroup_z(psl->transparent_pass);
/* Push instances attributes to the GPU. */
DRW_render_instance_buffer_finish();
@@ -508,20 +510,17 @@ void EEVEE_render_draw(EEVEE_Data *vedata, RenderEngine *engine, RenderLayer *rl
GPU_framebuffer_bind(fbl->main_fb);
DRW_hair_update();
- uint tot_sample = scene_eval->eevee.taa_render_samples;
+ /* Sort transparents before the loop. */
+ DRW_pass_sort_shgroup_z(psl->transparent_pass);
+
+ uint tot_sample = stl->g_data->render_tot_samples;
uint render_samples = 0;
/* SSR needs one iteration to start properly. */
- if (stl->effects->enabled_effects & EFFECT_SSR) {
+ if ((stl->effects->enabled_effects & EFFECT_SSR) && !stl->effects->ssr_was_valid_double_buffer) {
tot_sample += 1;
}
- EEVEE_renderpasses_output_init(sldata, vedata, tot_sample);
-
- if (RE_engine_test_break(engine)) {
- return;
- }
-
while (render_samples < tot_sample && !RE_engine_test_break(engine)) {
float clear_col[4] = {0.0f, 0.0f, 0.0f, 0.0f};
float clear_depth = 1.0f;
@@ -617,6 +616,15 @@ void EEVEE_render_draw(EEVEE_Data *vedata, RenderEngine *engine, RenderLayer *rl
RE_engine_update_progress(engine, (float)(render_samples++) / (float)tot_sample);
}
+}
+
+void EEVEE_render_read_result(EEVEE_Data *vedata,
+ RenderEngine *engine,
+ RenderLayer *rl,
+ const rcti *rect)
+{
+ const char *viewname = RE_GetActiveRenderView(engine->re);
+ EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
eevee_render_result_combined(rl, viewname, rect, vedata, sldata);
eevee_render_result_mist(rl, viewname, rect, vedata, sldata);
@@ -631,9 +639,6 @@ void EEVEE_render_draw(EEVEE_Data *vedata, RenderEngine *engine, RenderLayer *rl
eevee_render_result_bloom(rl, viewname, rect, vedata, sldata);
eevee_render_result_volume_scatter(rl, viewname, rect, vedata, sldata);
eevee_render_result_volume_transmittance(rl, viewname, rect, vedata, sldata);
-
- /* Restore original viewport size. */
- DRW_render_viewport_size_set((int[2]){g_data->size_orig[0], g_data->size_orig[1]});
}
void EEVEE_render_update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer)
diff --git a/source/blender/draw/engines/eevee/eevee_renderpasses.c b/source/blender/draw/engines/eevee/eevee_renderpasses.c
index 9a47ca19e7b..be771d7cf42 100644
--- a/source/blender/draw/engines/eevee/eevee_renderpasses.c
+++ b/source/blender/draw/engines/eevee/eevee_renderpasses.c
@@ -136,24 +136,13 @@ void EEVEE_renderpasses_output_init(EEVEE_ViewLayerData *sldata,
{
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_TextureList *txl = vedata->txl;
- EEVEE_PassList *psl = vedata->psl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
EEVEE_PrivateData *g_data = stl->g_data;
- DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
const bool needs_post_processing = (g_data->render_passes &
EEVEE_RENDERPASSES_WITH_POST_PROCESSING) > 0;
if (needs_post_processing) {
- if (e_data.postprocess_sh == NULL) {
- char *frag_str = BLI_string_joinN(datatoc_common_view_lib_glsl,
- datatoc_common_uniforms_lib_glsl,
- datatoc_bsdf_common_lib_glsl,
- datatoc_renderpass_postprocess_frag_glsl);
- e_data.postprocess_sh = DRW_shader_create_fullscreen(frag_str, NULL);
- MEM_freeN(frag_str);
- }
-
/* Create FrameBuffer. */
/* Should be enough to store the data needs for a single pass.
@@ -188,29 +177,51 @@ void EEVEE_renderpasses_output_init(EEVEE_ViewLayerData *sldata,
EEVEE_volumes_output_init(sldata, vedata, tot_samples);
}
- /* Create Pass. */
- DRW_PASS_CREATE(psl->renderpass_pass, DRW_STATE_WRITE_COLOR);
- DRWShadingGroup *grp = DRW_shgroup_create(e_data.postprocess_sh, psl->renderpass_pass);
/* We set a default texture as not all post processes uses the inputBuffer. */
g_data->renderpass_input = txl->color;
g_data->renderpass_col_input = txl->color;
g_data->renderpass_light_input = txl->color;
+ }
+ else {
+ /* Free unneeded memory */
+ DRW_TEXTURE_FREE_SAFE(txl->renderpass);
+ GPU_FRAMEBUFFER_FREE_SAFE(fbl->renderpass_fb);
+ }
+}
+
+void EEVEE_renderpasses_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
+{
+ EEVEE_PassList *psl = vedata->psl;
+ EEVEE_PrivateData *g_data = vedata->stl->g_data;
+ DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
+
+ const bool needs_post_processing = (g_data->render_passes &
+ EEVEE_RENDERPASSES_WITH_POST_PROCESSING) > 0;
+ if (needs_post_processing) {
+ if (e_data.postprocess_sh == NULL) {
+ char *frag_str = BLI_string_joinN(datatoc_common_view_lib_glsl,
+ datatoc_common_uniforms_lib_glsl,
+ datatoc_bsdf_common_lib_glsl,
+ datatoc_renderpass_postprocess_frag_glsl);
+ e_data.postprocess_sh = DRW_shader_create_fullscreen(frag_str, NULL);
+ MEM_freeN(frag_str);
+ }
+
+ DRW_PASS_CREATE(psl->renderpass_pass, DRW_STATE_WRITE_COLOR);
+ DRWShadingGroup *grp = DRW_shgroup_create(e_data.postprocess_sh, psl->renderpass_pass);
DRW_shgroup_uniform_texture_ref(grp, "inputBuffer", &g_data->renderpass_input);
DRW_shgroup_uniform_texture_ref(grp, "inputColorBuffer", &g_data->renderpass_col_input);
DRW_shgroup_uniform_texture_ref(
grp, "inputSecondLightBuffer", &g_data->renderpass_light_input);
DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth);
- DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
- DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
+ DRW_shgroup_uniform_block_ref(grp, "common_block", &sldata->common_ubo);
+ DRW_shgroup_uniform_block_ref(grp, "renderpass_block", &sldata->renderpass_ubo.combined);
DRW_shgroup_uniform_int(grp, "currentSample", &g_data->renderpass_current_sample, 1);
DRW_shgroup_uniform_int(grp, "renderpassType", &g_data->renderpass_type, 1);
DRW_shgroup_uniform_int(grp, "postProcessType", &g_data->renderpass_postprocess, 1);
DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL);
}
else {
- /* Free unneeded memory */
- DRW_TEXTURE_FREE_SAFE(txl->renderpass);
- GPU_FRAMEBUFFER_FREE_SAFE(fbl->renderpass_fb);
psl->renderpass_pass = NULL;
}
}
diff --git a/source/blender/draw/engines/eevee/eevee_screen_raytrace.c b/source/blender/draw/engines/eevee/eevee_screen_raytrace.c
index cece67334c5..32d758dba4b 100644
--- a/source/blender/draw/engines/eevee/eevee_screen_raytrace.c
+++ b/source/blender/draw/engines/eevee/eevee_screen_raytrace.c
@@ -358,7 +358,7 @@ void EEVEE_reflection_output_init(EEVEE_ViewLayerData *UNUSED(sldata),
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->ssr_accum)});
/* Clear texture. */
- if (DRW_state_is_image_render() || effects->taa_current_sample == 1) {
+ if (effects->taa_current_sample == 1) {
GPU_framebuffer_bind(fbl->ssr_accum_fb);
GPU_framebuffer_clear_color(fbl->ssr_accum_fb, clear);
}
diff --git a/source/blender/draw/engines/eevee/eevee_shadows.c b/source/blender/draw/engines/eevee/eevee_shadows.c
index 84c50a22ae6..d0e430e115f 100644
--- a/source/blender/draw/engines/eevee/eevee_shadows.c
+++ b/source/blender/draw/engines/eevee/eevee_shadows.c
@@ -412,7 +412,7 @@ void EEVEE_shadow_output_init(EEVEE_ViewLayerData *sldata,
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->shadow_accum)});
/* Clear texture. */
- if (DRW_state_is_image_render() || effects->taa_current_sample == 1) {
+ if (effects->taa_current_sample == 1) {
GPU_framebuffer_bind(fbl->shadow_accum_fb);
GPU_framebuffer_clear_color(fbl->shadow_accum_fb, clear);
}
diff --git a/source/blender/draw/engines/eevee/eevee_subsurface.c b/source/blender/draw/engines/eevee/eevee_subsurface.c
index 7674148f76a..ef4588f4aca 100644
--- a/source/blender/draw/engines/eevee/eevee_subsurface.c
+++ b/source/blender/draw/engines/eevee/eevee_subsurface.c
@@ -75,18 +75,8 @@ static void eevee_create_shader_subsurface(void)
MEM_freeN(frag_str);
}
-void EEVEE_subsurface_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
+void EEVEE_subsurface_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *UNUSED(vedata))
{
- EEVEE_CommonUniformBuffer *common_data = &sldata->common_data;
- EEVEE_StorageList *stl = vedata->stl;
- EEVEE_EffectsInfo *effects = stl->effects;
-
- const DRWContextState *draw_ctx = DRW_context_state_get();
- const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
-
- effects->sss_sample_count = 1 + scene_eval->eevee.sss_samples * 2;
- effects->sss_surface_count = 0;
- common_data->sss_jitter_threshold = scene_eval->eevee.sss_jitter_threshold;
}
void EEVEE_subsurface_draw_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
@@ -197,22 +187,31 @@ void EEVEE_subsurface_output_init(EEVEE_ViewLayerData *UNUSED(sldata),
* already higher than one. This is noticeable when loading a file that has the diffuse light
* pass in look dev mode active. `texture_created` will make sure that newly created textures
* are cleared. */
- if (DRW_state_is_image_render() || effects->taa_current_sample == 1 || texture_created) {
+ if (effects->taa_current_sample == 1 || texture_created) {
float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
GPU_framebuffer_bind(fbl->sss_accum_fb);
GPU_framebuffer_clear_color(fbl->sss_accum_fb, clear);
}
}
-void EEVEE_subsurface_cache_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
+void EEVEE_subsurface_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
{
+ EEVEE_CommonUniformBuffer *common_data = &sldata->common_data;
+ EEVEE_EffectsInfo *effects = vedata->stl->effects;
EEVEE_PassList *psl = vedata->psl;
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
+
/* Shaders */
if (!e_data.sss_sh[0]) {
eevee_create_shader_subsurface();
}
+ effects->sss_sample_count = 1 + scene_eval->eevee.sss_samples * 2;
+ effects->sss_surface_count = 0;
+ common_data->sss_jitter_threshold = scene_eval->eevee.sss_jitter_threshold;
+
/** Screen Space SubSurface Scattering overview
* TODO
*/
diff --git a/source/blender/draw/engines/eevee/eevee_temporal_sampling.c b/source/blender/draw/engines/eevee/eevee_temporal_sampling.c
index d57048f2c4e..714481c39f1 100644
--- a/source/blender/draw/engines/eevee/eevee_temporal_sampling.c
+++ b/source/blender/draw/engines/eevee/eevee_temporal_sampling.c
@@ -186,6 +186,18 @@ void EEVEE_temporal_sampling_reset(EEVEE_Data *vedata)
vedata->stl->effects->taa_current_sample = 1;
}
+void EEVEE_temporal_sampling_create_view(EEVEE_Data *vedata)
+{
+ EEVEE_EffectsInfo *effects = vedata->stl->effects;
+ /* Create a sub view to disable clipping planes (if any). */
+ const DRWView *default_view = DRW_view_default_get();
+ float viewmat[4][4], winmat[4][4];
+ DRW_view_viewmat_get(default_view, viewmat, false);
+ DRW_view_winmat_get(default_view, winmat, false);
+ effects->taa_view = DRW_view_create_sub(default_view, viewmat, winmat);
+ DRW_view_clip_planes_set(effects->taa_view, NULL, 0);
+}
+
int EEVEE_temporal_sampling_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
{
EEVEE_StorageList *stl = vedata->stl;
@@ -201,15 +213,9 @@ int EEVEE_temporal_sampling_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data
* we accumulate the redraw inside the drawing loop in eevee_draw_scene().
**/
effects->taa_render_sample = 1;
- effects->taa_view = NULL;
+ effects->bypass_drawing = false;
- /* Create a sub view to disable clipping planes (if any). */
- const DRWView *default_view = DRW_view_default_get();
- float viewmat[4][4], winmat[4][4];
- DRW_view_viewmat_get(default_view, viewmat, false);
- DRW_view_winmat_get(default_view, winmat, false);
- effects->taa_view = DRW_view_create_sub(default_view, viewmat, winmat);
- DRW_view_clip_planes_set(effects->taa_view, NULL, 0);
+ EEVEE_temporal_sampling_create_view(vedata);
const DRWContextState *draw_ctx = DRW_context_state_get();
const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
@@ -241,7 +247,6 @@ int EEVEE_temporal_sampling_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data
DRW_view_persmat_get(NULL, persmat, false);
view_is_valid = view_is_valid && compare_m4m4(persmat, effects->prev_drw_persmat, FLT_MIN);
- copy_m4_m4(effects->prev_drw_persmat, persmat);
/* Prevent ghosting from probe data. */
view_is_valid = view_is_valid && (effects->prev_drw_support == DRW_state_draw_support()) &&
@@ -251,7 +256,7 @@ int EEVEE_temporal_sampling_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data
if (((effects->taa_total_sample == 0) ||
(effects->taa_current_sample < effects->taa_total_sample)) ||
- DRW_state_is_image_render()) {
+ (!view_is_valid) || DRW_state_is_image_render()) {
if (view_is_valid) {
/* Viewport rendering updates the matrices in `eevee_draw_scene` */
if (!DRW_state_is_image_render()) {
@@ -264,7 +269,7 @@ int EEVEE_temporal_sampling_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data
}
}
else {
- effects->taa_current_sample = 1;
+ effects->bypass_drawing = true;
}
return repro_flag | EFFECT_TAA | EFFECT_DOUBLE_BUFFER | EFFECT_DEPTH_DOUBLE_BUFFER |
@@ -283,7 +288,7 @@ void EEVEE_temporal_sampling_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data
EEVEE_TextureList *txl = vedata->txl;
EEVEE_EffectsInfo *effects = stl->effects;
- if ((effects->enabled_effects & (EFFECT_TAA | EFFECT_TAA_REPROJECT)) != 0) {
+ if (effects->enabled_effects & EFFECT_TAA) {
struct GPUShader *sh = EEVEE_shaders_taa_resolve_sh_get(effects->enabled_effects);
DRW_PASS_CREATE(psl->taa_resolve, DRW_STATE_WRITE_COLOR);
@@ -295,8 +300,9 @@ void EEVEE_temporal_sampling_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data
DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
if (effects->enabled_effects & EFFECT_TAA_REPROJECT) {
- // DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
- DRW_shgroup_uniform_texture_ref(grp, "velocityBuffer", &effects->velocity_tx);
+ DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
+ DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth);
+ DRW_shgroup_uniform_mat4(grp, "prevViewProjectionMatrix", effects->prev_drw_persmat);
}
else {
DRW_shgroup_uniform_float(grp, "alpha", &effects->taa_alpha, 1);
@@ -359,10 +365,13 @@ void EEVEE_temporal_sampling_draw(EEVEE_Data *vedata)
effects->taa_current_sample += 1;
}
else {
- if ((effects->taa_total_sample == 0) ||
- (effects->taa_current_sample < effects->taa_total_sample)) {
+ if (!DRW_state_is_playback() &&
+ ((effects->taa_total_sample == 0) ||
+ (effects->taa_current_sample < effects->taa_total_sample))) {
DRW_viewport_request_redraw();
}
}
+
+ DRW_view_persmat_get(NULL, effects->prev_drw_persmat, false);
}
}
diff --git a/source/blender/draw/engines/eevee/eevee_volumes.c b/source/blender/draw/engines/eevee/eevee_volumes.c
index 90860e94270..17f53113939 100644
--- a/source/blender/draw/engines/eevee/eevee_volumes.c
+++ b/source/blender/draw/engines/eevee/eevee_volumes.c
@@ -894,7 +894,7 @@ void EEVEE_volumes_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata,
GPU_ATTACHMENT_TEXTURE(txl->volume_transmittance_accum)});
/* Clear texture. */
- if (DRW_state_is_image_render() || effects->taa_current_sample == 1) {
+ if (effects->taa_current_sample == 1) {
GPU_framebuffer_bind(fbl->volumetric_accum_fb);
GPU_framebuffer_clear_color(fbl->volumetric_accum_fb, clear);
}
diff --git a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl
index 402d306df45..393ecaf1fc5 100644
--- a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl
@@ -877,6 +877,14 @@ Closure closure_mix(Closure cl1, Closure cl2, float fac)
{
Closure cl;
cl.holdout = mix(cl1.holdout, cl2.holdout, fac);
+
+ if (FLAG_TEST(cl1.flag, CLOSURE_HOLDOUT_FLAG)) {
+ fac = 1.0;
+ }
+ else if (FLAG_TEST(cl2.flag, CLOSURE_HOLDOUT_FLAG)) {
+ fac = 0.0;
+ }
+
cl.transmittance = mix(cl1.transmittance, cl2.transmittance, fac);
cl.radiance = mix(cl1.radiance, cl2.radiance, fac);
cl.flag = cl1.flag | cl2.flag;
@@ -958,7 +966,7 @@ void main()
{
Closure cl = nodetree_exec();
- float holdout = 1.0 - saturate(cl.holdout);
+ float holdout = saturate(1.0 - cl.holdout);
float transmit = saturate(avg(cl.transmittance));
float alpha = 1.0 - transmit;
@@ -972,8 +980,9 @@ void main()
* Since we do that using the blending pipeline we need to account for material transmittance. */
vol_scatter -= vol_scatter * cl.transmittance;
- outRadiance = vec4(cl.radiance * vol_transmit + vol_scatter, alpha * holdout);
- outTransmittance = vec4(cl.transmittance, transmit * holdout);
+ cl.radiance = cl.radiance * holdout * vol_transmit + vol_scatter;
+ outRadiance = vec4(cl.radiance, alpha * holdout);
+ outTransmittance = vec4(cl.transmittance, transmit) * holdout;
# else
outRadiance = vec4(cl.radiance, holdout);
ssrNormals = cl.ssr_normal;
diff --git a/source/blender/draw/engines/eevee/shaders/effect_motion_blur_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_motion_blur_frag.glsl
index b7935235d06..fbf507a2e40 100644
--- a/source/blender/draw/engines/eevee/shaders/effect_motion_blur_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/effect_motion_blur_frag.glsl
@@ -1,64 +1,235 @@
+/*
+ * Based on:
+ * A Fast and Stable Feature-Aware Motion Blur Filter
+ * by Jean-Philippe Guertin, Morgan McGuire, Derek Nowrouzezahrai
+ *
+ * With modification from the presentation:
+ * Next Generation Post Processing in Call of Duty Advanced Warfare
+ * by Jorge Jimenez
+ */
uniform sampler2D colorBuffer;
uniform sampler2D depthBuffer;
+uniform sampler2D velocityBuffer;
+uniform sampler2D tileMaxBuffer;
-/* current frame */
-uniform mat4 currInvViewProjMatrix;
+#define KERNEL 8
-/* past frame frame */
-uniform mat4 pastViewProjMatrix;
+/* TODO(fclem) deduplicate this code. */
+uniform sampler2DArray utilTex;
+#define LUT_SIZE 64
+#define texelfetch_noise_tex(coord) texelFetch(utilTex, ivec3(ivec2(coord) % LUT_SIZE, 2.0), 0)
+
+uniform float depthScale;
+uniform ivec2 tileBufferSize;
+uniform vec2 viewportSize;
+uniform vec2 viewportSizeInv;
+uniform bool isPerspective;
+uniform vec2 nearFar; /* Near & far view depths values */
+
+#define linear_depth(z) \
+ ((isPerspective) ? (nearFar.x * nearFar.y) / (z * (nearFar.x - nearFar.y) + nearFar.y) : \
+ z * (nearFar.y - nearFar.x) + nearFar.x) /* Only true for camera view! */
in vec4 uvcoordsvar;
-out vec4 FragColor;
+out vec4 fragColor;
-#define MAX_SAMPLE 64
+#define saturate(a) clamp(a, 0.0, 1.0)
-uniform int samples;
+vec2 spread_compare(float center_motion_length, float sample_motion_length, float offset_length)
+{
+ return saturate(vec2(center_motion_length, sample_motion_length) - offset_length + 1.0);
+}
-float wang_hash_noise(uint s)
+vec2 depth_compare(float center_depth, float sample_depth)
{
- uint seed = (uint(gl_FragCoord.x) * 1664525u + uint(gl_FragCoord.y)) + s;
+ return saturate(0.5 + vec2(depthScale, -depthScale) * (sample_depth - center_depth));
+}
- seed = (seed ^ 61u) ^ (seed >> 16u);
- seed *= 9u;
- seed = seed ^ (seed >> 4u);
- seed *= 0x27d4eb2du;
- seed = seed ^ (seed >> 15u);
+/* Kill contribution if not going the same direction. */
+float dir_compare(vec2 offset, vec2 sample_motion, float sample_motion_length)
+{
+ if (sample_motion_length < 0.5) {
+ return 1.0;
+ }
+ return (dot(offset, sample_motion) > 0.0) ? 1.0 : 0.0;
+}
- float value = float(seed);
- value *= 1.0 / 4294967296.0;
- return fract(value);
+/* Return background (x) and foreground (y) weights. */
+vec2 sample_weights(float center_depth,
+ float sample_depth,
+ float center_motion_length,
+ float sample_motion_length,
+ float offset_length)
+{
+ /* Clasify foreground/background. */
+ vec2 depth_weight = depth_compare(center_depth, sample_depth);
+ /* Weight if sample is overlapping or under the center pixel. */
+ vec2 spread_weight = spread_compare(center_motion_length, sample_motion_length, offset_length);
+ return depth_weight * spread_weight;
}
-void main()
+vec4 decode_velocity(vec4 velocity)
{
- vec3 ndc_pos;
- ndc_pos.xy = uvcoordsvar.xy;
- ndc_pos.z = texture(depthBuffer, uvcoordsvar.xy).x;
+ velocity = velocity * 2.0 - 1.0;
+ /* Needed to match cycles. Can't find why... (fclem) */
+ velocity *= 0.5;
+ /* Transpose to pixelspace. */
+ velocity *= viewportSize.xyxy;
+ return velocity;
+}
- float inv_samples = 1.0 / float(samples);
- float noise = 2.0 * wang_hash_noise(0u) * inv_samples;
+vec4 sample_velocity(vec2 uv)
+{
+ vec4 data = texture(velocityBuffer, uv);
+ return decode_velocity(data);
+}
- /* Normalize Device Coordinates are [-1, +1]. */
- ndc_pos = ndc_pos * 2.0 - 1.0;
+vec2 sample_velocity(vec2 uv, const bool next)
+{
+ vec4 data = sample_velocity(uv);
+ data.xy = (next ? data.zw : data.xy);
+ return data.xy;
+}
- vec4 p = currInvViewProjMatrix * vec4(ndc_pos, 1.0);
- vec3 world_pos = p.xyz / p.w; /* Perspective divide */
+void gather_sample(vec2 screen_uv,
+ float center_depth,
+ float center_motion_len,
+ vec2 offset,
+ float offset_len,
+ const bool next,
+ inout vec4 accum,
+ inout vec4 accum_bg,
+ inout vec3 w_accum)
+{
+ vec2 sample_uv = screen_uv - offset * viewportSizeInv;
+ vec2 sample_motion = sample_velocity(sample_uv, next);
+ float sample_motion_len = length(sample_motion);
+ float sample_depth = linear_depth(texture(depthBuffer, sample_uv).r);
+ vec4 col = textureLod(colorBuffer, sample_uv, 0.0);
+
+ vec3 weights;
+ weights.xy = sample_weights(
+ center_depth, sample_depth, center_motion_len, sample_motion_len, offset_len);
+ weights.z = dir_compare(offset, sample_motion, sample_motion_len);
+ weights.xy *= weights.z;
+
+ accum += col * weights.y;
+ accum_bg += col * weights.x;
+ w_accum += weights;
+}
- /* Now find where was this pixel position
- * inside the past camera viewport */
- vec4 old_ndc = pastViewProjMatrix * vec4(world_pos, 1.0);
- old_ndc.xyz /= old_ndc.w; /* Perspective divide */
+void gather_blur(vec2 screen_uv,
+ vec2 center_motion,
+ float center_depth,
+ vec2 max_motion,
+ float ofs,
+ const bool next,
+ inout vec4 accum,
+ inout vec4 accum_bg,
+ inout vec3 w_accum)
+{
+ float center_motion_len = length(center_motion);
+ float max_motion_len = length(max_motion);
+
+ /* Tile boundaries randomization can fetch a tile where there is less motion than this pixel.
+ * Fix this by overriding the max_motion. */
+ if (max_motion_len < center_motion_len) {
+ max_motion_len = center_motion_len;
+ max_motion = center_motion;
+ }
- vec2 motion = (ndc_pos.xy - old_ndc.xy) * 0.25; /* 0.25 fit cycles ref */
+ if (max_motion_len < 0.5) {
+ return;
+ }
- float inc = 2.0 * inv_samples;
- float i = -1.0 + noise;
+ int i;
+ float t, inc = 1.0 / float(KERNEL);
+ for (i = 0, t = ofs * inc; i < KERNEL; i++, t += inc) {
+ gather_sample(screen_uv,
+ center_depth,
+ center_motion_len,
+ max_motion * t,
+ max_motion_len * t,
+ next,
+ accum,
+ accum_bg,
+ w_accum);
+ }
- FragColor = vec4(0.0);
- for (int j = 0; j < samples && j < MAX_SAMPLE; j++) {
- FragColor += textureLod(colorBuffer, uvcoordsvar.xy + motion * i, 0.0) * inv_samples;
- i += inc;
+ if (center_motion_len < 0.5) {
+ return;
}
+
+ for (i = 0, t = ofs * inc; i < KERNEL; i++, t += inc) {
+ /* Also sample in center motion direction.
+ * Allow to recover motion where there is conflicting
+ * motion between foreground and background. */
+ gather_sample(screen_uv,
+ center_depth,
+ center_motion_len,
+ center_motion * t,
+ center_motion_len * t,
+ next,
+ accum,
+ accum_bg,
+ w_accum);
+ }
+}
+
+void main()
+{
+ vec2 uv = uvcoordsvar.xy;
+
+ /* Data of the center pixel of the gather (target). */
+ float center_depth = linear_depth(texture(depthBuffer, uv).r);
+ vec4 center_motion = sample_velocity(uv);
+ vec4 center_color = textureLod(colorBuffer, uv, 0.0);
+
+ vec2 rand = texelfetch_noise_tex(gl_FragCoord.xy).xy;
+
+ /* Randomize tile boundary to avoid ugly discontinuities. Randomize 1/4th of the tile.
+ * Note this randomize only in one direction but in practice it's enough. */
+ rand.x = rand.x * 2.0 - 1.0;
+ ivec2 tile = ivec2(gl_FragCoord.xy + rand.x * float(EEVEE_VELOCITY_TILE_SIZE) * 0.25) /
+ EEVEE_VELOCITY_TILE_SIZE;
+ tile = clamp(tile, ivec2(0), tileBufferSize - 1);
+ vec4 max_motion = decode_velocity(texelFetch(tileMaxBuffer, tile, 0));
+
+ /* First (center) sample: time = T */
+ /* x: Background, y: Foreground, z: dir. */
+ vec3 w_accum = vec3(0.0, 0.0, 1.0);
+ vec4 accum_bg = vec4(0.0);
+ vec4 accum = vec4(0.0);
+ /* First linear gather. time = [T - delta, T] */
+ gather_blur(
+ uv, center_motion.xy, center_depth, max_motion.xy, rand.y, false, accum, accum_bg, w_accum);
+ /* Second linear gather. time = [T, T + delta] */
+ gather_blur(
+ uv, center_motion.zw, center_depth, max_motion.zw, rand.y, true, accum, accum_bg, w_accum);
+
+#if 1
+ /* Avoid division by 0.0. */
+ float w = 1.0 / (50.0 * float(KERNEL) * 4.0);
+ accum_bg += center_color * w;
+ w_accum.x += w;
+ /* Note: In Jimenez's presentation, they used center sample.
+ * We use background color as it contains more informations for foreground
+ * elements that have not enough weights.
+ * Yield beter blur in complex motion. */
+ center_color = accum_bg / w_accum.x;
+#endif
+ /* Merge background. */
+ accum += accum_bg;
+ w_accum.y += w_accum.x;
+ /* Balance accumulation for failled samples.
+ * We replace the missing foreground by the background. */
+ float blend_fac = saturate(1.0 - w_accum.y / w_accum.z);
+ fragColor = (accum / w_accum.z) + center_color * blend_fac;
+
+#if 0 /* For debugging. */
+ fragColor.rgb = fragColor.ggg;
+ fragColor.rg += max_motion.xy;
+#endif
}
diff --git a/source/blender/draw/engines/eevee/shaders/effect_temporal_aa.glsl b/source/blender/draw/engines/eevee/shaders/effect_temporal_aa.glsl
index 428318e3c68..b44645174bd 100644
--- a/source/blender/draw/engines/eevee/shaders/effect_temporal_aa.glsl
+++ b/source/blender/draw/engines/eevee/shaders/effect_temporal_aa.glsl
@@ -1,6 +1,6 @@
uniform sampler2D colorHistoryBuffer;
-uniform sampler2D velocityBuffer;
+uniform mat4 prevViewProjectionMatrix;
out vec4 FragColor;
@@ -38,16 +38,19 @@ vec3 clip_to_aabb(vec3 color, vec3 minimum, vec3 maximum, vec3 average)
*/
void main()
{
+ vec2 screen_res = vec2(textureSize(colorBuffer, 0).xy);
+ vec2 uv = gl_FragCoord.xy / screen_res;
ivec2 texel = ivec2(gl_FragCoord.xy);
- vec2 motion = texelFetch(velocityBuffer, texel, 0).rg;
-
- /* Decode from unsigned normalized 16bit texture. */
- motion = motion * 2.0 - 1.0;
/* Compute pixel position in previous frame. */
- vec2 screen_res = vec2(textureSize(colorBuffer, 0).xy);
- vec2 uv = gl_FragCoord.xy / screen_res;
- vec2 uv_history = uv - motion;
+ float depth = textureLod(depthBuffer, uv, 0.0).r;
+ vec3 pos = get_world_space_from_depth(uv, depth);
+ vec2 uv_history = project_point(prevViewProjectionMatrix, pos).xy * 0.5 + 0.5;
+
+ /* HACK: Reject lookdev spheres from TAA reprojection. */
+ if (depth == 0.0) {
+ uv_history = uv;
+ }
ivec2 texel_history = ivec2(uv_history * screen_res);
vec4 color_history = textureLod(colorHistoryBuffer, uv_history, 0.0);
diff --git a/source/blender/draw/engines/eevee/shaders/effect_velocity_resolve_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_velocity_resolve_frag.glsl
index 7d701bce5cb..d927fd78d30 100644
--- a/source/blender/draw/engines/eevee/shaders/effect_velocity_resolve_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/effect_velocity_resolve_frag.glsl
@@ -1,8 +1,9 @@
-uniform mat4 currPersinv;
-uniform mat4 pastPersmat;
+uniform mat4 prevViewProjMatrix;
+uniform mat4 currViewProjMatrixInv;
+uniform mat4 nextViewProjMatrix;
-out vec2 outData;
+out vec4 outData;
void main()
{
@@ -12,13 +13,12 @@ void main()
float depth = texelFetch(depthBuffer, texel, 0).r;
- vec3 world_position = project_point(currPersinv, vec3(uv, depth) * 2.0 - 1.0);
- vec2 uv_history = project_point(pastPersmat, world_position).xy * 0.5 + 0.5;
+ vec3 world_position = project_point(currViewProjMatrixInv, vec3(uv, depth) * 2.0 - 1.0);
+ vec2 uv_prev = project_point(prevViewProjMatrix, world_position).xy * 0.5 + 0.5;
+ vec2 uv_next = project_point(nextViewProjMatrix, world_position).xy * 0.5 + 0.5;
- outData = uv - uv_history;
-
- /* HACK: Reject lookdev spheres from TAA reprojection. */
- outData = (depth > 0.0) ? outData : vec2(0.0);
+ outData.xy = uv_prev - uv;
+ outData.zw = uv_next - uv;
/* Encode to unsigned normalized 16bit texture. */
outData = outData * 0.5 + 0.5;
diff --git a/source/blender/draw/engines/eevee/shaders/effect_velocity_tile_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_velocity_tile_frag.glsl
new file mode 100644
index 00000000000..0eb598521af
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/effect_velocity_tile_frag.glsl
@@ -0,0 +1,151 @@
+/**
+ * Shaders that down-sample velocity buffer,
+ *
+ * Based on:
+ * A Fast and Stable Feature-Aware Motion Blur Filter
+ * by Jean-Philippe Guertin, Morgan McGuire, Derek Nowrouzezahrai
+ *
+ * Adapted from G3D Innovation Engine implementation.
+ */
+
+uniform sampler2D velocityBuffer;
+uniform vec2 viewportSize;
+uniform vec2 viewportSizeInv;
+uniform ivec2 velocityBufferSize;
+
+out vec4 tileMaxVelocity;
+
+vec4 sample_velocity(ivec2 texel)
+{
+ texel = clamp(texel, ivec2(0), velocityBufferSize - 1);
+ vec4 data = texelFetch(velocityBuffer, texel, 0);
+ /* Decode data. */
+ return (data * 2.0 - 1.0) * viewportSize.xyxy;
+}
+
+vec4 encode_velocity(vec4 velocity)
+{
+ return velocity * viewportSizeInv.xyxy * 0.5 + 0.5;
+}
+
+#ifdef TILE_GATHER
+
+uniform ivec2 gatherStep;
+
+void main()
+{
+ vec4 max_motion = vec4(0.0);
+ float max_motion_len_sqr_prev = 0.0;
+ float max_motion_len_sqr_next = 0.0;
+ ivec2 texel = ivec2(gl_FragCoord.xy);
+ texel = texel * gatherStep.yx + texel * EEVEE_VELOCITY_TILE_SIZE * gatherStep;
+
+ for (int i = 0; i < EEVEE_VELOCITY_TILE_SIZE; ++i) {
+ vec4 motion = sample_velocity(texel + i * gatherStep);
+ float motion_len_sqr_prev = dot(motion.xy, motion.xy);
+ float motion_len_sqr_next = dot(motion.zw, motion.zw);
+
+ if (motion_len_sqr_prev > max_motion_len_sqr_prev) {
+ max_motion_len_sqr_prev = motion_len_sqr_prev;
+ max_motion.xy = motion.xy;
+ }
+ if (motion_len_sqr_next > max_motion_len_sqr_next) {
+ max_motion_len_sqr_next = motion_len_sqr_next;
+ max_motion.zw = motion.zw;
+ }
+ }
+
+ tileMaxVelocity = encode_velocity(max_motion);
+}
+
+#else /* TILE_EXPANSION */
+
+bool neighbor_affect_this_tile(ivec2 offset, vec2 velocity)
+{
+ /* Manhattan distance to the tiles, which is used for
+ * differentiating corners versus middle blocks */
+ float displacement = float(abs(offset.x) + abs(offset.y));
+ /**
+ * Relative sign on each axis of the offset compared
+ * to the velocity for that tile. In order for a tile
+ * to affect the center tile, it must have a
+ * neighborhood velocity in which x and y both have
+ * identical or both have opposite signs relative to
+ * offset. If the offset coordinate is zero then
+ * velocity is irrelevant.
+ **/
+ vec2 point = sign(offset * velocity);
+
+ float dist = (point.x + point.y);
+ /**
+ * Here's an example of the logic for this code.
+ * In this diagram, the upper-left tile has offset = (-1, -1).
+ * V1 is velocity = (1, -2). point in this case = (-1, 1), and therefore dist = 0,
+ * so the upper-left tile does not affect the center.
+ *
+ * Now, look at another case. V2 = (-1, -2). point = (1, 1), so dist = 2 and the tile
+ * does affect the center.
+ *
+ * V2(-1,-2) V1(1, -2)
+ * \ /
+ * \ /
+ * \/___ ____ ____
+ * (-1, -1)| | | |
+ * |____|____|____|
+ * | | | |
+ * |____|____|____|
+ * | | | |
+ * |____|____|____|
+ **/
+ return (abs(dist) == displacement);
+}
+
+/**
+ * Only gather neighborhood velocity into tiles that could be affected by it.
+ * In the general case, only six of the eight neighbors contribute:
+ *
+ * This tile can't possibly be affected by the center one
+ * |
+ * v
+ * ____ ____ ____
+ * | | ///|/// |
+ * |____|////|//__|
+ * | |////|/ |
+ * |___/|////|____|
+ * | //|////| | <--- This tile can't possibly be affected by the center one
+ * |_///|///_|____|
+ **/
+void main()
+{
+ vec4 max_motion = vec4(0.0);
+ float max_motion_len_sqr_prev = -1.0;
+ float max_motion_len_sqr_next = -1.0;
+
+ ivec2 tile = ivec2(gl_FragCoord.xy);
+ ivec2 offset = ivec2(0);
+ for (offset.y = -1; offset.y <= 1; ++offset.y) {
+ for (offset.x = -1; offset.x <= 1; ++offset.x) {
+ vec4 motion = sample_velocity(tile + offset);
+ float motion_len_sqr_prev = dot(motion.xy, motion.xy);
+ float motion_len_sqr_next = dot(motion.zw, motion.zw);
+
+ if (motion_len_sqr_prev > max_motion_len_sqr_prev) {
+ if (neighbor_affect_this_tile(offset, motion.xy)) {
+ max_motion_len_sqr_prev = motion_len_sqr_prev;
+ max_motion.xy = motion.xy;
+ }
+ }
+
+ if (motion_len_sqr_next > max_motion_len_sqr_next) {
+ if (neighbor_affect_this_tile(offset, motion.zw)) {
+ max_motion_len_sqr_next = motion_len_sqr_next;
+ max_motion.zw = motion.zw;
+ }
+ }
+ }
+ }
+
+ tileMaxVelocity = encode_velocity(max_motion);
+}
+
+#endif \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee/shaders/object_motion_frag.glsl b/source/blender/draw/engines/eevee/shaders/object_motion_frag.glsl
new file mode 100644
index 00000000000..66b098ef6c5
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/object_motion_frag.glsl
@@ -0,0 +1,27 @@
+
+uniform mat4 prevViewProjMatrix;
+uniform mat4 currViewProjMatrix;
+uniform mat4 nextViewProjMatrix;
+
+in vec3 prevWorldPos;
+in vec3 currWorldPos;
+in vec3 nextWorldPos;
+
+out vec4 outData;
+
+void main()
+{
+ vec4 prev_wpos = prevViewProjMatrix * vec4(prevWorldPos, 1.0);
+ vec4 curr_wpos = currViewProjMatrix * vec4(currWorldPos, 1.0);
+ vec4 next_wpos = nextViewProjMatrix * vec4(nextWorldPos, 1.0);
+
+ vec2 prev_uv = (prev_wpos.xy / prev_wpos.w);
+ vec2 curr_uv = (curr_wpos.xy / curr_wpos.w);
+ vec2 next_uv = (next_wpos.xy / next_wpos.w);
+
+ outData.xy = prev_uv - curr_uv;
+ outData.zw = next_uv - curr_uv;
+
+ /* Encode to unsigned normalized 16bit texture. */
+ outData = outData * 0.5 + 0.5;
+}
diff --git a/source/blender/draw/engines/eevee/shaders/object_motion_vert.glsl b/source/blender/draw/engines/eevee/shaders/object_motion_vert.glsl
new file mode 100644
index 00000000000..95cd69ba310
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/object_motion_vert.glsl
@@ -0,0 +1,54 @@
+
+uniform mat4 currModelMatrix;
+uniform mat4 prevModelMatrix;
+uniform mat4 nextModelMatrix;
+uniform bool useDeform;
+
+#ifdef HAIR
+uniform samplerBuffer prvBuffer; /* RGBA32F */
+uniform samplerBuffer nxtBuffer; /* RGBA32F */
+#else
+in vec3 pos;
+in vec3 prv; /* Previous frame position. */
+in vec3 nxt; /* Next frame position. */
+#endif
+
+out vec3 currWorldPos;
+out vec3 prevWorldPos;
+out vec3 nextWorldPos;
+
+void main()
+{
+#ifdef HAIR
+ bool is_persp = (ProjectionMatrix[3][3] == 0.0);
+ float time, thick_time, thickness;
+ vec3 tan, binor;
+ vec3 wpos;
+
+ hair_get_pos_tan_binor_time(is_persp,
+ ModelMatrixInverse,
+ ViewMatrixInverse[3].xyz,
+ ViewMatrixInverse[2].xyz,
+ wpos,
+ tan,
+ binor,
+ time,
+ thickness,
+ thick_time);
+
+ int id = hair_get_base_id();
+ vec3 pos = texelFetch(hairPointBuffer, id).point_position;
+ vec3 prv = texelFetch(prvBuffer, id).point_position;
+ vec3 nxt = texelFetch(nxtBuffer, id).point_position;
+#endif
+ prevWorldPos = (prevModelMatrix * vec4(useDeform ? prv : pos, 1.0)).xyz;
+ currWorldPos = (currModelMatrix * vec4(pos, 1.0)).xyz;
+ nextWorldPos = (nextModelMatrix * vec4(useDeform ? nxt : pos, 1.0)).xyz;
+ /* Use jittered projmatrix to be able to match exact sample depth (depth equal test).
+ * Note that currModelMatrix needs to also be equal to ModelMatrix for the samples to match. */
+#ifndef HAIR
+ gl_Position = ViewProjectionMatrix * vec4(currWorldPos, 1.0);
+#else
+ gl_Position = ViewProjectionMatrix * vec4(wpos, 1.0);
+#endif
+}
diff --git a/source/blender/draw/engines/external/external_engine.c b/source/blender/draw/engines/external/external_engine.c
index 2f448b784ed..3ef20dbe9ec 100644
--- a/source/blender/draw/engines/external/external_engine.c
+++ b/source/blender/draw/engines/external/external_engine.c
@@ -97,8 +97,6 @@ typedef struct EXTERNAL_PrivateData {
/* Do we need to update the depth or can we reuse the last calculated texture. */
bool need_depth;
bool update_depth;
-
- float last_persmat[4][4];
} EXTERNAL_PrivateData; /* Transient data */
/* Functions */
diff --git a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c
index 2b811f1d52e..d97bf9255d2 100644
--- a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c
+++ b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c
@@ -159,7 +159,7 @@ void gpencil_object_cache_sort(GPENCIL_PrivateData *pd)
}
}
- /* Join both lists, adding infront. */
+ /* Join both lists, adding in front. */
if (pd->tobjects_infront.first != NULL) {
if (pd->tobjects.last != NULL) {
pd->tobjects.last->next = pd->tobjects_infront.first;
@@ -265,8 +265,10 @@ GPENCIL_tLayer *gpencil_layer_cache_add(GPENCIL_PrivateData *pd,
GPENCIL_VERTEX_MODE(gpd) || pd->is_render;
bool is_masked = (gpl->flag & GP_LAYER_USE_MASK) && !BLI_listbase_is_empty(&gpl->mask_layers);
- float vert_col_opacity = (overide_vertcol) ? (is_vert_col_mode ? 1.0f : 0.0f) :
- gpl->vertex_paint_opacity;
+ float vert_col_opacity = (overide_vertcol) ?
+ (is_vert_col_mode ? pd->vertex_paint_opacity : 0.0f) :
+ pd->is_render ? gpl->vertex_paint_opacity :
+ pd->vertex_paint_opacity;
/* Negate thickness sign to tag that strokes are in screen space.
* Convert to world units (by default, 1 meter = 2000 px). */
float thickness_scale = (is_screenspace) ? -1.0f : (gpd->pixfactor / GPENCIL_PIXEL_FACTOR);
diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.c b/source/blender/draw/engines/gpencil/gpencil_engine.c
index 495de7ef10b..dded83bacf1 100644
--- a/source/blender/draw/engines/gpencil/gpencil_engine.c
+++ b/source/blender/draw/engines/gpencil/gpencil_engine.c
@@ -224,6 +224,7 @@ void GPENCIL_cache_init(void *ved)
const bool is_fade_layer = ((!hide_overlay) && (!pd->is_render) &&
(draw_ctx->v3d->gp_flag & V3D_GP_FADE_NOACTIVE_LAYERS));
pd->fade_layer_opacity = (is_fade_layer) ? draw_ctx->v3d->overlay.gpencil_fade_layer : -1.0f;
+ pd->vertex_paint_opacity = draw_ctx->v3d->overlay.gpencil_vertex_paint_opacity;
/* Fade GPencil Objects. */
const bool is_fade_object = ((!hide_overlay) && (!pd->is_render) &&
(draw_ctx->v3d->gp_flag & V3D_GP_FADE_OBJECTS) &&
@@ -383,7 +384,7 @@ static void gpencil_drawcall_flush(gpIterPopulateData *iter)
}
/* Group drawcalls that are consecutive and with the same type. Reduces GPU driver overhead. */
-static void gp_drawcall_add(
+static void gpencil_drawcall_add(
gpIterPopulateData *iter, struct GPUBatch *geom, bool instancing, int v_first, int v_count)
{
#if DISABLE_BATCHING
@@ -413,7 +414,7 @@ static void gpencil_stroke_cache_populate(bGPDlayer *gpl,
bGPDstroke *gps,
void *thunk);
-static void gp_sbuffer_cache_populate(gpIterPopulateData *iter)
+static void gpencil_sbuffer_cache_populate(gpIterPopulateData *iter)
{
iter->do_sbuffer_call = DRAW_NOW;
/* In order to draw the sbuffer stroke correctly mixed with other strokes,
@@ -450,7 +451,7 @@ static void gpencil_layer_cache_populate(bGPDlayer *gpl,
gpencil_drawcall_flush(iter);
if (iter->do_sbuffer_call) {
- gp_sbuffer_cache_populate(iter);
+ gpencil_sbuffer_cache_populate(iter);
}
else {
iter->do_sbuffer_call = !pd->do_fast_drawing && (gpd == pd->sbuffer_gpd) &&
@@ -492,8 +493,10 @@ static void gpencil_stroke_cache_populate(bGPDlayer *gpl,
(!iter->pd->simplify_fill) && ((gps->flag & GP_STROKE_NOFILL) == 0);
bool only_lines = gpl && gpf && gpl->actframe != gpf && iter->pd->use_multiedit_lines_only;
+ bool hide_onion = gpl && gpf && gpf->runtime.onion_id != 0 &&
+ ((gp_style->flag & GP_MATERIAL_HIDE_ONIONSKIN) != 0);
- if (hide_material || (!show_stroke && !show_fill) || only_lines) {
+ if (hide_material || (!show_stroke && !show_fill) || only_lines || hide_onion) {
return;
}
@@ -537,7 +540,7 @@ static void gpencil_stroke_cache_populate(bGPDlayer *gpl,
DRW_cache_gpencil_fills_get(iter->ob, iter->pd->cfra);
int vfirst = gps->runtime.fill_start * 3;
int vcount = gps->tot_triangles * 3;
- gp_drawcall_add(iter, geom, false, vfirst, vcount);
+ gpencil_drawcall_add(iter, geom, false, vfirst, vcount);
}
if (show_stroke) {
@@ -547,13 +550,13 @@ static void gpencil_stroke_cache_populate(bGPDlayer *gpl,
int vfirst = gps->runtime.stroke_start - 1;
/* Include "potential" cyclic vertex and start adj vertex (see shader). */
int vcount = gps->totpoints + 1 + 1;
- gp_drawcall_add(iter, geom, true, vfirst, vcount);
+ gpencil_drawcall_add(iter, geom, true, vfirst, vcount);
}
iter->stroke_index_last = gps->runtime.stroke_start + gps->totpoints + 1;
}
-static void gp_sbuffer_cache_populate_fast(GPENCIL_Data *vedata, gpIterPopulateData *iter)
+static void gpencil_sbuffer_cache_populate_fast(GPENCIL_Data *vedata, gpIterPopulateData *iter)
{
bGPdata *gpd = (bGPdata *)iter->ob->data;
if (gpd != iter->pd->sbuffer_gpd) {
@@ -630,13 +633,13 @@ void GPENCIL_cache_populate(void *ved, Object *ob)
gpencil_drawcall_flush(&iter);
if (iter.do_sbuffer_call) {
- gp_sbuffer_cache_populate(&iter);
+ gpencil_sbuffer_cache_populate(&iter);
}
gpencil_vfx_cache_populate(vedata, ob, iter.tgp_ob);
if (pd->do_fast_drawing) {
- gp_sbuffer_cache_populate_fast(vedata, &iter);
+ gpencil_sbuffer_cache_populate_fast(vedata, &iter);
}
}
diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.h b/source/blender/draw/engines/gpencil/gpencil_engine.h
index cedd75af813..7baca28dca3 100644
--- a/source/blender/draw/engines/gpencil/gpencil_engine.h
+++ b/source/blender/draw/engines/gpencil/gpencil_engine.h
@@ -363,6 +363,8 @@ typedef struct GPENCIL_PrivateData {
float xray_alpha;
/* Mask invert uniform. */
int mask_invert;
+ /* Vertex Paint opacity. */
+ float vertex_paint_opacity;
} GPENCIL_PrivateData;
/* geometry batch cache functions */
diff --git a/source/blender/draw/engines/gpencil/gpencil_shader_fx.c b/source/blender/draw/engines/gpencil/gpencil_shader_fx.c
index 7ce7a726bb7..cf6e78f4702 100644
--- a/source/blender/draw/engines/gpencil/gpencil_shader_fx.c
+++ b/source/blender/draw/engines/gpencil/gpencil_shader_fx.c
@@ -262,6 +262,8 @@ static void gpencil_vfx_pixelize(PixelShaderFxData *fx, Object *ob, gpIterVfxDat
mul_v3_m4v3(ob_center, persmat, ob->obmat[3]);
mul_v3_fl(ob_center, 1.0f / w);
+ const bool use_antialiasing = ((fx->flag & FX_PIXEL_FILTER_NEAREST) == 0);
+
/* Convert to uvs. */
mul_v2_fl(ob_center, 0.5f);
add_v2_fl(ob_center, 0.5f);
@@ -285,7 +287,8 @@ static void gpencil_vfx_pixelize(PixelShaderFxData *fx, Object *ob, gpIterVfxDat
DRW_shgroup_uniform_vec2_copy(grp, "targetPixelSize", pixsize_uniform);
DRW_shgroup_uniform_vec2_copy(grp, "targetPixelOffset", ob_center);
DRW_shgroup_uniform_vec2_copy(grp, "accumOffset", (float[2]){pixel_size[0], 0.0f});
- DRW_shgroup_uniform_int_copy(grp, "sampCount", (pixel_size[0] / vp_size_inv[0] > 3.0) ? 2 : 1);
+ int samp_count = (pixel_size[0] / vp_size_inv[0] > 3.0) ? 2 : 1;
+ DRW_shgroup_uniform_int_copy(grp, "sampCount", use_antialiasing ? samp_count : 0);
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
}
@@ -294,7 +297,8 @@ static void gpencil_vfx_pixelize(PixelShaderFxData *fx, Object *ob, gpIterVfxDat
grp = gpencil_vfx_pass_create("Fx Pixelize Y", state, iter, sh);
DRW_shgroup_uniform_vec2_copy(grp, "targetPixelSize", pixsize_uniform);
DRW_shgroup_uniform_vec2_copy(grp, "accumOffset", (float[2]){0.0f, pixel_size[1]});
- DRW_shgroup_uniform_int_copy(grp, "sampCount", (pixel_size[1] / vp_size_inv[1] > 3.0) ? 2 : 1);
+ int samp_count = (pixel_size[1] / vp_size_inv[1] > 3.0) ? 2 : 1;
+ DRW_shgroup_uniform_int_copy(grp, "sampCount", use_antialiasing ? samp_count : 0);
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
}
}
diff --git a/source/blender/draw/engines/overlay/overlay_armature.c b/source/blender/draw/engines/overlay/overlay_armature.c
index 481dec340ba..7ccb5d5a753 100644
--- a/source/blender/draw/engines/overlay/overlay_armature.c
+++ b/source/blender/draw/engines/overlay/overlay_armature.c
@@ -228,11 +228,12 @@ void OVERLAY_armature_cache_init(OVERLAY_Data *vedata)
{
format = formats->instance_extra;
- sh = OVERLAY_shader_armature_degrees_of_freedom();
+ sh = OVERLAY_shader_armature_degrees_of_freedom_wire();
grp = DRW_shgroup_create(sh, armature_ps);
DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo);
cb->dof_lines = BUF_INSTANCE(grp, format, DRW_cache_bone_dof_lines_get());
+ sh = OVERLAY_shader_armature_degrees_of_freedom_solid();
grp = DRW_shgroup_create(sh, armature_transp_ps);
DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo);
cb->dof_sphere = BUF_INSTANCE(grp, format, DRW_cache_bone_dof_sphere_get());
@@ -2167,7 +2168,8 @@ static void armature_context_setup(ArmatureDrawContext *ctx,
ctx->do_relations = !DRW_state_is_select() && pd->armature.show_relations &&
(is_edit_mode | is_pose_mode);
ctx->const_color = DRW_state_is_select() ? select_const_color : const_color;
- ctx->const_wire = (((ob->base_flag & BASE_SELECTED) || (arm->drawtype == ARM_WIRE)) ?
+ ctx->const_wire = ((((ob->base_flag & BASE_SELECTED) && (pd->v3d_flag & V3D_SELECT_OUTLINE)) ||
+ (arm->drawtype == ARM_WIRE)) ?
1.5f :
((!is_filled || is_transparent) ? 1.0f : 0.0f));
}
diff --git a/source/blender/draw/engines/overlay/overlay_edit_text.c b/source/blender/draw/engines/overlay/overlay_edit_text.c
index c4d020adc11..4ee936f5ce6 100644
--- a/source/blender/draw/engines/overlay/overlay_edit_text.c
+++ b/source/blender/draw/engines/overlay/overlay_edit_text.c
@@ -53,13 +53,22 @@ void OVERLAY_edit_text_cache_init(OVERLAY_Data *vedata)
DRW_shgroup_uniform_vec4_copy(grp, "color", G_draw.block.colorWire);
}
{
- state = DRW_STATE_WRITE_COLOR | DRW_STATE_LOGIC_INVERT;
+ state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA;
DRW_PASS_CREATE(psl->edit_text_overlay_ps, state | pd->clipping_state);
sh = OVERLAY_shader_uniform_color();
pd->edit_text_overlay_grp = grp = DRW_shgroup_create(sh, psl->edit_text_overlay_ps);
- DRW_shgroup_uniform_vec4_copy(grp, "color", (float[4]){1.0f, 1.0f, 1.0f, 1.0f});
+ DRW_shgroup_uniform_vec4(grp, "color", pd->edit_text.overlay_color, 1);
+
+ state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_MUL | DRW_STATE_DEPTH_GREATER_EQUAL |
+ pd->clipping_state;
+ DRW_PASS_INSTANCE_CREATE(psl->edit_text_darken_ps, psl->edit_text_overlay_ps, state);
+ }
+ {
+ /* Create view which will render everything (hopefully) behind the text geometry. */
+ DRWView *default_view = (DRWView *)DRW_view_default_get();
+ pd->view_edit_text = DRW_view_create_with_zoffset(default_view, draw_ctx->rv3d, -5.0f);
}
}
@@ -193,16 +202,24 @@ void OVERLAY_edit_text_cache_populate(OVERLAY_Data *vedata, Object *ob)
void OVERLAY_edit_text_draw(OVERLAY_Data *vedata)
{
+ OVERLAY_PrivateData *pd = vedata->stl->pd;
OVERLAY_PassList *psl = vedata->psl;
- DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
+ OVERLAY_FramebufferList *fbl = vedata->fbl;
if (DRW_state_is_fbo()) {
- /* Text overlay need final color for color inversion. */
- GPU_framebuffer_bind(dfbl->default_fb);
+ GPU_framebuffer_bind(fbl->overlay_default_fb);
}
DRW_draw_pass(psl->edit_text_wire_ps[0]);
DRW_draw_pass(psl->edit_text_wire_ps[1]);
+ DRW_view_set_active(pd->view_edit_text);
+
+ /* Alpha blended. */
+ copy_v4_fl4(pd->edit_text.overlay_color, 0.8f, 0.8f, 0.8f, 0.5f);
DRW_draw_pass(psl->edit_text_overlay_ps);
+
+ /* Multiply previous result where depth test fail. */
+ copy_v4_fl4(pd->edit_text.overlay_color, 0.0f, 0.0f, 0.0f, 1.0f);
+ DRW_draw_pass(psl->edit_text_darken_ps);
}
diff --git a/source/blender/draw/engines/overlay/overlay_engine.c b/source/blender/draw/engines/overlay/overlay_engine.c
index 61337ac8d1d..e76b3c82c1d 100644
--- a/source/blender/draw/engines/overlay/overlay_engine.c
+++ b/source/blender/draw/engines/overlay/overlay_engine.c
@@ -423,7 +423,7 @@ static void OVERLAY_cache_finish(void *vedata)
{
/* TODO(fclem) Only do this when really needed. */
{
- /* HACK we allocate the infront depth here to avoid the overhead when if is not needed. */
+ /* HACK we allocate the in front depth here to avoid the overhead when if is not needed. */
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
diff --git a/source/blender/draw/engines/overlay/overlay_extra.c b/source/blender/draw/engines/overlay/overlay_extra.c
index 15bf26a5fa8..e591f54c750 100644
--- a/source/blender/draw/engines/overlay/overlay_extra.c
+++ b/source/blender/draw/engines/overlay/overlay_extra.c
@@ -1318,7 +1318,7 @@ static void OVERLAY_relationship_lines(OVERLAY_ExtraCallBuffers *cb,
else {
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(curcon);
- if ((cti && cti->get_constraint_targets) && (curcon->flag & CONSTRAINT_EXPAND)) {
+ if ((cti && cti->get_constraint_targets) && (curcon->ui_expand_flag && (1 << 0))) {
ListBase targets = {NULL, NULL};
bConstraintTarget *ct;
diff --git a/source/blender/draw/engines/overlay/overlay_grid.c b/source/blender/draw/engines/overlay/overlay_grid.c
index 5ed32de6d93..e3079870d8f 100644
--- a/source/blender/draw/engines/overlay/overlay_grid.c
+++ b/source/blender/draw/engines/overlay/overlay_grid.c
@@ -41,6 +41,7 @@ enum {
CLIP_ZPOS = (1 << 7),
CLIP_ZNEG = (1 << 8),
GRID_BACK = (1 << 9),
+ GRID_CAMERA = (1 << 10),
};
void OVERLAY_grid_init(OVERLAY_Data *vedata)
@@ -145,6 +146,9 @@ void OVERLAY_grid_init(OVERLAY_Data *vedata)
if (rv3d->persp == RV3D_CAMOB && v3d->camera && v3d->camera->type == OB_CAMERA) {
Object *camera_object = DEG_get_evaluated_object(draw_ctx->depsgraph, v3d->camera);
dist = ((Camera *)(camera_object->data))->clip_end;
+ shd->grid_flag |= GRID_CAMERA;
+ shd->zneg_flag |= GRID_CAMERA;
+ shd->zpos_flag |= GRID_CAMERA;
}
else {
dist = v3d->clip_end;
diff --git a/source/blender/draw/engines/overlay/overlay_private.h b/source/blender/draw/engines/overlay/overlay_private.h
index ed0a9cf6981..59fa58c0c03 100644
--- a/source/blender/draw/engines/overlay/overlay_private.h
+++ b/source/blender/draw/engines/overlay/overlay_private.h
@@ -71,6 +71,7 @@ typedef struct OVERLAY_PassList {
DRWPass *edit_mesh_normals_ps;
DRWPass *edit_particle_ps;
DRWPass *edit_text_overlay_ps;
+ DRWPass *edit_text_darken_ps;
DRWPass *edit_text_wire_ps[2];
DRWPass *extra_ps[2];
DRWPass *extra_blend_ps;
@@ -264,6 +265,7 @@ typedef struct OVERLAY_PrivateData {
DRWView *view_edit_faces_cage;
DRWView *view_edit_edges;
DRWView *view_edit_verts;
+ DRWView *view_edit_text;
DRWView *view_reference_images;
/** TODO get rid of this. */
@@ -300,6 +302,9 @@ typedef struct OVERLAY_PrivateData {
int handle_display;
} edit_curve;
struct {
+ float overlay_color[4];
+ } edit_text;
+ struct {
int ghost_ob;
int edit_ob;
bool do_zbufclip;
@@ -563,7 +568,8 @@ void OVERLAY_wireframe_draw(OVERLAY_Data *vedata);
void OVERLAY_wireframe_in_front_draw(OVERLAY_Data *vedata);
GPUShader *OVERLAY_shader_antialiasing(void);
-GPUShader *OVERLAY_shader_armature_degrees_of_freedom(void);
+GPUShader *OVERLAY_shader_armature_degrees_of_freedom_wire(void);
+GPUShader *OVERLAY_shader_armature_degrees_of_freedom_solid(void);
GPUShader *OVERLAY_shader_armature_envelope(bool use_outline);
GPUShader *OVERLAY_shader_armature_shape(bool use_outline);
GPUShader *OVERLAY_shader_armature_shape_wire(void);
diff --git a/source/blender/draw/engines/overlay/overlay_shader.c b/source/blender/draw/engines/overlay/overlay_shader.c
index 0610b8397a1..edf91c99531 100644
--- a/source/blender/draw/engines/overlay/overlay_shader.c
+++ b/source/blender/draw/engines/overlay/overlay_shader.c
@@ -31,6 +31,7 @@
extern char datatoc_antialiasing_frag_glsl[];
extern char datatoc_antialiasing_vert_glsl[];
extern char datatoc_armature_dof_vert_glsl[];
+extern char datatoc_armature_dof_solid_frag_glsl[];
extern char datatoc_armature_envelope_distance_frag_glsl[];
extern char datatoc_armature_envelope_outline_vert_glsl[];
extern char datatoc_armature_envelope_solid_frag_glsl[];
@@ -130,7 +131,8 @@ extern char datatoc_common_view_lib_glsl[];
typedef struct OVERLAY_Shaders {
GPUShader *antialiasing;
- GPUShader *armature_dof;
+ GPUShader *armature_dof_wire;
+ GPUShader *armature_dof_solid;
GPUShader *armature_envelope_outline;
GPUShader *armature_envelope_solid;
GPUShader *armature_shape_outline;
@@ -473,13 +475,13 @@ GPUShader *OVERLAY_shader_armature_stick(void)
return sh_data->armature_stick;
}
-GPUShader *OVERLAY_shader_armature_degrees_of_freedom(void)
+GPUShader *OVERLAY_shader_armature_degrees_of_freedom_wire(void)
{
const DRWContextState *draw_ctx = DRW_context_state_get();
const GPUShaderConfigData *sh_cfg = &GPU_shader_cfg_data[draw_ctx->sh_cfg];
OVERLAY_Shaders *sh_data = &e_data.sh_data[draw_ctx->sh_cfg];
- if (!sh_data->armature_dof) {
- sh_data->armature_dof = GPU_shader_create_from_arrays({
+ if (!sh_data->armature_dof_wire) {
+ sh_data->armature_dof_wire = GPU_shader_create_from_arrays({
.vert = (const char *[]){sh_cfg->lib,
datatoc_common_globals_lib_glsl,
datatoc_common_view_lib_glsl,
@@ -487,10 +489,31 @@ GPUShader *OVERLAY_shader_armature_degrees_of_freedom(void)
NULL},
.frag =
(const char *[]){datatoc_common_view_lib_glsl, datatoc_armature_wire_frag_glsl, NULL},
+ .defs = (const char *[]){sh_cfg->def, "#define EDGE\n", NULL},
+ });
+ }
+ return sh_data->armature_dof_wire;
+}
+
+GPUShader *OVERLAY_shader_armature_degrees_of_freedom_solid(void)
+{
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ const GPUShaderConfigData *sh_cfg = &GPU_shader_cfg_data[draw_ctx->sh_cfg];
+ OVERLAY_Shaders *sh_data = &e_data.sh_data[draw_ctx->sh_cfg];
+ if (!sh_data->armature_dof_solid) {
+ sh_data->armature_dof_solid = GPU_shader_create_from_arrays({
+ .vert = (const char *[]){sh_cfg->lib,
+ datatoc_common_globals_lib_glsl,
+ datatoc_common_view_lib_glsl,
+ datatoc_armature_dof_vert_glsl,
+ NULL},
+ .frag = (const char *[]){datatoc_common_view_lib_glsl,
+ datatoc_armature_dof_solid_frag_glsl,
+ NULL},
.defs = (const char *[]){sh_cfg->def, NULL},
});
}
- return sh_data->armature_dof;
+ return sh_data->armature_dof_solid;
}
GPUShader *OVERLAY_shader_armature_wire(void)
diff --git a/source/blender/draw/engines/overlay/overlay_wireframe.c b/source/blender/draw/engines/overlay/overlay_wireframe.c
index eebfc88fdce..ea45ad5190c 100644
--- a/source/blender/draw/engines/overlay/overlay_wireframe.c
+++ b/source/blender/draw/engines/overlay/overlay_wireframe.c
@@ -76,7 +76,8 @@ void OVERLAY_wireframe_cache_init(OVERLAY_Data *vedata)
DRWState state = DRW_STATE_FIRST_VERTEX_CONVENTION | DRW_STATE_WRITE_COLOR |
DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL;
DRWPass *pass;
- GPUTexture **depth_tx = ((pd->xray_enabled || pd->xray_opacity > 0.0f) && DRW_state_is_fbo()) ?
+ GPUTexture **depth_tx = ((!pd->xray_enabled || pd->xray_opacity > 0.0f) &&
+ DRW_state_is_fbo()) ?
&txl->temp_depth_tx :
&txl->dummy_depth_tx;
diff --git a/source/blender/draw/engines/overlay/shaders/armature_dof_solid_frag.glsl b/source/blender/draw/engines/overlay/shaders/armature_dof_solid_frag.glsl
new file mode 100644
index 00000000000..e511aab69c1
--- /dev/null
+++ b/source/blender/draw/engines/overlay/shaders/armature_dof_solid_frag.glsl
@@ -0,0 +1,11 @@
+
+flat in vec4 finalColor;
+
+layout(location = 0) out vec4 fragColor;
+layout(location = 1) out vec4 lineOutput;
+
+void main()
+{
+ fragColor = finalColor;
+ lineOutput = vec4(0.0);
+}
diff --git a/source/blender/draw/engines/overlay/shaders/armature_dof_vert.glsl b/source/blender/draw/engines/overlay/shaders/armature_dof_vert.glsl
index b15554bbb6a..18a80fc1fb4 100644
--- a/source/blender/draw/engines/overlay/shaders/armature_dof_vert.glsl
+++ b/source/blender/draw/engines/overlay/shaders/armature_dof_vert.glsl
@@ -8,8 +8,10 @@ in vec4 color;
in mat4 inst_obmat;
flat out vec4 finalColor;
+#ifdef EDGE
flat out vec2 edgeStart;
noperspective out vec2 edgePos;
+#endif
vec3 sphere_project(float ax, float az)
{
@@ -35,7 +37,9 @@ void main()
gl_Position = point_world_to_ndc(world_pos);
finalColor = color;
+#ifdef EDGE
edgeStart = edgePos = ((gl_Position.xy / gl_Position.w) * 0.5 + 0.5) * sizeViewport.xy;
+#endif
#ifdef USE_WORLD_CLIP_PLANES
world_clip_planes_calc_clip_distance(world_pos);
diff --git a/source/blender/draw/engines/overlay/shaders/edit_mesh_vert.glsl b/source/blender/draw/engines/overlay/shaders/edit_mesh_vert.glsl
index 203f6cb1901..2cefab56722 100644
--- a/source/blender/draw/engines/overlay/shaders/edit_mesh_vert.glsl
+++ b/source/blender/draw/engines/overlay/shaders/edit_mesh_vert.glsl
@@ -83,7 +83,7 @@ void main()
finalColor = EDIT_MESH_facedot_color(norAndFlag.w);
/* Bias Facedot Z position in clipspace. */
- gl_Position.z -= 0.00035;
+ gl_Position.z -= (ProjectionMatrix[3][3] == 0.0) ? 0.00035 : 1e-6;
gl_PointSize = sizeFaceDot;
bool occluded = test_occlusion();
diff --git a/source/blender/draw/engines/overlay/shaders/grid_frag.glsl b/source/blender/draw/engines/overlay/shaders/grid_frag.glsl
index 9743f918ce3..317e9fe0447 100644
--- a/source/blender/draw/engines/overlay/shaders/grid_frag.glsl
+++ b/source/blender/draw/engines/overlay/shaders/grid_frag.glsl
@@ -28,7 +28,8 @@ uniform float gridSteps[STEPS_LEN] = float[](0.001, 0.01, 0.1, 1.0, 10.0, 100.0,
#define PLANE_XY (1 << 4)
#define PLANE_XZ (1 << 5)
#define PLANE_YZ (1 << 6)
-#define GRID_BACK (1 << 9) /* grid is behind objects */
+#define GRID_BACK (1 << 9) /* grid is behind objects */
+#define GRID_CAMERA (1 << 10) /* In camera view */
#define M_1_SQRTPI 0.5641895835477563 /* 1/sqrt(pi) */
@@ -104,7 +105,9 @@ void main()
fade *= 1.0 - smoothstep(0.0, gridDistance, dist - gridDistance);
}
else {
- dist = abs(gl_FragCoord.z * 2.0 - 1.0);
+ dist = gl_FragCoord.z * 2.0 - 1.0;
+ /* Avoid fading in +Z direction in camera view (see T70193). */
+ dist = ((gridFlag & GRID_CAMERA) != 0) ? clamp(dist, 0.0, 1.0) : abs(dist);
fade = 1.0 - smoothstep(0.0, 0.5, dist - 0.5);
dist = 1.0; /* avoid branch after */
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_cavity_lib.glsl b/source/blender/draw/engines/workbench/shaders/workbench_cavity_lib.glsl
index 87d04144cde..d8cb4f86f7b 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_cavity_lib.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_cavity_lib.glsl
@@ -23,7 +23,7 @@ void cavity_compute(vec2 screenco,
float depth = texture(depthBuffer, screenco).x;
- /* Early out if background and infront. */
+ /* Early out if background and in front. */
if (depth == 1.0 || depth == 0.0) {
return;
}
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl
index 0c2b7850f94..2920a504062 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl
@@ -107,7 +107,6 @@ void volume_properties(vec3 ls_pos, out vec3 scattering, out float extinction)
#ifdef USE_COBA
float val = sample_volume_texture(densityTexture, co).r;
vec4 tval = texture(transferTexture, val) * densityScale;
- tval.rgb = pow(tval.rgb, vec3(2.2));
scattering = tval.rgb * 1500.0;
extinction = max(1e-4, tval.a * 50.0);
#else
@@ -127,7 +126,7 @@ void volume_properties(vec3 ls_pos, out vec3 scattering, out float extinction)
# ifdef VOLUME_SMOKE
/* 800 is arbitrary and here to mimic old viewport. TODO make it a parameter */
- scattering += pow(emission.rgb, vec3(2.2)) * emission.a * 800.0;
+ scattering += emission.rgb * emission.a * 800.0;
# endif
#endif
}
diff --git a/source/blender/draw/engines/workbench/workbench_engine.c b/source/blender/draw/engines/workbench/workbench_engine.c
index 828a9127fb1..c8dde4d513b 100644
--- a/source/blender/draw/engines/workbench/workbench_engine.c
+++ b/source/blender/draw/engines/workbench/workbench_engine.c
@@ -174,8 +174,19 @@ static void workbench_cache_common_populate(WORKBENCH_PrivateData *wpd,
color_type, V3D_SHADING_MATERIAL_COLOR, V3D_SHADING_TEXTURE_COLOR);
if (use_single_drawcall) {
- struct GPUBatch *geom = (use_vcol) ? DRW_cache_mesh_surface_vertpaint_get(ob) :
- DRW_cache_object_surface_get(ob);
+ struct GPUBatch *geom;
+ if (use_vcol) {
+ if (ob->mode & OB_MODE_VERTEX_PAINT) {
+ geom = DRW_cache_mesh_surface_vertpaint_get(ob);
+ }
+ else {
+ geom = DRW_cache_mesh_surface_sculptcolors_get(ob);
+ }
+ }
+ else {
+ geom = DRW_cache_object_surface_get(ob);
+ }
+
if (geom) {
DRWShadingGroup *grp = workbench_material_setup(wpd, ob, 0, color_type, r_transp);
DRW_shgroup_call(grp, geom, ob);
@@ -240,15 +251,19 @@ static eV3DShadingColorType workbench_color_type_get(WORKBENCH_PrivateData *wpd,
const bool is_texpaint_mode = is_active && (wpd->ctx_mode == CTX_MODE_PAINT_TEXTURE);
const bool is_vertpaint_mode = is_active && (wpd->ctx_mode == CTX_MODE_PAINT_VERTEX);
- if ((color_type == V3D_SHADING_TEXTURE_COLOR) && (ob->dt < OB_TEXTURE)) {
- color_type = V3D_SHADING_MATERIAL_COLOR;
- }
- /* Disable color mode if data layer is unavailable. */
- if ((color_type == V3D_SHADING_TEXTURE_COLOR) && (me == NULL || me->mloopuv == NULL)) {
- color_type = V3D_SHADING_MATERIAL_COLOR;
+ if (color_type == V3D_SHADING_TEXTURE_COLOR) {
+ if (ob->dt < OB_TEXTURE) {
+ color_type = V3D_SHADING_MATERIAL_COLOR;
+ }
+ else if ((me == NULL) || (me->mloopuv == NULL)) {
+ /* Disable color mode if data layer is unavailable. */
+ color_type = V3D_SHADING_MATERIAL_COLOR;
+ }
}
- if ((color_type == V3D_SHADING_VERTEX_COLOR) && (me == NULL || me->mloopcol == NULL)) {
- color_type = V3D_SHADING_OBJECT_COLOR;
+ else if (color_type == V3D_SHADING_VERTEX_COLOR) {
+ if ((me == NULL) || !CustomData_has_layer(&me->vdata, CD_PROP_COLOR)) {
+ color_type = V3D_SHADING_OBJECT_COLOR;
+ }
}
if (r_sculpt_pbvh) {
@@ -379,7 +394,7 @@ void workbench_cache_finish(void *ved)
/* TODO(fclem) Only do this when really needed. */
{
- /* HACK we allocate the infront depth here to avoid the overhead when if is not needed. */
+ /* HACK we allocate the in front depth here to avoid the overhead when if is not needed. */
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
diff --git a/source/blender/draw/engines/workbench/workbench_materials.c b/source/blender/draw/engines/workbench/workbench_materials.c
index 0b2d508fee5..d6d3ff8610b 100644
--- a/source/blender/draw/engines/workbench/workbench_materials.c
+++ b/source/blender/draw/engines/workbench/workbench_materials.c
@@ -57,7 +57,7 @@ void workbench_material_ubo_data(WORKBENCH_PrivateData *wpd,
case V3D_SHADING_RANDOM_COLOR: {
uint hash = BLI_ghashutil_strhash_p_murmur(ob->id.name);
if (ob->id.lib) {
- hash = (hash * 13) ^ BLI_ghashutil_strhash_p_murmur(ob->id.lib->name);
+ hash = (hash * 13) ^ BLI_ghashutil_strhash_p_murmur(ob->id.lib->filepath);
}
float hue = BLI_hash_int_01(hash);
float hsv[3] = {hue, HSV_SATURATION, HSV_VALUE};
diff --git a/source/blender/draw/engines/workbench/workbench_private.h b/source/blender/draw/engines/workbench/workbench_private.h
index 20b6d368ac0..ee9960ea0ef 100644
--- a/source/blender/draw/engines/workbench/workbench_private.h
+++ b/source/blender/draw/engines/workbench/workbench_private.h
@@ -293,7 +293,7 @@ typedef struct WORKBENCH_PrivateData {
/** Object IDs buffer for curvature & outline. */
struct GPUTexture *object_id_tx;
- /** Prepass infos for each draw types [transparent][infront][hair]. */
+ /** Pre-pass information for each draw types [transparent][infront][hair]. */
WORKBENCH_Prepass prepass[2][2][2];
/* Materials */
diff --git a/source/blender/draw/engines/workbench/workbench_transparent.c b/source/blender/draw/engines/workbench/workbench_transparent.c
index 5fd8229304a..1c40a350300 100644
--- a/source/blender/draw/engines/workbench/workbench_transparent.c
+++ b/source/blender/draw/engines/workbench/workbench_transparent.c
@@ -157,7 +157,7 @@ void workbench_transparent_draw_depth_pass(WORKBENCH_Data *data)
WORKBENCH_FramebufferList *fbl = data->fbl;
WORKBENCH_PassList *psl = data->psl;
- const bool do_xray_depth_pass = XRAY_ALPHA(wpd) > 0.0f;
+ const bool do_xray_depth_pass = !XRAY_FLAG_ENABLED(wpd) || XRAY_ALPHA(wpd) > 0.0f;
const bool do_transparent_depth_pass = psl->outline_ps || wpd->dof_enabled || do_xray_depth_pass;
if (do_transparent_depth_pass) {
@@ -165,14 +165,14 @@ void workbench_transparent_draw_depth_pass(WORKBENCH_Data *data)
if (!DRW_pass_is_empty(psl->transp_accum_ps)) {
GPU_framebuffer_bind(fbl->opaque_fb);
- /* TODO(fclem) Disable writting to first two buffers. Unecessary waste of bandwidth. */
+ /* TODO(fclem) Disable writing to first two buffers. Unnecessary waste of bandwidth. */
DRW_pass_state_set(psl->transp_accum_ps, state | wpd->cull_state | wpd->clip_state);
DRW_draw_pass(psl->transp_accum_ps);
}
if (!DRW_pass_is_empty(psl->transp_accum_infront_ps)) {
GPU_framebuffer_bind(fbl->opaque_infront_fb);
- /* TODO(fclem) Disable writting to first two buffers. Unecessary waste of bandwidth. */
+ /* TODO(fclem) Disable writing to first two buffers. Unnecessary waste of bandwidth. */
DRW_pass_state_set(psl->transp_accum_infront_ps, state | wpd->cull_state | wpd->clip_state);
DRW_draw_pass(psl->transp_accum_infront_ps);
}
diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h
index 89dd6fa210c..555043ab408 100644
--- a/source/blender/draw/intern/DRW_render.h
+++ b/source/blender/draw/intern/DRW_render.h
@@ -462,6 +462,10 @@ void DRW_shgroup_uniform_texture_ex(DRWShadingGroup *shgroup,
const char *name,
const struct GPUTexture *tex,
eGPUSamplerState sampler_state);
+void DRW_shgroup_uniform_texture_ref_ex(DRWShadingGroup *shgroup,
+ const char *name,
+ GPUTexture **tex,
+ eGPUSamplerState sampler_state);
void DRW_shgroup_uniform_texture(DRWShadingGroup *shgroup,
const char *name,
const struct GPUTexture *tex);
@@ -569,7 +573,7 @@ void DRW_view_update_sub(DRWView *view, const float viewmat[4][4], const float w
const DRWView *DRW_view_default_get(void);
void DRW_view_default_set(DRWView *view);
-
+void DRW_view_reset(void);
void DRW_view_set_active(DRWView *view);
void DRW_view_clip_planes_set(DRWView *view, float (*planes)[4], int plane_len);
@@ -626,6 +630,8 @@ void DRW_custom_pipeline(DrawEngineType *draw_engine_type,
void (*callback)(void *vedata, void *user_data),
void *user_data);
+void DRW_cache_restart(void);
+
/* ViewLayers */
void *DRW_view_layer_engine_data_get(DrawEngineType *engine_type);
void **DRW_view_layer_engine_data_ensure_ex(struct ViewLayer *view_layer,
diff --git a/source/blender/draw/intern/draw_cache.c b/source/blender/draw/intern/draw_cache.c
index af5b9cd05dd..9f30cd85957 100644
--- a/source/blender/draw/intern/draw_cache.c
+++ b/source/blender/draw/intern/draw_cache.c
@@ -888,6 +888,32 @@ GPUBatch *DRW_cache_object_surface_get(Object *ob)
}
}
+/* Returns the vertbuf used by shaded surface batch. */
+GPUVertBuf *DRW_cache_object_pos_vertbuf_get(Object *ob)
+{
+ Mesh *me = BKE_object_get_evaluated_mesh(ob);
+ short type = (me != NULL) ? OB_MESH : ob->type;
+
+ switch (type) {
+ case OB_MESH:
+ return DRW_mesh_batch_cache_pos_vertbuf_get((me != NULL) ? me : ob->data);
+ case OB_CURVE:
+ case OB_SURF:
+ case OB_FONT:
+ return DRW_curve_batch_cache_pos_vertbuf_get(ob->data);
+ case OB_MBALL:
+ return DRW_mball_batch_cache_pos_vertbuf_get(ob);
+ case OB_HAIR:
+ return NULL;
+ case OB_POINTCLOUD:
+ return NULL;
+ case OB_VOLUME:
+ return NULL;
+ default:
+ return NULL;
+ }
+}
+
int DRW_cache_object_material_count_get(struct Object *ob)
{
Mesh *me = BKE_object_get_evaluated_mesh(ob);
@@ -2844,6 +2870,12 @@ GPUBatch *DRW_cache_mesh_surface_vertpaint_get(Object *ob)
return DRW_mesh_batch_cache_get_surface_vertpaint(ob->data);
}
+GPUBatch *DRW_cache_mesh_surface_sculptcolors_get(Object *ob)
+{
+ BLI_assert(ob->type == OB_MESH);
+ return DRW_mesh_batch_cache_get_surface_sculpt(ob->data);
+}
+
GPUBatch *DRW_cache_mesh_surface_weights_get(Object *ob)
{
BLI_assert(ob->type == OB_MESH);
diff --git a/source/blender/draw/intern/draw_cache.h b/source/blender/draw/intern/draw_cache.h
index 221fb89612f..2f289bf4110 100644
--- a/source/blender/draw/intern/draw_cache.h
+++ b/source/blender/draw/intern/draw_cache.h
@@ -63,6 +63,8 @@ struct GPUBatch **DRW_cache_object_surface_material_get(struct Object *ob,
struct GPUBatch *DRW_cache_object_face_wireframe_get(struct Object *ob);
int DRW_cache_object_material_count_get(struct Object *ob);
+struct GPUVertBuf *DRW_cache_object_pos_vertbuf_get(struct Object *ob);
+
/* Empties */
struct GPUBatch *DRW_cache_plain_axes_get(void);
struct GPUBatch *DRW_cache_single_arrow_get(void);
@@ -134,6 +136,7 @@ struct GPUBatch **DRW_cache_mesh_surface_shaded_get(struct Object *ob,
struct GPUBatch **DRW_cache_mesh_surface_texpaint_get(struct Object *ob);
struct GPUBatch *DRW_cache_mesh_surface_texpaint_single_get(struct Object *ob);
struct GPUBatch *DRW_cache_mesh_surface_vertpaint_get(struct Object *ob);
+struct GPUBatch *DRW_cache_mesh_surface_sculptcolors_get(struct Object *ob);
struct GPUBatch *DRW_cache_mesh_surface_weights_get(struct Object *ob);
struct GPUBatch *DRW_cache_mesh_surface_mesh_analysis_get(struct Object *ob);
struct GPUBatch *DRW_cache_mesh_face_wireframe_get(struct Object *ob);
diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h
index f203c2ff1ea..302f9a0d3a8 100644
--- a/source/blender/draw/intern/draw_cache_extract.h
+++ b/source/blender/draw/intern/draw_cache_extract.h
@@ -53,6 +53,7 @@ typedef struct DRW_MeshCDMask {
uint32_t uv : 8;
uint32_t tan : 8;
uint32_t vcol : 8;
+ uint32_t sculpt_vcol : 8;
uint32_t orco : 1;
uint32_t tan_orco : 1;
/** Edit uv layer is from the base edit mesh as
@@ -62,7 +63,7 @@ typedef struct DRW_MeshCDMask {
typedef enum eMRIterType {
MR_ITER_LOOPTRI = 1 << 0,
- MR_ITER_LOOP = 1 << 1,
+ MR_ITER_POLY = 1 << 1,
MR_ITER_LEDGE = 1 << 2,
MR_ITER_LVERT = 1 << 3,
} eMRIterType;
diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.c b/source/blender/draw/intern/draw_cache_extract_mesh.c
index f3dc8f0fd2a..c92722fad7e 100644
--- a/source/blender/draw/intern/draw_cache_extract_mesh.c
+++ b/source/blender/draw/intern/draw_cache_extract_mesh.c
@@ -128,7 +128,7 @@ typedef struct MeshRenderData {
BMEdge *eed_act;
BMFace *efa_act;
BMFace *efa_act_uv;
- /* Data created on-demand (usually not for bmesh-based data). */
+ /* Data created on-demand (usually not for #BMesh based data). */
MLoopTri *mlooptri;
float (*loop_normals)[3];
float (*poly_normals)[3];
@@ -145,17 +145,17 @@ static void mesh_render_data_update_loose_geom(MeshRenderData *mr,
mr->vert_loose_len = 0;
mr->edge_loose_len = 0;
- BLI_bitmap *lvert_map = BLI_BITMAP_NEW(mr->vert_len, "lvert map");
+ BLI_bitmap *lvert_map = BLI_BITMAP_NEW(mr->vert_len, __func__);
mr->ledges = MEM_mallocN(mr->edge_len * sizeof(int), __func__);
- const MEdge *medge = mr->medge;
- for (int e = 0; e < mr->edge_len; e++, medge++) {
- if (medge->flag & ME_LOOSEEDGE) {
- mr->ledges[mr->edge_loose_len++] = e;
+ const MEdge *med = mr->medge;
+ for (int med_index = 0; med_index < mr->edge_len; med_index++, med++) {
+ if (med->flag & ME_LOOSEEDGE) {
+ mr->ledges[mr->edge_loose_len++] = med_index;
}
/* Tag verts as not loose. */
- BLI_BITMAP_ENABLE(lvert_map, medge->v1);
- BLI_BITMAP_ENABLE(lvert_map, medge->v2);
+ BLI_BITMAP_ENABLE(lvert_map, med->v1);
+ BLI_BITMAP_ENABLE(lvert_map, med->v2);
}
if (mr->edge_loose_len < mr->edge_len) {
mr->ledges = MEM_reallocN(mr->ledges, mr->edge_loose_len * sizeof(*mr->ledges));
@@ -173,11 +173,11 @@ static void mesh_render_data_update_loose_geom(MeshRenderData *mr,
MEM_freeN(lvert_map);
- mr->loop_loose_len = mr->vert_loose_len + mr->edge_loose_len * 2;
+ mr->loop_loose_len = mr->vert_loose_len + (mr->edge_loose_len * 2);
}
}
else {
- /* BMesh */
+ /* #BMesh */
BMesh *bm = mr->bm;
if (iter_type & (MR_ITER_LEDGE | MR_ITER_LVERT)) {
int elem_id;
@@ -212,7 +212,9 @@ static void mesh_render_data_update_loose_geom(MeshRenderData *mr,
}
}
-/* Part of the creation of the MeshRenderData that happens in a thread. */
+/**
+ * Part of the creation of the #MeshRenderData that happens in a thread.
+ */
static void mesh_render_data_update_looptris(MeshRenderData *mr,
const eMRIterType iter_type,
const eMRDataType data_flag)
@@ -227,7 +229,7 @@ static void mesh_render_data_update_looptris(MeshRenderData *mr,
}
}
else {
- /* BMesh */
+ /* #BMesh */
if ((iter_type & MR_ITER_LOOPTRI) || (data_flag & MR_DATA_LOOPTRI)) {
/* Edit mode ensures this is valid, no need to calculate. */
BLI_assert((mr->bm->totloop == 0) || (mr->edit_bmesh->looptris != NULL));
@@ -278,9 +280,9 @@ static void mesh_render_data_update_normals(MeshRenderData *mr,
}
}
else {
- /* BMesh */
+ /* #BMesh */
if (data_flag & MR_DATA_POLY_NOR) {
- /* Use bmface->no instead. */
+ /* Use #BMFace.no instead. */
}
if (((data_flag & MR_DATA_LOOP_NOR) && is_auto_smooth) || (data_flag & MR_DATA_TAN_LOOP_NOR)) {
@@ -295,7 +297,7 @@ static void mesh_render_data_update_normals(MeshRenderData *mr,
}
mr->loop_normals = MEM_mallocN(sizeof(*mr->loop_normals) * mr->loop_len, __func__);
- int clnors_offset = CustomData_get_offset(&mr->bm->ldata, CD_CUSTOMLOOPNORMAL);
+ const int clnors_offset = CustomData_get_offset(&mr->bm->ldata, CD_CUSTOMLOOPNORMAL);
BM_loops_calc_normal_vcos(mr->bm,
vert_coords,
vert_normals,
@@ -419,7 +421,7 @@ static MeshRenderData *mesh_render_data_create(Mesh *me,
mr->p_origindex = CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX);
}
else {
- /* BMesh */
+ /* #BMesh */
BMesh *bm = mr->bm;
mr->vert_len = bm->totvert;
@@ -486,7 +488,7 @@ BLI_INLINE const float *bm_vert_no_get(const MeshRenderData *mr, const BMVert *e
}
else {
UNUSED_VARS(mr);
- return eve->co;
+ return eve->no;
}
}
@@ -505,48 +507,283 @@ BLI_INLINE const float *bm_face_no_get(const MeshRenderData *mr, const BMFace *e
/** \} */
/* ---------------------------------------------------------------------- */
-/** \name Mesh Elements Extract Iter
+/** \name Mesh Elements Extract: Loop Triangles
+ * \{ */
+
+typedef struct ExtractTriBMesh_Params {
+ BMLoop *(*looptris)[3];
+ int tri_range[2];
+} ExtractTriBMesh_Params;
+typedef void(ExtractTriBMeshFn)(const MeshRenderData *mr,
+ const ExtractTriBMesh_Params *params,
+ void *data);
+
+#define EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elem_tri, index_tri, params) \
+ CHECK_TYPE(params, const ExtractTriBMesh_Params *); \
+ { \
+ const int _tri_index_end = (params)->tri_range[1]; \
+ BMLoop **elem_tri = (params)->looptris[(params)->tri_range[0]]; \
+ for (int index_tri = (params)->tri_range[0]; index_tri < _tri_index_end; \
+ index_tri += 1, elem_tri += 3)
+#define EXTRACT_TRIS_LOOPTRI_FOREACH_BM_END }
+
+typedef struct ExtractTriMesh_Params {
+ const MLoopTri *mlooptri;
+ int tri_range[2];
+} ExtractTriMesh_Params;
+typedef void(ExtractTriMeshFn)(const MeshRenderData *mr,
+ const ExtractTriMesh_Params *params,
+ void *data);
+
+#define EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(elem_tri, index_tri, params) \
+ CHECK_TYPE(params, const ExtractTriMesh_Params *); \
+ { \
+ const int _tri_index_end = (params)->tri_range[1]; \
+ const MLoopTri *elem_tri = &(params)->mlooptri[(params)->tri_range[0]]; \
+ for (int index_tri = (params)->tri_range[0]; index_tri < _tri_index_end; \
+ index_tri += 1, elem_tri += 1)
+#define EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END }
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Mesh Elements Extract: Polygons, Loops
+ * \{ */
+
+typedef struct ExtractPolyBMesh_Params {
+ BMLoop *(*looptris)[3];
+ int poly_range[2];
+} ExtractPolyBMesh_Params;
+typedef void(ExtractPolyBMeshFn)(const MeshRenderData *mr,
+ const ExtractPolyBMesh_Params *params,
+ void *data);
+
+#define EXTRACT_POLY_FOREACH_BM_BEGIN(elem_poly, index_poly, params, mr) \
+ CHECK_TYPE(params, const ExtractPolyBMesh_Params *); \
+ { \
+ BLI_assert((mr->bm->elem_table_dirty & BM_FACE) == 0); \
+ BMFace **_ftable = mr->bm->ftable; \
+ const int _poly_index_end = (params)->poly_range[1]; \
+ for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \
+ index_poly += 1) { \
+ BMFace *elem_poly = _ftable[index_poly]; \
+ (void)elem_poly;
+
+#define EXTRACT_POLY_FOREACH_BM_END \
+ } \
+ }
+
+/* Iterate over polygon and loop. */
+#define EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(elem_loop, index_loop, params, mr) \
+ CHECK_TYPE(params, const ExtractPolyBMesh_Params *); \
+ { \
+ BLI_assert((mr->bm->elem_table_dirty & BM_FACE) == 0); \
+ BMFace **_ftable = mr->bm->ftable; \
+ const int _poly_index_end = (params)->poly_range[1]; \
+ for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \
+ index_poly += 1) { \
+ BMFace *elem_face = _ftable[index_poly]; \
+ BMLoop *elem_loop, *l_first; \
+ elem_loop = l_first = BM_FACE_FIRST_LOOP(elem_face); \
+ do { \
+ const int index_loop = BM_elem_index_get(elem_loop); \
+ (void)index_loop; /* Quiet warning when unused. */
+
+#define EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(elem_loop) \
+ } \
+ while ((elem_loop = elem_loop->next) != l_first) \
+ ; \
+ } \
+ }
+
+typedef struct ExtractPolyMesh_Params {
+ int poly_range[2];
+} ExtractPolyMesh_Params;
+typedef void(ExtractPolyMeshFn)(const MeshRenderData *mr,
+ const ExtractPolyMesh_Params *params,
+ void *data);
+
+#define EXTRACT_POLY_FOREACH_MESH_BEGIN(elem_poly, index_poly, params, mr) \
+ CHECK_TYPE(params, const ExtractPolyMesh_Params *); \
+ { \
+ const MPoly *_mpoly = mr->mpoly; \
+ const int _poly_index_end = (params)->poly_range[1]; \
+ for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \
+ index_poly += 1) { \
+ const MPoly *elem_poly = &_mpoly[index_poly]; \
+ (void)elem_poly;
+
+#define EXTRACT_POLY_FOREACH_MESH_END \
+ } \
+ }
+
+/* Iterate over polygon and loop. */
+#define EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN( \
+ elem_poly, index_poly, elem_loop, index_loop, params, mr) \
+ CHECK_TYPE(params, const ExtractPolyMesh_Params *); \
+ { \
+ const MPoly *_mpoly = mr->mpoly; \
+ const MLoop *_mloop = mr->mloop; \
+ const int _poly_index_end = (params)->poly_range[1]; \
+ for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \
+ index_poly += 1) { \
+ const MPoly *elem_poly = &_mpoly[index_poly]; \
+ const int _index_end = elem_poly->loopstart + elem_poly->totloop; \
+ for (int index_loop = elem_poly->loopstart; index_loop < _index_end; index_loop += 1) { \
+ const MLoop *elem_loop = &_mloop[index_loop]; \
+ (void)elem_loop;
+
+#define EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END \
+ } \
+ } \
+ }
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Mesh Elements Extract: Loose Edges
+ * \{ */
+
+typedef struct ExtractLEdgeBMesh_Params {
+ const int *ledge;
+ int ledge_range[2];
+} ExtractLEdgeBMesh_Params;
+typedef void(ExtractLEdgeBMeshFn)(const MeshRenderData *mr,
+ const ExtractLEdgeBMesh_Params *params,
+ void *data);
+
+#define EXTRACT_LEDGE_FOREACH_BM_BEGIN(elem_edge, index_ledge, params) \
+ CHECK_TYPE(params, const ExtractLEdgeBMesh_Params *); \
+ { \
+ BLI_assert((mr->bm->elem_table_dirty & BM_EDGE) == 0); \
+ BMEdge **_etable = mr->bm->etable; \
+ const int *_ledge = (params)->ledge; \
+ const int _ledge_index_end = (params)->ledge_range[1]; \
+ for (int index_ledge = (params)->ledge_range[0]; index_ledge < _ledge_index_end; \
+ index_ledge += 1) { \
+ BMEdge *elem_edge = _etable[_ledge[index_ledge]]; \
+ (void)elem_edge; /* Quiet warning when unused. */ \
+ {
+#define EXTRACT_LEDGE_FOREACH_BM_END \
+ } \
+ } \
+ }
+
+typedef struct ExtractLEdgeMesh_Params {
+ const int *ledge;
+ int ledge_range[2];
+} ExtractLEdgeMesh_Params;
+typedef void(ExtractLEdgeMeshFn)(const MeshRenderData *mr,
+ const ExtractLEdgeMesh_Params *params,
+ void *data);
+
+#define EXTRACT_LEDGE_FOREACH_MESH_BEGIN(elem_edge, index_ledge, params, mr) \
+ CHECK_TYPE(params, const ExtractLEdgeMesh_Params *); \
+ { \
+ const MEdge *_medge = mr->medge; \
+ const int *_ledge = (params)->ledge; \
+ const int _ledge_index_end = (params)->ledge_range[1]; \
+ for (int index_ledge = (params)->ledge_range[0]; index_ledge < _ledge_index_end; \
+ index_ledge += 1) { \
+ const MEdge *elem_edge = &_medge[_ledge[index_ledge]]; \
+ (void)elem_edge; /* Quiet warning when unused. */ \
+ {
+#define EXTRACT_LEDGE_FOREACH_MESH_END \
+ } \
+ } \
+ }
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Mesh Elements Extract: Loose Vertices
+ * \{ */
+
+typedef struct ExtractLVertBMesh_Params {
+ const int *lvert;
+ int lvert_range[2];
+} ExtractLVertBMesh_Params;
+typedef void(ExtractLVertBMeshFn)(const MeshRenderData *mr,
+ const ExtractLVertBMesh_Params *params,
+ void *data);
+
+#define EXTRACT_LVERT_FOREACH_BM_BEGIN(elem_vert, index_lvert, params) \
+ CHECK_TYPE(params, const ExtractLVertBMesh_Params *); \
+ { \
+ BLI_assert((mr->bm->elem_table_dirty & BM_FACE) == 0); \
+ BMVert **vtable = mr->bm->vtable; \
+ const int *lverts = (params)->lvert; \
+ const int _lvert_index_end = (params)->lvert_range[1]; \
+ for (int index_lvert = (params)->lvert_range[0]; index_lvert < _lvert_index_end; \
+ index_lvert += 1) { \
+ BMVert *elem_vert = vtable[lverts[index_lvert]]; \
+ (void)elem_vert; /* Quiet warning when unused. */ \
+ {
+#define EXTRACT_LVERT_FOREACH_BM_END \
+ } \
+ } \
+ }
+
+typedef struct ExtractLVertMesh_Params {
+ const int *lvert;
+ int lvert_range[2];
+} ExtractLVertMesh_Params;
+typedef void(ExtractLVertMeshFn)(const MeshRenderData *mr,
+ const ExtractLVertMesh_Params *params,
+ void *data);
+
+#define EXTRACT_LVERT_FOREACH_MESH_BEGIN(elem, index_lvert, params, mr) \
+ CHECK_TYPE(params, const ExtractLVertMesh_Params *); \
+ { \
+ const MVert *mvert = mr->mvert; \
+ const int *lverts = (params)->lvert; \
+ const int _lvert_index_end = (params)->lvert_range[1]; \
+ for (int index_lvert = (params)->lvert_range[0]; index_lvert < _lvert_index_end; \
+ index_lvert += 1) { \
+ const MVert *elem = &mvert[lverts[index_lvert]]; \
+ (void)elem; /* Quiet warning when unused. */ \
+ {
+#define EXTRACT_LVERT_FOREACH_MESH_END \
+ } \
+ } \
+ }
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Mesh Elements Extract Struct
* \{ */
typedef void *(ExtractInitFn)(const MeshRenderData *mr, void *buffer);
-typedef void(ExtractEditTriFn)(const MeshRenderData *mr, int t, BMLoop **e, void *data);
-typedef void(ExtractEditLoopFn)(const MeshRenderData *mr, int l, BMLoop *el, void *data);
-typedef void(ExtractEditLedgeFn)(const MeshRenderData *mr, int e, BMEdge *ed, void *data);
-typedef void(ExtractEditLvertFn)(const MeshRenderData *mr, int v, BMVert *ev, void *data);
-typedef void(ExtractTriFn)(const MeshRenderData *mr, int t, const MLoopTri *mlt, void *data);
-typedef void(ExtractLoopFn)(
- const MeshRenderData *mr, int l, const MLoop *mloop, int p, const MPoly *mpoly, void *data);
-typedef void(ExtractLedgeFn)(const MeshRenderData *mr, int e, const MEdge *medge, void *data);
-typedef void(ExtractLvertFn)(const MeshRenderData *mr, int v, const MVert *mvert, void *data);
typedef void(ExtractFinishFn)(const MeshRenderData *mr, void *buffer, void *data);
typedef struct MeshExtract {
- /** Executed on main thread and return user data for iter functions. */
+ /** Executed on main thread and return user data for iteration functions. */
ExtractInitFn *init;
/** Executed on one (or more if use_threading) worker thread(s). */
- ExtractEditTriFn *iter_looptri_bm;
- ExtractTriFn *iter_looptri;
- ExtractEditLoopFn *iter_loop_bm;
- ExtractLoopFn *iter_loop;
- ExtractEditLedgeFn *iter_ledge_bm;
- ExtractLedgeFn *iter_ledge;
- ExtractEditLvertFn *iter_lvert_bm;
- ExtractLvertFn *iter_lvert;
+ ExtractTriBMeshFn *iter_looptri_bm;
+ ExtractTriMeshFn *iter_looptri_mesh;
+ ExtractPolyBMeshFn *iter_poly_bm;
+ ExtractPolyMeshFn *iter_poly_mesh;
+ ExtractLEdgeBMeshFn *iter_ledge_bm;
+ ExtractLEdgeMeshFn *iter_ledge_mesh;
+ ExtractLVertBMeshFn *iter_lvert_bm;
+ ExtractLVertMeshFn *iter_lvert_mesh;
/** Executed on one worker thread after all elements iterations. */
ExtractFinishFn *finish;
/** Used to request common data. */
const eMRDataType data_flag;
- /** Used to know if the element callbacks are threadsafe and can be parallelized. */
+ /** Used to know if the element callbacks are thread-safe and can be parallelized. */
const bool use_threading;
} MeshExtract;
BLI_INLINE eMRIterType mesh_extract_iter_type(const MeshExtract *ext)
{
eMRIterType type = 0;
- SET_FLAG_FROM_TEST(type, (ext->iter_looptri_bm || ext->iter_looptri), MR_ITER_LOOPTRI);
- SET_FLAG_FROM_TEST(type, (ext->iter_loop_bm || ext->iter_loop), MR_ITER_LOOP);
- SET_FLAG_FROM_TEST(type, (ext->iter_ledge_bm || ext->iter_ledge), MR_ITER_LEDGE);
- SET_FLAG_FROM_TEST(type, (ext->iter_lvert_bm || ext->iter_lvert), MR_ITER_LVERT);
+ SET_FLAG_FROM_TEST(type, (ext->iter_looptri_bm || ext->iter_looptri_mesh), MR_ITER_LOOPTRI);
+ SET_FLAG_FROM_TEST(type, (ext->iter_poly_bm || ext->iter_poly_mesh), MR_ITER_POLY);
+ SET_FLAG_FROM_TEST(type, (ext->iter_ledge_bm || ext->iter_ledge_mesh), MR_ITER_LEDGE);
+ SET_FLAG_FROM_TEST(type, (ext->iter_lvert_bm || ext->iter_lvert_mesh), MR_ITER_LVERT);
return type;
}
@@ -583,15 +820,15 @@ static void *extract_tris_init(const MeshRenderData *mr, void *UNUSED(ibo))
}
}
else {
- const MPoly *mpoly = mr->mpoly;
- for (int p = 0; p < mr->poly_len; p++, mpoly++) {
- if (!(mr->use_hide && (mpoly->flag & ME_HIDE))) {
- int mat = min_ii(mpoly->mat_nr, mr->mat_len - 1);
- mat_tri_len[mat] += mpoly->totloop - 2;
+ const MPoly *mp = mr->mpoly;
+ for (int mp_index = 0; mp_index < mr->poly_len; mp_index++, mp++) {
+ if (!(mr->use_hide && (mp->flag & ME_HIDE))) {
+ int mat = min_ii(mp->mat_nr, mr->mat_len - 1);
+ mat_tri_len[mat] += mp->totloop - 2;
}
}
}
- /* Accumulate tri len per mat to have correct offsets. */
+ /* Accumulate triangle lengths per material to have correct offsets. */
int ofs = mat_tri_len[0];
mat_tri_len[0] = 0;
for (int i = 1; i < mr->mat_len; i++) {
@@ -608,51 +845,59 @@ static void *extract_tris_init(const MeshRenderData *mr, void *UNUSED(ibo))
return data;
}
-static void extract_tris_looptri_bmesh(const MeshRenderData *mr,
- int UNUSED(t),
- BMLoop **elt,
- void *_data)
+static void extract_tris_iter_looptri_bm(const MeshRenderData *mr,
+ const struct ExtractTriBMesh_Params *params,
+ void *_data)
{
- if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) {
- MeshExtract_Tri_Data *data = _data;
- int *mat_tri_ofs = data->tri_mat_end;
- int mat = min_ii(elt[0]->f->mat_nr, mr->mat_len - 1);
- GPU_indexbuf_set_tri_verts(&data->elb,
- mat_tri_ofs[mat]++,
- BM_elem_index_get(elt[0]),
- BM_elem_index_get(elt[1]),
- BM_elem_index_get(elt[2]));
+ MeshExtract_Tri_Data *data = _data;
+ const int mat_last = mr->mat_len - 1;
+ EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elt, _elt_index, params)
+ {
+ if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) {
+ int *mat_tri_ofs = data->tri_mat_end;
+ const int mat = min_ii(elt[0]->f->mat_nr, mat_last);
+ GPU_indexbuf_set_tri_verts(&data->elb,
+ mat_tri_ofs[mat]++,
+ BM_elem_index_get(elt[0]),
+ BM_elem_index_get(elt[1]),
+ BM_elem_index_get(elt[2]));
+ }
}
+ EXTRACT_TRIS_LOOPTRI_FOREACH_BM_END;
}
-static void extract_tris_looptri_mesh(const MeshRenderData *mr,
- int UNUSED(t),
- const MLoopTri *mlt,
- void *_data)
+static void extract_tris_iter_looptri_mesh(const MeshRenderData *mr,
+ const struct ExtractTriMesh_Params *params,
+ void *_data)
{
- const MPoly *mpoly = &mr->mpoly[mlt->poly];
- if (!(mr->use_hide && (mpoly->flag & ME_HIDE))) {
- MeshExtract_Tri_Data *data = _data;
- int *mat_tri_ofs = data->tri_mat_end;
- int mat = min_ii(mpoly->mat_nr, mr->mat_len - 1);
- GPU_indexbuf_set_tri_verts(
- &data->elb, mat_tri_ofs[mat]++, mlt->tri[0], mlt->tri[1], mlt->tri[2]);
+ MeshExtract_Tri_Data *data = _data;
+ const int mat_last = mr->mat_len - 1;
+ EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(mlt, _mlt_index, params)
+ {
+ const MPoly *mp = &mr->mpoly[mlt->poly];
+ if (!(mr->use_hide && (mp->flag & ME_HIDE))) {
+ int *mat_tri_ofs = data->tri_mat_end;
+ const int mat = min_ii(mp->mat_nr, mat_last);
+ GPU_indexbuf_set_tri_verts(
+ &data->elb, mat_tri_ofs[mat]++, mlt->tri[0], mlt->tri[1], mlt->tri[2]);
+ }
}
+ EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END;
}
static void extract_tris_finish(const MeshRenderData *mr, void *ibo, void *_data)
{
MeshExtract_Tri_Data *data = _data;
GPU_indexbuf_build_in_place(&data->elb, ibo);
- /* HACK Create ibo subranges and assign them to each GPUBatch. */
+ /* HACK: Create ibo sub-ranges and assign them to each #GPUBatch. */
if (mr->use_final_mesh && mr->cache->surface_per_mat && mr->cache->surface_per_mat[0]) {
BLI_assert(mr->cache->surface_per_mat[0]->elem == ibo);
for (int i = 0; i < mr->mat_len; i++) {
/* Multiply by 3 because these are triangle indices. */
- int start = data->tri_mat_start[i] * 3;
- int len = data->tri_mat_end[i] * 3 - data->tri_mat_start[i] * 3;
+ const int start = data->tri_mat_start[i] * 3;
+ const int len = data->tri_mat_end[i] * 3 - data->tri_mat_start[i] * 3;
GPUIndexBuf *sub_ibo = GPU_indexbuf_create_subrange(ibo, start, len);
- /* WARNING: We modify the GPUBatch here! */
+ /* WARNING: We modify the #GPUBatch here! */
GPU_batch_elembuf_set(mr->cache->surface_per_mat[i], sub_ibo, true);
}
}
@@ -662,18 +907,12 @@ static void extract_tris_finish(const MeshRenderData *mr, void *ibo, void *_data
}
static const MeshExtract extract_tris = {
- extract_tris_init,
- extract_tris_looptri_bmesh,
- extract_tris_looptri_mesh,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- extract_tris_finish,
- 0,
- false,
+ .init = extract_tris_init,
+ .iter_looptri_bm = extract_tris_iter_looptri_bm,
+ .iter_looptri_mesh = extract_tris_iter_looptri_mesh,
+ .finish = extract_tris_finish,
+ .data_flag = 0,
+ .use_threading = false,
};
/** \} */
@@ -691,71 +930,113 @@ static void *extract_lines_init(const MeshRenderData *mr, void *UNUSED(buf))
return elb;
}
-static void extract_lines_loop_bmesh(const MeshRenderData *UNUSED(mr),
- int l,
- BMLoop *loop,
- void *elb)
-{
- if (!BM_elem_flag_test(loop->e, BM_ELEM_HIDDEN)) {
- GPU_indexbuf_set_line_verts(elb, BM_elem_index_get(loop->e), l, BM_elem_index_get(loop->next));
- }
- else {
- GPU_indexbuf_set_line_restart(elb, BM_elem_index_get(loop->e));
- }
-}
-
-static void extract_lines_loop_mesh(const MeshRenderData *mr,
- int l,
- const MLoop *mloop,
- int UNUSED(p),
- const MPoly *mpoly,
- void *elb)
-{
- const MEdge *medge = &mr->medge[mloop->e];
- if (!((mr->use_hide && (medge->flag & ME_HIDE)) ||
- ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) &&
- (mr->e_origindex[mloop->e] == ORIGINDEX_NONE)))) {
- int loopend = mpoly->totloop + mpoly->loopstart - 1;
- int other_loop = (l == loopend) ? mpoly->loopstart : (l + 1);
- GPU_indexbuf_set_line_verts(elb, mloop->e, l, other_loop);
+static void extract_lines_iter_poly_bm(const MeshRenderData *mr,
+ const ExtractPolyBMesh_Params *params,
+ void *elb)
+{
+ /* Using poly & loop iterator would complicate accessing the adjacent loop. */
+ EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr)
+ {
+ BMLoop *l_iter, *l_first;
+ /* Use #BMLoop.prev to match mesh order (to avoid minor differences in data extraction). */
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f)->prev;
+ do {
+ if (!BM_elem_flag_test(l_iter->e, BM_ELEM_HIDDEN)) {
+ GPU_indexbuf_set_line_verts(elb,
+ BM_elem_index_get(l_iter->e),
+ BM_elem_index_get(l_iter),
+ BM_elem_index_get(l_iter->next));
+ }
+ else {
+ GPU_indexbuf_set_line_restart(elb, BM_elem_index_get(l_iter->e));
+ }
+ } while ((l_iter = l_iter->next) != l_first);
+ }
+ EXTRACT_POLY_FOREACH_BM_END;
+}
+
+static void extract_lines_iter_poly_mesh(const MeshRenderData *mr,
+ const ExtractPolyMesh_Params *params,
+ void *elb)
+{
+ /* Using poly & loop iterator would complicate accessing the adjacent loop. */
+ const MLoop *mloop = mr->mloop;
+ const MEdge *medge = mr->medge;
+ if (mr->use_hide || (mr->extract_type == MR_EXTRACT_MAPPED) || (mr->e_origindex != NULL)) {
+ EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr)
+ {
+ const int ml_index_last = mp->loopstart + (mp->totloop - 1);
+ int ml_index = ml_index_last, ml_index_next = mp->loopstart;
+ do {
+ const MLoop *ml = &mloop[ml_index];
+ const MEdge *med = &medge[ml->e];
+ if (!((mr->use_hide && (med->flag & ME_HIDE)) ||
+ ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) &&
+ (mr->e_origindex[ml->e] == ORIGINDEX_NONE)))) {
+ GPU_indexbuf_set_line_verts(elb, ml->e, ml_index, ml_index_next);
+ }
+ else {
+ GPU_indexbuf_set_line_restart(elb, ml->e);
+ }
+ } while ((ml_index = ml_index_next++) != ml_index_last);
+ }
+ EXTRACT_POLY_FOREACH_MESH_END;
}
else {
- GPU_indexbuf_set_line_restart(elb, mloop->e);
+ EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr)
+ {
+ const int ml_index_last = mp->loopstart + (mp->totloop - 1);
+ int ml_index = ml_index_last, ml_index_next = mp->loopstart;
+ do {
+ const MLoop *ml = &mloop[ml_index];
+ GPU_indexbuf_set_line_verts(elb, ml->e, ml_index, ml_index_next);
+ } while ((ml_index = ml_index_next++) != ml_index_last);
+ }
+ EXTRACT_POLY_FOREACH_MESH_END;
}
}
-static void extract_lines_ledge_bmesh(const MeshRenderData *mr, int e, BMEdge *eed, void *elb)
+static void extract_lines_iter_ledge_bm(const MeshRenderData *mr,
+ const ExtractLEdgeBMesh_Params *params,
+ void *elb)
{
- int ledge_idx = mr->edge_len + e;
- if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) {
- int l = mr->loop_len + e * 2;
- GPU_indexbuf_set_line_verts(elb, ledge_idx, l, l + 1);
- }
- else {
- GPU_indexbuf_set_line_restart(elb, ledge_idx);
+ EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params)
+ {
+ const int l_index_offset = mr->edge_len + ledge_index;
+ if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) {
+ const int l_index = mr->loop_len + ledge_index * 2;
+ GPU_indexbuf_set_line_verts(elb, l_index_offset, l_index, l_index + 1);
+ }
+ else {
+ GPU_indexbuf_set_line_restart(elb, l_index_offset);
+ }
+ /* Don't render the edge twice. */
+ GPU_indexbuf_set_line_restart(elb, BM_elem_index_get(eed));
}
- /* Don't render the edge twice. */
- GPU_indexbuf_set_line_restart(elb, BM_elem_index_get(eed));
+ EXTRACT_LEDGE_FOREACH_BM_END;
}
-static void extract_lines_ledge_mesh(const MeshRenderData *mr,
- int e,
- const MEdge *medge,
- void *elb)
+static void extract_lines_iter_ledge_mesh(const MeshRenderData *mr,
+ const ExtractLEdgeMesh_Params *params,
+ void *elb)
{
- int ledge_idx = mr->edge_len + e;
- int edge_idx = mr->ledges[e];
- if (!((mr->use_hide && (medge->flag & ME_HIDE)) ||
- ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) &&
- (mr->e_origindex[edge_idx] == ORIGINDEX_NONE)))) {
- int l = mr->loop_len + e * 2;
- GPU_indexbuf_set_line_verts(elb, ledge_idx, l, l + 1);
- }
- else {
- GPU_indexbuf_set_line_restart(elb, ledge_idx);
+ EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr)
+ {
+ const int l_index_offset = mr->edge_len + ledge_index;
+ const int e_index = mr->ledges[ledge_index];
+ if (!((mr->use_hide && (med->flag & ME_HIDE)) ||
+ ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) &&
+ (mr->e_origindex[e_index] == ORIGINDEX_NONE)))) {
+ const int l_index = mr->loop_len + ledge_index * 2;
+ GPU_indexbuf_set_line_verts(elb, l_index_offset, l_index, l_index + 1);
+ }
+ else {
+ GPU_indexbuf_set_line_restart(elb, l_index_offset);
+ }
+ /* Don't render the edge twice. */
+ GPU_indexbuf_set_line_restart(elb, e_index);
}
- /* Don't render the edge twice. */
- GPU_indexbuf_set_line_restart(elb, edge_idx);
+ EXTRACT_LEDGE_FOREACH_MESH_END;
}
static void extract_lines_finish(const MeshRenderData *UNUSED(mr), void *ibo, void *elb)
@@ -765,18 +1046,14 @@ static void extract_lines_finish(const MeshRenderData *UNUSED(mr), void *ibo, vo
}
static const MeshExtract extract_lines = {
- extract_lines_init,
- NULL,
- NULL,
- extract_lines_loop_bmesh,
- extract_lines_loop_mesh,
- extract_lines_ledge_bmesh,
- extract_lines_ledge_mesh,
- NULL,
- NULL,
- extract_lines_finish,
- 0,
- false,
+ .init = extract_lines_init,
+ .iter_poly_bm = extract_lines_iter_poly_bm,
+ .iter_poly_mesh = extract_lines_iter_poly_mesh,
+ .iter_ledge_bm = extract_lines_iter_ledge_bm,
+ .iter_ledge_mesh = extract_lines_iter_ledge_mesh,
+ .finish = extract_lines_finish,
+ .data_flag = 0,
+ .use_threading = false,
};
/** \} */
@@ -803,18 +1080,14 @@ static void extract_lines_with_lines_loose_finish(const MeshRenderData *mr, void
}
static const MeshExtract extract_lines_with_lines_loose = {
- extract_lines_init,
- NULL,
- NULL,
- extract_lines_loop_bmesh,
- extract_lines_loop_mesh,
- extract_lines_ledge_bmesh,
- extract_lines_ledge_mesh,
- NULL,
- NULL,
- extract_lines_with_lines_loose_finish,
- 0,
- false,
+ .init = extract_lines_init,
+ .iter_poly_bm = extract_lines_iter_poly_bm,
+ .iter_poly_mesh = extract_lines_iter_poly_mesh,
+ .iter_ledge_bm = extract_lines_iter_ledge_bm,
+ .iter_ledge_mesh = extract_lines_iter_ledge_mesh,
+ .finish = extract_lines_with_lines_loose_finish,
+ .data_flag = 0,
+ .use_threading = false,
};
/** \} */
@@ -830,77 +1103,101 @@ static void *extract_points_init(const MeshRenderData *mr, void *UNUSED(buf))
return elb;
}
-BLI_INLINE void vert_set_bmesh(GPUIndexBufBuilder *elb, BMVert *eve, int loop)
+BLI_INLINE void vert_set_bm(GPUIndexBufBuilder *elb, BMVert *eve, int l_index)
{
- int vert_idx = BM_elem_index_get(eve);
+ const int v_index = BM_elem_index_get(eve);
if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
- GPU_indexbuf_set_point_vert(elb, vert_idx, loop);
+ GPU_indexbuf_set_point_vert(elb, v_index, l_index);
}
else {
- GPU_indexbuf_set_point_restart(elb, vert_idx);
+ GPU_indexbuf_set_point_restart(elb, v_index);
}
}
BLI_INLINE void vert_set_mesh(GPUIndexBufBuilder *elb,
const MeshRenderData *mr,
- int vert_idx,
- int loop)
+ const int v_index,
+ const int l_index)
{
- const MVert *mvert = &mr->mvert[vert_idx];
- if (!((mr->use_hide && (mvert->flag & ME_HIDE)) ||
+ const MVert *mv = &mr->mvert[v_index];
+ if (!((mr->use_hide && (mv->flag & ME_HIDE)) ||
((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->v_origindex) &&
- (mr->v_origindex[vert_idx] == ORIGINDEX_NONE)))) {
- GPU_indexbuf_set_point_vert(elb, vert_idx, loop);
+ (mr->v_origindex[v_index] == ORIGINDEX_NONE)))) {
+ GPU_indexbuf_set_point_vert(elb, v_index, l_index);
}
else {
- GPU_indexbuf_set_point_restart(elb, vert_idx);
+ GPU_indexbuf_set_point_restart(elb, v_index);
}
}
-static void extract_points_loop_bmesh(const MeshRenderData *UNUSED(mr),
- int l,
- BMLoop *loop,
- void *elb)
+static void extract_points_iter_poly_bm(const MeshRenderData *mr,
+ const ExtractPolyBMesh_Params *params,
+ void *elb)
{
- vert_set_bmesh(elb, loop->v, l);
+ EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
+ {
+ vert_set_bm(elb, l->v, l_index);
+ }
+ EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
}
-static void extract_points_loop_mesh(const MeshRenderData *mr,
- int l,
- const MLoop *mloop,
- int UNUSED(p),
- const MPoly *UNUSED(mpoly),
- void *elb)
+static void extract_points_iter_poly_mesh(const MeshRenderData *mr,
+ const ExtractPolyMesh_Params *params,
+ void *elb)
{
- vert_set_mesh(elb, mr, mloop->v, l);
+ EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
+ {
+ vert_set_mesh(elb, mr, ml->v, ml_index);
+ }
+ EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
}
-static void extract_points_ledge_bmesh(const MeshRenderData *mr, int e, BMEdge *eed, void *elb)
+static void extract_points_iter_ledge_bm(const MeshRenderData *mr,
+ const ExtractLEdgeBMesh_Params *params,
+ void *elb)
{
- vert_set_bmesh(elb, eed->v1, mr->loop_len + e * 2);
- vert_set_bmesh(elb, eed->v2, mr->loop_len + e * 2 + 1);
+ EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params)
+ {
+ vert_set_bm(elb, eed->v1, mr->loop_len + (ledge_index * 2));
+ vert_set_bm(elb, eed->v2, mr->loop_len + (ledge_index * 2) + 1);
+ }
+ EXTRACT_LEDGE_FOREACH_BM_END;
}
-static void extract_points_ledge_mesh(const MeshRenderData *mr,
- int e,
- const MEdge *medge,
- void *elb)
+static void extract_points_iter_ledge_mesh(const MeshRenderData *mr,
+ const ExtractLEdgeMesh_Params *params,
+ void *elb)
{
- vert_set_mesh(elb, mr, medge->v1, mr->loop_len + e * 2);
- vert_set_mesh(elb, mr, medge->v2, mr->loop_len + e * 2 + 1);
+ EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr)
+ {
+ vert_set_mesh(elb, mr, med->v1, mr->loop_len + (ledge_index * 2));
+ vert_set_mesh(elb, mr, med->v2, mr->loop_len + (ledge_index * 2) + 1);
+ }
+ EXTRACT_LEDGE_FOREACH_MESH_END;
}
-static void extract_points_lvert_bmesh(const MeshRenderData *mr, int v, BMVert *eve, void *elb)
+static void extract_points_iter_lvert_bm(const MeshRenderData *mr,
+ const ExtractLVertBMesh_Params *params,
+ void *elb)
{
- vert_set_bmesh(elb, eve, mr->loop_len + mr->edge_loose_len * 2 + v);
+ const int offset = mr->loop_len + (mr->edge_loose_len * 2);
+ EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params)
+ {
+ vert_set_bm(elb, eve, offset + lvert_index);
+ }
+ EXTRACT_LVERT_FOREACH_BM_END;
}
-static void extract_points_lvert_mesh(const MeshRenderData *mr,
- int v,
- const MVert *UNUSED(mvert),
- void *elb)
+static void extract_points_iter_lvert_mesh(const MeshRenderData *mr,
+ const ExtractLVertMesh_Params *params,
+ void *elb)
{
- vert_set_mesh(elb, mr, mr->lverts[v], mr->loop_len + mr->edge_loose_len * 2 + v);
+ const int offset = mr->loop_len + (mr->edge_loose_len * 2);
+ EXTRACT_LVERT_FOREACH_MESH_BEGIN(mv, lvert_index, params, mr)
+ {
+ vert_set_mesh(elb, mr, mr->lverts[lvert_index], offset + lvert_index);
+ }
+ EXTRACT_LVERT_FOREACH_MESH_END;
}
static void extract_points_finish(const MeshRenderData *UNUSED(mr), void *ibo, void *elb)
@@ -910,18 +1207,16 @@ static void extract_points_finish(const MeshRenderData *UNUSED(mr), void *ibo, v
}
static const MeshExtract extract_points = {
- extract_points_init,
- NULL,
- NULL,
- extract_points_loop_bmesh,
- extract_points_loop_mesh,
- extract_points_ledge_bmesh,
- extract_points_ledge_mesh,
- extract_points_lvert_bmesh,
- extract_points_lvert_mesh,
- extract_points_finish,
- 0,
- false,
+ .init = extract_points_init,
+ .iter_poly_bm = extract_points_iter_poly_bm,
+ .iter_poly_mesh = extract_points_iter_poly_mesh,
+ .iter_ledge_bm = extract_points_iter_ledge_bm,
+ .iter_ledge_mesh = extract_points_iter_ledge_mesh,
+ .iter_lvert_bm = extract_points_iter_lvert_bm,
+ .iter_lvert_mesh = extract_points_iter_lvert_mesh,
+ .finish = extract_points_finish,
+ .data_flag = 0,
+ .use_threading = false,
};
/** \} */
@@ -937,34 +1232,51 @@ static void *extract_fdots_init(const MeshRenderData *mr, void *UNUSED(buf))
return elb;
}
-static void extract_fdots_loop_bmesh(const MeshRenderData *UNUSED(mr),
- int UNUSED(l),
- BMLoop *loop,
- void *elb)
+static void extract_fdots_iter_poly_bm(const MeshRenderData *mr,
+ const ExtractPolyBMesh_Params *params,
+ void *elb)
{
- int face_idx = BM_elem_index_get(loop->f);
- if (!BM_elem_flag_test(loop->f, BM_ELEM_HIDDEN)) {
- GPU_indexbuf_set_point_vert(elb, face_idx, face_idx);
- }
- else {
- GPU_indexbuf_set_point_restart(elb, face_idx);
+ EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr)
+ {
+ if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
+ GPU_indexbuf_set_point_vert(elb, f_index, f_index);
+ }
+ else {
+ GPU_indexbuf_set_point_restart(elb, f_index);
+ }
}
+ EXTRACT_POLY_FOREACH_BM_END;
}
-static void extract_fdots_loop_mesh(const MeshRenderData *mr,
- int UNUSED(l),
- const MLoop *mloop,
- int p,
- const MPoly *mpoly,
- void *elb)
+static void extract_fdots_iter_poly_mesh(const MeshRenderData *mr,
+ const ExtractPolyMesh_Params *params,
+ void *elb)
{
- const MVert *mvert = &mr->mvert[mloop->v];
- if ((!mr->use_subsurf_fdots || (mvert->flag & ME_VERT_FACEDOT)) &&
- !(mr->use_hide && (mpoly->flag & ME_HIDE))) {
- GPU_indexbuf_set_point_vert(elb, p, p);
+ if (mr->use_subsurf_fdots) {
+ /* Check #ME_VERT_FACEDOT. */
+ EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
+ {
+ const MVert *mv = &mr->mvert[ml->v];
+ if ((mv->flag & ME_VERT_FACEDOT) && !(mr->use_hide && (mp->flag & ME_HIDE))) {
+ GPU_indexbuf_set_point_vert(elb, mp_index, mp_index);
+ }
+ else {
+ GPU_indexbuf_set_point_restart(elb, mp_index);
+ }
+ }
+ EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
}
else {
- GPU_indexbuf_set_point_restart(elb, p);
+ EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr)
+ {
+ if (!(mr->use_hide && (mp->flag & ME_HIDE))) {
+ GPU_indexbuf_set_point_vert(elb, mp_index, mp_index);
+ }
+ else {
+ GPU_indexbuf_set_point_restart(elb, mp_index);
+ }
+ }
+ EXTRACT_POLY_FOREACH_MESH_END;
}
}
@@ -975,18 +1287,12 @@ static void extract_fdots_finish(const MeshRenderData *UNUSED(mr), void *ibo, vo
}
static const MeshExtract extract_fdots = {
- extract_fdots_init,
- NULL,
- NULL,
- extract_fdots_loop_bmesh,
- extract_fdots_loop_mesh,
- NULL,
- NULL,
- NULL,
- NULL,
- extract_fdots_finish,
- 0,
- false,
+ .init = extract_fdots_init,
+ .iter_poly_bm = extract_fdots_iter_poly_bm,
+ .iter_poly_mesh = extract_fdots_iter_poly_mesh,
+ .finish = extract_fdots_finish,
+ .data_flag = 0,
+ .use_threading = false,
};
/** \} */
@@ -1009,66 +1315,60 @@ static void *extract_lines_paint_mask_init(const MeshRenderData *mr, void *UNUSE
return data;
}
-static void extract_lines_paint_mask_loop_mesh(const MeshRenderData *mr,
- int l,
- const MLoop *mloop,
- int UNUSED(p),
- const MPoly *mpoly,
- void *_data)
-{
- MeshExtract_LinePaintMask_Data *data = (MeshExtract_LinePaintMask_Data *)_data;
- const int edge_idx = mloop->e;
- const MEdge *medge = &mr->medge[edge_idx];
- if (!((mr->use_hide && (medge->flag & ME_HIDE)) ||
- ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) &&
- (mr->e_origindex[edge_idx] == ORIGINDEX_NONE)))) {
-
- int loopend = mpoly->totloop + mpoly->loopstart - 1;
- int other_loop = (l == loopend) ? mpoly->loopstart : (l + 1);
- if (mpoly->flag & ME_FACE_SEL) {
- if (BLI_BITMAP_TEST_AND_SET_ATOMIC(data->select_map, edge_idx)) {
- /* Hide edge as it has more than 2 selected loop. */
- GPU_indexbuf_set_line_restart(&data->elb, edge_idx);
+static void extract_lines_paint_mask_iter_poly_mesh(const MeshRenderData *mr,
+ const ExtractPolyMesh_Params *params,
+ void *_data)
+{
+ MeshExtract_LinePaintMask_Data *data = _data;
+ EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
+ {
+ const int e_index = ml->e;
+ const MEdge *me = &mr->medge[e_index];
+ if (!((mr->use_hide && (me->flag & ME_HIDE)) ||
+ ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) &&
+ (mr->e_origindex[e_index] == ORIGINDEX_NONE)))) {
+
+ const int ml_index_last = mp->totloop + mp->loopstart - 1;
+ const int ml_index_other = (ml_index == ml_index_last) ? mp->loopstart : (ml_index + 1);
+ if (mp->flag & ME_FACE_SEL) {
+ if (BLI_BITMAP_TEST_AND_SET_ATOMIC(data->select_map, e_index)) {
+ /* Hide edge as it has more than 2 selected loop. */
+ GPU_indexbuf_set_line_restart(&data->elb, e_index);
+ }
+ else {
+ /* First selected loop. Set edge visible, overwriting any unselected loop. */
+ GPU_indexbuf_set_line_verts(&data->elb, e_index, ml_index, ml_index_other);
+ }
}
else {
- /* First selected loop. Set edge visible, overwritting any unsel loop. */
- GPU_indexbuf_set_line_verts(&data->elb, edge_idx, l, other_loop);
+ /* Set theses unselected loop only if this edge has no other selected loop. */
+ if (!BLI_BITMAP_TEST(data->select_map, e_index)) {
+ GPU_indexbuf_set_line_verts(&data->elb, e_index, ml_index, ml_index_other);
+ }
}
}
else {
- /* Set theses unselected loop only if this edge has no other selected loop. */
- if (!BLI_BITMAP_TEST(data->select_map, edge_idx)) {
- GPU_indexbuf_set_line_verts(&data->elb, edge_idx, l, other_loop);
- }
+ GPU_indexbuf_set_line_restart(&data->elb, e_index);
}
}
- else {
- GPU_indexbuf_set_line_restart(&data->elb, edge_idx);
- }
+ EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
}
static void extract_lines_paint_mask_finish(const MeshRenderData *UNUSED(mr),
void *ibo,
void *_data)
{
- MeshExtract_LinePaintMask_Data *data = (MeshExtract_LinePaintMask_Data *)_data;
+ MeshExtract_LinePaintMask_Data *data = _data;
GPU_indexbuf_build_in_place(&data->elb, ibo);
MEM_freeN(data);
}
static const MeshExtract extract_lines_paint_mask = {
- extract_lines_paint_mask_init,
- NULL,
- NULL,
- NULL,
- extract_lines_paint_mask_loop_mesh,
- NULL,
- NULL,
- NULL,
- NULL,
- extract_lines_paint_mask_finish,
- 0,
- false,
+ .init = extract_lines_paint_mask_init,
+ .iter_poly_mesh = extract_lines_paint_mask_iter_poly_mesh,
+ .finish = extract_lines_paint_mask_finish,
+ .data_flag = 0,
+ .use_threading = false,
};
/** \} */
@@ -1090,7 +1390,7 @@ typedef struct MeshExtract_LineAdjacency_Data {
static void *extract_lines_adjacency_init(const MeshRenderData *mr, void *UNUSED(buf))
{
/* Similar to poly_to_tri_count().
- * There is always loop + tri - 1 edges inside a polygon.
+ * There is always (loop + triangle - 1) edges inside a polygon.
* Accumulate for all polys and you get : */
uint tess_edge_len = mr->loop_len + mr->tri_len - mr->poly_len;
@@ -1107,7 +1407,7 @@ BLI_INLINE void lines_adjacency_triangle(
uint v1, uint v2, uint v3, uint l1, uint l2, uint l3, MeshExtract_LineAdjacency_Data *data)
{
GPUIndexBufBuilder *elb = &data->elb;
- /* Iter around the triangle's edges. */
+ /* Iterate around the triangle's edges. */
for (int e = 0; e < 3; e++) {
SHIFT3(uint, v3, v2, v1);
SHIFT3(uint, l3, l2, l1);
@@ -1118,7 +1418,7 @@ BLI_INLINE void lines_adjacency_triangle(
int v_data = POINTER_AS_INT(*pval);
if (!value_is_init || v_data == NO_EDGE) {
/* Save the winding order inside the sign bit. Because the
- * edgehash sort the keys and we need to compare winding later. */
+ * Edge-hash sort the keys and we need to compare winding later. */
int value = (int)l1 + 1; /* 0 cannot be signed so add one. */
*pval = POINTER_FROM_INT((inv_indices) ? -value : value);
/* Store loop indices for remaining non-manifold edges. */
@@ -1130,7 +1430,7 @@ BLI_INLINE void lines_adjacency_triangle(
*pval = POINTER_FROM_INT(NO_EDGE);
bool inv_opposite = (v_data < 0);
uint l_opposite = (uint)abs(v_data) - 1;
- /* TODO Make this part threadsafe. */
+ /* TODO Make this part thread-safe. */
if (inv_opposite == inv_indices) {
/* Don't share edge if triangles have non matching winding. */
GPU_indexbuf_add_line_adj_verts(elb, l1, l2, l3, l1);
@@ -1144,42 +1444,48 @@ BLI_INLINE void lines_adjacency_triangle(
}
}
-static void extract_lines_adjacency_looptri_bmesh(const MeshRenderData *UNUSED(mr),
- int UNUSED(t),
- BMLoop **elt,
- void *data)
+static void extract_lines_adjacency_iter_looptri_bm(const MeshRenderData *UNUSED(mr),
+ const struct ExtractTriBMesh_Params *params,
+ void *data)
{
- if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) {
- lines_adjacency_triangle(BM_elem_index_get(elt[0]->v),
- BM_elem_index_get(elt[1]->v),
- BM_elem_index_get(elt[2]->v),
- BM_elem_index_get(elt[0]),
- BM_elem_index_get(elt[1]),
- BM_elem_index_get(elt[2]),
- data);
+ EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elt, _elt_index, params)
+ {
+ if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) {
+ lines_adjacency_triangle(BM_elem_index_get(elt[0]->v),
+ BM_elem_index_get(elt[1]->v),
+ BM_elem_index_get(elt[2]->v),
+ BM_elem_index_get(elt[0]),
+ BM_elem_index_get(elt[1]),
+ BM_elem_index_get(elt[2]),
+ data);
+ }
}
+ EXTRACT_TRIS_LOOPTRI_FOREACH_BM_END;
}
-static void extract_lines_adjacency_looptri_mesh(const MeshRenderData *mr,
- int UNUSED(t),
- const MLoopTri *mlt,
- void *data)
+static void extract_lines_adjacency_iter_looptri_mesh(const MeshRenderData *mr,
+ const struct ExtractTriMesh_Params *params,
+ void *data)
{
- const MPoly *mpoly = &mr->mpoly[mlt->poly];
- if (!(mr->use_hide && (mpoly->flag & ME_HIDE))) {
- lines_adjacency_triangle(mr->mloop[mlt->tri[0]].v,
- mr->mloop[mlt->tri[1]].v,
- mr->mloop[mlt->tri[2]].v,
- mlt->tri[0],
- mlt->tri[1],
- mlt->tri[2],
- data);
+ EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(mlt, _mlt_index, params)
+ {
+ const MPoly *mp = &mr->mpoly[mlt->poly];
+ if (!(mr->use_hide && (mp->flag & ME_HIDE))) {
+ lines_adjacency_triangle(mr->mloop[mlt->tri[0]].v,
+ mr->mloop[mlt->tri[1]].v,
+ mr->mloop[mlt->tri[2]].v,
+ mlt->tri[0],
+ mlt->tri[1],
+ mlt->tri[2],
+ data);
+ }
}
+ EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END;
}
static void extract_lines_adjacency_finish(const MeshRenderData *mr, void *ibo, void *_data)
{
- MeshExtract_LineAdjacency_Data *data = (MeshExtract_LineAdjacency_Data *)_data;
+ MeshExtract_LineAdjacency_Data *data = _data;
/* Create edges for remaining non manifold edges. */
EdgeHashIterator *ehi = BLI_edgehashIterator_new(data->eh);
for (; !BLI_edgehashIterator_isDone(ehi); BLI_edgehashIterator_step(ehi)) {
@@ -1209,18 +1515,12 @@ static void extract_lines_adjacency_finish(const MeshRenderData *mr, void *ibo,
#undef NO_EDGE
static const MeshExtract extract_lines_adjacency = {
- extract_lines_adjacency_init,
- extract_lines_adjacency_looptri_bmesh,
- extract_lines_adjacency_looptri_mesh,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- extract_lines_adjacency_finish,
- 0,
- false,
+ .init = extract_lines_adjacency_init,
+ .iter_looptri_bm = extract_lines_adjacency_iter_looptri_bm,
+ .iter_looptri_mesh = extract_lines_adjacency_iter_looptri_mesh,
+ .finish = extract_lines_adjacency_finish,
+ .data_flag = 0,
+ .use_threading = false,
};
/** \} */
@@ -1250,53 +1550,53 @@ BLI_INLINE void edituv_tri_add(
}
}
-static void extract_edituv_tris_looptri_bmesh(const MeshRenderData *UNUSED(mr),
- int UNUSED(t),
- BMLoop **elt,
- void *data)
+static void extract_edituv_tris_iter_looptri_bm(const MeshRenderData *UNUSED(mr),
+ const struct ExtractTriBMesh_Params *params,
+ void *data)
{
- edituv_tri_add(data,
- BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN),
- BM_elem_flag_test(elt[0]->f, BM_ELEM_SELECT),
- BM_elem_index_get(elt[0]),
- BM_elem_index_get(elt[1]),
- BM_elem_index_get(elt[2]));
+ EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elt, _elt_index, params)
+ {
+ edituv_tri_add(data,
+ BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN),
+ BM_elem_flag_test(elt[0]->f, BM_ELEM_SELECT),
+ BM_elem_index_get(elt[0]),
+ BM_elem_index_get(elt[1]),
+ BM_elem_index_get(elt[2]));
+ }
+ EXTRACT_TRIS_LOOPTRI_FOREACH_BM_END;
}
-static void extract_edituv_tris_looptri_mesh(const MeshRenderData *mr,
- int UNUSED(t),
- const MLoopTri *mlt,
- void *data)
+static void extract_edituv_tris_iter_looptri_mesh(const MeshRenderData *mr,
+ const struct ExtractTriMesh_Params *params,
+ void *data)
{
- const MPoly *mpoly = &mr->mpoly[mlt->poly];
- edituv_tri_add(data,
- (mpoly->flag & ME_HIDE) != 0,
- (mpoly->flag & ME_FACE_SEL) != 0,
- mlt->tri[0],
- mlt->tri[1],
- mlt->tri[2]);
+ EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(mlt, _mlt_index, params)
+ {
+ const MPoly *mp = &mr->mpoly[mlt->poly];
+ edituv_tri_add(data,
+ (mp->flag & ME_HIDE) != 0,
+ (mp->flag & ME_FACE_SEL) != 0,
+ mlt->tri[0],
+ mlt->tri[1],
+ mlt->tri[2]);
+ }
+ EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END;
}
static void extract_edituv_tris_finish(const MeshRenderData *UNUSED(mr), void *ibo, void *data)
{
- MeshExtract_EditUvElem_Data *extract_data = (MeshExtract_EditUvElem_Data *)data;
+ MeshExtract_EditUvElem_Data *extract_data = data;
GPU_indexbuf_build_in_place(&extract_data->elb, ibo);
MEM_freeN(extract_data);
}
static const MeshExtract extract_edituv_tris = {
- extract_edituv_tris_init,
- extract_edituv_tris_looptri_bmesh,
- extract_edituv_tris_looptri_mesh,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- extract_edituv_tris_finish,
- 0,
- false,
+ .init = extract_edituv_tris_init,
+ .iter_looptri_bm = extract_edituv_tris_iter_looptri_bm,
+ .iter_looptri_mesh = extract_edituv_tris_iter_looptri_mesh,
+ .finish = extract_edituv_tris_finish,
+ .data_flag = 0,
+ .use_threading = false,
};
/** \} */
@@ -1322,55 +1622,53 @@ BLI_INLINE void edituv_edge_add(
}
}
-static void extract_edituv_lines_loop_bmesh(const MeshRenderData *UNUSED(mr),
- int l,
- BMLoop *loop,
- void *data)
+static void extract_edituv_lines_iter_poly_bm(const MeshRenderData *mr,
+ const ExtractPolyBMesh_Params *params,
+ void *data)
{
- edituv_edge_add(data,
- BM_elem_flag_test(loop->f, BM_ELEM_HIDDEN),
- BM_elem_flag_test(loop->f, BM_ELEM_SELECT),
- l,
- BM_elem_index_get(loop->next));
+ EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(loop, l_index, params, mr)
+ {
+ edituv_edge_add(data,
+ BM_elem_flag_test(loop->f, BM_ELEM_HIDDEN),
+ BM_elem_flag_test(loop->f, BM_ELEM_SELECT),
+ l_index,
+ BM_elem_index_get(loop->next));
+ }
+ EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(loop);
}
-static void extract_edituv_lines_loop_mesh(const MeshRenderData *mr,
- int loop_idx,
- const MLoop *mloop,
- int UNUSED(p),
- const MPoly *mpoly,
- void *data)
+static void extract_edituv_lines_iter_poly_mesh(const MeshRenderData *mr,
+ const ExtractPolyMesh_Params *params,
+ void *data)
{
- int loopend = mpoly->totloop + mpoly->loopstart - 1;
- int loop_next_idx = (loop_idx == loopend) ? mpoly->loopstart : (loop_idx + 1);
- const bool real_edge = (mr->e_origindex == NULL || mr->e_origindex[mloop->e] != ORIGINDEX_NONE);
- edituv_edge_add(data,
- (mpoly->flag & ME_HIDE) != 0 || !real_edge,
- (mpoly->flag & ME_FACE_SEL) != 0,
- loop_idx,
- loop_next_idx);
+ EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
+ {
+ const int ml_index_last = mp->totloop + mp->loopstart - 1;
+ const int ml_index_next = (ml_index == ml_index_last) ? mp->loopstart : (ml_index + 1);
+ const bool real_edge = (mr->e_origindex == NULL || mr->e_origindex[ml->e] != ORIGINDEX_NONE);
+ edituv_edge_add(data,
+ (mp->flag & ME_HIDE) != 0 || !real_edge,
+ (mp->flag & ME_FACE_SEL) != 0,
+ ml_index,
+ ml_index_next);
+ }
+ EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
}
static void extract_edituv_lines_finish(const MeshRenderData *UNUSED(mr), void *ibo, void *data)
{
- MeshExtract_EditUvElem_Data *extract_data = (MeshExtract_EditUvElem_Data *)data;
+ MeshExtract_EditUvElem_Data *extract_data = data;
GPU_indexbuf_build_in_place(&extract_data->elb, ibo);
MEM_freeN(extract_data);
}
static const MeshExtract extract_edituv_lines = {
- extract_edituv_lines_init,
- NULL,
- NULL,
- extract_edituv_lines_loop_bmesh,
- extract_edituv_lines_loop_mesh,
- NULL,
- NULL,
- NULL,
- NULL,
- extract_edituv_lines_finish,
- 0,
- false,
+ .init = extract_edituv_lines_init,
+ .iter_poly_bm = extract_edituv_lines_iter_poly_bm,
+ .iter_poly_mesh = extract_edituv_lines_iter_poly_mesh,
+ .finish = extract_edituv_lines_finish,
+ .data_flag = 0,
+ .use_threading = false,
};
/** \} */
@@ -1398,50 +1696,48 @@ BLI_INLINE void edituv_point_add(MeshExtract_EditUvElem_Data *data,
}
}
-static void extract_edituv_points_loop_bmesh(const MeshRenderData *UNUSED(mr),
- int l,
- BMLoop *loop,
- void *data)
+static void extract_edituv_points_iter_poly_bm(const MeshRenderData *mr,
+ const ExtractPolyBMesh_Params *params,
+ void *data)
{
- edituv_point_add(data,
- BM_elem_flag_test(loop->f, BM_ELEM_HIDDEN),
- BM_elem_flag_test(loop->f, BM_ELEM_SELECT),
- l);
+ EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
+ {
+ edituv_point_add(data,
+ BM_elem_flag_test(l->f, BM_ELEM_HIDDEN),
+ BM_elem_flag_test(l->f, BM_ELEM_SELECT),
+ l_index);
+ }
+ EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
}
-static void extract_edituv_points_loop_mesh(const MeshRenderData *mr,
- int l,
- const MLoop *mloop,
- int UNUSED(p),
- const MPoly *mpoly,
- void *data)
+static void extract_edituv_points_iter_poly_mesh(const MeshRenderData *mr,
+ const ExtractPolyMesh_Params *params,
+ void *data)
{
- const bool real_vert = (mr->extract_type == MR_EXTRACT_MAPPED && (mr->v_origindex) &&
- mr->v_origindex[mloop->v] != ORIGINDEX_NONE);
- edituv_point_add(
- data, ((mpoly->flag & ME_HIDE) != 0) || !real_vert, (mpoly->flag & ME_FACE_SEL) != 0, l);
+ EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
+ {
+ const bool real_vert = (mr->extract_type == MR_EXTRACT_MAPPED && (mr->v_origindex) &&
+ mr->v_origindex[ml->v] != ORIGINDEX_NONE);
+ edituv_point_add(
+ data, ((mp->flag & ME_HIDE) != 0) || !real_vert, (mp->flag & ME_FACE_SEL) != 0, ml_index);
+ }
+ EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
}
static void extract_edituv_points_finish(const MeshRenderData *UNUSED(mr), void *ibo, void *data)
{
- MeshExtract_EditUvElem_Data *extract_data = (MeshExtract_EditUvElem_Data *)data;
+ MeshExtract_EditUvElem_Data *extract_data = data;
GPU_indexbuf_build_in_place(&extract_data->elb, ibo);
MEM_freeN(extract_data);
}
static const MeshExtract extract_edituv_points = {
- extract_edituv_points_init,
- NULL,
- NULL,
- extract_edituv_points_loop_bmesh,
- extract_edituv_points_loop_mesh,
- NULL,
- NULL,
- NULL,
- NULL,
- extract_edituv_points_finish,
- 0,
- false,
+ .init = extract_edituv_points_init,
+ .iter_poly_bm = extract_edituv_points_iter_poly_bm,
+ .iter_poly_mesh = extract_edituv_points_iter_poly_mesh,
+ .finish = extract_edituv_points_finish,
+ .data_flag = 0,
+ .use_threading = false,
};
/** \} */
@@ -1462,64 +1758,75 @@ static void *extract_edituv_fdots_init(const MeshRenderData *mr, void *UNUSED(ib
BLI_INLINE void edituv_facedot_add(MeshExtract_EditUvElem_Data *data,
bool hidden,
bool selected,
- int face_idx)
+ int face_index)
{
if (!hidden && (data->sync_selection || selected)) {
- GPU_indexbuf_set_point_vert(&data->elb, face_idx, face_idx);
+ GPU_indexbuf_set_point_vert(&data->elb, face_index, face_index);
}
else {
- GPU_indexbuf_set_point_restart(&data->elb, face_idx);
+ GPU_indexbuf_set_point_restart(&data->elb, face_index);
}
}
-static void extract_edituv_fdots_loop_bmesh(const MeshRenderData *UNUSED(mr),
- int UNUSED(l),
- BMLoop *loop,
- void *data)
+static void extract_edituv_fdots_iter_poly_bm(const MeshRenderData *mr,
+ const ExtractPolyBMesh_Params *params,
+ void *data)
{
- edituv_facedot_add(data,
- BM_elem_flag_test(loop->f, BM_ELEM_HIDDEN),
- BM_elem_flag_test(loop->f, BM_ELEM_SELECT),
- BM_elem_index_get(loop->f));
+ EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr)
+ {
+ edituv_facedot_add(
+ data, BM_elem_flag_test(f, BM_ELEM_HIDDEN), BM_elem_flag_test(f, BM_ELEM_SELECT), f_index);
+ }
+ EXTRACT_POLY_FOREACH_BM_END;
}
-static void extract_edituv_fdots_loop_mesh(const MeshRenderData *mr,
- int UNUSED(l),
- const MLoop *mloop,
- int p,
- const MPoly *mpoly,
- void *data)
+static void extract_edituv_fdots_iter_poly_mesh(const MeshRenderData *mr,
+ const ExtractPolyMesh_Params *params,
+ void *data)
{
- const bool real_fdot = (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex &&
- mr->p_origindex[p] != ORIGINDEX_NONE);
- const bool subd_fdot = (!mr->use_subsurf_fdots ||
- (mr->mvert[mloop->v].flag & ME_VERT_FACEDOT) != 0);
- edituv_facedot_add(data,
- ((mpoly->flag & ME_HIDE) != 0) || !real_fdot || !subd_fdot,
- (mpoly->flag & ME_FACE_SEL) != 0,
- p);
+ if (mr->use_subsurf_fdots) {
+ /* Check #ME_VERT_FACEDOT. */
+ EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
+ {
+ const bool real_fdot = (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex &&
+ mr->p_origindex[mp_index] != ORIGINDEX_NONE);
+ const bool subd_fdot = (!mr->use_subsurf_fdots ||
+ (mr->mvert[ml->v].flag & ME_VERT_FACEDOT) != 0);
+ edituv_facedot_add(data,
+ ((mp->flag & ME_HIDE) != 0) || !real_fdot || !subd_fdot,
+ (mp->flag & ME_FACE_SEL) != 0,
+ mp_index);
+ }
+ EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
+ }
+ else {
+ EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr)
+ {
+ const bool real_fdot = (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex &&
+ mr->p_origindex[mp_index] != ORIGINDEX_NONE);
+ edituv_facedot_add(data,
+ ((mp->flag & ME_HIDE) != 0) || !real_fdot,
+ (mp->flag & ME_FACE_SEL) != 0,
+ mp_index);
+ }
+ EXTRACT_POLY_FOREACH_MESH_END;
+ }
}
static void extract_edituv_fdots_finish(const MeshRenderData *UNUSED(mr), void *ibo, void *_data)
{
- MeshExtract_EditUvElem_Data *data = (MeshExtract_EditUvElem_Data *)_data;
+ MeshExtract_EditUvElem_Data *data = _data;
GPU_indexbuf_build_in_place(&data->elb, ibo);
MEM_freeN(data);
}
static const MeshExtract extract_edituv_fdots = {
- extract_edituv_fdots_init,
- NULL,
- NULL,
- extract_edituv_fdots_loop_bmesh,
- extract_edituv_fdots_loop_mesh,
- NULL,
- NULL,
- NULL,
- NULL,
- extract_edituv_fdots_finish,
- 0,
- false,
+ .init = extract_edituv_fdots_init,
+ .iter_poly_bm = extract_edituv_fdots_iter_poly_bm,
+ .iter_poly_mesh = extract_edituv_fdots_iter_poly_mesh,
+ .finish = extract_edituv_fdots_finish,
+ .data_flag = 0,
+ .use_threading = false,
};
/** \} */
@@ -1542,7 +1849,7 @@ static void *extract_pos_nor_init(const MeshRenderData *mr, void *buf)
{
static GPUVertFormat format = {0};
if (format.attr_len == 0) {
- /* WARNING Adjust PosNorLoop struct accordingly. */
+ /* WARNING Adjust #PosNorLoop struct accordingly. */
GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
GPU_vertformat_alias_add(&format, "vnor");
@@ -1566,95 +1873,122 @@ static void *extract_pos_nor_init(const MeshRenderData *mr, void *buf)
}
}
else {
- const MVert *mvert = mr->mvert;
- for (int v = 0; v < mr->vert_len; v++, mvert++) {
- data->packed_nor[v] = GPU_normal_convert_i10_s3(mvert->no);
+ const MVert *mv = mr->mvert;
+ for (int v = 0; v < mr->vert_len; v++, mv++) {
+ data->packed_nor[v] = GPU_normal_convert_i10_s3(mv->no);
}
}
return data;
}
-static void extract_pos_nor_loop_bmesh(const MeshRenderData *mr, int l, BMLoop *loop, void *_data)
+static void extract_pos_nor_iter_poly_bm(const MeshRenderData *mr,
+ const ExtractPolyBMesh_Params *params,
+ void *_data)
{
MeshExtract_PosNor_Data *data = _data;
- PosNorLoop *vert = data->vbo_data + l;
- copy_v3_v3(vert->pos, bm_vert_co_get(mr, loop->v));
- vert->nor = data->packed_nor[BM_elem_index_get(loop->v)];
- BMFace *efa = loop->f;
- vert->nor.w = BM_elem_flag_test(efa, BM_ELEM_HIDDEN) ? -1 : 0;
+ EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
+ {
+ PosNorLoop *vert = &data->vbo_data[l_index];
+ copy_v3_v3(vert->pos, bm_vert_co_get(mr, l->v));
+ vert->nor = data->packed_nor[BM_elem_index_get(l->v)];
+ BMFace *efa = l->f;
+ vert->nor.w = BM_elem_flag_test(efa, BM_ELEM_HIDDEN) ? -1 : 0;
+ }
+ EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
}
-static void extract_pos_nor_loop_mesh(const MeshRenderData *mr,
- int l,
- const MLoop *mloop,
- int UNUSED(p),
- const MPoly *mpoly,
- void *_data)
+static void extract_pos_nor_iter_poly_mesh(const MeshRenderData *mr,
+ const ExtractPolyMesh_Params *params,
+ void *_data)
{
MeshExtract_PosNor_Data *data = _data;
- PosNorLoop *vert = data->vbo_data + l;
- const MVert *mvert = &mr->mvert[mloop->v];
- copy_v3_v3(vert->pos, mvert->co);
- vert->nor = data->packed_nor[mloop->v];
- /* Flag for paint mode overlay. */
- if (mpoly->flag & ME_HIDE || mvert->flag & ME_HIDE ||
- ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->v_origindex) &&
- (mr->v_origindex[mloop->v] == ORIGINDEX_NONE))) {
- vert->nor.w = -1;
- }
- else if (mvert->flag & SELECT) {
- vert->nor.w = 1;
- }
- else {
- vert->nor.w = 0;
+ EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
+ {
+ PosNorLoop *vert = &data->vbo_data[ml_index];
+ const MVert *mv = &mr->mvert[ml->v];
+ copy_v3_v3(vert->pos, mv->co);
+ vert->nor = data->packed_nor[ml->v];
+ /* Flag for paint mode overlay. */
+ if (mp->flag & ME_HIDE || mv->flag & ME_HIDE ||
+ ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->v_origindex) &&
+ (mr->v_origindex[ml->v] == ORIGINDEX_NONE))) {
+ vert->nor.w = -1;
+ }
+ else if (mv->flag & SELECT) {
+ vert->nor.w = 1;
+ }
+ else {
+ vert->nor.w = 0;
+ }
}
+ EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
}
-static void extract_pos_nor_ledge_bmesh(const MeshRenderData *mr, int e, BMEdge *eed, void *_data)
+static void extract_pos_nor_iter_ledge_bm(const MeshRenderData *mr,
+ const ExtractLEdgeBMesh_Params *params,
+ void *_data)
{
- int l = mr->loop_len + e * 2;
MeshExtract_PosNor_Data *data = _data;
- PosNorLoop *vert = data->vbo_data + l;
- copy_v3_v3(vert[0].pos, bm_vert_co_get(mr, eed->v1));
- copy_v3_v3(vert[1].pos, bm_vert_co_get(mr, eed->v2));
- vert[0].nor = data->packed_nor[BM_elem_index_get(eed->v1)];
- vert[1].nor = data->packed_nor[BM_elem_index_get(eed->v2)];
+ EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params)
+ {
+ int l_index = mr->loop_len + ledge_index * 2;
+ PosNorLoop *vert = &data->vbo_data[l_index];
+ copy_v3_v3(vert[0].pos, bm_vert_co_get(mr, eed->v1));
+ copy_v3_v3(vert[1].pos, bm_vert_co_get(mr, eed->v2));
+ vert[0].nor = data->packed_nor[BM_elem_index_get(eed->v1)];
+ vert[1].nor = data->packed_nor[BM_elem_index_get(eed->v2)];
+ }
+ EXTRACT_LEDGE_FOREACH_BM_END;
}
-static void extract_pos_nor_ledge_mesh(const MeshRenderData *mr,
- int e,
- const MEdge *medge,
- void *_data)
+static void extract_pos_nor_iter_ledge_mesh(const MeshRenderData *mr,
+ const ExtractLEdgeMesh_Params *params,
+ void *_data)
{
- int l = mr->loop_len + e * 2;
MeshExtract_PosNor_Data *data = _data;
- PosNorLoop *vert = data->vbo_data + l;
- copy_v3_v3(vert[0].pos, mr->mvert[medge->v1].co);
- copy_v3_v3(vert[1].pos, mr->mvert[medge->v2].co);
- vert[0].nor = data->packed_nor[medge->v1];
- vert[1].nor = data->packed_nor[medge->v2];
+ EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr)
+ {
+ const int ml_index = mr->loop_len + ledge_index * 2;
+ PosNorLoop *vert = &data->vbo_data[ml_index];
+ copy_v3_v3(vert[0].pos, mr->mvert[med->v1].co);
+ copy_v3_v3(vert[1].pos, mr->mvert[med->v2].co);
+ vert[0].nor = data->packed_nor[med->v1];
+ vert[1].nor = data->packed_nor[med->v2];
+ }
+ EXTRACT_LEDGE_FOREACH_MESH_END;
}
-static void extract_pos_nor_lvert_bmesh(const MeshRenderData *mr, int v, BMVert *eve, void *_data)
+static void extract_pos_nor_iter_lvert_bm(const MeshRenderData *mr,
+ const ExtractLVertBMesh_Params *params,
+ void *_data)
{
- int l = mr->loop_len + mr->edge_loose_len * 2 + v;
MeshExtract_PosNor_Data *data = _data;
- PosNorLoop *vert = data->vbo_data + l;
- copy_v3_v3(vert->pos, bm_vert_co_get(mr, eve));
- vert->nor = data->packed_nor[BM_elem_index_get(eve)];
+ const int offset = mr->loop_len + (mr->edge_loose_len * 2);
+ EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params)
+ {
+ const int l_index = offset + lvert_index;
+ PosNorLoop *vert = &data->vbo_data[l_index];
+ copy_v3_v3(vert->pos, bm_vert_co_get(mr, eve));
+ vert->nor = data->packed_nor[BM_elem_index_get(eve)];
+ }
+ EXTRACT_LVERT_FOREACH_BM_END;
}
-static void extract_pos_nor_lvert_mesh(const MeshRenderData *mr,
- int v,
- const MVert *mvert,
- void *_data)
+static void extract_pos_nor_iter_lvert_mesh(const MeshRenderData *mr,
+ const ExtractLVertMesh_Params *params,
+ void *_data)
{
- int l = mr->loop_len + mr->edge_loose_len * 2 + v;
- int v_idx = mr->lverts[v];
MeshExtract_PosNor_Data *data = _data;
- PosNorLoop *vert = data->vbo_data + l;
- copy_v3_v3(vert->pos, mvert->co);
- vert->nor = data->packed_nor[v_idx];
+ const int offset = mr->loop_len + (mr->edge_loose_len * 2);
+ EXTRACT_LVERT_FOREACH_MESH_BEGIN(mv, lvert_index, params, mr)
+ {
+ const int ml_index = offset + lvert_index;
+ const int v_index = mr->lverts[lvert_index];
+ PosNorLoop *vert = &data->vbo_data[ml_index];
+ copy_v3_v3(vert->pos, mv->co);
+ vert->nor = data->packed_nor[v_index];
+ }
+ EXTRACT_LVERT_FOREACH_MESH_END;
}
static void extract_pos_nor_finish(const MeshRenderData *UNUSED(mr), void *UNUSED(vbo), void *data)
@@ -1663,18 +1997,16 @@ static void extract_pos_nor_finish(const MeshRenderData *UNUSED(mr), void *UNUSE
}
static const MeshExtract extract_pos_nor = {
- extract_pos_nor_init,
- NULL,
- NULL,
- extract_pos_nor_loop_bmesh,
- extract_pos_nor_loop_mesh,
- extract_pos_nor_ledge_bmesh,
- extract_pos_nor_ledge_mesh,
- extract_pos_nor_lvert_bmesh,
- extract_pos_nor_lvert_mesh,
- extract_pos_nor_finish,
- 0,
- true,
+ .init = extract_pos_nor_init,
+ .iter_poly_bm = extract_pos_nor_iter_poly_bm,
+ .iter_poly_mesh = extract_pos_nor_iter_poly_mesh,
+ .iter_ledge_bm = extract_pos_nor_iter_ledge_bm,
+ .iter_ledge_mesh = extract_pos_nor_iter_ledge_mesh,
+ .iter_lvert_bm = extract_pos_nor_iter_lvert_bm,
+ .iter_lvert_mesh = extract_pos_nor_iter_lvert_mesh,
+ .finish = extract_pos_nor_finish,
+ .data_flag = 0,
+ .use_threading = true,
};
/** \} */
@@ -1700,62 +2032,71 @@ static void *extract_lnor_hq_init(const MeshRenderData *mr, void *buf)
return vbo->data;
}
-static void extract_lnor_hq_loop_bmesh(const MeshRenderData *mr, int l, BMLoop *loop, void *data)
+static void extract_lnor_hq_iter_poly_bm(const MeshRenderData *mr,
+ const ExtractPolyBMesh_Params *params,
+ void *data)
{
if (mr->loop_normals) {
- normal_float_to_short_v3(&((gpuHQNor *)data)[l].x, mr->loop_normals[l]);
- }
- else if (BM_elem_flag_test(loop->f, BM_ELEM_SMOOTH)) {
- normal_float_to_short_v3(&((gpuHQNor *)data)[l].x, bm_vert_no_get(mr, loop->v));
+ EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(_l, l_index, params, mr)
+ {
+ normal_float_to_short_v3(&((gpuHQNor *)data)[l_index].x, mr->loop_normals[l_index]);
+ }
+ EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(_l);
}
else {
- normal_float_to_short_v3(&((gpuHQNor *)data)[l].x, bm_face_no_get(mr, loop->f));
+ EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
+ {
+ if (BM_elem_flag_test(l->f, BM_ELEM_SMOOTH)) {
+ normal_float_to_short_v3(&((gpuHQNor *)data)[l_index].x, bm_vert_no_get(mr, l->v));
+ }
+ else {
+ normal_float_to_short_v3(&((gpuHQNor *)data)[l_index].x, bm_face_no_get(mr, l->f));
+ }
+ }
+ EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
}
}
-static void extract_lnor_hq_loop_mesh(
- const MeshRenderData *mr, int l, const MLoop *mloop, int p, const MPoly *mpoly, void *data)
+static void extract_lnor_hq_iter_poly_mesh(const MeshRenderData *mr,
+ const ExtractPolyMesh_Params *params,
+ void *data)
{
- gpuHQNor *lnor_data = &((gpuHQNor *)data)[l];
- if (mr->loop_normals) {
- normal_float_to_short_v3(&lnor_data->x, mr->loop_normals[l]);
- }
- else if (mpoly->flag & ME_SMOOTH) {
- copy_v3_v3_short(&lnor_data->x, mr->mvert[mloop->v].no);
- }
- else {
- normal_float_to_short_v3(&lnor_data->x, mr->poly_normals[p]);
- }
+ EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
+ {
+ gpuHQNor *lnor_data = &((gpuHQNor *)data)[ml_index];
+ if (mr->loop_normals) {
+ normal_float_to_short_v3(&lnor_data->x, mr->loop_normals[ml_index]);
+ }
+ else if (mp->flag & ME_SMOOTH) {
+ copy_v3_v3_short(&lnor_data->x, mr->mvert[ml->v].no);
+ }
+ else {
+ normal_float_to_short_v3(&lnor_data->x, mr->poly_normals[mp_index]);
+ }
- /* Flag for paint mode overlay.
- * Only use MR_EXTRACT_MAPPED in edit mode where it is used to display the edge-normals. In
- * paint mode it will use the unmapped data to draw the wireframe. */
- if (mpoly->flag & ME_HIDE ||
- (mr->edit_bmesh && mr->extract_type == MR_EXTRACT_MAPPED && (mr->v_origindex) &&
- mr->v_origindex[mloop->v] == ORIGINDEX_NONE)) {
- lnor_data->w = -1;
- }
- else if (mpoly->flag & ME_FACE_SEL) {
- lnor_data->w = 1;
- }
- else {
- lnor_data->w = 0;
+ /* Flag for paint mode overlay.
+ * Only use #MR_EXTRACT_MAPPED in edit mode where it is used to display the edge-normals.
+ * In paint mode it will use the un-mapped data to draw the wire-frame. */
+ if (mp->flag & ME_HIDE || (mr->edit_bmesh && mr->extract_type == MR_EXTRACT_MAPPED &&
+ (mr->v_origindex) && mr->v_origindex[ml->v] == ORIGINDEX_NONE)) {
+ lnor_data->w = -1;
+ }
+ else if (mp->flag & ME_FACE_SEL) {
+ lnor_data->w = 1;
+ }
+ else {
+ lnor_data->w = 0;
+ }
}
+ EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
}
static const MeshExtract extract_lnor_hq = {
- extract_lnor_hq_init,
- NULL,
- NULL,
- extract_lnor_hq_loop_bmesh,
- extract_lnor_hq_loop_mesh,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- MR_DATA_LOOP_NOR,
- true,
+ .init = extract_lnor_hq_init,
+ .iter_poly_bm = extract_lnor_hq_iter_poly_bm,
+ .iter_poly_mesh = extract_lnor_hq_iter_poly_mesh,
+ .data_flag = MR_DATA_LOOP_NOR,
+ .use_threading = true,
};
/** \} */
@@ -1777,64 +2118,75 @@ static void *extract_lnor_init(const MeshRenderData *mr, void *buf)
return vbo->data;
}
-static void extract_lnor_loop_bmesh(const MeshRenderData *mr, int l, BMLoop *loop, void *data)
+static void extract_lnor_iter_poly_bm(const MeshRenderData *mr,
+ const ExtractPolyBMesh_Params *params,
+ void *data)
{
if (mr->loop_normals) {
- ((GPUPackedNormal *)data)[l] = GPU_normal_convert_i10_v3(mr->loop_normals[l]);
- }
- else if (BM_elem_flag_test(loop->f, BM_ELEM_SMOOTH)) {
- ((GPUPackedNormal *)data)[l] = GPU_normal_convert_i10_v3(bm_vert_no_get(mr, loop->v));
+ EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
+ {
+ ((GPUPackedNormal *)data)[l_index] = GPU_normal_convert_i10_v3(mr->loop_normals[l_index]);
+ BMFace *efa = l->f;
+ ((GPUPackedNormal *)data)[l_index].w = BM_elem_flag_test(efa, BM_ELEM_HIDDEN) ? -1 : 0;
+ }
+ EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
}
else {
- ((GPUPackedNormal *)data)[l] = GPU_normal_convert_i10_v3(bm_face_no_get(mr, loop->f));
+ EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
+ {
+ if (BM_elem_flag_test(l->f, BM_ELEM_SMOOTH)) {
+ ((GPUPackedNormal *)data)[l_index] = GPU_normal_convert_i10_v3(bm_vert_no_get(mr, l->v));
+ }
+ else {
+ ((GPUPackedNormal *)data)[l_index] = GPU_normal_convert_i10_v3(bm_face_no_get(mr, l->f));
+ }
+ BMFace *efa = l->f;
+ ((GPUPackedNormal *)data)[l_index].w = BM_elem_flag_test(efa, BM_ELEM_HIDDEN) ? -1 : 0;
+ }
+ EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
}
- BMFace *efa = loop->f;
- ((GPUPackedNormal *)data)[l].w = BM_elem_flag_test(efa, BM_ELEM_HIDDEN) ? -1 : 0;
}
-static void extract_lnor_loop_mesh(
- const MeshRenderData *mr, int l, const MLoop *mloop, int p, const MPoly *mpoly, void *data)
+static void extract_lnor_iter_poly_mesh(const MeshRenderData *mr,
+ const ExtractPolyMesh_Params *params,
+ void *data)
{
- GPUPackedNormal *lnor_data = &((GPUPackedNormal *)data)[l];
- if (mr->loop_normals) {
- *lnor_data = GPU_normal_convert_i10_v3(mr->loop_normals[l]);
- }
- else if (mpoly->flag & ME_SMOOTH) {
- *lnor_data = GPU_normal_convert_i10_s3(mr->mvert[mloop->v].no);
- }
- else {
- *lnor_data = GPU_normal_convert_i10_v3(mr->poly_normals[p]);
- }
+ EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
+ {
+ GPUPackedNormal *lnor_data = &((GPUPackedNormal *)data)[ml_index];
+ if (mr->loop_normals) {
+ *lnor_data = GPU_normal_convert_i10_v3(mr->loop_normals[ml_index]);
+ }
+ else if (mp->flag & ME_SMOOTH) {
+ *lnor_data = GPU_normal_convert_i10_s3(mr->mvert[ml->v].no);
+ }
+ else {
+ *lnor_data = GPU_normal_convert_i10_v3(mr->poly_normals[mp_index]);
+ }
- /* Flag for paint mode overlay.
- * Only use MR_EXTRACT_MAPPED in edit mode where it is used to display the edge-normals. In
- * paint mode it will use the unmapped data to draw the wireframe. */
- if (mpoly->flag & ME_HIDE ||
- (mr->edit_bmesh && mr->extract_type == MR_EXTRACT_MAPPED && (mr->v_origindex) &&
- mr->v_origindex[mloop->v] == ORIGINDEX_NONE)) {
- lnor_data->w = -1;
- }
- else if (mpoly->flag & ME_FACE_SEL) {
- lnor_data->w = 1;
- }
- else {
- lnor_data->w = 0;
+ /* Flag for paint mode overlay.
+ * Only use MR_EXTRACT_MAPPED in edit mode where it is used to display the edge-normals.
+ * In paint mode it will use the un-mapped data to draw the wire-frame. */
+ if (mp->flag & ME_HIDE || (mr->edit_bmesh && mr->extract_type == MR_EXTRACT_MAPPED &&
+ (mr->v_origindex) && mr->v_origindex[ml->v] == ORIGINDEX_NONE)) {
+ lnor_data->w = -1;
+ }
+ else if (mp->flag & ME_FACE_SEL) {
+ lnor_data->w = 1;
+ }
+ else {
+ lnor_data->w = 0;
+ }
}
+ EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
}
static const MeshExtract extract_lnor = {
- extract_lnor_init,
- NULL,
- NULL,
- extract_lnor_loop_bmesh,
- extract_lnor_loop_mesh,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- MR_DATA_LOOP_NOR,
- true,
+ .init = extract_lnor_init,
+ .iter_poly_bm = extract_lnor_iter_poly_bm,
+ .iter_poly_mesh = extract_lnor_iter_poly_mesh,
+ .data_flag = MR_DATA_LOOP_NOR,
+ .use_threading = true,
};
/** \} */
@@ -1878,7 +2230,7 @@ static void *extract_uv_init(const MeshRenderData *mr, void *buf)
/* Active display layer name. */
if (i == CustomData_get_active_layer(cd_ldata, CD_MLOOPUV)) {
GPU_vertformat_alias_add(&format, "au");
- /* Alias to pos for edit uvs. */
+ /* Alias to `pos` for edit uvs. */
GPU_vertformat_alias_add(&format, "pos");
}
/* Stencil mask uv layer name. */
@@ -1904,20 +2256,21 @@ static void *extract_uv_init(const MeshRenderData *mr, void *buf)
if (uv_layers & (1 << i)) {
if (mr->extract_type == MR_EXTRACT_BMESH) {
int cd_ofs = CustomData_get_n_offset(cd_ldata, CD_MLOOPUV, i);
- BMIter f_iter, l_iter;
+ BMIter f_iter;
BMFace *efa;
- BMLoop *loop;
BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) {
- BM_ITER_ELEM (loop, &l_iter, efa, BM_LOOPS_OF_FACE) {
- MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(loop, cd_ofs);
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
+ do {
+ MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l_iter, cd_ofs);
memcpy(uv_data, luv->uv, sizeof(*uv_data));
uv_data++;
- }
+ } while ((l_iter = l_iter->next) != l_first);
}
}
else {
MLoopUV *layer_data = CustomData_get_layer_n(cd_ldata, CD_MLOOPUV, i);
- for (int l = 0; l < mr->loop_len; l++, uv_data++, layer_data++) {
+ for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, uv_data++, layer_data++) {
memcpy(uv_data, layer_data->uv, sizeof(*uv_data));
}
}
@@ -1928,18 +2281,9 @@ static void *extract_uv_init(const MeshRenderData *mr, void *buf)
}
static const MeshExtract extract_uv = {
- extract_uv_init,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- 0,
- false,
+ .init = extract_uv_init,
+ .data_flag = 0,
+ .use_threading = false,
};
/** \} */
@@ -1987,7 +2331,7 @@ static void extract_tan_ex(const MeshRenderData *mr, GPUVertBuf *vbo, const bool
}
}
if (use_orco_tan && orco == NULL) {
- /* If orco is not available compute it ourselves */
+ /* If `orco` is not available compute it ourselves */
orco_allocated = true;
orco = MEM_mallocN(sizeof(*orco) * mr->vert_len, __func__);
@@ -2001,17 +2345,18 @@ static void extract_tan_ex(const MeshRenderData *mr, GPUVertBuf *vbo, const bool
}
}
else {
- const MVert *mvert = mr->mvert;
- for (int v = 0; v < mr->vert_len; v++, mvert++) {
- copy_v3_v3(orco[v], mvert->co);
+ const MVert *mv = mr->mvert;
+ for (int v = 0; v < mr->vert_len; v++, mv++) {
+ copy_v3_v3(orco[v], mv->co);
}
}
BKE_mesh_orco_verts_transform(mr->me, orco, mr->vert_len, 0);
}
/* Start Fresh */
- CustomData_free_layers(cd_ldata, CD_TANGENT, mr->loop_len);
-
+ CustomData loop_data;
+ CustomData_reset(&loop_data);
+ CustomData *ldata = cd_ldata;
if (tan_len != 0 || use_orco_tan) {
short tangent_mask = 0;
bool calc_active_tangent = false;
@@ -2041,9 +2386,10 @@ static void extract_tan_ex(const MeshRenderData *mr, GPUVertBuf *vbo, const bool
mr->poly_normals,
mr->loop_normals,
orco,
- cd_ldata,
+ &loop_data,
mr->loop_len,
&tangent_mask);
+ ldata = &loop_data;
}
}
@@ -2075,18 +2421,18 @@ static void extract_tan_ex(const MeshRenderData *mr, GPUVertBuf *vbo, const bool
short(*tan_data)[4] = (short(*)[4])vbo->data;
for (int i = 0; i < tan_len; i++) {
const char *name = tangent_names[i];
- float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_named(cd_ldata, CD_TANGENT, name);
- for (int l = 0; l < mr->loop_len; l++) {
- normal_float_to_short_v3(*tan_data, layer_data[l]);
- (*tan_data)[3] = (layer_data[l][3] > 0.0f) ? SHRT_MAX : SHRT_MIN;
+ float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_named(ldata, CD_TANGENT, name);
+ for (int ml_index = 0; ml_index < mr->loop_len; ml_index++) {
+ normal_float_to_short_v3(*tan_data, layer_data[ml_index]);
+ (*tan_data)[3] = (layer_data[ml_index][3] > 0.0f) ? SHRT_MAX : SHRT_MIN;
tan_data++;
}
}
if (use_orco_tan) {
- float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_n(cd_ldata, CD_TANGENT, 0);
- for (int l = 0; l < mr->loop_len; l++) {
- normal_float_to_short_v3(*tan_data, layer_data[l]);
- (*tan_data)[3] = (layer_data[l][3] > 0.0f) ? SHRT_MAX : SHRT_MIN;
+ float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_n(ldata, CD_TANGENT, 0);
+ for (int ml_index = 0; ml_index < mr->loop_len; ml_index++) {
+ normal_float_to_short_v3(*tan_data, layer_data[ml_index]);
+ (*tan_data)[3] = (layer_data[ml_index][3] > 0.0f) ? SHRT_MAX : SHRT_MIN;
tan_data++;
}
}
@@ -2095,24 +2441,25 @@ static void extract_tan_ex(const MeshRenderData *mr, GPUVertBuf *vbo, const bool
GPUPackedNormal *tan_data = (GPUPackedNormal *)vbo->data;
for (int i = 0; i < tan_len; i++) {
const char *name = tangent_names[i];
- float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_named(cd_ldata, CD_TANGENT, name);
- for (int l = 0; l < mr->loop_len; l++) {
- *tan_data = GPU_normal_convert_i10_v3(layer_data[l]);
- tan_data->w = (layer_data[l][3] > 0.0f) ? 1 : -2;
+ float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_named(ldata, CD_TANGENT, name);
+ for (int ml_index = 0; ml_index < mr->loop_len; ml_index++) {
+ *tan_data = GPU_normal_convert_i10_v3(layer_data[ml_index]);
+ tan_data->w = (layer_data[ml_index][3] > 0.0f) ? 1 : -2;
tan_data++;
}
}
if (use_orco_tan) {
- float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_n(cd_ldata, CD_TANGENT, 0);
- for (int l = 0; l < mr->loop_len; l++) {
- *tan_data = GPU_normal_convert_i10_v3(layer_data[l]);
- tan_data->w = (layer_data[l][3] > 0.0f) ? 1 : -2;
+ float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_n(ldata, CD_TANGENT, 0);
+ for (int ml_index = 0; ml_index < mr->loop_len; ml_index++) {
+ *tan_data = GPU_normal_convert_i10_v3(layer_data[ml_index]);
+ tan_data->w = (layer_data[ml_index][3] > 0.0f) ? 1 : -2;
tan_data++;
}
}
}
- CustomData_free_layers(cd_ldata, CD_TANGENT, mr->loop_len);
+ CustomData_free_layers(ldata, CD_TANGENT, mr->loop_len);
+ CustomData_free(&loop_data, mr->loop_len);
}
static void *extract_tan_init(const MeshRenderData *mr, void *buf)
@@ -2122,18 +2469,9 @@ static void *extract_tan_init(const MeshRenderData *mr, void *buf)
}
static const MeshExtract extract_tan = {
- extract_tan_init,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | MR_DATA_LOOPTRI,
- false,
+ .init = extract_tan_init,
+ .data_flag = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | MR_DATA_LOOPTRI,
+ .use_threading = false,
};
/** \} */
@@ -2149,18 +2487,9 @@ static void *extract_tan_hq_init(const MeshRenderData *mr, void *buf)
}
static const MeshExtract extract_tan_hq = {
- extract_tan_hq_init,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | MR_DATA_LOOPTRI,
- false,
+ .init = extract_tan_hq_init,
+ .data_flag = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | MR_DATA_LOOPTRI,
+ .use_threading = false,
};
/** \} */
@@ -2175,7 +2504,9 @@ static void *extract_vcol_init(const MeshRenderData *mr, void *buf)
GPU_vertformat_deinterleave(&format);
CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata;
+ CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata;
uint32_t vcol_layers = mr->cache->cd_used.vcol;
+ uint32_t svcol_layers = mr->cache->cd_used.sculpt_vcol;
for (int i = 0; i < MAX_MCOL; i++) {
if (vcol_layers & (1 << i)) {
@@ -2192,14 +2523,42 @@ static void *extract_vcol_init(const MeshRenderData *mr, void *buf)
if (i == CustomData_get_active_layer(cd_ldata, CD_MLOOPCOL)) {
GPU_vertformat_alias_add(&format, "ac");
}
+
/* Gather number of auto layers. */
- /* We only do vcols that are not overridden by uvs */
+ /* We only do `vcols` that are not overridden by `uvs` and sculpt vertex colors. */
+ if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, layer_name) == -1 &&
+ CustomData_get_named_layer_index(cd_vdata, CD_PROP_COLOR, layer_name) == -1) {
+ BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name);
+ GPU_vertformat_alias_add(&format, attr_name);
+ }
+ }
+ }
+
+ /* Sculpt Vertex Colors */
+ for (int i = 0; i < 8; i++) {
+ if (svcol_layers & (1 << i)) {
+ char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME];
+ const char *layer_name = CustomData_get_layer_name(cd_vdata, CD_PROP_COLOR, i);
+ GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME);
+
+ BLI_snprintf(attr_name, sizeof(attr_name), "c%s", attr_safe_name);
+ GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_U16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
+
+ if (i == CustomData_get_render_layer(cd_vdata, CD_PROP_COLOR)) {
+ GPU_vertformat_alias_add(&format, "c");
+ }
+ if (i == CustomData_get_active_layer(cd_vdata, CD_PROP_COLOR)) {
+ GPU_vertformat_alias_add(&format, "ac");
+ }
+ /* Gather number of auto layers. */
+ /* We only do `vcols` that are not overridden by `uvs`. */
if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, layer_name) == -1) {
BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name);
GPU_vertformat_alias_add(&format, attr_name);
}
}
}
+
GPUVertBuf *vbo = buf;
GPU_vertbuf_init_with_format(vbo, &format);
GPU_vertbuf_data_alloc(vbo, mr->loop_len);
@@ -2209,27 +2568,30 @@ static void *extract_vcol_init(const MeshRenderData *mr, void *buf)
} gpuMeshVcol;
gpuMeshVcol *vcol_data = (gpuMeshVcol *)vbo->data;
+ MLoop *loops = CustomData_get_layer(cd_ldata, CD_MLOOP);
+
for (int i = 0; i < MAX_MCOL; i++) {
if (vcol_layers & (1 << i)) {
if (mr->extract_type == MR_EXTRACT_BMESH) {
int cd_ofs = CustomData_get_n_offset(cd_ldata, CD_MLOOPCOL, i);
- BMIter f_iter, l_iter;
+ BMIter f_iter;
BMFace *efa;
- BMLoop *loop;
BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) {
- BM_ITER_ELEM (loop, &l_iter, efa, BM_LOOPS_OF_FACE) {
- const MLoopCol *mloopcol = BM_ELEM_CD_GET_VOID_P(loop, cd_ofs);
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
+ do {
+ const MLoopCol *mloopcol = BM_ELEM_CD_GET_VOID_P(l_iter, cd_ofs);
vcol_data->r = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->r]);
vcol_data->g = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->g]);
vcol_data->b = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->b]);
vcol_data->a = unit_float_to_ushort_clamp(mloopcol->a * (1.0f / 255.0f));
vcol_data++;
- }
+ } while ((l_iter = l_iter->next) != l_first);
}
}
else {
const MLoopCol *mloopcol = (MLoopCol *)CustomData_get_layer_n(cd_ldata, CD_MLOOPCOL, i);
- for (int l = 0; l < mr->loop_len; l++, mloopcol++, vcol_data++) {
+ for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, mloopcol++, vcol_data++) {
vcol_data->r = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->r]);
vcol_data->g = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->g]);
vcol_data->b = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->b]);
@@ -2237,22 +2599,45 @@ static void *extract_vcol_init(const MeshRenderData *mr, void *buf)
}
}
}
+
+ if (svcol_layers & (1 << i)) {
+ if (mr->extract_type == MR_EXTRACT_BMESH) {
+ int cd_ofs = CustomData_get_n_offset(cd_vdata, CD_PROP_COLOR, i);
+ BMIter f_iter;
+ BMFace *efa;
+ BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) {
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
+ do {
+ const MPropCol *prop_col = BM_ELEM_CD_GET_VOID_P(l_iter->v, cd_ofs);
+ vcol_data->r = unit_float_to_ushort_clamp(prop_col->color[0]);
+ vcol_data->g = unit_float_to_ushort_clamp(prop_col->color[1]);
+ vcol_data->b = unit_float_to_ushort_clamp(prop_col->color[2]);
+ vcol_data->a = unit_float_to_ushort_clamp(prop_col->color[3]);
+ vcol_data++;
+ } while ((l_iter = l_iter->next) != l_first);
+ }
+ }
+ else {
+ MPropCol *vcol = CustomData_get_layer_n(cd_vdata, CD_PROP_COLOR, i);
+ for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, vcol_data++) {
+ vcol_data->r = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[0]);
+ vcol_data->g = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[1]);
+ vcol_data->b = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[2]);
+ vcol_data->a = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[3]);
+ }
+ }
+
+ vcol_data += mr->loop_len;
+ }
}
return NULL;
}
static const MeshExtract extract_vcol = {
- extract_vcol_init,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- 0,
- false,
+ .init = extract_vcol_init,
+ .data_flag = 0,
+ .use_threading = false,
};
/** \} */
@@ -2271,7 +2656,7 @@ static void *extract_orco_init(const MeshRenderData *mr, void *buf)
static GPUVertFormat format = {0};
if (format.attr_len == 0) {
/* FIXME(fclem): We use the last component as a way to differentiate from generic vertex
- * attributes. This is a substantial waste of Vram and should be done another way.
+ * attributes. This is a substantial waste of video-ram and should be done another way.
* Unfortunately, at the time of writing, I did not found any other "non disruptive"
* alternative. */
GPU_vertformat_attr_add(&format, "orco", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
@@ -2286,33 +2671,37 @@ static void *extract_orco_init(const MeshRenderData *mr, void *buf)
MeshExtract_Orco_Data *data = MEM_mallocN(sizeof(*data), __func__);
data->vbo_data = (float(*)[4])vbo->data;
data->orco = CustomData_get_layer(cd_vdata, CD_ORCO);
- /* Make sure orco layer was requested only if needed! */
+ /* Make sure `orco` layer was requested only if needed! */
BLI_assert(data->orco);
return data;
}
-static void extract_orco_loop_bmesh(const MeshRenderData *UNUSED(mr),
- int l,
- BMLoop *loop,
- void *data)
+static void extract_orco_iter_poly_bm(const MeshRenderData *mr,
+ const ExtractPolyBMesh_Params *params,
+ void *data)
{
MeshExtract_Orco_Data *orco_data = (MeshExtract_Orco_Data *)data;
- float *loop_orco = orco_data->vbo_data[l];
- copy_v3_v3(loop_orco, orco_data->orco[BM_elem_index_get(loop->v)]);
- loop_orco[3] = 0.0; /* Tag as not a generic attribute. */
+ EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(loop, l_index, params, mr)
+ {
+ float *loop_orco = orco_data->vbo_data[l_index];
+ copy_v3_v3(loop_orco, orco_data->orco[BM_elem_index_get(loop->v)]);
+ loop_orco[3] = 0.0; /* Tag as not a generic attribute. */
+ }
+ EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(loop);
}
-static void extract_orco_loop_mesh(const MeshRenderData *UNUSED(mr),
- int l,
- const MLoop *mloop,
- int UNUSED(p),
- const MPoly *UNUSED(mpoly),
- void *data)
+static void extract_orco_iter_poly_mesh(const MeshRenderData *mr,
+ const ExtractPolyMesh_Params *params,
+ void *data)
{
- MeshExtract_Orco_Data *orco_data = (MeshExtract_Orco_Data *)data;
- float *loop_orco = orco_data->vbo_data[l];
- copy_v3_v3(loop_orco, orco_data->orco[mloop->v]);
- loop_orco[3] = 0.0; /* Tag as not a generic attribute. */
+ EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
+ {
+ MeshExtract_Orco_Data *orco_data = (MeshExtract_Orco_Data *)data;
+ float *loop_orco = orco_data->vbo_data[ml_index];
+ copy_v3_v3(loop_orco, orco_data->orco[ml->v]);
+ loop_orco[3] = 0.0; /* Tag as not a generic attribute. */
+ }
+ EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
}
static void extract_orco_finish(const MeshRenderData *UNUSED(mr), void *UNUSED(buf), void *data)
@@ -2321,18 +2710,12 @@ static void extract_orco_finish(const MeshRenderData *UNUSED(mr), void *UNUSED(b
}
static const MeshExtract extract_orco = {
- extract_orco_init,
- NULL,
- NULL,
- extract_orco_loop_bmesh,
- extract_orco_loop_mesh,
- NULL,
- NULL,
- NULL,
- NULL,
- extract_orco_finish,
- 0,
- true,
+ .init = extract_orco_init,
+ .iter_poly_bm = extract_orco_iter_poly_bm,
+ .iter_poly_mesh = extract_orco_iter_poly_mesh,
+ .finish = extract_orco_finish,
+ .data_flag = 0,
+ .use_threading = true,
};
/** \} */
@@ -2359,7 +2742,7 @@ static float loop_edge_factor_get(const float f_no[3],
cross_v3_v3v3(enor, v_no, evec);
normalize_v3(enor);
float d = fabsf(dot_v3v3(enor, f_no));
- /* Rescale to the slider range. */
+ /* Re-scale to the slider range. */
d *= (1.0f / 0.065f);
CLAMP(d, 0.0f, 1.0f);
return d;
@@ -2383,9 +2766,9 @@ static void *extract_edge_fac_init(const MeshRenderData *mr, void *buf)
/* HACK(fclem) Detecting the need for edge render.
* We could have a flag in the mesh instead or check the modifier stack. */
- const MEdge *medge = mr->medge;
- for (int e = 0; e < mr->edge_len; e++, medge++) {
- if ((medge->flag & ME_EDGERENDER) == 0) {
+ const MEdge *med = mr->medge;
+ for (int e_index = 0; e_index < mr->edge_len; e_index++, med++) {
+ if ((med->flag & ME_EDGERENDER) == 0) {
data->use_edge_render = true;
break;
}
@@ -2401,81 +2784,103 @@ static void *extract_edge_fac_init(const MeshRenderData *mr, void *buf)
return data;
}
-static void extract_edge_fac_loop_bmesh(const MeshRenderData *mr, int l, BMLoop *loop, void *_data)
+static void extract_edge_fac_iter_poly_bm(const MeshRenderData *mr,
+ const ExtractPolyBMesh_Params *params,
+ void *_data)
{
- MeshExtract_EdgeFac_Data *data = (MeshExtract_EdgeFac_Data *)_data;
- if (BM_edge_is_manifold(loop->e)) {
- float ratio = loop_edge_factor_get(bm_face_no_get(mr, loop->f),
- bm_vert_co_get(mr, loop->v),
- bm_vert_no_get(mr, loop->v),
- bm_vert_co_get(mr, loop->next->v));
- data->vbo_data[l] = ratio * 253 + 1;
- }
- else {
- data->vbo_data[l] = 255;
+ MeshExtract_EdgeFac_Data *data = _data;
+ EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
+ {
+ if (BM_edge_is_manifold(l->e)) {
+ float ratio = loop_edge_factor_get(bm_face_no_get(mr, l->f),
+ bm_vert_co_get(mr, l->v),
+ bm_vert_no_get(mr, l->v),
+ bm_vert_co_get(mr, l->next->v));
+ data->vbo_data[l_index] = ratio * 253 + 1;
+ }
+ else {
+ data->vbo_data[l_index] = 255;
+ }
}
+ EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
}
-static void extract_edge_fac_loop_mesh(
- const MeshRenderData *mr, int l, const MLoop *mloop, int p, const MPoly *mpoly, void *_data)
+static void extract_edge_fac_iter_poly_mesh(const MeshRenderData *mr,
+ const ExtractPolyMesh_Params *params,
+ void *_data)
{
MeshExtract_EdgeFac_Data *data = (MeshExtract_EdgeFac_Data *)_data;
+
if (data->use_edge_render) {
- const MEdge *medge = &mr->medge[mloop->e];
- data->vbo_data[l] = (medge->flag & ME_EDGERENDER) ? 255 : 0;
+ EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
+ {
+ const MEdge *med = &mr->medge[ml->e];
+ data->vbo_data[ml_index] = (med->flag & ME_EDGERENDER) ? 255 : 0;
+ }
+ EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
}
else {
- /* Count loop per edge to detect non-manifold. */
- if (data->edge_loop_count[mloop->e] < 3) {
- data->edge_loop_count[mloop->e]++;
- }
- if (data->edge_loop_count[mloop->e] == 2) {
- /* Manifold */
- int loopend = mpoly->totloop + mpoly->loopstart - 1;
- int other_loop = (l == loopend) ? mpoly->loopstart : (l + 1);
- const MLoop *mloop_next = &mr->mloop[other_loop];
- const MVert *v1 = &mr->mvert[mloop->v];
- const MVert *v2 = &mr->mvert[mloop_next->v];
- float vnor_f[3];
- normal_short_to_float_v3(vnor_f, v1->no);
- float ratio = loop_edge_factor_get(mr->poly_normals[p], v1->co, vnor_f, v2->co);
- data->vbo_data[l] = ratio * 253 + 1;
- }
- else {
- /* Non-manifold */
- data->vbo_data[l] = 255;
+ EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
+ {
+ /* Count loop per edge to detect non-manifold. */
+ if (data->edge_loop_count[ml->e] < 3) {
+ data->edge_loop_count[ml->e]++;
+ }
+ if (data->edge_loop_count[ml->e] == 2) {
+ /* Manifold */
+ const int ml_index_last = mp->totloop + mp->loopstart - 1;
+ const int ml_index_other = (ml_index == ml_index_last) ? mp->loopstart : (ml_index + 1);
+ const MLoop *ml_next = &mr->mloop[ml_index_other];
+ const MVert *v1 = &mr->mvert[ml->v];
+ const MVert *v2 = &mr->mvert[ml_next->v];
+ float vnor_f[3];
+ normal_short_to_float_v3(vnor_f, v1->no);
+ float ratio = loop_edge_factor_get(mr->poly_normals[mp_index], v1->co, vnor_f, v2->co);
+ data->vbo_data[ml_index] = ratio * 253 + 1;
+ }
+ else {
+ /* Non-manifold */
+ data->vbo_data[ml_index] = 255;
+ }
}
+ EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
}
}
-static void extract_edge_fac_ledge_bmesh(const MeshRenderData *mr,
- int e,
- BMEdge *UNUSED(eed),
- void *_data)
+static void extract_edge_fac_iter_ledge_bm(const MeshRenderData *mr,
+ const ExtractLEdgeBMesh_Params *params,
+ void *_data)
{
- MeshExtract_EdgeFac_Data *data = (MeshExtract_EdgeFac_Data *)_data;
- data->vbo_data[mr->loop_len + e * 2 + 0] = 255;
- data->vbo_data[mr->loop_len + e * 2 + 1] = 255;
+ MeshExtract_EdgeFac_Data *data = _data;
+ EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params)
+ {
+ data->vbo_data[mr->loop_len + (ledge_index * 2) + 0] = 255;
+ data->vbo_data[mr->loop_len + (ledge_index * 2) + 1] = 255;
+ }
+ EXTRACT_LEDGE_FOREACH_BM_END;
}
-static void extract_edge_fac_ledge_mesh(const MeshRenderData *mr,
- int e,
- const MEdge *UNUSED(edge),
- void *_data)
+static void extract_edge_fac_iter_ledge_mesh(const MeshRenderData *mr,
+ const ExtractLEdgeMesh_Params *params,
+ void *_data)
{
- MeshExtract_EdgeFac_Data *data = (MeshExtract_EdgeFac_Data *)_data;
- data->vbo_data[mr->loop_len + e * 2 + 0] = 255;
- data->vbo_data[mr->loop_len + e * 2 + 1] = 255;
+ MeshExtract_EdgeFac_Data *data = _data;
+ EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr)
+ {
+ data->vbo_data[mr->loop_len + ledge_index * 2 + 0] = 255;
+ data->vbo_data[mr->loop_len + ledge_index * 2 + 1] = 255;
+ }
+ EXTRACT_LEDGE_FOREACH_MESH_END;
}
static void extract_edge_fac_finish(const MeshRenderData *mr, void *buf, void *_data)
{
- MeshExtract_EdgeFac_Data *data = (MeshExtract_EdgeFac_Data *)_data;
+ MeshExtract_EdgeFac_Data *data = _data;
if (GPU_crappy_amd_driver()) {
GPUVertBuf *vbo = (GPUVertBuf *)buf;
- /* Some AMD drivers strangely crash with VBOs with a one byte format.
- * To workaround we reinit the vbo with another format and convert
+ /* Some AMD drivers strangely crash with VBO's with a one byte format.
+ * To workaround we reinitialize the VBO with another format and convert
* all bytes to floats. */
static GPUVertFormat format = {0};
if (format.attr_len == 0) {
@@ -2490,8 +2895,8 @@ static void extract_edge_fac_finish(const MeshRenderData *mr, void *buf, void *_
GPU_vertbuf_data_alloc(vbo, buf_len);
float *fdata = (float *)vbo->data;
- for (int l = 0; l < buf_len; l++, fdata++) {
- *fdata = data->vbo_data[l] / 255.0f;
+ for (int ml_index = 0; ml_index < buf_len; ml_index++, fdata++) {
+ *fdata = data->vbo_data[ml_index] / 255.0f;
}
/* Free old byte data. */
MEM_freeN(data->vbo_data);
@@ -2500,18 +2905,14 @@ static void extract_edge_fac_finish(const MeshRenderData *mr, void *buf, void *_
}
static const MeshExtract extract_edge_fac = {
- extract_edge_fac_init,
- NULL,
- NULL,
- extract_edge_fac_loop_bmesh,
- extract_edge_fac_loop_mesh,
- extract_edge_fac_ledge_bmesh,
- extract_edge_fac_ledge_mesh,
- NULL,
- NULL,
- extract_edge_fac_finish,
- MR_DATA_POLY_NOR,
- false,
+ .init = extract_edge_fac_init,
+ .iter_poly_bm = extract_edge_fac_iter_poly_bm,
+ .iter_poly_mesh = extract_edge_fac_iter_poly_mesh,
+ .iter_ledge_bm = extract_edge_fac_iter_ledge_bm,
+ .iter_ledge_mesh = extract_edge_fac_iter_ledge_mesh,
+ .finish = extract_edge_fac_finish,
+ .data_flag = MR_DATA_POLY_NOR,
+ .use_threading = false,
};
/** \} */
@@ -2522,8 +2923,8 @@ static const MeshExtract extract_edge_fac = {
typedef struct MeshExtract_Weight_Data {
float *vbo_data;
const DRW_MeshWeightState *wstate;
- const MDeformVert *dvert; /* For Mesh. */
- int cd_ofs; /* For BMesh. */
+ const MDeformVert *dvert; /* For #Mesh. */
+ int cd_ofs; /* For #BMesh. */
} MeshExtract_Weight_Data;
static float evaluate_vertex_weight(const MDeformVert *dvert, const DRW_MeshWeightState *wstate)
@@ -2609,27 +3010,49 @@ static void *extract_weights_init(const MeshRenderData *mr, void *buf)
return data;
}
-static void extract_weights_loop_bmesh(const MeshRenderData *UNUSED(mr),
- int l,
- BMLoop *loop,
- void *_data)
+static void extract_weights_iter_poly_bm(const MeshRenderData *mr,
+ const ExtractPolyBMesh_Params *params,
+ void *_data)
{
- MeshExtract_Weight_Data *data = (MeshExtract_Weight_Data *)_data;
- const MDeformVert *dvert = (data->cd_ofs != -1) ? BM_ELEM_CD_GET_VOID_P(loop->v, data->cd_ofs) :
- NULL;
- data->vbo_data[l] = evaluate_vertex_weight(dvert, data->wstate);
+ MeshExtract_Weight_Data *data = _data;
+ if (data->cd_ofs != -1) {
+ EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
+ {
+ const MDeformVert *dvert = BM_ELEM_CD_GET_VOID_P(l->v, data->cd_ofs);
+ data->vbo_data[l_index] = evaluate_vertex_weight(dvert, data->wstate);
+ }
+ EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
+ }
+ else {
+ EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
+ {
+ data->vbo_data[l_index] = evaluate_vertex_weight(NULL, data->wstate);
+ }
+ EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
+ }
}
-static void extract_weights_loop_mesh(const MeshRenderData *UNUSED(mr),
- int l,
- const MLoop *mloop,
- int UNUSED(p),
- const MPoly *UNUSED(mpoly),
- void *_data)
+static void extract_weights_iter_poly_mesh(const MeshRenderData *mr,
+ const ExtractPolyMesh_Params *params,
+ void *_data)
{
- MeshExtract_Weight_Data *data = (MeshExtract_Weight_Data *)_data;
- const MDeformVert *dvert = data->dvert ? &data->dvert[mloop->v] : NULL;
- data->vbo_data[l] = evaluate_vertex_weight(dvert, data->wstate);
+ MeshExtract_Weight_Data *data = _data;
+ if (data->dvert != NULL) {
+ EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
+ {
+ const MDeformVert *dvert = &data->dvert[ml->v];
+ data->vbo_data[ml_index] = evaluate_vertex_weight(dvert, data->wstate);
+ }
+ EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
+ }
+ else {
+ const MDeformVert *dvert = NULL;
+ EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
+ {
+ data->vbo_data[ml_index] = evaluate_vertex_weight(dvert, data->wstate);
+ }
+ EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
+ }
}
static void extract_weights_finish(const MeshRenderData *UNUSED(mr), void *UNUSED(buf), void *data)
@@ -2638,18 +3061,12 @@ static void extract_weights_finish(const MeshRenderData *UNUSED(mr), void *UNUSE
}
static const MeshExtract extract_weights = {
- extract_weights_init,
- NULL,
- NULL,
- extract_weights_loop_bmesh,
- extract_weights_loop_mesh,
- NULL,
- NULL,
- NULL,
- NULL,
- extract_weights_finish,
- 0,
- true,
+ .init = extract_weights_init,
+ .iter_poly_bm = extract_weights_iter_poly_bm,
+ .iter_poly_mesh = extract_weights_iter_poly_mesh,
+ .finish = extract_weights_finish,
+ .data_flag = 0,
+ .use_threading = true,
};
/** \} */
@@ -2756,31 +3173,31 @@ static void mesh_render_data_edge_flag(const MeshRenderData *mr, BMEdge *eed, Ed
}
static void mesh_render_data_loop_flag(const MeshRenderData *mr,
- BMLoop *loop,
+ BMLoop *l,
const int cd_ofs,
EditLoopData *eattr)
{
if (cd_ofs == -1) {
return;
}
- MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(loop, cd_ofs);
+ MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_ofs);
if (luv != NULL && (luv->flag & MLOOPUV_PINNED)) {
eattr->v_flag |= VFLAG_VERT_UV_PINNED;
}
- if (uvedit_uv_select_test_ex(mr->toolsettings, loop, cd_ofs)) {
+ if (uvedit_uv_select_test_ex(mr->toolsettings, l, cd_ofs)) {
eattr->v_flag |= VFLAG_VERT_UV_SELECT;
}
}
static void mesh_render_data_loop_edge_flag(const MeshRenderData *mr,
- BMLoop *loop,
+ BMLoop *l,
const int cd_ofs,
EditLoopData *eattr)
{
if (cd_ofs == -1) {
return;
}
- if (uvedit_edge_select_test_ex(mr->toolsettings, loop, cd_ofs)) {
+ if (uvedit_edge_select_test_ex(mr->toolsettings, l, cd_ofs)) {
eattr->v_flag |= VFLAG_EDGE_UV_SELECT;
eattr->v_flag |= VFLAG_VERT_UV_SELECT;
}
@@ -2800,7 +3217,7 @@ static void *extract_edit_data_init(const MeshRenderData *mr, void *buf)
{
static GPUVertFormat format = {0};
if (format.attr_len == 0) {
- /* WARNING Adjust EditLoopData struct accordingly. */
+ /* WARNING: Adjust #EditLoopData struct accordingly. */
GPU_vertformat_attr_add(&format, "data", GPU_COMP_U8, 4, GPU_FETCH_INT);
GPU_vertformat_alias_add(&format, "flag");
}
@@ -2810,114 +3227,130 @@ static void *extract_edit_data_init(const MeshRenderData *mr, void *buf)
return vbo->data;
}
-static void extract_edit_data_loop_bmesh(const MeshRenderData *mr,
- int l,
- BMLoop *loop,
- void *_data)
+static void extract_edit_data_iter_poly_bm(const MeshRenderData *mr,
+ const ExtractPolyBMesh_Params *params,
+ void *_data)
{
- EditLoopData *data = (EditLoopData *)_data + l;
- memset(data, 0x0, sizeof(*data));
- mesh_render_data_face_flag(mr, loop->f, -1, data);
- mesh_render_data_edge_flag(mr, loop->e, data);
- mesh_render_data_vert_flag(mr, loop->v, data);
-}
-static void extract_edit_data_loop_mesh(const MeshRenderData *mr,
- int l,
- const MLoop *mloop,
- int p,
- const MPoly *UNUSED(mpoly),
- void *_data)
-{
- EditLoopData *data = (EditLoopData *)_data + l;
- memset(data, 0x0, sizeof(*data));
- BMFace *efa = bm_original_face_get(mr, p);
- BMEdge *eed = bm_original_edge_get(mr, mloop->e);
- BMVert *eve = bm_original_vert_get(mr, mloop->v);
- if (efa) {
- mesh_render_data_face_flag(mr, efa, -1, data);
- }
- if (eed) {
- mesh_render_data_edge_flag(mr, eed, data);
- }
- if (eve) {
- mesh_render_data_vert_flag(mr, eve, data);
+ EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
+ {
+ EditLoopData *data = (EditLoopData *)_data + l_index;
+ memset(data, 0x0, sizeof(*data));
+ mesh_render_data_face_flag(mr, l->f, -1, data);
+ mesh_render_data_edge_flag(mr, l->e, data);
+ mesh_render_data_vert_flag(mr, l->v, data);
}
+ EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
}
-static void extract_edit_data_ledge_bmesh(const MeshRenderData *mr,
- int e,
- BMEdge *eed,
- void *_data)
+static void extract_edit_data_iter_poly_mesh(const MeshRenderData *mr,
+ const ExtractPolyMesh_Params *params,
+ void *_data)
{
- EditLoopData *data = (EditLoopData *)_data + mr->loop_len + e * 2;
- memset(data, 0x0, sizeof(*data) * 2);
- mesh_render_data_edge_flag(mr, eed, &data[0]);
- data[1] = data[0];
- mesh_render_data_vert_flag(mr, eed->v1, &data[0]);
- mesh_render_data_vert_flag(mr, eed->v2, &data[1]);
+ EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
+ {
+ EditLoopData *data = (EditLoopData *)_data + ml_index;
+ memset(data, 0x0, sizeof(*data));
+ BMFace *efa = bm_original_face_get(mr, mp_index);
+ BMEdge *eed = bm_original_edge_get(mr, ml->e);
+ BMVert *eve = bm_original_vert_get(mr, ml->v);
+ if (efa) {
+ mesh_render_data_face_flag(mr, efa, -1, data);
+ }
+ if (eed) {
+ mesh_render_data_edge_flag(mr, eed, data);
+ }
+ if (eve) {
+ mesh_render_data_vert_flag(mr, eve, data);
+ }
+ }
+ EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
}
-static void extract_edit_data_ledge_mesh(const MeshRenderData *mr,
- int e,
- const MEdge *edge,
- void *_data)
+static void extract_edit_data_iter_ledge_bm(const MeshRenderData *mr,
+ const ExtractLEdgeBMesh_Params *params,
+ void *_data)
{
- EditLoopData *data = (EditLoopData *)_data + mr->loop_len + e * 2;
- memset(data, 0x0, sizeof(*data) * 2);
- int e_idx = mr->ledges[e];
- BMEdge *eed = bm_original_edge_get(mr, e_idx);
- BMVert *eve1 = bm_original_vert_get(mr, edge->v1);
- BMVert *eve2 = bm_original_vert_get(mr, edge->v2);
- if (eed) {
+ EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params)
+ {
+ EditLoopData *data = (EditLoopData *)_data + mr->loop_len + (ledge_index * 2);
+ memset(data, 0x0, sizeof(*data) * 2);
mesh_render_data_edge_flag(mr, eed, &data[0]);
data[1] = data[0];
+ mesh_render_data_vert_flag(mr, eed->v1, &data[0]);
+ mesh_render_data_vert_flag(mr, eed->v2, &data[1]);
+ }
+ EXTRACT_LEDGE_FOREACH_BM_END;
+}
+
+static void extract_edit_data_iter_ledge_mesh(const MeshRenderData *mr,
+ const ExtractLEdgeMesh_Params *params,
+ void *_data)
+{
+ EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr)
+ {
+ EditLoopData *data = (EditLoopData *)_data + mr->loop_len + ledge_index * 2;
+ memset(data, 0x0, sizeof(*data) * 2);
+ const int e_index = mr->ledges[ledge_index];
+ BMEdge *eed = bm_original_edge_get(mr, e_index);
+ BMVert *eve1 = bm_original_vert_get(mr, med->v1);
+ BMVert *eve2 = bm_original_vert_get(mr, med->v2);
+ if (eed) {
+ mesh_render_data_edge_flag(mr, eed, &data[0]);
+ data[1] = data[0];
+ }
+ if (eve1) {
+ mesh_render_data_vert_flag(mr, eve1, &data[0]);
+ }
+ if (eve2) {
+ mesh_render_data_vert_flag(mr, eve2, &data[1]);
+ }
}
- if (eve1) {
- mesh_render_data_vert_flag(mr, eve1, &data[0]);
- }
- if (eve2) {
- mesh_render_data_vert_flag(mr, eve2, &data[1]);
- }
+ EXTRACT_LEDGE_FOREACH_MESH_END;
}
-static void extract_edit_data_lvert_bmesh(const MeshRenderData *mr,
- int v,
- BMVert *eve,
- void *_data)
+static void extract_edit_data_iter_lvert_bm(const MeshRenderData *mr,
+ const ExtractLVertBMesh_Params *params,
+ void *_data)
{
- EditLoopData *data = (EditLoopData *)_data + mr->loop_len + mr->edge_loose_len * 2 + v;
- memset(data, 0x0, sizeof(*data));
- mesh_render_data_vert_flag(mr, eve, data);
+ const int offset = mr->loop_len + (mr->edge_loose_len * 2);
+ EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params)
+ {
+ EditLoopData *data = (EditLoopData *)_data + offset + lvert_index;
+ memset(data, 0x0, sizeof(*data));
+ mesh_render_data_vert_flag(mr, eve, data);
+ }
+ EXTRACT_LVERT_FOREACH_BM_END;
}
-static void extract_edit_data_lvert_mesh(const MeshRenderData *mr,
- int v,
- const MVert *UNUSED(mvert),
- void *_data)
+static void extract_edit_data_iter_lvert_mesh(const MeshRenderData *mr,
+ const ExtractLVertMesh_Params *params,
+ void *_data)
{
- EditLoopData *data = (EditLoopData *)_data + mr->loop_len + mr->edge_loose_len * 2 + v;
- memset(data, 0x0, sizeof(*data));
- int v_idx = mr->lverts[v];
- BMVert *eve = bm_original_vert_get(mr, v_idx);
- if (eve) {
- mesh_render_data_vert_flag(mr, eve, data);
+ const int offset = mr->loop_len + (mr->edge_loose_len * 2);
+ EXTRACT_LVERT_FOREACH_MESH_BEGIN(mv, lvert_index, params, mr)
+ {
+ EditLoopData *data = (EditLoopData *)_data + offset + lvert_index;
+ memset(data, 0x0, sizeof(*data));
+ const int v_index = mr->lverts[lvert_index];
+ BMVert *eve = bm_original_vert_get(mr, v_index);
+ if (eve) {
+ mesh_render_data_vert_flag(mr, eve, data);
+ }
}
+ EXTRACT_LVERT_FOREACH_MESH_END;
}
static const MeshExtract extract_edit_data = {
- extract_edit_data_init,
- NULL,
- NULL,
- extract_edit_data_loop_bmesh,
- extract_edit_data_loop_mesh,
- extract_edit_data_ledge_bmesh,
- extract_edit_data_ledge_mesh,
- extract_edit_data_lvert_bmesh,
- extract_edit_data_lvert_mesh,
- NULL,
- 0,
- true,
+ .init = extract_edit_data_init,
+ .iter_poly_bm = extract_edit_data_iter_poly_bm,
+ .iter_poly_mesh = extract_edit_data_iter_poly_mesh,
+ .iter_ledge_bm = extract_edit_data_iter_ledge_bm,
+ .iter_ledge_mesh = extract_edit_data_iter_ledge_mesh,
+ .iter_lvert_bm = extract_edit_data_iter_lvert_bm,
+ .iter_lvert_mesh = extract_edit_data_iter_lvert_mesh,
+ .data_flag = 0,
+ .use_threading = true,
};
/** \} */
@@ -2935,7 +3368,7 @@ static void *extract_edituv_data_init(const MeshRenderData *mr, void *buf)
{
static GPUVertFormat format = {0};
if (format.attr_len == 0) {
- /* WARNING Adjust EditLoopData struct accordingly. */
+ /* WARNING: Adjust #EditLoopData struct accordingly. */
GPU_vertformat_attr_add(&format, "data", GPU_COMP_U8, 4, GPU_FETCH_INT);
GPU_vertformat_alias_add(&format, "flag");
}
@@ -2952,51 +3385,59 @@ static void *extract_edituv_data_init(const MeshRenderData *mr, void *buf)
return data;
}
-static void extract_edituv_data_loop_bmesh(const MeshRenderData *mr,
- int l,
- BMLoop *loop,
- void *_data)
+static void extract_edituv_data_iter_poly_bm(const MeshRenderData *mr,
+ const ExtractPolyBMesh_Params *params,
+ void *_data)
{
- MeshExtract_EditUVData_Data *data = (MeshExtract_EditUVData_Data *)_data;
- EditLoopData *eldata = data->vbo_data + l;
- memset(eldata, 0x0, sizeof(*eldata));
- mesh_render_data_loop_flag(mr, loop, data->cd_ofs, eldata);
- mesh_render_data_face_flag(mr, loop->f, data->cd_ofs, eldata);
- mesh_render_data_loop_edge_flag(mr, loop, data->cd_ofs, eldata);
-}
-
-static void extract_edituv_data_loop_mesh(
- const MeshRenderData *mr, int l, const MLoop *mloop, int p, const MPoly *mpoly, void *_data)
-{
- MeshExtract_EditUVData_Data *data = (MeshExtract_EditUVData_Data *)_data;
- EditLoopData *eldata = data->vbo_data + l;
- memset(eldata, 0x0, sizeof(*eldata));
- BMFace *efa = bm_original_face_get(mr, p);
- if (efa) {
- BMEdge *eed = bm_original_edge_get(mr, mloop->e);
- BMVert *eve = bm_original_vert_get(mr, mloop->v);
- if (eed && eve) {
- /* Loop on an edge endpoint. */
- BMLoop *loop = BM_face_edge_share_loop(efa, eed);
- mesh_render_data_loop_flag(mr, loop, data->cd_ofs, eldata);
- mesh_render_data_loop_edge_flag(mr, loop, data->cd_ofs, eldata);
- }
- else {
- if (eed == NULL) {
- /* Find if the loop's vert is not part of an edit edge.
- * For this, we check if the previous loop was on an edge. */
- int loopend = mpoly->loopstart + mpoly->totloop - 1;
- int l_prev = (l == mpoly->loopstart) ? loopend : (l - 1);
- const MLoop *mloop_prev = &mr->mloop[l_prev];
- eed = bm_original_edge_get(mr, mloop_prev->e);
+ EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
+ {
+ MeshExtract_EditUVData_Data *data = _data;
+ EditLoopData *eldata = &data->vbo_data[l_index];
+ memset(eldata, 0x0, sizeof(*eldata));
+ mesh_render_data_loop_flag(mr, l, data->cd_ofs, eldata);
+ mesh_render_data_face_flag(mr, l->f, data->cd_ofs, eldata);
+ mesh_render_data_loop_edge_flag(mr, l, data->cd_ofs, eldata);
+ }
+ EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
+}
+
+static void extract_edituv_data_iter_poly_mesh(const MeshRenderData *mr,
+ const ExtractPolyMesh_Params *params,
+ void *_data)
+{
+ MeshExtract_EditUVData_Data *data = _data;
+ EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
+ {
+ EditLoopData *eldata = &data->vbo_data[ml_index];
+ memset(eldata, 0x0, sizeof(*eldata));
+ BMFace *efa = bm_original_face_get(mr, mp_index);
+ if (efa) {
+ BMEdge *eed = bm_original_edge_get(mr, ml->e);
+ BMVert *eve = bm_original_vert_get(mr, ml->v);
+ if (eed && eve) {
+ /* Loop on an edge endpoint. */
+ BMLoop *l = BM_face_edge_share_loop(efa, eed);
+ mesh_render_data_loop_flag(mr, l, data->cd_ofs, eldata);
+ mesh_render_data_loop_edge_flag(mr, l, data->cd_ofs, eldata);
}
- if (eed) {
- /* Mapped points on an edge between two edit verts. */
- BMLoop *loop = BM_face_edge_share_loop(efa, eed);
- mesh_render_data_loop_edge_flag(mr, loop, data->cd_ofs, eldata);
+ else {
+ if (eed == NULL) {
+ /* Find if the loop's vert is not part of an edit edge.
+ * For this, we check if the previous loop was on an edge. */
+ const int ml_index_last = mp->loopstart + mp->totloop - 1;
+ const int l_prev = (ml_index == mp->loopstart) ? ml_index_last : (ml_index - 1);
+ const MLoop *ml_prev = &mr->mloop[l_prev];
+ eed = bm_original_edge_get(mr, ml_prev->e);
+ }
+ if (eed) {
+ /* Mapped points on an edge between two edit verts. */
+ BMLoop *l = BM_face_edge_share_loop(efa, eed);
+ mesh_render_data_loop_edge_flag(mr, l, data->cd_ofs, eldata);
+ }
}
}
}
+ EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
}
static void extract_edituv_data_finish(const MeshRenderData *UNUSED(mr),
@@ -3007,18 +3448,12 @@ static void extract_edituv_data_finish(const MeshRenderData *UNUSED(mr),
}
static const MeshExtract extract_edituv_data = {
- extract_edituv_data_init,
- NULL,
- NULL,
- extract_edituv_data_loop_bmesh,
- extract_edituv_data_loop_mesh,
- NULL,
- NULL,
- NULL,
- NULL,
- extract_edituv_data_finish,
- 0,
- true,
+ .init = extract_edituv_data_init,
+ .iter_poly_bm = extract_edituv_data_iter_poly_bm,
+ .iter_poly_mesh = extract_edituv_data_iter_poly_mesh,
+ .finish = extract_edituv_data_finish,
+ .data_flag = 0,
+ .use_threading = true,
};
/** \} */
@@ -3078,13 +3513,13 @@ static void mesh_stretch_area_finish(const MeshRenderData *mr, void *buf, void *
}
else if (mr->extract_type == MR_EXTRACT_MAPPED) {
const MLoopUV *uv_data = CustomData_get_layer(&mr->me->ldata, CD_MLOOPUV);
- const MPoly *mpoly = mr->mpoly;
- for (int p = 0; p < mr->poly_len; p++, mpoly++) {
- float area = BKE_mesh_calc_poly_area(mpoly, &mr->mloop[mpoly->loopstart], mr->mvert);
- float uvarea = BKE_mesh_calc_poly_uv_area(mpoly, uv_data);
+ const MPoly *mp = mr->mpoly;
+ for (int mp_index = 0; mp_index < mr->poly_len; mp_index++, mp++) {
+ float area = BKE_mesh_calc_poly_area(mp, &mr->mloop[mp->loopstart], mr->mvert);
+ float uvarea = BKE_mesh_calc_poly_uv_area(mp, uv_data);
tot_area += area;
tot_uv_area += uvarea;
- area_ratio[p] = area_ratio_get(area, uvarea);
+ area_ratio[mp_index] = area_ratio_get(area, uvarea);
}
}
else {
@@ -3097,8 +3532,8 @@ static void mesh_stretch_area_finish(const MeshRenderData *mr, void *buf, void *
/* Convert in place to avoid an extra allocation */
uint16_t *poly_stretch = (uint16_t *)area_ratio;
- for (int p = 0; p < mr->poly_len; p++) {
- poly_stretch[p] = area_ratio[p] * SHRT_MAX;
+ for (int mp_index = 0; mp_index < mr->poly_len; mp_index++) {
+ poly_stretch[mp_index] = area_ratio[mp_index] * SHRT_MAX;
}
/* Copy face data for each loop. */
@@ -3108,18 +3543,18 @@ static void mesh_stretch_area_finish(const MeshRenderData *mr, void *buf, void *
if (mr->extract_type == MR_EXTRACT_BMESH) {
BMFace *efa;
BMIter f_iter;
- int f, l = 0;
+ int f, l_index = 0;
BM_ITER_MESH_INDEX (efa, &f_iter, mr->bm, BM_FACES_OF_MESH, f) {
- for (int i = 0; i < efa->len; i++, l++) {
- loop_stretch[l] = poly_stretch[f];
+ for (int i = 0; i < efa->len; i++, l_index++) {
+ loop_stretch[l_index] = poly_stretch[f];
}
}
}
else if (mr->extract_type == MR_EXTRACT_MAPPED) {
- const MPoly *mpoly = mr->mpoly;
- for (int p = 0, l = 0; p < mr->poly_len; p++, mpoly++) {
- for (int i = 0; i < mpoly->totloop; i++, l++) {
- loop_stretch[l] = poly_stretch[p];
+ const MPoly *mp = mr->mpoly;
+ for (int mp_index = 0, l_index = 0; mp_index < mr->poly_len; mp_index++, mp++) {
+ for (int i = 0; i < mp->totloop; i++, l_index++) {
+ loop_stretch[l_index] = poly_stretch[mp_index];
}
}
}
@@ -3132,18 +3567,10 @@ static void mesh_stretch_area_finish(const MeshRenderData *mr, void *buf, void *
}
static const MeshExtract extract_stretch_area = {
- extract_stretch_area_init,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- mesh_stretch_area_finish,
- 0,
- false,
+ .init = extract_stretch_area_init,
+ .finish = mesh_stretch_area_finish,
+ .data_flag = 0,
+ .use_threading = false,
};
/** \} */
@@ -3190,7 +3617,7 @@ static short v2_to_short_angle(float v[2])
static void edituv_get_stretch_angle(float auv[2][2], float av[2][3], UVStretchAngle *r_stretch)
{
- /* Send uvs to the shader and let it compute the aspect corrected angle. */
+ /* Send UV's to the shader and let it compute the aspect corrected angle. */
r_stretch->uv_angles[0] = v2_to_short_angle(auv[0]);
r_stretch->uv_angles[1] = v2_to_short_angle(auv[1]);
/* Compute 3D angle here. */
@@ -3208,7 +3635,7 @@ static void *extract_stretch_angle_init(const MeshRenderData *mr, void *buf)
{
static GPUVertFormat format = {0};
if (format.attr_len == 0) {
- /* WARNING Adjust UVStretchAngle struct accordingly. */
+ /* Waning: adjust #UVStretchAngle struct accordingly. */
GPU_vertformat_attr_add(&format, "angle", GPU_COMP_I16, 1, GPU_FETCH_INT_TO_FLOAT_UNIT);
GPU_vertformat_attr_add(&format, "uv_angles", GPU_COMP_I16, 2, GPU_FETCH_INT_TO_FLOAT_UNIT);
}
@@ -3220,7 +3647,7 @@ static void *extract_stretch_angle_init(const MeshRenderData *mr, void *buf)
MeshExtract_StretchAngle_Data *data = MEM_callocN(sizeof(*data), __func__);
data->vbo_data = (UVStretchAngle *)vbo->data;
- /* Special iter nneded to save about half of the computing cost. */
+ /* Special iterator needed to save about half of the computing cost. */
if (mr->extract_type == MR_EXTRACT_BMESH) {
data->cd_ofs = CustomData_get_offset(&mr->bm->ldata, CD_MLOOPUV);
}
@@ -3233,94 +3660,95 @@ static void *extract_stretch_angle_init(const MeshRenderData *mr, void *buf)
return data;
}
-static void extract_stretch_angle_loop_bmesh(const MeshRenderData *mr,
- int l,
- BMLoop *loop,
- void *_data)
+static void extract_stretch_angle_iter_poly_bm(const MeshRenderData *mr,
+ const ExtractPolyBMesh_Params *params,
+ void *_data)
{
- MeshExtract_StretchAngle_Data *data = (MeshExtract_StretchAngle_Data *)_data;
+ MeshExtract_StretchAngle_Data *data = _data;
float(*auv)[2] = data->auv, *last_auv = data->last_auv;
float(*av)[3] = data->av, *last_av = data->last_av;
- const MLoopUV *luv, *luv_next;
- BMLoop *l_next = loop->next;
- BMFace *efa = loop->f;
- if (loop == efa->l_first) {
- /* First loop in face. */
- BMLoop *l_tmp = loop->prev;
- BMLoop *l_next_tmp = loop;
- luv = BM_ELEM_CD_GET_VOID_P(l_tmp, data->cd_ofs);
- luv_next = BM_ELEM_CD_GET_VOID_P(l_next_tmp, data->cd_ofs);
- compute_normalize_edge_vectors(auv,
- av,
- luv->uv,
- luv_next->uv,
- bm_vert_co_get(mr, l_tmp->v),
- bm_vert_co_get(mr, l_next_tmp->v));
- /* Save last edge. */
- copy_v2_v2(last_auv, auv[1]);
- copy_v3_v3(last_av, av[1]);
- }
- if (l_next == efa->l_first) {
- /* Move previous edge. */
- copy_v2_v2(auv[0], auv[1]);
- copy_v3_v3(av[0], av[1]);
- /* Copy already calculated last edge. */
- copy_v2_v2(auv[1], last_auv);
- copy_v3_v3(av[1], last_av);
+ EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
+ {
+ const MLoopUV *luv, *luv_next;
+ BMLoop *l_next = l->next;
+ BMFace *efa = l->f;
+ if (l == BM_FACE_FIRST_LOOP(efa)) {
+ /* First loop in face. */
+ BMLoop *l_tmp = l->prev;
+ BMLoop *l_next_tmp = l;
+ luv = BM_ELEM_CD_GET_VOID_P(l_tmp, data->cd_ofs);
+ luv_next = BM_ELEM_CD_GET_VOID_P(l_next_tmp, data->cd_ofs);
+ compute_normalize_edge_vectors(auv,
+ av,
+ luv->uv,
+ luv_next->uv,
+ bm_vert_co_get(mr, l_tmp->v),
+ bm_vert_co_get(mr, l_next_tmp->v));
+ /* Save last edge. */
+ copy_v2_v2(last_auv, auv[1]);
+ copy_v3_v3(last_av, av[1]);
+ }
+ if (l_next == BM_FACE_FIRST_LOOP(efa)) {
+ /* Move previous edge. */
+ copy_v2_v2(auv[0], auv[1]);
+ copy_v3_v3(av[0], av[1]);
+ /* Copy already calculated last edge. */
+ copy_v2_v2(auv[1], last_auv);
+ copy_v3_v3(av[1], last_av);
+ }
+ else {
+ luv = BM_ELEM_CD_GET_VOID_P(l, data->cd_ofs);
+ luv_next = BM_ELEM_CD_GET_VOID_P(l_next, data->cd_ofs);
+ compute_normalize_edge_vectors(
+ auv, av, luv->uv, luv_next->uv, bm_vert_co_get(mr, l->v), bm_vert_co_get(mr, l_next->v));
+ }
+ edituv_get_stretch_angle(auv, av, &data->vbo_data[l_index]);
}
- else {
- luv = BM_ELEM_CD_GET_VOID_P(loop, data->cd_ofs);
- luv_next = BM_ELEM_CD_GET_VOID_P(l_next, data->cd_ofs);
- compute_normalize_edge_vectors(auv,
- av,
- luv->uv,
- luv_next->uv,
- bm_vert_co_get(mr, loop->v),
- bm_vert_co_get(mr, l_next->v));
- }
- edituv_get_stretch_angle(auv, av, data->vbo_data + l);
-}
-
-static void extract_stretch_angle_loop_mesh(const MeshRenderData *mr,
- int l,
- const MLoop *UNUSED(mloop),
- int UNUSED(p),
- const MPoly *mpoly,
- void *_data)
+ EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
+}
+
+static void extract_stretch_angle_iter_poly_mesh(const MeshRenderData *mr,
+ const ExtractPolyMesh_Params *params,
+ void *_data)
{
- MeshExtract_StretchAngle_Data *data = (MeshExtract_StretchAngle_Data *)_data;
- float(*auv)[2] = data->auv, *last_auv = data->last_auv;
- float(*av)[3] = data->av, *last_av = data->last_av;
- int l_next = l + 1, loopend = mpoly->loopstart + mpoly->totloop;
- const MVert *v, *v_next;
- if (l == mpoly->loopstart) {
- /* First loop in face. */
- int l_tmp = loopend - 1;
- int l_next_tmp = mpoly->loopstart;
- v = &mr->mvert[mr->mloop[l_tmp].v];
- v_next = &mr->mvert[mr->mloop[l_next_tmp].v];
- compute_normalize_edge_vectors(
- auv, av, data->luv[l_tmp].uv, data->luv[l_next_tmp].uv, v->co, v_next->co);
- /* Save last edge. */
- copy_v2_v2(last_auv, auv[1]);
- copy_v3_v3(last_av, av[1]);
- }
- if (l_next == loopend) {
- l_next = mpoly->loopstart;
- /* Move previous edge. */
- copy_v2_v2(auv[0], auv[1]);
- copy_v3_v3(av[0], av[1]);
- /* Copy already calculated last edge. */
- copy_v2_v2(auv[1], last_auv);
- copy_v3_v3(av[1], last_av);
- }
- else {
- v = &mr->mvert[mr->mloop[l].v];
- v_next = &mr->mvert[mr->mloop[l_next].v];
- compute_normalize_edge_vectors(
- auv, av, data->luv[l].uv, data->luv[l_next].uv, v->co, v_next->co);
+ MeshExtract_StretchAngle_Data *data = _data;
+
+ EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
+ {
+ float(*auv)[2] = data->auv, *last_auv = data->last_auv;
+ float(*av)[3] = data->av, *last_av = data->last_av;
+ int l_next = ml_index + 1, ml_index_end = mp->loopstart + mp->totloop;
+ const MVert *v, *v_next;
+ if (ml_index == mp->loopstart) {
+ /* First loop in face. */
+ const int ml_index_last = ml_index_end - 1;
+ const int l_next_tmp = mp->loopstart;
+ v = &mr->mvert[mr->mloop[ml_index_last].v];
+ v_next = &mr->mvert[mr->mloop[l_next_tmp].v];
+ compute_normalize_edge_vectors(
+ auv, av, data->luv[ml_index_last].uv, data->luv[l_next_tmp].uv, v->co, v_next->co);
+ /* Save last edge. */
+ copy_v2_v2(last_auv, auv[1]);
+ copy_v3_v3(last_av, av[1]);
+ }
+ if (l_next == ml_index_end) {
+ l_next = mp->loopstart;
+ /* Move previous edge. */
+ copy_v2_v2(auv[0], auv[1]);
+ copy_v3_v3(av[0], av[1]);
+ /* Copy already calculated last edge. */
+ copy_v2_v2(auv[1], last_auv);
+ copy_v3_v3(av[1], last_av);
+ }
+ else {
+ v = &mr->mvert[mr->mloop[ml_index].v];
+ v_next = &mr->mvert[mr->mloop[l_next].v];
+ compute_normalize_edge_vectors(
+ auv, av, data->luv[ml_index].uv, data->luv[l_next].uv, v->co, v_next->co);
+ }
+ edituv_get_stretch_angle(auv, av, &data->vbo_data[ml_index]);
}
- edituv_get_stretch_angle(auv, av, data->vbo_data + l);
+ EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
}
static void extract_stretch_angle_finish(const MeshRenderData *UNUSED(mr),
@@ -3331,18 +3759,12 @@ static void extract_stretch_angle_finish(const MeshRenderData *UNUSED(mr),
}
static const MeshExtract extract_stretch_angle = {
- extract_stretch_angle_init,
- NULL,
- NULL,
- extract_stretch_angle_loop_bmesh,
- extract_stretch_angle_loop_mesh,
- NULL,
- NULL,
- NULL,
- NULL,
- extract_stretch_angle_finish,
- 0,
- false,
+ .init = extract_stretch_angle_init,
+ .iter_poly_bm = extract_stretch_angle_iter_poly_bm,
+ .iter_poly_mesh = extract_stretch_angle_iter_poly_mesh,
+ .finish = extract_stretch_angle_finish,
+ .data_flag = 0,
+ .use_threading = false,
};
/** \} */
@@ -3414,28 +3836,30 @@ static void statvis_calc_overhang(const MeshRenderData *mr, float *r_overhang)
normalize_v3(dir);
if (mr->extract_type == MR_EXTRACT_BMESH) {
- int l = 0;
+ int l_index = 0;
BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
float fac = angle_normalized_v3v3(bm_face_no_get(mr, f), dir) / (float)M_PI;
fac = overhang_remap(fac, min, max, minmax_irange);
- for (int i = 0; i < f->len; i++, l++) {
- r_overhang[l] = fac;
+ for (int i = 0; i < f->len; i++, l_index++) {
+ r_overhang[l_index] = fac;
}
}
}
else {
- const MPoly *mpoly = mr->mpoly;
- for (int p = 0, l = 0; p < mr->poly_len; p++, mpoly++) {
- float fac = angle_normalized_v3v3(mr->poly_normals[p], dir) / (float)M_PI;
+ const MPoly *mp = mr->mpoly;
+ for (int mp_index = 0, l_index = 0; mp_index < mr->poly_len; mp_index++, mp++) {
+ float fac = angle_normalized_v3v3(mr->poly_normals[mp_index], dir) / (float)M_PI;
fac = overhang_remap(fac, min, max, minmax_irange);
- for (int i = 0; i < mpoly->totloop; i++, l++) {
- r_overhang[l] = fac;
+ for (int i = 0; i < mp->totloop; i++, l_index++) {
+ r_overhang[l_index] = fac;
}
}
}
}
-/* so we can use jitter values for face interpolation */
+/**
+ * Needed so we can use jitter values for face interpolation.
+ */
static void uv_from_jitter_v2(float uv[2])
{
uv[0] += 0.5f;
@@ -3527,12 +3951,12 @@ static void statvis_calc_thickness(const MeshRenderData *mr, float *r_thickness)
BMIter iter;
BMFace *f;
- int l = 0;
+ int l_index = 0;
BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
float fac = face_dists[BM_elem_index_get(f)];
fac = thickness_remap(fac, min, max, minmax_irange);
- for (int i = 0; i < f->len; i++, l++) {
- r_thickness[l] = fac;
+ for (int i = 0; i < f->len; i++, l_index++) {
+ r_thickness[l_index] = fac;
}
}
}
@@ -3573,12 +3997,12 @@ static void statvis_calc_thickness(const MeshRenderData *mr, float *r_thickness)
}
}
- const MPoly *mpoly = mr->mpoly;
- for (int p = 0, l = 0; p < mr->poly_len; p++, mpoly++) {
- float fac = face_dists[p];
+ const MPoly *mp = mr->mpoly;
+ for (int mp_index = 0, l_index = 0; mp_index < mr->poly_len; mp_index++, mp++) {
+ float fac = face_dists[mp_index];
fac = thickness_remap(fac, min, max, minmax_irange);
- for (int i = 0; i < mpoly->totloop; i++, l++) {
- r_thickness[l] = fac;
+ for (int i = 0; i < mp->totloop; i++, l_index++) {
+ r_thickness[l_index] = fac;
}
}
}
@@ -3629,8 +4053,8 @@ static void statvis_calc_intersect(const MeshRenderData *mr, float *r_intersect)
{
BMEditMesh *em = mr->edit_bmesh;
- for (int l = 0; l < mr->loop_len; l++) {
- r_intersect[l] = -1.0f;
+ for (int l_index = 0; l_index < mr->loop_len; l_index++) {
+ r_intersect[l_index] = -1.0f;
}
if (mr->extract_type == MR_EXTRACT_BMESH) {
@@ -3651,9 +4075,9 @@ static void statvis_calc_intersect(const MeshRenderData *mr, float *r_intersect)
for (int j = 0; j < 2; j++) {
BMFace *f_hit = f_hit_pair[j];
BMLoop *l_first = BM_FACE_FIRST_LOOP(f_hit);
- int l = BM_elem_index_get(l_first);
- for (int k = 0; k < f_hit->len; k++, l++) {
- r_intersect[l] = 1.0f;
+ int l_index = BM_elem_index_get(l_first);
+ for (int k = 0; k < f_hit->len; k++, l_index++) {
+ r_intersect[l_index] = 1.0f;
}
}
}
@@ -3680,9 +4104,9 @@ static void statvis_calc_intersect(const MeshRenderData *mr, float *r_intersect)
};
for (int j = 0; j < 2; j++) {
const MPoly *f_hit = f_hit_pair[j];
- int l = f_hit->loopstart;
- for (int k = 0; k < f_hit->totloop; k++, l++) {
- r_intersect[l] = 1.0f;
+ int l_index = f_hit->loopstart;
+ for (int k = 0; k < f_hit->totloop; k++, l_index++) {
+ r_intersect[l_index] = 1.0f;
}
}
}
@@ -3725,9 +4149,9 @@ static void statvis_calc_distort(const MeshRenderData *mr, float *r_distort)
BM_mesh_elem_index_ensure(em->bm, BM_VERT);
}
- int l = 0;
- int p = 0;
- BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, p) {
+ int l_index = 0;
+ int f_index = 0;
+ BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, f_index) {
float fac = -1.0f;
if (f->len > 3) {
@@ -3739,7 +4163,7 @@ static void statvis_calc_distort(const MeshRenderData *mr, float *r_distort)
const float *no_face;
float no_corner[3];
if (mr->bm_vert_coords != NULL) {
- no_face = mr->bm_poly_normals[p];
+ no_face = mr->bm_poly_normals[f_index];
BM_loop_calc_face_normal_safe_vcos(l_iter, no_face, mr->bm_vert_coords, no_corner);
}
else {
@@ -3758,24 +4182,24 @@ static void statvis_calc_distort(const MeshRenderData *mr, float *r_distort)
}
fac = distort_remap(fac, min, max, minmax_irange);
- for (int i = 0; i < f->len; i++, l++) {
- r_distort[l] = fac;
+ for (int i = 0; i < f->len; i++, l_index++) {
+ r_distort[l_index] = fac;
}
}
}
else {
- const MPoly *mpoly = mr->mpoly;
- for (int p = 0, l = 0; p < mr->poly_len; p++, mpoly++) {
+ const MPoly *mp = mr->mpoly;
+ for (int mp_index = 0, l_index = 0; mp_index < mr->poly_len; mp_index++, mp++) {
float fac = -1.0f;
- if (mpoly->totloop > 3) {
- float *f_no = mr->poly_normals[p];
+ if (mp->totloop > 3) {
+ float *f_no = mr->poly_normals[mp_index];
fac = 0.0f;
- for (int i = 1; i <= mpoly->totloop; i++) {
- const MLoop *l_prev = &mr->mloop[mpoly->loopstart + (i - 1) % mpoly->totloop];
- const MLoop *l_curr = &mr->mloop[mpoly->loopstart + (i + 0) % mpoly->totloop];
- const MLoop *l_next = &mr->mloop[mpoly->loopstart + (i + 1) % mpoly->totloop];
+ for (int i = 1; i <= mp->totloop; i++) {
+ const MLoop *l_prev = &mr->mloop[mp->loopstart + (i - 1) % mp->totloop];
+ const MLoop *l_curr = &mr->mloop[mp->loopstart + (i + 0) % mp->totloop];
+ const MLoop *l_next = &mr->mloop[mp->loopstart + (i + 1) % mp->totloop];
float no_corner[3];
normal_tri_v3(no_corner,
mr->mvert[l_prev->v].co,
@@ -3791,8 +4215,8 @@ static void statvis_calc_distort(const MeshRenderData *mr, float *r_distort)
}
fac = distort_remap(fac, min, max, minmax_irange);
- for (int i = 0; i < mpoly->totloop; i++, l++) {
- r_distort[l] = fac;
+ for (int i = 0; i < mp->totloop; i++, l_index++) {
+ r_distort[l_index] = fac;
}
}
}
@@ -3825,11 +4249,10 @@ static void statvis_calc_sharp(const MeshRenderData *mr, float *r_sharp)
copy_vn_fl(vert_angles, mr->vert_len, -M_PI);
if (mr->extract_type == MR_EXTRACT_BMESH) {
- BMIter iter, l_iter;
+ BMIter iter;
BMesh *bm = em->bm;
BMFace *efa;
BMEdge *e;
- BMLoop *loop;
/* first assign float values to verts */
BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
float angle = BM_edge_calc_face_angle_signed(e);
@@ -3840,35 +4263,37 @@ static void statvis_calc_sharp(const MeshRenderData *mr, float *r_sharp)
}
/* Copy vert value to loops. */
BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
- BM_ITER_ELEM (loop, &l_iter, efa, BM_LOOPS_OF_FACE) {
- int l = BM_elem_index_get(loop);
- int v = BM_elem_index_get(loop->v);
- r_sharp[l] = sharp_remap(vert_angles[v], min, max, minmax_irange);
- }
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
+ do {
+ int l_index = BM_elem_index_get(l_iter);
+ int v_index = BM_elem_index_get(l_iter->v);
+ r_sharp[l_index] = sharp_remap(vert_angles[v_index], min, max, minmax_irange);
+ } while ((l_iter = l_iter->next) != l_first);
}
}
else {
/* first assign float values to verts */
- const MPoly *mpoly = mr->mpoly;
+ const MPoly *mp = mr->mpoly;
EdgeHash *eh = BLI_edgehash_new_ex(__func__, mr->edge_len);
- for (int p = 0; p < mr->poly_len; p++, mpoly++) {
- for (int i = 0; i < mpoly->totloop; i++) {
- const MLoop *l_curr = &mr->mloop[mpoly->loopstart + (i + 0) % mpoly->totloop];
- const MLoop *l_next = &mr->mloop[mpoly->loopstart + (i + 1) % mpoly->totloop];
+ for (int mp_index = 0; mp_index < mr->poly_len; mp_index++, mp++) {
+ for (int i = 0; i < mp->totloop; i++) {
+ const MLoop *l_curr = &mr->mloop[mp->loopstart + (i + 0) % mp->totloop];
+ const MLoop *l_next = &mr->mloop[mp->loopstart + (i + 1) % mp->totloop];
const MVert *v_curr = &mr->mvert[l_curr->v];
const MVert *v_next = &mr->mvert[l_next->v];
float angle;
void **pval;
bool value_is_init = BLI_edgehash_ensure_p(eh, l_curr->v, l_next->v, &pval);
if (!value_is_init) {
- *pval = mr->poly_normals[p];
+ *pval = mr->poly_normals[mp_index];
/* non-manifold edge, yet... */
continue;
}
else if (*pval != NULL) {
- const float *f1_no = mr->poly_normals[p];
+ const float *f1_no = mr->poly_normals[mp_index];
const float *f2_no = *pval;
angle = angle_normalized_v3v3(f1_no, f2_no);
angle = is_edge_convex_v3(v_curr->co, v_next->co, f1_no, f2_no) ? angle : -angle;
@@ -3901,9 +4326,9 @@ static void statvis_calc_sharp(const MeshRenderData *mr, float *r_sharp)
BLI_edgehashIterator_free(ehi);
BLI_edgehash_free(eh, NULL);
- const MLoop *mloop = mr->mloop;
- for (int l = 0; l < mr->loop_len; l++, mloop++) {
- r_sharp[l] = sharp_remap(vert_angles[mloop->v], min, max, minmax_irange);
+ const MLoop *ml = mr->mloop;
+ for (int l_index = 0; l_index < mr->loop_len; l_index++, ml++) {
+ r_sharp[l_index] = sharp_remap(vert_angles[ml->v], min, max, minmax_irange);
}
}
@@ -3937,20 +4362,12 @@ static void extract_mesh_analysis_finish(const MeshRenderData *mr, void *buf, vo
}
static const MeshExtract extract_mesh_analysis = {
- extract_mesh_analysis_init,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- extract_mesh_analysis_finish,
- /* This is not needed for all vis type.
- * Maybe split into different extract. */
- MR_DATA_POLY_NOR | MR_DATA_LOOPTRI,
- false,
+ .init = extract_mesh_analysis_init,
+ .finish = extract_mesh_analysis_finish,
+ /* This is not needed for all visualization types.
+ * * Maybe split into different extract. */
+ .data_flag = MR_DATA_POLY_NOR | MR_DATA_LOOPTRI,
+ .use_threading = false,
};
/** \} */
@@ -3975,49 +4392,46 @@ static void *extract_fdots_pos_init(const MeshRenderData *mr, void *buf)
return vbo->data;
}
-static void extract_fdots_pos_loop_bmesh(const MeshRenderData *mr,
- int UNUSED(l),
- BMLoop *loop,
- void *data)
+static void extract_fdots_pos_iter_poly_bm(const MeshRenderData *mr,
+ const ExtractPolyBMesh_Params *params,
+ void *data)
{
- float(*center)[3] = (float(*)[3])data;
- float w = 1.0f / (float)loop->f->len;
- madd_v3_v3fl(center[BM_elem_index_get(loop->f)], bm_vert_co_get(mr, loop->v), w);
+ float(*center)[3] = data;
+ EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
+ {
+ float w = 1.0f / (float)l->f->len;
+ madd_v3_v3fl(center[BM_elem_index_get(l->f)], bm_vert_co_get(mr, l->v), w);
+ }
+ EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
}
-static void extract_fdots_pos_loop_mesh(const MeshRenderData *mr,
- int UNUSED(l),
- const MLoop *mloop,
- int p,
- const MPoly *mpoly,
- void *data)
+static void extract_fdots_pos_iter_poly_mesh(const MeshRenderData *mr,
+ const ExtractPolyMesh_Params *params,
+ void *data)
{
float(*center)[3] = (float(*)[3])data;
- const MVert *mvert = &mr->mvert[mloop->v];
- if (mr->use_subsurf_fdots) {
- if (mvert->flag & ME_VERT_FACEDOT) {
- copy_v3_v3(center[p], mvert->co);
+ EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
+ {
+ const MVert *mv = &mr->mvert[ml->v];
+ if (mr->use_subsurf_fdots) {
+ if (mv->flag & ME_VERT_FACEDOT) {
+ copy_v3_v3(center[mp_index], mv->co);
+ }
+ }
+ else {
+ float w = 1.0f / (float)mp->totloop;
+ madd_v3_v3fl(center[mp_index], mv->co, w);
}
}
- else {
- float w = 1.0f / (float)mpoly->totloop;
- madd_v3_v3fl(center[p], mvert->co, w);
- }
+ EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
}
static const MeshExtract extract_fdots_pos = {
- extract_fdots_pos_init,
- NULL,
- NULL,
- extract_fdots_pos_loop_bmesh,
- extract_fdots_pos_loop_mesh,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- 0,
- true,
+ .init = extract_fdots_pos_init,
+ .iter_poly_bm = extract_fdots_pos_iter_poly_bm,
+ .iter_poly_mesh = extract_fdots_pos_iter_poly_mesh,
+ .data_flag = 0,
+ .use_threading = true,
};
/** \} */
@@ -4090,18 +4504,10 @@ static void extract_fdots_nor_finish(const MeshRenderData *mr, void *buf, void *
}
static const MeshExtract extract_fdots_nor = {
- extract_fdots_nor_init,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- extract_fdots_nor_finish,
- MR_DATA_POLY_NOR,
- false,
+ .init = extract_fdots_nor_init,
+ .finish = extract_fdots_nor_finish,
+ .data_flag = MR_DATA_POLY_NOR,
+ .use_threading = false,
};
/** \} */
@@ -4145,30 +4551,42 @@ static void *extract_fdots_uv_init(const MeshRenderData *mr, void *buf)
return data;
}
-static void extract_fdots_uv_loop_bmesh(const MeshRenderData *UNUSED(mr),
- int UNUSED(l),
- BMLoop *loop,
- void *_data)
+static void extract_fdots_uv_iter_poly_bm(const MeshRenderData *mr,
+ const ExtractPolyBMesh_Params *params,
+ void *_data)
{
- MeshExtract_FdotUV_Data *data = (MeshExtract_FdotUV_Data *)_data;
- float w = 1.0f / (float)loop->f->len;
- const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(loop, data->cd_ofs);
- madd_v2_v2fl(data->vbo_data[BM_elem_index_get(loop->f)], luv->uv, w);
+ MeshExtract_FdotUV_Data *data = _data;
+ EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
+ {
+ float w = 1.0f / (float)l->f->len;
+ const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, data->cd_ofs);
+ madd_v2_v2fl(data->vbo_data[BM_elem_index_get(l->f)], luv->uv, w);
+ }
+ EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
}
-static void extract_fdots_uv_loop_mesh(
- const MeshRenderData *mr, int l, const MLoop *mloop, int p, const MPoly *mpoly, void *_data)
+static void extract_fdots_uv_iter_poly_mesh(const MeshRenderData *mr,
+ const ExtractPolyMesh_Params *params,
+ void *_data)
{
- MeshExtract_FdotUV_Data *data = (MeshExtract_FdotUV_Data *)_data;
+ MeshExtract_FdotUV_Data *data = _data;
if (mr->use_subsurf_fdots) {
- const MVert *mvert = &mr->mvert[mloop->v];
- if (mvert->flag & ME_VERT_FACEDOT) {
- copy_v2_v2(data->vbo_data[p], data->uv_data[l].uv);
+ EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
+ {
+ const MVert *mv = &mr->mvert[ml->v];
+ if (mv->flag & ME_VERT_FACEDOT) {
+ copy_v2_v2(data->vbo_data[mp_index], data->uv_data[ml_index].uv);
+ }
}
+ EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
}
else {
- float w = 1.0f / (float)mpoly->totloop;
- madd_v2_v2fl(data->vbo_data[p], data->uv_data[l].uv, w);
+ EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
+ {
+ float w = 1.0f / (float)mp->totloop;
+ madd_v2_v2fl(data->vbo_data[mp_index], data->uv_data[ml_index].uv, w);
+ }
+ EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
}
}
@@ -4180,18 +4598,12 @@ static void extract_fdots_uv_finish(const MeshRenderData *UNUSED(mr),
}
static const MeshExtract extract_fdots_uv = {
- extract_fdots_uv_init,
- NULL,
- NULL,
- extract_fdots_uv_loop_bmesh,
- extract_fdots_uv_loop_mesh,
- NULL,
- NULL,
- NULL,
- NULL,
- extract_fdots_uv_finish,
- 0,
- true,
+ .init = extract_fdots_uv_init,
+ .iter_poly_bm = extract_fdots_uv_iter_poly_bm,
+ .iter_poly_mesh = extract_fdots_uv_iter_poly_mesh,
+ .finish = extract_fdots_uv_finish,
+ .data_flag = 0,
+ .use_threading = true,
};
/** \} */
@@ -4220,31 +4632,35 @@ static void *extract_fdots_edituv_data_init(const MeshRenderData *mr, void *buf)
return data;
}
-static void extract_fdots_edituv_data_loop_bmesh(const MeshRenderData *mr,
- int UNUSED(l),
- BMLoop *loop,
- void *_data)
+static void extract_fdots_edituv_data_iter_poly_bm(const MeshRenderData *mr,
+ const ExtractPolyBMesh_Params *params,
+ void *_data)
{
- MeshExtract_EditUVFdotData_Data *data = (MeshExtract_EditUVFdotData_Data *)_data;
- EditLoopData *eldata = data->vbo_data + BM_elem_index_get(loop->f);
- memset(eldata, 0x0, sizeof(*eldata));
- mesh_render_data_face_flag(mr, loop->f, data->cd_ofs, eldata);
+ MeshExtract_EditUVFdotData_Data *data = _data;
+ EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr)
+ {
+ EditLoopData *eldata = &data->vbo_data[BM_elem_index_get(f)];
+ memset(eldata, 0x0, sizeof(*eldata));
+ mesh_render_data_face_flag(mr, f, data->cd_ofs, eldata);
+ }
+ EXTRACT_POLY_FOREACH_BM_END;
}
-static void extract_fdots_edituv_data_loop_mesh(const MeshRenderData *mr,
- int UNUSED(l),
- const MLoop *UNUSED(mloop),
- int p,
- const MPoly *UNUSED(mpoly),
- void *_data)
+static void extract_fdots_edituv_data_iter_poly_mesh(const MeshRenderData *mr,
+ const ExtractPolyMesh_Params *params,
+ void *_data)
{
- MeshExtract_EditUVFdotData_Data *data = (MeshExtract_EditUVFdotData_Data *)_data;
- EditLoopData *eldata = data->vbo_data + p;
- memset(eldata, 0x0, sizeof(*eldata));
- BMFace *efa = bm_original_face_get(mr, p);
- if (efa) {
- mesh_render_data_face_flag(mr, efa, data->cd_ofs, eldata);
+ MeshExtract_EditUVFdotData_Data *data = _data;
+ EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr)
+ {
+ EditLoopData *eldata = &data->vbo_data[mp_index];
+ memset(eldata, 0x0, sizeof(*eldata));
+ BMFace *efa = bm_original_face_get(mr, mp_index);
+ if (efa) {
+ mesh_render_data_face_flag(mr, efa, data->cd_ofs, eldata);
+ }
}
+ EXTRACT_POLY_FOREACH_MESH_END;
}
static void extract_fdots_edituv_data_finish(const MeshRenderData *UNUSED(mr),
@@ -4255,18 +4671,12 @@ static void extract_fdots_edituv_data_finish(const MeshRenderData *UNUSED(mr),
}
static const MeshExtract extract_fdots_edituv_data = {
- extract_fdots_edituv_data_init,
- NULL,
- NULL,
- extract_fdots_edituv_data_loop_bmesh,
- extract_fdots_edituv_data_loop_mesh,
- NULL,
- NULL,
- NULL,
- NULL,
- extract_fdots_edituv_data_finish,
- 0,
- true,
+ .init = extract_fdots_edituv_data_init,
+ .iter_poly_bm = extract_fdots_edituv_data_iter_poly_bm,
+ .iter_poly_mesh = extract_fdots_edituv_data_iter_poly_mesh,
+ .finish = extract_fdots_edituv_data_finish,
+ .data_flag = 0,
+ .use_threading = true,
};
/** \} */
@@ -4317,18 +4727,9 @@ static void *extract_skin_roots_init(const MeshRenderData *mr, void *buf)
}
static const MeshExtract extract_skin_roots = {
- extract_skin_roots_init,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- 0,
- false,
+ .init = extract_skin_roots_init,
+ .data_flag = 0,
+ .use_threading = false,
};
/** \} */
@@ -4350,157 +4751,183 @@ static void *extract_select_idx_init(const MeshRenderData *mr, void *buf)
return vbo->data;
}
-/* TODO Use glVertexID to get loop index and use the data structure on the CPU to retrieve the
+/* TODO Use #glVertexID to get loop index and use the data structure on the CPU to retrieve the
* select element associated with this loop ID. This would remove the need for this separate
- * index VBOs. We could upload the p/e/v_origindex as a buffer texture and sample it inside the
+ * index VBO's. We could upload the p/e/v_origindex as a buffer texture and sample it inside the
* shader to output original index. */
-static void extract_poly_idx_loop_bmesh(const MeshRenderData *UNUSED(mr),
- int l,
- BMLoop *loop,
- void *data)
+static void extract_poly_idx_iter_poly_bm(const MeshRenderData *mr,
+ const ExtractPolyBMesh_Params *params,
+ void *data)
{
- ((uint32_t *)data)[l] = BM_elem_index_get(loop->f);
+ EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
+ {
+ ((uint32_t *)data)[l_index] = BM_elem_index_get(l->f);
+ }
+ EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
}
-static void extract_edge_idx_loop_bmesh(const MeshRenderData *UNUSED(mr),
- int l,
- BMLoop *loop,
- void *data)
+static void extract_edge_idx_iter_poly_bm(const MeshRenderData *mr,
+ const ExtractPolyBMesh_Params *params,
+ void *data)
{
- ((uint32_t *)data)[l] = BM_elem_index_get(loop->e);
+ EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
+ {
+ ((uint32_t *)data)[l_index] = BM_elem_index_get(l->e);
+ }
+ EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
}
-static void extract_vert_idx_loop_bmesh(const MeshRenderData *UNUSED(mr),
- int l,
- BMLoop *loop,
- void *data)
+static void extract_vert_idx_iter_poly_bm(const MeshRenderData *mr,
+ const ExtractPolyBMesh_Params *params,
+ void *data)
{
- ((uint32_t *)data)[l] = BM_elem_index_get(loop->v);
+ EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
+ {
+ ((uint32_t *)data)[l_index] = BM_elem_index_get(l->v);
+ }
+ EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
}
-static void extract_edge_idx_ledge_bmesh(const MeshRenderData *mr, int e, BMEdge *eed, void *data)
+static void extract_edge_idx_iter_ledge_bm(const MeshRenderData *mr,
+ const ExtractLEdgeBMesh_Params *params,
+ void *data)
{
- ((uint32_t *)data)[mr->loop_len + e * 2 + 0] = BM_elem_index_get(eed);
- ((uint32_t *)data)[mr->loop_len + e * 2 + 1] = BM_elem_index_get(eed);
+ EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params)
+ {
+ ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = BM_elem_index_get(eed);
+ ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = BM_elem_index_get(eed);
+ }
+ EXTRACT_LEDGE_FOREACH_BM_END;
}
-static void extract_vert_idx_ledge_bmesh(const MeshRenderData *mr, int e, BMEdge *eed, void *data)
+static void extract_vert_idx_iter_ledge_bm(const MeshRenderData *mr,
+ const ExtractLEdgeBMesh_Params *params,
+ void *data)
{
- ((uint32_t *)data)[mr->loop_len + e * 2 + 0] = BM_elem_index_get(eed->v1);
- ((uint32_t *)data)[mr->loop_len + e * 2 + 1] = BM_elem_index_get(eed->v2);
+ EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params)
+ {
+ ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = BM_elem_index_get(eed->v1);
+ ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = BM_elem_index_get(eed->v2);
+ }
+ EXTRACT_LEDGE_FOREACH_BM_END;
}
-static void extract_vert_idx_lvert_bmesh(const MeshRenderData *mr, int v, BMVert *eve, void *data)
+static void extract_vert_idx_iter_lvert_bm(const MeshRenderData *mr,
+ const ExtractLVertBMesh_Params *params,
+ void *data)
{
- ((uint32_t *)data)[mr->loop_len + mr->edge_loose_len * 2 + v] = BM_elem_index_get(eve);
+ const int offset = mr->loop_len + (mr->edge_loose_len * 2);
+ EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params)
+ {
+ ((uint32_t *)data)[offset + lvert_index] = BM_elem_index_get(eve);
+ }
+ EXTRACT_LVERT_FOREACH_BM_END;
}
-static void extract_poly_idx_loop_mesh(const MeshRenderData *mr,
- int l,
- const MLoop *UNUSED(mloop),
- int p,
- const MPoly *UNUSED(mpoly),
- void *data)
+static void extract_poly_idx_iter_poly_mesh(const MeshRenderData *mr,
+ const ExtractPolyMesh_Params *params,
+ void *data)
{
- ((uint32_t *)data)[l] = (mr->p_origindex) ? mr->p_origindex[p] : p;
+ EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
+ {
+ ((uint32_t *)data)[ml_index] = (mr->p_origindex) ? mr->p_origindex[mp_index] : mp_index;
+ }
+ EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
}
-static void extract_edge_idx_loop_mesh(const MeshRenderData *mr,
- int l,
- const MLoop *mloop,
- int UNUSED(p),
- const MPoly *UNUSED(mpoly),
- void *data)
+static void extract_edge_idx_iter_poly_mesh(const MeshRenderData *mr,
+ const ExtractPolyMesh_Params *params,
+ void *data)
{
- ((uint32_t *)data)[l] = (mr->e_origindex) ? mr->e_origindex[mloop->e] : mloop->e;
+ EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
+ {
+ ((uint32_t *)data)[ml_index] = (mr->e_origindex) ? mr->e_origindex[ml->e] : ml->e;
+ }
+ EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
}
-static void extract_vert_idx_loop_mesh(const MeshRenderData *mr,
- int l,
- const MLoop *mloop,
- int UNUSED(p),
- const MPoly *UNUSED(mpoly),
- void *data)
+static void extract_vert_idx_iter_poly_mesh(const MeshRenderData *mr,
+ const ExtractPolyMesh_Params *params,
+ void *data)
{
- ((uint32_t *)data)[l] = (mr->v_origindex) ? mr->v_origindex[mloop->v] : mloop->v;
+ EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
+ {
+ ((uint32_t *)data)[ml_index] = (mr->v_origindex) ? mr->v_origindex[ml->v] : ml->v;
+ }
+ EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
}
-static void extract_edge_idx_ledge_mesh(const MeshRenderData *mr,
- int e,
- const MEdge *UNUSED(medge),
- void *data)
+static void extract_edge_idx_iter_ledge_mesh(const MeshRenderData *mr,
+ const ExtractLEdgeMesh_Params *params,
+ void *data)
{
- int e_idx = mr->ledges[e];
- int e_orig = (mr->e_origindex) ? mr->e_origindex[e_idx] : e_idx;
- ((uint32_t *)data)[mr->loop_len + e * 2 + 0] = e_orig;
- ((uint32_t *)data)[mr->loop_len + e * 2 + 1] = e_orig;
+ EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr)
+ {
+ const int e_index = mr->ledges[ledge_index];
+ const int e_orig = (mr->e_origindex) ? mr->e_origindex[e_index] : e_index;
+ ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = e_orig;
+ ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = e_orig;
+ }
+ EXTRACT_LEDGE_FOREACH_MESH_END;
}
-static void extract_vert_idx_ledge_mesh(const MeshRenderData *mr,
- int e,
- const MEdge *medge,
- void *data)
+static void extract_vert_idx_iter_ledge_mesh(const MeshRenderData *mr,
+ const ExtractLEdgeMesh_Params *params,
+ void *data)
{
- int v1_orig = (mr->v_origindex) ? mr->v_origindex[medge->v1] : medge->v1;
- int v2_orig = (mr->v_origindex) ? mr->v_origindex[medge->v2] : medge->v2;
- ((uint32_t *)data)[mr->loop_len + e * 2 + 0] = v1_orig;
- ((uint32_t *)data)[mr->loop_len + e * 2 + 1] = v2_orig;
+ EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr)
+ {
+ int v1_orig = (mr->v_origindex) ? mr->v_origindex[med->v1] : med->v1;
+ int v2_orig = (mr->v_origindex) ? mr->v_origindex[med->v2] : med->v2;
+ ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = v1_orig;
+ ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = v2_orig;
+ }
+ EXTRACT_LEDGE_FOREACH_MESH_END;
}
-static void extract_vert_idx_lvert_mesh(const MeshRenderData *mr,
- int v,
- const MVert *UNUSED(mvert),
- void *data)
+static void extract_vert_idx_iter_lvert_mesh(const MeshRenderData *mr,
+ const ExtractLVertMesh_Params *params,
+ void *data)
{
- int v_idx = mr->lverts[v];
- int v_orig = (mr->v_origindex) ? mr->v_origindex[v_idx] : v_idx;
- ((uint32_t *)data)[mr->loop_len + mr->edge_loose_len * 2 + v] = v_orig;
+ const int offset = mr->loop_len + (mr->edge_loose_len * 2);
+ EXTRACT_LVERT_FOREACH_MESH_BEGIN(med, lvert_index, params, mr)
+ {
+ const int v_index = mr->lverts[lvert_index];
+ const int v_orig = (mr->v_origindex) ? mr->v_origindex[v_index] : v_index;
+ ((uint32_t *)data)[offset + lvert_index] = v_orig;
+ }
+ EXTRACT_LVERT_FOREACH_MESH_END;
}
static const MeshExtract extract_poly_idx = {
- extract_select_idx_init,
- NULL,
- NULL,
- extract_poly_idx_loop_bmesh,
- extract_poly_idx_loop_mesh,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- 0,
- true,
+ .init = extract_select_idx_init,
+ .iter_poly_bm = extract_poly_idx_iter_poly_bm,
+ .iter_poly_mesh = extract_poly_idx_iter_poly_mesh,
+ .data_flag = 0,
+ .use_threading = true,
};
static const MeshExtract extract_edge_idx = {
- extract_select_idx_init,
- NULL,
- NULL,
- extract_edge_idx_loop_bmesh,
- extract_edge_idx_loop_mesh,
- extract_edge_idx_ledge_bmesh,
- extract_edge_idx_ledge_mesh,
- NULL,
- NULL,
- NULL,
- 0,
- true,
+ .init = extract_select_idx_init,
+ .iter_poly_bm = extract_edge_idx_iter_poly_bm,
+ .iter_poly_mesh = extract_edge_idx_iter_poly_mesh,
+ .iter_ledge_bm = extract_edge_idx_iter_ledge_bm,
+ .iter_ledge_mesh = extract_edge_idx_iter_ledge_mesh,
+ .data_flag = 0,
+ .use_threading = true,
};
static const MeshExtract extract_vert_idx = {
- extract_select_idx_init,
- NULL,
- NULL,
- extract_vert_idx_loop_bmesh,
- extract_vert_idx_loop_mesh,
- extract_vert_idx_ledge_bmesh,
- extract_vert_idx_ledge_mesh,
- extract_vert_idx_lvert_bmesh,
- extract_vert_idx_lvert_mesh,
- NULL,
- 0,
- true,
+ .init = extract_select_idx_init,
+ .iter_poly_bm = extract_vert_idx_iter_poly_bm,
+ .iter_poly_mesh = extract_vert_idx_iter_poly_mesh,
+ .iter_ledge_bm = extract_vert_idx_iter_ledge_bm,
+ .iter_ledge_mesh = extract_vert_idx_iter_ledge_mesh,
+ .iter_lvert_bm = extract_vert_idx_iter_lvert_bm,
+ .iter_lvert_mesh = extract_vert_idx_iter_lvert_mesh,
+ .data_flag = 0,
+ .use_threading = true,
};
static void *extract_select_fdot_idx_init(const MeshRenderData *mr, void *buf)
@@ -4516,37 +4943,43 @@ static void *extract_select_fdot_idx_init(const MeshRenderData *mr, void *buf)
return vbo->data;
}
-static void extract_fdot_idx_loop_bmesh(const MeshRenderData *UNUSED(mr),
- int UNUSED(l),
- BMLoop *loop,
- void *data)
+static void extract_fdot_idx_iter_poly_bm(const MeshRenderData *mr,
+ const ExtractPolyBMesh_Params *params,
+ void *data)
{
- ((uint32_t *)data)[BM_elem_index_get(loop->f)] = BM_elem_index_get(loop->f);
+ EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr)
+ {
+ ((uint32_t *)data)[f_index] = f_index;
+ }
+ EXTRACT_POLY_FOREACH_BM_END;
}
-static void extract_fdot_idx_loop_mesh(const MeshRenderData *mr,
- int UNUSED(l),
- const MLoop *UNUSED(mloop),
- int p,
- const MPoly *UNUSED(mpoly),
- void *data)
+static void extract_fdot_idx_iter_poly_mesh(const MeshRenderData *mr,
+ const ExtractPolyMesh_Params *params,
+ void *data)
{
- ((uint32_t *)data)[p] = (mr->p_origindex) ? mr->p_origindex[p] : p;
+ if (mr->p_origindex != NULL) {
+ EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr)
+ {
+ ((uint32_t *)data)[mp_index] = mr->p_origindex[mp_index];
+ }
+ EXTRACT_POLY_FOREACH_MESH_END;
+ }
+ else {
+ EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr)
+ {
+ ((uint32_t *)data)[mp_index] = mp_index;
+ }
+ EXTRACT_POLY_FOREACH_MESH_END;
+ }
}
static const MeshExtract extract_fdot_idx = {
- extract_select_fdot_idx_init,
- NULL,
- NULL,
- extract_fdot_idx_loop_bmesh,
- extract_fdot_idx_loop_mesh,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- 0,
- true,
+ .init = extract_select_fdot_idx_init,
+ .iter_poly_bm = extract_fdot_idx_iter_poly_bm,
+ .iter_poly_mesh = extract_fdot_idx_iter_poly_mesh,
+ .data_flag = 0,
+ .use_threading = true,
};
/** \} */
@@ -4589,9 +5022,9 @@ static ExtractTaskData *extract_task_data_create_mesh_extract(const MeshRenderDa
taskdata->extract = extract;
taskdata->buf = buf;
- /* ExtractUserData is shared between the iterations as it holds counters to detect if the
- * extraction is finished. To make sure the duplication of the userdata does not create a new
- * instance of the counters we allocate the userdata in its own container.
+ /* #ExtractUserData is shared between the iterations as it holds counters to detect if the
+ * extraction is finished. To make sure the duplication of the user_data does not create a new
+ * instance of the counters we allocate the user_data in its own container.
*
* This structure makes sure that when extract_init is called, that the user data of all
* iterations are updated. */
@@ -4628,67 +5061,69 @@ BLI_INLINE void mesh_extract_iter(const MeshRenderData *mr,
switch (mr->extract_type) {
case MR_EXTRACT_BMESH:
if (iter_type & MR_ITER_LOOPTRI) {
- int t_end = min_ii(mr->tri_len, end);
- for (int t = start; t < t_end; t++) {
- BMLoop **elt = &mr->edit_bmesh->looptris[t][0];
- extract->iter_looptri_bm(mr, t, elt, user_data);
- }
+ extract->iter_looptri_bm(mr,
+ &(const ExtractTriBMesh_Params){
+ .looptris = mr->edit_bmesh->looptris,
+ .tri_range = {start, min_ii(mr->tri_len, end)},
+ },
+ user_data);
}
- if (iter_type & MR_ITER_LOOP) {
- int l_end = min_ii(mr->poly_len, end);
- for (int f = start; f < l_end; f++) {
- BMFace *efa = BM_face_at_index(mr->bm, f);
- BMLoop *loop;
- BMIter l_iter;
- BM_ITER_ELEM (loop, &l_iter, efa, BM_LOOPS_OF_FACE) {
- extract->iter_loop_bm(mr, BM_elem_index_get(loop), loop, user_data);
- }
- }
+ if (iter_type & MR_ITER_POLY) {
+ extract->iter_poly_bm(mr,
+ &(const ExtractPolyBMesh_Params){
+ .poly_range = {start, min_ii(mr->poly_len, end)},
+ },
+ user_data);
}
if (iter_type & MR_ITER_LEDGE) {
- int le_end = min_ii(mr->edge_loose_len, end);
- for (int e = start; e < le_end; e++) {
- BMEdge *eed = BM_edge_at_index(mr->bm, mr->ledges[e]);
- extract->iter_ledge_bm(mr, e, eed, user_data);
- }
+ extract->iter_ledge_bm(mr,
+ &(const ExtractLEdgeBMesh_Params){
+ .ledge = mr->ledges,
+ .ledge_range = {start, min_ii(mr->edge_loose_len, end)},
+ },
+ user_data);
}
if (iter_type & MR_ITER_LVERT) {
- int lv_end = min_ii(mr->vert_loose_len, end);
- for (int v = start; v < lv_end; v++) {
- BMVert *eve = BM_vert_at_index(mr->bm, mr->lverts[v]);
- extract->iter_lvert_bm(mr, v, eve, user_data);
- }
+ extract->iter_lvert_bm(mr,
+ &(const ExtractLVertBMesh_Params){
+ .lvert = mr->lverts,
+ .lvert_range = {start, min_ii(mr->vert_loose_len, end)},
+ },
+ user_data);
}
break;
case MR_EXTRACT_MAPPED:
case MR_EXTRACT_MESH:
if (iter_type & MR_ITER_LOOPTRI) {
- int t_end = min_ii(mr->tri_len, end);
- for (int t = start; t < t_end; t++) {
- extract->iter_looptri(mr, t, &mr->mlooptri[t], user_data);
- }
+ extract->iter_looptri_mesh(mr,
+ &(const ExtractTriMesh_Params){
+ .mlooptri = mr->mlooptri,
+ .tri_range = {start, min_ii(mr->tri_len, end)},
+ },
+ user_data);
}
- if (iter_type & MR_ITER_LOOP) {
- int l_end = min_ii(mr->poly_len, end);
- for (int p = start; p < l_end; p++) {
- const MPoly *mpoly = &mr->mpoly[p];
- int l = mpoly->loopstart;
- for (int i = 0; i < mpoly->totloop; i++, l++) {
- extract->iter_loop(mr, l, &mr->mloop[l], p, mpoly, user_data);
- }
- }
+ if (iter_type & MR_ITER_POLY) {
+ extract->iter_poly_mesh(mr,
+ &(const ExtractPolyMesh_Params){
+ .poly_range = {start, min_ii(mr->poly_len, end)},
+ },
+ user_data);
}
if (iter_type & MR_ITER_LEDGE) {
- int le_end = min_ii(mr->edge_loose_len, end);
- for (int e = start; e < le_end; e++) {
- extract->iter_ledge(mr, e, &mr->medge[mr->ledges[e]], user_data);
- }
+ extract->iter_ledge_mesh(mr,
+ &(const ExtractLEdgeMesh_Params){
+ .ledge = mr->ledges,
+ .ledge_range = {start, min_ii(mr->edge_loose_len, end)},
+ },
+ user_data);
}
if (iter_type & MR_ITER_LVERT) {
- int lv_end = min_ii(mr->vert_loose_len, end);
- for (int v = start; v < lv_end; v++) {
- extract->iter_lvert(mr, v, &mr->mvert[mr->lverts[v]], user_data);
- }
+ extract->iter_lvert_mesh(mr,
+ &(const ExtractLVertMesh_Params){
+ .lvert = mr->lverts,
+ .lvert_range = {start, min_ii(mr->vert_loose_len, end)},
+ },
+ user_data);
}
break;
}
@@ -4915,10 +5350,10 @@ static void extract_task_create(struct TaskGraph *task_graph,
task_graph, task_node_user_data_init, taskdata, MR_ITER_LOOPTRI, i, chunk_size);
}
}
- if (taskdata->iter_type & MR_ITER_LOOP) {
+ if (taskdata->iter_type & MR_ITER_POLY) {
for (int i = 0; i < mr->poly_len; i += chunk_size) {
extract_range_task_create(
- task_graph, task_node_user_data_init, taskdata, MR_ITER_LOOP, i, chunk_size);
+ task_graph, task_node_user_data_init, taskdata, MR_ITER_POLY, i, chunk_size);
}
}
if (taskdata->iter_type & MR_ITER_LEDGE) {
@@ -4973,9 +5408,9 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph,
* `extract_single_threaded_task_node`.
*
* Other extractions will create a node for each loop exceeding 8192 items. these nodes are
- * linked to the `user_data_init_task_node`. the `user_data_init_task_node` prepares the userdata
- * needed for the extraction based on the data extracted from the mesh. counters are used to
- * check if the finalize of a task has to be called.
+ * linked to the `user_data_init_task_node`. the `user_data_init_task_node` prepares the
+ * user_data needed for the extraction based on the data extracted from the mesh.
+ * counters are used to check if the finalize of a task has to be called.
*
* Mesh extraction sub graph
*
diff --git a/source/blender/draw/intern/draw_cache_impl.h b/source/blender/draw/intern/draw_cache_impl.h
index 191d75342d0..5cf1c24af0b 100644
--- a/source/blender/draw/intern/draw_cache_impl.h
+++ b/source/blender/draw/intern/draw_cache_impl.h
@@ -170,6 +170,7 @@ struct GPUBatch **DRW_mesh_batch_cache_get_surface_shaded(struct Mesh *me,
struct GPUBatch **DRW_mesh_batch_cache_get_surface_texpaint(struct Mesh *me);
struct GPUBatch *DRW_mesh_batch_cache_get_surface_texpaint_single(struct Mesh *me);
struct GPUBatch *DRW_mesh_batch_cache_get_surface_vertpaint(struct Mesh *me);
+struct GPUBatch *DRW_mesh_batch_cache_get_surface_sculpt(struct Mesh *me);
struct GPUBatch *DRW_mesh_batch_cache_get_surface_weights(struct Mesh *me);
/* edit-mesh drawing */
struct GPUBatch *DRW_mesh_batch_cache_get_edit_triangles(struct Mesh *me);
@@ -199,6 +200,11 @@ struct GPUBatch *DRW_mesh_batch_cache_get_edituv_facedots(struct Mesh *me);
struct GPUBatch *DRW_mesh_batch_cache_get_uv_edges(struct Mesh *me);
struct GPUBatch *DRW_mesh_batch_cache_get_edit_mesh_analysis(struct Mesh *me);
+/* For direct data access. */
+struct GPUVertBuf *DRW_mesh_batch_cache_pos_vertbuf_get(struct Mesh *me);
+struct GPUVertBuf *DRW_curve_batch_cache_pos_vertbuf_get(struct Curve *cu);
+struct GPUVertBuf *DRW_mball_batch_cache_pos_vertbuf_get(struct Object *ob);
+
int DRW_mesh_material_count_get(struct Mesh *me);
/* See 'common_globals_lib.glsl' for duplicate defines. */
diff --git a/source/blender/draw/intern/draw_cache_impl_curve.c b/source/blender/draw/intern/draw_cache_impl_curve.c
index 8798549a416..73e0ff7ef83 100644
--- a/source/blender/draw/intern/draw_cache_impl_curve.c
+++ b/source/blender/draw/intern/draw_cache_impl_curve.c
@@ -903,6 +903,16 @@ GPUBatch **DRW_curve_batch_cache_get_surface_shaded(struct Curve *cu,
return cache->surf_per_mat;
}
+GPUVertBuf *DRW_curve_batch_cache_pos_vertbuf_get(struct Curve *cu)
+{
+ CurveBatchCache *cache = curve_batch_cache_get(cu);
+ /* Request surface to trigger the vbo filling. Otherwise it may do nothing. */
+ DRW_batch_request(&cache->batch.surfaces);
+
+ DRW_vbo_request(NULL, &cache->ordered.loop_pos_nor);
+ return cache->ordered.loop_pos_nor;
+}
+
GPUBatch *DRW_curve_batch_cache_get_wireframes_face(Curve *cu)
{
CurveBatchCache *cache = curve_batch_cache_get(cu);
diff --git a/source/blender/draw/intern/draw_cache_impl_gpencil.c b/source/blender/draw/intern/draw_cache_impl_gpencil.c
index b4974330043..53e04fb61ee 100644
--- a/source/blender/draw/intern/draw_cache_impl_gpencil.c
+++ b/source/blender/draw/intern/draw_cache_impl_gpencil.c
@@ -349,10 +349,10 @@ static void gpencil_stroke_iter_cb(bGPDlayer *UNUSED(gpl),
}
}
-static void gp_object_verts_count_cb(bGPDlayer *UNUSED(gpl),
- bGPDframe *UNUSED(gpf),
- bGPDstroke *gps,
- void *thunk)
+static void gpencil_object_verts_count_cb(bGPDlayer *UNUSED(gpl),
+ bGPDframe *UNUSED(gpf),
+ bGPDstroke *gps,
+ void *thunk)
{
gpIterData *iter = (gpIterData *)thunk;
@@ -387,7 +387,7 @@ static void gpencil_batches_ensure(Object *ob, GpencilBatchCache *cache, int cfr
.tri_len = 0,
};
BKE_gpencil_visible_stroke_iter(
- NULL, ob, NULL, gp_object_verts_count_cb, &iter, do_onion, cfra);
+ NULL, ob, NULL, gpencil_object_verts_count_cb, &iter, do_onion, cfra);
/* Create VBOs. */
GPUVertFormat *format = gpencil_stroke_format();
@@ -441,10 +441,10 @@ GPUBatch *DRW_cache_gpencil_fills_get(Object *ob, int cfra)
return cache->fill_batch;
}
-static void gp_lines_indices_cb(bGPDlayer *UNUSED(gpl),
- bGPDframe *UNUSED(gpf),
- bGPDstroke *gps,
- void *thunk)
+static void gpencil_lines_indices_cb(bGPDlayer *UNUSED(gpl),
+ bGPDframe *UNUSED(gpf),
+ bGPDstroke *gps,
+ void *thunk)
{
gpIterData *iter = (gpIterData *)thunk;
int pts_len = gps->totpoints + gpencil_stroke_is_cyclic(gps);
@@ -477,7 +477,8 @@ GPUBatch *DRW_cache_gpencil_face_wireframe_get(Object *ob)
/* IMPORTANT: Keep in sync with gpencil_edit_batches_ensure() */
bool do_onion = true;
- BKE_gpencil_visible_stroke_iter(NULL, ob, NULL, gp_lines_indices_cb, &iter, do_onion, cfra);
+ BKE_gpencil_visible_stroke_iter(
+ NULL, ob, NULL, gpencil_lines_indices_cb, &iter, do_onion, cfra);
GPUIndexBuf *ibo = GPU_indexbuf_build(&iter.ibo);
diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c
index 99e285a18f1..ea1717f0684 100644
--- a/source/blender/draw/intern/draw_cache_impl_mesh.c
+++ b/source/blender/draw/intern/draw_cache_impl_mesh.c
@@ -112,6 +112,21 @@ BLI_INLINE const CustomData *mesh_cd_ldata_get_from_mesh(const Mesh *me)
return &me->ldata;
}
+BLI_INLINE const CustomData *mesh_cd_vdata_get_from_mesh(const Mesh *me)
+{
+ switch ((eMeshWrapperType)me->runtime.wrapper_type) {
+ case ME_WRAPPER_TYPE_MDATA:
+ return &me->vdata;
+ break;
+ case ME_WRAPPER_TYPE_BMESH:
+ return &me->edit_mesh->bm->vdata;
+ break;
+ }
+
+ BLI_assert(0);
+ return &me->vdata;
+}
+
static void mesh_cd_calc_active_uv_layer(const Mesh *me, DRW_MeshCDMask *cd_used)
{
const Mesh *me_final = (me->edit_mesh) ? me->edit_mesh->mesh_eval_final : me;
@@ -135,7 +150,19 @@ static void mesh_cd_calc_active_mask_uv_layer(const Mesh *me, DRW_MeshCDMask *cd
static void mesh_cd_calc_active_vcol_layer(const Mesh *me, DRW_MeshCDMask *cd_used)
{
const Mesh *me_final = (me->edit_mesh) ? me->edit_mesh->mesh_eval_final : me;
- const CustomData *cd_ldata = mesh_cd_ldata_get_from_mesh(me_final);
+ const CustomData *cd_vdata = mesh_cd_vdata_get_from_mesh(me_final);
+
+ int layer = CustomData_get_active_layer(cd_vdata, CD_PROP_COLOR);
+ if (layer != -1) {
+ cd_used->sculpt_vcol |= (1 << layer);
+ }
+}
+
+static void mesh_cd_calc_active_mloopcol_layer(const Mesh *me, DRW_MeshCDMask *cd_used)
+{
+ const Mesh *me_final = (me->edit_mesh) ? me->edit_mesh->mesh_eval_final : me;
+ const CustomData *cd_ldata = &me_final->ldata;
+
int layer = CustomData_get_active_layer(cd_ldata, CD_MLOOPCOL);
if (layer != -1) {
cd_used->vcol |= (1 << layer);
@@ -148,6 +175,7 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me,
{
const Mesh *me_final = (me->edit_mesh) ? me->edit_mesh->mesh_eval_final : me;
const CustomData *cd_ldata = mesh_cd_ldata_get_from_mesh(me_final);
+ const CustomData *cd_vdata = mesh_cd_vdata_get_from_mesh(me_final);
/* See: DM_vertex_attributes_from_gpu for similar logic */
DRW_MeshCDMask cd_used;
@@ -175,6 +203,11 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me,
layer = CustomData_get_named_layer(cd_ldata, CD_MLOOPCOL, name);
type = CD_MCOL;
}
+
+ if (layer == -1) {
+ layer = CustomData_get_named_layer(cd_vdata, CD_PROP_COLOR, name);
+ type = CD_PROP_COLOR;
+ }
#if 0 /* Tangents are always from UV's - this will never happen. */
if (layer == -1) {
layer = CustomData_get_named_layer(cd_ldata, CD_TANGENT, name);
@@ -222,7 +255,20 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me,
}
break;
}
+ case CD_PROP_COLOR: {
+ /* Sculpt Vertex Colors */
+ if (layer == -1) {
+ layer = (name[0] != '\0') ?
+ CustomData_get_named_layer(cd_vdata, CD_PROP_COLOR, name) :
+ CustomData_get_render_layer(cd_vdata, CD_PROP_COLOR);
+ }
+ if (layer != -1) {
+ cd_used.sculpt_vcol |= (1 << layer);
+ }
+ break;
+ }
case CD_MCOL: {
+ /* Vertex Color Data */
if (layer == -1) {
layer = (name[0] != '\0') ? CustomData_get_named_layer(cd_ldata, CD_MLOOPCOL, name) :
CustomData_get_render_layer(cd_ldata, CD_MLOOPCOL);
@@ -230,6 +276,7 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me,
if (layer != -1) {
cd_used.vcol |= (1 << layer);
}
+
break;
}
case CD_ORCO: {
@@ -679,10 +726,22 @@ static void texpaint_request_active_vcol(MeshBatchCache *cache, Mesh *me)
{
DRW_MeshCDMask cd_needed;
mesh_cd_layers_type_clear(&cd_needed);
- mesh_cd_calc_active_vcol_layer(me, &cd_needed);
+ mesh_cd_calc_active_mloopcol_layer(me, &cd_needed);
BLI_assert(cd_needed.vcol != 0 &&
- "No vcol layer available in vertpaint, but batches requested anyway!");
+ "No MLOOPCOL layer available in vertpaint, but batches requested anyway!");
+
+ mesh_cd_layers_type_merge(&cache->cd_needed, cd_needed);
+}
+
+static void sculpt_request_active_vcol(MeshBatchCache *cache, Mesh *me)
+{
+ DRW_MeshCDMask cd_needed;
+ mesh_cd_layers_type_clear(&cd_needed);
+ mesh_cd_calc_active_vcol_layer(me, &cd_needed);
+
+ BLI_assert(cd_needed.sculpt_vcol != 0 &&
+ "No MPropCol layer available in Sculpt, but batches requested anyway!");
mesh_cd_layers_type_merge(&cache->cd_needed, cd_needed);
}
@@ -799,6 +858,14 @@ GPUBatch *DRW_mesh_batch_cache_get_surface_vertpaint(Mesh *me)
return DRW_batch_request(&cache->batch.surface);
}
+GPUBatch *DRW_mesh_batch_cache_get_surface_sculpt(Mesh *me)
+{
+ MeshBatchCache *cache = mesh_batch_cache_get(me);
+ sculpt_request_active_vcol(cache, me);
+ mesh_batch_cache_add_request(cache, MBC_SURFACE);
+ return DRW_batch_request(&cache->batch.surface);
+}
+
int DRW_mesh_material_count_get(Mesh *me)
{
return mesh_render_mat_len_get(me);
@@ -810,6 +877,23 @@ int DRW_mesh_material_count_get(Mesh *me)
/** \name Edit Mode API
* \{ */
+GPUVertBuf *DRW_mesh_batch_cache_pos_vertbuf_get(Mesh *me)
+{
+ MeshBatchCache *cache = mesh_batch_cache_get(me);
+ /* Request surface to trigger the vbo filling. Otherwise it may do nothing. */
+ mesh_batch_cache_add_request(cache, MBC_SURFACE);
+ DRW_batch_request(&cache->batch.surface);
+
+ DRW_vbo_request(NULL, &cache->final.vbo.pos_nor);
+ return cache->final.vbo.pos_nor;
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Edit Mode API
+ * \{ */
+
GPUBatch *DRW_mesh_batch_cache_get_edit_triangles(Mesh *me)
{
MeshBatchCache *cache = mesh_batch_cache_get(me);
@@ -1120,7 +1204,9 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
if (cache->cd_used.orco != cache->cd_needed.orco) {
GPU_VERTBUF_DISCARD_SAFE(mbuffercache->vbo.orco);
}
- if ((cache->cd_used.vcol & cache->cd_needed.vcol) != cache->cd_needed.vcol) {
+ if (((cache->cd_used.vcol & cache->cd_needed.vcol) != cache->cd_needed.vcol) ||
+ ((cache->cd_used.sculpt_vcol & cache->cd_needed.sculpt_vcol) !=
+ cache->cd_needed.sculpt_vcol)) {
GPU_VERTBUF_DISCARD_SAFE(mbuffercache->vbo.vcol);
}
}
@@ -1201,7 +1287,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
if (cache->cd_used.uv != 0) {
DRW_vbo_request(cache->batch.surface, &mbufcache->vbo.uv);
}
- if (cache->cd_used.vcol != 0) {
+ if (cache->cd_used.vcol != 0 || cache->cd_used.sculpt_vcol != 0) {
DRW_vbo_request(cache->batch.surface, &mbufcache->vbo.vcol);
}
}
@@ -1269,7 +1355,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
if ((cache->cd_used.tan != 0) || (cache->cd_used.tan_orco != 0)) {
DRW_vbo_request(cache->surface_per_mat[i], &mbufcache->vbo.tan);
}
- if (cache->cd_used.vcol != 0) {
+ if (cache->cd_used.vcol != 0 || cache->cd_used.sculpt_vcol != 0) {
DRW_vbo_request(cache->surface_per_mat[i], &mbufcache->vbo.vcol);
}
if (cache->cd_used.orco != 0) {
diff --git a/source/blender/draw/intern/draw_cache_impl_metaball.c b/source/blender/draw/intern/draw_cache_impl_metaball.c
index c14e66c2b47..076d32ffe1f 100644
--- a/source/blender/draw/intern/draw_cache_impl_metaball.c
+++ b/source/blender/draw/intern/draw_cache_impl_metaball.c
@@ -274,6 +274,18 @@ struct GPUBatch *DRW_metaball_batch_cache_get_edge_detection(struct Object *ob,
return cache->edge_detection;
}
+struct GPUVertBuf *DRW_mball_batch_cache_pos_vertbuf_get(Object *ob)
+{
+ if (!BKE_mball_is_basis(ob)) {
+ return NULL;
+ }
+
+ MetaBall *mb = ob->data;
+ MetaBallBatchCache *cache = metaball_batch_cache_get(mb);
+
+ return mball_batch_cache_get_pos_and_normals(ob, cache);
+}
+
int DRW_metaball_material_count_get(MetaBall *mb)
{
return max_ii(1, mb->totcol);
diff --git a/source/blender/draw/intern/draw_cache_inline.h b/source/blender/draw/intern/draw_cache_inline.h
index a067434f3bb..67f44b5fb0c 100644
--- a/source/blender/draw/intern/draw_cache_inline.h
+++ b/source/blender/draw/intern/draw_cache_inline.h
@@ -90,17 +90,19 @@ BLI_INLINE void DRW_vbo_request(GPUBatch *batch, GPUVertBuf **vbo)
if (*vbo == NULL) {
*vbo = MEM_callocN(sizeof(GPUVertBuf), "GPUVertBuf");
}
- /* HACK set first vbo if not init. */
- if (batch->verts[0] == NULL) {
- GPU_batch_vao_cache_clear(batch);
- batch->verts[0] = *vbo;
- }
- else {
- /* HACK: bypass assert */
- int vbo_vert_len = (*vbo)->vertex_len;
- (*vbo)->vertex_len = batch->verts[0]->vertex_len;
- GPU_batch_vertbuf_add(batch, *vbo);
- (*vbo)->vertex_len = vbo_vert_len;
+ if (batch != NULL) {
+ /* HACK set first vbo if not init. */
+ if (batch->verts[0] == NULL) {
+ GPU_batch_vao_cache_clear(batch);
+ batch->verts[0] = *vbo;
+ }
+ else {
+ /* HACK: bypass assert */
+ int vbo_vert_len = (*vbo)->vertex_len;
+ (*vbo)->vertex_len = batch->verts[0]->vertex_len;
+ GPU_batch_vertbuf_add(batch, *vbo);
+ (*vbo)->vertex_len = vbo_vert_len;
+ }
}
}
diff --git a/source/blender/draw/intern/draw_common.h b/source/blender/draw/intern/draw_common.h
index 656d72b2808..81c0e97a1a8 100644
--- a/source/blender/draw/intern/draw_common.h
+++ b/source/blender/draw/intern/draw_common.h
@@ -176,6 +176,13 @@ struct DRWShadingGroup *DRW_shgroup_hair_create_sub(struct Object *object,
struct ParticleSystem *psys,
struct ModifierData *md,
struct DRWShadingGroup *shgrp);
+struct GPUVertBuf *DRW_hair_pos_buffer_get(struct Object *object,
+ struct ParticleSystem *psys,
+ struct ModifierData *md);
+void DRW_hair_duplimat_get(struct Object *object,
+ struct ParticleSystem *psys,
+ struct ModifierData *md,
+ float (*dupli_mat)[4]);
void DRW_hair_init(void);
void DRW_hair_update(void);
diff --git a/source/blender/draw/intern/draw_hair.c b/source/blender/draw/intern/draw_hair.c
index 2fdaf0d5345..6cfba0e2a78 100644
--- a/source/blender/draw/intern/draw_hair.c
+++ b/source/blender/draw/intern/draw_hair.c
@@ -124,32 +124,109 @@ void DRW_hair_init(void)
}
}
-DRWShadingGroup *DRW_shgroup_hair_create_sub(Object *object,
- ParticleSystem *psys,
- ModifierData *md,
- DRWShadingGroup *shgrp_parent)
+static ParticleHairCache *drw_hair_particle_cache_get(
+ Object *object, ParticleSystem *psys, ModifierData *md, int subdiv, int thickness_res)
+{
+ bool update;
+ ParticleHairCache *cache;
+ if (psys) {
+ /* Old particle hair. */
+ update = particles_ensure_procedural_data(object, psys, md, &cache, subdiv, thickness_res);
+ }
+ else {
+ /* New hair object. */
+ update = hair_ensure_procedural_data(object, &cache, subdiv, thickness_res);
+ }
+
+ if (update) {
+ int final_points_len = cache->final[subdiv].strands_res * cache->strands_len;
+ if (final_points_len > 0) {
+ GPUShader *tf_shader = hair_refine_shader_get(PART_REFINE_CATMULL_ROM);
+
+#ifdef USE_TRANSFORM_FEEDBACK
+ DRWShadingGroup *tf_shgrp = DRW_shgroup_transform_feedback_create(
+ tf_shader, g_tf_pass, cache->final[subdiv].proc_buf);
+#else
+ DRWShadingGroup *tf_shgrp = DRW_shgroup_create(tf_shader, g_tf_pass);
+
+ ParticleRefineCall *pr_call = MEM_mallocN(sizeof(*pr_call), __func__);
+ pr_call->next = g_tf_calls;
+ pr_call->vbo = cache->final[subdiv].proc_buf;
+ pr_call->shgrp = tf_shgrp;
+ pr_call->vert_len = final_points_len;
+ g_tf_calls = pr_call;
+ DRW_shgroup_uniform_int(tf_shgrp, "targetHeight", &g_tf_target_height, 1);
+ DRW_shgroup_uniform_int(tf_shgrp, "targetWidth", &g_tf_target_width, 1);
+ DRW_shgroup_uniform_int(tf_shgrp, "idOffset", &g_tf_id_offset, 1);
+#endif
+
+ DRW_shgroup_uniform_texture(tf_shgrp, "hairPointBuffer", cache->point_tex);
+ DRW_shgroup_uniform_texture(tf_shgrp, "hairStrandBuffer", cache->strand_tex);
+ DRW_shgroup_uniform_texture(tf_shgrp, "hairStrandSegBuffer", cache->strand_seg_tex);
+ DRW_shgroup_uniform_int(tf_shgrp, "hairStrandsRes", &cache->final[subdiv].strands_res, 1);
+ DRW_shgroup_call_procedural_points(tf_shgrp, NULL, final_points_len);
+ }
+ }
+ return cache;
+}
+
+/* Note: Only valid after DRW_hair_update(). */
+GPUVertBuf *DRW_hair_pos_buffer_get(Object *object, ParticleSystem *psys, ModifierData *md)
{
- /* TODO(fclem): Pass the scene as parameter */
const DRWContextState *draw_ctx = DRW_context_state_get();
Scene *scene = draw_ctx->scene;
- float dupli_mat[4][4];
- Object *dupli_parent = DRW_object_get_dupli_parent(object);
- DupliObject *dupli_object = DRW_object_get_dupli(object);
int subdiv = scene->r.hair_subdiv;
int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2;
- ParticleHairCache *hair_cache;
- bool need_ft_update;
+ ParticleHairCache *cache = drw_hair_particle_cache_get(object, psys, md, subdiv, thickness_res);
+
+ return cache->final[subdiv].proc_buf;
+}
+
+void DRW_hair_duplimat_get(Object *object,
+ ParticleSystem *psys,
+ ModifierData *UNUSED(md),
+ float (*dupli_mat)[4])
+{
+ Object *dupli_parent = DRW_object_get_dupli_parent(object);
+ DupliObject *dupli_object = DRW_object_get_dupli(object);
+
if (psys) {
- /* Old particle hair. */
- need_ft_update = particles_ensure_procedural_data(
- object, psys, md, &hair_cache, subdiv, thickness_res);
+ if ((dupli_parent != NULL) && (dupli_object != NULL)) {
+ if (dupli_object->type & OB_DUPLICOLLECTION) {
+ copy_m4_m4(dupli_mat, dupli_parent->obmat);
+ }
+ else {
+ copy_m4_m4(dupli_mat, dupli_object->ob->obmat);
+ invert_m4(dupli_mat);
+ mul_m4_m4m4(dupli_mat, object->obmat, dupli_mat);
+ }
+ }
+ else {
+ unit_m4(dupli_mat);
+ }
}
else {
/* New hair object. */
- need_ft_update = hair_ensure_procedural_data(object, &hair_cache, subdiv, thickness_res);
+ copy_m4_m4(dupli_mat, object->obmat);
}
+}
+
+DRWShadingGroup *DRW_shgroup_hair_create_sub(Object *object,
+ ParticleSystem *psys,
+ ModifierData *md,
+ DRWShadingGroup *shgrp_parent)
+{
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ Scene *scene = draw_ctx->scene;
+ float dupli_mat[4][4];
+
+ int subdiv = scene->r.hair_subdiv;
+ int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2;
+
+ ParticleHairCache *hair_cache = drw_hair_particle_cache_get(
+ object, psys, md, subdiv, thickness_res);
DRWShadingGroup *shgrp = DRW_shgroup_create_sub(shgrp_parent);
@@ -177,25 +254,7 @@ DRWShadingGroup *DRW_shgroup_hair_create_sub(Object *object,
DRW_shgroup_uniform_texture(shgrp, "ac", g_dummy_texture);
}
- if (psys) {
- if ((dupli_parent != NULL) && (dupli_object != NULL)) {
- if (dupli_object->type & OB_DUPLICOLLECTION) {
- copy_m4_m4(dupli_mat, dupli_parent->obmat);
- }
- else {
- copy_m4_m4(dupli_mat, dupli_object->ob->obmat);
- invert_m4(dupli_mat);
- mul_m4_m4m4(dupli_mat, object->obmat, dupli_mat);
- }
- }
- else {
- unit_m4(dupli_mat);
- }
- }
- else {
- /* New hair object. */
- copy_m4_m4(dupli_mat, object->obmat);
- }
+ DRW_hair_duplimat_get(object, psys, md, dupli_mat);
/* Get hair shape parameters. */
float hair_rad_shape, hair_rad_root, hair_rad_tip;
@@ -229,38 +288,6 @@ DRWShadingGroup *DRW_shgroup_hair_create_sub(Object *object,
GPUBatch *geom = hair_cache->final[subdiv].proc_hairs[thickness_res - 1];
DRW_shgroup_call_no_cull(shgrp, geom, object);
- /* Transform Feedback subdiv. */
- if (need_ft_update) {
- int final_points_len = hair_cache->final[subdiv].strands_res * hair_cache->strands_len;
- if (final_points_len) {
- GPUShader *tf_shader = hair_refine_shader_get(PART_REFINE_CATMULL_ROM);
-
-#ifdef USE_TRANSFORM_FEEDBACK
- DRWShadingGroup *tf_shgrp = DRW_shgroup_transform_feedback_create(
- tf_shader, g_tf_pass, hair_cache->final[subdiv].proc_buf);
-#else
- DRWShadingGroup *tf_shgrp = DRW_shgroup_create(tf_shader, g_tf_pass);
-
- ParticleRefineCall *pr_call = MEM_mallocN(sizeof(*pr_call), __func__);
- pr_call->next = g_tf_calls;
- pr_call->vbo = hair_cache->final[subdiv].proc_buf;
- pr_call->shgrp = tf_shgrp;
- pr_call->vert_len = final_points_len;
- g_tf_calls = pr_call;
- DRW_shgroup_uniform_int(tf_shgrp, "targetHeight", &g_tf_target_height, 1);
- DRW_shgroup_uniform_int(tf_shgrp, "targetWidth", &g_tf_target_width, 1);
- DRW_shgroup_uniform_int(tf_shgrp, "idOffset", &g_tf_id_offset, 1);
-#endif
-
- DRW_shgroup_uniform_texture(tf_shgrp, "hairPointBuffer", hair_cache->point_tex);
- DRW_shgroup_uniform_texture(tf_shgrp, "hairStrandBuffer", hair_cache->strand_tex);
- DRW_shgroup_uniform_texture(tf_shgrp, "hairStrandSegBuffer", hair_cache->strand_seg_tex);
- DRW_shgroup_uniform_int(
- tf_shgrp, "hairStrandsRes", &hair_cache->final[subdiv].strands_res, 1);
- DRW_shgroup_call_procedural_points(tf_shgrp, NULL, final_points_len);
- }
- }
-
return shgrp;
}
diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c
index 10ef8d9c4c8..3e42c4cdb23 100644
--- a/source/blender/draw/intern/draw_manager.c
+++ b/source/blender/draw/intern/draw_manager.c
@@ -373,6 +373,8 @@ void DRW_render_viewport_size_set(int size[2])
{
DST.size[0] = size[0];
DST.size[1] = size[1];
+ DST.inv_size[0] = 1.0f / size[0];
+ DST.inv_size[1] = 1.0f / size[1];
}
const float *DRW_viewport_size_get(void)
@@ -1635,13 +1637,6 @@ bool DRW_render_check_grease_pencil(Depsgraph *depsgraph)
return false;
}
-static void drw_view_reset(void)
-{
- DST.view_default = NULL;
- DST.view_active = NULL;
- DST.view_previous = NULL;
-}
-
static void DRW_render_gpencil_to_image(RenderEngine *engine,
struct RenderLayer *render_layer,
const rcti *rect)
@@ -1719,7 +1714,7 @@ void DRW_render_gpencil(struct RenderEngine *engine, struct Depsgraph *depsgraph
for (RenderView *render_view = render_result->views.first; render_view != NULL;
render_view = render_view->next) {
RE_SetActiveRenderView(render, render_view->name);
- drw_view_reset();
+ DRW_view_reset();
DST.buffer_finish_called = false;
DRW_render_gpencil_to_image(engine, render_layer, &render_rect);
}
@@ -1827,7 +1822,7 @@ void DRW_render_to_image(RenderEngine *engine, struct Depsgraph *depsgraph)
for (RenderView *render_view = render_result->views.first; render_view != NULL;
render_view = render_view->next) {
RE_SetActiveRenderView(render, render_view->name);
- drw_view_reset();
+ DRW_view_reset();
engine_type->draw_engine->render_to_image(data, engine, render_layer, &render_rect);
DST.buffer_finish_called = false;
}
@@ -1949,6 +1944,28 @@ void DRW_custom_pipeline(DrawEngineType *draw_engine_type,
#endif
}
+/* Used when the render engine want to redo another cache populate inside the same render frame. */
+void DRW_cache_restart(void)
+{
+ /* Save viewport size. */
+ float size[2], inv_size[2];
+ copy_v2_v2(size, DST.size);
+ copy_v2_v2(inv_size, DST.inv_size);
+
+ /* Force cache to reset. */
+ drw_viewport_cache_resize();
+
+ drw_viewport_var_init();
+
+ DST.buffer_finish_called = false;
+
+ DRW_hair_init();
+
+ /* Restore. */
+ copy_v2_v2(DST.size, size);
+ copy_v2_v2(DST.inv_size, inv_size);
+}
+
static struct DRWSelectBuffer {
struct GPUFrameBuffer *framebuffer_depth_only;
struct GPUTexture *texture_depth;
@@ -2374,6 +2391,17 @@ void DRW_draw_depth_loop_gpencil(struct Depsgraph *depsgraph,
void DRW_draw_select_id(Depsgraph *depsgraph, ARegion *region, View3D *v3d, const rcti *rect)
{
+ SELECTID_Context *sel_ctx = DRW_select_engine_context_get();
+ GPUViewport *viewport = WM_draw_region_get_viewport(region);
+ if (!viewport) {
+ /* Selection engine requires a viewport.
+ * TODO (germano): This should be done internally in the engine. */
+ sel_ctx->is_dirty = true;
+ sel_ctx->objects_drawn_len = 0;
+ sel_ctx->index_drawn_len = 1;
+ return;
+ }
+
Scene *scene = DEG_get_evaluated_scene(depsgraph);
ViewLayer *view_layer = DEG_get_evaluated_view_layer(depsgraph);
@@ -2394,14 +2422,13 @@ void DRW_draw_select_id(Depsgraph *depsgraph, ARegion *region, View3D *v3d, cons
drw_context_state_init();
/* Setup viewport */
- DST.viewport = WM_draw_region_get_viewport(region);
+ DST.viewport = viewport;
drw_viewport_var_init();
/* Update ubos */
DRW_globals_update();
/* Init Select Engine */
- struct SELECTID_Context *sel_ctx = DRW_select_engine_context_get();
sel_ctx->last_rect = *rect;
use_drw_engine(&draw_engine_select_type);
diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c
index 07fb97236fb..3d83b918757 100644
--- a/source/blender/draw/intern/draw_manager_data.c
+++ b/source/blender/draw/intern/draw_manager_data.c
@@ -76,7 +76,7 @@ static void draw_call_sort(DRWCommand *array, DRWCommand *array_tmp, int array_l
for (int i = 1; i < ARRAY_SIZE(idx); i++) {
idx[i] += idx[i - 1];
}
- /* Traverse in reverse to not change the order of the resource ids. */
+ /* Traverse in reverse to not change the order of the resource ID's. */
for (int src = array_len - 1; src >= 0; src--) {
array_tmp[--idx[KEY(array[src])]] = array[src];
}
@@ -116,7 +116,7 @@ void drw_resource_buffer_finish(ViewportMemoryPool *vmempool)
vmempool->ubo_len = ubo_len;
}
- /* Remove unecessary buffers */
+ /* Remove unnecessary buffers */
for (int i = ubo_len; i < vmempool->ubo_len; i++) {
GPU_uniformbuffer_free(vmempool->matrices_ubo[i]);
GPU_uniformbuffer_free(vmempool->obinfos_ubo[i]);
@@ -151,7 +151,7 @@ void drw_resource_buffer_finish(ViewportMemoryPool *vmempool)
BLI_memblock_iternew(vmempool->commands, &iter);
while ((chunk = BLI_memblock_iterstep(&iter))) {
bool sortable = true;
- /* We can only sort chunks that contain DRWCommandDraw only. */
+ /* We can only sort chunks that contain #DRWCommandDraw only. */
for (int i = 0; i < ARRAY_SIZE(chunk->command_type) && sortable; i++) {
if (chunk->command_type[i] != 0) {
sortable = false;
@@ -179,7 +179,7 @@ static void drw_shgroup_uniform_create_ex(DRWShadingGroup *shgroup,
int arraysize)
{
if (loc == -1) {
- /* Nice to enable eventually, for now eevee uses uniforms that might not exist. */
+ /* Nice to enable eventually, for now EEVEE uses uniforms that might not exist. */
// BLI_assert(0);
return;
}
@@ -262,11 +262,19 @@ void DRW_shgroup_uniform_texture(DRWShadingGroup *shgroup, const char *name, con
DRW_shgroup_uniform_texture_ex(shgroup, name, tex, GPU_SAMPLER_MAX);
}
-void DRW_shgroup_uniform_texture_ref(DRWShadingGroup *shgroup, const char *name, GPUTexture **tex)
+void DRW_shgroup_uniform_texture_ref_ex(DRWShadingGroup *shgroup,
+ const char *name,
+ GPUTexture **tex,
+ eGPUSamplerState sampler_state)
{
BLI_assert(tex != NULL);
int loc = GPU_shader_get_texture_binding(shgroup->shader, name);
- drw_shgroup_uniform_create_ex(shgroup, loc, DRW_UNIFORM_TEXTURE_REF, tex, GPU_SAMPLER_MAX, 0, 1);
+ drw_shgroup_uniform_create_ex(shgroup, loc, DRW_UNIFORM_TEXTURE_REF, tex, sampler_state, 0, 1);
+}
+
+void DRW_shgroup_uniform_texture_ref(DRWShadingGroup *shgroup, const char *name, GPUTexture **tex)
+{
+ DRW_shgroup_uniform_texture_ref_ex(shgroup, name, tex, GPU_SAMPLER_MAX);
}
void DRW_shgroup_uniform_block(DRWShadingGroup *shgroup,
@@ -424,7 +432,7 @@ void DRW_shgroup_uniform_vec4_array_copy(DRWShadingGroup *shgroup,
int location = GPU_shader_get_uniform(shgroup->shader, name);
if (location == -1) {
- /* Nice to enable eventually, for now eevee uses uniforms that might not exist. */
+ /* Nice to enable eventually, for now EEVEE uses uniforms that might not exist. */
// BLI_assert(0);
return;
}
@@ -530,6 +538,11 @@ static void drw_call_culling_init(DRWCullingState *cull, Object *ob)
mul_v3_m4v3(corner, ob->obmat, bbox->vec[0]);
mul_m4_v3(ob->obmat, cull->bsphere.center);
cull->bsphere.radius = len_v3v3(cull->bsphere.center, corner);
+
+ /* Bypass test for very large objects (see T67319). */
+ if (UNLIKELY(cull->bsphere.radius > 1e12)) {
+ cull->bsphere.radius = -1.0f;
+ }
}
else {
/* Bypass test. */
@@ -569,7 +582,7 @@ uint32_t DRW_object_resource_id_get(Object *UNUSED(ob))
/* Handle not yet allocated. Return next handle. */
handle = DST.resource_handle;
}
- return handle;
+ return handle & ~(1 << 31);
}
static DRWResourceHandle drw_resource_handle(DRWShadingGroup *shgroup,
@@ -1786,6 +1799,14 @@ const DRWView *DRW_view_default_get(void)
return DST.view_default;
}
+/* WARNING: Only use in render AND only if you are going to set view_default again. */
+void DRW_view_reset(void)
+{
+ DST.view_default = NULL;
+ DST.view_active = NULL;
+ DST.view_previous = NULL;
+}
+
/* MUST only be called once per render and only in render mode. Sets default view. */
void DRW_view_default_set(DRWView *view)
{
diff --git a/source/blender/draw/intern/draw_select_buffer.c b/source/blender/draw/intern/draw_select_buffer.c
index 81c9f400f6e..558d5441136 100644
--- a/source/blender/draw/intern/draw_select_buffer.c
+++ b/source/blender/draw/intern/draw_select_buffer.c
@@ -122,7 +122,6 @@ uint *DRW_select_buffer_read(struct Depsgraph *depsgraph,
/**
* \param rect: The rectangle to sample indices from (min/max inclusive).
- * \param mask: Specifies the rect pixels (optional).
* \returns a #BLI_bitmap the length of \a bitmap_len or NULL on failure.
*/
uint *DRW_select_buffer_bitmap_from_rect(struct Depsgraph *depsgraph,
@@ -165,10 +164,10 @@ uint *DRW_select_buffer_bitmap_from_rect(struct Depsgraph *depsgraph,
}
/**
- * \param bitmap_len: Number of indices in the selection id buffer.
* \param center: Circle center.
* \param radius: Circle radius.
- * \returns a #BLI_bitmap the length of \a bitmap_len or NULL on failure.
+ * \param r_bitmap_len: Number of indices in the selection id buffer.
+ * \returns a #BLI_bitmap the length of \a r_bitmap_len or NULL on failure.
*/
uint *DRW_select_buffer_bitmap_from_circle(struct Depsgraph *depsgraph,
struct ARegion *region,
@@ -338,7 +337,7 @@ uint DRW_select_buffer_sample_point(struct Depsgraph *depsgraph,
/**
* Find the selection id closest to \a center.
- * \param dist[in,out]: Use to initialize the distance,
+ * \param dist: Use to initialize the distance,
* when found, this value is set to the distance of the selection that's returned.
*/
uint DRW_select_buffer_find_nearest_to_point(struct Depsgraph *depsgraph,
diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c
index bd83bdae31b..9564b662b12 100644
--- a/source/blender/editors/animation/anim_filter.c
+++ b/source/blender/editors/animation/anim_filter.c
@@ -3414,12 +3414,13 @@ static size_t animdata_filter_remove_duplis(ListBase *anim_data)
/* ----------- Public API --------------- */
-/* This function filters the active data source to leave only animation channels suitable for
+/**
+ * This function filters the active data source to leave only animation channels suitable for
* usage by the caller. It will return the length of the list
*
- * *anim_data: is a pointer to a ListBase, to which the filtered animation channels
- * will be placed for use.
- * filter_mode: how should the data be filtered - bitmapping accessed flags
+ * \param anim_data: Is a pointer to a #ListBase,
+ * to which the filtered animation channels will be placed for use.
+ * \param filter_mode: how should the data be filtered - bit-mapping accessed flags.
*/
size_t ANIM_animdata_filter(bAnimContext *ac,
ListBase *anim_data,
diff --git a/source/blender/editors/animation/time_scrub_ui.c b/source/blender/editors/animation/time_scrub_ui.c
index 863f433c778..edc36326c57 100644
--- a/source/blender/editors/animation/time_scrub_ui.c
+++ b/source/blender/editors/animation/time_scrub_ui.c
@@ -92,7 +92,9 @@ static void draw_current_frame(const Scene *scene,
bool display_seconds,
const View2D *v2d,
const rcti *scrub_region_rect,
- int current_frame)
+ int current_frame,
+ float sub_frame,
+ bool draw_line)
{
const uiFontStyle *fstyle = UI_FSTYLE_WIDGET;
int frame_x = UI_view2d_view_to_region_x(v2d, current_frame);
@@ -106,6 +108,22 @@ static void draw_current_frame(const Scene *scene,
float bg_color[4];
UI_GetThemeColorShade4fv(TH_CFRAME, -5, bg_color);
+ if (draw_line) {
+ /* Draw vertical line to from the bottom of the current frame box to the bottom of the screen.
+ */
+ const float subframe_x = UI_view2d_view_to_region_x(v2d, current_frame + sub_frame);
+ GPUVertFormat *format = immVertexFormat();
+ uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immUniformThemeColor(TH_CFRAME);
+ immRectf(pos,
+ subframe_x - U.pixelsize,
+ scrub_region_rect->ymax - box_padding,
+ subframe_x + U.pixelsize,
+ 0.0f);
+ immUnbindProgram();
+ }
+
UI_draw_roundbox_corner_set(UI_CNR_ALL);
UI_draw_roundbox_3fv_alpha(true,
@@ -135,6 +153,28 @@ static void draw_current_frame(const Scene *scene,
text_color);
}
+void ED_time_scrub_draw_current_frame(const ARegion *region,
+ const Scene *scene,
+ bool display_seconds,
+ bool draw_line)
+{
+ const View2D *v2d = &region->v2d;
+ GPU_matrix_push_projection();
+ wmOrtho2_region_pixelspace(region);
+
+ rcti scrub_region_rect;
+ get_time_scrub_region_rect(region, &scrub_region_rect);
+
+ draw_current_frame(scene,
+ display_seconds,
+ v2d,
+ &scrub_region_rect,
+ scene->r.cfra,
+ scene->r.subframe,
+ draw_line);
+ GPU_matrix_pop_projection();
+}
+
void ED_time_scrub_draw(const ARegion *region,
const Scene *scene,
bool display_seconds,
@@ -161,8 +201,6 @@ void ED_time_scrub_draw(const ARegion *region,
region, v2d, &numbers_rect, scene, display_seconds, TH_TEXT);
}
- draw_current_frame(scene, display_seconds, v2d, &scrub_region_rect, scene->r.cfra);
-
GPU_matrix_pop_projection();
}
diff --git a/source/blender/editors/armature/armature_edit.c b/source/blender/editors/armature/armature_edit.c
index a7a705a6202..a010fbd5e81 100644
--- a/source/blender/editors/armature/armature_edit.c
+++ b/source/blender/editors/armature/armature_edit.c
@@ -140,7 +140,16 @@ void ED_armature_origin_set(
mul_m4_v3(ob->imat, cent);
}
else {
- if (around == V3D_AROUND_CENTER_MEDIAN) {
+ if (around == V3D_AROUND_CENTER_BOUNDS) {
+ float min[3], max[3];
+ INIT_MINMAX(min, max);
+ for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
+ minmax_v3v3_v3(min, max, ebone->head);
+ minmax_v3v3_v3(min, max, ebone->tail);
+ }
+ mid_v3_v3v3(cent, min, max);
+ }
+ else { /* #V3D_AROUND_CENTER_MEDIAN. */
int total = 0;
zero_v3(cent);
for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
@@ -152,15 +161,6 @@ void ED_armature_origin_set(
mul_v3_fl(cent, 1.0f / (float)total);
}
}
- else {
- float min[3], max[3];
- INIT_MINMAX(min, max);
- for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
- minmax_v3v3_v3(min, max, ebone->head);
- minmax_v3v3_v3(min, max, ebone->tail);
- }
- mid_v3_v3v3(cent, min, max);
- }
}
/* Do the adjustments */
diff --git a/source/blender/editors/armature/armature_relations.c b/source/blender/editors/armature/armature_relations.c
index d3d00fc44f2..f90d781baca 100644
--- a/source/blender/editors/armature/armature_relations.c
+++ b/source/blender/editors/armature/armature_relations.c
@@ -270,7 +270,7 @@ static void joined_armature_fix_links(
}
/* join armature exec is exported for use in object->join objects operator... */
-int join_armature_exec(bContext *C, wmOperator *op)
+int ED_armature_join_objects_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
@@ -304,10 +304,10 @@ int join_armature_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- /* Get editbones of active armature to add editbones to */
+ /* Get edit-bones of active armature to add edit-bones to */
ED_armature_to_edit(arm);
- /* get pose of active object and move it out of posemode */
+ /* Get pose of active object and move it out of pose-mode */
pose = ob_active->pose;
ob_active->mode &= ~OB_MODE_POSE;
@@ -325,7 +325,7 @@ int join_armature_exec(bContext *C, wmOperator *op)
afd.tarArm = ob_active;
afd.names_map = BLI_ghash_str_new("join_armature_adt_fix");
- /* Make a list of editbones in current armature */
+ /* Make a list of edit-bones in current armature */
ED_armature_to_edit(ob_iter->data);
/* Get Pose of current armature */
@@ -549,7 +549,7 @@ static void separated_armature_fix_links(Main *bmain, Object *origArm, Object *n
/* Helper function for armature separating - remove certain bones from the given armature
* sel: remove selected bones from the armature, otherwise the unselected bones are removed
- * (ob is not in editmode)
+ * (ob is not in edit-mode)
*/
static void separate_armature_bones(Main *bmain, Object *ob, const bool is_select)
{
@@ -557,7 +557,7 @@ static void separate_armature_bones(Main *bmain, Object *ob, const bool is_selec
bPoseChannel *pchan, *pchann;
EditBone *curbone;
- /* make local set of editbones to manipulate here */
+ /* make local set of edit-bones to manipulate here */
ED_armature_to_edit(arm);
/* go through pose-channels, checking if a bone should be removed */
@@ -591,7 +591,7 @@ static void separate_armature_bones(Main *bmain, Object *ob, const bool is_selec
}
}
- /* free any of the extra-data this pchan might have */
+ /* Free any of the extra-data this pchan might have. */
BKE_pose_channel_free(pchan);
BKE_pose_channels_hash_free(ob->pose);
@@ -601,7 +601,7 @@ static void separate_armature_bones(Main *bmain, Object *ob, const bool is_selec
}
}
- /* exit editmode (recalculates pchans too) */
+ /* Exit edit-mode (recalculates pose-channels too). */
ED_armature_edit_deselect_all(ob);
ED_armature_from_edit(bmain, ob->data);
ED_armature_edit_free(ob->data);
@@ -652,15 +652,15 @@ static int separate_armature_exec(bContext *C, wmOperator *op)
}
/* We are going to do this as follows (unlike every other instance of separate):
- * 1. Exit editmode +posemode for active armature/base. Take note of what this is.
+ * 1. Exit edit-mode & pose-mode for active armature/base. Take note of what this is.
* 2. Duplicate base - BASACT is the new one now
* 3. For each of the two armatures,
- * enter editmode -> remove appropriate bones -> exit editmode + recalc.
+ * enter edit-mode -> remove appropriate bones -> exit edit-mode + recalculate.
* 4. Fix constraint links
- * 5. Make original armature active and enter editmode
+ * 5. Make original armature active and enter edit-mode
*/
- /* 1) store starting settings and exit editmode */
+ /* 1) store starting settings and exit edit-mode */
ob_old->mode &= ~OB_MODE_POSE;
ED_armature_from_edit(bmain, ob_old->data);
@@ -700,7 +700,7 @@ static int separate_armature_exec(bContext *C, wmOperator *op)
}
MEM_freeN(bases);
- /* recalc/redraw + cleanup */
+ /* Recalculate/redraw + cleanup */
WM_cursor_wait(0);
if (ok) {
@@ -754,7 +754,7 @@ static void bone_connect_to_new_parent(ListBase *edbo,
float offset[3];
if ((selbone->parent) && (selbone->flag & BONE_CONNECTED)) {
- selbone->parent->flag &= ~(BONE_TIPSEL);
+ selbone->parent->flag &= ~BONE_TIPSEL;
}
/* make actbone the parent of selbone */
@@ -956,7 +956,7 @@ static void editbone_clear_parent(EditBone *ebone, int mode)
{
if (ebone->parent) {
/* for nice selection */
- ebone->parent->flag &= ~(BONE_TIPSEL);
+ ebone->parent->flag &= ~BONE_TIPSEL;
}
if (mode == 1) {
diff --git a/source/blender/editors/armature/armature_select.c b/source/blender/editors/armature/armature_select.c
index eb7c1bc74ea..ccd39429704 100644
--- a/source/blender/editors/armature/armature_select.c
+++ b/source/blender/editors/armature/armature_select.c
@@ -188,7 +188,7 @@ static void *ed_armature_pick_bone_from_selectbuffer_impl(const bool is_editmode
Base *base = NULL;
bool sel;
- hitresult &= ~(BONESEL_ANY);
+ hitresult &= ~BONESEL_ANY;
/* Determine what the current bone is */
if (is_editmode == false) {
base = ED_armature_base_and_pchan_from_select_buffer(bases, bases_len, hitresult, &pchan);
@@ -1302,7 +1302,7 @@ static int armature_de_select_all_exec(bContext *C, wmOperator *op)
if ((ebone->flag & BONE_UNSELECTABLE) == 0) {
ebone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
if (ebone->parent) {
- ebone->parent->flag |= (BONE_TIPSEL);
+ ebone->parent->flag |= BONE_TIPSEL;
}
}
break;
@@ -1317,7 +1317,7 @@ static int armature_de_select_all_exec(bContext *C, wmOperator *op)
if ((ebone->flag & BONE_UNSELECTABLE) == 0) {
ebone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
if (ebone->parent) {
- ebone->parent->flag |= (BONE_TIPSEL);
+ ebone->parent->flag |= BONE_TIPSEL;
}
}
}
diff --git a/source/blender/editors/armature/armature_skinning.c b/source/blender/editors/armature/armature_skinning.c
index 61d8856afbc..b3c58f2575b 100644
--- a/source/blender/editors/armature/armature_skinning.c
+++ b/source/blender/editors/armature/armature_skinning.c
@@ -283,11 +283,11 @@ static void add_verts_to_dgroups(ReportList *reports,
* weights, either through envelopes or using a heat equilibrium.
*
* This function can be called both when parenting a mesh to an armature,
- * or in weightpaint + posemode. In the latter case selection is taken
+ * or in weight-paint + pose-mode. In the latter case selection is taken
* into account and vertex weights can be mirrored.
*
* The mesh vertex positions used are either the final deformed coords
- * from the evaluated mesh in weightpaint mode, the final subsurf coords
+ * from the evaluated mesh in weight-paint mode, the final sub-surface coords
* when parenting, or simply the original mesh coords.
*/
diff --git a/source/blender/editors/armature/meshlaplacian.c b/source/blender/editors/armature/meshlaplacian.c
index d8a6a22a7df..145071522ed 100644
--- a/source/blender/editors/armature/meshlaplacian.c
+++ b/source/blender/editors/armature/meshlaplacian.c
@@ -94,9 +94,8 @@ struct LaplacianSystem {
float (*verts)[3]; /* vertex coordinates */
float (*vnors)[3]; /* vertex normals */
- float (*root)[3]; /* bone root */
- float (*tip)[3]; /* bone tip */
- float (*source)[3]; /* vertex source */
+ float (*root)[3]; /* bone root */
+ float (*tip)[3]; /* bone tip */
int numsource;
float *H; /* diagonal H matrix */
diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c
index 4e1c07af001..a39c8261b32 100644
--- a/source/blender/editors/curve/editcurve.c
+++ b/source/blender/editors/curve/editcurve.c
@@ -1432,7 +1432,7 @@ static int separate_exec(bContext *C, wmOperator *op)
/* 2. Duplicate the object and data. */
/* Take into account user preferences for duplicating actions. */
- short dupflag = (U.dupflag & USER_DUP_ACT);
+ const eDupli_ID_Flags dupflag = (U.dupflag & USER_DUP_ACT);
newbase = ED_object_add_duplicate(bmain, scene, view_layer, oldbase, dupflag);
DEG_relations_tag_update(bmain);
@@ -6895,7 +6895,7 @@ void CURVE_OT_shade_flat(wmOperatorType *ot)
* This is used externally, by #OBJECT_OT_join.
* TODO: shape keys - as with meshes.
*/
-int join_curve_exec(bContext *C, wmOperator *op)
+int ED_curve_join_objects_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
diff --git a/source/blender/editors/curve/editcurve_select.c b/source/blender/editors/curve/editcurve_select.c
index 9294bc6e91b..73f970876b1 100644
--- a/source/blender/editors/curve/editcurve_select.c
+++ b/source/blender/editors/curve/editcurve_select.c
@@ -988,7 +988,7 @@ void CURVE_OT_select_more(wmOperatorType *ot)
/* identifiers */
ot->name = "Select More";
ot->idname = "CURVE_OT_select_more";
- ot->description = "Select control points directly linked to already selected ones";
+ ot->description = "Select control points at the boundary of each selection region";
/* api callbacks */
ot->exec = curve_select_more_exec;
@@ -1203,7 +1203,7 @@ void CURVE_OT_select_less(wmOperatorType *ot)
/* identifiers */
ot->name = "Select Less";
ot->idname = "CURVE_OT_select_less";
- ot->description = "Reduce current selection by deselecting boundary elements";
+ ot->description = "Deselect control points at the boundary of each selection region";
/* api callbacks */
ot->exec = curve_select_less_exec;
diff --git a/source/blender/editors/curve/editfont.c b/source/blender/editors/curve/editfont.c
index dc5dc71106f..1bbd4b4a5bc 100644
--- a/source/blender/editors/curve/editfont.c
+++ b/source/blender/editors/curve/editfont.c
@@ -1635,7 +1635,7 @@ static int insert_text_exec(bContext *C, wmOperator *op)
{
Object *obedit = CTX_data_edit_object(C);
char *inserted_utf8;
- wchar_t *inserted_text;
+ char32_t *inserted_text;
int a, len;
if (!RNA_struct_property_is_set(op->ptr, "text")) {
@@ -1645,8 +1645,8 @@ static int insert_text_exec(bContext *C, wmOperator *op)
inserted_utf8 = RNA_string_get_alloc(op->ptr, "text", NULL, 0);
len = BLI_strlen_utf8(inserted_utf8);
- inserted_text = MEM_callocN(sizeof(wchar_t) * (len + 1), "FONT_insert_text");
- BLI_strncpy_wchar_from_utf8(inserted_text, inserted_utf8, len + 1);
+ inserted_text = MEM_callocN(sizeof(char32_t) * (len + 1), "FONT_insert_text");
+ len = BLI_str_utf8_as_utf32(inserted_text, inserted_utf8, MAXTEXT);
for (a = 0; a < len; a++) {
insert_into_textbuf(obedit, inserted_text[a]);
@@ -2133,7 +2133,7 @@ static int open_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)
vfont = (VFont *)idptr.owner_id;
}
- path = (vfont && !BKE_vfont_is_builtin(vfont)) ? vfont->name : U.fontdir;
+ path = (vfont && !BKE_vfont_is_builtin(vfont)) ? vfont->filepath : U.fontdir;
if (RNA_struct_property_is_set(op->ptr, "filepath")) {
return font_open_exec(C, op);
diff --git a/source/blender/editors/datafiles/CMakeLists.txt b/source/blender/editors/datafiles/CMakeLists.txt
index 1a5b3d6ac45..0dcb8de37f1 100644
--- a/source/blender/editors/datafiles/CMakeLists.txt
+++ b/source/blender/editors/datafiles/CMakeLists.txt
@@ -713,6 +713,7 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES
ops.mesh.bisect
ops.mesh.dupli_extrude_cursor
ops.mesh.extrude_faces_move
+ ops.mesh.extrude_manifold
ops.mesh.extrude_region_move
ops.mesh.extrude_region_shrink_fatten
ops.mesh.inset
diff --git a/source/blender/editors/gpencil/CMakeLists.txt b/source/blender/editors/gpencil/CMakeLists.txt
index 4083169d65c..735ad8bc039 100644
--- a/source/blender/editors/gpencil/CMakeLists.txt
+++ b/source/blender/editors/gpencil/CMakeLists.txt
@@ -49,6 +49,7 @@ set(SRC
gpencil_fill.c
gpencil_interpolate.c
gpencil_merge.c
+ gpencil_mesh.c
gpencil_ops.c
gpencil_ops_versioning.c
gpencil_paint.c
diff --git a/source/blender/editors/gpencil/annotate_draw.c b/source/blender/editors/gpencil/annotate_draw.c
index 22df7bbbf31..20307e7f809 100644
--- a/source/blender/editors/gpencil/annotate_draw.c
+++ b/source/blender/editors/gpencil/annotate_draw.c
@@ -172,9 +172,14 @@ static void annotation_draw_stroke_buffer(bGPdata *gps,
float oldpressure = points[0].pressure;
/* draw stroke curve */
- GPU_line_width(max_ff(oldpressure * thickness, 1.0));
+ immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR);
+
+ float viewport[4];
+ GPU_viewport_size_get_f(viewport);
+ immUniform2fv("viewportSize", &viewport[2]);
+
+ immUniform1f("lineWidth", max_ff(oldpressure * thickness, 1.0) * U.pixelsize);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
immUniformColor3fvAlpha(ink, ink[3]);
immBeginAtMost(GPU_PRIM_LINE_STRIP, totpoints);
@@ -193,7 +198,7 @@ static void annotation_draw_stroke_buffer(bGPdata *gps,
immEnd();
draw_points = 0;
- GPU_line_width(max_ff(pt->pressure * thickness, 1.0f));
+ immUniform1f("lineWidth", max_ff(pt->pressure * thickness, 1.0) * U.pixelsize);
immBeginAtMost(GPU_PRIM_LINE_STRIP, totpoints - i + 1);
/* need to roll-back one point to ensure that there are no gaps in the stroke */
@@ -327,11 +332,17 @@ static void annotation_draw_stroke_3d(
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR);
+
+ float viewport[4];
+ GPU_viewport_size_get_f(viewport);
+ immUniform2fv("viewportSize", &viewport[2]);
+
+ immUniform1f("lineWidth", max_ff(curpressure * thickness, 1.0) * U.pixelsize);
+
immUniformColor3fvAlpha(ink, ink[3]);
/* draw stroke curve */
- GPU_line_width(max_ff(curpressure * thickness, 1.0f));
immBeginAtMost(GPU_PRIM_LINE_STRIP, totpoints + cyclic_add);
const bGPDspoint *pt = points;
for (int i = 0; i < totpoints; i++, pt++) {
@@ -351,7 +362,7 @@ static void annotation_draw_stroke_3d(
draw_points = 0;
curpressure = pt->pressure;
- GPU_line_width(max_ff(curpressure * thickness, 1.0f));
+ immUniform1f("lineWidth", max_ff(curpressure * thickness, 1.0) * U.pixelsize);
immBeginAtMost(GPU_PRIM_LINE_STRIP, totpoints - i + 1 + cyclic_add);
/* need to roll-back one point to ensure that there are no gaps in the stroke */
@@ -424,11 +435,15 @@ static void annotation_draw_stroke_2d(const bGPDspoint *points,
}
else {
/* draw stroke curve */
- GPU_line_width(max_ff(oldpressure * thickness, 1.0));
-
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR);
immUniformColor3fvAlpha(ink, ink[3]);
+ float viewport[4];
+ GPU_viewport_size_get_f(viewport);
+ immUniform2fv("viewportSize", &viewport[2]);
+
+ immUniform1f("lineWidth", max_ff(oldpressure * thickness, 1.0) * U.pixelsize);
+
immBeginAtMost(GPU_PRIM_LINE_STRIP, totpoints);
for (int i = 0; i < totpoints; i++) {
@@ -448,7 +463,8 @@ static void annotation_draw_stroke_2d(const bGPDspoint *points,
immEnd();
draw_points = 0;
- GPU_line_width(max_ff(pt->pressure * thickness, 1.0f));
+ immUniform1f("lineWidth", max_ff(pt->pressure * thickness, 1.0) * U.pixelsize);
+
immBeginAtMost(GPU_PRIM_LINE_STRIP, totpoints - i + 1);
/* need to roll-back one point to ensure that there are no gaps in the stroke */
@@ -683,9 +699,6 @@ static void annotation_draw_data_layers(
continue;
}
- /* set basic stroke thickness */
- GPU_line_width(lthick);
-
/* Add layer drawing settings to the set of "draw flags"
* NOTE: If the setting doesn't apply, it *must* be cleared,
* as dflag's carry over from the previous layer
@@ -774,13 +787,7 @@ static void annotation_draw_data_all(Scene *scene,
}
}
-/* ----- Grease Pencil Sketches Drawing API ------ */
-
-/* ............................
- * XXX
- * We need to review the calls below, since they may be/are not that suitable for
- * the new ways that we intend to be drawing data...
- * ............................ */
+/* ----- Annotation Sketches Drawing API ------ */
/* draw grease-pencil sketches to specified 2d-view that uses ibuf corrections */
void ED_annotation_draw_2dimage(const bContext *C)
diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c
index 5c5adb32a97..9d80a75b959 100644
--- a/source/blender/editors/gpencil/annotate_paint.c
+++ b/source/blender/editors/gpencil/annotate_paint.c
@@ -204,12 +204,12 @@ typedef struct tGPsdata {
/* minimum length of new segment before new point can be added */
#define MIN_EUCLIDEAN_PX (U.gp_euclideandist)
-static bool gp_stroke_added_check(tGPsdata *p)
+static bool annotation_stroke_added_check(tGPsdata *p)
{
return (p->gpf && p->gpf->strokes.last && p->flags & GP_PAINTFLAG_STROKEADDED);
}
-static void gp_stroke_added_enable(tGPsdata *p)
+static void annotation_stroke_added_enable(tGPsdata *p)
{
BLI_assert(p->gpf->strokes.last != NULL);
p->flags |= GP_PAINTFLAG_STROKEADDED;
@@ -218,13 +218,13 @@ static void gp_stroke_added_enable(tGPsdata *p)
/* ------ */
/* Forward defines for some functions... */
-static void gp_session_validatebuffer(tGPsdata *p);
+static void annotation_session_validatebuffer(tGPsdata *p);
/* ******************************************* */
/* Context Wrangling... */
/* check if context is suitable for drawing */
-static bool gpencil_draw_poll(bContext *C)
+static bool annotation_draw_poll(bContext *C)
{
if (ED_operator_regionactive(C)) {
/* check if current context can support GPencil data */
@@ -249,7 +249,7 @@ static bool gpencil_draw_poll(bContext *C)
}
/* check if projecting strokes into 3d-geometry in the 3D-View */
-static bool gpencil_project_check(tGPsdata *p)
+static bool annotation_project_check(tGPsdata *p)
{
bGPdata *gpd = p->gpd;
return ((gpd->runtime.sbuffer_sflag & GP_STROKE_3DSPACE) &&
@@ -262,7 +262,7 @@ static bool gpencil_project_check(tGPsdata *p)
/* Utilities --------------------------------- */
/* get the reference point for stroke-point conversions */
-static void gp_get_3d_reference(tGPsdata *p, float vec[3])
+static void annotation_get_3d_reference(tGPsdata *p, float vec[3])
{
const float *fp = p->scene->cursor.location;
@@ -273,7 +273,7 @@ static void gp_get_3d_reference(tGPsdata *p, float vec[3])
/* Stroke Editing ---------------------------- */
/* check if the current mouse position is suitable for adding a new point */
-static bool gp_stroke_filtermval(tGPsdata *p, const float mval[2], float pmval[2])
+static bool annotation_stroke_filtermval(tGPsdata *p, const float mval[2], float pmval[2])
{
int dx = (int)fabsf(mval[0] - pmval[0]);
int dy = (int)fabsf(mval[1] - pmval[1]);
@@ -318,7 +318,10 @@ static bool gp_stroke_filtermval(tGPsdata *p, const float mval[2], float pmval[2
}
/* convert screen-coordinates to buffer-coordinates */
-static void gp_stroke_convertcoords(tGPsdata *p, const float mval[2], float out[3], float *depth)
+static void annotation_stroke_convertcoords(tGPsdata *p,
+ const float mval[2],
+ float out[3],
+ float *depth)
{
bGPdata *gpd = p->gpd;
@@ -326,7 +329,7 @@ static void gp_stroke_convertcoords(tGPsdata *p, const float mval[2], float out[
if (gpd->runtime.sbuffer_sflag & GP_STROKE_3DSPACE) {
int mval_i[2];
round_v2i_v2fl(mval_i, mval);
- if (gpencil_project_check(p) &&
+ if (annotation_project_check(p) &&
(ED_view3d_autodist_simple(p->region, mval_i, out, 0, depth))) {
/* projecting onto 3D-Geometry
* - nothing more needs to be done here, since view_autodist_simple() has already done it
@@ -346,7 +349,7 @@ static void gp_stroke_convertcoords(tGPsdata *p, const float mval[2], float out[
* reference point instead or as offset, for easier stroke matching
*/
- gp_get_3d_reference(p, rvec);
+ annotation_get_3d_reference(p, rvec);
zfac = ED_view3d_calc_zfac(p->region->regiondata, rvec, NULL);
if (ED_view3d_project_float_global(p->region, rvec, mval_prj, V3D_PROJ_TEST_NOP) ==
@@ -381,16 +384,17 @@ static void gp_stroke_convertcoords(tGPsdata *p, const float mval[2], float out[
}
}
-/* Apply smooth to buffer while drawing
+/**
+ * Apply smooth to buffer while drawing
* to smooth point C, use 2 before (A, B) and current point (D):
*
- * A----B-----C------D
+ * `A----B-----C------D`
*
* \param p: Temp data
* \param inf: Influence factor
* \param idx: Index of the last point (need minimum 3 points in the array)
*/
-static void gp_smooth_buffer(tGPsdata *p, float inf, int idx)
+static void annotation_smooth_buffer(tGPsdata *p, float inf, int idx)
{
bGPdata *gpd = p->gpd;
short num_points = gpd->runtime.sbuffer_used;
@@ -438,12 +442,12 @@ static void gp_smooth_buffer(tGPsdata *p, float inf, int idx)
copy_v2_v2(&ptc->x, c);
}
-static void gp_stroke_arrow_calc_points_segment(float stroke_points[8],
- const float ref_point[2],
- const float dir_cw[2],
- const float dir_ccw[2],
- const float lenght,
- const float sign)
+static void annotation_stroke_arrow_calc_points_segment(float stroke_points[8],
+ const float ref_point[2],
+ const float dir_cw[2],
+ const float dir_ccw[2],
+ const float lenght,
+ const float sign)
{
stroke_points[0] = ref_point[0] + dir_cw[0] * lenght * sign;
stroke_points[1] = ref_point[1] + dir_cw[1] * lenght * sign;
@@ -451,11 +455,11 @@ static void gp_stroke_arrow_calc_points_segment(float stroke_points[8],
stroke_points[3] = ref_point[1] + dir_ccw[1] * lenght * sign;
}
-static void gp_stroke_arrow_calc_points(tGPspoint *point,
- const float stroke_dir[2],
- float corner[2],
- float stroke_points[8],
- const int arrow_style)
+static void annotation_stroke_arrow_calc_points(tGPspoint *point,
+ const float stroke_dir[2],
+ float corner[2],
+ float stroke_points[8],
+ const int arrow_style)
{
const int arrow_lenght = 8;
float norm_dir[2];
@@ -473,12 +477,12 @@ static void gp_stroke_arrow_calc_points(tGPspoint *point,
stroke_points[3] = corner[1] + inv_norm_dir_counterclockwise[1] * arrow_lenght + norm_dir[1];
break;
case GP_STROKE_ARROWSTYLE_SEGMENT:
- gp_stroke_arrow_calc_points_segment(stroke_points,
- corner,
- inv_norm_dir_clockwise,
- inv_norm_dir_counterclockwise,
- arrow_lenght,
- 1.0f);
+ annotation_stroke_arrow_calc_points_segment(stroke_points,
+ corner,
+ inv_norm_dir_clockwise,
+ inv_norm_dir_counterclockwise,
+ arrow_lenght,
+ 1.0f);
break;
case GP_STROKE_ARROWSTYLE_CLOSED:
mul_v2_fl(norm_dir, arrow_lenght);
@@ -486,12 +490,12 @@ static void gp_stroke_arrow_calc_points(tGPspoint *point,
add_v2_v2(&point->x, norm_dir);
copy_v2_v2(corner, &point->x);
}
- gp_stroke_arrow_calc_points_segment(stroke_points,
- corner,
- inv_norm_dir_clockwise,
- inv_norm_dir_counterclockwise,
- arrow_lenght,
- -1.0f);
+ annotation_stroke_arrow_calc_points_segment(stroke_points,
+ corner,
+ inv_norm_dir_clockwise,
+ inv_norm_dir_counterclockwise,
+ arrow_lenght,
+ -1.0f);
stroke_points[4] = corner[0] - norm_dir[0];
stroke_points[5] = corner[1] - norm_dir[1];
break;
@@ -501,12 +505,12 @@ static void gp_stroke_arrow_calc_points(tGPspoint *point,
add_v2_v2(&point->x, norm_dir);
copy_v2_v2(corner, &point->x);
}
- gp_stroke_arrow_calc_points_segment(stroke_points,
- corner,
- inv_norm_dir_clockwise,
- inv_norm_dir_counterclockwise,
- arrow_lenght * 0.75f,
- -1.0f);
+ annotation_stroke_arrow_calc_points_segment(stroke_points,
+ corner,
+ inv_norm_dir_clockwise,
+ inv_norm_dir_counterclockwise,
+ arrow_lenght * 0.75f,
+ -1.0f);
stroke_points[4] = stroke_points[0] - norm_dir[0];
stroke_points[5] = stroke_points[1] - norm_dir[1];
stroke_points[6] = stroke_points[2] - norm_dir[0];
@@ -518,7 +522,10 @@ static void gp_stroke_arrow_calc_points(tGPspoint *point,
}
/* add current stroke-point to buffer (returns whether point was successfully added) */
-static short gp_stroke_addpoint(tGPsdata *p, const float mval[2], float pressure, double curtime)
+static short annotation_stroke_addpoint(tGPsdata *p,
+ const float mval[2],
+ float pressure,
+ double curtime)
{
bGPdata *gpd = p->gpd;
tGPspoint *pt;
@@ -571,14 +578,14 @@ static short gp_stroke_addpoint(tGPsdata *p, const float mval[2], float pressure
pt++;
float e_heading[2] = {start[0] - end[0], start[1] - end[1]};
/* Calculate points for ending arrow. */
- gp_stroke_arrow_calc_points(
+ annotation_stroke_arrow_calc_points(
pt, e_heading, end, gpd->runtime.arrow_end, gpd->runtime.arrow_end_style);
}
/* Arrow start corner. */
if (gpd->runtime.sbuffer_sflag & GP_STROKE_USE_ARROW_START) {
float s_heading[2] = {end[0] - start[0], end[1] - start[1]};
/* Calculate points for starting arrow. */
- gp_stroke_arrow_calc_points(
+ annotation_stroke_arrow_calc_points(
NULL, s_heading, start, gpd->runtime.arrow_start, gpd->runtime.arrow_start_style);
}
}
@@ -611,7 +618,7 @@ static short gp_stroke_addpoint(tGPsdata *p, const float mval[2], float pressure
if ((p->flags & GP_PAINTFLAG_USE_STABILIZER_TEMP) == 0) {
/* smooth while drawing previous points with a reduction factor for previous */
for (int s = 0; s < 3; s++) {
- gp_smooth_buffer(p, 0.5f * ((3.0f - s) / 3.0f), gpd->runtime.sbuffer_used - s);
+ annotation_smooth_buffer(p, 0.5f * ((3.0f - s) / 3.0f), gpd->runtime.sbuffer_used - s);
}
}
@@ -632,7 +639,7 @@ static short gp_stroke_addpoint(tGPsdata *p, const float mval[2], float pressure
* to stroke. This allows to draw lines more interactively (see new segment
* during mouse slide, e.g.)
*/
- if (gp_stroke_added_check(p)) {
+ if (annotation_stroke_added_check(p)) {
bGPDstroke *gps = p->gpf->strokes.last;
bGPDspoint *pts;
@@ -649,7 +656,7 @@ static short gp_stroke_addpoint(tGPsdata *p, const float mval[2], float pressure
* but poly lines are converting to stroke instantly,
* so initialize depth buffer before converting coordinates
*/
- if (gpencil_project_check(p)) {
+ if (annotation_project_check(p)) {
View3D *v3d = p->area->spacedata.first;
view3d_region_operator_needs_opengl(p->win, p->region);
@@ -660,7 +667,7 @@ static short gp_stroke_addpoint(tGPsdata *p, const float mval[2], float pressure
}
/* convert screen-coordinates to appropriate coordinates (and store them) */
- gp_stroke_convertcoords(p, &pt->x, &pts->x, NULL);
+ annotation_stroke_convertcoords(p, &pt->x, &pts->x, NULL);
/* copy pressure and time */
pts->pressure = pt->pressure;
@@ -681,115 +688,115 @@ static short gp_stroke_addpoint(tGPsdata *p, const float mval[2], float pressure
return GP_STROKEADD_INVALID;
}
-static void gp_stroke_arrow_init_point_default(bGPDspoint *pt)
+static void annotation_stroke_arrow_init_point_default(bGPDspoint *pt)
{
pt->pressure = 1.0f;
pt->strength = 1.0f;
pt->time = 1.0f;
}
-static void gp_stroke_arrow_init_conv_point(bGPDspoint *pt, const float point[3])
+static void annotation_stroke_arrow_init_conv_point(bGPDspoint *pt, const float point[3])
{
copy_v3_v3(&pt->x, point);
- gp_stroke_arrow_init_point_default(pt);
+ annotation_stroke_arrow_init_point_default(pt);
}
-static void gp_stroke_arrow_init_point(
+static void annotation_stroke_arrow_init_point(
tGPsdata *p, tGPspoint *ptc, bGPDspoint *pt, const float co[8], const int co_idx)
{
/* Note: provided co_idx should be always pair number as it's [x1, y1, x2, y2, x3, y3]. */
float real_co[2] = {co[co_idx], co[co_idx + 1]};
copy_v2_v2(&ptc->x, real_co);
- gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL);
- gp_stroke_arrow_init_point_default(pt);
+ annotation_stroke_convertcoords(p, &ptc->x, &pt->x, NULL);
+ annotation_stroke_arrow_init_point_default(pt);
}
-static void gp_stroke_arrow_allocate(bGPDstroke *gps, const int totpoints)
+static void annotation_stroke_arrow_allocate(bGPDstroke *gps, const int totpoints)
{
/* Copy appropriate settings for stroke. */
gps->totpoints = totpoints;
/* Allocate enough memory for a continuous array for storage points. */
- gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points");
+ gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "annotation_stroke_points");
}
-static void gp_arrow_create_open(tGPsdata *p,
- tGPspoint *ptc,
- bGPDspoint *pt,
- const float corner_point[3],
- const float arrow_points[8])
+static void annotation_arrow_create_open(tGPsdata *p,
+ tGPspoint *ptc,
+ bGPDspoint *pt,
+ const float corner_point[3],
+ const float arrow_points[8])
{
- gp_stroke_arrow_init_point(p, ptc, pt, arrow_points, 0);
+ annotation_stroke_arrow_init_point(p, ptc, pt, arrow_points, 0);
pt++;
- gp_stroke_arrow_init_conv_point(pt, corner_point);
+ annotation_stroke_arrow_init_conv_point(pt, corner_point);
pt++;
- gp_stroke_arrow_init_point(p, ptc, pt, arrow_points, 2);
+ annotation_stroke_arrow_init_point(p, ptc, pt, arrow_points, 2);
}
-static void gp_arrow_create_segm(tGPsdata *p,
- tGPspoint *ptc,
- bGPDspoint *pt,
- const float arrow_points[8])
+static void annotation_arrow_create_segm(tGPsdata *p,
+ tGPspoint *ptc,
+ bGPDspoint *pt,
+ const float arrow_points[8])
{
- gp_stroke_arrow_init_point(p, ptc, pt, arrow_points, 0);
+ annotation_stroke_arrow_init_point(p, ptc, pt, arrow_points, 0);
pt++;
- gp_stroke_arrow_init_point(p, ptc, pt, arrow_points, 2);
+ annotation_stroke_arrow_init_point(p, ptc, pt, arrow_points, 2);
}
-static void gp_arrow_create_closed(tGPsdata *p,
- tGPspoint *ptc,
- bGPDspoint *pt,
- const float arrow_points[8])
+static void annotation_arrow_create_closed(tGPsdata *p,
+ tGPspoint *ptc,
+ bGPDspoint *pt,
+ const float arrow_points[8])
{
- gp_stroke_arrow_init_point(p, ptc, pt, arrow_points, 0);
+ annotation_stroke_arrow_init_point(p, ptc, pt, arrow_points, 0);
pt++;
- gp_stroke_arrow_init_point(p, ptc, pt, arrow_points, 2);
+ annotation_stroke_arrow_init_point(p, ptc, pt, arrow_points, 2);
pt++;
- gp_stroke_arrow_init_point(p, ptc, pt, arrow_points, 4);
+ annotation_stroke_arrow_init_point(p, ptc, pt, arrow_points, 4);
pt++;
- gp_stroke_arrow_init_point(p, ptc, pt, arrow_points, 0);
+ annotation_stroke_arrow_init_point(p, ptc, pt, arrow_points, 0);
}
-static void gp_arrow_create_square(tGPsdata *p,
- tGPspoint *ptc,
- bGPDspoint *pt,
- const float corner_point[3],
- const float arrow_points[8])
+static void annotation_arrow_create_square(tGPsdata *p,
+ tGPspoint *ptc,
+ bGPDspoint *pt,
+ const float corner_point[3],
+ const float arrow_points[8])
{
- gp_stroke_arrow_init_conv_point(pt, corner_point);
+ annotation_stroke_arrow_init_conv_point(pt, corner_point);
pt++;
- gp_stroke_arrow_init_point(p, ptc, pt, arrow_points, 0);
+ annotation_stroke_arrow_init_point(p, ptc, pt, arrow_points, 0);
pt++;
- gp_stroke_arrow_init_point(p, ptc, pt, arrow_points, 4);
+ annotation_stroke_arrow_init_point(p, ptc, pt, arrow_points, 4);
pt++;
- gp_stroke_arrow_init_point(p, ptc, pt, arrow_points, 6);
+ annotation_stroke_arrow_init_point(p, ptc, pt, arrow_points, 6);
pt++;
- gp_stroke_arrow_init_point(p, ptc, pt, arrow_points, 2);
+ annotation_stroke_arrow_init_point(p, ptc, pt, arrow_points, 2);
pt++;
- gp_stroke_arrow_init_conv_point(pt, corner_point);
+ annotation_stroke_arrow_init_conv_point(pt, corner_point);
}
-static void gp_arrow_create(tGPsdata *p,
- tGPspoint *ptc,
- bGPDspoint *pt,
- bGPDstroke *arrow_stroke,
- const float arrow_points[8],
- const int style)
+static void annotation_arrow_create(tGPsdata *p,
+ tGPspoint *ptc,
+ bGPDspoint *pt,
+ bGPDstroke *arrow_stroke,
+ const float arrow_points[8],
+ const int style)
{
float corner_conv[3];
copy_v3_v3(corner_conv, &pt->x);
switch (style) {
case GP_STROKE_ARROWSTYLE_SEGMENT:
- gp_arrow_create_segm(p, ptc, pt, arrow_points);
+ annotation_arrow_create_segm(p, ptc, pt, arrow_points);
break;
case GP_STROKE_ARROWSTYLE_CLOSED:
- gp_arrow_create_closed(p, ptc, pt, arrow_points);
+ annotation_arrow_create_closed(p, ptc, pt, arrow_points);
break;
case GP_STROKE_ARROWSTYLE_OPEN:
- gp_arrow_create_open(p, ptc, pt, corner_conv, arrow_points);
+ annotation_arrow_create_open(p, ptc, pt, corner_conv, arrow_points);
break;
case GP_STROKE_ARROWSTYLE_SQUARE:
- gp_arrow_create_square(p, ptc, pt, corner_conv, arrow_points);
+ annotation_arrow_create_square(p, ptc, pt, corner_conv, arrow_points);
break;
default:
break;
@@ -799,7 +806,7 @@ static void gp_arrow_create(tGPsdata *p,
}
/* make a new stroke from the buffer data */
-static void gp_stroke_newfrombuffer(tGPsdata *p)
+static void annotation_stroke_newfrombuffer(tGPsdata *p)
{
bGPdata *gpd = p->gpd;
bGPDlayer *gpl = p->gpl;
@@ -837,13 +844,13 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
* interactive behavior
*/
if (p->paintmode == GP_PAINTMODE_DRAW_POLY) {
- if (gp_stroke_added_check(p)) {
+ if (annotation_stroke_added_check(p)) {
return;
}
}
/* allocate memory for a new stroke */
- gps = MEM_callocN(sizeof(bGPDstroke), "gp_stroke");
+ gps = MEM_callocN(sizeof(bGPDstroke), "annotation_stroke");
/* copy appropriate settings for stroke */
gps->totpoints = totelem;
@@ -857,7 +864,7 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
gps->tot_triangles = 0;
/* allocate enough memory for a continuous array for storage points */
- gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points");
+ gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "annotation_stroke_points");
gps->tot_triangles = 0;
/* set pointer to first non-initialized point */
@@ -871,7 +878,7 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
ptc = gpd->runtime.sbuffer;
/* convert screen-coordinates to appropriate coordinates (and store them) */
- gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL);
+ annotation_stroke_convertcoords(p, &ptc->x, &pt->x, NULL);
/* copy pressure and time */
pt->pressure = ptc->pressure;
@@ -889,7 +896,7 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
ptc = ((tGPspoint *)runtime.sbuffer) + (runtime.sbuffer_used - 1);
/* Convert screen-coordinates to appropriate coordinates (and store them). */
- gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL);
+ annotation_stroke_convertcoords(p, &ptc->x, &pt->x, NULL);
/* Copy pressure and time. */
pt->pressure = ptc->pressure;
@@ -905,18 +912,19 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
/* Setting up arrow stroke. */
bGPDstroke *e_arrow_gps = BKE_gpencil_stroke_duplicate(gps, false);
- gp_stroke_arrow_allocate(e_arrow_gps, totarrowpoints);
+ annotation_stroke_arrow_allocate(e_arrow_gps, totarrowpoints);
/* Set pointer to first non-initialized point. */
pt = e_arrow_gps->points + (e_arrow_gps->totpoints - totarrowpoints);
/* End point. */
ptc = ((tGPspoint *)runtime.sbuffer) + (runtime.sbuffer_used - 1);
- gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL);
- gp_stroke_arrow_init_point_default(pt);
+ annotation_stroke_convertcoords(p, &ptc->x, &pt->x, NULL);
+ annotation_stroke_arrow_init_point_default(pt);
/* Fill and convert arrow points to create arrow shape. */
- gp_arrow_create(p, ptc, pt, e_arrow_gps, runtime.arrow_end, runtime.arrow_end_style);
+ annotation_arrow_create(
+ p, ptc, pt, e_arrow_gps, runtime.arrow_end, runtime.arrow_end_style);
}
/* Start arrow stroke. */
if ((runtime.sbuffer_sflag & GP_STROKE_USE_ARROW_START) &&
@@ -925,18 +933,19 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
/* Setting up arrow stroke. */
bGPDstroke *s_arrow_gps = BKE_gpencil_stroke_duplicate(gps, false);
- gp_stroke_arrow_allocate(s_arrow_gps, totarrowpoints);
+ annotation_stroke_arrow_allocate(s_arrow_gps, totarrowpoints);
/* Set pointer to first non-initialized point. */
pt = s_arrow_gps->points + (s_arrow_gps->totpoints - totarrowpoints);
/* Start point. */
ptc = runtime.sbuffer;
- gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL);
- gp_stroke_arrow_init_point_default(pt);
+ annotation_stroke_convertcoords(p, &ptc->x, &pt->x, NULL);
+ annotation_stroke_arrow_init_point_default(pt);
/* Fill and convert arrow points to create arrow shape. */
- gp_arrow_create(p, ptc, pt, s_arrow_gps, runtime.arrow_start, runtime.arrow_start_style);
+ annotation_arrow_create(
+ p, ptc, pt, s_arrow_gps, runtime.arrow_start, runtime.arrow_start_style);
}
}
}
@@ -945,7 +954,7 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
ptc = gpd->runtime.sbuffer;
/* convert screen-coordinates to appropriate coordinates (and store them) */
- gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL);
+ annotation_stroke_convertcoords(p, &ptc->x, &pt->x, NULL);
/* copy pressure and time */
pt->pressure = ptc->pressure;
@@ -956,7 +965,7 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
float *depth_arr = NULL;
/* get an array of depths, far depths are blended */
- if (gpencil_project_check(p)) {
+ if (annotation_project_check(p)) {
int mval_i[2], mval_prev[2] = {0};
int interp_depth = 0;
int found_depth = 0;
@@ -1024,7 +1033,7 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
for (i = 0, ptc = gpd->runtime.sbuffer; i < gpd->runtime.sbuffer_used && ptc;
i++, ptc++, pt++) {
/* convert screen-coordinates to appropriate coordinates (and store them) */
- gp_stroke_convertcoords(p, &ptc->x, &pt->x, depth_arr ? depth_arr + i : NULL);
+ annotation_stroke_convertcoords(p, &ptc->x, &pt->x, depth_arr ? depth_arr + i : NULL);
/* copy pressure and time */
pt->pressure = ptc->pressure;
@@ -1040,7 +1049,7 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
/* add stroke to frame */
BLI_addtail(&p->gpf->strokes, gps);
- gp_stroke_added_enable(p);
+ annotation_stroke_added_enable(p);
}
/* --- 'Eraser' for 'Paint' Tool ------ */
@@ -1048,7 +1057,7 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
/* helper to free a stroke
* NOTE: gps->dvert and gps->triangles should be NULL, but check anyway for good measure
*/
-static void gp_free_stroke(bGPDframe *gpf, bGPDstroke *gps)
+static void annotation_free_stroke(bGPDframe *gpf, bGPDstroke *gps)
{
if (gps->points) {
MEM_freeN(gps->points);
@@ -1066,7 +1075,9 @@ static void gp_free_stroke(bGPDframe *gpf, bGPDstroke *gps)
BLI_freelinkN(&gpf->strokes, gps);
}
-/* which which point is infront (result should only be used for comparison) */
+/**
+ * Which which point is in front (result should only be used for comparison).
+ */
static float view3d_point_depth(const RegionView3D *rv3d, const float co[3])
{
if (rv3d->is_persp) {
@@ -1078,10 +1089,10 @@ static float view3d_point_depth(const RegionView3D *rv3d, const float co[3])
}
/* only erase stroke points that are visible (3d view) */
-static bool gp_stroke_eraser_is_occluded(tGPsdata *p,
- const bGPDspoint *pt,
- const int x,
- const int y)
+static bool annotation_stroke_eraser_is_occluded(tGPsdata *p,
+ const bGPDspoint *pt,
+ const int x,
+ const int y)
{
if ((p->area->spacetype == SPACE_VIEW3D) && (p->flags & GP_PAINTFLAG_V3D_ERASER_DEPTH)) {
RegionView3D *rv3d = p->region->regiondata;
@@ -1100,14 +1111,13 @@ static bool gp_stroke_eraser_is_occluded(tGPsdata *p,
return false;
}
-/* eraser tool - evaluation per stroke */
-/* TODO: this could really do with some optimization (KD-Tree/BVH?) */
-static void gp_stroke_eraser_dostroke(tGPsdata *p,
- bGPDframe *gpf,
- bGPDstroke *gps,
- const float mval[2],
- const int radius,
- const rcti *rect)
+/* Eraser tool - evaluation per stroke. */
+static void annotation_stroke_eraser_dostroke(tGPsdata *p,
+ bGPDframe *gpf,
+ bGPDstroke *gps,
+ const float mval[2],
+ const int radius,
+ const rcti *rect)
{
bGPDspoint *pt1, *pt2;
int pc1[2] = {0};
@@ -1118,19 +1128,19 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p,
if (gps->totpoints == 0) {
/* just free stroke */
- gp_free_stroke(gpf, gps);
+ annotation_free_stroke(gpf, gps);
}
else if (gps->totpoints == 1) {
/* only process if it hasn't been masked out... */
if (!(p->flags & GP_PAINTFLAG_SELECTMASK) || (gps->points->flag & GP_SPOINT_SELECT)) {
- gp_point_to_xy(&p->gsc, gps, gps->points, &pc1[0], &pc1[1]);
+ gpencil_point_to_xy(&p->gsc, gps, gps->points, &pc1[0], &pc1[1]);
/* do boundbox check first */
if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) {
/* only check if point is inside */
if (len_v2v2_int(mval_i, pc1) <= radius) {
/* free stroke */
- gp_free_stroke(gpf, gps);
+ annotation_free_stroke(gpf, gps);
}
}
}
@@ -1164,8 +1174,8 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p,
continue;
}
- gp_point_to_xy(&p->gsc, gps, pt1, &pc1[0], &pc1[1]);
- gp_point_to_xy(&p->gsc, gps, pt2, &pc2[0], &pc2[1]);
+ gpencil_point_to_xy(&p->gsc, gps, pt1, &pc1[0], &pc1[1]);
+ gpencil_point_to_xy(&p->gsc, gps, pt2, &pc2[0], &pc2[1]);
/* Check that point segment of the boundbox of the eraser stroke */
if (((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) ||
@@ -1174,9 +1184,9 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p,
* eraser region (either within stroke painted, or on its lines)
* - this assumes that linewidth is irrelevant
*/
- if (gp_stroke_inside_circle(mval, radius, pc1[0], pc1[1], pc2[0], pc2[1])) {
- if ((gp_stroke_eraser_is_occluded(p, pt1, pc1[0], pc1[1]) == false) ||
- (gp_stroke_eraser_is_occluded(p, pt2, pc2[0], pc2[1]) == false)) {
+ if (gpencil_stroke_inside_circle(mval, radius, pc1[0], pc1[1], pc2[0], pc2[1])) {
+ if ((annotation_stroke_eraser_is_occluded(p, pt1, pc1[0], pc1[1]) == false) ||
+ (annotation_stroke_eraser_is_occluded(p, pt2, pc2[0], pc2[1]) == false)) {
/* Edge is affected - Check individual points now */
if (len_v2v2_int(mval_i, pc1) <= radius) {
pt1->flag |= GP_SPOINT_TAG;
@@ -1192,13 +1202,13 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p,
/* Second Pass: Remove any points that are tagged */
if (do_cull) {
- gp_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_TAG, false, 0);
+ gpencil_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_TAG, false, 0);
}
}
}
/* erase strokes which fall under the eraser strokes */
-static void gp_stroke_doeraser(tGPsdata *p)
+static void annotation_stroke_doeraser(tGPsdata *p)
{
bGPDframe *gpf = p->gpf;
bGPDstroke *gps, *gpn;
@@ -1227,7 +1237,7 @@ static void gp_stroke_doeraser(tGPsdata *p)
* (e.g. 2D space strokes in the 3D view, if the same datablock is shared)
*/
if (ED_gpencil_stroke_can_use_direct(p->area, gps)) {
- gp_stroke_eraser_dostroke(p, gpf, gps, p->mval, p->radius, &rect);
+ annotation_stroke_eraser_dostroke(p, gpf, gps, p->mval, p->radius, &rect);
}
}
}
@@ -1236,7 +1246,7 @@ static void gp_stroke_doeraser(tGPsdata *p)
/* Sketching Operator */
/* clear the session buffers (call this before AND after a paint operation) */
-static void gp_session_validatebuffer(tGPsdata *p)
+static void annotation_session_validatebuffer(tGPsdata *p)
{
bGPdata *gpd = p->gpd;
@@ -1251,7 +1261,7 @@ static void gp_session_validatebuffer(tGPsdata *p)
}
/* (re)init new painting data */
-static bool gp_session_initdata(bContext *C, tGPsdata *p)
+static bool annotation_session_initdata(bContext *C, tGPsdata *p)
{
Main *bmain = CTX_data_main(C);
bGPdata **gpd_ptr = NULL;
@@ -1422,13 +1432,13 @@ static bool gp_session_initdata(bContext *C, tGPsdata *p)
}
/* clear out buffer (stored in gp-data), in case something contaminated it */
- gp_session_validatebuffer(p);
+ annotation_session_validatebuffer(p);
return 1;
}
/* init new painting session */
-static tGPsdata *gp_session_initpaint(bContext *C)
+static tGPsdata *annotation_session_initpaint(bContext *C)
{
tGPsdata *p = NULL;
@@ -1438,7 +1448,7 @@ static tGPsdata *gp_session_initpaint(bContext *C)
/* Try to initialize context data
* WARNING: This may not always succeed (e.g. using GP in an annotation-only context)
*/
- if (gp_session_initdata(C, p) == 0) {
+ if (annotation_session_initdata(C, p) == 0) {
/* Invalid state - Exit
* NOTE: It should be safe to just free the data, since failing context checks should
* only happen when no data has been allocated.
@@ -1458,7 +1468,7 @@ static tGPsdata *gp_session_initpaint(bContext *C)
}
/* cleanup after a painting session */
-static void gp_session_cleanup(tGPsdata *p)
+static void annotation_session_cleanup(tGPsdata *p)
{
bGPdata *gpd = (p) ? p->gpd : NULL;
@@ -1481,13 +1491,15 @@ static void gp_session_cleanup(tGPsdata *p)
p->inittime = 0.0;
}
-static void gp_session_free(tGPsdata *p)
+static void annotation_session_free(tGPsdata *p)
{
MEM_freeN(p);
}
/* init new stroke */
-static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Depsgraph *depsgraph)
+static void annotation_paint_initstroke(tGPsdata *p,
+ eGPencil_PaintModes paintmode,
+ Depsgraph *depsgraph)
{
Scene *scene = p->scene;
ToolSettings *ts = scene->toolsettings;
@@ -1638,13 +1650,13 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Deps
}
/* finish off a stroke (clears buffer, but doesn't finish the paint operation) */
-static void gp_paint_strokeend(tGPsdata *p)
+static void annotation_paint_strokeend(tGPsdata *p)
{
ToolSettings *ts = p->scene->toolsettings;
/* for surface sketching, need to set the right OpenGL context stuff so that
* the conversions will project the values correctly...
*/
- if (gpencil_project_check(p)) {
+ if (annotation_project_check(p)) {
View3D *v3d = p->area->spacedata.first;
/* need to restore the original projection settings before packing up */
@@ -1656,22 +1668,22 @@ static void gp_paint_strokeend(tGPsdata *p)
/* check if doing eraser or not */
if ((p->gpd->runtime.sbuffer_sflag & GP_STROKE_ERASER) == 0) {
/* transfer stroke to frame */
- gp_stroke_newfrombuffer(p);
+ annotation_stroke_newfrombuffer(p);
}
/* clean up buffer now */
- gp_session_validatebuffer(p);
+ annotation_session_validatebuffer(p);
}
/* finish off stroke painting operation */
-static void gp_paint_cleanup(tGPsdata *p)
+static void annotation_paint_cleanup(tGPsdata *p)
{
/* p->gpd==NULL happens when stroke failed to initialize,
* for example when GP is hidden in current space (sergey)
*/
if (p->gpd) {
/* finish off a stroke */
- gp_paint_strokeend(p);
+ annotation_paint_strokeend(p);
}
/* "unlock" frame */
@@ -1683,7 +1695,7 @@ static void gp_paint_cleanup(tGPsdata *p)
/* ------------------------------- */
/* Helper callback for drawing the cursor itself */
-static void gpencil_draw_eraser(bContext *UNUSED(C), int x, int y, void *p_ptr)
+static void annotation_draw_eraser(bContext *UNUSED(C), int x, int y, void *p_ptr)
{
tGPsdata *p = (tGPsdata *)p_ptr;
@@ -1729,7 +1741,7 @@ static void gpencil_draw_eraser(bContext *UNUSED(C), int x, int y, void *p_ptr)
}
/* Turn brush cursor in 3D view on/off */
-static void gpencil_draw_toggle_eraser_cursor(tGPsdata *p, short enable)
+static void annotation_draw_toggle_eraser_cursor(tGPsdata *p, short enable)
{
if (p->erasercursor && !enable) {
/* clear cursor */
@@ -1741,11 +1753,11 @@ static void gpencil_draw_toggle_eraser_cursor(tGPsdata *p, short enable)
p->erasercursor = WM_paint_cursor_activate(SPACE_TYPE_ANY,
RGN_TYPE_ANY,
NULL, /* XXX */
- gpencil_draw_eraser,
+ annotation_draw_eraser,
p);
}
}
-static void gpencil_draw_stabilizer(bContext *C, int x, int y, void *p_ptr)
+static void annotation_draw_stabilizer(bContext *C, int x, int y, void *p_ptr)
{
ARegion *region = CTX_wm_region(C);
tGPsdata *p = (tGPsdata *)p_ptr;
@@ -1793,7 +1805,7 @@ static void gpencil_draw_stabilizer(bContext *C, int x, int y, void *p_ptr)
}
/* Turn *stabilizer* brush cursor in 3D view on/off */
-static void gpencil_draw_toggle_stabilizer_cursor(tGPsdata *p, short enable)
+static void annotation_draw_toggle_stabilizer_cursor(tGPsdata *p, short enable)
{
if (p->stabilizer_cursor && !enable) {
/* clear cursor */
@@ -1803,19 +1815,19 @@ static void gpencil_draw_toggle_stabilizer_cursor(tGPsdata *p, short enable)
else if (enable && !p->stabilizer_cursor) {
/* enable cursor */
p->stabilizer_cursor = WM_paint_cursor_activate(
- SPACE_TYPE_ANY, RGN_TYPE_ANY, NULL, gpencil_draw_stabilizer, p);
+ SPACE_TYPE_ANY, RGN_TYPE_ANY, NULL, annotation_draw_stabilizer, p);
}
}
/* Check if tablet eraser is being used (when processing events) */
-static bool gpencil_is_tablet_eraser_active(const wmEvent *event)
+static bool annotation_is_tablet_eraser_active(const wmEvent *event)
{
return (event->tablet.active == EVT_TABLET_ERASER);
}
/* ------------------------------- */
-static void gpencil_draw_exit(bContext *C, wmOperator *op)
+static void annotation_draw_exit(bContext *C, wmOperator *op)
{
tGPsdata *p = op->customdata;
@@ -1827,10 +1839,10 @@ static void gpencil_draw_exit(bContext *C, wmOperator *op)
/* check size of buffer before cleanup, to determine if anything happened here */
if (p->paintmode == GP_PAINTMODE_ERASER) {
/* turn off radial brush cursor */
- gpencil_draw_toggle_eraser_cursor(p, false);
+ annotation_draw_toggle_eraser_cursor(p, false);
}
else if (p->paintmode == GP_PAINTMODE_DRAW) {
- gpencil_draw_toggle_stabilizer_cursor(p, false);
+ annotation_draw_toggle_stabilizer_cursor(p, false);
}
/* always store the new eraser size to be used again next time
@@ -1843,40 +1855,40 @@ static void gpencil_draw_exit(bContext *C, wmOperator *op)
gpencil_undo_finish();
/* cleanup */
- gp_paint_cleanup(p);
- gp_session_cleanup(p);
- gp_session_free(p);
+ annotation_paint_cleanup(p);
+ annotation_session_cleanup(p);
+ annotation_session_free(p);
p = NULL;
}
op->customdata = NULL;
}
-static void gpencil_draw_cancel(bContext *C, wmOperator *op)
+static void annotation_draw_cancel(bContext *C, wmOperator *op)
{
/* this is just a wrapper around exit() */
- gpencil_draw_exit(C, op);
+ annotation_draw_exit(C, op);
}
/* ------------------------------- */
-static int gpencil_draw_init(bContext *C, wmOperator *op, const wmEvent *event)
+static int annotation_draw_init(bContext *C, wmOperator *op, const wmEvent *event)
{
tGPsdata *p;
eGPencil_PaintModes paintmode = RNA_enum_get(op->ptr, "mode");
/* check context */
- p = op->customdata = gp_session_initpaint(C);
+ p = op->customdata = annotation_session_initpaint(C);
if ((p == NULL) || (p->status == GP_STATUS_ERROR)) {
/* something wasn't set correctly in context */
- gpencil_draw_exit(C, op);
+ annotation_draw_exit(C, op);
return 0;
}
/* init painting data */
- gp_paint_initstroke(p, paintmode, CTX_data_ensure_evaluated_depsgraph(C));
+ annotation_paint_initstroke(p, paintmode, CTX_data_ensure_evaluated_depsgraph(C));
if (p->status == GP_STATUS_ERROR) {
- gpencil_draw_exit(C, op);
+ annotation_draw_exit(C, op);
return 0;
}
@@ -1894,7 +1906,7 @@ static int gpencil_draw_init(bContext *C, wmOperator *op, const wmEvent *event)
/* ------------------------------- */
/* ensure that the correct cursor icon is set */
-static void gpencil_draw_cursor_set(tGPsdata *p)
+static void annotation_draw_cursor_set(tGPsdata *p)
{
if (p->paintmode == GP_PAINTMODE_ERASER) {
WM_cursor_modal_set(p->win, WM_CURSOR_ERASER);
@@ -1905,7 +1917,7 @@ static void gpencil_draw_cursor_set(tGPsdata *p)
}
/* update UI indicators of status, including cursor and header prints */
-static void gpencil_draw_status_indicators(bContext *C, tGPsdata *p)
+static void annotation_draw_status_indicators(bContext *C, tGPsdata *p)
{
/* header prints */
switch (p->status) {
@@ -1970,12 +1982,12 @@ static void gpencil_draw_status_indicators(bContext *C, tGPsdata *p)
/* ------------------------------- */
/* create a new stroke point at the point indicated by the painting context */
-static void gpencil_draw_apply(wmOperator *op, tGPsdata *p, Depsgraph *depsgraph)
+static void annotation_draw_apply(wmOperator *op, tGPsdata *p, Depsgraph *depsgraph)
{
/* handle drawing/erasing -> test for erasing first */
if (p->paintmode == GP_PAINTMODE_ERASER) {
/* do 'live' erasing now */
- gp_stroke_doeraser(p);
+ annotation_stroke_doeraser(p);
/* store used values */
p->mvalo[0] = p->mval[0];
@@ -1984,7 +1996,7 @@ static void gpencil_draw_apply(wmOperator *op, tGPsdata *p, Depsgraph *depsgraph
}
/* Only add current point to buffer if mouse moved
* (even though we got an event, it might be just noise). */
- else if (gp_stroke_filtermval(p, p->mval, p->mvalo)) {
+ else if (annotation_stroke_filtermval(p, p->mval, p->mvalo)) {
/* If lazy mouse, interpolate the last and current mouse positions. */
if (p->flags & GP_PAINTFLAG_USE_STABILIZER_TEMP) {
float now_mouse[2];
@@ -1996,26 +2008,24 @@ static void gpencil_draw_apply(wmOperator *op, tGPsdata *p, Depsgraph *depsgraph
}
/* try to add point */
- short ok = gp_stroke_addpoint(p, p->mval, p->pressure, p->curtime);
+ short ok = annotation_stroke_addpoint(p, p->mval, p->pressure, p->curtime);
/* handle errors while adding point */
if ((ok == GP_STROKEADD_FULL) || (ok == GP_STROKEADD_OVERFLOW)) {
/* finish off old stroke */
- gp_paint_strokeend(p);
+ annotation_paint_strokeend(p);
/* And start a new one!!! Else, projection errors! */
- gp_paint_initstroke(p, p->paintmode, depsgraph);
+ annotation_paint_initstroke(p, p->paintmode, depsgraph);
/* start a new stroke, starting from previous point */
- /* XXX Must manually reset inittime... */
- /* XXX We only need to reuse previous point if overflow! */
if (ok == GP_STROKEADD_OVERFLOW) {
p->inittime = p->ocurtime;
- gp_stroke_addpoint(p, p->mvalo, p->opressure, p->ocurtime);
+ annotation_stroke_addpoint(p, p->mvalo, p->opressure, p->ocurtime);
}
else {
p->inittime = p->curtime;
}
- gp_stroke_addpoint(p, p->mval, p->pressure, p->curtime);
+ annotation_stroke_addpoint(p, p->mval, p->pressure, p->curtime);
}
else if (ok == GP_STROKEADD_INVALID) {
/* the painting operation cannot continue... */
@@ -2053,16 +2063,16 @@ static void annotation_draw_apply_event(
/* Key to toggle stabilization. */
if (event->shift > 0 && p->paintmode == GP_PAINTMODE_DRAW) {
/* Using permanent stabilization, shift will deactivate the flag. */
- if (p->flags & (GP_PAINTFLAG_USE_STABILIZER)) {
+ if (p->flags & GP_PAINTFLAG_USE_STABILIZER) {
if (p->flags & GP_PAINTFLAG_USE_STABILIZER_TEMP) {
- gpencil_draw_toggle_stabilizer_cursor(p, false);
+ annotation_draw_toggle_stabilizer_cursor(p, false);
p->flags &= ~GP_PAINTFLAG_USE_STABILIZER_TEMP;
}
}
/* Not using any stabilization flag. Activate temporal one. */
else if ((p->flags & GP_PAINTFLAG_USE_STABILIZER_TEMP) == 0) {
p->flags |= GP_PAINTFLAG_USE_STABILIZER_TEMP;
- gpencil_draw_toggle_stabilizer_cursor(p, true);
+ annotation_draw_toggle_stabilizer_cursor(p, true);
}
}
/* verify key status for straight lines */
@@ -2091,7 +2101,7 @@ static void annotation_draw_apply_event(
so activate the temp flag back again. */
if (p->flags & GP_PAINTFLAG_USE_STABILIZER) {
if ((p->flags & GP_PAINTFLAG_USE_STABILIZER_TEMP) == 0) {
- gpencil_draw_toggle_stabilizer_cursor(p, true);
+ annotation_draw_toggle_stabilizer_cursor(p, true);
p->flags |= GP_PAINTFLAG_USE_STABILIZER_TEMP;
}
}
@@ -2101,7 +2111,7 @@ static void annotation_draw_apply_event(
else if (p->flags & GP_PAINTFLAG_USE_STABILIZER_TEMP) {
/* Reset temporal stabilizer flag and remove cursor. */
p->flags &= ~GP_PAINTFLAG_USE_STABILIZER_TEMP;
- gpencil_draw_toggle_stabilizer_cursor(p, false);
+ annotation_draw_toggle_stabilizer_cursor(p, false);
}
}
@@ -2165,7 +2175,7 @@ static void annotation_draw_apply_event(
RNA_float_set(&itemptr, "time", p->curtime - p->inittime);
/* apply the current latest drawing point */
- gpencil_draw_apply(op, p, depsgraph);
+ annotation_draw_apply(op, p, depsgraph);
/* force refresh */
/* just active area for now, since doing whole screen is too slow */
@@ -2175,7 +2185,7 @@ static void annotation_draw_apply_event(
/* ------------------------------- */
/* operator 'redo' (i.e. after changing some properties, but also for repeat last) */
-static int gpencil_draw_exec(bContext *C, wmOperator *op)
+static int annotation_draw_exec(bContext *C, wmOperator *op)
{
tGPsdata *p = NULL;
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
@@ -2183,7 +2193,7 @@ static int gpencil_draw_exec(bContext *C, wmOperator *op)
/* printf("GPencil - Starting Re-Drawing\n"); */
/* try to initialize context data needed while drawing */
- if (!gpencil_draw_init(C, op, NULL)) {
+ if (!annotation_draw_init(C, op, NULL)) {
if (op->customdata) {
MEM_freeN(op->customdata);
}
@@ -2217,8 +2227,8 @@ static int gpencil_draw_exec(bContext *C, wmOperator *op)
*/
if ((p->flags & GP_PAINTFLAG_FIRSTRUN) == 0) {
/* TODO: both of these ops can set error-status, but we probably don't need to worry */
- gp_paint_strokeend(p);
- gp_paint_initstroke(p, p->paintmode, depsgraph);
+ annotation_paint_strokeend(p);
+ annotation_paint_initstroke(p, p->paintmode, depsgraph);
}
}
@@ -2233,14 +2243,14 @@ static int gpencil_draw_exec(bContext *C, wmOperator *op)
}
/* apply this data as necessary now (as per usual) */
- gpencil_draw_apply(op, p, depsgraph);
+ annotation_draw_apply(op, p, depsgraph);
}
RNA_END;
/* printf("\tGP - done\n"); */
/* cleanup */
- gpencil_draw_exit(C, op);
+ annotation_draw_exit(C, op);
/* refreshes */
WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL);
@@ -2252,12 +2262,12 @@ static int gpencil_draw_exec(bContext *C, wmOperator *op)
/* ------------------------------- */
/* start of interactive drawing part of operator */
-static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+static int annotation_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
tGPsdata *p = NULL;
/* support for tablets eraser pen */
- if (gpencil_is_tablet_eraser_active(event)) {
+ if (annotation_is_tablet_eraser_active(event)) {
RNA_enum_set(op->ptr, "mode", GP_PAINTMODE_ERASER);
}
@@ -2266,7 +2276,7 @@ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event
}
/* try to initialize context data needed while drawing */
- if (!gpencil_draw_init(C, op, event)) {
+ if (!annotation_draw_init(C, op, event)) {
if (op->customdata) {
MEM_freeN(op->customdata);
}
@@ -2281,21 +2291,15 @@ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event
/* if empty erase capture and finish */
if (p->status == GP_STATUS_CAPTURE) {
- gpencil_draw_exit(C, op);
+ annotation_draw_exit(C, op);
BKE_report(op->reports, RPT_ERROR, "Nothing to erase");
return OPERATOR_FINISHED;
}
- /* TODO: set any additional settings that we can take from the events?
- * TODO? if tablet is erasing, force eraser to be on? */
-
- /* TODO: move cursor setting stuff to stroke-start so that paintmode can be changed midway...
- */
-
/* if eraser is on, draw radial aid */
if (p->paintmode == GP_PAINTMODE_ERASER) {
- gpencil_draw_toggle_eraser_cursor(p, true);
+ annotation_draw_toggle_eraser_cursor(p, true);
}
else if (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT) {
if (RNA_enum_get(op->ptr, "arrowstyle_start") != GP_STROKE_ARROWSTYLE_NONE) {
@@ -2312,18 +2316,18 @@ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event
p->stabilizer_radius = RNA_int_get(op->ptr, "stabilizer_radius");
if (RNA_boolean_get(op->ptr, "use_stabilizer")) {
p->flags |= GP_PAINTFLAG_USE_STABILIZER | GP_PAINTFLAG_USE_STABILIZER_TEMP;
- gpencil_draw_toggle_stabilizer_cursor(p, true);
+ annotation_draw_toggle_stabilizer_cursor(p, true);
}
else if (event->shift > 0) {
p->flags |= GP_PAINTFLAG_USE_STABILIZER_TEMP;
- gpencil_draw_toggle_stabilizer_cursor(p, true);
+ annotation_draw_toggle_stabilizer_cursor(p, true);
}
}
/* set cursor
* NOTE: This may change later (i.e. intentionally via brush toggle,
* or unintentionally if the user scrolls outside the area)...
*/
- gpencil_draw_cursor_set(p);
+ annotation_draw_cursor_set(p);
/* only start drawing immediately if we're allowed to do so... */
if (RNA_boolean_get(op->ptr, "wait_for_input") == false) {
@@ -2348,13 +2352,13 @@ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event
}
/* gpencil modal operator stores area, which can be removed while using it (like fullscreen) */
-static bool gpencil_area_exists(bContext *C, ScrArea *area_test)
+static bool annotation_area_exists(bContext *C, ScrArea *area_test)
{
bScreen *screen = CTX_wm_screen(C);
return (BLI_findindex(&screen->areabase, area_test) != -1);
}
-static tGPsdata *gpencil_stroke_begin(bContext *C, wmOperator *op)
+static tGPsdata *annotation_stroke_begin(bContext *C, wmOperator *op)
{
tGPsdata *p = op->customdata;
@@ -2372,8 +2376,8 @@ static tGPsdata *gpencil_stroke_begin(bContext *C, wmOperator *op)
/* XXX: watch it with the paintmode! in future,
* it'd be nice to allow changing paint-mode when in sketching-sessions */
- if (gp_session_initdata(C, p)) {
- gp_paint_initstroke(p, p->paintmode, CTX_data_ensure_evaluated_depsgraph(C));
+ if (annotation_session_initdata(C, p)) {
+ annotation_paint_initstroke(p, p->paintmode, CTX_data_ensure_evaluated_depsgraph(C));
}
if (p->status != GP_STATUS_ERROR) {
@@ -2384,15 +2388,15 @@ static tGPsdata *gpencil_stroke_begin(bContext *C, wmOperator *op)
return op->customdata;
}
-static void gpencil_stroke_end(wmOperator *op)
+static void annotation_stroke_end(wmOperator *op)
{
tGPsdata *p = op->customdata;
- gp_paint_cleanup(p);
+ annotation_paint_cleanup(p);
gpencil_undo_push(p->gpd);
- gp_session_cleanup(p);
+ annotation_session_cleanup(p);
p->status = GP_STATUS_IDLING;
op->flag |= OP_IS_MODAL_CURSOR_REGION;
@@ -2439,7 +2443,7 @@ static void annotation_add_missing_events(bContext *C,
}
/* events handling during interactive drawing part of operator */
-static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
+static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
tGPsdata *p = op->customdata;
/* default exit state - pass through to support MMB view nav, etc. */
@@ -2542,7 +2546,7 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
if (sketch) {
/* end stroke only, and then wait to resume painting soon */
/* printf("\t\tGP - end stroke only\n"); */
- gpencil_stroke_end(op);
+ annotation_stroke_end(op);
/* If eraser mode is on, turn it off after the stroke finishes
* NOTE: This just makes it nicer to work with drawing sessions
@@ -2561,7 +2565,7 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
* Just hiding this makes it seem like
* you can paint again...
*/
- gpencil_draw_toggle_eraser_cursor(p, false);
+ annotation_draw_toggle_eraser_cursor(p, false);
}
}
@@ -2637,7 +2641,7 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
/* Switch paintmode (temporarily if need be) based on which button was used
* NOTE: This is to make it more convenient to erase strokes when using drawing sessions
*/
- if ((event->type == RIGHTMOUSE) || gpencil_is_tablet_eraser_active(event)) {
+ if ((event->type == RIGHTMOUSE) || annotation_is_tablet_eraser_active(event)) {
/* turn on eraser */
p->paintmode = GP_PAINTMODE_ERASER;
}
@@ -2646,10 +2650,10 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
p->paintmode = RNA_enum_get(op->ptr, "mode");
}
- gpencil_draw_toggle_eraser_cursor(p, p->paintmode == GP_PAINTMODE_ERASER);
+ annotation_draw_toggle_eraser_cursor(p, p->paintmode == GP_PAINTMODE_ERASER);
/* not painting, so start stroke (this should be mouse-button down) */
- p = gpencil_stroke_begin(C, op);
+ p = annotation_stroke_begin(C, op);
if (p->status == GP_STATUS_ERROR) {
estate = OPERATOR_CANCELLED;
@@ -2733,26 +2737,26 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
}
/* gpencil modal operator stores area, which can be removed while using it (like fullscreen) */
- if (0 == gpencil_area_exists(C, p->area)) {
+ if (0 == annotation_area_exists(C, p->area)) {
estate = OPERATOR_CANCELLED;
}
else {
/* update status indicators - cursor, header, etc. */
- gpencil_draw_status_indicators(C, p);
+ annotation_draw_status_indicators(C, p);
/* cursor may have changed outside our control - T44084 */
- gpencil_draw_cursor_set(p);
+ annotation_draw_cursor_set(p);
}
/* process last operations before exiting */
switch (estate) {
case OPERATOR_FINISHED:
/* one last flush before we're done */
- gpencil_draw_exit(C, op);
+ annotation_draw_exit(C, op);
WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL);
break;
case OPERATOR_CANCELLED:
- gpencil_draw_exit(C, op);
+ annotation_draw_exit(C, op);
break;
case OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH:
@@ -2811,11 +2815,11 @@ void GPENCIL_OT_annotate(wmOperatorType *ot)
ot->description = "Make annotations on the active data";
/* api callbacks */
- ot->exec = gpencil_draw_exec;
- ot->invoke = gpencil_draw_invoke;
- ot->modal = gpencil_draw_modal;
- ot->cancel = gpencil_draw_cancel;
- ot->poll = gpencil_draw_poll;
+ ot->exec = annotation_draw_exec;
+ ot->invoke = annotation_draw_invoke;
+ ot->modal = annotation_draw_modal;
+ ot->cancel = annotation_draw_cancel;
+ ot->poll = annotation_draw_poll;
/* flags */
ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING;
diff --git a/source/blender/editors/gpencil/drawgpencil.c b/source/blender/editors/gpencil/drawgpencil.c
index 6d41e9bddbe..60fd52db707 100644
--- a/source/blender/editors/gpencil/drawgpencil.c
+++ b/source/blender/editors/gpencil/drawgpencil.c
@@ -114,10 +114,10 @@ typedef enum eDrawStrokeFlags {
/* ----- Tool Buffer Drawing ------ */
/* helper functions to set color of buffer point */
-static void gp_set_point_varying_color(const bGPDspoint *pt,
- const float ink[4],
- uint attr_id,
- bool fix_strength)
+static void gpencil_set_point_varying_color(const bGPDspoint *pt,
+ const float ink[4],
+ uint attr_id,
+ bool fix_strength)
{
float alpha = ink[3] * pt->strength;
if ((fix_strength) && (alpha >= 0.1f)) {
@@ -130,10 +130,10 @@ static void gp_set_point_varying_color(const bGPDspoint *pt,
/* ----------- Volumetric Strokes --------------- */
/* draw a 3D stroke in "volumetric" style */
-static void gp_draw_stroke_volumetric_3d(const bGPDspoint *points,
- int totpoints,
- short thickness,
- const float ink[4])
+static void gpencil_draw_stroke_volumetric_3d(const bGPDspoint *points,
+ int totpoints,
+ short thickness,
+ const float ink[4])
{
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
@@ -147,7 +147,7 @@ static void gp_draw_stroke_volumetric_3d(const bGPDspoint *points,
const bGPDspoint *pt = points;
for (int i = 0; i < totpoints && pt; i++, pt++) {
- gp_set_point_varying_color(pt, ink, color, false);
+ gpencil_set_point_varying_color(pt, ink, color, false);
/* TODO: scale based on view transform */
immAttr1f(size, pt->pressure * thickness);
/* we can adjust size in vertex shader based on view/projection! */
@@ -162,7 +162,10 @@ static void gp_draw_stroke_volumetric_3d(const bGPDspoint *points,
/* ----- Existing Strokes Drawing (3D and Point) ------ */
/* draw a given stroke in 3d (i.e. in 3d-space) */
-static void gp_draw_stroke_3d(tGPDdraw *tgpw, short thickness, const float ink[4], bool cyclic)
+static void gpencil_draw_stroke_3d(tGPDdraw *tgpw,
+ short thickness,
+ const float ink[4],
+ bool cyclic)
{
bGPDspoint *points = tgpw->gps->points;
int totpoints = tgpw->gps->totpoints;
@@ -209,7 +212,7 @@ static void gp_draw_stroke_3d(tGPDdraw *tgpw, short thickness, const float ink[4
for (int i = 0; i < totpoints; i++, pt++) {
/* first point for adjacency (not drawn) */
if (i == 0) {
- gp_set_point_varying_color(points, ink, attr_id.color, (bool)tgpw->is_fill_stroke);
+ gpencil_set_point_varying_color(points, ink, attr_id.color, (bool)tgpw->is_fill_stroke);
if ((cyclic) && (totpoints > 2)) {
immAttr1f(attr_id.thickness, max_ff((points + totpoints - 1)->pressure * thickness, 1.0f));
@@ -222,7 +225,7 @@ static void gp_draw_stroke_3d(tGPDdraw *tgpw, short thickness, const float ink[4
immVertex3fv(attr_id.pos, fpt);
}
/* set point */
- gp_set_point_varying_color(pt, ink, attr_id.color, (bool)tgpw->is_fill_stroke);
+ gpencil_set_point_varying_color(pt, ink, attr_id.color, (bool)tgpw->is_fill_stroke);
immAttr1f(attr_id.thickness, max_ff(pt->pressure * thickness, 1.0f));
mul_v3_m4v3(fpt, tgpw->diff_mat, &pt->x);
immVertex3fv(attr_id.pos, fpt);
@@ -241,7 +244,7 @@ static void gp_draw_stroke_3d(tGPDdraw *tgpw, short thickness, const float ink[4
}
/* last adjacency point (not drawn) */
else {
- gp_set_point_varying_color(
+ gpencil_set_point_varying_color(
points + totpoints - 2, ink, attr_id.color, (bool)tgpw->is_fill_stroke);
immAttr1f(attr_id.thickness, max_ff((points + totpoints - 2)->pressure * thickness, 1.0f));
@@ -256,7 +259,7 @@ static void gp_draw_stroke_3d(tGPDdraw *tgpw, short thickness, const float ink[4
/* ----- Strokes Drawing ------ */
/* Helper for doing all the checks on whether a stroke can be drawn */
-static bool gp_can_draw_stroke(const bGPDstroke *gps, const int dflag)
+static bool gpencil_can_draw_stroke(const bGPDstroke *gps, const int dflag)
{
/* skip stroke if it isn't in the right display space for this drawing context */
/* 1) 3D Strokes */
@@ -293,7 +296,7 @@ static bool gp_can_draw_stroke(const bGPDstroke *gps, const int dflag)
}
/* draw a set of strokes */
-static void gp_draw_strokes(tGPDdraw *tgpw)
+static void gpencil_draw_strokes(tGPDdraw *tgpw)
{
float tcolor[4];
short sthickness;
@@ -307,7 +310,7 @@ static void gp_draw_strokes(tGPDdraw *tgpw)
for (bGPDstroke *gps = gps_init; gps; gps = gps->next) {
/* check if stroke can be drawn */
- if (gp_can_draw_stroke(gps, tgpw->dflag) == false) {
+ if (gpencil_can_draw_stroke(gps, tgpw->dflag) == false) {
continue;
}
/* check if the color is visible */
@@ -316,7 +319,7 @@ static void gp_draw_strokes(tGPDdraw *tgpw)
if ((gp_style == NULL) || (gp_style->flag & GP_MATERIAL_HIDE) ||
/* if onion and ghost flag do not draw*/
- (tgpw->onion && (gp_style->flag & GP_MATERIAL_ONIONSKIN))) {
+ (tgpw->onion && (gp_style->flag & GP_MATERIAL_HIDE_ONIONSKIN))) {
continue;
}
@@ -379,14 +382,14 @@ static void gp_draw_strokes(tGPDdraw *tgpw)
if (gp_style->mode == GP_MATERIAL_MODE_DOT) {
/* volumetric stroke drawing */
if (tgpw->disable_fill != 1) {
- gp_draw_stroke_volumetric_3d(gps->points, gps->totpoints, sthickness, ink);
+ gpencil_draw_stroke_volumetric_3d(gps->points, gps->totpoints, sthickness, ink);
}
}
else {
/* 3D Lines - OpenGL primitives-based */
if (gps->totpoints > 1) {
tgpw->gps = gps;
- gp_draw_stroke_3d(tgpw, sthickness, ink, gps->flag & GP_STROKE_CYCLIC);
+ gpencil_draw_stroke_3d(tgpw, sthickness, ink, gps->flag & GP_STROKE_CYCLIC);
}
}
if (no_xray) {
@@ -408,7 +411,7 @@ static void gp_draw_strokes(tGPDdraw *tgpw)
/* ----- General Drawing ------ */
/* wrapper to draw strokes for filling operator */
-void ED_gp_draw_fill(tGPDdraw *tgpw)
+void ED_gpencil_draw_fill(tGPDdraw *tgpw)
{
- gp_draw_strokes(tgpw);
+ gpencil_draw_strokes(tgpw);
}
diff --git a/source/blender/editors/gpencil/editaction_gpencil.c b/source/blender/editors/gpencil/editaction_gpencil.c
index d2b1eba7d86..752b8a74f4f 100644
--- a/source/blender/editors/gpencil/editaction_gpencil.c
+++ b/source/blender/editors/gpencil/editaction_gpencil.c
@@ -56,7 +56,9 @@
/* Generics - Loopers */
/* Loops over the gp-frames for a gp-layer, and applies the given callback */
-bool ED_gplayer_frames_looper(bGPDlayer *gpl, Scene *scene, short (*gpf_cb)(bGPDframe *, Scene *))
+bool ED_gpencil_layer_frames_looper(bGPDlayer *gpl,
+ Scene *scene,
+ short (*gpf_cb)(bGPDframe *, Scene *))
{
/* error checker */
if (gpl == NULL) {
@@ -79,7 +81,7 @@ bool ED_gplayer_frames_looper(bGPDlayer *gpl, Scene *scene, short (*gpf_cb)(bGPD
/* Data Conversion Tools */
/* make a listing all the gp-frames in a layer as cfraelems */
-void ED_gplayer_make_cfra_list(bGPDlayer *gpl, ListBase *elems, bool onlysel)
+void ED_gpencil_layer_make_cfra_list(bGPDlayer *gpl, ListBase *elems, bool onlysel)
{
CfraElem *ce;
@@ -105,7 +107,7 @@ void ED_gplayer_make_cfra_list(bGPDlayer *gpl, ListBase *elems, bool onlysel)
/* Selection Tools */
/* check if one of the frames in this layer is selected */
-bool ED_gplayer_frame_select_check(bGPDlayer *gpl)
+bool ED_gpencil_layer_frame_select_check(bGPDlayer *gpl)
{
/* error checking */
if (gpl == NULL) {
@@ -124,7 +126,7 @@ bool ED_gplayer_frame_select_check(bGPDlayer *gpl)
}
/* helper function - select gp-frame based on SELECT_* mode */
-static void gpframe_select(bGPDframe *gpf, short select_mode)
+static void gpencil_frame_select(bGPDframe *gpf, short select_mode)
{
if (gpf == NULL) {
return;
@@ -153,12 +155,12 @@ void ED_gpencil_select_frames(bGPDlayer *gpl, short select_mode)
/* handle according to mode */
LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
- gpframe_select(gpf, select_mode);
+ gpencil_frame_select(gpf, select_mode);
}
}
/* set all/none/invert select */
-void ED_gplayer_frame_select_set(bGPDlayer *gpl, short mode)
+void ED_gpencil_layer_frame_select_set(bGPDlayer *gpl, short mode)
{
/* error checking */
if (gpl == NULL) {
@@ -181,12 +183,12 @@ void ED_gpencil_select_frame(bGPDlayer *gpl, int selx, short select_mode)
gpf = BKE_gpencil_layer_frame_find(gpl, selx);
if (gpf) {
- gpframe_select(gpf, select_mode);
+ gpencil_frame_select(gpf, select_mode);
}
}
/* select the frames in this layer that occur within the bounds specified */
-void ED_gplayer_frames_select_box(bGPDlayer *gpl, float min, float max, short select_mode)
+void ED_gpencil_layer_frames_select_box(bGPDlayer *gpl, float min, float max, short select_mode)
{
if (gpl == NULL) {
return;
@@ -195,16 +197,16 @@ void ED_gplayer_frames_select_box(bGPDlayer *gpl, float min, float max, short se
/* only select those frames which are in bounds */
LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
if (IN_RANGE(gpf->framenum, min, max)) {
- gpframe_select(gpf, select_mode);
+ gpencil_frame_select(gpf, select_mode);
}
}
}
/* select the frames in this layer that occur within the lasso/circle region specified */
-void ED_gplayer_frames_select_region(KeyframeEditData *ked,
- bGPDlayer *gpl,
- short tool,
- short select_mode)
+void ED_gpencil_layer_frames_select_region(KeyframeEditData *ked,
+ bGPDlayer *gpl,
+ short tool,
+ short select_mode)
{
if (gpl == NULL) {
return;
@@ -222,13 +224,13 @@ void ED_gplayer_frames_select_region(KeyframeEditData *ked,
if (tool == BEZT_OK_CHANNEL_LASSO) {
/* Lasso */
if (keyframe_region_lasso_test(ked->data, pt)) {
- gpframe_select(gpf, select_mode);
+ gpencil_frame_select(gpf, select_mode);
}
}
else if (tool == BEZT_OK_CHANNEL_CIRCLE) {
/* Circle */
if (keyframe_region_circle_test(ked->data, pt)) {
- gpframe_select(gpf, select_mode);
+ gpencil_frame_select(gpf, select_mode);
}
}
}
@@ -238,7 +240,7 @@ void ED_gplayer_frames_select_region(KeyframeEditData *ked,
/* Frame Editing Tools */
/* Delete selected frames */
-bool ED_gplayer_frames_delete(bGPDlayer *gpl)
+bool ED_gpencil_layer_frames_delete(bGPDlayer *gpl)
{
bool changed = false;
@@ -259,7 +261,7 @@ bool ED_gplayer_frames_delete(bGPDlayer *gpl)
}
/* Duplicate selected frames from given gp-layer */
-void ED_gplayer_frames_duplicate(bGPDlayer *gpl)
+void ED_gpencil_layer_frames_duplicate(bGPDlayer *gpl)
{
/* error checking */
if (gpl == NULL) {
@@ -282,10 +284,12 @@ void ED_gplayer_frames_duplicate(bGPDlayer *gpl)
}
}
-/* Set keyframe type for selected frames from given gp-layer
- * \param type: The type of keyframe (eBezTriple_KeyframeType) to set selected frames to
+/**
+ * Set keyframe type for selected frames from given gp-layer
+ *
+ * \param type: The type of keyframe (#eBezTriple_KeyframeType) to set selected frames to.
*/
-void ED_gplayer_frames_keytype_set(bGPDlayer *gpl, short type)
+void ED_gpencil_layer_frames_keytype_set(bGPDlayer *gpl, short type)
{
if (gpl == NULL) {
return;
@@ -311,20 +315,20 @@ void ED_gplayer_frames_keytype_set(bGPDlayer *gpl, short type)
*/
/* globals for copy/paste data (like for other copy/paste buffers) */
-static ListBase gp_anim_copybuf = {NULL, NULL};
-static int gp_anim_copy_firstframe = 999999999;
-static int gp_anim_copy_lastframe = -999999999;
-static int gp_anim_copy_cfra = 0;
+static ListBase gpencil_anim_copybuf = {NULL, NULL};
+static int gpencil_anim_copy_firstframe = 999999999;
+static int gpencil_anim_copy_lastframe = -999999999;
+static int gpencil_anim_copy_cfra = 0;
/* This function frees any MEM_calloc'ed copy/paste buffer data */
void ED_gpencil_anim_copybuf_free(void)
{
- BKE_gpencil_free_layers(&gp_anim_copybuf);
- BLI_listbase_clear(&gp_anim_copybuf);
+ BKE_gpencil_free_layers(&gpencil_anim_copybuf);
+ BLI_listbase_clear(&gpencil_anim_copybuf);
- gp_anim_copy_firstframe = 999999999;
- gp_anim_copy_lastframe = -999999999;
- gp_anim_copy_cfra = 0;
+ gpencil_anim_copy_firstframe = 999999999;
+ gpencil_anim_copy_lastframe = -999999999;
+ gpencil_anim_copy_cfra = 0;
}
/* This function adds data to the copy/paste buffer, freeing existing data first
@@ -361,11 +365,11 @@ bool ED_gpencil_anim_copybuf_copy(bAnimContext *ac)
BLI_addtail(&copied_frames, new_frame);
/* extend extents for keyframes encountered */
- if (gpf->framenum < gp_anim_copy_firstframe) {
- gp_anim_copy_firstframe = gpf->framenum;
+ if (gpf->framenum < gpencil_anim_copy_firstframe) {
+ gpencil_anim_copy_firstframe = gpf->framenum;
}
- if (gpf->framenum > gp_anim_copy_lastframe) {
- gp_anim_copy_lastframe = gpf->framenum;
+ if (gpf->framenum > gpencil_anim_copy_lastframe) {
+ gpencil_anim_copy_lastframe = gpf->framenum;
}
}
}
@@ -373,7 +377,7 @@ bool ED_gpencil_anim_copybuf_copy(bAnimContext *ac)
/* create a new layer in buffer if there were keyframes here */
if (BLI_listbase_is_empty(&copied_frames) == false) {
bGPDlayer *new_layer = MEM_callocN(sizeof(bGPDlayer), "GPCopyPasteLayer");
- BLI_addtail(&gp_anim_copybuf, new_layer);
+ BLI_addtail(&gpencil_anim_copybuf, new_layer);
/* move over copied frames */
BLI_movelisttolist(&new_layer->frames, &copied_frames);
@@ -385,13 +389,13 @@ bool ED_gpencil_anim_copybuf_copy(bAnimContext *ac)
}
/* in case 'relative' paste method is used */
- gp_anim_copy_cfra = CFRA;
+ gpencil_anim_copy_cfra = CFRA;
/* clean up */
ANIM_animdata_freelist(&anim_data);
/* check if anything ended up in the buffer */
- if (ELEM(NULL, gp_anim_copybuf.first, gp_anim_copybuf.last)) {
+ if (ELEM(NULL, gpencil_anim_copybuf.first, gpencil_anim_copybuf.last)) {
BKE_report(ac->reports, RPT_ERROR, "No keyframes copied to keyframes copy/paste buffer");
return false;
}
@@ -412,26 +416,26 @@ bool ED_gpencil_anim_copybuf_paste(bAnimContext *ac, const short offset_mode)
int offset = 0;
/* check if buffer is empty */
- if (BLI_listbase_is_empty(&gp_anim_copybuf)) {
+ if (BLI_listbase_is_empty(&gpencil_anim_copybuf)) {
BKE_report(ac->reports, RPT_ERROR, "No data in buffer to paste");
return false;
}
/* check if single channel in buffer (disregard names if so) */
- if (gp_anim_copybuf.first == gp_anim_copybuf.last) {
+ if (gpencil_anim_copybuf.first == gpencil_anim_copybuf.last) {
no_name = true;
}
/* methods of offset (eKeyPasteOffset) */
switch (offset_mode) {
case KEYFRAME_PASTE_OFFSET_CFRA_START:
- offset = (CFRA - gp_anim_copy_firstframe);
+ offset = (CFRA - gpencil_anim_copy_firstframe);
break;
case KEYFRAME_PASTE_OFFSET_CFRA_END:
- offset = (CFRA - gp_anim_copy_lastframe);
+ offset = (CFRA - gpencil_anim_copy_lastframe);
break;
case KEYFRAME_PASTE_OFFSET_CFRA_RELATIVE:
- offset = (CFRA - gp_anim_copy_cfra);
+ offset = (CFRA - gpencil_anim_copy_cfra);
break;
case KEYFRAME_PASTE_OFFSET_NONE:
offset = 0;
@@ -451,7 +455,7 @@ bool ED_gpencil_anim_copybuf_paste(bAnimContext *ac, const short offset_mode)
bGPDframe *gpfs, *gpf;
/* find suitable layer from buffer to use to paste from */
- for (gpls = gp_anim_copybuf.first; gpls; gpls = gpls->next) {
+ for (gpls = gpencil_anim_copybuf.first; gpls; gpls = gpls->next) {
/* check if layer name matches */
if ((no_name) || STREQ(gpls->info, gpld->info)) {
break;
@@ -507,7 +511,7 @@ bool ED_gpencil_anim_copybuf_paste(bAnimContext *ac, const short offset_mode)
/* -------------------------------------- */
/* Snap Tools */
-static short snap_gpf_nearest(bGPDframe *UNUSED(gpf), Scene *UNUSED(scene))
+static short gpencil_frame_snap_nearest(bGPDframe *UNUSED(gpf), Scene *UNUSED(scene))
{
#if 0 /* note: gpf->framenum is already an int! */
if (gpf->flag & GP_FRAME_SELECT) {
@@ -517,7 +521,7 @@ static short snap_gpf_nearest(bGPDframe *UNUSED(gpf), Scene *UNUSED(scene))
return 0;
}
-static short snap_gpf_nearestsec(bGPDframe *gpf, Scene *scene)
+static short gpencil_frame_snap_nearestsec(bGPDframe *gpf, Scene *scene)
{
float secf = (float)FPS;
if (gpf->flag & GP_FRAME_SELECT) {
@@ -526,7 +530,7 @@ static short snap_gpf_nearestsec(bGPDframe *gpf, Scene *scene)
return 0;
}
-static short snap_gpf_cframe(bGPDframe *gpf, Scene *scene)
+static short gpencil_frame_snap_cframe(bGPDframe *gpf, Scene *scene)
{
if (gpf->flag & GP_FRAME_SELECT) {
gpf->framenum = (int)CFRA;
@@ -534,7 +538,7 @@ static short snap_gpf_cframe(bGPDframe *gpf, Scene *scene)
return 0;
}
-static short snap_gpf_nearmarker(bGPDframe *gpf, Scene *scene)
+static short gpencil_frame_snap_nearmarker(bGPDframe *gpf, Scene *scene)
{
if (gpf->flag & GP_FRAME_SELECT) {
gpf->framenum = (int)ED_markers_find_nearest_marker_time(&scene->markers,
@@ -544,20 +548,20 @@ static short snap_gpf_nearmarker(bGPDframe *gpf, Scene *scene)
}
/* snap selected frames to ... */
-void ED_gplayer_snap_frames(bGPDlayer *gpl, Scene *scene, short mode)
+void ED_gpencil_layer_snap_frames(bGPDlayer *gpl, Scene *scene, short mode)
{
switch (mode) {
case SNAP_KEYS_NEARFRAME: /* snap to nearest frame */
- ED_gplayer_frames_looper(gpl, scene, snap_gpf_nearest);
+ ED_gpencil_layer_frames_looper(gpl, scene, gpencil_frame_snap_nearest);
break;
case SNAP_KEYS_CURFRAME: /* snap to current frame */
- ED_gplayer_frames_looper(gpl, scene, snap_gpf_cframe);
+ ED_gpencil_layer_frames_looper(gpl, scene, gpencil_frame_snap_cframe);
break;
case SNAP_KEYS_NEARMARKER: /* snap to nearest marker */
- ED_gplayer_frames_looper(gpl, scene, snap_gpf_nearmarker);
+ ED_gpencil_layer_frames_looper(gpl, scene, gpencil_frame_snap_nearmarker);
break;
case SNAP_KEYS_NEARSEC: /* snap to nearest second */
- ED_gplayer_frames_looper(gpl, scene, snap_gpf_nearestsec);
+ ED_gpencil_layer_frames_looper(gpl, scene, gpencil_frame_snap_nearestsec);
break;
default: /* just in case */
break;
@@ -567,7 +571,7 @@ void ED_gplayer_snap_frames(bGPDlayer *gpl, Scene *scene, short mode)
/* -------------------------------------- */
/* Mirror Tools */
-static short mirror_gpf_cframe(bGPDframe *gpf, Scene *scene)
+static short gpencil_frame_mirror_cframe(bGPDframe *gpf, Scene *scene)
{
int diff;
@@ -579,7 +583,7 @@ static short mirror_gpf_cframe(bGPDframe *gpf, Scene *scene)
return 0;
}
-static short mirror_gpf_yaxis(bGPDframe *gpf, Scene *UNUSED(scene))
+static short gpencil_frame_mirror_yaxis(bGPDframe *gpf, Scene *UNUSED(scene))
{
int diff;
@@ -591,7 +595,7 @@ static short mirror_gpf_yaxis(bGPDframe *gpf, Scene *UNUSED(scene))
return 0;
}
-static short mirror_gpf_xaxis(bGPDframe *gpf, Scene *UNUSED(scene))
+static short gpencil_frame_mirror_xaxis(bGPDframe *gpf, Scene *UNUSED(scene))
{
int diff;
@@ -604,7 +608,7 @@ static short mirror_gpf_xaxis(bGPDframe *gpf, Scene *UNUSED(scene))
return 0;
}
-static short mirror_gpf_marker(bGPDframe *gpf, Scene *scene)
+static short gpencil_frame_mirror_marker(bGPDframe *gpf, Scene *scene)
{
static TimeMarker *marker;
static short initialized = 0;
@@ -646,25 +650,25 @@ static short mirror_gpf_marker(bGPDframe *gpf, Scene *scene)
/* mirror selected gp-frames on... */
// TODO: mirror over a specific time
-void ED_gplayer_mirror_frames(bGPDlayer *gpl, Scene *scene, short mode)
+void ED_gpencil_layer_mirror_frames(bGPDlayer *gpl, Scene *scene, short mode)
{
switch (mode) {
case MIRROR_KEYS_CURFRAME: /* mirror over current frame */
- ED_gplayer_frames_looper(gpl, scene, mirror_gpf_cframe);
+ ED_gpencil_layer_frames_looper(gpl, scene, gpencil_frame_mirror_cframe);
break;
case MIRROR_KEYS_YAXIS: /* mirror over frame 0 */
- ED_gplayer_frames_looper(gpl, scene, mirror_gpf_yaxis);
+ ED_gpencil_layer_frames_looper(gpl, scene, gpencil_frame_mirror_yaxis);
break;
case MIRROR_KEYS_XAXIS: /* mirror over value 0 */
- ED_gplayer_frames_looper(gpl, scene, mirror_gpf_xaxis);
+ ED_gpencil_layer_frames_looper(gpl, scene, gpencil_frame_mirror_xaxis);
break;
case MIRROR_KEYS_MARKER: /* mirror over marker */
- mirror_gpf_marker(NULL, scene);
- ED_gplayer_frames_looper(gpl, scene, mirror_gpf_marker);
- mirror_gpf_marker(NULL, scene);
+ gpencil_frame_mirror_marker(NULL, scene);
+ ED_gpencil_layer_frames_looper(gpl, scene, gpencil_frame_mirror_marker);
+ gpencil_frame_mirror_marker(NULL, scene);
break;
default: /* just in case */
- ED_gplayer_frames_looper(gpl, scene, mirror_gpf_yaxis);
+ ED_gpencil_layer_frames_looper(gpl, scene, gpencil_frame_mirror_yaxis);
break;
}
}
diff --git a/source/blender/editors/gpencil/gpencil_add_stroke.c b/source/blender/editors/gpencil/gpencil_add_stroke.c
index 60a4404beaf..39a2d594c13 100644
--- a/source/blender/editors/gpencil/gpencil_add_stroke.c
+++ b/source/blender/editors/gpencil/gpencil_add_stroke.c
@@ -49,7 +49,10 @@ typedef struct ColorTemplate {
} ColorTemplate;
/* Add color an ensure duplications (matched by name) */
-static int gp_stroke_material(Main *bmain, Object *ob, const ColorTemplate *pct, const bool fill)
+static int gpencil_stroke_material(Main *bmain,
+ Object *ob,
+ const ColorTemplate *pct,
+ const bool fill)
{
short *totcol = BKE_object_material_len_p(ob);
Material *ma = NULL;
@@ -224,12 +227,12 @@ void ED_gpencil_create_stroke(bContext *C, Object *ob, float mat[4][4])
bGPDstroke *gps;
/* create colors */
- int color_black = gp_stroke_material(bmain, ob, &gp_stroke_material_black, false);
- gp_stroke_material(bmain, ob, &gp_stroke_material_white, false);
- gp_stroke_material(bmain, ob, &gp_stroke_material_red, false);
- gp_stroke_material(bmain, ob, &gp_stroke_material_green, false);
- gp_stroke_material(bmain, ob, &gp_stroke_material_blue, false);
- gp_stroke_material(bmain, ob, &gp_stroke_material_grey, true);
+ int color_black = gpencil_stroke_material(bmain, ob, &gp_stroke_material_black, false);
+ gpencil_stroke_material(bmain, ob, &gp_stroke_material_white, false);
+ gpencil_stroke_material(bmain, ob, &gp_stroke_material_red, false);
+ gpencil_stroke_material(bmain, ob, &gp_stroke_material_green, false);
+ gpencil_stroke_material(bmain, ob, &gp_stroke_material_blue, false);
+ gpencil_stroke_material(bmain, ob, &gp_stroke_material_grey, true);
/* set first color as active and in brushes */
ob->actcol = color_black + 1;
diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c
index 78a34cda2f5..e111ce44bc4 100644
--- a/source/blender/editors/gpencil/gpencil_convert.c
+++ b/source/blender/editors/gpencil/gpencil_convert.c
@@ -54,8 +54,11 @@
#include "BKE_fcurve.h"
#include "BKE_global.h"
#include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
+#include "BKE_image.h"
#include "BKE_layer.h"
#include "BKE_main.h"
+#include "BKE_material.h"
#include "BKE_object.h"
#include "BKE_report.h"
#include "BKE_scene.h"
@@ -151,12 +154,12 @@ static const EnumPropertyItem *rna_GPConvert_mode_items(bContext *UNUSED(C),
/* convert the coordinates from the given stroke point into 3d-coordinates
* - assumes that the active space is the 3D-View
*/
-static void gp_strokepoint_convertcoords(bContext *C,
- bGPDlayer *gpl,
- bGPDstroke *gps,
- bGPDspoint *source_pt,
- float p3d[3],
- const rctf *subrect)
+static void gpencil_strokepoint_convertcoords(bContext *C,
+ bGPDlayer *gpl,
+ bGPDstroke *gps,
+ bGPDspoint *source_pt,
+ float p3d[3],
+ const rctf *subrect)
{
Scene *scene = CTX_data_scene(C);
View3D *v3d = CTX_wm_view3d(C);
@@ -240,7 +243,7 @@ typedef struct tGpTimingData {
/* Init point buffers for timing data.
* Note this assumes we only grow those arrays!
*/
-static void gp_timing_data_set_nbr(tGpTimingData *gtd, const int nbr)
+static void gpencil_timing_data_set_nbr(tGpTimingData *gtd, const int nbr)
{
float *tmp;
@@ -266,10 +269,10 @@ static void gp_timing_data_set_nbr(tGpTimingData *gtd, const int nbr)
}
/* add stroke point to timing buffers */
-static void gp_timing_data_add_point(tGpTimingData *gtd,
- const double stroke_inittime,
- const float time,
- const float delta_dist)
+static void gpencil_timing_data_add_point(tGpTimingData *gtd,
+ const double stroke_inittime,
+ const float time,
+ const float delta_dist)
{
float delta_time = 0.0f;
const int cur_point = gtd->cur_point;
@@ -305,14 +308,14 @@ static void gp_timing_data_add_point(tGpTimingData *gtd,
#define MIN_TIME_DELTA 0.02f
/* Loop over next points to find the end of the stroke, and compute */
-static int gp_find_end_of_stroke_idx(tGpTimingData *gtd,
- RNG *rng,
- const int idx,
- const int nbr_gaps,
- int *nbr_done_gaps,
- const float tot_gaps_time,
- const float delta_time,
- float *next_delta_time)
+static int gpencil_find_end_of_stroke_idx(tGpTimingData *gtd,
+ RNG *rng,
+ const int idx,
+ const int nbr_gaps,
+ int *nbr_done_gaps,
+ const float tot_gaps_time,
+ const float delta_time,
+ float *next_delta_time)
{
int j;
@@ -362,10 +365,10 @@ static int gp_find_end_of_stroke_idx(tGpTimingData *gtd,
return j - 1;
}
-static void gp_stroke_path_animation_preprocess_gaps(tGpTimingData *gtd,
- RNG *rng,
- int *nbr_gaps,
- float *tot_gaps_time)
+static void gpencil_stroke_path_animation_preprocess_gaps(tGpTimingData *gtd,
+ RNG *rng,
+ int *nbr_gaps,
+ float *tot_gaps_time)
{
int i;
float delta_time = 0.0f;
@@ -393,16 +396,16 @@ static void gp_stroke_path_animation_preprocess_gaps(tGpTimingData *gtd,
}
}
-static void gp_stroke_path_animation_add_keyframes(ReportList *reports,
- PointerRNA ptr,
- PropertyRNA *prop,
- FCurve *fcu,
- Curve *cu,
- tGpTimingData *gtd,
- RNG *rng,
- const float time_range,
- const int nbr_gaps,
- const float tot_gaps_time)
+static void gpencil_stroke_path_animation_add_keyframes(ReportList *reports,
+ PointerRNA ptr,
+ PropertyRNA *prop,
+ FCurve *fcu,
+ Curve *cu,
+ tGpTimingData *gtd,
+ RNG *rng,
+ const float time_range,
+ const int nbr_gaps,
+ const float tot_gaps_time)
{
/* Use actual recorded timing! */
const float time_start = (float)gtd->start_frame;
@@ -428,7 +431,7 @@ static void gp_stroke_path_animation_add_keyframes(ReportList *reports,
start_stroke_idx = i;
delta_time = next_delta_time;
/* find end of that new stroke */
- end_stroke_idx = gp_find_end_of_stroke_idx(
+ end_stroke_idx = gpencil_find_end_of_stroke_idx(
gtd, rng, i, nbr_gaps, &nbr_done_gaps, tot_gaps_time, delta_time, &next_delta_time);
/* This one should *never* be negative! */
end_stroke_time = time_start +
@@ -486,10 +489,10 @@ static void gp_stroke_path_animation_add_keyframes(ReportList *reports,
}
}
-static void gp_stroke_path_animation(bContext *C,
- ReportList *reports,
- Curve *cu,
- tGpTimingData *gtd)
+static void gpencil_stroke_path_animation(bContext *C,
+ ReportList *reports,
+ Curve *cu,
+ tGpTimingData *gtd)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
@@ -557,7 +560,7 @@ static void gp_stroke_path_animation(bContext *C,
/* Pre-process gaps, in case we don't want to keep their original timing */
if (gtd->mode == GP_STROKECONVERT_TIMING_CUSTOMGAP) {
- gp_stroke_path_animation_preprocess_gaps(gtd, rng, &nbr_gaps, &tot_gaps_time);
+ gpencil_stroke_path_animation_preprocess_gaps(gtd, rng, &nbr_gaps, &tot_gaps_time);
}
if (gtd->realtime) {
@@ -571,7 +574,7 @@ static void gp_stroke_path_animation(bContext *C,
printf("GP Stroke Path Conversion: Starting keying!\n");
}
- gp_stroke_path_animation_add_keyframes(
+ gpencil_stroke_path_animation_add_keyframes(
reports, ptr, prop, fcu, cu, gtd, rng, time_range, nbr_gaps, tot_gaps_time);
BLI_rng_free(rng);
@@ -603,16 +606,16 @@ static void gp_stroke_path_animation(bContext *C,
/* convert stroke to 3d path */
/* helper */
-static void gp_stroke_to_path_add_point(tGpTimingData *gtd,
- BPoint *bp,
- const float p[3],
- const float prev_p[3],
- const bool do_gtd,
- const double inittime,
- const float time,
- const float width,
- const float rad_fac,
- float minmax_weights[2])
+static void gpencil_stroke_to_path_add_point(tGpTimingData *gtd,
+ BPoint *bp,
+ const float p[3],
+ const float prev_p[3],
+ const bool do_gtd,
+ const double inittime,
+ const float time,
+ const float width,
+ const float rad_fac,
+ float minmax_weights[2])
{
copy_v3_v3(bp->vec, p);
bp->vec[3] = 1.0f;
@@ -631,22 +634,22 @@ static void gp_stroke_to_path_add_point(tGpTimingData *gtd,
/* Update timing data */
if (do_gtd) {
- gp_timing_data_add_point(gtd, inittime, time, len_v3v3(prev_p, p));
+ gpencil_timing_data_add_point(gtd, inittime, time, len_v3v3(prev_p, p));
}
}
-static void gp_stroke_to_path(bContext *C,
- bGPDlayer *gpl,
- bGPDstroke *gps,
- Curve *cu,
- rctf *subrect,
- Nurb **curnu,
- float minmax_weights[2],
- const float rad_fac,
- bool stitch,
- const bool add_start_point,
- const bool add_end_point,
- tGpTimingData *gtd)
+static void gpencil_stroke_to_path(bContext *C,
+ bGPDlayer *gpl,
+ bGPDstroke *gps,
+ Curve *cu,
+ rctf *subrect,
+ Nurb **curnu,
+ float minmax_weights[2],
+ const float rad_fac,
+ bool stitch,
+ const bool add_start_point,
+ const bool add_end_point,
+ tGpTimingData *gtd)
{
bGPDspoint *pt;
Nurb *nu = (curnu) ? *curnu : NULL;
@@ -682,7 +685,7 @@ static void gp_stroke_to_path(bContext *C,
}
if (do_gtd) {
- gp_timing_data_set_nbr(gtd, nu->pntsu);
+ gpencil_timing_data_set_nbr(gtd, nu->pntsu);
}
/* If needed, make the link between both strokes with two zero-radius additional points */
@@ -710,7 +713,7 @@ static void gp_stroke_to_path(bContext *C,
bp = &nu->bp[old_nbp - 1];
/* First point */
- gp_strokepoint_convertcoords(C, gpl, gps, gps->points, p, subrect);
+ gpencil_strokepoint_convertcoords(C, gpl, gps, gps->points, p, subrect);
if (prev_bp) {
interp_v3_v3v3(p1, bp->vec, prev_bp->vec, -GAP_DFAC);
if (do_gtd) {
@@ -725,21 +728,21 @@ static void gp_stroke_to_path(bContext *C,
}
}
bp++;
- gp_stroke_to_path_add_point(gtd,
- bp,
- p1,
- (bp - 1)->vec,
- do_gtd,
- gps->prev->inittime,
- dt1,
- 0.0f,
- rad_fac,
- minmax_weights);
+ gpencil_stroke_to_path_add_point(gtd,
+ bp,
+ p1,
+ (bp - 1)->vec,
+ do_gtd,
+ gps->prev->inittime,
+ dt1,
+ 0.0f,
+ rad_fac,
+ minmax_weights);
/* Second point */
/* Note dt2 is always negative, which marks the gap. */
if (gps->totpoints > 1) {
- gp_strokepoint_convertcoords(C, gpl, gps, gps->points + 1, next_p, subrect);
+ gpencil_strokepoint_convertcoords(C, gpl, gps, gps->points + 1, next_p, subrect);
interp_v3_v3v3(p2, p, next_p, -GAP_DFAC);
if (do_gtd) {
dt2 = interpf(gps->points[1].time, gps->points[0].time, -GAP_DFAC);
@@ -752,7 +755,7 @@ static void gp_stroke_to_path(bContext *C,
}
}
bp++;
- gp_stroke_to_path_add_point(
+ gpencil_stroke_to_path_add_point(
gtd, bp, p2, p1, do_gtd, gps->inittime, dt2, 0.0f, rad_fac, minmax_weights);
old_nbp += 2;
@@ -761,9 +764,9 @@ static void gp_stroke_to_path(bContext *C,
float p[3], next_p[3];
float dt = 0.0f;
- gp_strokepoint_convertcoords(C, gpl, gps, gps->points, p, subrect);
+ gpencil_strokepoint_convertcoords(C, gpl, gps, gps->points, p, subrect);
if (gps->totpoints > 1) {
- gp_strokepoint_convertcoords(C, gpl, gps, gps->points + 1, next_p, subrect);
+ gpencil_strokepoint_convertcoords(C, gpl, gps, gps->points + 1, next_p, subrect);
interp_v3_v3v3(p, p, next_p, -GAP_DFAC);
if (do_gtd) {
dt = interpf(gps->points[1].time, gps->points[0].time, -GAP_DFAC);
@@ -778,7 +781,7 @@ static void gp_stroke_to_path(bContext *C,
* (which would be expected value) would not work
* (it would be *before* gtd->inittime, which is not supported currently).
*/
- gp_stroke_to_path_add_point(
+ gpencil_stroke_to_path_add_point(
gtd, bp, p, p, do_gtd, gps->inittime, dt, 0.0f, rad_fac, minmax_weights);
old_nbp++;
@@ -796,18 +799,18 @@ static void gp_stroke_to_path(bContext *C,
float width = pt->pressure * (gps->thickness + gpl->line_change) * WIDTH_CORR_FAC;
/* get coordinates to add at */
- gp_strokepoint_convertcoords(C, gpl, gps, pt, p, subrect);
-
- gp_stroke_to_path_add_point(gtd,
- bp,
- p,
- (prev_bp) ? prev_bp->vec : p,
- do_gtd,
- gps->inittime,
- pt->time,
- width,
- rad_fac,
- minmax_weights);
+ gpencil_strokepoint_convertcoords(C, gpl, gps, pt, p, subrect);
+
+ gpencil_stroke_to_path_add_point(gtd,
+ bp,
+ p,
+ (prev_bp) ? prev_bp->vec : p,
+ do_gtd,
+ gps->inittime,
+ pt->time,
+ width,
+ rad_fac,
+ minmax_weights);
prev_bp = bp;
}
@@ -829,7 +832,7 @@ static void gp_stroke_to_path(bContext *C,
dt = GAP_DFAC; /* Rather arbitrary too! */
}
/* Note bp has already been incremented in main loop above, so it points to the right place. */
- gp_stroke_to_path_add_point(
+ gpencil_stroke_to_path_add_point(
gtd, bp, p, prev_bp->vec, do_gtd, gps->inittime, dt, 0.0f, rad_fac, minmax_weights);
}
@@ -847,18 +850,18 @@ static void gp_stroke_to_path(bContext *C,
/* convert stroke to 3d bezier */
/* helper */
-static void gp_stroke_to_bezier_add_point(tGpTimingData *gtd,
- BezTriple *bezt,
- const float p[3],
- const float h1[3],
- const float h2[3],
- const float prev_p[3],
- const bool do_gtd,
- const double inittime,
- const float time,
- const float width,
- const float rad_fac,
- float minmax_weights[2])
+static void gpencil_stroke_to_bezier_add_point(tGpTimingData *gtd,
+ BezTriple *bezt,
+ const float p[3],
+ const float h1[3],
+ const float h2[3],
+ const float prev_p[3],
+ const bool do_gtd,
+ const double inittime,
+ const float time,
+ const float width,
+ const float rad_fac,
+ float minmax_weights[2])
{
copy_v3_v3(bezt->vec[0], h1);
copy_v3_v3(bezt->vec[1], p);
@@ -879,22 +882,22 @@ static void gp_stroke_to_bezier_add_point(tGpTimingData *gtd,
/* Update timing data */
if (do_gtd) {
- gp_timing_data_add_point(gtd, inittime, time, len_v3v3(prev_p, p));
+ gpencil_timing_data_add_point(gtd, inittime, time, len_v3v3(prev_p, p));
}
}
-static void gp_stroke_to_bezier(bContext *C,
- bGPDlayer *gpl,
- bGPDstroke *gps,
- Curve *cu,
- rctf *subrect,
- Nurb **curnu,
- float minmax_weights[2],
- const float rad_fac,
- bool stitch,
- const bool add_start_point,
- const bool add_end_point,
- tGpTimingData *gtd)
+static void gpencil_stroke_to_bezier(bContext *C,
+ bGPDlayer *gpl,
+ bGPDstroke *gps,
+ Curve *cu,
+ rctf *subrect,
+ Nurb **curnu,
+ float minmax_weights[2],
+ const float rad_fac,
+ bool stitch,
+ const bool add_start_point,
+ const bool add_end_point,
+ tGpTimingData *gtd)
{
bGPDspoint *pt;
Nurb *nu = (curnu) ? *curnu : NULL;
@@ -927,7 +930,7 @@ static void gp_stroke_to_bezier(bContext *C,
}
if (do_gtd) {
- gp_timing_data_set_nbr(gtd, nu->pntsu);
+ gpencil_timing_data_set_nbr(gtd, nu->pntsu);
}
tot = gps->totpoints;
@@ -935,12 +938,13 @@ static void gp_stroke_to_bezier(bContext *C,
/* get initial coordinates */
pt = gps->points;
if (tot) {
- gp_strokepoint_convertcoords(C, gpl, gps, pt, (stitch) ? p3d_prev : p3d_cur, subrect);
+ gpencil_strokepoint_convertcoords(C, gpl, gps, pt, (stitch) ? p3d_prev : p3d_cur, subrect);
if (tot > 1) {
- gp_strokepoint_convertcoords(C, gpl, gps, pt + 1, (stitch) ? p3d_cur : p3d_next, subrect);
+ gpencil_strokepoint_convertcoords(
+ C, gpl, gps, pt + 1, (stitch) ? p3d_cur : p3d_next, subrect);
}
if (stitch && tot > 2) {
- gp_strokepoint_convertcoords(C, gpl, gps, pt + 2, p3d_next, subrect);
+ gpencil_strokepoint_convertcoords(C, gpl, gps, pt + 2, p3d_next, subrect);
}
}
@@ -1017,24 +1021,24 @@ static void gp_stroke_to_bezier(bContext *C,
interp_v3_v3v3(h1, p1, bezt->vec[1], BEZT_HANDLE_FAC);
interp_v3_v3v3(h2, p1, p2, BEZT_HANDLE_FAC);
bezt++;
- gp_stroke_to_bezier_add_point(gtd,
- bezt,
- p1,
- h1,
- h2,
- (bezt - 1)->vec[1],
- do_gtd,
- gps->prev->inittime,
- dt1,
- 0.0f,
- rad_fac,
- minmax_weights);
+ gpencil_stroke_to_bezier_add_point(gtd,
+ bezt,
+ p1,
+ h1,
+ h2,
+ (bezt - 1)->vec[1],
+ do_gtd,
+ gps->prev->inittime,
+ dt1,
+ 0.0f,
+ rad_fac,
+ minmax_weights);
/* Second point */
interp_v3_v3v3(h1, p2, p1, BEZT_HANDLE_FAC);
interp_v3_v3v3(h2, p2, p3d_cur, BEZT_HANDLE_FAC);
bezt++;
- gp_stroke_to_bezier_add_point(
+ gpencil_stroke_to_bezier_add_point(
gtd, bezt, p2, h1, h2, p1, do_gtd, gps->inittime, dt2, 0.0f, rad_fac, minmax_weights);
old_nbezt += 2;
@@ -1059,7 +1063,7 @@ static void gp_stroke_to_bezier(bContext *C,
interp_v3_v3v3(h1, p, p3d_cur, -BEZT_HANDLE_FAC);
interp_v3_v3v3(h2, p, p3d_cur, BEZT_HANDLE_FAC);
bezt = &nu->bezt[old_nbezt];
- gp_stroke_to_bezier_add_point(
+ gpencil_stroke_to_bezier_add_point(
gtd, bezt, p, h1, h2, p, do_gtd, gps->inittime, dt, 0.0f, rad_fac, minmax_weights);
old_nbezt++;
@@ -1088,25 +1092,25 @@ static void gp_stroke_to_bezier(bContext *C,
interp_v3_v3v3(h2, p3d_cur, p3d_prev, -BEZT_HANDLE_FAC);
}
- gp_stroke_to_bezier_add_point(gtd,
- bezt,
- p3d_cur,
- h1,
- h2,
- prev_bezt ? prev_bezt->vec[1] : p3d_cur,
- do_gtd,
- gps->inittime,
- pt->time,
- width,
- rad_fac,
- minmax_weights);
+ gpencil_stroke_to_bezier_add_point(gtd,
+ bezt,
+ p3d_cur,
+ h1,
+ h2,
+ prev_bezt ? prev_bezt->vec[1] : p3d_cur,
+ do_gtd,
+ gps->inittime,
+ pt->time,
+ width,
+ rad_fac,
+ minmax_weights);
/* shift coord vects */
copy_v3_v3(p3d_prev, p3d_cur);
copy_v3_v3(p3d_cur, p3d_next);
if (i + 2 < tot) {
- gp_strokepoint_convertcoords(C, gpl, gps, pt + 2, p3d_next, subrect);
+ gpencil_strokepoint_convertcoords(C, gpl, gps, pt + 2, p3d_next, subrect);
}
prev_bezt = bezt;
@@ -1138,18 +1142,18 @@ static void gp_stroke_to_bezier(bContext *C,
interp_v3_v3v3(h2, p, prev_bezt->vec[1], -BEZT_HANDLE_FAC);
/* Note bezt has already been incremented in main loop above,
* so it points to the right place. */
- gp_stroke_to_bezier_add_point(gtd,
- bezt,
- p,
- h1,
- h2,
- prev_bezt->vec[1],
- do_gtd,
- gps->inittime,
- dt,
- 0.0f,
- rad_fac,
- minmax_weights);
+ gpencil_stroke_to_bezier_add_point(gtd,
+ bezt,
+ p,
+ h1,
+ h2,
+ prev_bezt->vec[1],
+ do_gtd,
+ gps->inittime,
+ dt,
+ 0.0f,
+ rad_fac,
+ minmax_weights);
}
/* must calculate handles or else we crash */
@@ -1167,7 +1171,7 @@ static void gp_stroke_to_bezier(bContext *C,
#undef WIDTH_CORR_FAC
#undef BEZT_HANDLE_FAC
-static void gp_stroke_finalize_curve_endpoints(Curve *cu)
+static void gpencil_stroke_finalize_curve_endpoints(Curve *cu)
{
/* start */
Nurb *nu = cu->nurb.first;
@@ -1202,7 +1206,7 @@ static void gp_stroke_finalize_curve_endpoints(Curve *cu)
}
}
-static void gp_stroke_norm_curve_weights(Curve *cu, const float minmax_weights[2])
+static void gpencil_stroke_norm_curve_weights(Curve *cu, const float minmax_weights[2])
{
Nurb *nu;
const float delta = minmax_weights[0];
@@ -1233,7 +1237,7 @@ static void gp_stroke_norm_curve_weights(Curve *cu, const float minmax_weights[2
}
}
-static int gp_camera_view_subrect(bContext *C, rctf *subrect)
+static int gpencil_camera_view_subrect(bContext *C, rctf *subrect)
{
View3D *v3d = CTX_wm_view3d(C);
ARegion *region = CTX_wm_region(C);
@@ -1255,15 +1259,15 @@ static int gp_camera_view_subrect(bContext *C, rctf *subrect)
/* convert a given grease-pencil layer to a 3d-curve representation
* (using current view if appropriate) */
-static void gp_layer_to_curve(bContext *C,
- ReportList *reports,
- bGPdata *gpd,
- bGPDlayer *gpl,
- const int mode,
- const bool norm_weights,
- const float rad_fac,
- const bool link_strokes,
- tGpTimingData *gtd)
+static void gpencil_layer_to_curve(bContext *C,
+ ReportList *reports,
+ bGPdata *gpd,
+ bGPDlayer *gpl,
+ const int mode,
+ const bool norm_weights,
+ const float rad_fac,
+ const bool link_strokes,
+ tGpTimingData *gtd)
{
struct Main *bmain = CTX_data_main(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
@@ -1292,7 +1296,7 @@ static void gp_layer_to_curve(bContext *C,
}
/* initialize camera framing */
- if (gp_camera_view_subrect(C, &subrect)) {
+ if (gpencil_camera_view_subrect(C, &subrect)) {
subrect_ptr = &subrect;
}
@@ -1335,33 +1339,33 @@ static void gp_layer_to_curve(bContext *C,
switch (mode) {
case GP_STROKECONVERT_PATH:
- gp_stroke_to_path(C,
- gpl,
- gps,
- cu,
- subrect_ptr,
- &nu,
- minmax_weights,
- rad_fac,
- stitch,
- add_start_point,
- add_end_point,
- gtd);
+ gpencil_stroke_to_path(C,
+ gpl,
+ gps,
+ cu,
+ subrect_ptr,
+ &nu,
+ minmax_weights,
+ rad_fac,
+ stitch,
+ add_start_point,
+ add_end_point,
+ gtd);
break;
case GP_STROKECONVERT_CURVE:
case GP_STROKECONVERT_POLY: /* convert after */
- gp_stroke_to_bezier(C,
- gpl,
- gps,
- cu,
- subrect_ptr,
- &nu,
- minmax_weights,
- rad_fac,
- stitch,
- add_start_point,
- add_end_point,
- gtd);
+ gpencil_stroke_to_bezier(C,
+ gpl,
+ gps,
+ cu,
+ subrect_ptr,
+ &nu,
+ minmax_weights,
+ rad_fac,
+ stitch,
+ add_start_point,
+ add_end_point,
+ gtd);
break;
default:
BLI_assert(!"invalid mode");
@@ -1372,16 +1376,16 @@ static void gp_layer_to_curve(bContext *C,
/* If link_strokes, be sure first and last points have a zero weight/size! */
if (link_strokes) {
- gp_stroke_finalize_curve_endpoints(cu);
+ gpencil_stroke_finalize_curve_endpoints(cu);
}
/* Update curve's weights, if needed */
if (norm_weights && ((minmax_weights[0] > 0.0f) || (minmax_weights[1] < 1.0f))) {
- gp_stroke_norm_curve_weights(cu, minmax_weights);
+ gpencil_stroke_norm_curve_weights(cu, minmax_weights);
}
/* Create the path animation, if needed */
- gp_stroke_path_animation(C, reports, cu, gtd);
+ gpencil_stroke_path_animation(C, reports, cu, gtd);
if (mode == GP_STROKECONVERT_POLY) {
for (nu = cu->nurb.first; nu; nu = nu->next) {
@@ -1399,7 +1403,7 @@ static void gp_layer_to_curve(bContext *C,
/* Check a GP layer has valid timing data! Else, most timing options are hidden in the operator.
* op may be NULL.
*/
-static bool gp_convert_check_has_valid_timing(bContext *C, bGPDlayer *gpl, wmOperator *op)
+static bool gpencil_convert_check_has_valid_timing(bContext *C, bGPDlayer *gpl, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
@@ -1447,9 +1451,9 @@ static bool gp_convert_check_has_valid_timing(bContext *C, bGPDlayer *gpl, wmOpe
}
/* Check end_frame is always > start frame! */
-static void gp_convert_set_end_frame(struct Main *UNUSED(main),
- struct Scene *UNUSED(scene),
- struct PointerRNA *ptr)
+static void gpencil_convert_set_end_frame(struct Main *UNUSED(main),
+ struct Scene *UNUSED(scene),
+ struct PointerRNA *ptr)
{
int start_frame = RNA_int_get(ptr, "start_frame");
int end_frame = RNA_int_get(ptr, "end_frame");
@@ -1459,7 +1463,7 @@ static void gp_convert_set_end_frame(struct Main *UNUSED(main),
}
}
-static bool gp_convert_poll(bContext *C)
+static bool gpencil_convert_poll(bContext *C)
{
Object *ob = CTX_data_active_object(C);
Scene *scene = CTX_data_scene(C);
@@ -1481,7 +1485,7 @@ static bool gp_convert_poll(bContext *C)
(gpf->strokes.first) && (!GPENCIL_ANY_EDIT_MODE(gpd)));
}
-static int gp_convert_layer_exec(bContext *C, wmOperator *op)
+static int gpencil_convert_layer_exec(bContext *C, wmOperator *op)
{
PropertyRNA *prop = RNA_struct_find_property(op->ptr, "use_timing_data");
Object *ob = CTX_data_active_object(C);
@@ -1502,7 +1506,7 @@ static int gp_convert_layer_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- if (!RNA_property_is_set(op->ptr, prop) && !gp_convert_check_has_valid_timing(C, gpl, op)) {
+ if (!RNA_property_is_set(op->ptr, prop) && !gpencil_convert_check_has_valid_timing(C, gpl, op)) {
BKE_report(op->reports,
RPT_WARNING,
"Current Grease Pencil strokes have no valid timing data, most timing options will "
@@ -1539,7 +1543,8 @@ static int gp_convert_layer_exec(bContext *C, wmOperator *op)
gtd.offset_time = 0.0f;
/* perform conversion */
- gp_layer_to_curve(C, op->reports, gpd, gpl, mode, norm_weights, rad_fac, link_strokes, &gtd);
+ gpencil_layer_to_curve(
+ C, op->reports, gpd, gpl, mode, norm_weights, rad_fac, link_strokes, &gtd);
/* free temp memory */
if (gtd.dists) {
@@ -1560,9 +1565,9 @@ static int gp_convert_layer_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
-static bool gp_convert_poll_property(const bContext *UNUSED(C),
- wmOperator *op,
- const PropertyRNA *prop)
+static bool gpencil_convert_poll_property(const bContext *UNUSED(C),
+ wmOperator *op,
+ const PropertyRNA *prop)
{
PointerRNA *ptr = op->ptr;
const char *prop_id = RNA_property_identifier(prop);
@@ -1641,9 +1646,9 @@ void GPENCIL_OT_convert(wmOperatorType *ot)
/* callbacks */
ot->invoke = WM_menu_invoke;
- ot->exec = gp_convert_layer_exec;
- ot->poll = gp_convert_poll;
- ot->poll_property = gp_convert_poll_property;
+ ot->exec = gpencil_convert_layer_exec;
+ ot->poll = gpencil_convert_poll;
+ ot->poll_property = gpencil_convert_poll_property;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -1725,7 +1730,7 @@ void GPENCIL_OT_convert(wmOperatorType *ot)
"The end frame of the path control curve (if Realtime is not set)",
1,
100000);
- RNA_def_property_update_runtime(prop, gp_convert_set_end_frame);
+ RNA_def_property_update_runtime(prop, gpencil_convert_set_end_frame);
RNA_def_float(ot->srna,
"gap_duration",
@@ -1771,7 +1776,10 @@ static bool image_to_gpencil_poll(bContext *C)
{
SpaceLink *sl = CTX_wm_space_data(C);
if ((sl != NULL) && (sl->spacetype == SPACE_IMAGE)) {
- return true;
+ SpaceImage *sima = CTX_wm_space_image(C);
+ Image *image = sima->image;
+ ImageUser iuser = sima->iuser;
+ return BKE_image_has_ibuf(image, &iuser);
}
return false;
@@ -1811,7 +1819,7 @@ static int image_to_gpencil_exec(bContext *C, wmOperator *op)
if (done) {
/* Delete any selected point. */
LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
- gp_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_SELECT, false, 0);
+ gpencil_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_SELECT, false, 0);
}
BKE_reportf(op->reports, RPT_INFO, "Object created");
diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c
index 8c80334bf8a..44b866d81d7 100644
--- a/source/blender/editors/gpencil/gpencil_data.c
+++ b/source/blender/editors/gpencil/gpencil_data.c
@@ -90,7 +90,7 @@
/* Datablock Operators */
/* ******************* Add New Data ************************ */
-static bool gp_data_add_poll(bContext *C)
+static bool gpencil_data_add_poll(bContext *C)
{
/* the base line we have is that we have somewhere to add Grease Pencil data */
@@ -98,7 +98,7 @@ static bool gp_data_add_poll(bContext *C)
}
/* add new datablock - wrapper around API */
-static int gp_data_add_exec(bContext *C, wmOperator *op)
+static int gpencil_data_add_exec(bContext *C, wmOperator *op)
{
PointerRNA gpd_owner = {NULL};
bGPdata **gpd_ptr = ED_annotation_data_get_pointers(C, &gpd_owner);
@@ -145,14 +145,14 @@ void GPENCIL_OT_annotation_add(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* callbacks */
- ot->exec = gp_data_add_exec;
- ot->poll = gp_data_add_poll;
+ ot->exec = gpencil_data_add_exec;
+ ot->poll = gpencil_data_add_poll;
}
/* ******************* Unlink Data ************************ */
/* poll callback for adding data/layers - special */
-static bool gp_data_unlink_poll(bContext *C)
+static bool gpencil_data_unlink_poll(bContext *C)
{
bGPdata **gpd_ptr = ED_annotation_data_get_pointers(C, NULL);
@@ -168,7 +168,7 @@ static bool gp_data_unlink_poll(bContext *C)
}
/* unlink datablock - wrapper around API */
-static int gp_data_unlink_exec(bContext *C, wmOperator *op)
+static int gpencil_data_unlink_exec(bContext *C, wmOperator *op)
{
bGPdata **gpd_ptr = ED_annotation_data_get_pointers(C, NULL);
@@ -199,8 +199,8 @@ void GPENCIL_OT_data_unlink(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* callbacks */
- ot->exec = gp_data_unlink_exec;
- ot->poll = gp_data_unlink_poll;
+ ot->exec = gpencil_data_unlink_exec;
+ ot->poll = gpencil_data_unlink_poll;
}
/* ************************************************ */
@@ -209,7 +209,7 @@ void GPENCIL_OT_data_unlink(wmOperatorType *ot)
/* ******************* Add New Layer ************************ */
/* add new layer - wrapper around API */
-static int gp_layer_add_exec(bContext *C, wmOperator *op)
+static int gpencil_layer_add_exec(bContext *C, wmOperator *op)
{
const bool is_annotation = STREQ(op->idname, "GPENCIL_OT_layer_annotation_add");
@@ -268,11 +268,11 @@ void GPENCIL_OT_layer_add(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* callbacks */
- ot->exec = gp_layer_add_exec;
- ot->poll = gp_add_poll;
+ ot->exec = gpencil_layer_add_exec;
+ ot->poll = gpencil_add_poll;
}
-static bool gp_add_annotation_poll(bContext *C)
+static bool gpencil_add_annotation_poll(bContext *C)
{
return ED_annotation_data_get_pointers(C, NULL) != NULL;
}
@@ -287,12 +287,12 @@ void GPENCIL_OT_layer_annotation_add(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* callbacks */
- ot->exec = gp_layer_add_exec;
- ot->poll = gp_add_annotation_poll;
+ ot->exec = gpencil_layer_add_exec;
+ ot->poll = gpencil_add_annotation_poll;
}
/* ******************* Remove Active Layer ************************* */
-static int gp_layer_remove_exec(bContext *C, wmOperator *op)
+static int gpencil_layer_remove_exec(bContext *C, wmOperator *op)
{
const bool is_annotation = STREQ(op->idname, "GPENCIL_OT_layer_annotation_remove");
@@ -345,11 +345,11 @@ void GPENCIL_OT_layer_remove(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* callbacks */
- ot->exec = gp_layer_remove_exec;
- ot->poll = gp_active_layer_poll;
+ ot->exec = gpencil_layer_remove_exec;
+ ot->poll = gpencil_active_layer_poll;
}
-static bool gp_active_layer_annotation_poll(bContext *C)
+static bool gpencil_active_layer_annotation_poll(bContext *C)
{
bGPdata *gpd = ED_annotation_data_get_active(C);
bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd);
@@ -367,8 +367,8 @@ void GPENCIL_OT_layer_annotation_remove(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* callbacks */
- ot->exec = gp_layer_remove_exec;
- ot->poll = gp_active_layer_annotation_poll;
+ ot->exec = gpencil_layer_remove_exec;
+ ot->poll = gpencil_active_layer_annotation_poll;
}
/* ******************* Move Layer Up/Down ************************** */
@@ -377,7 +377,7 @@ enum {
GP_LAYER_MOVE_DOWN = 1,
};
-static int gp_layer_move_exec(bContext *C, wmOperator *op)
+static int gpencil_layer_move_exec(bContext *C, wmOperator *op)
{
const bool is_annotation = STREQ(op->idname, "GPENCIL_OT_layer_annotation_move");
@@ -418,8 +418,8 @@ void GPENCIL_OT_layer_move(wmOperatorType *ot)
ot->description = "Move the active Grease Pencil layer up/down in the list";
/* api callbacks */
- ot->exec = gp_layer_move_exec;
- ot->poll = gp_active_layer_poll;
+ ot->exec = gpencil_layer_move_exec;
+ ot->poll = gpencil_active_layer_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -441,8 +441,8 @@ void GPENCIL_OT_layer_annotation_move(wmOperatorType *ot)
ot->description = "Move the active Annotation layer up/down in the list";
/* api callbacks */
- ot->exec = gp_layer_move_exec;
- ot->poll = gp_active_layer_annotation_poll;
+ ot->exec = gpencil_layer_move_exec;
+ ot->poll = gpencil_active_layer_annotation_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -451,7 +451,7 @@ void GPENCIL_OT_layer_annotation_move(wmOperatorType *ot)
}
/* ********************* Duplicate Layer ************************** */
-static int gp_layer_copy_exec(bContext *C, wmOperator *UNUSED(op))
+static int gpencil_layer_copy_exec(bContext *C, wmOperator *UNUSED(op))
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd);
@@ -491,8 +491,8 @@ void GPENCIL_OT_layer_duplicate(wmOperatorType *ot)
ot->description = "Make a copy of the active Grease Pencil layer";
/* callbacks */
- ot->exec = gp_layer_copy_exec;
- ot->poll = gp_active_layer_poll;
+ ot->exec = gpencil_layer_copy_exec;
+ ot->poll = gpencil_active_layer_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -504,7 +504,7 @@ enum {
GP_LAYER_COPY_OBJECT_ACT_FRAME = 1,
};
-static bool gp_layer_duplicate_object_poll(bContext *C)
+static bool gpencil_layer_duplicate_object_poll(bContext *C)
{
ViewLayer *view_layer = CTX_data_view_layer(C);
Object *ob = CTX_data_active_object(C);
@@ -529,7 +529,7 @@ static bool gp_layer_duplicate_object_poll(bContext *C)
return false;
}
-static int gp_layer_duplicate_object_exec(bContext *C, wmOperator *op)
+static int gpencil_layer_duplicate_object_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
@@ -622,8 +622,8 @@ void GPENCIL_OT_layer_duplicate_object(wmOperatorType *ot)
ot->description = "Make a copy of the active Grease Pencil layer to new object";
/* callbacks */
- ot->exec = gp_layer_duplicate_object_exec;
- ot->poll = gp_layer_duplicate_object_poll;
+ ot->exec = gpencil_layer_duplicate_object_exec;
+ ot->poll = gpencil_layer_duplicate_object_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -641,7 +641,7 @@ enum {
GP_FRAME_DUP_ALL = 1,
};
-static int gp_frame_duplicate_exec(bContext *C, wmOperator *op)
+static int gpencil_frame_duplicate_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
bGPDlayer *gpl_active = BKE_gpencil_layer_active_get(gpd);
@@ -685,8 +685,8 @@ void GPENCIL_OT_frame_duplicate(wmOperatorType *ot)
ot->description = "Make a copy of the active Grease Pencil Frame";
/* callbacks */
- ot->exec = gp_frame_duplicate_exec;
- ot->poll = gp_active_layer_poll;
+ ot->exec = gpencil_frame_duplicate_exec;
+ ot->poll = gpencil_active_layer_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -700,7 +700,7 @@ enum {
GP_FRAME_CLEAN_FILL_ALL = 1,
};
-static int gp_frame_clean_fill_exec(bContext *C, wmOperator *op)
+static int gpencil_frame_clean_fill_exec(bContext *C, wmOperator *op)
{
bool changed = false;
bGPdata *gpd = ED_gpencil_data_get_active(C);
@@ -770,8 +770,8 @@ void GPENCIL_OT_frame_clean_fill(wmOperatorType *ot)
ot->description = "Remove 'no fill' boundary strokes";
/* callbacks */
- ot->exec = gp_frame_clean_fill_exec;
- ot->poll = gp_active_layer_poll;
+ ot->exec = gpencil_frame_clean_fill_exec;
+ ot->poll = gpencil_active_layer_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -780,7 +780,7 @@ void GPENCIL_OT_frame_clean_fill(wmOperatorType *ot)
}
/* ********************* Clean Loose Boundaries on Frame ************************** */
-static int gp_frame_clean_loose_exec(bContext *C, wmOperator *op)
+static int gpencil_frame_clean_loose_exec(bContext *C, wmOperator *op)
{
bool changed = false;
bGPdata *gpd = ED_gpencil_data_get_active(C);
@@ -846,8 +846,8 @@ void GPENCIL_OT_frame_clean_loose(wmOperatorType *ot)
ot->description = "Remove loose points";
/* callbacks */
- ot->exec = gp_frame_clean_loose_exec;
- ot->poll = gp_active_layer_poll;
+ ot->exec = gpencil_frame_clean_loose_exec;
+ ot->poll = gpencil_active_layer_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -865,7 +865,7 @@ void GPENCIL_OT_frame_clean_loose(wmOperatorType *ot)
/* *********************** Hide Layers ******************************** */
-static int gp_hide_exec(bContext *C, wmOperator *op)
+static int gpencil_hide_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
bGPDlayer *layer = BKE_gpencil_layer_active_get(gpd);
@@ -908,8 +908,8 @@ void GPENCIL_OT_hide(wmOperatorType *ot)
ot->description = "Hide selected/unselected Grease Pencil layers";
/* callbacks */
- ot->exec = gp_hide_exec;
- ot->poll = gp_active_layer_poll; /* NOTE: we need an active layer to play with */
+ ot->exec = gpencil_hide_exec;
+ ot->poll = gpencil_active_layer_poll; /* NOTE: we need an active layer to play with */
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -922,12 +922,12 @@ void GPENCIL_OT_hide(wmOperatorType *ot)
/* ********************** Show All Layers ***************************** */
/* poll callback for showing layers */
-static bool gp_reveal_poll(bContext *C)
+static bool gpencil_reveal_poll(bContext *C)
{
return ED_gpencil_data_get_active(C) != NULL;
}
-static void gp_reveal_select_frame(bContext *C, bGPDframe *frame, bool select)
+static void gpencil_reveal_select_frame(bContext *C, bGPDframe *frame, bool select)
{
bGPDstroke *gps;
for (gps = frame->strokes.first; gps; gps = gps->next) {
@@ -948,7 +948,7 @@ static void gp_reveal_select_frame(bContext *C, bGPDframe *frame, bool select)
}
}
-static int gp_reveal_exec(bContext *C, wmOperator *op)
+static int gpencil_reveal_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
const bool select = RNA_boolean_get(op->ptr, "select");
@@ -967,14 +967,14 @@ static int gp_reveal_exec(bContext *C, wmOperator *op)
if (select) {
/* select all strokes on active frame only (same as select all operator) */
if (gpl->actframe) {
- gp_reveal_select_frame(C, gpl->actframe, true);
+ gpencil_reveal_select_frame(C, gpl->actframe, true);
}
}
else {
/* deselect strokes on all frames (same as deselect all operator) */
bGPDframe *gpf;
for (gpf = gpl->frames.first; gpf; gpf = gpf->next) {
- gp_reveal_select_frame(C, gpf, false);
+ gpencil_reveal_select_frame(C, gpf, false);
}
}
}
@@ -996,8 +996,8 @@ void GPENCIL_OT_reveal(wmOperatorType *ot)
ot->description = "Show all Grease Pencil layers";
/* callbacks */
- ot->exec = gp_reveal_exec;
- ot->poll = gp_reveal_poll;
+ ot->exec = gpencil_reveal_exec;
+ ot->poll = gpencil_reveal_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -1008,7 +1008,7 @@ void GPENCIL_OT_reveal(wmOperatorType *ot)
/* ***************** Lock/Unlock All Layers ************************ */
-static int gp_lock_all_exec(bContext *C, wmOperator *UNUSED(op))
+static int gpencil_lock_all_exec(bContext *C, wmOperator *UNUSED(op))
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
@@ -1038,8 +1038,8 @@ void GPENCIL_OT_lock_all(wmOperatorType *ot)
"Lock all Grease Pencil layers to prevent them from being accidentally modified";
/* callbacks */
- ot->exec = gp_lock_all_exec;
- ot->poll = gp_reveal_poll;
+ ot->exec = gpencil_lock_all_exec;
+ ot->poll = gpencil_reveal_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -1047,7 +1047,7 @@ void GPENCIL_OT_lock_all(wmOperatorType *ot)
/* -------------------------- */
-static int gp_unlock_all_exec(bContext *C, wmOperator *UNUSED(op))
+static int gpencil_unlock_all_exec(bContext *C, wmOperator *UNUSED(op))
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
@@ -1076,8 +1076,8 @@ void GPENCIL_OT_unlock_all(wmOperatorType *ot)
ot->description = "Unlock all Grease Pencil layers so that they can be edited";
/* callbacks */
- ot->exec = gp_unlock_all_exec;
- ot->poll = gp_reveal_poll;
+ ot->exec = gpencil_unlock_all_exec;
+ ot->poll = gpencil_reveal_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -1085,7 +1085,7 @@ void GPENCIL_OT_unlock_all(wmOperatorType *ot)
/* ********************** Isolate Layer **************************** */
-static int gp_isolate_layer_exec(bContext *C, wmOperator *op)
+static int gpencil_isolate_layer_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
bGPDlayer *layer = BKE_gpencil_layer_active_get(gpd);
@@ -1153,8 +1153,8 @@ void GPENCIL_OT_layer_isolate(wmOperatorType *ot)
"Toggle whether the active layer is the only one that can be edited and/or visible";
/* callbacks */
- ot->exec = gp_isolate_layer_exec;
- ot->poll = gp_active_layer_poll;
+ ot->exec = gpencil_isolate_layer_exec;
+ ot->poll = gpencil_active_layer_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -1169,7 +1169,7 @@ void GPENCIL_OT_layer_isolate(wmOperatorType *ot)
/* ********************** Merge Layer with the next layer **************************** */
-static int gp_merge_layer_exec(bContext *C, wmOperator *op)
+static int gpencil_merge_layer_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
bGPDlayer *gpl_next = BKE_gpencil_layer_active_get(gpd);
@@ -1242,8 +1242,8 @@ void GPENCIL_OT_layer_merge(wmOperatorType *ot)
ot->description = "Merge the current layer with the layer below";
/* callbacks */
- ot->exec = gp_merge_layer_exec;
- ot->poll = gp_active_layer_poll;
+ ot->exec = gpencil_merge_layer_exec;
+ ot->poll = gpencil_active_layer_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -1251,7 +1251,7 @@ void GPENCIL_OT_layer_merge(wmOperatorType *ot)
/* ********************** Change Layer ***************************** */
-static int gp_layer_change_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt))
+static int gpencil_layer_change_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt))
{
uiPopupMenu *pup;
uiLayout *layout;
@@ -1265,7 +1265,7 @@ static int gp_layer_change_invoke(bContext *C, wmOperator *op, const wmEvent *UN
return OPERATOR_INTERFACE;
}
-static int gp_layer_change_exec(bContext *C, wmOperator *op)
+static int gpencil_layer_change_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = CTX_data_gpencil_data(C);
bGPDlayer *gpl = NULL;
@@ -1306,9 +1306,9 @@ void GPENCIL_OT_layer_change(wmOperatorType *ot)
ot->description = "Change active Grease Pencil layer";
/* callbacks */
- ot->invoke = gp_layer_change_invoke;
- ot->exec = gp_layer_change_exec;
- ot->poll = gp_active_layer_poll;
+ ot->invoke = gpencil_layer_change_invoke;
+ ot->exec = gpencil_layer_change_exec;
+ ot->poll = gpencil_active_layer_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -1318,7 +1318,7 @@ void GPENCIL_OT_layer_change(wmOperatorType *ot)
RNA_def_enum_funcs(ot->prop, ED_gpencil_layers_with_new_enum_itemf);
}
-static int gp_layer_active_exec(bContext *C, wmOperator *op)
+static int gpencil_layer_active_exec(bContext *C, wmOperator *op)
{
Object *ob = CTX_data_active_object(C);
bGPdata *gpd = (bGPdata *)ob->data;
@@ -1352,8 +1352,8 @@ void GPENCIL_OT_layer_active(wmOperatorType *ot)
ot->description = "Active Grease Pencil layer";
/* callbacks */
- ot->exec = gp_layer_active_exec;
- ot->poll = gp_active_layer_poll;
+ ot->exec = gpencil_layer_active_exec;
+ ot->poll = gpencil_active_layer_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -1373,7 +1373,7 @@ enum {
GP_STROKE_MOVE_BOTTOM = 3,
};
-static int gp_stroke_arrange_exec(bContext *C, wmOperator *op)
+static int gpencil_stroke_arrange_exec(bContext *C, wmOperator *op)
{
Object *ob = CTX_data_active_object(C);
bGPdata *gpd = ED_gpencil_data_get_active(C);
@@ -1503,8 +1503,8 @@ void GPENCIL_OT_stroke_arrange(wmOperatorType *ot)
ot->description = "Arrange selected strokes up/down in the drawing order of the active layer";
/* callbacks */
- ot->exec = gp_stroke_arrange_exec;
- ot->poll = gp_active_layer_poll;
+ ot->exec = gpencil_stroke_arrange_exec;
+ ot->poll = gpencil_active_layer_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -1515,7 +1515,7 @@ void GPENCIL_OT_stroke_arrange(wmOperatorType *ot)
/* ******************* Move Stroke to new color ************************** */
-static int gp_stroke_change_color_exec(bContext *C, wmOperator *op)
+static int gpencil_stroke_change_color_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Material *ma = NULL;
@@ -1599,8 +1599,8 @@ void GPENCIL_OT_stroke_change_color(wmOperatorType *ot)
ot->description = "Move selected strokes to active material";
/* callbacks */
- ot->exec = gp_stroke_change_color_exec;
- ot->poll = gp_active_layer_poll;
+ ot->exec = gpencil_stroke_change_color_exec;
+ ot->poll = gpencil_active_layer_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -1610,7 +1610,7 @@ void GPENCIL_OT_stroke_change_color(wmOperatorType *ot)
/* ******************* Lock color of non selected Strokes colors ************************** */
-static int gp_material_lock_unsused_exec(bContext *C, wmOperator *UNUSED(op))
+static int gpencil_material_lock_unsused_exec(bContext *C, wmOperator *UNUSED(op))
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
@@ -1674,8 +1674,8 @@ void GPENCIL_OT_material_lock_unused(wmOperatorType *ot)
ot->description = "Lock any material not used in any selected stroke";
/* api callbacks */
- ot->exec = gp_material_lock_unsused_exec;
- ot->poll = gp_active_layer_poll;
+ ot->exec = gpencil_material_lock_unsused_exec;
+ ot->poll = gpencil_active_layer_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -1685,7 +1685,7 @@ void GPENCIL_OT_material_lock_unused(wmOperatorType *ot)
/* Drawing Brushes Operators */
/* ******************* Brush resets ************************** */
-static int gp_brush_reset_exec(bContext *C, wmOperator *UNUSED(op))
+static int gpencil_brush_reset_exec(bContext *C, wmOperator *UNUSED(op))
{
Main *bmain = CTX_data_main(C);
ToolSettings *ts = CTX_data_tool_settings(C);
@@ -1743,16 +1743,16 @@ void GPENCIL_OT_brush_reset(wmOperatorType *ot)
ot->description = "Reset Brush to default parameters";
/* api callbacks */
- ot->exec = gp_brush_reset_exec;
+ ot->exec = gpencil_brush_reset_exec;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-static Brush *gp_brush_get_first_by_mode(Main *bmain,
- Paint *UNUSED(paint),
- const enum eContextObjectMode mode,
- char tool)
+static Brush *gpencil_brush_get_first_by_mode(Main *bmain,
+ Paint *UNUSED(paint),
+ const enum eContextObjectMode mode,
+ char tool)
{
Brush *brush_next = NULL;
for (Brush *brush = bmain->brushes.first; brush; brush = brush_next) {
@@ -1782,9 +1782,9 @@ static Brush *gp_brush_get_first_by_mode(Main *bmain,
return NULL;
}
-static void gp_brush_delete_mode_brushes(Main *bmain,
- Paint *paint,
- const enum eContextObjectMode mode)
+static void gpencil_brush_delete_mode_brushes(Main *bmain,
+ Paint *paint,
+ const enum eContextObjectMode mode)
{
Brush *brush_active = paint->brush;
Brush *brush_next = NULL;
@@ -1847,7 +1847,7 @@ static void gp_brush_delete_mode_brushes(Main *bmain,
}
}
-static int gp_brush_reset_all_exec(bContext *C, wmOperator *UNUSED(op))
+static int gpencil_brush_reset_all_exec(bContext *C, wmOperator *UNUSED(op))
{
Main *bmain = CTX_data_main(C);
ToolSettings *ts = CTX_data_tool_settings(C);
@@ -1903,7 +1903,7 @@ static int gp_brush_reset_all_exec(bContext *C, wmOperator *UNUSED(op))
}
}
- gp_brush_delete_mode_brushes(bmain, paint, mode);
+ gpencil_brush_delete_mode_brushes(bmain, paint, mode);
switch (mode) {
case CTX_MODE_PAINT_GPENCIL: {
@@ -1930,7 +1930,7 @@ static int gp_brush_reset_all_exec(bContext *C, wmOperator *UNUSED(op))
BKE_paint_toolslots_brush_validate(bmain, paint);
/* Set Again the first brush of the mode. */
- Brush *deft_brush = gp_brush_get_first_by_mode(bmain, paint, mode, tool);
+ Brush *deft_brush = gpencil_brush_get_first_by_mode(bmain, paint, mode, tool);
if (deft_brush) {
BKE_paint_brush_set(paint, deft_brush);
}
@@ -1950,7 +1950,7 @@ void GPENCIL_OT_brush_reset_all(wmOperatorType *ot)
ot->description = "Delete all mode brushes and recreate a default set";
/* api callbacks */
- ot->exec = gp_brush_reset_all_exec;
+ ot->exec = gpencil_brush_reset_all_exec;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -2496,7 +2496,7 @@ typedef struct tJoinGPencil_AdtFixData {
* Callback to pass to #BKE_fcurves_main_cb()
* for RNA Paths attached to each F-Curve used in the #AnimData.
*/
-static void joined_gpencil_fix_animdata_cb(ID *id, FCurve *fcu, void *user_data)
+static void gpencil_joined_fix_animdata_cb(ID *id, FCurve *fcu, void *user_data)
{
tJoinGPencil_AdtFixData *afd = (tJoinGPencil_AdtFixData *)user_data;
ID *src_id = &afd->src_gpd->id;
@@ -2534,8 +2534,8 @@ static void joined_gpencil_fix_animdata_cb(ID *id, FCurve *fcu, void *user_data)
if (dtar->id == src_id) {
dtar->id = dst_id;
- /* Also check on the subtarget...
- * We duplicate the logic from drivers_path_rename_fix() here, with our own
+ /* Also check on the sub-target.
+ * We duplicate the logic from #drivers_path_rename_fix() here, with our own
* little twists so that we know that it isn't going to clobber the wrong data
*/
if (dtar->rna_path && strstr(dtar->rna_path, "layers[")) {
@@ -2721,7 +2721,7 @@ int ED_gpencil_join_objects_exec(bContext *C, wmOperator *op)
}
/* Fix all the animation data */
- BKE_fcurves_main_cb(bmain, joined_gpencil_fix_animdata_cb, &afd);
+ BKE_fcurves_main_cb(bmain, gpencil_joined_fix_animdata_cb, &afd);
BLI_ghash_free(afd.names_map, MEM_freeN, NULL);
/* Only copy over animdata now, after all the remapping has been done,
@@ -2856,7 +2856,7 @@ void GPENCIL_OT_lock_layer(wmOperatorType *ot)
/* api callbacks */
ot->exec = gpencil_lock_layer_exec;
- ot->poll = gp_active_layer_poll;
+ ot->poll = gpencil_active_layer_poll;
}
/* ********************** Isolate gpencil_ color **************************** */
@@ -3415,7 +3415,7 @@ bool ED_gpencil_add_lattice_modifier(const bContext *C,
}
/* Masking operators */
-static int gp_layer_mask_add_exec(bContext *C, wmOperator *op)
+static int gpencil_layer_mask_add_exec(bContext *C, wmOperator *op)
{
Object *ob = CTX_data_active_object(C);
if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
@@ -3476,14 +3476,14 @@ void GPENCIL_OT_layer_mask_add(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* callbacks */
- ot->exec = gp_layer_mask_add_exec;
- ot->poll = gp_add_poll;
+ ot->exec = gpencil_layer_mask_add_exec;
+ ot->poll = gpencil_add_poll;
/* properties */
RNA_def_string(ot->srna, "name", NULL, 128, "Layer", "Name of the layer");
}
-static int gp_layer_mask_remove_exec(bContext *C, wmOperator *UNUSED(op))
+static int gpencil_layer_mask_remove_exec(bContext *C, wmOperator *UNUSED(op))
{
Object *ob = CTX_data_active_object(C);
if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
@@ -3525,6 +3525,6 @@ void GPENCIL_OT_layer_mask_remove(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* callbacks */
- ot->exec = gp_layer_mask_remove_exec;
- ot->poll = gp_active_layer_poll;
+ ot->exec = gpencil_layer_mask_remove_exec;
+ ot->poll = gpencil_active_layer_poll;
}
diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c
index 76776b5c23c..78e2812fdef 100644
--- a/source/blender/editors/gpencil/gpencil_edit.c
+++ b/source/blender/editors/gpencil/gpencil_edit.c
@@ -95,7 +95,7 @@
* \{ */
/* poll callback for all stroke editing operators */
-static bool gp_stroke_edit_poll(bContext *C)
+static bool gpencil_stroke_edit_poll(bContext *C)
{
/* edit only supported with grease pencil objects */
Object *ob = CTX_data_active_object(C);
@@ -108,7 +108,7 @@ static bool gp_stroke_edit_poll(bContext *C)
}
/* poll callback to verify edit mode in 3D view only */
-static bool gp_strokes_edit3d_poll(bContext *C)
+static bool gpencil_strokes_edit3d_poll(bContext *C)
{
/* edit only supported with grease pencil objects */
Object *ob = CTX_data_active_object(C);
@@ -120,7 +120,7 @@ static bool gp_strokes_edit3d_poll(bContext *C)
* - 1) Editable GP data
* - 2) 3D View only
*/
- return (gp_stroke_edit_poll(C) && ED_operator_view3d_active(C));
+ return (gpencil_stroke_edit_poll(C) && ED_operator_view3d_active(C));
}
static bool gpencil_editmode_toggle_poll(bContext *C)
@@ -789,7 +789,7 @@ void GPENCIL_OT_selection_opacity_toggle(wmOperatorType *ot)
/* callbacks */
ot->exec = gpencil_hideselect_toggle_exec;
- ot->poll = gp_stroke_edit_poll;
+ ot->poll = gpencil_stroke_edit_poll;
/* flags */
ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
@@ -802,9 +802,9 @@ void GPENCIL_OT_selection_opacity_toggle(wmOperatorType *ot)
* \{ */
/* Make copies of selected point segments in a selected stroke */
-static void gp_duplicate_points(const bGPDstroke *gps,
- ListBase *new_strokes,
- const char *layername)
+static void gpencil_duplicate_points(const bGPDstroke *gps,
+ ListBase *new_strokes,
+ const char *layername)
{
bGPDspoint *pt;
int i;
@@ -880,7 +880,7 @@ static void gp_duplicate_points(const bGPDstroke *gps,
}
}
-static int gp_duplicate_exec(bContext *C, wmOperator *op)
+static int gpencil_duplicate_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
@@ -933,7 +933,7 @@ static int gp_duplicate_exec(bContext *C, wmOperator *op)
}
else {
/* delegate to a helper, as there's too much to fit in here (for copying subsets)... */
- gp_duplicate_points(gps, &new_strokes, gpl->info);
+ gpencil_duplicate_points(gps, &new_strokes, gpl->info);
}
/* deselect original stroke, or else the originals get moved too
@@ -969,8 +969,8 @@ void GPENCIL_OT_duplicate(wmOperatorType *ot)
ot->description = "Duplicate the selected Grease Pencil strokes";
/* callbacks */
- ot->exec = gp_duplicate_exec;
- ot->poll = gp_stroke_edit_poll;
+ ot->exec = gpencil_duplicate_exec;
+ ot->poll = gpencil_stroke_edit_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -983,12 +983,12 @@ void GPENCIL_OT_duplicate(wmOperatorType *ot)
* \{ */
/* helper to copy a point to temp area */
-static void copy_move_point(bGPDstroke *gps,
- bGPDspoint *temp_points,
- MDeformVert *temp_dverts,
- int from_idx,
- int to_idx,
- const bool copy)
+static void gpencil_copy_move_point(bGPDstroke *gps,
+ bGPDspoint *temp_points,
+ MDeformVert *temp_dverts,
+ int from_idx,
+ int to_idx,
+ const bool copy)
{
bGPDspoint *pt = &temp_points[from_idx];
bGPDspoint *pt_final = &gps->points[to_idx];
@@ -1053,7 +1053,7 @@ static void gpencil_add_move_points(bGPDframe *gpf, bGPDstroke *gps)
BLI_insertlinkafter(&gpf->strokes, gps, gps_new);
/* copy selected point data to new stroke */
- copy_move_point(gps_new, gps->points, gps->dvert, i, 0, true);
+ gpencil_copy_move_point(gps_new, gps->points, gps->dvert, i, 0, true);
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(gps);
@@ -1098,13 +1098,13 @@ static void gpencil_add_move_points(bGPDframe *gpf, bGPDstroke *gps)
/* move points to new position */
for (int i = 0; i < oldtotpoints; i++) {
- copy_move_point(gps, temp_points, temp_dverts, i, i2, false);
+ gpencil_copy_move_point(gps, temp_points, temp_dverts, i, i2, false);
i2++;
}
/* If first point, add new point at the beginning. */
if (do_first) {
- copy_move_point(gps, temp_points, temp_dverts, 0, 0, true);
+ gpencil_copy_move_point(gps, temp_points, temp_dverts, 0, 0, true);
/* deselect old */
pt = &gps->points[1];
pt->flag &= ~GP_SPOINT_SELECT;
@@ -1115,7 +1115,8 @@ static void gpencil_add_move_points(bGPDframe *gpf, bGPDstroke *gps)
/* if last point, add new point at the end */
if (do_last) {
- copy_move_point(gps, temp_points, temp_dverts, oldtotpoints - 1, gps->totpoints - 1, true);
+ gpencil_copy_move_point(
+ gps, temp_points, temp_dverts, oldtotpoints - 1, gps->totpoints - 1, true);
/* deselect old */
pt = &gps->points[gps->totpoints - 2];
@@ -1138,7 +1139,7 @@ static void gpencil_add_move_points(bGPDframe *gpf, bGPDstroke *gps)
}
}
-static int gp_extrude_exec(bContext *C, wmOperator *op)
+static int gpencil_extrude_exec(bContext *C, wmOperator *op)
{
Object *obact = CTX_data_active_object(C);
bGPdata *gpd = (bGPdata *)obact->data;
@@ -1194,8 +1195,8 @@ void GPENCIL_OT_extrude(wmOperatorType *ot)
ot->description = "Extrude the selected Grease Pencil points";
/* callbacks */
- ot->exec = gp_extrude_exec;
- ot->poll = gp_stroke_edit_poll;
+ ot->exec = gpencil_extrude_exec;
+ ot->poll = gpencil_stroke_edit_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -1216,16 +1217,16 @@ void GPENCIL_OT_extrude(wmOperatorType *ot)
/* list of bGPDstroke instances */
/* NOTE: is exposed within the editors/gpencil module so that other tools can use it too */
-ListBase gp_strokes_copypastebuf = {NULL, NULL};
+ListBase gpencil_strokes_copypastebuf = {NULL, NULL};
/* Hash for hanging on to all the colors used by strokes in the buffer
*
* This is needed to prevent dangling and unsafe pointers when pasting across data-blocks,
* or after a color used by a stroke in the buffer gets deleted (via user action or undo).
*/
-static GHash *gp_strokes_copypastebuf_colors = NULL;
+static GHash *gpencil_strokes_copypastebuf_colors = NULL;
-static GHash *gp_strokes_copypastebuf_colors_material_to_name_create(Main *bmain)
+static GHash *gpencil_strokes_copypastebuf_colors_material_to_name_create(Main *bmain)
{
GHash *ma_to_name = BLI_ghash_ptr_new(__func__);
@@ -1237,12 +1238,12 @@ static GHash *gp_strokes_copypastebuf_colors_material_to_name_create(Main *bmain
return ma_to_name;
}
-static void gp_strokes_copypastebuf_colors_material_to_name_free(GHash *ma_to_name)
+static void gpencil_strokes_copypastebuf_colors_material_to_name_free(GHash *ma_to_name)
{
BLI_ghash_free(ma_to_name, NULL, MEM_freeN);
}
-static GHash *gp_strokes_copypastebuf_colors_name_to_material_create(Main *bmain)
+static GHash *gpencil_strokes_copypastebuf_colors_name_to_material_create(Main *bmain)
{
GHash *name_to_ma = BLI_ghash_str_new(__func__);
@@ -1254,7 +1255,7 @@ static GHash *gp_strokes_copypastebuf_colors_name_to_material_create(Main *bmain
return name_to_ma;
}
-static void gp_strokes_copypastebuf_colors_name_to_material_free(GHash *name_to_ma)
+static void gpencil_strokes_copypastebuf_colors_name_to_material_free(GHash *name_to_ma)
{
BLI_ghash_free(name_to_ma, MEM_freeN, NULL);
}
@@ -1267,13 +1268,13 @@ void ED_gpencil_strokes_copybuf_free(void)
/* Free the colors buffer
* NOTE: This is done before the strokes so that the ptrs are still safe
*/
- if (gp_strokes_copypastebuf_colors) {
- BLI_ghash_free(gp_strokes_copypastebuf_colors, NULL, MEM_freeN);
- gp_strokes_copypastebuf_colors = NULL;
+ if (gpencil_strokes_copypastebuf_colors) {
+ BLI_ghash_free(gpencil_strokes_copypastebuf_colors, NULL, MEM_freeN);
+ gpencil_strokes_copypastebuf_colors = NULL;
}
/* Free the stroke buffer */
- for (gps = gp_strokes_copypastebuf.first; gps; gps = gpsn) {
+ for (gps = gpencil_strokes_copypastebuf.first; gps; gps = gpsn) {
gpsn = gps->next;
if (gps->points) {
@@ -1286,17 +1287,17 @@ void ED_gpencil_strokes_copybuf_free(void)
MEM_SAFE_FREE(gps->triangles);
- BLI_freelinkN(&gp_strokes_copypastebuf, gps);
+ BLI_freelinkN(&gpencil_strokes_copypastebuf, gps);
}
- gp_strokes_copypastebuf.first = gp_strokes_copypastebuf.last = NULL;
+ gpencil_strokes_copypastebuf.first = gpencil_strokes_copypastebuf.last = NULL;
}
/**
* Ensure that destination datablock has all the colors the pasted strokes need.
* Helper function for copy-pasting strokes
*/
-GHash *gp_copybuf_validate_colormap(bContext *C)
+GHash *gpencil_copybuf_validate_colormap(bContext *C)
{
Main *bmain = CTX_data_main(C);
Object *ob = CTX_data_active_object(C);
@@ -1304,9 +1305,9 @@ GHash *gp_copybuf_validate_colormap(bContext *C)
GHashIterator gh_iter;
/* For each color, check if exist and add if not */
- GHash *name_to_ma = gp_strokes_copypastebuf_colors_name_to_material_create(bmain);
+ GHash *name_to_ma = gpencil_strokes_copypastebuf_colors_name_to_material_create(bmain);
- GHASH_ITER (gh_iter, gp_strokes_copypastebuf_colors) {
+ GHASH_ITER (gh_iter, gpencil_strokes_copypastebuf_colors) {
int *key = BLI_ghashIterator_getKey(&gh_iter);
char *ma_name = BLI_ghashIterator_getValue(&gh_iter);
Material *ma = BLI_ghash_lookup(name_to_ma, ma_name);
@@ -1319,7 +1320,7 @@ GHash *gp_copybuf_validate_colormap(bContext *C)
}
}
- gp_strokes_copypastebuf_colors_name_to_material_free(name_to_ma);
+ gpencil_strokes_copypastebuf_colors_name_to_material_free(name_to_ma);
return new_colors;
}
@@ -1330,7 +1331,7 @@ GHash *gp_copybuf_validate_colormap(bContext *C)
/** \name Copy Selected Strokes Operator
* \{ */
-static int gp_strokes_copy_exec(bContext *C, wmOperator *op)
+static int gpencil_strokes_copy_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Object *ob = CTX_data_active_object(C);
@@ -1389,11 +1390,11 @@ static int gp_strokes_copy_exec(bContext *C, wmOperator *op)
/* add to temp buffer */
gpsd->next = gpsd->prev = NULL;
- BLI_addtail(&gp_strokes_copypastebuf, gpsd);
+ BLI_addtail(&gpencil_strokes_copypastebuf, gpsd);
}
else {
/* delegate to a helper, as there's too much to fit in here (for copying subsets)... */
- gp_duplicate_points(gps, &gp_strokes_copypastebuf, gpl->info);
+ gpencil_duplicate_points(gps, &gpencil_strokes_copypastebuf, gpl->info);
}
}
}
@@ -1401,10 +1402,10 @@ static int gp_strokes_copy_exec(bContext *C, wmOperator *op)
CTX_DATA_END;
/* Build up hash of material colors used in these strokes */
- if (gp_strokes_copypastebuf.first) {
- gp_strokes_copypastebuf_colors = BLI_ghash_int_new("GPencil CopyBuf Colors");
- GHash *ma_to_name = gp_strokes_copypastebuf_colors_material_to_name_create(bmain);
- LISTBASE_FOREACH (bGPDstroke *, gps, &gp_strokes_copypastebuf) {
+ if (gpencil_strokes_copypastebuf.first) {
+ gpencil_strokes_copypastebuf_colors = BLI_ghash_int_new("GPencil CopyBuf Colors");
+ GHash *ma_to_name = gpencil_strokes_copypastebuf_colors_material_to_name_create(bmain);
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpencil_strokes_copypastebuf) {
if (ED_gpencil_stroke_can_use(C, gps)) {
Material *ma = BKE_object_material_get(ob, gps->mat_nr + 1);
/* Avoid default material. */
@@ -1414,13 +1415,13 @@ static int gp_strokes_copy_exec(bContext *C, wmOperator *op)
char **ma_name_val;
if (!BLI_ghash_ensure_p(
- gp_strokes_copypastebuf_colors, &gps->mat_nr, (void ***)&ma_name_val)) {
+ gpencil_strokes_copypastebuf_colors, &gps->mat_nr, (void ***)&ma_name_val)) {
char *ma_name = BLI_ghash_lookup(ma_to_name, ma);
*ma_name_val = MEM_dupallocN(ma_name);
}
}
}
- gp_strokes_copypastebuf_colors_material_to_name_free(ma_to_name);
+ gpencil_strokes_copypastebuf_colors_material_to_name_free(ma_to_name);
}
/* updates (to ensure operator buttons are refreshed, when used via hotkeys) */
@@ -1438,8 +1439,8 @@ void GPENCIL_OT_copy(wmOperatorType *ot)
ot->description = "Copy selected Grease Pencil points and strokes";
/* callbacks */
- ot->exec = gp_strokes_copy_exec;
- ot->poll = gp_stroke_edit_poll;
+ ot->exec = gpencil_strokes_copy_exec;
+ ot->poll = gpencil_stroke_edit_poll;
/* flags */
// ot->flag = OPTYPE_REGISTER;
@@ -1451,7 +1452,7 @@ void GPENCIL_OT_copy(wmOperatorType *ot)
/** \name Paste Selected Strokes Operator
* \{ */
-static bool gp_strokes_paste_poll(bContext *C)
+static bool gpencil_strokes_paste_poll(bContext *C)
{
ScrArea *area = CTX_wm_area(C);
if (!((area != NULL) && (area->spacetype == SPACE_VIEW3D))) {
@@ -1464,7 +1465,7 @@ static bool gp_strokes_paste_poll(bContext *C)
* 2) Copy buffer must at least have something (though it may be the wrong sort...).
*/
return (ED_gpencil_data_get_active(C) != NULL) &&
- (!BLI_listbase_is_empty(&gp_strokes_copypastebuf));
+ (!BLI_listbase_is_empty(&gpencil_strokes_copypastebuf));
}
typedef enum eGP_PasteMode {
@@ -1472,7 +1473,7 @@ typedef enum eGP_PasteMode {
GP_COPY_TO_ACTIVE = 1,
} eGP_PasteMode;
-static int gp_strokes_paste_exec(bContext *C, wmOperator *op)
+static int gpencil_strokes_paste_exec(bContext *C, wmOperator *op)
{
Object *ob = CTX_data_active_object(C);
bGPdata *gpd = (bGPdata *)ob->data;
@@ -1489,7 +1490,7 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op)
BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition");
return OPERATOR_CANCELLED;
}
- else if (BLI_listbase_is_empty(&gp_strokes_copypastebuf)) {
+ else if (BLI_listbase_is_empty(&gpencil_strokes_copypastebuf)) {
BKE_report(op->reports,
RPT_ERROR,
"No strokes to paste, select and copy some points before trying again");
@@ -1509,7 +1510,7 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op)
bGPDstroke *gps;
bool ok = false;
- for (gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) {
+ for (gps = gpencil_strokes_copypastebuf.first; gps; gps = gps->next) {
if (ED_gpencil_stroke_can_use(C, gps)) {
ok = true;
break;
@@ -1535,10 +1536,11 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op)
CTX_DATA_END;
/* Ensure that all the necessary colors exist */
- new_colors = gp_copybuf_validate_colormap(C);
+ new_colors = gpencil_copybuf_validate_colormap(C);
/* Copy over the strokes from the buffer (and adjust the colors) */
- bGPDstroke *gps_init = (!on_back) ? gp_strokes_copypastebuf.first : gp_strokes_copypastebuf.last;
+ bGPDstroke *gps_init = (!on_back) ? gpencil_strokes_copypastebuf.first :
+ gpencil_strokes_copypastebuf.last;
for (bGPDstroke *gps = gps_init; gps; gps = (!on_back) ? gps->next : gps->prev) {
if (ED_gpencil_stroke_can_use(C, gps)) {
/* Need to verify if layer exists */
@@ -1606,8 +1608,8 @@ void GPENCIL_OT_paste(wmOperatorType *ot)
ot->description = "Paste previously copied strokes to active layer or to original layer";
/* callbacks */
- ot->exec = gp_strokes_paste_exec;
- ot->poll = gp_strokes_paste_poll;
+ ot->exec = gpencil_strokes_paste_exec;
+ ot->poll = gpencil_strokes_paste_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -1626,7 +1628,7 @@ void GPENCIL_OT_paste(wmOperatorType *ot)
/** \name Move To Layer Operator
* \{ */
-static int gp_move_to_layer_exec(bContext *C, wmOperator *op)
+static int gpencil_move_to_layer_exec(bContext *C, wmOperator *op)
{
Object *ob = CTX_data_active_object(C);
bGPdata *gpd = (bGPdata *)ob->data;
@@ -1732,8 +1734,8 @@ void GPENCIL_OT_move_to_layer(wmOperatorType *ot)
"Move selected strokes to another layer"; // XXX: allow moving individual points too?
/* callbacks */
- ot->exec = gp_move_to_layer_exec;
- ot->poll = gp_stroke_edit_poll; // XXX?
+ ot->exec = gpencil_move_to_layer_exec;
+ ot->poll = gpencil_stroke_edit_poll; // XXX?
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -1750,7 +1752,7 @@ void GPENCIL_OT_move_to_layer(wmOperatorType *ot)
/** \name Add Blank Frame Operator
* \{ */
-static int gp_blank_frame_add_exec(bContext *C, wmOperator *op)
+static int gpencil_blank_frame_add_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
Scene *scene = CTX_data_scene(C);
@@ -1808,8 +1810,8 @@ void GPENCIL_OT_blank_frame_add(wmOperatorType *ot)
"(all subsequently existing frames, if any, are shifted right by one frame)";
/* callbacks */
- ot->exec = gp_blank_frame_add_exec;
- ot->poll = gp_add_poll;
+ ot->exec = gpencil_blank_frame_add_exec;
+ ot->poll = gpencil_add_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -1828,7 +1830,7 @@ void GPENCIL_OT_blank_frame_add(wmOperatorType *ot)
/** \name Delete Active Frame Operator
* \{ */
-static bool gp_actframe_delete_poll(bContext *C)
+static bool gpencil_actframe_delete_poll(bContext *C)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd);
@@ -1837,7 +1839,7 @@ static bool gp_actframe_delete_poll(bContext *C)
return (gpl && gpl->actframe);
}
-static bool gp_annotation_actframe_delete_poll(bContext *C)
+static bool annotation_actframe_delete_poll(bContext *C)
{
bGPdata *gpd = ED_annotation_data_get_active(C);
bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd);
@@ -1847,7 +1849,7 @@ static bool gp_annotation_actframe_delete_poll(bContext *C)
}
/* delete active frame - wrapper around API calls */
-static int gp_actframe_delete_exec(bContext *C, wmOperator *op)
+static int gpencil_actframe_delete_exec(bContext *C, wmOperator *op)
{
const bool is_annotation = STREQ(op->idname, "GPENCIL_OT_annotation_active_frame_delete");
@@ -1890,8 +1892,8 @@ void GPENCIL_OT_active_frame_delete(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* callbacks */
- ot->exec = gp_actframe_delete_exec;
- ot->poll = gp_actframe_delete_poll;
+ ot->exec = gpencil_actframe_delete_exec;
+ ot->poll = gpencil_actframe_delete_poll;
}
void GPENCIL_OT_annotation_active_frame_delete(wmOperatorType *ot)
@@ -1904,8 +1906,8 @@ void GPENCIL_OT_annotation_active_frame_delete(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* callbacks */
- ot->exec = gp_actframe_delete_exec;
- ot->poll = gp_annotation_actframe_delete_poll;
+ ot->exec = gpencil_actframe_delete_exec;
+ ot->poll = annotation_actframe_delete_poll;
}
/** \} */
@@ -1914,7 +1916,7 @@ void GPENCIL_OT_annotation_active_frame_delete(wmOperatorType *ot)
/** \name Delete All Active Frames
* \{ */
-static bool gp_actframe_delete_all_poll(bContext *C)
+static bool gpencil_actframe_delete_all_poll(bContext *C)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
@@ -1924,7 +1926,7 @@ static bool gp_actframe_delete_all_poll(bContext *C)
return (gpd && gpd->layers.first);
}
-static int gp_actframe_delete_all_exec(bContext *C, wmOperator *op)
+static int gpencil_actframe_delete_all_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
Scene *scene = CTX_data_scene(C);
@@ -1969,8 +1971,8 @@ void GPENCIL_OT_active_frames_delete_all(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* callbacks */
- ot->exec = gp_actframe_delete_all_exec;
- ot->poll = gp_actframe_delete_all_poll;
+ ot->exec = gpencil_actframe_delete_all_exec;
+ ot->poll = gpencil_actframe_delete_all_poll;
}
/** \} */
@@ -1998,7 +2000,7 @@ typedef enum eGP_DissolveMode {
} eGP_DissolveMode;
/* Delete selected strokes */
-static int gp_delete_selected_strokes(bContext *C)
+static int gpencil_delete_selected_strokes(bContext *C)
{
bool changed = false;
bGPdata *gpd = ED_gpencil_data_get_active(C);
@@ -2056,7 +2058,7 @@ static int gp_delete_selected_strokes(bContext *C)
/* ----------------------------------- */
/* Delete selected points but keep the stroke */
-static int gp_dissolve_selected_points(bContext *C, eGP_DissolveMode mode)
+static int gpencil_dissolve_selected_points(bContext *C, eGP_DissolveMode mode)
{
Object *ob = CTX_data_active_object(C);
bGPdata *gpd = (bGPdata *)ob->data;
@@ -2282,7 +2284,9 @@ typedef struct tGPDeleteIsland {
int end_idx;
} tGPDeleteIsland;
-static void gp_stroke_join_islands(bGPDframe *gpf, bGPDstroke *gps_first, bGPDstroke *gps_last)
+static void gpencil_stroke_join_islands(bGPDframe *gpf,
+ bGPDstroke *gps_first,
+ bGPDstroke *gps_last)
{
bGPDspoint *pt = NULL;
bGPDspoint *pt_final = NULL;
@@ -2382,12 +2386,12 @@ static void gp_stroke_join_islands(bGPDframe *gpf, bGPDstroke *gps_first, bGPDst
* 2) Each island gets converted to a new stroke
* If the number of points is <= limit, the stroke is deleted
*/
-void gp_stroke_delete_tagged_points(bGPDframe *gpf,
- bGPDstroke *gps,
- bGPDstroke *next_stroke,
- int tag_flags,
- bool select,
- int limit)
+void gpencil_stroke_delete_tagged_points(bGPDframe *gpf,
+ bGPDstroke *gps,
+ bGPDstroke *next_stroke,
+ int tag_flags,
+ bool select,
+ int limit)
{
tGPDeleteIsland *islands = MEM_callocN(sizeof(tGPDeleteIsland) * (gps->totpoints + 1) / 2,
"gp_point_islands");
@@ -2517,7 +2521,7 @@ void gp_stroke_delete_tagged_points(bGPDframe *gpf,
}
/* if cyclic, need to join last stroke with first stroke */
if ((is_cyclic) && (gps_first != NULL) && (gps_first != new_stroke)) {
- gp_stroke_join_islands(gpf, gps_first, new_stroke);
+ gpencil_stroke_join_islands(gpf, gps_first, new_stroke);
}
}
@@ -2530,7 +2534,7 @@ void gp_stroke_delete_tagged_points(bGPDframe *gpf,
}
/* Split selected strokes into segments, splitting on selected points */
-static int gp_delete_selected_points(bContext *C)
+static int gpencil_delete_selected_points(bContext *C)
{
Object *ob = CTX_data_active_object(C);
bGPdata *gpd = ED_gpencil_data_get_active(C);
@@ -2564,7 +2568,7 @@ static int gp_delete_selected_points(bContext *C)
gps->flag &= ~GP_STROKE_SELECT;
/* delete unwanted points by splitting stroke into several smaller ones */
- gp_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_SELECT, false, 0);
+ gpencil_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_SELECT, false, 0);
changed = true;
}
@@ -2585,9 +2589,9 @@ static int gp_delete_selected_points(bContext *C)
}
/* simple wrapper to external call */
-int gp_delete_selected_point_wrap(bContext *C)
+int gpencil_delete_selected_point_wrap(bContext *C)
{
- return gp_delete_selected_points(C);
+ return gpencil_delete_selected_points(C);
}
/** \} */
@@ -2596,22 +2600,22 @@ int gp_delete_selected_point_wrap(bContext *C)
/** \name Delete Operator
* \{ */
-static int gp_delete_exec(bContext *C, wmOperator *op)
+static int gpencil_delete_exec(bContext *C, wmOperator *op)
{
eGP_DeleteMode mode = RNA_enum_get(op->ptr, "type");
int result = OPERATOR_CANCELLED;
switch (mode) {
case GP_DELETEOP_STROKES: /* selected strokes */
- result = gp_delete_selected_strokes(C);
+ result = gpencil_delete_selected_strokes(C);
break;
case GP_DELETEOP_POINTS: /* selected points (breaks the stroke into segments) */
- result = gp_delete_selected_points(C);
+ result = gpencil_delete_selected_points(C);
break;
case GP_DELETEOP_FRAME: /* active frame */
- result = gp_actframe_delete_exec(C, op);
+ result = gpencil_actframe_delete_exec(C, op);
break;
}
@@ -2638,8 +2642,8 @@ void GPENCIL_OT_delete(wmOperatorType *ot)
/* callbacks */
ot->invoke = WM_menu_invoke;
- ot->exec = gp_delete_exec;
- ot->poll = gp_stroke_edit_poll;
+ ot->exec = gpencil_delete_exec;
+ ot->poll = gpencil_stroke_edit_poll;
/* flags */
ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
@@ -2659,11 +2663,11 @@ void GPENCIL_OT_delete(wmOperatorType *ot)
/** \name Dissolve Operator
* \{ */
-static int gp_dissolve_exec(bContext *C, wmOperator *op)
+static int gpencil_dissolve_exec(bContext *C, wmOperator *op)
{
eGP_DissolveMode mode = RNA_enum_get(op->ptr, "type");
- return gp_dissolve_selected_points(C, mode);
+ return gpencil_dissolve_selected_points(C, mode);
}
void GPENCIL_OT_dissolve(wmOperatorType *ot)
@@ -2686,8 +2690,8 @@ void GPENCIL_OT_dissolve(wmOperatorType *ot)
/* callbacks */
ot->invoke = WM_menu_invoke;
- ot->exec = gp_dissolve_exec;
- ot->poll = gp_stroke_edit_poll;
+ ot->exec = gpencil_dissolve_exec;
+ ot->poll = gpencil_stroke_edit_poll;
/* flags */
ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
@@ -2711,7 +2715,7 @@ void GPENCIL_OT_dissolve(wmOperatorType *ot)
/* NOTE: For now, we only allow these in the 3D view, as other editors do not
* define a cursor or gridstep which can be used
*/
-static bool gp_snap_poll(bContext *C)
+static bool gpencil_snap_poll(bContext *C)
{
ScrArea *area = CTX_wm_area(C);
Object *ob = CTX_data_active_object(C);
@@ -2720,7 +2724,7 @@ static bool gp_snap_poll(bContext *C)
((area != NULL) && (area->spacetype == SPACE_VIEW3D));
}
-static int gp_snap_to_grid(bContext *C, wmOperator *UNUSED(op))
+static int gpencil_snap_to_grid(bContext *C, wmOperator *UNUSED(op))
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
RegionView3D *rv3d = CTX_wm_region_data(C);
@@ -2766,7 +2770,7 @@ static int gp_snap_to_grid(bContext *C, wmOperator *UNUSED(op))
/* return data */
copy_v3_v3(&pt->x, fpt);
- gp_apply_parent_point(depsgraph, obact, gpl, pt);
+ gpencil_apply_parent_point(depsgraph, obact, gpl, pt);
}
}
}
@@ -2787,8 +2791,8 @@ void GPENCIL_OT_snap_to_grid(wmOperatorType *ot)
ot->description = "Snap selected points to the nearest grid points";
/* callbacks */
- ot->exec = gp_snap_to_grid;
- ot->poll = gp_snap_poll;
+ ot->exec = gpencil_snap_to_grid;
+ ot->poll = gpencil_snap_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -2800,7 +2804,7 @@ void GPENCIL_OT_snap_to_grid(wmOperatorType *ot)
/** \name Snapping Selection to Cursor Operator
* \{ */
-static int gp_snap_to_cursor(bContext *C, wmOperator *op)
+static int gpencil_snap_to_cursor(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
@@ -2854,7 +2858,7 @@ static int gp_snap_to_cursor(bContext *C, wmOperator *op)
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
if (pt->flag & GP_SPOINT_SELECT) {
copy_v3_v3(&pt->x, cursor_global);
- gp_apply_parent_point(depsgraph, obact, gpl, pt);
+ gpencil_apply_parent_point(depsgraph, obact, gpl, pt);
}
}
}
@@ -2876,8 +2880,8 @@ void GPENCIL_OT_snap_to_cursor(wmOperatorType *ot)
ot->description = "Snap selected points/strokes to the cursor";
/* callbacks */
- ot->exec = gp_snap_to_cursor;
- ot->poll = gp_snap_poll;
+ ot->exec = gpencil_snap_to_cursor;
+ ot->poll = gpencil_snap_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -2896,7 +2900,7 @@ void GPENCIL_OT_snap_to_cursor(wmOperatorType *ot)
/** \name Snapping Cursor to Selection Operator
* \{ */
-static int gp_snap_cursor_to_sel(bContext *C, wmOperator *UNUSED(op))
+static int gpencil_snap_cursor_to_sel(bContext *C, wmOperator *UNUSED(op))
{
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Object *obact = CTX_data_active_object(C);
@@ -2956,13 +2960,16 @@ static int gp_snap_cursor_to_sel(bContext *C, wmOperator *UNUSED(op))
}
}
- if (scene->toolsettings->transform_pivot_point == V3D_AROUND_CENTER_MEDIAN && count) {
- mul_v3_fl(centroid, 1.0f / (float)count);
- copy_v3_v3(cursor, centroid);
- }
- else {
+ if (scene->toolsettings->transform_pivot_point == V3D_AROUND_CENTER_BOUNDS) {
mid_v3_v3v3(cursor, min, max);
}
+ else { /* #V3D_AROUND_CENTER_MEDIAN. */
+ zero_v3(cursor);
+ if (count) {
+ mul_v3_fl(centroid, 1.0f / (float)count);
+ copy_v3_v3(cursor, centroid);
+ }
+ }
DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d);
@@ -2978,8 +2985,8 @@ void GPENCIL_OT_snap_cursor_to_selected(wmOperatorType *ot)
ot->description = "Snap cursor to center of selected points";
/* callbacks */
- ot->exec = gp_snap_cursor_to_sel;
- ot->poll = gp_snap_poll;
+ ot->exec = gpencil_snap_cursor_to_sel;
+ ot->poll = gpencil_snap_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -2991,7 +2998,7 @@ void GPENCIL_OT_snap_cursor_to_selected(wmOperatorType *ot)
/** \name Apply Layer Thickness Change to Strokes Operator
* \{ */
-static int gp_stroke_apply_thickness_exec(bContext *C, wmOperator *UNUSED(op))
+static int gpencil_stroke_apply_thickness_exec(bContext *C, wmOperator *UNUSED(op))
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd);
@@ -3032,8 +3039,8 @@ void GPENCIL_OT_stroke_apply_thickness(wmOperatorType *ot)
ot->description = "Apply the thickness change of the layer to its strokes";
/* api callbacks */
- ot->exec = gp_stroke_apply_thickness_exec;
- ot->poll = gp_active_layer_poll;
+ ot->exec = gpencil_stroke_apply_thickness_exec;
+ ot->poll = gpencil_active_layer_poll;
}
/** \} */
@@ -3048,7 +3055,7 @@ enum {
GP_STROKE_CYCLIC_TOGGLE = 3,
};
-static int gp_stroke_cyclical_set_exec(bContext *C, wmOperator *op)
+static int gpencil_stroke_cyclical_set_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
Object *ob = CTX_data_active_object(C);
@@ -3147,8 +3154,8 @@ void GPENCIL_OT_stroke_cyclical_set(wmOperatorType *ot)
ot->description = "Close or open the selected stroke adding an edge from last to first point";
/* api callbacks */
- ot->exec = gp_stroke_cyclical_set_exec;
- ot->poll = gp_active_layer_poll;
+ ot->exec = gpencil_stroke_cyclical_set_exec;
+ ot->poll = gpencil_active_layer_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -3173,7 +3180,7 @@ enum {
GP_STROKE_CAPS_TOGGLE_DEFAULT = 3,
};
-static int gp_stroke_caps_set_exec(bContext *C, wmOperator *op)
+static int gpencil_stroke_caps_set_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
Object *ob = CTX_data_active_object(C);
@@ -3250,8 +3257,8 @@ void GPENCIL_OT_stroke_caps_set(wmOperatorType *ot)
ot->description = "Change Stroke caps mode (rounded or flat)";
/* api callbacks */
- ot->exec = gp_stroke_caps_set_exec;
- ot->poll = gp_active_layer_poll;
+ ot->exec = gpencil_stroke_caps_set_exec;
+ ot->poll = gpencil_active_layer_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -3422,7 +3429,7 @@ static void gpencil_stroke_join_strokes(bGPDstroke *gps_a,
}
}
-static int gp_stroke_join_exec(bContext *C, wmOperator *op)
+static int gpencil_stroke_join_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
bGPDlayer *activegpl = BKE_gpencil_layer_active_get(gpd);
@@ -3549,8 +3556,8 @@ void GPENCIL_OT_stroke_join(wmOperatorType *ot)
ot->description = "Join selected strokes (optionally as new stroke)";
/* api callbacks */
- ot->exec = gp_stroke_join_exec;
- ot->poll = gp_active_layer_poll;
+ ot->exec = gpencil_stroke_join_exec;
+ ot->poll = gpencil_active_layer_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -3570,7 +3577,7 @@ void GPENCIL_OT_stroke_join(wmOperatorType *ot)
/** \name Stroke Flip Operator
* \{ */
-static int gp_stroke_flip_exec(bContext *C, wmOperator *UNUSED(op))
+static int gpencil_stroke_flip_exec(bContext *C, wmOperator *UNUSED(op))
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
Object *ob = CTX_data_active_object(C);
@@ -3620,8 +3627,8 @@ void GPENCIL_OT_stroke_flip(wmOperatorType *ot)
ot->description = "Change direction of the points of the selected strokes";
/* api callbacks */
- ot->exec = gp_stroke_flip_exec;
- ot->poll = gp_active_layer_poll;
+ ot->exec = gpencil_stroke_flip_exec;
+ ot->poll = gpencil_active_layer_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -3633,7 +3640,7 @@ void GPENCIL_OT_stroke_flip(wmOperatorType *ot)
/** \name Stroke Re-project Operator
* \{ */
-static int gp_strokes_reproject_exec(bContext *C, wmOperator *op)
+static int gpencil_strokes_reproject_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
Scene *scene = CTX_data_scene(C);
@@ -3647,7 +3654,7 @@ static int gp_strokes_reproject_exec(bContext *C, wmOperator *op)
/* Init space conversion stuff. */
GP_SpaceConversion gsc = {NULL};
SnapObjectContext *sctx = NULL;
- gp_point_conversion_init(C, &gsc);
+ gpencil_point_conversion_init(C, &gsc);
/* Init snap context for geometry projection. */
sctx = ED_transform_snap_object_context_create_view3d(scene, 0, region, CTX_wm_view3d(C));
@@ -3718,8 +3725,8 @@ void GPENCIL_OT_reproject(wmOperatorType *ot)
/* callbacks */
ot->invoke = WM_menu_invoke;
- ot->exec = gp_strokes_reproject_exec;
- ot->poll = gp_strokes_edit3d_poll;
+ ot->exec = gpencil_strokes_reproject_exec;
+ ot->poll = gpencil_strokes_edit3d_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -3736,7 +3743,7 @@ void GPENCIL_OT_reproject(wmOperatorType *ot)
"Keep original strokes and create a copy before reprojecting instead of reproject them");
}
-static int gp_recalc_geometry_exec(bContext *C, wmOperator *UNUSED(op))
+static int gpencil_recalc_geometry_exec(bContext *C, wmOperator *UNUSED(op))
{
Object *ob = CTX_data_active_object(C);
if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
@@ -3765,8 +3772,8 @@ void GPENCIL_OT_recalc_geometry(wmOperatorType *ot)
ot->description = "Update all internal geometry data";
/* callbacks */
- ot->exec = gp_recalc_geometry_exec;
- ot->poll = gp_active_layer_poll;
+ ot->exec = gpencil_recalc_geometry_exec;
+ ot->poll = gpencil_active_layer_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -3779,7 +3786,7 @@ void GPENCIL_OT_recalc_geometry(wmOperatorType *ot)
* \{ */
/* helper to smooth */
-static void gp_smooth_stroke(bContext *C, wmOperator *op)
+static void gpencil_smooth_stroke(bContext *C, wmOperator *op)
{
const int repeat = RNA_int_get(op->ptr, "repeat");
float factor = RNA_float_get(op->ptr, "factor");
@@ -3826,7 +3833,7 @@ static void gp_smooth_stroke(bContext *C, wmOperator *op)
}
/* helper: Count how many points need to be inserted */
-static int gp_count_subdivision_cuts(bGPDstroke *gps)
+static int gpencil_count_subdivision_cuts(bGPDstroke *gps)
{
bGPDspoint *pt;
int i;
@@ -3844,7 +3851,7 @@ static int gp_count_subdivision_cuts(bGPDstroke *gps)
return totnewpoints;
}
-static int gp_stroke_subdivide_exec(bContext *C, wmOperator *op)
+static int gpencil_stroke_subdivide_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
bGPDspoint *temp_points;
@@ -3863,7 +3870,7 @@ static int gp_stroke_subdivide_exec(bContext *C, wmOperator *op)
if (gps->flag & GP_STROKE_SELECT) {
/* loop as many times as cuts */
for (int s = 0; s < cuts; s++) {
- totnewpoints = gp_count_subdivision_cuts(gps);
+ totnewpoints = gpencil_count_subdivision_cuts(gps);
if (totnewpoints == 0) {
continue;
}
@@ -3963,7 +3970,7 @@ static int gp_stroke_subdivide_exec(bContext *C, wmOperator *op)
GP_EDITABLE_STROKES_END(gpstroke_iter);
/* smooth stroke */
- gp_smooth_stroke(C, op);
+ gpencil_smooth_stroke(C, op);
/* notifiers */
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
@@ -3984,8 +3991,8 @@ void GPENCIL_OT_stroke_subdivide(wmOperatorType *ot)
"them";
/* api callbacks */
- ot->exec = gp_stroke_subdivide_exec;
- ot->poll = gp_active_layer_poll;
+ ot->exec = gpencil_stroke_subdivide_exec;
+ ot->poll = gpencil_active_layer_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -4011,7 +4018,7 @@ void GPENCIL_OT_stroke_subdivide(wmOperatorType *ot)
}
/* ** simplify stroke *** */
-static int gp_stroke_simplify_exec(bContext *C, wmOperator *op)
+static int gpencil_stroke_simplify_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
float factor = RNA_float_get(op->ptr, "factor");
@@ -4047,8 +4054,8 @@ void GPENCIL_OT_stroke_simplify(wmOperatorType *ot)
ot->description = "Simplify selected stroked reducing number of points";
/* api callbacks */
- ot->exec = gp_stroke_simplify_exec;
- ot->poll = gp_active_layer_poll;
+ ot->exec = gpencil_stroke_simplify_exec;
+ ot->poll = gpencil_active_layer_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -4060,7 +4067,7 @@ void GPENCIL_OT_stroke_simplify(wmOperatorType *ot)
}
/* ** simplify stroke using fixed algorithm *** */
-static int gp_stroke_simplify_fixed_exec(bContext *C, wmOperator *op)
+static int gpencil_stroke_simplify_fixed_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
int steps = RNA_int_get(op->ptr, "step");
@@ -4097,8 +4104,8 @@ void GPENCIL_OT_stroke_simplify_fixed(wmOperatorType *ot)
ot->description = "Simplify selected stroked reducing number of points using fixed algorithm";
/* api callbacks */
- ot->exec = gp_stroke_simplify_fixed_exec;
- ot->poll = gp_active_layer_poll;
+ ot->exec = gpencil_stroke_simplify_fixed_exec;
+ ot->poll = gpencil_active_layer_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -4111,7 +4118,7 @@ void GPENCIL_OT_stroke_simplify_fixed(wmOperatorType *ot)
}
/* ** Resample stroke *** */
-static int gp_stroke_sample_exec(bContext *C, wmOperator *op)
+static int gpencil_stroke_sample_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
const float length = RNA_float_get(op->ptr, "length");
@@ -4146,8 +4153,8 @@ void GPENCIL_OT_stroke_sample(wmOperatorType *ot)
ot->description = "Sample stroke points to predefined segment length";
/* api callbacks */
- ot->exec = gp_stroke_sample_exec;
- ot->poll = gp_active_layer_poll;
+ ot->exec = gpencil_stroke_sample_exec;
+ ot->poll = gpencil_active_layer_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -4164,7 +4171,7 @@ void GPENCIL_OT_stroke_sample(wmOperatorType *ot)
/** \name Stroke Trim Operator
* \{ */
-static int gp_stroke_trim_exec(bContext *C, wmOperator *UNUSED(op))
+static int gpencil_stroke_trim_exec(bContext *C, wmOperator *UNUSED(op))
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
@@ -4221,8 +4228,8 @@ void GPENCIL_OT_stroke_trim(wmOperatorType *ot)
ot->description = "Trim selected stroke to first loop or intersection";
/* api callbacks */
- ot->exec = gp_stroke_trim_exec;
- ot->poll = gp_active_layer_poll;
+ ot->exec = gpencil_stroke_trim_exec;
+ ot->poll = gpencil_active_layer_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -4243,7 +4250,7 @@ typedef enum eGP_SeparateModes {
GP_SEPARATE_LAYER,
} eGP_SeparateModes;
-static int gp_stroke_separate_exec(bContext *C, wmOperator *op)
+static int gpencil_stroke_separate_exec(bContext *C, wmOperator *op)
{
Base *base_new;
Main *bmain = CTX_data_main(C);
@@ -4277,7 +4284,7 @@ static int gp_stroke_separate_exec(bContext *C, wmOperator *op)
/* Create a new object. */
/* Take into account user preferences for duplicating actions. */
- short dupflag = (U.dupflag & USER_DUP_ACT);
+ const eDupli_ID_Flags dupflag = (U.dupflag & USER_DUP_ACT);
base_new = ED_object_add_duplicate(bmain, scene, view_layer, base_prev, dupflag);
ob_dst = base_new->object;
@@ -4347,10 +4354,12 @@ static int gp_stroke_separate_exec(bContext *C, wmOperator *op)
}
/* delete selected points from destination stroke */
- gp_stroke_delete_tagged_points(gpf_dst, gps_dst, NULL, GP_SPOINT_SELECT, false, 0);
+ gpencil_stroke_delete_tagged_points(
+ gpf_dst, gps_dst, NULL, GP_SPOINT_SELECT, false, 0);
/* delete selected points from origin stroke */
- gp_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_SELECT, false, 0);
+ gpencil_stroke_delete_tagged_points(
+ gpf, gps, gps->next, GP_SPOINT_SELECT, false, 0);
}
/* selected strokes mode */
else if (mode == GP_SEPARATE_STROKE) {
@@ -4441,8 +4450,8 @@ void GPENCIL_OT_stroke_separate(wmOperatorType *ot)
/* callbacks */
ot->invoke = WM_menu_invoke;
- ot->exec = gp_stroke_separate_exec;
- ot->poll = gp_strokes_edit3d_poll;
+ ot->exec = gpencil_stroke_separate_exec;
+ ot->poll = gpencil_strokes_edit3d_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -4457,7 +4466,7 @@ void GPENCIL_OT_stroke_separate(wmOperatorType *ot)
/** \name Stroke Split Operator
* \{ */
-static int gp_stroke_split_exec(bContext *C, wmOperator *UNUSED(op))
+static int gpencil_stroke_split_exec(bContext *C, wmOperator *UNUSED(op))
{
Object *ob = CTX_data_active_object(C);
bGPdata *gpd = ED_gpencil_data_get_active(C);
@@ -4505,10 +4514,10 @@ static int gp_stroke_split_exec(bContext *C, wmOperator *UNUSED(op))
}
/* delete selected points from destination stroke */
- gp_stroke_delete_tagged_points(gpf, gps_dst, NULL, GP_SPOINT_SELECT, true, 0);
+ gpencil_stroke_delete_tagged_points(gpf, gps_dst, NULL, GP_SPOINT_SELECT, true, 0);
/* delete selected points from origin stroke */
- gp_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_SELECT, false, 0);
+ gpencil_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_SELECT, false, 0);
}
}
/* select again tagged points */
@@ -4546,8 +4555,8 @@ void GPENCIL_OT_stroke_split(wmOperatorType *ot)
ot->description = "Split selected points as new stroke on same frame";
/* callbacks */
- ot->exec = gp_stroke_split_exec;
- ot->poll = gp_strokes_edit3d_poll;
+ ot->exec = gpencil_stroke_split_exec;
+ ot->poll = gpencil_strokes_edit3d_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -4559,7 +4568,7 @@ void GPENCIL_OT_stroke_split(wmOperatorType *ot)
/** \name Stroke Smooth Operator
* \{ */
-static int gp_stroke_smooth_exec(bContext *C, wmOperator *op)
+static int gpencil_stroke_smooth_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
@@ -4568,7 +4577,7 @@ static int gp_stroke_smooth_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- gp_smooth_stroke(C, op);
+ gpencil_smooth_stroke(C, op);
/* notifiers */
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
@@ -4587,8 +4596,8 @@ void GPENCIL_OT_stroke_smooth(wmOperatorType *ot)
ot->description = "Smooth selected strokes";
/* api callbacks */
- ot->exec = gp_stroke_smooth_exec;
- ot->poll = gp_active_layer_poll;
+ ot->exec = gpencil_stroke_smooth_exec;
+ ot->poll = gpencil_active_layer_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -4631,8 +4640,8 @@ static bool gpencil_test_lasso(bGPDstroke *gps,
const struct GP_SelectLassoUserData *data = user_data;
bGPDspoint pt2;
int x0, y0;
- gp_point_to_parent_space(pt, diff_mat, &pt2);
- gp_point_to_xy(gsc, gps, &pt2, &x0, &y0);
+ gpencil_point_to_parent_space(pt, diff_mat, &pt2);
+ gpencil_point_to_xy(gsc, gps, &pt2, &x0, &y0);
/* test if in lasso */
return ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(&data->rect, x0, y0) &&
BLI_lasso_is_point_inside(data->mcoords, data->mcoords_len, x0, y0, INT_MAX));
@@ -4688,7 +4697,8 @@ static void gpencil_cutter_dissolve(bGPDlayer *hit_layer, bGPDstroke *hit_stroke
pt->flag &= ~GP_SPOINT_TAG;
}
}
- gp_stroke_delete_tagged_points(hit_layer->actframe, hit_stroke, gpsn, GP_SPOINT_TAG, false, 1);
+ gpencil_stroke_delete_tagged_points(
+ hit_layer->actframe, hit_stroke, gpsn, GP_SPOINT_TAG, false, 1);
}
}
@@ -4715,7 +4725,7 @@ static int gpencil_cutter_lasso_select(bContext *C,
}
/* init space conversion stuff */
- gp_point_conversion_init(C, &gsc);
+ gpencil_point_conversion_init(C, &gsc);
/* deselect all strokes first */
CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
@@ -4877,7 +4887,7 @@ bool ED_object_gpencil_exit(struct Main *bmain, Object *ob)
/** \name Merge By Distance Operator
* \{ */
-static bool gp_merge_by_distance_poll(bContext *C)
+static bool gpencil_merge_by_distance_poll(bContext *C)
{
Object *ob = CTX_data_active_object(C);
if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
@@ -4893,7 +4903,7 @@ static bool gp_merge_by_distance_poll(bContext *C)
return ((gpl != NULL) && (ob->mode == OB_MODE_EDIT_GPENCIL));
}
-static int gp_merge_by_distance_exec(bContext *C, wmOperator *op)
+static int gpencil_merge_by_distance_exec(bContext *C, wmOperator *op)
{
Object *ob = CTX_data_active_object(C);
bGPdata *gpd = (bGPdata *)ob->data;
@@ -4930,8 +4940,8 @@ void GPENCIL_OT_stroke_merge_by_distance(wmOperatorType *ot)
ot->description = "Merge points by distance";
/* api callbacks */
- ot->exec = gp_merge_by_distance_exec;
- ot->poll = gp_merge_by_distance_poll;
+ ot->exec = gpencil_merge_by_distance_exec;
+ ot->poll = gpencil_merge_by_distance_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c
index 502097a0678..168693ff517 100644
--- a/source/blender/editors/gpencil/gpencil_fill.c
+++ b/source/blender/editors/gpencil/gpencil_fill.c
@@ -162,13 +162,13 @@ typedef struct tGPDfill {
} tGPDfill;
/* draw a given stroke using same thickness and color for all points */
-static void gp_draw_basic_stroke(tGPDfill *tgpf,
- bGPDstroke *gps,
- const float diff_mat[4][4],
- const bool cyclic,
- const float ink[4],
- const int flag,
- const float thershold)
+static void gpencil_draw_basic_stroke(tGPDfill *tgpf,
+ bGPDstroke *gps,
+ const float diff_mat[4][4],
+ const bool cyclic,
+ const float ink[4],
+ const int flag,
+ const float thershold)
{
bGPDspoint *points = gps->points;
@@ -223,7 +223,7 @@ static void gp_draw_basic_stroke(tGPDfill *tgpf,
}
/* loop all layers */
-static void gp_draw_datablock(tGPDfill *tgpf, const float ink[4])
+static void gpencil_draw_datablock(tGPDfill *tgpf, const float ink[4])
{
/* duplicated: etempFlags */
enum {
@@ -298,19 +298,19 @@ static void gp_draw_datablock(tGPDfill *tgpf, const float ink[4])
/* normal strokes */
if ((tgpf->fill_draw_mode == GP_FILL_DMODE_STROKE) ||
(tgpf->fill_draw_mode == GP_FILL_DMODE_BOTH)) {
- ED_gp_draw_fill(&tgpw);
+ ED_gpencil_draw_fill(&tgpw);
}
/* 3D Lines with basic shapes and invisible lines */
if ((tgpf->fill_draw_mode == GP_FILL_DMODE_CONTROL) ||
(tgpf->fill_draw_mode == GP_FILL_DMODE_BOTH)) {
- gp_draw_basic_stroke(tgpf,
- gps,
- tgpw.diff_mat,
- gps->flag & GP_STROKE_CYCLIC,
- ink,
- tgpf->flag,
- tgpf->fill_threshold);
+ gpencil_draw_basic_stroke(tgpf,
+ gps,
+ tgpw.diff_mat,
+ gps->flag & GP_STROKE_CYCLIC,
+ ink,
+ tgpf->flag,
+ tgpf->fill_threshold);
}
}
}
@@ -319,7 +319,7 @@ static void gp_draw_datablock(tGPDfill *tgpf, const float ink[4])
}
/* draw strokes in offscreen buffer */
-static bool gp_render_offscreen(tGPDfill *tgpf)
+static bool gpencil_render_offscreen(tGPDfill *tgpf)
{
bool is_ortho = false;
float winmat[4][4];
@@ -410,7 +410,7 @@ static bool gp_render_offscreen(tGPDfill *tgpf)
/* draw strokes */
float ink[4] = {1.0f, 0.0f, 0.0f, 1.0f};
- gp_draw_datablock(tgpf, ink);
+ gpencil_draw_datablock(tgpf, ink);
GPU_matrix_pop_projection();
GPU_matrix_pop();
@@ -472,15 +472,16 @@ static void set_pixel(ImBuf *ibuf, int idx, const float col[4])
}
}
-/* check if the size of the leak is narrow to determine if the stroke is closed
+/**
+ * Check if the size of the leak is narrow to determine if the stroke is closed
* this is used for strokes with small gaps between them to get a full fill
* and do not get a full screen fill.
*
- * \param ibuf: Image pixel data
- * \param maxpixel: Maximum index
- * \param limit: Limit of pixels to analyze
- * \param index: Index of current pixel
- * \param type: 0-Horizontal 1-Vertical
+ * \param ibuf: Image pixel data.
+ * \param maxpixel: Maximum index.
+ * \param limit: Limit of pixels to analyze.
+ * \param index: Index of current pixel.
+ * \param type: 0-Horizontal 1-Vertical.
*/
static bool is_leak_narrow(ImBuf *ibuf, const int maxpixel, int limit, int index, int type)
{
@@ -576,11 +577,12 @@ static bool is_leak_narrow(ImBuf *ibuf, const int maxpixel, int limit, int index
return (bool)(t_a && t_b);
}
-/* Boundary fill inside strokes
+/**
+ * Boundary fill inside strokes
* Fills the space created by a set of strokes using the stroke color as the boundary
* of the shape to fill.
*
- * \param tgpf: Temporary fill data
+ * \param tgpf: Temporary fill data.
*/
static void gpencil_boundaryfill_area(tGPDfill *tgpf)
{
@@ -665,62 +667,6 @@ static void gpencil_boundaryfill_area(tGPDfill *tgpf)
BLI_stack_free(stack);
}
-/* Check if there are some pixel not filled with green. If no points, means nothing to fill. */
-static bool UNUSED_FUNCTION(gpencil_check_borders)(tGPDfill *tgpf)
-{
- ImBuf *ibuf;
- void *lock;
- ibuf = BKE_image_acquire_ibuf(tgpf->ima, NULL, &lock);
- int idx;
- int pixel = 0;
- float color[4];
- bool found = false;
-
- /* horizontal lines */
- for (idx = 0; idx < ibuf->x; idx++) {
- /* bottom line */
- get_pixel(ibuf, idx, color);
- if (color[1] != 1.0f) {
- found = true;
- break;
- }
- /* top line */
- pixel = idx + (ibuf->x * (ibuf->y - 1));
- get_pixel(ibuf, pixel, color);
- if (color[1] != 1.0f) {
- found = true;
- break;
- }
- }
- if (!found) {
- /* vertical lines */
- for (idx = 0; idx < ibuf->y; idx++) {
- /* left line */
- get_pixel(ibuf, ibuf->x * idx, color);
- if (color[1] != 1.0f) {
- found = true;
- break;
- }
- /* right line */
- pixel = ibuf->x * idx + (ibuf->x - 1);
- get_pixel(ibuf, pixel, color);
- if (color[1] != 1.0f) {
- found = true;
- break;
- }
- }
- }
-
- /* release ibuf */
- if (ibuf) {
- BKE_image_release_ibuf(tgpf->ima, ibuf, lock);
- }
-
- tgpf->ima->id.tag |= LIB_TAG_DOIT;
-
- return found;
-}
-
/* Set a border to create image limits. */
static void gpencil_set_borders(tGPDfill *tgpf, const bool transparent)
{
@@ -1137,12 +1083,12 @@ static void gpencil_stroke_from_buffer(tGPDfill *tgpf)
for (int i = 0; i < tgpf->sbuffer_used && point2D; i++, point2D++, pt++) {
/* convert screen-coordinates to 3D coordinates */
- gp_stroke_convertcoords_tpoint(tgpf->scene,
- tgpf->region,
- tgpf->ob,
- point2D,
- tgpf->depth_arr ? tgpf->depth_arr + i : NULL,
- &pt->x);
+ gpencil_stroke_convertcoords_tpoint(tgpf->scene,
+ tgpf->region,
+ tgpf->ob,
+ point2D,
+ tgpf->depth_arr ? tgpf->depth_arr + i : NULL,
+ &pt->x);
pt->pressure = 1.0f;
pt->strength = 1.0f;
@@ -1183,14 +1129,14 @@ static void gpencil_stroke_from_buffer(tGPDfill *tgpf)
((ts->gpencil_v3d_align & GP_PROJECT_DEPTH_VIEW) == 0)) {
float origin[3];
ED_gpencil_drawing_reference_get(tgpf->scene, tgpf->ob, ts->gpencil_v3d_align, origin);
- ED_gp_project_stroke_to_plane(
+ ED_gpencil_project_stroke_to_plane(
tgpf->scene, tgpf->ob, tgpf->rv3d, gps, origin, tgpf->lock_axis - 1);
}
/* if parented change position relative to parent object */
for (int a = 0; a < tgpf->sbuffer_used; a++) {
pt = &gps->points[a];
- gp_apply_parent_point(tgpf->depsgraph, tgpf->ob, tgpf->gpl, pt);
+ gpencil_apply_parent_point(tgpf->depsgraph, tgpf->ob, tgpf->gpl, pt);
}
/* if camera view, reproject flat to view to avoid perspective effect */
@@ -1223,7 +1169,7 @@ static void gpencil_draw_boundary_lines(const bContext *UNUSED(C), tGPDfill *tgp
return;
}
const float ink[4] = {1.0f, 0.0f, 0.0f, 1.0f};
- gp_draw_datablock(tgpf, ink);
+ gpencil_draw_datablock(tgpf, ink);
}
/* Drawing callback for modal operator in 3d mode */
@@ -1266,7 +1212,7 @@ static bool gpencil_fill_poll(bContext *C)
}
/* Allocate memory and initialize values */
-static tGPDfill *gp_session_init_fill(bContext *C, wmOperator *UNUSED(op))
+static tGPDfill *gpencil_session_init_fill(bContext *C, wmOperator *UNUSED(op))
{
tGPDfill *tgpf = MEM_callocN(sizeof(tGPDfill), "GPencil Fill Data");
@@ -1399,7 +1345,7 @@ static int gpencil_fill_init(bContext *C, wmOperator *op)
}
/* check context */
- tgpf = op->customdata = gp_session_init_fill(C, op);
+ tgpf = op->customdata = gpencil_session_init_fill(C, op);
if (tgpf == NULL) {
/* something wasn't set correctly in context */
gpencil_fill_exit(C, op);
@@ -1499,7 +1445,7 @@ static int gpencil_fill_modal(bContext *C, wmOperator *op, const wmEvent *event)
tgpf->active_cfra = CFRA;
/* render screen to temp image */
- if (gp_render_offscreen(tgpf)) {
+ if (gpencil_render_offscreen(tgpf)) {
/* Set red borders to create a external limit. */
gpencil_set_borders(tgpf, true);
diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h
index 473913c5459..bd697dbc768 100644
--- a/source/blender/editors/gpencil/gpencil_intern.h
+++ b/source/blender/editors/gpencil/gpencil_intern.h
@@ -248,7 +248,7 @@ typedef struct tGPDprimitive {
} tGPDprimitive;
/* Modal Operator Drawing Callbacks ------------------------ */
-void ED_gp_draw_fill(struct tGPDdraw *tgpw);
+void ED_gpencil_draw_fill(struct tGPDdraw *tgpw);
/* ***************************************************** */
/* Internal API */
@@ -272,83 +272,85 @@ typedef struct GP_SpaceConversion {
float mat[4][4]; /* transform matrix on the strokes (introduced in [b770964]) */
} GP_SpaceConversion;
-bool gp_stroke_inside_circle(const float mval[2], int rad, int x0, int y0, int x1, int y1);
+bool gpencil_stroke_inside_circle(const float mval[2], int rad, int x0, int y0, int x1, int y1);
-void gp_point_conversion_init(struct bContext *C, GP_SpaceConversion *r_gsc);
+void gpencil_point_conversion_init(struct bContext *C, GP_SpaceConversion *r_gsc);
-void gp_point_to_xy(const GP_SpaceConversion *gsc,
- const struct bGPDstroke *gps,
- const struct bGPDspoint *pt,
- int *r_x,
- int *r_y);
+void gpencil_point_to_xy(const GP_SpaceConversion *gsc,
+ const struct bGPDstroke *gps,
+ const struct bGPDspoint *pt,
+ int *r_x,
+ int *r_y);
-void gp_point_to_xy_fl(const GP_SpaceConversion *gsc,
- const bGPDstroke *gps,
- const bGPDspoint *pt,
- float *r_x,
- float *r_y);
+void gpencil_point_to_xy_fl(const GP_SpaceConversion *gsc,
+ const bGPDstroke *gps,
+ const bGPDspoint *pt,
+ float *r_x,
+ float *r_y);
-void gp_point_to_parent_space(const bGPDspoint *pt, const float diff_mat[4][4], bGPDspoint *r_pt);
+void gpencil_point_to_parent_space(const bGPDspoint *pt,
+ const float diff_mat[4][4],
+ bGPDspoint *r_pt);
/**
* Change points position relative to parent object
*/
-void gp_apply_parent(struct Depsgraph *depsgraph,
- struct Object *obact,
- bGPDlayer *gpl,
- bGPDstroke *gps);
+void gpencil_apply_parent(struct Depsgraph *depsgraph,
+ struct Object *obact,
+ bGPDlayer *gpl,
+ bGPDstroke *gps);
/**
* Change point position relative to parent object
*/
-void gp_apply_parent_point(struct Depsgraph *depsgraph,
- struct Object *obact,
- bGPDlayer *gpl,
- bGPDspoint *pt);
+void gpencil_apply_parent_point(struct Depsgraph *depsgraph,
+ struct Object *obact,
+ bGPDlayer *gpl,
+ bGPDspoint *pt);
-void gp_point_3d_to_xy(const GP_SpaceConversion *gsc,
- const short flag,
- const float pt[3],
- float xy[2]);
+void gpencil_point_3d_to_xy(const GP_SpaceConversion *gsc,
+ const short flag,
+ const float pt[3],
+ float xy[2]);
-bool gp_point_xy_to_3d(const GP_SpaceConversion *gsc,
- struct Scene *scene,
- const float screen_co[2],
- float r_out[3]);
+bool gpencil_point_xy_to_3d(const GP_SpaceConversion *gsc,
+ struct Scene *scene,
+ const float screen_co[2],
+ float r_out[3]);
/* helper to convert 2d to 3d */
-void gp_stroke_convertcoords_tpoint(struct Scene *scene,
- struct ARegion *region,
- struct Object *ob,
- const struct tGPspoint *point2D,
- float *depth,
- float out[3]);
+void gpencil_stroke_convertcoords_tpoint(struct Scene *scene,
+ struct ARegion *region,
+ struct Object *ob,
+ const struct tGPspoint *point2D,
+ float *depth,
+ float out[3]);
/* Poll Callbacks ------------------------------------ */
/* gpencil_utils.c */
-bool gp_add_poll(struct bContext *C);
-bool gp_active_layer_poll(struct bContext *C);
-bool gp_active_brush_poll(struct bContext *C);
-bool gp_brush_crt_presets_poll(bContext *C);
+bool gpencil_add_poll(struct bContext *C);
+bool gpencil_active_layer_poll(struct bContext *C);
+bool gpencil_active_brush_poll(struct bContext *C);
+bool gpencil_brush_create_presets_poll(bContext *C);
/* Copy/Paste Buffer --------------------------------- */
/* gpencil_edit.c */
-extern ListBase gp_strokes_copypastebuf;
+extern ListBase gpencil_strokes_copypastebuf;
/* Build a map for converting between old colornames and destination-color-refs */
-struct GHash *gp_copybuf_validate_colormap(struct bContext *C);
+struct GHash *gpencil_copybuf_validate_colormap(struct bContext *C);
/* Stroke Editing ------------------------------------ */
-void gp_stroke_delete_tagged_points(bGPDframe *gpf,
- bGPDstroke *gps,
- bGPDstroke *next_stroke,
- int tag_flags,
- bool select,
- int limit);
-int gp_delete_selected_point_wrap(bContext *C);
+void gpencil_stroke_delete_tagged_points(bGPDframe *gpf,
+ bGPDstroke *gps,
+ bGPDstroke *next_stroke,
+ int tag_flags,
+ bool select,
+ int limit);
+int gpencil_delete_selected_point_wrap(bContext *C);
-void gp_subdivide_stroke(bGPDstroke *gps, const int subdivide);
+void gpencil_subdivide_stroke(bGPDstroke *gps, const int subdivide);
/* Layers Enums -------------------------------------- */
@@ -485,6 +487,8 @@ void GPENCIL_OT_frame_clean_fill(struct wmOperatorType *ot);
void GPENCIL_OT_frame_clean_loose(struct wmOperatorType *ot);
void GPENCIL_OT_convert(struct wmOperatorType *ot);
+void GPENCIL_OT_bake_mesh_animation(struct wmOperatorType *ot);
+
void GPENCIL_OT_image_to_grease_pencil(struct wmOperatorType *ot);
enum {
diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c
index 5cb49600d05..5e653187edb 100644
--- a/source/blender/editors/gpencil/gpencil_interpolate.c
+++ b/source/blender/editors/gpencil/gpencil_interpolate.c
@@ -100,10 +100,10 @@ static bool gpencil_view3d_poll(bContext *C)
}
/* Perform interpolation */
-static void gp_interpolate_update_points(const bGPDstroke *gps_from,
- const bGPDstroke *gps_to,
- bGPDstroke *new_stroke,
- float factor)
+static void gpencil_interpolate_update_points(const bGPDstroke *gps_from,
+ const bGPDstroke *gps_to,
+ bGPDstroke *new_stroke,
+ float factor)
{
/* update points */
for (int i = 0; i < new_stroke->totpoints; i++) {
@@ -121,7 +121,7 @@ static void gp_interpolate_update_points(const bGPDstroke *gps_from,
/* ****************** Interpolate Interactive *********************** */
/* Helper: free all temp strokes for display. */
-static void gp_interpolate_free_temp_strokes(bGPDframe *gpf)
+static void gpencil_interpolate_free_temp_strokes(bGPDframe *gpf)
{
if (gpf == NULL) {
return;
@@ -136,9 +136,11 @@ static void gp_interpolate_free_temp_strokes(bGPDframe *gpf)
}
/* Helper: Untag all strokes. */
-static void gp_interpolate_untag_strokes(bGPDframe *gpf)
+static void gpencil_interpolate_untag_strokes(bGPDframe *gpf)
{
- BLI_assert(gpf != NULL);
+ if (gpf == NULL) {
+ return;
+ }
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
if (gps->flag & GP_STROKE_TAG) {
@@ -148,7 +150,7 @@ static void gp_interpolate_untag_strokes(bGPDframe *gpf)
}
/* Helper: Update all strokes interpolated */
-static void gp_interpolate_update_strokes(bContext *C, tGPDinterpolate *tgpi)
+static void gpencil_interpolate_update_strokes(bContext *C, tGPDinterpolate *tgpi)
{
bGPdata *gpd = tgpi->gpd;
const float shift = tgpi->shift;
@@ -158,7 +160,7 @@ static void gp_interpolate_update_strokes(bContext *C, tGPDinterpolate *tgpi)
bGPDframe *gpf = tgpil->gpl->actframe;
/* Free temp strokes. */
- gp_interpolate_free_temp_strokes(gpf);
+ gpencil_interpolate_free_temp_strokes(gpf);
LISTBASE_FOREACH (bGPDstroke *, new_stroke, &tgpil->interFrame->strokes) {
bGPDstroke *gps_from, *gps_to;
@@ -176,7 +178,7 @@ static void gp_interpolate_update_strokes(bContext *C, tGPDinterpolate *tgpi)
/* update points position */
if ((gps_from) && (gps_to)) {
- gp_interpolate_update_points(gps_from, gps_to, new_stroke, factor);
+ gpencil_interpolate_update_points(gps_from, gps_to, new_stroke, factor);
/* Add temp strokes. */
if (gpf) {
@@ -193,7 +195,7 @@ static void gp_interpolate_update_strokes(bContext *C, tGPDinterpolate *tgpi)
}
/* Helper: Verify valid strokes for interpolation */
-static bool gp_interpolate_check_todo(bContext *C, bGPdata *gpd)
+static bool gpencil_interpolate_check_todo(bContext *C, bGPdata *gpd)
{
Object *ob = CTX_data_active_object(C);
ToolSettings *ts = CTX_data_tool_settings(C);
@@ -243,7 +245,7 @@ static bool gp_interpolate_check_todo(bContext *C, bGPdata *gpd)
}
/* Helper: Create internal strokes interpolated */
-static void gp_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi)
+static void gpencil_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi)
{
bGPdata *gpd = tgpi->gpd;
bGPDlayer *active_gpl = CTX_data_active_gpencil_layer(C);
@@ -262,6 +264,14 @@ static void gp_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi)
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
tGPDinterpolate_layer *tgpil;
+ /* Untag strokes to be sure nothing is pending. This must be done for
+ * all layer because it could be anything tagged and it would be removed
+ * at the end of the process when all tagged strokes are removed. */
+ if (gpl->actframe != NULL) {
+ gpencil_interpolate_untag_strokes(gpl->actframe);
+ gpencil_interpolate_untag_strokes(gpl->actframe->next);
+ }
+
/* all layers or only active */
if (!(tgpi->flag & GP_TOOLFLAG_INTERPOLATE_ALL_LAYERS) && (gpl != active_gpl)) {
continue;
@@ -278,10 +288,6 @@ static void gp_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi)
tgpil->prevFrame = gpl->actframe;
tgpil->nextFrame = gpl->actframe->next;
- /* Untag interpolated strokes to be sure nothing is pending. */
- gp_interpolate_untag_strokes(tgpil->prevFrame);
- gp_interpolate_untag_strokes(tgpil->nextFrame);
-
BLI_addtail(&tgpi->ilayers, tgpil);
/* create a new temporary frame */
@@ -337,7 +343,7 @@ static void gp_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi)
new_stroke->totpoints = gps_to->totpoints;
}
/* update points position */
- gp_interpolate_update_points(gps_from, gps_to, new_stroke, tgpil->factor);
+ gpencil_interpolate_update_points(gps_from, gps_to, new_stroke, tgpil->factor);
}
else {
/* need an empty stroke to keep index correct for lookup, but resize to smallest size */
@@ -413,7 +419,7 @@ static void gpencil_interpolate_update(bContext *C, wmOperator *op, tGPDinterpol
/* apply... */
tgpi->shift = RNA_float_get(op->ptr, "shift");
/* update points position */
- gp_interpolate_update_strokes(C, tgpi);
+ gpencil_interpolate_update_strokes(C, tgpi);
}
/* ----------------------- */
@@ -433,7 +439,7 @@ static void gpencil_interpolate_exit(bContext *C, wmOperator *op)
/* Clear any temp stroke. */
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
- gp_interpolate_free_temp_strokes(gpf);
+ gpencil_interpolate_free_temp_strokes(gpf);
}
}
@@ -454,7 +460,7 @@ static void gpencil_interpolate_exit(bContext *C, wmOperator *op)
}
/* Init new temporary interpolation data */
-static bool gp_interpolate_set_init_values(bContext *C, wmOperator *op, tGPDinterpolate *tgpi)
+static bool gpencil_interpolate_set_init_values(bContext *C, wmOperator *op, tGPDinterpolate *tgpi)
{
ToolSettings *ts = CTX_data_tool_settings(C);
bGPdata *gpd = CTX_data_gpencil_data(C);
@@ -475,18 +481,18 @@ static bool gp_interpolate_set_init_values(bContext *C, wmOperator *op, tGPDinte
/* set interpolation weight */
tgpi->shift = RNA_float_get(op->ptr, "shift");
/* set layers */
- gp_interpolate_set_points(C, tgpi);
+ gpencil_interpolate_set_points(C, tgpi);
return 1;
}
/* Allocate memory and initialize values */
-static tGPDinterpolate *gp_session_init_interpolation(bContext *C, wmOperator *op)
+static tGPDinterpolate *gpencil_session_init_interpolation(bContext *C, wmOperator *op)
{
tGPDinterpolate *tgpi = MEM_callocN(sizeof(tGPDinterpolate), "GPencil Interpolate Data");
/* define initial values */
- gp_interpolate_set_init_values(C, op, tgpi);
+ gpencil_interpolate_set_init_values(C, op, tgpi);
/* return context data for running operator */
return tgpi;
@@ -498,7 +504,7 @@ static int gpencil_interpolate_init(bContext *C, wmOperator *op)
tGPDinterpolate *tgpi;
/* check context */
- tgpi = op->customdata = gp_session_init_interpolation(C, op);
+ tgpi = op->customdata = gpencil_session_init_interpolation(C, op);
if (tgpi == NULL) {
/* something wasn't set correctly in context */
gpencil_interpolate_exit(C, op);
@@ -540,7 +546,7 @@ static int gpencil_interpolate_invoke(bContext *C, wmOperator *op, const wmEvent
}
/* need editable strokes */
- if (!gp_interpolate_check_todo(C, gpd)) {
+ if (!gpencil_interpolate_check_todo(C, gpd)) {
BKE_report(op->reports, RPT_ERROR, "Interpolation requires some editable strokes");
return OPERATOR_CANCELLED;
}
@@ -731,7 +737,7 @@ void GPENCIL_OT_interpolate(wmOperatorType *ot)
/* ****************** Interpolate Sequence *********************** */
/* Helper: Perform easing equation calculations for GP interpolation operator */
-static float gp_interpolate_seq_easing_calc(GP_Interpolate_Settings *ipo_settings, float time)
+static float gpencil_interpolate_seq_easing_calc(GP_Interpolate_Settings *ipo_settings, float time)
{
const float begin = 0.0f;
const float change = 1.0f;
@@ -1008,7 +1014,7 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op)
}
else if (ipo_settings->type >= GP_IPO_BACK) {
/* easing equation... */
- factor = gp_interpolate_seq_easing_calc(ipo_settings, factor);
+ factor = gpencil_interpolate_seq_easing_calc(ipo_settings, factor);
}
/* create new strokes data with interpolated points reading original stroke */
@@ -1064,7 +1070,7 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op)
}
/* update points position */
- gp_interpolate_update_points(gps_from, gps_to, new_stroke, factor);
+ gpencil_interpolate_update_points(gps_from, gps_to, new_stroke, factor);
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(new_stroke);
diff --git a/source/blender/editors/gpencil/gpencil_merge.c b/source/blender/editors/gpencil/gpencil_merge.c
index e71bf2098dd..cf806e68837 100644
--- a/source/blender/editors/gpencil/gpencil_merge.c
+++ b/source/blender/editors/gpencil/gpencil_merge.c
@@ -132,7 +132,7 @@ static bGPDstroke *gpencil_prepare_stroke(bContext *C, wmOperator *op, int totpo
bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, add_frame_mode);
/* stroke */
- bGPDstroke *gps = BKE_gpencil_stroke_new(ob->actcol - 1, totpoints, brush->size);
+ bGPDstroke *gps = BKE_gpencil_stroke_new(MAX2(ob->actcol - 1, 0), totpoints, brush->size);
gps->flag |= GP_STROKE_SELECT;
if (cyclic) {
@@ -178,7 +178,7 @@ static void gpencil_dissolve_points(bContext *C)
}
LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
- gp_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_TAG, false, 0);
+ gpencil_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_TAG, false, 0);
}
}
CTX_DATA_END;
@@ -431,7 +431,7 @@ static int gpencil_analyze_strokes(tGPencilPointCache *src_array,
return last;
}
-static bool gp_strokes_merge_poll(bContext *C)
+static bool gpencil_strokes_merge_poll(bContext *C)
{
/* only supported with grease pencil objects */
Object *ob = CTX_data_active_object(C);
@@ -462,7 +462,7 @@ static bool gp_strokes_merge_poll(bContext *C)
return (CTX_DATA_COUNT(C, editable_gpencil_strokes) != 0) && ED_operator_view3d_active(C);
}
-static int gp_stroke_merge_exec(bContext *C, wmOperator *op)
+static int gpencil_stroke_merge_exec(bContext *C, wmOperator *op)
{
const int mode = RNA_enum_get(op->ptr, "mode");
const bool clear_point = RNA_boolean_get(op->ptr, "clear_point");
@@ -546,8 +546,8 @@ void GPENCIL_OT_stroke_merge(wmOperatorType *ot)
ot->description = "Create a new stroke with the selected stroke points";
/* api callbacks */
- ot->exec = gp_stroke_merge_exec;
- ot->poll = gp_strokes_merge_poll;
+ ot->exec = gpencil_stroke_merge_exec;
+ ot->poll = gpencil_strokes_merge_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -563,7 +563,7 @@ void GPENCIL_OT_stroke_merge(wmOperatorType *ot)
}
/* Merge similar materials. */
-static bool gp_stroke_merge_material_poll(bContext *C)
+static bool gpencil_stroke_merge_material_poll(bContext *C)
{
/* only supported with grease pencil objects */
Object *ob = CTX_data_active_object(C);
@@ -574,7 +574,7 @@ static bool gp_stroke_merge_material_poll(bContext *C)
return true;
}
-static int gp_stroke_merge_material_exec(bContext *C, wmOperator *op)
+static int gpencil_stroke_merge_material_exec(bContext *C, wmOperator *op)
{
Object *ob = CTX_data_active_object(C);
bGPdata *gpd = (bGPdata *)ob->data;
@@ -642,8 +642,8 @@ void GPENCIL_OT_stroke_merge_material(wmOperatorType *ot)
ot->description = "Replace materials in strokes merging similar";
/* api callbacks */
- ot->exec = gp_stroke_merge_material_exec;
- ot->poll = gp_stroke_merge_material_poll;
+ ot->exec = gpencil_stroke_merge_material_exec;
+ ot->poll = gpencil_stroke_merge_material_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
diff --git a/source/blender/editors/gpencil/gpencil_mesh.c b/source/blender/editors/gpencil/gpencil_mesh.c
new file mode 100644
index 00000000000..763f5687edf
--- /dev/null
+++ b/source/blender/editors/gpencil/gpencil_mesh.c
@@ -0,0 +1,404 @@
+/*
+ * 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) 2008, Blender Foundation
+ * This is a new part of Blender
+ * Operator for converting Grease Pencil data to geometry
+ */
+
+/** \file
+ * \ingroup edgpencil
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_math.h"
+
+#include "DNA_gpencil_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
+
+#include "BKE_context.h"
+#include "BKE_duplilist.h"
+#include "BKE_global.h"
+#include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
+#include "BKE_main.h"
+#include "BKE_material.h"
+#include "BKE_object.h"
+#include "BKE_report.h"
+#include "BKE_scene.h"
+
+#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_query.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "ED_gpencil.h"
+#include "ED_transform_snap_object_context.h"
+
+#include "gpencil_intern.h"
+
+/* Check frame_end is always > start frame! */
+static void gpencil_bake_set_frame_end(struct Main *UNUSED(main),
+ struct Scene *UNUSED(scene),
+ struct PointerRNA *ptr)
+{
+ int frame_start = RNA_int_get(ptr, "frame_start");
+ int frame_end = RNA_int_get(ptr, "frame_end");
+
+ if (frame_end <= frame_start) {
+ RNA_int_set(ptr, "frame_end", frame_start + 1);
+ }
+}
+
+/* Extract mesh animation to Grease Pencil. */
+static bool gpencil_bake_mesh_animation_poll(bContext *C)
+{
+ if (CTX_data_mode_enum(C) != CTX_MODE_OBJECT) {
+ return false;
+ }
+
+ /* Only if the current view is 3D View. */
+ ScrArea *area = CTX_wm_area(C);
+ return (area && area->spacetype);
+}
+
+typedef struct GpBakeOb {
+ struct GPBakelist *next, *prev;
+ Object *ob;
+} GpBakeOb;
+
+static void gpencil_bake_duplilist(Depsgraph *depsgraph, Scene *scene, Object *ob, ListBase *list)
+{
+ GpBakeOb *elem = NULL;
+ ListBase *lb;
+ DupliObject *dob;
+ lb = object_duplilist(depsgraph, scene, ob);
+ for (dob = lb->first; dob; dob = dob->next) {
+ if (dob->ob->type != OB_MESH) {
+ continue;
+ }
+ elem = MEM_callocN(sizeof(GpBakeOb), __func__);
+ elem->ob = dob->ob;
+ BLI_addtail(list, elem);
+ }
+
+ free_object_duplilist(lb);
+}
+
+static void gpencil_bake_ob_list(bContext *C, Depsgraph *depsgraph, Scene *scene, ListBase *list)
+{
+ GpBakeOb *elem = NULL;
+
+ /* Add active object. In some files this could not be in selected array. */
+ Object *obact = CTX_data_active_object(C);
+
+ if (obact->type == OB_MESH) {
+ elem = MEM_callocN(sizeof(GpBakeOb), __func__);
+ elem->ob = obact;
+ BLI_addtail(list, elem);
+ }
+ /* Add duplilist. */
+ else if (obact->type == OB_EMPTY) {
+ gpencil_bake_duplilist(depsgraph, scene, obact, list);
+ }
+
+ /* Add other selected objects. */
+ CTX_DATA_BEGIN (C, Object *, ob, selected_objects) {
+ if (ob == obact) {
+ continue;
+ }
+ /* Add selected meshes.*/
+ if (ob->type == OB_MESH) {
+ elem = MEM_callocN(sizeof(GpBakeOb), __func__);
+ elem->ob = ob;
+ BLI_addtail(list, elem);
+ }
+
+ /* Add duplilist. */
+ if (ob->type == OB_EMPTY) {
+ gpencil_bake_duplilist(depsgraph, scene, obact, list);
+ }
+ }
+ CTX_DATA_END;
+}
+
+static void gpencil_bake_free_ob_list(ListBase *list)
+{
+ LISTBASE_FOREACH_MUTABLE (GpBakeOb *, elem, list) {
+ MEM_SAFE_FREE(elem);
+ }
+}
+
+static int gpencil_bake_mesh_animation_exec(bContext *C, wmOperator *op)
+{
+ Main *bmain = CTX_data_main(C);
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ Scene *scene = CTX_data_scene(C);
+ ARegion *region = CTX_wm_region(C);
+ View3D *v3d = CTX_wm_view3d(C);
+ Object *ob_gpencil = NULL;
+
+ ListBase list = {NULL, NULL};
+ gpencil_bake_ob_list(C, depsgraph, scene, &list);
+
+ /* Cannot check this in poll because the active object changes. */
+ if (list.first == NULL) {
+ BKE_report(op->reports, RPT_INFO, "No valid object selected");
+ gpencil_bake_free_ob_list(&list);
+ return OPERATOR_CANCELLED;
+ }
+
+ /* Grab all relevant settings. */
+ const int step = RNA_int_get(op->ptr, "step");
+
+ const int frame_start = (scene->r.sfra > RNA_int_get(op->ptr, "frame_start")) ?
+ scene->r.sfra :
+ RNA_int_get(op->ptr, "frame_start");
+
+ const int frame_end = (scene->r.efra < RNA_int_get(op->ptr, "frame_end")) ?
+ scene->r.efra :
+ RNA_int_get(op->ptr, "frame_end");
+
+ const float angle = RNA_float_get(op->ptr, "angle");
+ const int thickness = RNA_int_get(op->ptr, "thickness");
+ const bool use_seams = RNA_boolean_get(op->ptr, "seams");
+ const bool use_faces = RNA_boolean_get(op->ptr, "faces");
+ const float offset = RNA_float_get(op->ptr, "offset");
+ const int frame_offset = RNA_int_get(op->ptr, "frame_target") - frame_start;
+ char target[64];
+ RNA_string_get(op->ptr, "target", target);
+ const int project_type = RNA_enum_get(op->ptr, "project_type");
+
+ /* Create a new grease pencil object in origin. */
+ bool newob = false;
+ if (STREQ(target, "*NEW")) {
+ ushort local_view_bits = (v3d && v3d->localvd) ? v3d->local_view_uuid : 0;
+ float loc[3] = {0.0f, 0.0f, 0.0f};
+ ob_gpencil = ED_gpencil_add_object(C, loc, local_view_bits);
+ newob = true;
+ }
+ else {
+ ob_gpencil = BLI_findstring(&bmain->objects, target, offsetof(ID, name) + 2);
+ }
+
+ if ((ob_gpencil == NULL) || (ob_gpencil->type != OB_GPENCIL)) {
+ BKE_report(op->reports, RPT_ERROR, "Target grease pencil object not valid");
+ gpencil_bake_free_ob_list(&list);
+ return OPERATOR_CANCELLED;
+ }
+
+ bGPdata *gpd = (bGPdata *)ob_gpencil->data;
+ gpd->draw_mode = (project_type == GP_REPROJECT_KEEP) ? GP_DRAWMODE_3D : GP_DRAWMODE_2D;
+
+ /* Set cursor to indicate working. */
+ WM_cursor_wait(1);
+
+ GP_SpaceConversion gsc = {NULL};
+ SnapObjectContext *sctx = NULL;
+ if (project_type != GP_REPROJECT_KEEP) {
+ /* Init space conversion stuff. */
+ gpencil_point_conversion_init(C, &gsc);
+ /* Init snap context for geometry projection. */
+ sctx = ED_transform_snap_object_context_create_view3d(scene, 0, region, CTX_wm_view3d(C));
+
+ /* Tag all existing strokes to avoid reprojections. */
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ gps->flag |= GP_STROKE_TAG;
+ }
+ }
+ }
+ }
+
+ /* Loop all frame range. */
+ int oldframe = (int)DEG_get_ctime(depsgraph);
+ int key = -1;
+ for (int i = frame_start; i < frame_end + 1; i++) {
+ key++;
+ /* Jump if not step limit but include last frame always. */
+ if ((key % step != 0) && (i != frame_end)) {
+ continue;
+ }
+
+ /* Move scene to new frame. */
+ CFRA = i;
+ BKE_scene_graph_update_for_newframe(depsgraph, bmain);
+
+ /* Loop all objects in the list. */
+ LISTBASE_FOREACH (GpBakeOb *, elem, &list) {
+ Object *ob_eval = (Object *)DEG_get_evaluated_object(depsgraph, elem->ob);
+
+ /* Generate strokes. */
+ BKE_gpencil_convert_mesh(bmain,
+ depsgraph,
+ scene,
+ ob_gpencil,
+ elem->ob,
+ angle,
+ thickness,
+ offset,
+ ob_eval->obmat,
+ frame_offset,
+ use_seams,
+ use_faces);
+
+ /* Reproject all untaged created strokes. */
+ if (project_type != GP_REPROJECT_KEEP) {
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ bGPDframe *gpf = gpl->actframe;
+ if (gpf != NULL) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ if ((gps->flag & GP_STROKE_TAG) == 0) {
+ ED_gpencil_stroke_reproject(
+ depsgraph, &gsc, sctx, gpl, gpf, gps, project_type, false);
+ gps->flag |= GP_STROKE_TAG;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* Return scene frame state and DB to original state. */
+ CFRA = oldframe;
+ BKE_scene_graph_update_for_newframe(depsgraph, bmain);
+
+ /* Remove unused materials. */
+ int actcol = ob_gpencil->actcol;
+ for (int slot = 1; slot <= ob_gpencil->totcol; slot++) {
+ while (slot <= ob_gpencil->totcol && !BKE_object_material_slot_used(ob_gpencil->data, slot)) {
+ ob_gpencil->actcol = slot;
+ BKE_object_material_slot_remove(CTX_data_main(C), ob_gpencil);
+
+ if (actcol >= slot) {
+ actcol--;
+ }
+ }
+ }
+ ob_gpencil->actcol = actcol;
+
+ /* Untag all strokes. */
+ if (project_type != GP_REPROJECT_KEEP) {
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ gps->flag &= ~GP_STROKE_TAG;
+ }
+ }
+ }
+ }
+
+ /* Free memory. */
+ gpencil_bake_free_ob_list(&list);
+ if (sctx != NULL) {
+ ED_transform_snap_object_context_destroy(sctx);
+ }
+
+ /* notifiers */
+ if (newob) {
+ DEG_relations_tag_update(bmain);
+ }
+ DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
+ WM_event_add_notifier(C, NC_OBJECT | NA_ADDED, NULL);
+ WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene);
+
+ /* Reset cursor. */
+ WM_cursor_wait(0);
+
+ /* done */
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_bake_mesh_animation(wmOperatorType *ot)
+{
+ static const EnumPropertyItem reproject_type[] = {
+ {GP_REPROJECT_KEEP, "KEEP", 0, "No Reproject", ""},
+ {GP_REPROJECT_FRONT, "FRONT", 0, "Front", "Reproject the strokes using the X-Z plane"},
+ {GP_REPROJECT_SIDE, "SIDE", 0, "Side", "Reproject the strokes using the Y-Z plane"},
+ {GP_REPROJECT_TOP, "TOP", 0, "Top", "Reproject the strokes using the X-Y plane"},
+ {GP_REPROJECT_VIEW,
+ "VIEW",
+ 0,
+ "View",
+ "Reproject the strokes to end up on the same plane, as if drawn from the current viewpoint "
+ "using 'Cursor' Stroke Placement"},
+ {GP_REPROJECT_CURSOR,
+ "CURSOR",
+ 0,
+ "Cursor",
+ "Reproject the strokes using the orientation of 3D cursor"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ PropertyRNA *prop;
+
+ /* identifiers */
+ ot->name = "Bake Mesh Animation to Grease Pencil";
+ ot->idname = "GPENCIL_OT_bake_mesh_animation";
+ ot->description = "Bake Mesh Animation to Grease Pencil strokes";
+
+ /* callbacks */
+ ot->exec = gpencil_bake_mesh_animation_exec;
+ ot->poll = gpencil_bake_mesh_animation_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ ot->prop = RNA_def_int(
+ ot->srna, "frame_start", 1, 1, 100000, "Start Frame", "The start frame", 1, 100000);
+
+ prop = RNA_def_int(
+ ot->srna, "frame_end", 250, 1, 100000, "End Frame", "The end frame of animation", 1, 100000);
+ RNA_def_property_update_runtime(prop, gpencil_bake_set_frame_end);
+
+ prop = RNA_def_int(ot->srna, "step", 1, 1, 100, "Step", "Step between generated frames", 1, 100);
+
+ prop = RNA_def_float_rotation(ot->srna,
+ "angle",
+ 0,
+ NULL,
+ DEG2RADF(0.0f),
+ DEG2RADF(180.0f),
+ "Threshold Angle",
+ "Threshold to determine ends of the strokes",
+ DEG2RADF(0.0f),
+ DEG2RADF(180.0f));
+ RNA_def_property_float_default(prop, DEG2RADF(70.0f));
+
+ RNA_def_int(ot->srna, "thickness", 1, 1, 100, "Thickness", "", 1, 100);
+ RNA_def_boolean(ot->srna, "seams", 0, "Only Seam Edges", "Convert only seam edges");
+ RNA_def_boolean(ot->srna, "faces", 1, "Export Faces", "Export faces as filled strokes");
+ RNA_def_float_distance(
+ ot->srna, "offset", 0.001f, 0.0, 100.0, "Offset", "Offset strokes from fill", 0.0, 100.00);
+ RNA_def_int(ot->srna, "frame_target", 1, 1, 100000, "Frame Target", "", 1, 100000);
+ RNA_def_string(ot->srna,
+ "target",
+ "*NEW",
+ 64,
+ "Target Object",
+ "Target grease pencil object name. Leave empty for new object");
+
+ RNA_def_enum(ot->srna, "project_type", reproject_type, GP_REPROJECT_VIEW, "Projection Type", "");
+}
diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c
index 94c86572fd3..e645668ef1c 100644
--- a/source/blender/editors/gpencil/gpencil_ops.c
+++ b/source/blender/editors/gpencil/gpencil_ops.c
@@ -63,21 +63,21 @@ static void ed_keymap_gpencil_general(wmKeyConfig *keyconf)
/* ==================== */
/* Poll callback for stroke editing mode */
-static bool gp_stroke_editmode_poll(bContext *C)
+static bool gpencil_stroke_editmode_poll(bContext *C)
{
bGPdata *gpd = CTX_data_gpencil_data(C);
return (gpd && (gpd->flag & GP_DATA_STROKE_EDITMODE));
}
/* Poll callback for stroke painting mode */
-static bool gp_stroke_paintmode_poll(bContext *C)
+static bool gpencil_stroke_paintmode_poll(bContext *C)
{
/* TODO: limit this to mode, but review 2D editors */
bGPdata *gpd = CTX_data_gpencil_data(C);
return (gpd && (gpd->flag & GP_DATA_STROKE_PAINTMODE));
}
-static bool gp_stroke_paintmode_poll_with_tool(bContext *C, const char gpencil_tool)
+static bool gpencil_stroke_paintmode_poll_with_tool(bContext *C, const char gpencil_tool)
{
/* TODO: limit this to mode, but review 2D editors */
bGPdata *gpd = CTX_data_gpencil_data(C);
@@ -95,7 +95,7 @@ static bool gp_stroke_paintmode_poll_with_tool(bContext *C, const char gpencil_t
WM_toolsystem_active_tool_is_brush(C) && (brush->gpencil_tool == gpencil_tool));
}
-static bool gp_stroke_vertexmode_poll_with_tool(bContext *C, const char gpencil_vertex_tool)
+static bool gpencil_stroke_vertexmode_poll_with_tool(bContext *C, const char gpencil_vertex_tool)
{
bGPdata *gpd = CTX_data_gpencil_data(C);
if (!gpd) {
@@ -113,7 +113,7 @@ static bool gp_stroke_vertexmode_poll_with_tool(bContext *C, const char gpencil_
(brush->gpencil_vertex_tool == gpencil_vertex_tool));
}
-static bool gp_stroke_sculptmode_poll_with_tool(bContext *C, const char gpencil_sculpt_tool)
+static bool gpencil_stroke_sculptmode_poll_with_tool(bContext *C, const char gpencil_sculpt_tool)
{
bGPdata *gpd = CTX_data_gpencil_data(C);
if (!gpd) {
@@ -131,7 +131,7 @@ static bool gp_stroke_sculptmode_poll_with_tool(bContext *C, const char gpencil_
(brush->gpencil_sculpt_tool == gpencil_sculpt_tool));
}
-static bool gp_stroke_weightmode_poll_with_tool(bContext *C, const char gpencil_weight_tool)
+static bool gpencil_stroke_weightmode_poll_with_tool(bContext *C, const char gpencil_weight_tool)
{
bGPdata *gpd = CTX_data_gpencil_data(C);
if (!gpd) {
@@ -150,31 +150,31 @@ static bool gp_stroke_weightmode_poll_with_tool(bContext *C, const char gpencil_
}
/* Poll callback for stroke painting (draw brush) */
-static bool gp_stroke_paintmode_draw_poll(bContext *C)
+static bool gpencil_stroke_paintmode_draw_poll(bContext *C)
{
- return gp_stroke_paintmode_poll_with_tool(C, GPAINT_TOOL_DRAW);
+ return gpencil_stroke_paintmode_poll_with_tool(C, GPAINT_TOOL_DRAW);
}
/* Poll callback for stroke painting (erase brush) */
-static bool gp_stroke_paintmode_erase_poll(bContext *C)
+static bool gpencil_stroke_paintmode_erase_poll(bContext *C)
{
- return gp_stroke_paintmode_poll_with_tool(C, GPAINT_TOOL_ERASE);
+ return gpencil_stroke_paintmode_poll_with_tool(C, GPAINT_TOOL_ERASE);
}
/* Poll callback for stroke painting (fill) */
-static bool gp_stroke_paintmode_fill_poll(bContext *C)
+static bool gpencil_stroke_paintmode_fill_poll(bContext *C)
{
- return gp_stroke_paintmode_poll_with_tool(C, GPAINT_TOOL_FILL);
+ return gpencil_stroke_paintmode_poll_with_tool(C, GPAINT_TOOL_FILL);
}
/* Poll callback for stroke painting (tint) */
-static bool gp_stroke_paintmode_tint_poll(bContext *C)
+static bool gpencil_stroke_paintmode_tint_poll(bContext *C)
{
- return gp_stroke_paintmode_poll_with_tool(C, GPAINT_TOOL_TINT);
+ return gpencil_stroke_paintmode_poll_with_tool(C, GPAINT_TOOL_TINT);
}
/* Poll callback for stroke sculpting mode */
-static bool gp_stroke_sculptmode_poll(bContext *C)
+static bool gpencil_stroke_sculptmode_poll(bContext *C)
{
bGPdata *gpd = CTX_data_gpencil_data(C);
Object *ob = CTX_data_active_object(C);
@@ -194,7 +194,7 @@ static bool gp_stroke_sculptmode_poll(bContext *C)
}
/* Poll callback for stroke weight paint mode */
-static bool gp_stroke_weightmode_poll(bContext *C)
+static bool gpencil_stroke_weightmode_poll(bContext *C)
{
bGPdata *gpd = CTX_data_gpencil_data(C);
Object *ob = CTX_data_active_object(C);
@@ -207,7 +207,7 @@ static bool gp_stroke_weightmode_poll(bContext *C)
}
/* Poll callback for stroke vertex paint mode */
-static bool gp_stroke_vertexmode_poll(bContext *C)
+static bool gpencil_stroke_vertexmode_poll(bContext *C)
{
bGPdata *gpd = CTX_data_gpencil_data(C);
Object *ob = CTX_data_active_object(C);
@@ -220,91 +220,91 @@ static bool gp_stroke_vertexmode_poll(bContext *C)
}
/* Poll callback for vertex painting (draw) */
-static bool gp_stroke_vertexmode_draw_poll(bContext *C)
+static bool gpencil_stroke_vertexmode_draw_poll(bContext *C)
{
- return gp_stroke_vertexmode_poll_with_tool(C, GPVERTEX_TOOL_DRAW);
+ return gpencil_stroke_vertexmode_poll_with_tool(C, GPVERTEX_TOOL_DRAW);
}
/* Poll callback for vertex painting (blur) */
-static bool gp_stroke_vertexmode_blur_poll(bContext *C)
+static bool gpencil_stroke_vertexmode_blur_poll(bContext *C)
{
- return gp_stroke_vertexmode_poll_with_tool(C, GPVERTEX_TOOL_BLUR);
+ return gpencil_stroke_vertexmode_poll_with_tool(C, GPVERTEX_TOOL_BLUR);
}
/* Poll callback for vertex painting (average) */
-static bool gp_stroke_vertexmode_average_poll(bContext *C)
+static bool gpencil_stroke_vertexmode_average_poll(bContext *C)
{
- return gp_stroke_vertexmode_poll_with_tool(C, GPVERTEX_TOOL_AVERAGE);
+ return gpencil_stroke_vertexmode_poll_with_tool(C, GPVERTEX_TOOL_AVERAGE);
}
/* Poll callback for vertex painting (smear) */
-static bool gp_stroke_vertexmode_smear_poll(bContext *C)
+static bool gpencil_stroke_vertexmode_smear_poll(bContext *C)
{
- return gp_stroke_vertexmode_poll_with_tool(C, GPVERTEX_TOOL_SMEAR);
+ return gpencil_stroke_vertexmode_poll_with_tool(C, GPVERTEX_TOOL_SMEAR);
}
/* Poll callback for vertex painting (replace) */
-static bool gp_stroke_vertexmode_replace_poll(bContext *C)
+static bool gpencil_stroke_vertexmode_replace_poll(bContext *C)
{
- return gp_stroke_vertexmode_poll_with_tool(C, GPVERTEX_TOOL_REPLACE);
+ return gpencil_stroke_vertexmode_poll_with_tool(C, GPVERTEX_TOOL_REPLACE);
}
/* Poll callback for sculpt (Smooth) */
-static bool gp_stroke_sculptmode_smooth_poll(bContext *C)
+static bool gpencil_stroke_sculptmode_smooth_poll(bContext *C)
{
- return gp_stroke_sculptmode_poll_with_tool(C, GPSCULPT_TOOL_SMOOTH);
+ return gpencil_stroke_sculptmode_poll_with_tool(C, GPSCULPT_TOOL_SMOOTH);
}
/* Poll callback for sculpt (Thickness) */
-static bool gp_stroke_sculptmode_thickness_poll(bContext *C)
+static bool gpencil_stroke_sculptmode_thickness_poll(bContext *C)
{
- return gp_stroke_sculptmode_poll_with_tool(C, GPSCULPT_TOOL_THICKNESS);
+ return gpencil_stroke_sculptmode_poll_with_tool(C, GPSCULPT_TOOL_THICKNESS);
}
/* Poll callback for sculpt (Strength) */
-static bool gp_stroke_sculptmode_strength_poll(bContext *C)
+static bool gpencil_stroke_sculptmode_strength_poll(bContext *C)
{
- return gp_stroke_sculptmode_poll_with_tool(C, GPSCULPT_TOOL_STRENGTH);
+ return gpencil_stroke_sculptmode_poll_with_tool(C, GPSCULPT_TOOL_STRENGTH);
}
/* Poll callback for sculpt (Grab) */
-static bool gp_stroke_sculptmode_grab_poll(bContext *C)
+static bool gpencil_stroke_sculptmode_grab_poll(bContext *C)
{
- return gp_stroke_sculptmode_poll_with_tool(C, GPSCULPT_TOOL_GRAB);
+ return gpencil_stroke_sculptmode_poll_with_tool(C, GPSCULPT_TOOL_GRAB);
}
/* Poll callback for sculpt (Push) */
-static bool gp_stroke_sculptmode_push_poll(bContext *C)
+static bool gpencil_stroke_sculptmode_push_poll(bContext *C)
{
- return gp_stroke_sculptmode_poll_with_tool(C, GPSCULPT_TOOL_PUSH);
+ return gpencil_stroke_sculptmode_poll_with_tool(C, GPSCULPT_TOOL_PUSH);
}
/* Poll callback for sculpt (Twist) */
-static bool gp_stroke_sculptmode_twist_poll(bContext *C)
+static bool gpencil_stroke_sculptmode_twist_poll(bContext *C)
{
- return gp_stroke_sculptmode_poll_with_tool(C, GPSCULPT_TOOL_TWIST);
+ return gpencil_stroke_sculptmode_poll_with_tool(C, GPSCULPT_TOOL_TWIST);
}
/* Poll callback for sculpt (Pinch) */
-static bool gp_stroke_sculptmode_pinch_poll(bContext *C)
+static bool gpencil_stroke_sculptmode_pinch_poll(bContext *C)
{
- return gp_stroke_sculptmode_poll_with_tool(C, GPSCULPT_TOOL_PINCH);
+ return gpencil_stroke_sculptmode_poll_with_tool(C, GPSCULPT_TOOL_PINCH);
}
/* Poll callback for sculpt (Randomize) */
-static bool gp_stroke_sculptmode_randomize_poll(bContext *C)
+static bool gpencil_stroke_sculptmode_randomize_poll(bContext *C)
{
- return gp_stroke_sculptmode_poll_with_tool(C, GPSCULPT_TOOL_RANDOMIZE);
+ return gpencil_stroke_sculptmode_poll_with_tool(C, GPSCULPT_TOOL_RANDOMIZE);
}
/* Poll callback for sculpt (Clone) */
-static bool gp_stroke_sculptmode_clone_poll(bContext *C)
+static bool gpencil_stroke_sculptmode_clone_poll(bContext *C)
{
- return gp_stroke_sculptmode_poll_with_tool(C, GPSCULPT_TOOL_CLONE);
+ return gpencil_stroke_sculptmode_poll_with_tool(C, GPSCULPT_TOOL_CLONE);
}
/* Poll callback for weight paint (Draw) */
-static bool gp_stroke_weightmode_draw_poll(bContext *C)
+static bool gpencil_stroke_weightmode_draw_poll(bContext *C)
{
- return gp_stroke_weightmode_poll_with_tool(C, GPWEIGHT_TOOL_DRAW);
+ return gpencil_stroke_weightmode_poll_with_tool(C, GPWEIGHT_TOOL_DRAW);
}
/* Stroke Editing Keymap - Only when editmode is enabled */
@@ -313,35 +313,35 @@ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf)
wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Edit Mode", 0, 0);
/* set poll callback - so that this keymap only gets enabled when stroke editmode is enabled */
- keymap->poll = gp_stroke_editmode_poll;
+ keymap->poll = gpencil_stroke_editmode_poll;
}
/* keys for draw with a drawing brush (no fill) */
static void ed_keymap_gpencil_painting_draw(wmKeyConfig *keyconf)
{
wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Paint (Draw brush)", 0, 0);
- keymap->poll = gp_stroke_paintmode_draw_poll;
+ keymap->poll = gpencil_stroke_paintmode_draw_poll;
}
/* keys for draw with a eraser brush (erase) */
static void ed_keymap_gpencil_painting_erase(wmKeyConfig *keyconf)
{
wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Paint (Erase)", 0, 0);
- keymap->poll = gp_stroke_paintmode_erase_poll;
+ keymap->poll = gpencil_stroke_paintmode_erase_poll;
}
/* keys for draw with a fill brush */
static void ed_keymap_gpencil_painting_fill(wmKeyConfig *keyconf)
{
wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Paint (Fill)", 0, 0);
- keymap->poll = gp_stroke_paintmode_fill_poll;
+ keymap->poll = gpencil_stroke_paintmode_fill_poll;
}
/* keys for draw with a tint brush */
static void ed_keymap_gpencil_painting_tint(wmKeyConfig *keyconf)
{
wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Paint (Tint)", 0, 0);
- keymap->poll = gp_stroke_paintmode_tint_poll;
+ keymap->poll = gpencil_stroke_paintmode_tint_poll;
}
/* Stroke Painting Keymap - Only when paintmode is enabled */
@@ -349,7 +349,7 @@ static void ed_keymap_gpencil_painting(wmKeyConfig *keyconf)
{
/* set poll callback - so that this keymap only gets enabled when stroke paintmode is enabled */
wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Paint Mode", 0, 0);
- keymap->poll = gp_stroke_paintmode_poll;
+ keymap->poll = gpencil_stroke_paintmode_poll;
}
/* Stroke Sculpting Keymap - Only when sculptmode is enabled */
@@ -357,7 +357,7 @@ static void ed_keymap_gpencil_sculpting(wmKeyConfig *keyconf)
{
/* set poll callback - so that this keymap only gets enabled when stroke sculptmode is enabled */
wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Sculpt Mode", 0, 0);
- keymap->poll = gp_stroke_sculptmode_poll;
+ keymap->poll = gpencil_stroke_sculptmode_poll;
}
/* Stroke Weight Paint Keymap - Only when weight is enabled */
@@ -365,106 +365,106 @@ static void ed_keymap_gpencil_weightpainting(wmKeyConfig *keyconf)
{
/* set poll callback - so that this keymap only gets enabled when stroke sculptmode is enabled */
wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Weight Mode", 0, 0);
- keymap->poll = gp_stroke_weightmode_poll;
+ keymap->poll = gpencil_stroke_weightmode_poll;
}
static void ed_keymap_gpencil_vertexpainting(wmKeyConfig *keyconf)
{
/* set poll callback - so that this keymap only gets enabled when stroke vertex is enabled */
wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Vertex Mode", 0, 0);
- keymap->poll = gp_stroke_vertexmode_poll;
+ keymap->poll = gpencil_stroke_vertexmode_poll;
}
/* keys for vertex with a draw brush */
static void ed_keymap_gpencil_vertexpainting_draw(wmKeyConfig *keyconf)
{
wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Vertex (Draw)", 0, 0);
- keymap->poll = gp_stroke_vertexmode_draw_poll;
+ keymap->poll = gpencil_stroke_vertexmode_draw_poll;
}
/* keys for vertex with a blur brush */
static void ed_keymap_gpencil_vertexpainting_blur(wmKeyConfig *keyconf)
{
wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Vertex (Blur)", 0, 0);
- keymap->poll = gp_stroke_vertexmode_blur_poll;
+ keymap->poll = gpencil_stroke_vertexmode_blur_poll;
}
/* keys for vertex with a average brush */
static void ed_keymap_gpencil_vertexpainting_average(wmKeyConfig *keyconf)
{
wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Vertex (Average)", 0, 0);
- keymap->poll = gp_stroke_vertexmode_average_poll;
+ keymap->poll = gpencil_stroke_vertexmode_average_poll;
}
/* keys for vertex with a smear brush */
static void ed_keymap_gpencil_vertexpainting_smear(wmKeyConfig *keyconf)
{
wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Vertex (Smear)", 0, 0);
- keymap->poll = gp_stroke_vertexmode_smear_poll;
+ keymap->poll = gpencil_stroke_vertexmode_smear_poll;
}
/* keys for vertex with a replace brush */
static void ed_keymap_gpencil_vertexpainting_replace(wmKeyConfig *keyconf)
{
wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Vertex (Replace)", 0, 0);
- keymap->poll = gp_stroke_vertexmode_replace_poll;
+ keymap->poll = gpencil_stroke_vertexmode_replace_poll;
}
/* keys for sculpt with a smooth brush */
static void ed_keymap_gpencil_sculptpainting_smooth(wmKeyConfig *keyconf)
{
wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Sculpt (Smooth)", 0, 0);
- keymap->poll = gp_stroke_sculptmode_smooth_poll;
+ keymap->poll = gpencil_stroke_sculptmode_smooth_poll;
}
/* keys for sculpt with a thickness brush */
static void ed_keymap_gpencil_sculptpainting_thickness(wmKeyConfig *keyconf)
{
wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Sculpt (Thickness)", 0, 0);
- keymap->poll = gp_stroke_sculptmode_thickness_poll;
+ keymap->poll = gpencil_stroke_sculptmode_thickness_poll;
}
/* keys for sculpt with a strength brush */
static void ed_keymap_gpencil_sculptpainting_strength(wmKeyConfig *keyconf)
{
wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Sculpt (Strength)", 0, 0);
- keymap->poll = gp_stroke_sculptmode_strength_poll;
+ keymap->poll = gpencil_stroke_sculptmode_strength_poll;
}
/* keys for sculpt with a grab brush */
static void ed_keymap_gpencil_sculptpainting_grab(wmKeyConfig *keyconf)
{
wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Sculpt (Grab)", 0, 0);
- keymap->poll = gp_stroke_sculptmode_grab_poll;
+ keymap->poll = gpencil_stroke_sculptmode_grab_poll;
}
/* keys for sculpt with a push brush */
static void ed_keymap_gpencil_sculptpainting_push(wmKeyConfig *keyconf)
{
wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Sculpt (Push)", 0, 0);
- keymap->poll = gp_stroke_sculptmode_push_poll;
+ keymap->poll = gpencil_stroke_sculptmode_push_poll;
}
/* keys for sculpt with a twist brush */
static void ed_keymap_gpencil_sculptpainting_twist(wmKeyConfig *keyconf)
{
wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Sculpt (Twist)", 0, 0);
- keymap->poll = gp_stroke_sculptmode_twist_poll;
+ keymap->poll = gpencil_stroke_sculptmode_twist_poll;
}
/* keys for sculpt with a pinch brush */
static void ed_keymap_gpencil_sculptpainting_pinch(wmKeyConfig *keyconf)
{
wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Sculpt (Pinch)", 0, 0);
- keymap->poll = gp_stroke_sculptmode_pinch_poll;
+ keymap->poll = gpencil_stroke_sculptmode_pinch_poll;
}
/* keys for sculpt with a randomize brush */
static void ed_keymap_gpencil_sculptpainting_randomize(wmKeyConfig *keyconf)
{
wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Sculpt (Randomize)", 0, 0);
- keymap->poll = gp_stroke_sculptmode_randomize_poll;
+ keymap->poll = gpencil_stroke_sculptmode_randomize_poll;
}
/* keys for sculpt with a clone brush */
static void ed_keymap_gpencil_sculptpainting_clone(wmKeyConfig *keyconf)
{
wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Sculpt (Clone)", 0, 0);
- keymap->poll = gp_stroke_sculptmode_clone_poll;
+ keymap->poll = gpencil_stroke_sculptmode_clone_poll;
}
/* keys for weight with a draw brush */
static void ed_keymap_gpencil_weightpainting_draw(wmKeyConfig *keyconf)
{
wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Weight (Draw)", 0, 0);
- keymap->poll = gp_stroke_weightmode_draw_poll;
+ keymap->poll = gpencil_stroke_weightmode_draw_poll;
}
/* ==================== */
@@ -603,6 +603,7 @@ void ED_operatortypes_gpencil(void)
WM_operatortype_append(GPENCIL_OT_frame_clean_loose);
WM_operatortype_append(GPENCIL_OT_convert);
+ WM_operatortype_append(GPENCIL_OT_bake_mesh_animation);
WM_operatortype_append(GPENCIL_OT_image_to_grease_pencil);
diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c
index a9eb94498ad..95133244dfb 100644
--- a/source/blender/editors/gpencil/gpencil_paint.c
+++ b/source/blender/editors/gpencil/gpencil_paint.c
@@ -272,7 +272,7 @@ typedef struct tGPsdata {
/* minimum length of new segment before new point can be added */
#define MIN_EUCLIDEAN_PX (U.gp_euclideandist)
-static void gp_update_cache(bGPdata *gpd)
+static void gpencil_update_cache(bGPdata *gpd)
{
if (gpd) {
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
@@ -280,19 +280,19 @@ static void gp_update_cache(bGPdata *gpd)
}
}
-static void gp_stroke_added_enable(tGPsdata *p)
+static void gpencil_stroke_added_enable(tGPsdata *p)
{
BLI_assert(p->gpf->strokes.last != NULL);
p->flags |= GP_PAINTFLAG_STROKEADDED;
/* drawing batch cache is dirty now */
- gp_update_cache(p->gpd);
+ gpencil_update_cache(p->gpd);
}
/* ------ */
/* Forward defines for some functions... */
-static void gp_session_validatebuffer(tGPsdata *p);
+static void gpencil_session_validatebuffer(tGPsdata *p);
/* ******************************************* */
/* Context Wrangling... */
@@ -352,7 +352,7 @@ static bool gpencil_project_check(tGPsdata *p)
/* Utilities --------------------------------- */
/* get the reference point for stroke-point conversions */
-static void gp_get_3d_reference(tGPsdata *p, float vec[3])
+static void gpencil_get_3d_reference(tGPsdata *p, float vec[3])
{
Object *ob = NULL;
if (p->ownerPtr.type == &RNA_Object) {
@@ -363,7 +363,7 @@ static void gp_get_3d_reference(tGPsdata *p, float vec[3])
/* Stroke Editing ---------------------------- */
/* check if the current mouse position is suitable for adding a new point */
-static bool gp_stroke_filtermval(tGPsdata *p, const float mval[2], float mvalo[2])
+static bool gpencil_stroke_filtermval(tGPsdata *p, const float mval[2], float mvalo[2])
{
Brush *brush = p->brush;
int dx = (int)fabsf(mval[0] - mvalo[0]);
@@ -410,7 +410,7 @@ static bool gp_stroke_filtermval(tGPsdata *p, const float mval[2], float mvalo[2
}
/* reproject stroke to plane locked to axis in 3d cursor location */
-static void gp_reproject_toplane(tGPsdata *p, bGPDstroke *gps)
+static void gpencil_reproject_toplane(tGPsdata *p, bGPDstroke *gps)
{
bGPdata *gpd = p->gpd;
Object *obact = (Object *)p->ownerPtr.data;
@@ -430,13 +430,15 @@ static void gp_reproject_toplane(tGPsdata *p, bGPDstroke *gps)
}
/* get drawing origin */
- gp_get_3d_reference(p, origin);
- ED_gp_project_stroke_to_plane(p->scene, obact, rv3d, gps, origin, p->lock_axis - 1);
+ gpencil_get_3d_reference(p, origin);
+ ED_gpencil_project_stroke_to_plane(p->scene, obact, rv3d, gps, origin, p->lock_axis - 1);
}
/* convert screen-coordinates to buffer-coordinates */
-/* XXX this method needs a total overhaul! */
-static void gp_stroke_convertcoords(tGPsdata *p, const float mval[2], float out[3], float *depth)
+static void gpencil_stroke_convertcoords(tGPsdata *p,
+ const float mval[2],
+ float out[3],
+ float *depth)
{
bGPdata *gpd = p->gpd;
@@ -476,7 +478,7 @@ static void gp_stroke_convertcoords(tGPsdata *p, const float mval[2], float out[
* 3D-coordinates using the 3D-cursor as reference. In general, this
* works OK, but it could of course be improved. */
- gp_get_3d_reference(p, rvec);
+ gpencil_get_3d_reference(p, rvec);
zfac = ED_view3d_calc_zfac(p->region->regiondata, rvec, NULL);
if (ED_view3d_project_float_global(p->region, rvec, mval_prj, V3D_PROJ_TEST_NOP) ==
@@ -492,7 +494,7 @@ static void gp_stroke_convertcoords(tGPsdata *p, const float mval[2], float out[
}
/* Apply jitter to stroke point. */
-static void gp_brush_jitter(bGPdata *gpd, tGPspoint *pt, const float amplitude)
+static void gpencil_brush_jitter(bGPdata *gpd, tGPspoint *pt, const float amplitude)
{
const float axis[2] = {0.0f, 1.0f};
/* Jitter is applied perpendicular to the mouse movement vector (2D space). */
@@ -513,8 +515,8 @@ static void gp_brush_jitter(bGPdata *gpd, tGPspoint *pt, const float amplitude)
}
}
-/* apply pressure change depending of the angle of the stroke to simulate a pen with shape */
-static void gp_brush_angle(bGPdata *gpd, Brush *brush, tGPspoint *pt, const float mval[2])
+/* Apply pressure change depending of the angle of the stroke to simulate a pen with shape */
+static void gpencil_brush_angle(bGPdata *gpd, Brush *brush, tGPspoint *pt, const float mval[2])
{
float mvec[2];
float sen = brush->gpencil_settings->draw_angle_factor; /* sensitivity */
@@ -551,16 +553,17 @@ static void gp_brush_angle(bGPdata *gpd, Brush *brush, tGPspoint *pt, const floa
}
}
-/* Apply smooth to buffer while drawing
+/**
+ * Apply smooth to buffer while drawing
* to smooth point C, use 2 before (A, B) and current point (D):
*
- * A----B-----C------D
+ * `A----B-----C------D`
*
* \param p: Temp data
* \param inf: Influence factor
* \param idx: Index of the last point (need minimum 3 points in the array)
*/
-static void gp_smooth_buffer(tGPsdata *p, float inf, int idx)
+static void gpencil_smooth_buffer(tGPsdata *p, float inf, int idx)
{
bGPdata *gpd = p->gpd;
GP_Sculpt_Guide *guide = &p->scene->toolsettings->gp_sculpt.guide;
@@ -624,7 +627,7 @@ static void gp_smooth_buffer(tGPsdata *p, float inf, int idx)
}
/* Helper: Apply smooth to segment from Index to Index */
-static void gp_smooth_segment(bGPdata *gpd, const float inf, int from_idx, int to_idx)
+static void gpencil_smooth_segment(bGPdata *gpd, const float inf, int from_idx, int to_idx)
{
const short num_points = to_idx - from_idx;
/* Do nothing if not enough points to smooth out */
@@ -691,12 +694,12 @@ static void gp_smooth_segment(bGPdata *gpd, const float inf, int from_idx, int t
}
}
-static void gp_apply_randomness(tGPsdata *p,
- BrushGpencilSettings *brush_settings,
- tGPspoint *pt,
- const bool press,
- const bool strength,
- const bool uv)
+static void gpencil_apply_randomness(tGPsdata *p,
+ BrushGpencilSettings *brush_settings,
+ tGPspoint *pt,
+ const bool press,
+ const bool strength,
+ const bool uv)
{
bGPdata *gpd = p->gpd;
GpRandomSettings random_settings = p->random_settings;
@@ -764,7 +767,10 @@ static void gp_apply_randomness(tGPsdata *p,
}
/* add current stroke-point to buffer (returns whether point was successfully added) */
-static short gp_stroke_addpoint(tGPsdata *p, const float mval[2], float pressure, double curtime)
+static short gpencil_stroke_addpoint(tGPsdata *p,
+ const float mval[2],
+ float pressure,
+ double curtime)
{
bGPdata *gpd = p->gpd;
Brush *brush = p->brush;
@@ -860,16 +866,16 @@ static short gp_stroke_addpoint(tGPsdata *p, const float mval[2], float pressure
/* FIXME the +2 means minimum jitter is 4 which is a bit strange for UX. */
const float exp_factor = brush_settings->draw_jitter + 2.0f;
const float fac = rand * square_f(exp_factor) * jitpress;
- gp_brush_jitter(gpd, pt, fac);
+ gpencil_brush_jitter(gpd, pt, fac);
}
/* Apply other randomness. */
- gp_apply_randomness(p, brush_settings, pt, true, true, true);
+ gpencil_apply_randomness(p, brush_settings, pt, true, true, true);
}
/* apply angle of stroke to brush size */
if (brush_settings->draw_angle_factor != 0.0f) {
- gp_brush_angle(gpd, brush, pt, mval);
+ gpencil_brush_angle(gpd, brush, pt, mval);
}
/* point time */
@@ -882,14 +888,14 @@ static short gp_stroke_addpoint(tGPsdata *p, const float mval[2], float pressure
/* get origin to reproject point */
float origin[3];
- gp_get_3d_reference(p, origin);
+ gpencil_get_3d_reference(p, origin);
/* reproject current */
ED_gpencil_tpoint_to_point(p->region, origin, pt, &spt);
- ED_gp_project_point_to_plane(p->scene, obact, rv3d, origin, p->lock_axis - 1, &spt);
+ ED_gpencil_project_point_to_plane(p->scene, obact, rv3d, origin, p->lock_axis - 1, &spt);
/* reproject previous */
ED_gpencil_tpoint_to_point(p->region, origin, ptb, &spt2);
- ED_gp_project_point_to_plane(p->scene, obact, rv3d, origin, p->lock_axis - 1, &spt2);
+ ED_gpencil_project_point_to_plane(p->scene, obact, rv3d, origin, p->lock_axis - 1, &spt2);
p->totpixlen += len_v3v3(&spt.x, &spt2.x);
pt->uv_fac = p->totpixlen;
}
@@ -904,9 +910,9 @@ static short gp_stroke_addpoint(tGPsdata *p, const float mval[2], float pressure
/* Smooth while drawing previous points with a reduction factor for previous. */
if (brush->gpencil_settings->active_smooth > 0.0f) {
for (int s = 0; s < 3; s++) {
- gp_smooth_buffer(p,
- brush->gpencil_settings->active_smooth * ((3.0f - s) / 3.0f),
- gpd->runtime.sbuffer_used - s);
+ gpencil_smooth_buffer(p,
+ brush->gpencil_settings->active_smooth * ((3.0f - s) / 3.0f),
+ gpd->runtime.sbuffer_used - s);
}
}
@@ -920,7 +926,7 @@ static short gp_stroke_addpoint(tGPsdata *p, const float mval[2], float pressure
}
/* make a new stroke from the buffer data */
-static void gp_stroke_newfrombuffer(tGPsdata *p)
+static void gpencil_stroke_newfrombuffer(tGPsdata *p)
{
bGPdata *gpd = p->gpd;
bGPDlayer *gpl = p->gpl;
@@ -1000,7 +1006,7 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
gps->dvert = NULL;
/* drawing batch cache is dirty now */
- gp_update_cache(p->gpd);
+ gpencil_update_cache(p->gpd);
/* set pointer to first non-initialized point */
pt = gps->points + (gps->totpoints - totelem);
if (gps->dvert != NULL) {
@@ -1018,7 +1024,7 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
ptc = gpd->runtime.sbuffer;
/* convert screen-coordinates to appropriate coordinates (and store them) */
- gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL);
+ gpencil_stroke_convertcoords(p, &ptc->x, &pt->x, NULL);
/* copy pressure and time */
pt->pressure = ptc->pressure;
pt->strength = ptc->strength;
@@ -1052,7 +1058,7 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
ptc = ((tGPspoint *)gpd->runtime.sbuffer) + (gpd->runtime.sbuffer_used - 1);
/* convert screen-coordinates to appropriate coordinates (and store them) */
- gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL);
+ gpencil_stroke_convertcoords(p, &ptc->x, &pt->x, NULL);
/* copy pressure and time */
pt->pressure = ptc->pressure;
pt->strength = ptc->strength;
@@ -1077,11 +1083,11 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
}
/* reproject to plane (only in 3d space) */
- gp_reproject_toplane(p, gps);
+ gpencil_reproject_toplane(p, gps);
pt = gps->points;
for (i = 0; i < gps->totpoints; i++, pt++) {
/* if parented change position relative to parent object */
- gp_apply_parent_point(depsgraph, obact, gpl, pt);
+ gpencil_apply_parent_point(depsgraph, obact, gpl, pt);
}
/* if camera view, reproject flat to view to avoid perspective effect */
@@ -1172,7 +1178,7 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
for (i = 0, ptc = gpd->runtime.sbuffer; i < gpd->runtime.sbuffer_used && ptc;
i++, ptc++, pt++) {
/* convert screen-coordinates to appropriate coordinates (and store them) */
- gp_stroke_convertcoords(p, &ptc->x, &pt->x, depth_arr ? depth_arr + i : NULL);
+ gpencil_stroke_convertcoords(p, &ptc->x, &pt->x, depth_arr ? depth_arr + i : NULL);
/* copy pressure and time */
pt->pressure = ptc->pressure;
@@ -1194,7 +1200,7 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
/* subdivide and smooth the stroke */
if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) && (subdivide > 0)) {
- gp_subdivide_stroke(gps, subdivide);
+ gpencil_subdivide_stroke(gps, subdivide);
}
/* Smooth stroke after subdiv - only if there's something to do for each iteration,
@@ -1229,9 +1235,9 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
}
/* reproject to plane (only in 3d space) */
- gp_reproject_toplane(p, gps);
+ gpencil_reproject_toplane(p, gps);
/* change position relative to parent object */
- gp_apply_parent(depsgraph, obact, gpl, gps);
+ gpencil_apply_parent(depsgraph, obact, gpl, gps);
/* if camera view, reproject flat to view to avoid perspective effect */
if (is_camera) {
ED_gpencil_project_stroke_to_view(p->C, p->gpl, gps);
@@ -1284,12 +1290,14 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(gps);
- gp_stroke_added_enable(p);
+ gpencil_stroke_added_enable(p);
}
/* --- 'Eraser' for 'Paint' Tool ------ */
-/* which which point is infront (result should only be used for comparison) */
+/**
+ * Which which point is in front (result should only be used for comparison).
+ */
static float view3d_point_depth(const RegionView3D *rv3d, const float co[3])
{
if (rv3d->is_persp) {
@@ -1301,10 +1309,10 @@ static float view3d_point_depth(const RegionView3D *rv3d, const float co[3])
}
/* only erase stroke points that are visible */
-static bool gp_stroke_eraser_is_occluded(tGPsdata *p,
- const bGPDspoint *pt,
- const int x,
- const int y)
+static bool gpencil_stroke_eraser_is_occluded(tGPsdata *p,
+ const bGPDspoint *pt,
+ const int x,
+ const int y)
{
Object *obact = (Object *)p->ownerPtr.data;
Brush *brush = p->brush;
@@ -1346,10 +1354,10 @@ static bool gp_stroke_eraser_is_occluded(tGPsdata *p,
}
/* apply a falloff effect to brush strength, based on distance */
-static float gp_stroke_eraser_calc_influence(tGPsdata *p,
- const float mval[2],
- const int radius,
- const int co[2])
+static float gpencil_stroke_eraser_calc_influence(tGPsdata *p,
+ const float mval[2],
+ const int radius,
+ const int co[2])
{
Brush *brush = p->brush;
/* Linear Falloff... */
@@ -1373,7 +1381,7 @@ static float gp_stroke_eraser_calc_influence(tGPsdata *p,
}
/* helper to free a stroke */
-static void gp_free_stroke(bGPdata *gpd, bGPDframe *gpf, bGPDstroke *gps)
+static void gpencil_free_stroke(bGPdata *gpd, bGPDframe *gpf, bGPDstroke *gps)
{
if (gps->points) {
MEM_freeN(gps->points);
@@ -1388,7 +1396,7 @@ static void gp_free_stroke(bGPdata *gpd, bGPDframe *gpf, bGPDstroke *gps)
MEM_freeN(gps->triangles);
}
BLI_freelinkN(&gpf->strokes, gps);
- gp_update_cache(gpd);
+ gpencil_update_cache(gpd);
}
/**
@@ -1396,7 +1404,7 @@ static void gp_free_stroke(bGPdata *gpd, bGPDframe *gpf, bGPDstroke *gps)
* to avoid that segments gets the end points rounded.
* The round caps breaks the artistic effect.
*/
-static void gp_stroke_soft_refine(bGPDstroke *gps)
+static void gpencil_stroke_soft_refine(bGPDstroke *gps)
{
bGPDspoint *pt = NULL;
bGPDspoint *pt2 = NULL;
@@ -1431,12 +1439,12 @@ static void gp_stroke_soft_refine(bGPDstroke *gps)
}
/* eraser tool - evaluation per stroke */
-static void gp_stroke_eraser_dostroke(tGPsdata *p,
- bGPDframe *gpf,
- bGPDstroke *gps,
- const float mval[2],
- const int radius,
- const rcti *rect)
+static void gpencil_stroke_eraser_dostroke(tGPsdata *p,
+ bGPDframe *gpf,
+ bGPDstroke *gps,
+ const float mval[2],
+ const int radius,
+ const rcti *rect)
{
Brush *eraser = p->eraser;
bGPDspoint *pt0, *pt1, *pt2;
@@ -1449,20 +1457,20 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p,
if (gps->totpoints == 0) {
/* just free stroke */
- gp_free_stroke(p->gpd, gpf, gps);
+ gpencil_free_stroke(p->gpd, gpf, gps);
}
else if (gps->totpoints == 1) {
/* only process if it hasn't been masked out... */
if (!(p->flags & GP_PAINTFLAG_SELECTMASK) || (gps->points->flag & GP_SPOINT_SELECT)) {
bGPDspoint pt_temp;
- gp_point_to_parent_space(gps->points, p->diff_mat, &pt_temp);
- gp_point_to_xy(&p->gsc, gps, &pt_temp, &pc1[0], &pc1[1]);
+ gpencil_point_to_parent_space(gps->points, p->diff_mat, &pt_temp);
+ gpencil_point_to_xy(&p->gsc, gps, &pt_temp, &pc1[0], &pc1[1]);
/* do boundbox check first */
if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) {
/* only check if point is inside */
if (len_v2v2_int(mval_i, pc1) <= radius) {
/* free stroke */
- gp_free_stroke(p->gpd, gpf, gps);
+ gpencil_free_stroke(p->gpd, gpf, gps);
}
}
}
@@ -1479,15 +1487,15 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p,
/* get points to work with */
pt1 = gps->points + i;
bGPDspoint npt;
- gp_point_to_parent_space(pt1, p->diff_mat, &npt);
- gp_point_to_xy(&p->gsc, gps, &npt, &pc1[0], &pc1[1]);
+ gpencil_point_to_parent_space(pt1, p->diff_mat, &npt);
+ gpencil_point_to_xy(&p->gsc, gps, &npt, &pc1[0], &pc1[1]);
/* do boundbox check first */
if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) {
/* only check if point is inside */
if (len_v2v2_int(mval_i, pc1) <= radius) {
/* free stroke */
- gp_free_stroke(p->gpd, gpf, gps);
+ gpencil_free_stroke(p->gpd, gpf, gps);
return;
}
}
@@ -1531,19 +1539,19 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p,
bGPDspoint npt;
if (pt0) {
- gp_point_to_parent_space(pt0, p->diff_mat, &npt);
- gp_point_to_xy(&p->gsc, gps, &npt, &pc0[0], &pc0[1]);
+ gpencil_point_to_parent_space(pt0, p->diff_mat, &npt);
+ gpencil_point_to_xy(&p->gsc, gps, &npt, &pc0[0], &pc0[1]);
}
else {
/* avoid null values */
copy_v2_v2_int(pc0, pc1);
}
- gp_point_to_parent_space(pt1, p->diff_mat, &npt);
- gp_point_to_xy(&p->gsc, gps, &npt, &pc1[0], &pc1[1]);
+ gpencil_point_to_parent_space(pt1, p->diff_mat, &npt);
+ gpencil_point_to_xy(&p->gsc, gps, &npt, &pc1[0], &pc1[1]);
- gp_point_to_parent_space(pt2, p->diff_mat, &npt);
- gp_point_to_xy(&p->gsc, gps, &npt, &pc2[0], &pc2[1]);
+ gpencil_point_to_parent_space(pt2, p->diff_mat, &npt);
+ gpencil_point_to_xy(&p->gsc, gps, &npt, &pc2[0], &pc2[1]);
/* Check that point segment of the boundbox of the eraser stroke */
if (((!ELEM(V2D_IS_CLIPPED, pc0[0], pc0[1])) && BLI_rcti_isect_pt(rect, pc0[0], pc0[1])) ||
@@ -1553,10 +1561,10 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p,
* eraser region (either within stroke painted, or on its lines)
* - this assumes that linewidth is irrelevant
*/
- if (gp_stroke_inside_circle(mval, radius, pc0[0], pc0[1], pc2[0], pc2[1])) {
- if ((gp_stroke_eraser_is_occluded(p, pt0, pc0[0], pc0[1]) == false) ||
- (gp_stroke_eraser_is_occluded(p, pt1, pc1[0], pc1[1]) == false) ||
- (gp_stroke_eraser_is_occluded(p, pt2, pc2[0], pc2[1]) == false)) {
+ if (gpencil_stroke_inside_circle(mval, radius, pc0[0], pc0[1], pc2[0], pc2[1])) {
+ if ((gpencil_stroke_eraser_is_occluded(p, pt0, pc0[0], pc0[1]) == false) ||
+ (gpencil_stroke_eraser_is_occluded(p, pt1, pc1[0], pc1[1]) == false) ||
+ (gpencil_stroke_eraser_is_occluded(p, pt2, pc2[0], pc2[1]) == false)) {
/* Point is affected: */
/* Adjust thickness
* - Influence of eraser falls off with distance from the middle of the eraser
@@ -1570,18 +1578,18 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p,
float influence = 0.0f;
if (pt0) {
- influence = gp_stroke_eraser_calc_influence(p, mval, radius, pc0);
+ influence = gpencil_stroke_eraser_calc_influence(p, mval, radius, pc0);
pt0->strength -= influence * strength * f_strength * 0.5f;
CLAMP_MIN(pt0->strength, 0.0f);
pt0->pressure -= influence * strength * f_thickness * 0.5f;
}
- influence = gp_stroke_eraser_calc_influence(p, mval, radius, pc1);
+ influence = gpencil_stroke_eraser_calc_influence(p, mval, radius, pc1);
pt1->strength -= influence * strength * f_strength;
CLAMP_MIN(pt1->strength, 0.0f);
pt1->pressure -= influence * strength * f_thickness;
- influence = gp_stroke_eraser_calc_influence(p, mval, radius, pc2);
+ influence = gpencil_stroke_eraser_calc_influence(p, mval, radius, pc2);
pt2->strength -= influence * strength * f_strength * 0.5f;
CLAMP_MIN(pt2->strength, 0.0f);
pt2->pressure -= influence * strength * f_thickness * 0.5f;
@@ -1604,9 +1612,10 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p,
}
}
else {
- pt1->pressure -= gp_stroke_eraser_calc_influence(p, mval, radius, pc1) * strength;
- pt2->pressure -= gp_stroke_eraser_calc_influence(p, mval, radius, pc2) * strength *
- 0.5f;
+ pt1->pressure -= gpencil_stroke_eraser_calc_influence(p, mval, radius, pc1) *
+ strength;
+ pt2->pressure -= gpencil_stroke_eraser_calc_influence(p, mval, radius, pc2) *
+ strength * 0.5f;
}
/* 2) Tag any point with overly low influence for removal in the next pass */
@@ -1630,17 +1639,17 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p,
/* if soft eraser, must analyze points to be sure the stroke ends
* don't get rounded */
if (eraser->gpencil_settings->eraser_mode == GP_BRUSH_ERASER_SOFT) {
- gp_stroke_soft_refine(gps);
+ gpencil_stroke_soft_refine(gps);
}
- gp_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_TAG, false, 0);
+ gpencil_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_TAG, false, 0);
}
- gp_update_cache(p->gpd);
+ gpencil_update_cache(p->gpd);
}
}
/* erase strokes which fall under the eraser strokes */
-static void gp_stroke_doeraser(tGPsdata *p)
+static void gpencil_stroke_doeraser(tGPsdata *p)
{
rcti rect;
Brush *brush = p->brush;
@@ -1710,7 +1719,7 @@ static void gp_stroke_doeraser(tGPsdata *p)
* (e.g. 2D space strokes in the 3D view, if the same datablock is shared)
*/
if (ED_gpencil_stroke_can_use_direct(p->area, gps)) {
- gp_stroke_eraser_dostroke(p, gpf, gps, p->mval, calc_radius, &rect);
+ gpencil_stroke_eraser_dostroke(p, gpf, gps, p->mval, calc_radius, &rect);
}
}
}
@@ -1720,7 +1729,7 @@ static void gp_stroke_doeraser(tGPsdata *p)
/* Sketching Operator */
/* clear the session buffers (call this before AND after a paint operation) */
-static void gp_session_validatebuffer(tGPsdata *p)
+static void gpencil_session_validatebuffer(tGPsdata *p)
{
bGPdata *gpd = p->gpd;
Brush *brush = p->brush;
@@ -1742,7 +1751,7 @@ static void gp_session_validatebuffer(tGPsdata *p)
}
/* helper to get default eraser and create one if no eraser brush */
-static Brush *gp_get_default_eraser(Main *bmain, ToolSettings *ts)
+static Brush *gpencil_get_default_eraser(Main *bmain, ToolSettings *ts)
{
Brush *brush_dft = NULL;
Paint *paint = &ts->gp_paint->paint;
@@ -1784,7 +1793,7 @@ static Brush *gp_get_default_eraser(Main *bmain, ToolSettings *ts)
}
/* helper to set default eraser and disable others */
-static void gp_set_default_eraser(Main *bmain, Brush *brush_dft)
+static void gpencil_set_default_eraser(Main *bmain, Brush *brush_dft)
{
if (brush_dft == NULL) {
return;
@@ -1803,7 +1812,7 @@ static void gp_set_default_eraser(Main *bmain, Brush *brush_dft)
}
/* initialize a drawing brush */
-static void gp_init_drawing_brush(bContext *C, tGPsdata *p)
+static void gpencil_init_drawing_brush(bContext *C, tGPsdata *p)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
@@ -1831,13 +1840,13 @@ static void gp_init_drawing_brush(bContext *C, tGPsdata *p)
/* assign to temp tGPsdata */
p->brush = paint->brush;
if (paint->brush->gpencil_tool != GPAINT_TOOL_ERASE) {
- p->eraser = gp_get_default_eraser(p->bmain, ts);
+ p->eraser = gpencil_get_default_eraser(p->bmain, ts);
}
else {
p->eraser = paint->brush;
}
/* set new eraser as default */
- gp_set_default_eraser(p->bmain, p->eraser);
+ gpencil_set_default_eraser(p->bmain, p->eraser);
/* use radius of eraser */
p->radius = (short)p->eraser->size;
@@ -1849,7 +1858,7 @@ static void gp_init_drawing_brush(bContext *C, tGPsdata *p)
}
/* initialize a paint brush and a default color if not exist */
-static void gp_init_colors(tGPsdata *p)
+static void gpencil_init_colors(tGPsdata *p)
{
bGPdata *gpd = p->gpd;
Brush *brush = p->brush;
@@ -1862,7 +1871,7 @@ static void gp_init_colors(tGPsdata *p)
}
/* (re)init new painting data */
-static bool gp_session_initdata(bContext *C, wmOperator *op, tGPsdata *p)
+static bool gpencil_session_initdata(bContext *C, wmOperator *op, tGPsdata *p)
{
Main *bmain = CTX_data_main(C);
bGPdata **gpd_ptr = NULL;
@@ -1959,15 +1968,15 @@ static bool gp_session_initdata(bContext *C, wmOperator *op, tGPsdata *p)
}
/* clear out buffer (stored in gp-data), in case something contaminated it */
- gp_session_validatebuffer(p);
+ gpencil_session_validatebuffer(p);
/* set brush and create a new one if null */
- gp_init_drawing_brush(C, p);
+ gpencil_init_drawing_brush(C, p);
/* setup active color */
/* region where paint was originated */
int totcol = p->ob->totcol;
- gp_init_colors(p);
+ gpencil_init_colors(p);
/* check whether the material was newly added */
if (totcol != p->ob->totcol) {
@@ -1987,7 +1996,7 @@ static bool gp_session_initdata(bContext *C, wmOperator *op, tGPsdata *p)
}
/* init new painting session */
-static tGPsdata *gp_session_initpaint(bContext *C, wmOperator *op)
+static tGPsdata *gpencil_session_initpaint(bContext *C, wmOperator *op)
{
tGPsdata *p = NULL;
@@ -1997,7 +2006,7 @@ static tGPsdata *gp_session_initpaint(bContext *C, wmOperator *op)
/* Try to initialize context data
* WARNING: This may not always succeed (e.g. using GP in an annotation-only context)
*/
- if (gp_session_initdata(C, op, p) == 0) {
+ if (gpencil_session_initdata(C, op, p) == 0) {
/* Invalid state - Exit
* NOTE: It should be safe to just free the data, since failing context checks should
* only happen when no data has been allocated.
@@ -2016,7 +2025,7 @@ static tGPsdata *gp_session_initpaint(bContext *C, wmOperator *op)
}
/* cleanup after a painting session */
-static void gp_session_cleanup(tGPsdata *p)
+static void gpencil_session_cleanup(tGPsdata *p)
{
bGPdata *gpd = (p) ? p->gpd : NULL;
@@ -2038,7 +2047,7 @@ static void gp_session_cleanup(tGPsdata *p)
p->inittime = 0.0;
}
-static void gp_session_free(tGPsdata *p)
+static void gpencil_session_free(tGPsdata *p)
{
if (p->rng != NULL) {
BLI_rng_free(p->rng);
@@ -2048,7 +2057,9 @@ static void gp_session_free(tGPsdata *p)
}
/* init new stroke */
-static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Depsgraph *depsgraph)
+static void gpencil_paint_initstroke(tGPsdata *p,
+ eGPencil_PaintModes paintmode,
+ Depsgraph *depsgraph)
{
Scene *scene = p->scene;
ToolSettings *ts = scene->toolsettings;
@@ -2209,12 +2220,12 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Deps
gpd_eval->runtime.sbuffer_brush = p->gpd->runtime.sbuffer_brush;
}
else {
- gp_update_cache(p->gpd);
+ gpencil_update_cache(p->gpd);
}
}
/* finish off a stroke (clears buffer, but doesn't finish the paint operation) */
-static void gp_paint_strokeend(tGPsdata *p)
+static void gpencil_paint_strokeend(tGPsdata *p)
{
ToolSettings *ts = p->scene->toolsettings;
/* for surface sketching, need to set the right OpenGL context stuff so that
@@ -2232,22 +2243,22 @@ static void gp_paint_strokeend(tGPsdata *p)
/* check if doing eraser or not */
if ((p->gpd->runtime.sbuffer_sflag & GP_STROKE_ERASER) == 0) {
/* transfer stroke to frame */
- gp_stroke_newfrombuffer(p);
+ gpencil_stroke_newfrombuffer(p);
}
/* clean up buffer now */
- gp_session_validatebuffer(p);
+ gpencil_session_validatebuffer(p);
}
/* finish off stroke painting operation */
-static void gp_paint_cleanup(tGPsdata *p)
+static void gpencil_paint_cleanup(tGPsdata *p)
{
/* p->gpd==NULL happens when stroke failed to initialize,
* for example when GP is hidden in current space (sergey)
*/
if (p->gpd) {
/* finish off a stroke */
- gp_paint_strokeend(p);
+ gpencil_paint_strokeend(p);
}
/* "unlock" frame */
@@ -2357,19 +2368,19 @@ static void gpencil_draw_exit(bContext *C, wmOperator *op)
else {
/* drawing batch cache is dirty now */
bGPdata *gpd = CTX_data_gpencil_data(C);
- gp_update_cache(gpd);
+ gpencil_update_cache(gpd);
}
/* clear undo stack */
gpencil_undo_finish();
/* cleanup */
- gp_paint_cleanup(p);
- gp_session_cleanup(p);
+ gpencil_paint_cleanup(p);
+ gpencil_session_cleanup(p);
ED_gpencil_toggle_brush_cursor(C, true, NULL);
/* finally, free the temp data */
- gp_session_free(p);
+ gpencil_session_free(p);
p = NULL;
}
@@ -2399,7 +2410,7 @@ static int gpencil_draw_init(bContext *C, wmOperator *op, const wmEvent *event)
}
/* check context */
- p = op->customdata = gp_session_initpaint(C, op);
+ p = op->customdata = gpencil_session_initpaint(C, op);
if ((p == NULL) || (p->status == GP_STATUS_ERROR)) {
/* something wasn't set correctly in context */
gpencil_draw_exit(C, op);
@@ -2407,7 +2418,7 @@ static int gpencil_draw_init(bContext *C, wmOperator *op, const wmEvent *event)
}
/* init painting data */
- gp_paint_initstroke(p, paintmode, CTX_data_ensure_evaluated_depsgraph(C));
+ gpencil_paint_initstroke(p, paintmode, CTX_data_ensure_evaluated_depsgraph(C));
if (p->status == GP_STATUS_ERROR) {
gpencil_draw_exit(C, op);
return 0;
@@ -2493,10 +2504,10 @@ static void gpencil_draw_status_indicators(bContext *C, tGPsdata *p)
/* ------------------------------- */
/* Helper to rotate point around origin */
-static void gp_rotate_v2_v2v2fl(float v[2],
- const float p[2],
- const float origin[2],
- const float angle)
+static void gpencil_rotate_v2_v2v2fl(float v[2],
+ const float p[2],
+ const float origin[2],
+ const float angle)
{
float pt[2];
float r[2];
@@ -2506,7 +2517,7 @@ static void gp_rotate_v2_v2v2fl(float v[2],
}
/* Helper to snap value to grid */
-static float gp_snap_to_grid_fl(float v, const float offset, const float spacing)
+static float gpencil_snap_to_grid_fl(float v, const float offset, const float spacing)
{
if (spacing > 0.0f) {
v -= spacing * 0.5f;
@@ -2521,25 +2532,25 @@ static float gp_snap_to_grid_fl(float v, const float offset, const float spacing
}
/* Helper to snap value to grid */
-static void gp_snap_to_rotated_grid_fl(float v[2],
- const float origin[2],
- const float spacing,
- const float angle)
+static void gpencil_snap_to_rotated_grid_fl(float v[2],
+ const float origin[2],
+ const float spacing,
+ const float angle)
{
- gp_rotate_v2_v2v2fl(v, v, origin, -angle);
- v[1] = gp_snap_to_grid_fl(v[1], origin[1], spacing);
- gp_rotate_v2_v2v2fl(v, v, origin, angle);
+ gpencil_rotate_v2_v2v2fl(v, v, origin, -angle);
+ v[1] = gpencil_snap_to_grid_fl(v[1], origin[1], spacing);
+ gpencil_rotate_v2_v2v2fl(v, v, origin, angle);
}
/* get reference point - screen coords to buffer coords */
-static void gp_origin_set(wmOperator *op, const int mval[2])
+static void gpencil_origin_set(wmOperator *op, const int mval[2])
{
tGPsdata *p = op->customdata;
GP_Sculpt_Guide *guide = &p->scene->toolsettings->gp_sculpt.guide;
float origin[2];
float point[3];
copy_v2fl_v2i(origin, mval);
- gp_stroke_convertcoords(p, origin, point, NULL);
+ gpencil_stroke_convertcoords(p, origin, point, NULL);
if (guide->reference_point == GP_GUIDE_REF_CUSTOM) {
copy_v3_v3(guide->location, point);
}
@@ -2549,7 +2560,7 @@ static void gp_origin_set(wmOperator *op, const int mval[2])
}
/* get reference point - buffer coords to screen coords */
-static void gp_origin_get(tGPsdata *p, float origin[2])
+static void gpencil_origin_get(tGPsdata *p, float origin[2])
{
GP_Sculpt_Guide *guide = &p->scene->toolsettings->gp_sculpt.guide;
float location[3];
@@ -2563,7 +2574,7 @@ static void gp_origin_get(tGPsdata *p, float origin[2])
copy_v3_v3(location, p->scene->cursor.location);
}
GP_SpaceConversion *gsc = &p->gsc;
- gp_point_3d_to_xy(gsc, p->gpd->runtime.sbuffer_sflag, location, origin);
+ gpencil_point_3d_to_xy(gsc, p->gpd->runtime.sbuffer_sflag, location, origin);
}
/* speed guide initial values */
@@ -2574,7 +2585,7 @@ static void gpencil_speed_guide_init(tGPsdata *p, GP_Sculpt_Guide *guide)
float scale = 1.0f;
if (rv3d->is_persp) {
float vec[3];
- gp_get_3d_reference(p, vec);
+ gpencil_get_3d_reference(p, vec);
mul_m4_v3(rv3d->persmat, vec);
scale = vec[2] * rv3d->pixsize;
}
@@ -2583,7 +2594,7 @@ static void gpencil_speed_guide_init(tGPsdata *p, GP_Sculpt_Guide *guide)
}
p->guide.spacing = guide->spacing / scale;
p->guide.half_spacing = p->guide.spacing * 0.5f;
- gp_origin_get(p, p->guide.origin);
+ gpencil_origin_get(p, p->guide.origin);
/* reference for angled snap */
copy_v2_v2(p->guide.unit, p->mvali);
@@ -2595,7 +2606,7 @@ static void gpencil_speed_guide_init(tGPsdata *p, GP_Sculpt_Guide *guide)
p->guide.origin_distance = len_v2v2(p->mvali, p->guide.origin);
if (guide->use_snapping && (guide->spacing > 0.0f)) {
- p->guide.origin_distance = gp_snap_to_grid_fl(
+ p->guide.origin_distance = gpencil_snap_to_grid_fl(
p->guide.origin_distance, 0.0f, p->guide.spacing);
}
@@ -2605,10 +2616,10 @@ static void gpencil_speed_guide_init(tGPsdata *p, GP_Sculpt_Guide *guide)
angle = p->guide.origin_angle + guide->angle;
angle = fmodf(angle + half_angle, guide->angle_snap);
angle -= half_angle;
- gp_rotate_v2_v2v2fl(p->guide.rot_point, p->mvali, p->guide.origin, -angle);
+ gpencil_rotate_v2_v2v2fl(p->guide.rot_point, p->mvali, p->guide.origin, -angle);
}
else {
- gp_rotate_v2_v2v2fl(p->guide.rot_point, p->guide.unit, p->mvali, guide->angle);
+ gpencil_rotate_v2_v2v2fl(p->guide.rot_point, p->guide.unit, p->mvali, guide->angle);
}
}
@@ -2633,14 +2644,15 @@ static void gpencil_snap_to_guide(const tGPsdata *p, const GP_Sculpt_Guide *guid
case GP_GUIDE_PARALLEL: {
closest_to_line_v2(point, point, p->mvali, p->guide.rot_point);
if (guide->use_snapping && (guide->spacing > 0.0f)) {
- gp_snap_to_rotated_grid_fl(point, p->guide.origin, p->guide.spacing, guide->angle);
+ gpencil_snap_to_rotated_grid_fl(point, p->guide.origin, p->guide.spacing, guide->angle);
}
break;
}
case GP_GUIDE_ISO: {
closest_to_line_v2(point, point, p->mvali, p->guide.rot_point);
if (guide->use_snapping && (guide->spacing > 0.0f)) {
- gp_snap_to_rotated_grid_fl(point, p->guide.origin, p->guide.spacing, p->guide.rot_angle);
+ gpencil_snap_to_rotated_grid_fl(
+ point, p->guide.origin, p->guide.spacing, p->guide.rot_angle);
}
break;
}
@@ -2648,10 +2660,10 @@ static void gpencil_snap_to_guide(const tGPsdata *p, const GP_Sculpt_Guide *guid
if (guide->use_snapping && (guide->spacing > 0.0f)) {
closest_to_line_v2(point, point, p->mvali, p->guide.rot_point);
if (p->straight == STROKE_HORIZONTAL) {
- point[1] = gp_snap_to_grid_fl(point[1], p->guide.origin[1], p->guide.spacing);
+ point[1] = gpencil_snap_to_grid_fl(point[1], p->guide.origin[1], p->guide.spacing);
}
else {
- point[0] = gp_snap_to_grid_fl(point[0], p->guide.origin[0], p->guide.spacing);
+ point[0] = gpencil_snap_to_grid_fl(point[0], p->guide.origin[0], p->guide.spacing);
}
}
else if (p->straight == STROKE_HORIZONTAL) {
@@ -2674,7 +2686,7 @@ static void gpencil_draw_apply(bContext *C, wmOperator *op, tGPsdata *p, Depsgra
/* handle drawing/erasing -> test for erasing first */
if (p->paintmode == GP_PAINTMODE_ERASER) {
/* do 'live' erasing now */
- gp_stroke_doeraser(p);
+ gpencil_stroke_doeraser(p);
/* store used values */
copy_v2_v2(p->mvalo, p->mval);
@@ -2682,7 +2694,7 @@ static void gpencil_draw_apply(bContext *C, wmOperator *op, tGPsdata *p, Depsgra
}
/* Only add current point to buffer if mouse moved
* (even though we got an event, it might be just noise). */
- else if (gp_stroke_filtermval(p, p->mval, p->mvalo)) {
+ else if (gpencil_stroke_filtermval(p, p->mval, p->mvalo)) {
/* if lazy mouse, interpolate the last and current mouse positions */
if (GPENCIL_LAZY_MODE(p->brush, p->shift)) {
@@ -2702,26 +2714,26 @@ static void gpencil_draw_apply(bContext *C, wmOperator *op, tGPsdata *p, Depsgra
}
/* try to add point */
- short ok = gp_stroke_addpoint(p, p->mval, p->pressure, p->curtime);
+ short ok = gpencil_stroke_addpoint(p, p->mval, p->pressure, p->curtime);
/* handle errors while adding point */
if ((ok == GP_STROKEADD_FULL) || (ok == GP_STROKEADD_OVERFLOW)) {
/* finish off old stroke */
- gp_paint_strokeend(p);
+ gpencil_paint_strokeend(p);
/* And start a new one!!! Else, projection errors! */
- gp_paint_initstroke(p, p->paintmode, depsgraph);
+ gpencil_paint_initstroke(p, p->paintmode, depsgraph);
/* start a new stroke, starting from previous point */
/* XXX Must manually reset inittime... */
/* XXX We only need to reuse previous point if overflow! */
if (ok == GP_STROKEADD_OVERFLOW) {
p->inittime = p->ocurtime;
- gp_stroke_addpoint(p, p->mvalo, p->opressure, p->ocurtime);
+ gpencil_stroke_addpoint(p, p->mvalo, p->opressure, p->ocurtime);
}
else {
p->inittime = p->curtime;
}
- gp_stroke_addpoint(p, p->mval, p->pressure, p->curtime);
+ gpencil_stroke_addpoint(p, p->mval, p->pressure, p->curtime);
}
else if (ok == GP_STROKEADD_INVALID) {
/* the painting operation cannot continue... */
@@ -2889,13 +2901,13 @@ static void gpencil_draw_apply_event(bContext *C,
else {
p->guide.rot_angle = DEG2RAD(90);
}
- gp_rotate_v2_v2v2fl(p->guide.rot_point, p->guide.unit, p->mvali, p->guide.rot_angle);
+ gpencil_rotate_v2_v2v2fl(p->guide.rot_point, p->guide.unit, p->mvali, p->guide.rot_angle);
}
else if (ELEM(guide->type, GP_GUIDE_GRID)) {
- gp_rotate_v2_v2v2fl(p->guide.rot_point,
- p->guide.unit,
- p->mvali,
- (p->straight == STROKE_VERTICAL) ? M_PI_2 : 0.0f);
+ gpencil_rotate_v2_v2v2fl(p->guide.rot_point,
+ p->guide.unit,
+ p->mvali,
+ (p->straight == STROKE_VERTICAL) ? M_PI_2 : 0.0f);
}
}
@@ -2968,8 +2980,8 @@ static int gpencil_draw_exec(bContext *C, wmOperator *op)
*/
if ((p->flags & GP_PAINTFLAG_FIRSTRUN) == 0) {
/* TODO: both of these ops can set error-status, but we probably don't need to worry */
- gp_paint_strokeend(p);
- gp_paint_initstroke(p, p->paintmode, depsgraph);
+ gpencil_paint_strokeend(p);
+ gpencil_paint_initstroke(p, p->paintmode, depsgraph);
}
}
@@ -3227,8 +3239,8 @@ static tGPsdata *gpencil_stroke_begin(bContext *C, wmOperator *op)
}
/* we may need to set up paint env again if we're resuming */
- if (gp_session_initdata(C, op, p)) {
- gp_paint_initstroke(p, p->paintmode, CTX_data_depsgraph_pointer(C));
+ if (gpencil_session_initdata(C, op, p)) {
+ gpencil_paint_initstroke(p, p->paintmode, CTX_data_depsgraph_pointer(C));
}
if (p->status != GP_STATUS_ERROR) {
@@ -3240,7 +3252,7 @@ static tGPsdata *gpencil_stroke_begin(bContext *C, wmOperator *op)
}
/* Apply pressure change depending of the angle of the stroke for a segment. */
-static void gp_brush_angle_segment(tGPsdata *p, tGPspoint *pt_prev, tGPspoint *pt)
+static void gpencil_brush_angle_segment(tGPsdata *p, tGPspoint *pt_prev, tGPspoint *pt)
{
Brush *brush = p->brush;
/* Sensitivity. */
@@ -3284,7 +3296,7 @@ static void gpencil_add_arc_points(tGPsdata *p, float mval[2], int segments)
/* Apply other randomness to first points. */
for (int i = 0; i < gpd->runtime.sbuffer_used; i++) {
tGPspoint *pt = &points[i];
- gp_apply_randomness(p, brush_settings, pt, false, false, true);
+ gpencil_apply_randomness(p, brush_settings, pt, false, false, true);
}
return;
}
@@ -3359,14 +3371,14 @@ static void gpencil_add_arc_points(tGPsdata *p, float mval[2], int segments)
/* Apply angle of stroke to brush size to interpolated points but slightly attenuated.. */
if (brush_settings->draw_angle_factor != 0.0f) {
- gp_brush_angle_segment(p, pt_step, pt);
+ gpencil_brush_angle_segment(p, pt_step, pt);
CLAMP(pt->pressure, pt_prev->pressure * 0.5f, 1.0f);
/* Use the previous interpolated point for next segment. */
pt_step = pt;
}
/* Apply other randomness. */
- gp_apply_randomness(p, brush_settings, pt, false, false, true);
+ gpencil_apply_randomness(p, brush_settings, pt, false, false, true);
a += step;
}
@@ -3411,7 +3423,7 @@ static void gpencil_add_guide_points(const tGPsdata *p,
for (int i = 0; i < segments; i++) {
pt = &points[idx_prev + i - 1];
- gp_rotate_v2_v2v2fl(&pt->x, start, p->guide.origin, -a);
+ gpencil_rotate_v2_v2v2fl(&pt->x, start, p->guide.origin, -a);
gpencil_snap_to_guide(p, guide, &pt->x);
a += step;
@@ -3547,7 +3559,7 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
/* set */
case LEFTMOUSE: {
if (ELEM(event->val, KM_RELEASE)) {
- gp_origin_set(op, event->mval);
+ gpencil_origin_set(op, event->mval);
drawmode = true;
}
break;
@@ -3750,7 +3762,7 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
* the 0.15 value gets a good result in Windows and Linux. */
if (!is_speed_guide && (size_after - size_before > 1)) {
for (int r = 0; r < 5; r++) {
- gp_smooth_segment(p->gpd, 0.15f, size_before - 1, size_after - 1);
+ gpencil_smooth_segment(p->gpd, 0.15f, size_before - 1, size_after - 1);
}
}
diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c
index 82ae99b30be..6bb9f9628fa 100644
--- a/source/blender/editors/gpencil/gpencil_primitive.c
+++ b/source/blender/editors/gpencil/gpencil_primitive.c
@@ -111,7 +111,7 @@
/* Core/Shared Utilities */
/* clear the session buffers (call this before AND after a paint operation) */
-static void gp_session_validatebuffer(tGPDprimitive *p)
+static void gpencil_session_validatebuffer(tGPDprimitive *p)
{
bGPdata *gpd = p->gpd;
@@ -137,7 +137,7 @@ static void gp_session_validatebuffer(tGPDprimitive *p)
}
}
-static void gp_init_colors(tGPDprimitive *p)
+static void gpencil_init_colors(tGPDprimitive *p)
{
bGPdata *gpd = p->gpd;
Brush *brush = p->brush;
@@ -196,10 +196,10 @@ static void gpencil_primitive_constrain(tGPDprimitive *tgpi, bool line_mode)
}
/* Helper to rotate point around origin */
-static void gp_rotate_v2_v2v2fl(float v[2],
- const float p[2],
- const float origin[2],
- const float angle)
+static void gpencil_rotate_v2_v2v2fl(float v[2],
+ const float p[2],
+ const float origin[2],
+ const float angle)
{
float pt[2];
float r[2];
@@ -209,17 +209,17 @@ static void gp_rotate_v2_v2v2fl(float v[2],
}
/* Helper to rotate line around line center. */
-static void gp_primitive_rotate_line(
+static void gpencil_primitive_rotate_line(
float va[2], float vb[2], const float a[2], const float b[2], const float angle)
{
float midpoint[2];
mid_v2_v2v2(midpoint, a, b);
- gp_rotate_v2_v2v2fl(va, a, midpoint, angle);
- gp_rotate_v2_v2v2fl(vb, b, midpoint, angle);
+ gpencil_rotate_v2_v2v2fl(va, a, midpoint, angle);
+ gpencil_rotate_v2_v2v2fl(vb, b, midpoint, angle);
}
/* Helper to update cps */
-static void gp_primitive_update_cps(tGPDprimitive *tgpi)
+static void gpencil_primitive_update_cps(tGPDprimitive *tgpi)
{
if (!tgpi->curve) {
mid_v2_v2v2(tgpi->midpoint, tgpi->start, tgpi->end);
@@ -233,10 +233,10 @@ static void gp_primitive_update_cps(tGPDprimitive *tgpi)
}
else if (tgpi->type == GP_STROKE_ARC) {
if (tgpi->flip) {
- gp_primitive_rotate_line(tgpi->cp1, tgpi->cp2, tgpi->start, tgpi->end, M_PI_2);
+ gpencil_primitive_rotate_line(tgpi->cp1, tgpi->cp2, tgpi->start, tgpi->end, M_PI_2);
}
else {
- gp_primitive_rotate_line(tgpi->cp1, tgpi->cp2, tgpi->end, tgpi->start, M_PI_2);
+ gpencil_primitive_rotate_line(tgpi->cp1, tgpi->cp2, tgpi->end, tgpi->start, M_PI_2);
}
}
}
@@ -294,7 +294,7 @@ static void gpencil_primitive_allocate_memory(tGPDprimitive *tgpi)
/* ****************** Primitive Interactive *********************** */
/* Helper: Create internal strokes primitives data */
-static void gp_primitive_set_initdata(bContext *C, tGPDprimitive *tgpi)
+static void gpencil_primitive_set_initdata(bContext *C, tGPDprimitive *tgpi)
{
Scene *scene = CTX_data_scene(C);
ToolSettings *ts = scene->toolsettings;
@@ -378,7 +378,7 @@ static void gpencil_primitive_add_segment(tGPDprimitive *tgpi)
}
/* Helper: set control point */
-static void gp_primitive_set_cp(tGPDprimitive *tgpi, float p[2], float color[4], int size)
+static void gpencil_primitive_set_cp(tGPDprimitive *tgpi, float p[2], float color[4], int size)
{
if (tgpi->flag == IN_PROGRESS) {
return;
@@ -500,7 +500,7 @@ static void gpencil_primitive_status_indicators(bContext *C, tGPDprimitive *tgpi
}
/* create a rectangle */
-static void gp_primitive_rectangle(tGPDprimitive *tgpi, tGPspoint *points2D)
+static void gpencil_primitive_rectangle(tGPDprimitive *tgpi, tGPspoint *points2D)
{
float coords[5][2];
@@ -531,20 +531,20 @@ static void gp_primitive_rectangle(tGPDprimitive *tgpi, tGPspoint *points2D)
mid_v2_v2v2(tgpi->midpoint, tgpi->start, tgpi->end);
float color[4];
UI_GetThemeColor4fv(TH_GIZMO_PRIMARY, color);
- gp_primitive_set_cp(tgpi, tgpi->end, color, BIG_SIZE_CTL);
+ gpencil_primitive_set_cp(tgpi, tgpi->end, color, BIG_SIZE_CTL);
if (tgpi->tot_stored_edges) {
UI_GetThemeColor4fv(TH_REDALERT, color);
- gp_primitive_set_cp(tgpi, tgpi->start, color, SMALL_SIZE_CTL);
+ gpencil_primitive_set_cp(tgpi, tgpi->start, color, SMALL_SIZE_CTL);
}
else {
- gp_primitive_set_cp(tgpi, tgpi->start, color, BIG_SIZE_CTL);
+ gpencil_primitive_set_cp(tgpi, tgpi->start, color, BIG_SIZE_CTL);
}
UI_GetThemeColor4fv(TH_REDALERT, color);
- gp_primitive_set_cp(tgpi, tgpi->midpoint, color, SMALL_SIZE_CTL);
+ gpencil_primitive_set_cp(tgpi, tgpi->midpoint, color, SMALL_SIZE_CTL);
}
/* create a line */
-static void gp_primitive_line(tGPDprimitive *tgpi, tGPspoint *points2D, bool editable)
+static void gpencil_primitive_line(tGPDprimitive *tgpi, tGPspoint *points2D, bool editable)
{
const int totpoints = (tgpi->tot_edges + tgpi->tot_stored_edges);
const float step = 1.0f / (float)(tgpi->tot_edges - 1);
@@ -559,24 +559,24 @@ static void gp_primitive_line(tGPDprimitive *tgpi, tGPspoint *points2D, bool edi
if (editable) {
float color[4];
UI_GetThemeColor4fv(TH_GIZMO_PRIMARY, color);
- gp_primitive_set_cp(tgpi, tgpi->end, color, BIG_SIZE_CTL);
+ gpencil_primitive_set_cp(tgpi, tgpi->end, color, BIG_SIZE_CTL);
if (tgpi->tot_stored_edges) {
UI_GetThemeColor4fv(TH_REDALERT, color);
- gp_primitive_set_cp(tgpi, tgpi->start, color, SMALL_SIZE_CTL);
+ gpencil_primitive_set_cp(tgpi, tgpi->start, color, SMALL_SIZE_CTL);
}
else {
- gp_primitive_set_cp(tgpi, tgpi->start, color, BIG_SIZE_CTL);
+ gpencil_primitive_set_cp(tgpi, tgpi->start, color, BIG_SIZE_CTL);
}
}
else {
float color[4];
UI_GetThemeColor4fv(TH_REDALERT, color);
- gp_primitive_set_cp(tgpi, tgpi->start, color, SMALL_SIZE_CTL);
+ gpencil_primitive_set_cp(tgpi, tgpi->start, color, SMALL_SIZE_CTL);
}
}
/* create an arc */
-static void gp_primitive_arc(tGPDprimitive *tgpi, tGPspoint *points2D)
+static void gpencil_primitive_arc(tGPDprimitive *tgpi, tGPspoint *points2D)
{
const int totpoints = (tgpi->tot_edges + tgpi->tot_stored_edges);
const float step = M_PI_2 / (float)(tgpi->tot_edges - 1);
@@ -604,20 +604,20 @@ static void gp_primitive_arc(tGPDprimitive *tgpi, tGPspoint *points2D)
}
float color[4];
UI_GetThemeColor4fv(TH_GIZMO_PRIMARY, color);
- gp_primitive_set_cp(tgpi, tgpi->end, color, BIG_SIZE_CTL);
+ gpencil_primitive_set_cp(tgpi, tgpi->end, color, BIG_SIZE_CTL);
if (tgpi->tot_stored_edges) {
UI_GetThemeColor4fv(TH_REDALERT, color);
- gp_primitive_set_cp(tgpi, tgpi->start, color, SMALL_SIZE_CTL);
+ gpencil_primitive_set_cp(tgpi, tgpi->start, color, SMALL_SIZE_CTL);
}
else {
- gp_primitive_set_cp(tgpi, tgpi->start, color, BIG_SIZE_CTL);
+ gpencil_primitive_set_cp(tgpi, tgpi->start, color, BIG_SIZE_CTL);
}
UI_GetThemeColor4fv(TH_GIZMO_SECONDARY, color);
- gp_primitive_set_cp(tgpi, tgpi->cp1, color, BIG_SIZE_CTL * 0.9f);
+ gpencil_primitive_set_cp(tgpi, tgpi->cp1, color, BIG_SIZE_CTL * 0.9f);
}
/* create a bezier */
-static void gp_primitive_bezier(tGPDprimitive *tgpi, tGPspoint *points2D)
+static void gpencil_primitive_bezier(tGPDprimitive *tgpi, tGPspoint *points2D)
{
const int totpoints = (tgpi->tot_edges + tgpi->tot_stored_edges);
const float step = 1.0f / (float)(tgpi->tot_edges - 1);
@@ -639,21 +639,21 @@ static void gp_primitive_bezier(tGPDprimitive *tgpi, tGPspoint *points2D)
}
float color[4];
UI_GetThemeColor4fv(TH_GIZMO_PRIMARY, color);
- gp_primitive_set_cp(tgpi, tgpi->end, color, BIG_SIZE_CTL);
+ gpencil_primitive_set_cp(tgpi, tgpi->end, color, BIG_SIZE_CTL);
if (tgpi->tot_stored_edges) {
UI_GetThemeColor4fv(TH_REDALERT, color);
- gp_primitive_set_cp(tgpi, tgpi->start, color, SMALL_SIZE_CTL);
+ gpencil_primitive_set_cp(tgpi, tgpi->start, color, SMALL_SIZE_CTL);
}
else {
- gp_primitive_set_cp(tgpi, tgpi->start, color, BIG_SIZE_CTL);
+ gpencil_primitive_set_cp(tgpi, tgpi->start, color, BIG_SIZE_CTL);
}
UI_GetThemeColor4fv(TH_GIZMO_SECONDARY, color);
- gp_primitive_set_cp(tgpi, tgpi->cp1, color, BIG_SIZE_CTL * 0.9f);
- gp_primitive_set_cp(tgpi, tgpi->cp2, color, BIG_SIZE_CTL * 0.9f);
+ gpencil_primitive_set_cp(tgpi, tgpi->cp1, color, BIG_SIZE_CTL * 0.9f);
+ gpencil_primitive_set_cp(tgpi, tgpi->cp2, color, BIG_SIZE_CTL * 0.9f);
}
/* create a circle */
-static void gp_primitive_circle(tGPDprimitive *tgpi, tGPspoint *points2D)
+static void gpencil_primitive_circle(tGPDprimitive *tgpi, tGPspoint *points2D)
{
const int totpoints = (tgpi->tot_edges + tgpi->tot_stored_edges);
const float step = (2.0f * M_PI) / (float)(tgpi->tot_edges);
@@ -674,14 +674,14 @@ static void gp_primitive_circle(tGPDprimitive *tgpi, tGPspoint *points2D)
}
float color[4];
UI_GetThemeColor4fv(TH_GIZMO_PRIMARY, color);
- gp_primitive_set_cp(tgpi, tgpi->end, color, BIG_SIZE_CTL);
- gp_primitive_set_cp(tgpi, tgpi->start, color, BIG_SIZE_CTL);
+ gpencil_primitive_set_cp(tgpi, tgpi->end, color, BIG_SIZE_CTL);
+ gpencil_primitive_set_cp(tgpi, tgpi->start, color, BIG_SIZE_CTL);
UI_GetThemeColor4fv(TH_REDALERT, color);
- gp_primitive_set_cp(tgpi, center, color, SMALL_SIZE_CTL);
+ gpencil_primitive_set_cp(tgpi, center, color, SMALL_SIZE_CTL);
}
/* Helper: Update shape of the stroke */
-static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi)
+static void gpencil_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi)
{
ToolSettings *ts = tgpi->scene->toolsettings;
bGPdata *gpd = tgpi->gpd;
@@ -715,30 +715,30 @@ static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi)
if (tgpi->tot_edges > 1) {
switch (tgpi->type) {
case GP_STROKE_BOX:
- gp_primitive_rectangle(tgpi, points2D);
+ gpencil_primitive_rectangle(tgpi, points2D);
break;
case GP_STROKE_LINE:
- gp_primitive_line(tgpi, points2D, true);
+ gpencil_primitive_line(tgpi, points2D, true);
break;
case GP_STROKE_POLYLINE:
- gp_primitive_line(tgpi, points2D, false);
+ gpencil_primitive_line(tgpi, points2D, false);
break;
case GP_STROKE_CIRCLE:
- gp_primitive_circle(tgpi, points2D);
+ gpencil_primitive_circle(tgpi, points2D);
break;
case GP_STROKE_ARC:
- gp_primitive_arc(tgpi, points2D);
+ gpencil_primitive_arc(tgpi, points2D);
break;
case GP_STROKE_CURVE:
- gp_primitive_bezier(tgpi, points2D);
+ gpencil_primitive_bezier(tgpi, points2D);
default:
break;
}
}
/* convert screen-coordinates to 3D coordinates */
- gp_session_validatebuffer(tgpi);
- gp_init_colors(tgpi);
+ gpencil_session_validatebuffer(tgpi);
+ gpencil_init_colors(tgpi);
if (gset->flag & GP_SCULPT_SETT_FLAG_PRIMITIVE_CURVE) {
BKE_curvemapping_initialize(ts->gp_sculpt.cur_primitive);
}
@@ -982,12 +982,12 @@ static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi)
ED_gpencil_drawing_reference_get(tgpi->scene, tgpi->ob, ts->gpencil_v3d_align, origin);
/* reproject current */
ED_gpencil_tpoint_to_point(tgpi->region, origin, tpt, &spt);
- ED_gp_project_point_to_plane(
+ ED_gpencil_project_point_to_plane(
tgpi->scene, tgpi->ob, tgpi->rv3d, origin, tgpi->lock_axis - 1, &spt);
/* reproject previous */
ED_gpencil_tpoint_to_point(tgpi->region, origin, tptb, &spt2);
- ED_gp_project_point_to_plane(
+ ED_gpencil_project_point_to_plane(
tgpi->scene, tgpi->ob, tgpi->rv3d, origin, tgpi->lock_axis - 1, &spt2);
tgpi->totpixlen += len_v3v3(&spt.x, &spt2.x);
tpt->uv_fac = tgpi->totpixlen;
@@ -1011,7 +1011,7 @@ static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi)
}
/* convert screen-coordinates to 3D coordinates */
- gp_stroke_convertcoords_tpoint(
+ gpencil_stroke_convertcoords_tpoint(
tgpi->scene, tgpi->region, tgpi->ob, p2d, depth_arr ? depth_arr + i : NULL, &pt->x);
pt->pressure = pressure;
@@ -1036,7 +1036,7 @@ static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi)
bGPDcontrolpoint *cps = tgpi->gpd->runtime.cp_points;
for (int i = 0; i < tgpi->gpd->runtime.tot_cp_points; i++) {
bGPDcontrolpoint *cp = &cps[i];
- gp_stroke_convertcoords_tpoint(
+ gpencil_stroke_convertcoords_tpoint(
tgpi->scene, tgpi->region, tgpi->ob, (tGPspoint *)cp, NULL, &cp->x);
}
}
@@ -1045,14 +1045,14 @@ static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi)
if (!is_depth) {
float origin[3];
ED_gpencil_drawing_reference_get(tgpi->scene, tgpi->ob, ts->gpencil_v3d_align, origin);
- ED_gp_project_stroke_to_plane(
+ ED_gpencil_project_stroke_to_plane(
tgpi->scene, tgpi->ob, tgpi->rv3d, gps, origin, ts->gp_sculpt.lock_axis - 1);
}
/* if parented change position relative to parent object */
for (int i = 0; i < gps->totpoints; i++) {
bGPDspoint *pt = &gps->points[i];
- gp_apply_parent_point(tgpi->depsgraph, tgpi->ob, tgpi->gpl, pt);
+ gpencil_apply_parent_point(tgpi->depsgraph, tgpi->ob, tgpi->gpl, pt);
}
/* if camera view, reproject flat to view to avoid perspective effect */
@@ -1082,7 +1082,7 @@ static void gpencil_primitive_update(bContext *C, wmOperator *op, tGPDprimitive
tgpi->type = RNA_enum_get(op->ptr, "type");
tgpi->tot_edges = RNA_int_get(op->ptr, "edges");
/* update points position */
- gp_primitive_update_strokes(C, tgpi);
+ gpencil_primitive_update_strokes(C, tgpi);
}
/* Initialise mouse points */
@@ -1237,7 +1237,7 @@ static void gpencil_primitive_init(bContext *C, wmOperator *op)
tgpi->lock_axis = ts->gp_sculpt.lock_axis;
/* set temp layer, frame and stroke */
- gp_primitive_set_initdata(C, tgpi);
+ gpencil_primitive_set_initdata(C, tgpi);
}
/* Invoke handler: Initialize the operator */
@@ -1396,14 +1396,14 @@ static void gpencil_primitive_edit_event_handling(
gpencil_primitive_add_segment(tgpi);
copy_v2_v2(tgpi->start, tgpi->end);
copy_v2_v2(tgpi->origin, tgpi->start);
- gp_primitive_update_cps(tgpi);
+ gpencil_primitive_update_cps(tgpi);
tgpi->flag = IN_POLYLINE;
WM_cursor_modal_set(win, WM_CURSOR_CROSS);
}
else {
tgpi->flag = IN_CURVE_EDIT;
- gp_primitive_update_cps(tgpi);
+ gpencil_primitive_update_cps(tgpi);
gpencil_primitive_update(C, op, tgpi);
}
}
@@ -1460,7 +1460,7 @@ static void gpencil_primitive_edit_event_handling(
case EVT_MKEY: {
if ((event->val == KM_PRESS) && (tgpi->curve) && (ELEM(tgpi->orign_type, GP_STROKE_ARC))) {
tgpi->flip ^= 1;
- gp_primitive_update_cps(tgpi);
+ gpencil_primitive_update_cps(tgpi);
gpencil_primitive_update(C, op, tgpi);
}
break;
@@ -1472,7 +1472,7 @@ static void gpencil_primitive_edit_event_handling(
gpencil_primitive_add_segment(tgpi);
copy_v2_v2(tgpi->start, tgpi->end);
copy_v2_v2(tgpi->origin, tgpi->start);
- gp_primitive_update_cps(tgpi);
+ gpencil_primitive_update_cps(tgpi);
}
break;
}
@@ -1625,7 +1625,7 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e
if (event->val == KM_PRESS) {
tgpi->flag = IDLE;
tgpi->tot_edges = tgpi->tot_stored_edges ? 1 : 0;
- gp_primitive_update_strokes(C, tgpi);
+ gpencil_primitive_update_strokes(C, tgpi);
gpencil_primitive_interaction_end(C, op, win, tgpi);
return OPERATOR_FINISHED;
}
@@ -1749,7 +1749,7 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e
(!ELEM(tgpi->type, GP_STROKE_POLYLINE))) {
/* set control points and enter edit mode */
tgpi->flag = IN_CURVE_EDIT;
- gp_primitive_update_cps(tgpi);
+ gpencil_primitive_update_cps(tgpi);
gpencil_primitive_update(C, op, tgpi);
}
else if ((event->val == KM_RELEASE) && (tgpi->flag == IN_PROGRESS) &&
@@ -1792,7 +1792,7 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e
if (tgpi->tot_stored_edges > 0) {
tgpi->flag = IDLE;
tgpi->tot_edges = tgpi->tot_stored_edges ? 1 : 0;
- gp_primitive_update_strokes(C, tgpi);
+ gpencil_primitive_update_strokes(C, tgpi);
gpencil_primitive_interaction_end(C, op, win, tgpi);
/* done! */
return OPERATOR_FINISHED;
@@ -1871,7 +1871,7 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e
}
RNA_enum_set(op->ptr, "type", tgpi->type);
- gp_primitive_update_cps(tgpi);
+ gpencil_primitive_update_cps(tgpi);
gpencil_primitive_update(C, op, tgpi);
}
break;
@@ -1880,7 +1880,7 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e
if (tgpi->flag == IN_CURVE_EDIT) {
tgpi->flag = IN_PROGRESS;
WM_cursor_modal_set(win, WM_CURSOR_NSEW_SCROLL);
- gp_primitive_update_cps(tgpi);
+ gpencil_primitive_update_cps(tgpi);
gpencil_primitive_update(C, op, tgpi);
}
break;
@@ -1908,7 +1908,7 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e
tgpi->start[0] = tgpi->origin[0] - (tgpi->end[0] - tgpi->origin[0]);
tgpi->start[1] = tgpi->origin[1] - (tgpi->end[1] - tgpi->origin[1]);
}
- gp_primitive_update_cps(tgpi);
+ gpencil_primitive_update_cps(tgpi);
/* update screen */
gpencil_primitive_update(C, op, tgpi);
}
diff --git a/source/blender/editors/gpencil/gpencil_sculpt_paint.c b/source/blender/editors/gpencil/gpencil_sculpt_paint.c
index 6bd945160a6..d0adcea25c1 100644
--- a/source/blender/editors/gpencil/gpencil_sculpt_paint.c
+++ b/source/blender/editors/gpencil/gpencil_sculpt_paint.c
@@ -172,9 +172,9 @@ typedef bool (*GP_BrushApplyCb)(tGP_BrushEditData *gso,
/* Utility Functions */
/* apply lock axis reset */
-static void gpsculpt_compute_lock_axis(tGP_BrushEditData *gso,
- bGPDspoint *pt,
- const float save_pt[3])
+static void gpencil_sculpt_compute_lock_axis(tGP_BrushEditData *gso,
+ bGPDspoint *pt,
+ const float save_pt[3])
{
const ToolSettings *ts = gso->scene->toolsettings;
const View3DCursor *cursor = &gso->scene->cursor;
@@ -221,7 +221,7 @@ static void gpsculpt_compute_lock_axis(tGP_BrushEditData *gso,
/* Context ---------------------------------------- */
/* Get the sculpting settings */
-static GP_Sculpt_Settings *gpsculpt_get_settings(Scene *scene)
+static GP_Sculpt_Settings *gpencil_sculpt_get_settings(Scene *scene)
{
return &scene->toolsettings->gp_sculpt;
}
@@ -229,7 +229,7 @@ static GP_Sculpt_Settings *gpsculpt_get_settings(Scene *scene)
/* Brush Operations ------------------------------- */
/* Invert behavior of brush? */
-static bool gp_brush_invert_check(tGP_BrushEditData *gso)
+static bool gpencil_brush_invert_check(tGP_BrushEditData *gso)
{
/* The basic setting is the brush's setting (from the panel) */
bool invert = ((gso->brush->gpencil_settings->sculpt_flag & GP_SCULPT_FLAG_INVERT) != 0) ||
@@ -251,7 +251,9 @@ static bool gp_brush_invert_check(tGP_BrushEditData *gso)
}
/* Compute strength of effect */
-static float gp_brush_influence_calc(tGP_BrushEditData *gso, const int radius, const int co[2])
+static float gpencil_brush_influence_calc(tGP_BrushEditData *gso,
+ const int radius,
+ const int co[2])
{
Brush *brush = gso->brush;
@@ -322,14 +324,14 @@ static void gpencil_update_geometry(bGPdata *gpd)
/* A simple (but slower + inaccurate)
* smooth-brush implementation to test the algorithm for stroke smoothing. */
-static bool gp_brush_smooth_apply(tGP_BrushEditData *gso,
- bGPDstroke *gps,
- float UNUSED(rot_eval),
- int pt_index,
- const int radius,
- const int co[2])
+static bool gpencil_brush_smooth_apply(tGP_BrushEditData *gso,
+ bGPDstroke *gps,
+ float UNUSED(rot_eval),
+ int pt_index,
+ const int radius,
+ const int co[2])
{
- float inf = gp_brush_influence_calc(gso, radius, co);
+ float inf = gpencil_brush_influence_calc(gso, radius, co);
/* perform smoothing */
if (gso->brush->gpencil_settings->sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_POSITION) {
@@ -352,12 +354,12 @@ static bool gp_brush_smooth_apply(tGP_BrushEditData *gso,
/* Line Thickness Brush */
/* Make lines thicker or thinner by the specified amounts */
-static bool gp_brush_thickness_apply(tGP_BrushEditData *gso,
- bGPDstroke *gps,
- float UNUSED(rot_eval),
- int pt_index,
- const int radius,
- const int co[2])
+static bool gpencil_brush_thickness_apply(tGP_BrushEditData *gso,
+ bGPDstroke *gps,
+ float UNUSED(rot_eval),
+ int pt_index,
+ const int radius,
+ const int co[2])
{
bGPDspoint *pt = gps->points + pt_index;
float inf;
@@ -366,12 +368,12 @@ static bool gp_brush_thickness_apply(tGP_BrushEditData *gso,
* - We divide the strength by 10, so that users can set "sane" values.
* Otherwise, good default values are in the range of 0.093
*/
- inf = gp_brush_influence_calc(gso, radius, co) / 10.0f;
+ inf = gpencil_brush_influence_calc(gso, radius, co) / 10.0f;
/* apply */
/* XXX: this is much too strong,
* and it should probably do some smoothing with the surrounding stuff. */
- if (gp_brush_invert_check(gso)) {
+ if (gpencil_brush_invert_check(gso)) {
/* make line thinner - reduce stroke pressure */
pt->pressure -= inf;
}
@@ -396,21 +398,21 @@ static bool gp_brush_thickness_apply(tGP_BrushEditData *gso,
/* Color Strength Brush */
/* Make color more or less transparent by the specified amounts */
-static bool gp_brush_strength_apply(tGP_BrushEditData *gso,
- bGPDstroke *gps,
- float UNUSED(rot_eval),
- int pt_index,
- const int radius,
- const int co[2])
+static bool gpencil_brush_strength_apply(tGP_BrushEditData *gso,
+ bGPDstroke *gps,
+ float UNUSED(rot_eval),
+ int pt_index,
+ const int radius,
+ const int co[2])
{
bGPDspoint *pt = gps->points + pt_index;
float inf;
/* Compute strength of effect */
- inf = gp_brush_influence_calc(gso, radius, co) * 0.125f;
+ inf = gpencil_brush_influence_calc(gso, radius, co) * 0.125f;
/* Invert effect. */
- if (gp_brush_invert_check(gso)) {
+ if (gpencil_brush_invert_check(gso)) {
inf *= -1.0f;
}
@@ -443,7 +445,7 @@ typedef struct tGPSB_Grab_StrokeData {
} tGPSB_Grab_StrokeData;
/* initialise custom data for handling this stroke */
-static void gp_brush_grab_stroke_init(tGP_BrushEditData *gso, bGPDstroke *gps)
+static void gpencil_brush_grab_stroke_init(tGP_BrushEditData *gso, bGPDstroke *gps)
{
tGPSB_Grab_StrokeData *data = NULL;
@@ -481,15 +483,15 @@ static void gp_brush_grab_stroke_init(tGP_BrushEditData *gso, bGPDstroke *gps)
}
/* store references to stroke points in the initial stage */
-static bool gp_brush_grab_store_points(tGP_BrushEditData *gso,
- bGPDstroke *gps,
- float rot_eval,
- int pt_index,
- const int radius,
- const int co[2])
+static bool gpencil_brush_grab_store_points(tGP_BrushEditData *gso,
+ bGPDstroke *gps,
+ float rot_eval,
+ int pt_index,
+ const int radius,
+ const int co[2])
{
tGPSB_Grab_StrokeData *data = BLI_ghash_lookup(gso->stroke_customdata, gps);
- float inf = gp_brush_influence_calc(gso, radius, co);
+ float inf = gpencil_brush_influence_calc(gso, radius, co);
BLI_assert(data != NULL);
BLI_assert(data->size < data->capacity);
@@ -505,7 +507,7 @@ static bool gp_brush_grab_store_points(tGP_BrushEditData *gso,
}
/* Compute effect vector for grab brush */
-static void gp_brush_grab_calc_dvec(tGP_BrushEditData *gso)
+static void gpencil_brush_grab_calc_dvec(tGP_BrushEditData *gso)
{
/* Convert mouse-movements to movement vector */
RegionView3D *rv3d = gso->region->regiondata;
@@ -532,9 +534,9 @@ static void gp_brush_grab_calc_dvec(tGP_BrushEditData *gso)
}
/* Apply grab transform to all relevant points of the affected strokes */
-static void gp_brush_grab_apply_cached(tGP_BrushEditData *gso,
- bGPDstroke *gps,
- const float diff_mat[4][4])
+static void gpencil_brush_grab_apply_cached(tGP_BrushEditData *gso,
+ bGPDstroke *gps,
+ const float diff_mat[4][4])
{
tGPSB_Grab_StrokeData *data = BLI_ghash_lookup(gso->stroke_customdata, gps);
/* If a new frame is created, could be impossible find the stroke. */
@@ -553,7 +555,7 @@ static void gp_brush_grab_apply_cached(tGP_BrushEditData *gso,
/* get evaluated transformation */
gso->rot_eval = data->rot_eval[i];
- gp_brush_grab_calc_dvec(gso);
+ gpencil_brush_grab_calc_dvec(gso);
/* adjust the amount of displacement to apply */
mul_v3_v3fl(delta, gso->dvec, data->weights[i]);
@@ -569,12 +571,12 @@ static void gp_brush_grab_apply_cached(tGP_BrushEditData *gso,
mul_m4_v3(inverse_diff_mat, &pt->x);
/* compute lock axis */
- gpsculpt_compute_lock_axis(gso, pt, save_pt);
+ gpencil_sculpt_compute_lock_axis(gso, pt, save_pt);
}
}
/* free customdata used for handling this stroke */
-static void gp_brush_grab_stroke_free(void *ptr)
+static void gpencil_brush_grab_stroke_free(void *ptr)
{
tGPSB_Grab_StrokeData *data = (tGPSB_Grab_StrokeData *)ptr;
@@ -589,19 +591,19 @@ static void gp_brush_grab_stroke_free(void *ptr)
/* ----------------------------------------------- */
/* Push Brush */
-/* NOTE: Depends on gp_brush_grab_calc_dvec() */
-static bool gp_brush_push_apply(tGP_BrushEditData *gso,
- bGPDstroke *gps,
- float UNUSED(rot_eval),
- int pt_index,
- const int radius,
- const int co[2])
+/* NOTE: Depends on gpencil_brush_grab_calc_dvec() */
+static bool gpencil_brush_push_apply(tGP_BrushEditData *gso,
+ bGPDstroke *gps,
+ float UNUSED(rot_eval),
+ int pt_index,
+ const int radius,
+ const int co[2])
{
bGPDspoint *pt = gps->points + pt_index;
float save_pt[3];
copy_v3_v3(save_pt, &pt->x);
- float inf = gp_brush_influence_calc(gso, radius, co);
+ float inf = gpencil_brush_influence_calc(gso, radius, co);
float delta[3] = {0.0f};
/* adjust the amount of displacement to apply */
@@ -612,7 +614,7 @@ static bool gp_brush_push_apply(tGP_BrushEditData *gso,
add_v3_v3(&pt->x, delta);
/* compute lock axis */
- gpsculpt_compute_lock_axis(gso, pt, save_pt);
+ gpencil_sculpt_compute_lock_axis(gso, pt, save_pt);
/* done */
return true;
@@ -621,10 +623,10 @@ static bool gp_brush_push_apply(tGP_BrushEditData *gso,
/* ----------------------------------------------- */
/* Pinch Brush */
/* Compute reference midpoint for the brush - this is what we'll be moving towards */
-static void gp_brush_calc_midpoint(tGP_BrushEditData *gso)
+static void gpencil_brush_calc_midpoint(tGP_BrushEditData *gso)
{
/* Convert mouse position to 3D space
- * See: gpencil_paint.c :: gp_stroke_convertcoords()
+ * See: gpencil_paint.c :: gpencil_stroke_convertcoords()
*/
RegionView3D *rv3d = gso->region->regiondata;
const float *rvec = gso->object->loc;
@@ -647,12 +649,12 @@ static void gp_brush_calc_midpoint(tGP_BrushEditData *gso)
}
/* Shrink distance between midpoint and this point... */
-static bool gp_brush_pinch_apply(tGP_BrushEditData *gso,
- bGPDstroke *gps,
- float UNUSED(rot_eval),
- int pt_index,
- const int radius,
- const int co[2])
+static bool gpencil_brush_pinch_apply(tGP_BrushEditData *gso,
+ bGPDstroke *gps,
+ float UNUSED(rot_eval),
+ int pt_index,
+ const int radius,
+ const int co[2])
{
bGPDspoint *pt = gps->points + pt_index;
float fac, inf;
@@ -665,7 +667,7 @@ static bool gp_brush_pinch_apply(tGP_BrushEditData *gso,
* - Div 10 = Not enough effect
* - Div 5 = Happy medium... (by trial and error)
*/
- inf = gp_brush_influence_calc(gso, radius, co) / 5.0f;
+ inf = gpencil_brush_influence_calc(gso, radius, co) / 5.0f;
/* 1) Make this point relative to the cursor/midpoint (dvec) */
float fpt[3];
@@ -677,7 +679,7 @@ static bool gp_brush_pinch_apply(tGP_BrushEditData *gso,
* OR
* Increase the distance (if inverting the brush action!)
*/
- if (gp_brush_invert_check(gso)) {
+ if (gpencil_brush_invert_check(gso)) {
/* Inflate (inverse) */
fac = 1.0f + (inf * inf); /* squared to temper the effect... */
}
@@ -692,7 +694,7 @@ static bool gp_brush_pinch_apply(tGP_BrushEditData *gso,
mul_v3_m4v3(&pt->x, gso->object->imat, fpt);
/* compute lock axis */
- gpsculpt_compute_lock_axis(gso, pt, save_pt);
+ gpencil_sculpt_compute_lock_axis(gso, pt, save_pt);
/* done */
return true;
@@ -704,12 +706,12 @@ static bool gp_brush_pinch_apply(tGP_BrushEditData *gso,
* convert the rotated point and convert it into "data" space
*/
-static bool gp_brush_twist_apply(tGP_BrushEditData *gso,
- bGPDstroke *gps,
- float UNUSED(rot_eval),
- int pt_index,
- const int radius,
- const int co[2])
+static bool gpencil_brush_twist_apply(tGP_BrushEditData *gso,
+ bGPDstroke *gps,
+ float UNUSED(rot_eval),
+ int pt_index,
+ const int radius,
+ const int co[2])
{
bGPDspoint *pt = gps->points + pt_index;
float angle, inf;
@@ -717,10 +719,10 @@ static bool gp_brush_twist_apply(tGP_BrushEditData *gso,
copy_v3_v3(save_pt, &pt->x);
/* Angle to rotate by */
- inf = gp_brush_influence_calc(gso, radius, co);
+ inf = gpencil_brush_influence_calc(gso, radius, co);
angle = DEG2RADF(1.0f) * inf;
- if (gp_brush_invert_check(gso)) {
+ if (gpencil_brush_invert_check(gso)) {
/* invert angle that we rotate by */
angle *= -1;
}
@@ -749,7 +751,7 @@ static bool gp_brush_twist_apply(tGP_BrushEditData *gso,
mul_v3_m4v3(&pt->x, gso->object->imat, fpt);
/* compute lock axis */
- gpsculpt_compute_lock_axis(gso, pt, save_pt);
+ gpencil_sculpt_compute_lock_axis(gso, pt, save_pt);
}
else {
const float axis[3] = {0.0f, 0.0f, 1.0f};
@@ -787,12 +789,12 @@ static bool gp_brush_twist_apply(tGP_BrushEditData *gso,
/* ----------------------------------------------- */
/* Randomize Brush */
/* Apply some random jitter to the point */
-static bool gp_brush_randomize_apply(tGP_BrushEditData *gso,
- bGPDstroke *gps,
- float UNUSED(rot_eval),
- int pt_index,
- const int radius,
- const int co[2])
+static bool gpencil_brush_randomize_apply(tGP_BrushEditData *gso,
+ bGPDstroke *gps,
+ float UNUSED(rot_eval),
+ int pt_index,
+ const int radius,
+ const int co[2])
{
bGPDspoint *pt = gps->points + pt_index;
float save_pt[3];
@@ -801,7 +803,7 @@ static bool gp_brush_randomize_apply(tGP_BrushEditData *gso,
/* Amount of jitter to apply depends on the distance of the point to the cursor,
* as well as the strength of the brush
*/
- const float inf = gp_brush_influence_calc(gso, radius, co) / 2.0f;
+ const float inf = gpencil_brush_influence_calc(gso, radius, co) / 2.0f;
const float fac = BLI_rng_get_float(gso->rng) * inf;
/* apply random to position */
@@ -840,7 +842,7 @@ static bool gp_brush_randomize_apply(tGP_BrushEditData *gso,
ED_view3d_win_to_delta(gso->gsc.region, svec, dvec, zfac);
add_v3_v3(&pt->x, dvec);
/* compute lock axis */
- gpsculpt_compute_lock_axis(gso, pt, save_pt);
+ gpencil_sculpt_compute_lock_axis(gso, pt, save_pt);
}
}
}
@@ -909,7 +911,7 @@ typedef struct tGPSB_CloneBrushData {
} tGPSB_CloneBrushData;
/* Initialise "clone" brush data */
-static void gp_brush_clone_init(bContext *C, tGP_BrushEditData *gso)
+static void gpencil_brush_clone_init(bContext *C, tGP_BrushEditData *gso)
{
tGPSB_CloneBrushData *data;
bGPDstroke *gps;
@@ -918,7 +920,7 @@ static void gp_brush_clone_init(bContext *C, tGP_BrushEditData *gso)
gso->customdata = data = MEM_callocN(sizeof(tGPSB_CloneBrushData), "CloneBrushData");
/* compute midpoint of strokes on clipboard */
- for (gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) {
+ for (gps = gpencil_strokes_copypastebuf.first; gps; gps = gps->next) {
if (ED_gpencil_stroke_can_use(C, gps)) {
const float dfac = 1.0f / ((float)gps->totpoints);
float mid[3] = {0.0f};
@@ -954,11 +956,11 @@ static void gp_brush_clone_init(bContext *C, tGP_BrushEditData *gso)
/* Init colormap for mapping between the pasted stroke's source color (names)
* and the final colors that will be used here instead.
*/
- data->new_colors = gp_copybuf_validate_colormap(C);
+ data->new_colors = gpencil_copybuf_validate_colormap(C);
}
/* Free custom data used for "clone" brush */
-static void gp_brush_clone_free(tGP_BrushEditData *gso)
+static void gpencil_brush_clone_free(tGP_BrushEditData *gso)
{
tGPSB_CloneBrushData *data = gso->customdata;
@@ -980,7 +982,7 @@ static void gp_brush_clone_free(tGP_BrushEditData *gso)
}
/* Create new copies of the strokes on the clipboard */
-static void gp_brush_clone_add(bContext *C, tGP_BrushEditData *gso)
+static void gpencil_brush_clone_add(bContext *C, tGP_BrushEditData *gso)
{
tGPSB_CloneBrushData *data = gso->customdata;
@@ -995,11 +997,11 @@ static void gp_brush_clone_add(bContext *C, tGP_BrushEditData *gso)
/* Compute amount to offset the points by */
/* NOTE: This assumes that screenspace strokes are NOT used in the 3D view... */
- gp_brush_calc_midpoint(gso); /* this puts the cursor location into gso->dvec */
+ gpencil_brush_calc_midpoint(gso); /* this puts the cursor location into gso->dvec */
sub_v3_v3v3(delta, gso->dvec, data->buffer_midpoint);
/* Copy each stroke into the layer */
- for (gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) {
+ for (gps = gpencil_strokes_copypastebuf.first; gps; gps = gps->next) {
if (ED_gpencil_stroke_can_use(C, gps)) {
bGPDstroke *new_stroke;
bGPDspoint *pt;
@@ -1051,14 +1053,14 @@ static void gp_brush_clone_add(bContext *C, tGP_BrushEditData *gso)
}
/* Move newly-added strokes around - "Stamp" mode of the Clone brush */
-static void gp_brush_clone_adjust(tGP_BrushEditData *gso)
+static void gpencil_brush_clone_adjust(tGP_BrushEditData *gso)
{
tGPSB_CloneBrushData *data = gso->customdata;
size_t snum;
/* Compute the amount of movement to apply (overwrites dvec) */
gso->rot_eval = 0.0f;
- gp_brush_grab_calc_dvec(gso);
+ gpencil_brush_grab_calc_dvec(gso);
/* For each of the stored strokes, apply the offset to each point */
/* NOTE: Again this assumes that in the 3D view,
@@ -1075,8 +1077,8 @@ static void gp_brush_clone_adjust(tGP_BrushEditData *gso)
float influence;
/* compute influence on point */
- gp_point_to_xy(&gso->gsc, gps, pt, &sco[0], &sco[1]);
- influence = gp_brush_influence_calc(gso, gso->brush->size, sco);
+ gpencil_point_to_xy(&gso->gsc, gps, pt, &sco[0], &sco[1]);
+ influence = gpencil_brush_influence_calc(gso, gso->brush->size, sco);
/* adjust the amount of displacement to apply */
mul_v3_v3fl(delta, gso->dvec, influence);
@@ -1088,24 +1090,24 @@ static void gp_brush_clone_adjust(tGP_BrushEditData *gso)
}
/* Entrypoint for applying "clone" brush */
-static bool gpsculpt_brush_apply_clone(bContext *C, tGP_BrushEditData *gso)
+static bool gpencil_sculpt_brush_apply_clone(bContext *C, tGP_BrushEditData *gso)
{
/* Which "mode" are we operating in? */
if (gso->first) {
/* Create initial clones */
- gp_brush_clone_add(C, gso);
+ gpencil_brush_clone_add(C, gso);
}
else {
/* Stamp or Continuous Mode */
if (1 /*gso->brush->mode == GP_EDITBRUSH_CLONE_MODE_STAMP*/) {
/* Stamp - Proceed to translate the newly added strokes */
- gp_brush_clone_adjust(gso);
+ gpencil_brush_clone_adjust(gso);
}
else {
/* Continuous - Just keep pasting everytime we move */
/* TODO: The spacing of repeat should be controlled using a
* "stepsize" or similar property? */
- gp_brush_clone_add(C, gso);
+ gpencil_brush_clone_add(C, gso);
}
}
@@ -1115,7 +1117,7 @@ static bool gpsculpt_brush_apply_clone(bContext *C, tGP_BrushEditData *gso)
/* ************************************************ */
/* Header Info for GPencil Sculpt */
-static void gpsculpt_brush_header_set(bContext *C, tGP_BrushEditData *gso)
+static void gpencil_sculpt_brush_header_set(bContext *C, tGP_BrushEditData *gso)
{
Brush *brush = gso->brush;
char str[UI_MAX_DRAW_STR] = "";
@@ -1135,7 +1137,7 @@ static void gpsculpt_brush_header_set(bContext *C, tGP_BrushEditData *gso)
/* Init/Exit ----------------------------------------------- */
-static bool gpsculpt_brush_init(bContext *C, wmOperator *op)
+static bool gpencil_sculpt_brush_init(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
ToolSettings *ts = scene->toolsettings;
@@ -1151,7 +1153,7 @@ static bool gpsculpt_brush_init(bContext *C, wmOperator *op)
gso->depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
gso->bmain = CTX_data_main(C);
/* store state */
- gso->settings = gpsculpt_get_settings(scene);
+ gso->settings = gpencil_sculpt_get_settings(scene);
/* Random generator, only init once. */
uint rng_seed = (uint)(PIL_check_seconds_timer_i() & UINT_MAX);
@@ -1209,7 +1211,7 @@ static bool gpsculpt_brush_init(bContext *C, wmOperator *op)
bool found = false;
/* check that there are some usable strokes in the buffer */
- for (gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) {
+ for (gps = gpencil_strokes_copypastebuf.first; gps; gps = gps->next) {
if (ED_gpencil_stroke_can_use(C, gps)) {
found = true;
break;
@@ -1229,7 +1231,7 @@ static bool gpsculpt_brush_init(bContext *C, wmOperator *op)
}
else {
/* initialise customdata */
- gp_brush_clone_init(C, gso);
+ gpencil_brush_clone_init(C, gso);
}
break;
}
@@ -1246,15 +1248,15 @@ static bool gpsculpt_brush_init(bContext *C, wmOperator *op)
}
/* setup space conversions */
- gp_point_conversion_init(C, &gso->gsc);
+ gpencil_point_conversion_init(C, &gso->gsc);
/* update header */
- gpsculpt_brush_header_set(C, gso);
+ gpencil_sculpt_brush_header_set(C, gso);
return true;
}
-static void gpsculpt_brush_exit(bContext *C, wmOperator *op)
+static void gpencil_sculpt_brush_exit(bContext *C, wmOperator *op)
{
tGP_BrushEditData *gso = op->customdata;
wmWindow *win = CTX_wm_window(C);
@@ -1267,13 +1269,13 @@ static void gpsculpt_brush_exit(bContext *C, wmOperator *op)
* - Keys don't need to be freed, as those are the strokes
* - Values assigned to those keys do, as they are custom structs
*/
- BLI_ghash_free(gso->stroke_customdata, NULL, gp_brush_grab_stroke_free);
+ BLI_ghash_free(gso->stroke_customdata, NULL, gpencil_brush_grab_stroke_free);
break;
}
case GPSCULPT_TOOL_CLONE: {
/* Free customdata */
- gp_brush_clone_free(gso);
+ gpencil_brush_clone_free(gso);
break;
}
@@ -1305,7 +1307,7 @@ static void gpsculpt_brush_exit(bContext *C, wmOperator *op)
}
/* poll callback for stroke sculpting operator(s) */
-static bool gpsculpt_brush_poll(bContext *C)
+static bool gpencil_sculpt_brush_poll(bContext *C)
{
ScrArea *area = CTX_wm_area(C);
if (area && area->spacetype != SPACE_VIEW3D) {
@@ -1318,7 +1320,7 @@ static bool gpsculpt_brush_poll(bContext *C)
/* Init Sculpt Stroke ---------------------------------- */
-static void gpsculpt_brush_init_stroke(bContext *C, tGP_BrushEditData *gso)
+static void gpencil_sculpt_brush_init_stroke(bContext *C, tGP_BrushEditData *gso)
{
bGPdata *gpd = gso->gpd;
@@ -1360,10 +1362,10 @@ static void gpsculpt_brush_init_stroke(bContext *C, tGP_BrushEditData *gso)
* For strokes with one point only this is impossible to calculate because there isn't a
* valid reference point.
*/
-static float gpsculpt_rotation_eval_get(tGP_BrushEditData *gso,
- bGPDstroke *gps_eval,
- bGPDspoint *pt_eval,
- int idx_eval)
+static float gpencil_sculpt_rotation_eval_get(tGP_BrushEditData *gso,
+ bGPDstroke *gps_eval,
+ bGPDspoint *pt_eval,
+ int idx_eval)
{
/* If multiframe or no modifiers, return 0. */
if ((GPENCIL_MULTIEDIT_SESSIONS_ON(gso->gpd)) || (!gso->is_transformed)) {
@@ -1402,22 +1404,22 @@ static float gpsculpt_rotation_eval_get(tGP_BrushEditData *gso,
/* create 2D vectors of the stroke segments */
float v_orig_a[2], v_orig_b[2], v_eval_a[2], v_eval_b[2];
- gp_point_3d_to_xy(gsc, GP_STROKE_3DSPACE, &pt_orig->x, v_orig_a);
- gp_point_3d_to_xy(gsc, GP_STROKE_3DSPACE, &pt_orig_prev->x, v_orig_b);
+ gpencil_point_3d_to_xy(gsc, GP_STROKE_3DSPACE, &pt_orig->x, v_orig_a);
+ gpencil_point_3d_to_xy(gsc, GP_STROKE_3DSPACE, &pt_orig_prev->x, v_orig_b);
sub_v2_v2(v_orig_a, v_orig_b);
- gp_point_3d_to_xy(gsc, GP_STROKE_3DSPACE, &pt_eval->x, v_eval_a);
- gp_point_3d_to_xy(gsc, GP_STROKE_3DSPACE, &pt_prev_eval->x, v_eval_b);
+ gpencil_point_3d_to_xy(gsc, GP_STROKE_3DSPACE, &pt_eval->x, v_eval_a);
+ gpencil_point_3d_to_xy(gsc, GP_STROKE_3DSPACE, &pt_prev_eval->x, v_eval_b);
sub_v2_v2(v_eval_a, v_eval_b);
return angle_v2v2(v_orig_a, v_eval_a);
}
/* Apply brush operation to points in this stroke */
-static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso,
- bGPDstroke *gps,
- const float diff_mat[4][4],
- GP_BrushApplyCb apply)
+static bool gpencil_sculpt_brush_do_stroke(tGP_BrushEditData *gso,
+ bGPDstroke *gps,
+ const float diff_mat[4][4],
+ GP_BrushApplyCb apply)
{
GP_SpaceConversion *gsc = &gso->gsc;
rcti *rect = &gso->brush_rect;
@@ -1447,8 +1449,8 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso,
if (gps->totpoints == 1) {
bGPDspoint pt_temp;
pt = &gps->points[0];
- gp_point_to_parent_space(gps->points, diff_mat, &pt_temp);
- gp_point_to_xy(gsc, gps, &pt_temp, &pc1[0], &pc1[1]);
+ gpencil_point_to_parent_space(gps->points, diff_mat, &pt_temp);
+ gpencil_point_to_xy(gsc, gps, &pt_temp, &pc1[0], &pc1[1]);
pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
/* do boundbox check first */
@@ -1459,7 +1461,7 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso,
if (len_v2v2_int(mval_i, pc1) <= radius) {
/* apply operation to this point */
if (pt_active != NULL) {
- rot_eval = gpsculpt_rotation_eval_get(gso, gps, pt, 0);
+ rot_eval = gpencil_sculpt_rotation_eval_get(gso, gps, pt, 0);
changed = apply(gso, gps_active, rot_eval, 0, radius, pc1);
}
}
@@ -1483,11 +1485,11 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso,
}
}
bGPDspoint npt;
- gp_point_to_parent_space(pt1, diff_mat, &npt);
- gp_point_to_xy(gsc, gps, &npt, &pc1[0], &pc1[1]);
+ gpencil_point_to_parent_space(pt1, diff_mat, &npt);
+ gpencil_point_to_xy(gsc, gps, &npt, &pc1[0], &pc1[1]);
- gp_point_to_parent_space(pt2, diff_mat, &npt);
- gp_point_to_xy(gsc, gps, &npt, &pc2[0], &pc2[1]);
+ gpencil_point_to_parent_space(pt2, diff_mat, &npt);
+ gpencil_point_to_xy(gsc, gps, &npt, &pc2[0], &pc2[1]);
/* Check that point segment of the boundbox of the selection stroke */
if (((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) ||
@@ -1496,7 +1498,7 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso,
* brush region (either within stroke painted, or on its lines)
* - this assumes that linewidth is irrelevant
*/
- if (gp_stroke_inside_circle(gso->mval, radius, pc1[0], pc1[1], pc2[0], pc2[1])) {
+ if (gpencil_stroke_inside_circle(gso->mval, radius, pc1[0], pc1[1], pc2[0], pc2[1])) {
/* Apply operation to these points */
bool ok = false;
@@ -1513,7 +1515,7 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso,
}
index = (pt->runtime.pt_orig) ? pt->runtime.idx_orig : i;
if ((pt_active != NULL) && (index < gps_active->totpoints)) {
- rot_eval = gpsculpt_rotation_eval_get(gso, gps, pt, i);
+ rot_eval = gpencil_sculpt_rotation_eval_get(gso, gps, pt, i);
ok = apply(gso, gps_active, rot_eval, index, radius, pc1);
}
@@ -1530,7 +1532,7 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso,
pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
index = (pt->runtime.pt_orig) ? pt->runtime.idx_orig : i + 1;
if ((pt_active != NULL) && (index < gps_active->totpoints)) {
- rot_eval = gpsculpt_rotation_eval_get(gso, gps, pt, i + 1);
+ rot_eval = gpencil_sculpt_rotation_eval_get(gso, gps, pt, i + 1);
ok |= apply(gso, gps_active, rot_eval, index, radius, pc2);
include_last = false;
}
@@ -1551,7 +1553,7 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso,
pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
index = (pt->runtime.pt_orig) ? pt->runtime.idx_orig : i;
if ((pt_active != NULL) && (index < gps_active->totpoints)) {
- rot_eval = gpsculpt_rotation_eval_get(gso, gps, pt, i);
+ rot_eval = gpencil_sculpt_rotation_eval_get(gso, gps, pt, i);
changed |= apply(gso, gps_active, rot_eval, index, radius, pc1);
include_last = false;
}
@@ -1564,11 +1566,11 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso,
}
/* Apply sculpt brushes to strokes in the given frame */
-static bool gpsculpt_brush_do_frame(bContext *C,
- tGP_BrushEditData *gso,
- bGPDlayer *gpl,
- bGPDframe *gpf,
- const float diff_mat[4][4])
+static bool gpencil_sculpt_brush_do_frame(bContext *C,
+ tGP_BrushEditData *gso,
+ bGPDlayer *gpl,
+ bGPDframe *gpf,
+ const float diff_mat[4][4])
{
bool changed = false;
bool redo_geom = false;
@@ -1588,20 +1590,22 @@ static bool gpsculpt_brush_do_frame(bContext *C,
switch (tool) {
case GPSCULPT_TOOL_SMOOTH: /* Smooth strokes */
{
- changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_smooth_apply);
+ changed |= gpencil_sculpt_brush_do_stroke(gso, gps, diff_mat, gpencil_brush_smooth_apply);
redo_geom |= changed;
break;
}
case GPSCULPT_TOOL_THICKNESS: /* Adjust stroke thickness */
{
- changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_thickness_apply);
+ changed |= gpencil_sculpt_brush_do_stroke(
+ gso, gps, diff_mat, gpencil_brush_thickness_apply);
break;
}
case GPSCULPT_TOOL_STRENGTH: /* Adjust stroke color strength */
{
- changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_strength_apply);
+ changed |= gpencil_sculpt_brush_do_stroke(
+ gso, gps, diff_mat, gpencil_brush_strength_apply);
break;
}
@@ -1614,13 +1618,13 @@ static bool gpsculpt_brush_do_frame(bContext *C,
* 1) Prepare data buffers (init/clear) for this stroke
* 2) Use the points now under the cursor
*/
- gp_brush_grab_stroke_init(gso, gps_active);
- changed |= gpsculpt_brush_do_stroke(
- gso, gps_active, diff_mat, gp_brush_grab_store_points);
+ gpencil_brush_grab_stroke_init(gso, gps_active);
+ changed |= gpencil_sculpt_brush_do_stroke(
+ gso, gps_active, diff_mat, gpencil_brush_grab_store_points);
}
else {
/* Apply effect to the stored points */
- gp_brush_grab_apply_cached(gso, gps_active, diff_mat);
+ gpencil_brush_grab_apply_cached(gso, gps_active, diff_mat);
changed |= true;
}
}
@@ -1630,28 +1634,29 @@ static bool gpsculpt_brush_do_frame(bContext *C,
case GPSCULPT_TOOL_PUSH: /* Push points */
{
- changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_push_apply);
+ changed |= gpencil_sculpt_brush_do_stroke(gso, gps, diff_mat, gpencil_brush_push_apply);
redo_geom |= changed;
break;
}
case GPSCULPT_TOOL_PINCH: /* Pinch points */
{
- changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_pinch_apply);
+ changed |= gpencil_sculpt_brush_do_stroke(gso, gps, diff_mat, gpencil_brush_pinch_apply);
redo_geom |= changed;
break;
}
case GPSCULPT_TOOL_TWIST: /* Twist points around midpoint */
{
- changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_twist_apply);
+ changed |= gpencil_sculpt_brush_do_stroke(gso, gps, diff_mat, gpencil_brush_twist_apply);
redo_geom |= changed;
break;
}
case GPSCULPT_TOOL_RANDOMIZE: /* Apply jitter */
{
- changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_randomize_apply);
+ changed |= gpencil_sculpt_brush_do_stroke(
+ gso, gps, diff_mat, gpencil_brush_randomize_apply);
redo_geom |= changed;
break;
}
@@ -1685,7 +1690,7 @@ static bool gpsculpt_brush_do_frame(bContext *C,
}
/* Perform two-pass brushes which modify the existing strokes */
-static bool gpsculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso)
+static bool gpencil_sculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso)
{
ToolSettings *ts = gso->scene->toolsettings;
Depsgraph *depsgraph = gso->depsgraph;
@@ -1703,7 +1708,7 @@ static bool gpsculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso)
{
/* calculate amount of displacement to apply */
gso->rot_eval = 0.0f;
- gp_brush_grab_calc_dvec(gso);
+ gpencil_brush_grab_calc_dvec(gso);
break;
}
@@ -1711,7 +1716,7 @@ static bool gpsculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso)
case GPSCULPT_TOOL_TWIST: /* Twist points around midpoint */
{
/* calculate midpoint of the brush (in data space) */
- gp_brush_calc_midpoint(gso);
+ gpencil_brush_calc_midpoint(gso);
break;
}
@@ -1719,7 +1724,7 @@ static bool gpsculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso)
{
/* compute the displacement vector for the cursor (in data space) */
gso->rot_eval = 0.0f;
- gp_brush_grab_calc_dvec(gso);
+ gpencil_brush_grab_calc_dvec(gso);
break;
}
@@ -1764,7 +1769,7 @@ static bool gpsculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso)
}
/* affect strokes in this frame */
- changed |= gpsculpt_brush_do_frame(C, gso, gpl, gpf, diff_mat);
+ changed |= gpencil_sculpt_brush_do_frame(C, gso, gpl, gpf, diff_mat);
}
}
}
@@ -1772,7 +1777,7 @@ static bool gpsculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso)
if (gpl->actframe != NULL) {
/* Apply to active frame's strokes */
gso->mf_falloff = 1.0f;
- changed |= gpsculpt_brush_do_frame(C, gso, gpl, gpl->actframe, diff_mat);
+ changed |= gpencil_sculpt_brush_do_frame(C, gso, gpl, gpl->actframe, diff_mat);
}
}
}
@@ -1781,7 +1786,7 @@ static bool gpsculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso)
}
/* Calculate settings for applying brush */
-static void gpsculpt_brush_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
+static void gpencil_sculpt_brush_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
{
tGP_BrushEditData *gso = op->customdata;
Brush *brush = gso->brush;
@@ -1821,10 +1826,10 @@ static void gpsculpt_brush_apply(bContext *C, wmOperator *op, PointerRNA *itempt
/* Apply brush */
char tool = gso->brush->gpencil_sculpt_tool;
if (tool == GPSCULPT_TOOL_CLONE) {
- changed = gpsculpt_brush_apply_clone(C, gso);
+ changed = gpencil_sculpt_brush_apply_clone(C, gso);
}
else {
- changed = gpsculpt_brush_apply_standard(C, gso);
+ changed = gpencil_sculpt_brush_apply_standard(C, gso);
}
/* Updates */
@@ -1841,7 +1846,7 @@ static void gpsculpt_brush_apply(bContext *C, wmOperator *op, PointerRNA *itempt
}
/* Running --------------------------------------------- */
-static Brush *gpsculpt_get_smooth_brush(tGP_BrushEditData *gso)
+static Brush *gpencil_sculpt_get_smooth_brush(tGP_BrushEditData *gso)
{
Main *bmain = gso->bmain;
Brush *brush = BLI_findstring(&bmain->brushes, "Smooth Stroke", offsetof(ID, name) + 2);
@@ -1850,7 +1855,7 @@ static Brush *gpsculpt_get_smooth_brush(tGP_BrushEditData *gso)
}
/* helper - a record stroke, and apply paint event */
-static void gpsculpt_brush_apply_event(bContext *C, wmOperator *op, const wmEvent *event)
+static void gpencil_sculpt_brush_apply_event(bContext *C, wmOperator *op, const wmEvent *event)
{
tGP_BrushEditData *gso = op->customdata;
PointerRNA itemptr;
@@ -1878,7 +1883,7 @@ static void gpsculpt_brush_apply_event(bContext *C, wmOperator *op, const wmEven
if (event->shift) {
gso->brush_prev = gso->brush;
- gso->brush = gpsculpt_get_smooth_brush(gso);
+ gso->brush = gpencil_sculpt_get_smooth_brush(gso);
if (gso->brush == NULL) {
gso->brush = gso->brush_prev;
}
@@ -1890,28 +1895,28 @@ static void gpsculpt_brush_apply_event(bContext *C, wmOperator *op, const wmEven
}
/* apply */
- gpsculpt_brush_apply(C, op, &itemptr);
+ gpencil_sculpt_brush_apply(C, op, &itemptr);
}
/* reapply */
-static int gpsculpt_brush_exec(bContext *C, wmOperator *op)
+static int gpencil_sculpt_brush_exec(bContext *C, wmOperator *op)
{
- if (!gpsculpt_brush_init(C, op)) {
+ if (!gpencil_sculpt_brush_init(C, op)) {
return OPERATOR_CANCELLED;
}
RNA_BEGIN (op->ptr, itemptr, "stroke") {
- gpsculpt_brush_apply(C, op, &itemptr);
+ gpencil_sculpt_brush_apply(C, op, &itemptr);
}
RNA_END;
- gpsculpt_brush_exit(C, op);
+ gpencil_sculpt_brush_exit(C, op);
return OPERATOR_FINISHED;
}
/* start modal painting */
-static int gpsculpt_brush_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+static int gpencil_sculpt_brush_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
tGP_BrushEditData *gso = NULL;
const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input");
@@ -1927,7 +1932,7 @@ static int gpsculpt_brush_invoke(bContext *C, wmOperator *op, const wmEvent *eve
}
/* init painting data */
- if (!gpsculpt_brush_init(C, op)) {
+ if (!gpencil_sculpt_brush_init(C, op)) {
return OPERATOR_CANCELLED;
}
@@ -1974,11 +1979,11 @@ static int gpsculpt_brush_invoke(bContext *C, wmOperator *op, const wmEvent *eve
ARegion *region = CTX_wm_region(C);
/* ensure that we'll have a new frame to draw on */
- gpsculpt_brush_init_stroke(C, gso);
+ gpencil_sculpt_brush_init_stroke(C, gso);
/* apply first dab... */
gso->is_painting = true;
- gpsculpt_brush_apply_event(C, op, event);
+ gpencil_sculpt_brush_apply_event(C, op, event);
/* redraw view with feedback */
ED_region_tag_redraw(region);
@@ -1988,7 +1993,7 @@ static int gpsculpt_brush_invoke(bContext *C, wmOperator *op, const wmEvent *eve
}
/* painting - handle events */
-static int gpsculpt_brush_modal(bContext *C, wmOperator *op, const wmEvent *event)
+static int gpencil_sculpt_brush_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
tGP_BrushEditData *gso = op->customdata;
const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input");
@@ -2003,7 +2008,7 @@ static int gpsculpt_brush_modal(bContext *C, wmOperator *op, const wmEvent *even
case MOUSEMOVE:
case INBETWEEN_MOUSEMOVE:
/* apply brush effect at new position */
- gpsculpt_brush_apply_event(C, op, event);
+ gpencil_sculpt_brush_apply_event(C, op, event);
/* force redraw, so that the cursor will at least be valid */
redraw_region = true;
@@ -2013,7 +2018,7 @@ static int gpsculpt_brush_modal(bContext *C, wmOperator *op, const wmEvent *even
case TIMER:
if (event->customdata == gso->timer) {
gso->timerTick = true;
- gpsculpt_brush_apply_event(C, op, event);
+ gpencil_sculpt_brush_apply_event(C, op, event);
gso->timerTick = false;
}
break;
@@ -2029,7 +2034,7 @@ static int gpsculpt_brush_modal(bContext *C, wmOperator *op, const wmEvent *even
/* end sculpt session, since we're not modal */
gso->is_painting = false;
- gpsculpt_brush_exit(C, op);
+ gpencil_sculpt_brush_exit(C, op);
return OPERATOR_FINISHED;
}
break;
@@ -2038,7 +2043,7 @@ static int gpsculpt_brush_modal(bContext *C, wmOperator *op, const wmEvent *even
case MIDDLEMOUSE:
case RIGHTMOUSE:
case EVT_ESCKEY:
- gpsculpt_brush_exit(C, op);
+ gpencil_sculpt_brush_exit(C, op);
return OPERATOR_FINISHED;
}
}
@@ -2053,14 +2058,14 @@ static int gpsculpt_brush_modal(bContext *C, wmOperator *op, const wmEvent *even
gso->is_painting = true;
gso->first = true;
- gpsculpt_brush_init_stroke(C, gso);
- gpsculpt_brush_apply_event(C, op, event);
+ gpencil_sculpt_brush_init_stroke(C, gso);
+ gpencil_sculpt_brush_apply_event(C, op, event);
break;
/* Exit modal operator, based on the "standard" ops */
case RIGHTMOUSE:
case EVT_ESCKEY:
- gpsculpt_brush_exit(C, op);
+ gpencil_sculpt_brush_exit(C, op);
return OPERATOR_FINISHED;
/* MMB is often used for view manipulations */
@@ -2124,11 +2129,11 @@ void GPENCIL_OT_sculpt_paint(wmOperatorType *ot)
ot->description = "Apply tweaks to strokes by painting over the strokes"; // XXX
/* api callbacks */
- ot->exec = gpsculpt_brush_exec;
- ot->invoke = gpsculpt_brush_invoke;
- ot->modal = gpsculpt_brush_modal;
- ot->cancel = gpsculpt_brush_exit;
- ot->poll = gpsculpt_brush_poll;
+ ot->exec = gpencil_sculpt_brush_exec;
+ ot->invoke = gpencil_sculpt_brush_invoke;
+ ot->modal = gpencil_sculpt_brush_modal;
+ ot->cancel = gpencil_sculpt_brush_exit;
+ ot->poll = gpencil_sculpt_brush_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c
index c41b2993a80..ea6f53d24bb 100644
--- a/source/blender/editors/gpencil/gpencil_select.c
+++ b/source/blender/editors/gpencil/gpencil_select.c
@@ -369,7 +369,7 @@ typedef enum eGP_SelectGrouped {
/* ----------------------------------- */
/* On each visible layer, check for selected strokes - if found, select all others */
-static void gp_select_same_layer(bContext *C)
+static void gpencil_select_same_layer(bContext *C)
{
Scene *scene = CTX_data_scene(C);
@@ -412,7 +412,7 @@ static void gp_select_same_layer(bContext *C)
}
/* Select all strokes with same colors as selected ones */
-static void gp_select_same_material(bContext *C)
+static void gpencil_select_same_material(bContext *C)
{
/* First, build set containing all the colors of selected strokes */
GSet *selected_colors = BLI_gset_str_new("GP Selected Colors");
@@ -462,10 +462,10 @@ static int gpencil_select_grouped_exec(bContext *C, wmOperator *op)
switch (mode) {
case GP_SEL_SAME_LAYER:
- gp_select_same_layer(C);
+ gpencil_select_same_layer(C);
break;
case GP_SEL_SAME_MATERIAL:
- gp_select_same_material(C);
+ gpencil_select_same_material(C);
break;
default:
@@ -853,142 +853,87 @@ void GPENCIL_OT_select_less(wmOperatorType *ot)
* Helper to check if a given stroke is within the area.
*
* \note Code here is adapted (i.e. copied directly)
- * from gpencil_paint.c #gp_stroke_eraser_dostroke().
+ * from gpencil_paint.c #gpencil_stroke_eraser_dostroke().
* It would be great to de-duplicate the logic here sometime, but that can wait.
*/
-static bool gp_stroke_do_circle_sel(bGPdata *UNUSED(gpd),
- bGPDlayer *gpl,
- bGPDstroke *gps,
- GP_SpaceConversion *gsc,
- const int mx,
- const int my,
- const int radius,
- const bool select,
- rcti *rect,
- const float diff_mat[4][4],
- const int selectmode,
- const float scale)
+static bool gpencil_stroke_do_circle_sel(bGPdata *UNUSED(gpd),
+ bGPDlayer *gpl,
+ bGPDstroke *gps,
+ GP_SpaceConversion *gsc,
+ const int mx,
+ const int my,
+ const int radius,
+ const bool select,
+ rcti *rect,
+ const float diff_mat[4][4],
+ const int selectmode,
+ const float scale)
{
- bGPDspoint *pt1 = NULL;
- bGPDspoint *pt2 = NULL;
- int x0 = 0, y0 = 0, x1 = 0, y1 = 0;
+ bGPDspoint *pt = NULL;
+ int x0 = 0, y0 = 0;
int i;
bool changed = false;
bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps;
bGPDspoint *pt_active = NULL;
+ bool hit = false;
+
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
- if (gps->totpoints == 1) {
bGPDspoint pt_temp;
- gp_point_to_parent_space(gps->points, diff_mat, &pt_temp);
- gp_point_to_xy(gsc, gps, &pt_temp, &x0, &y0);
+ gpencil_point_to_parent_space(pt_active, diff_mat, &pt_temp);
+ gpencil_point_to_xy(gsc, gps, &pt_temp, &x0, &y0);
/* do boundbox check first */
if ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(rect, x0, y0)) {
/* only check if point is inside */
if (((x0 - mx) * (x0 - mx) + (y0 - my) * (y0 - my)) <= radius * radius) {
+ hit = true;
+
/* change selection */
if (select) {
- gps_active->points->flag |= GP_SPOINT_SELECT;
+ pt_active->flag |= GP_SPOINT_SELECT;
gps_active->flag |= GP_STROKE_SELECT;
}
else {
- gps_active->points->flag &= ~GP_SPOINT_SELECT;
+ pt_active->flag &= ~GP_SPOINT_SELECT;
gps_active->flag &= ~GP_STROKE_SELECT;
}
+ changed = true;
+ /* if stroke mode, don't check more points */
+ if ((hit) && (selectmode == GP_SELECTMODE_STROKE)) {
+ break;
+ }
- return true;
- }
- }
- }
- else {
- /* Loop over the points in the stroke, checking for intersections
- * - an intersection means that we touched the stroke
- */
- bool hit = false;
- for (i = 0; (i + 1) < gps->totpoints; i++) {
- /* get points to work with */
- pt1 = gps->points + i;
- pt2 = gps->points + i + 1;
- bGPDspoint npt;
- gp_point_to_parent_space(pt1, diff_mat, &npt);
- gp_point_to_xy(gsc, gps, &npt, &x0, &y0);
-
- gp_point_to_parent_space(pt2, diff_mat, &npt);
- gp_point_to_xy(gsc, gps, &npt, &x1, &y1);
-
- /* check that point segment of the boundbox of the selection stroke */
- if (((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(rect, x0, y0)) ||
- ((!ELEM(V2D_IS_CLIPPED, x1, y1)) && BLI_rcti_isect_pt(rect, x1, y1))) {
- float mval[2] = {(float)mx, (float)my};
-
- /* check if point segment of stroke had anything to do with
- * eraser region (either within stroke painted, or on its lines)
- * - this assumes that linewidth is irrelevant
- */
- if (gp_stroke_inside_circle(mval, radius, x0, y0, x1, y1)) {
- /* change selection of stroke, and then of both points
- * (as the last point otherwise wouldn't get selected
- * as we only do n-1 loops through).
- */
- hit = true;
- if (select) {
- pt_active = pt1->runtime.pt_orig;
- if (pt_active != NULL) {
- pt_active->flag |= GP_SPOINT_SELECT;
- }
- pt_active = pt2->runtime.pt_orig;
- if (pt_active != NULL) {
- pt_active->flag |= GP_SPOINT_SELECT;
- }
- changed = true;
- }
- else {
- pt_active = pt1->runtime.pt_orig;
- if (pt_active != NULL) {
- pt_active->flag &= ~GP_SPOINT_SELECT;
- }
- pt_active = pt2->runtime.pt_orig;
- if (pt_active != NULL) {
- pt_active->flag &= ~GP_SPOINT_SELECT;
- }
- changed = true;
- }
+ /* Expand selection to segment. */
+ if ((hit) && (selectmode == GP_SELECTMODE_SEGMENT) && (select) && (pt_active != NULL)) {
+ float r_hita[3], r_hitb[3];
+ bool hit_select = (bool)(pt_active->flag & GP_SPOINT_SELECT);
+ ED_gpencil_select_stroke_segment(
+ gpl, gps_active, pt_active, hit_select, false, scale, r_hita, r_hitb);
}
}
- /* if stroke mode, don't check more points */
- if ((hit) && (selectmode == GP_SELECTMODE_STROKE)) {
- break;
- }
}
+ }
- /* if stroke mode expand selection */
- if ((hit) && (selectmode == GP_SELECTMODE_STROKE)) {
- for (i = 0, pt1 = gps->points; i < gps->totpoints; i++, pt1++) {
- pt_active = (pt1->runtime.pt_orig) ? pt1->runtime.pt_orig : pt1;
- if (pt_active != NULL) {
- if (select) {
- pt_active->flag |= GP_SPOINT_SELECT;
- }
- else {
- pt_active->flag &= ~GP_SPOINT_SELECT;
- }
+ /* If stroke mode expand selection. */
+ if ((hit) && (selectmode == GP_SELECTMODE_STROKE)) {
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
+ if (pt_active != NULL) {
+ if (select) {
+ pt_active->flag |= GP_SPOINT_SELECT;
+ }
+ else {
+ pt_active->flag &= ~GP_SPOINT_SELECT;
}
}
}
-
- /* expand selection to segment */
- pt_active = (pt1->runtime.pt_orig) ? pt1->runtime.pt_orig : pt1;
- if ((hit) && (selectmode == GP_SELECTMODE_SEGMENT) && (select) && (pt_active != NULL)) {
- float r_hita[3], r_hitb[3];
- bool hit_select = (bool)(pt1->flag & GP_SPOINT_SELECT);
- ED_gpencil_select_stroke_segment(
- gpl, gps_active, pt_active, hit_select, false, scale, r_hita, r_hitb);
- }
-
- /* Ensure that stroke selection is in sync with its points */
- BKE_gpencil_stroke_sync_selection(gps_active);
}
+ /* Ensure that stroke selection is in sync with its points. */
+ BKE_gpencil_stroke_sync_selection(gps_active);
+
return changed;
}
@@ -1043,7 +988,7 @@ static int gpencil_circle_select_exec(bContext *C, wmOperator *op)
}
/* init space conversion stuff */
- gp_point_conversion_init(C, &gsc);
+ gpencil_point_conversion_init(C, &gsc);
/* rect is rectangle of selection circle */
rect.xmin = mx - radius;
@@ -1053,18 +998,18 @@ static int gpencil_circle_select_exec(bContext *C, wmOperator *op)
/* find visible strokes, and select if hit */
GP_EVALUATED_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
- changed |= gp_stroke_do_circle_sel(gpd,
- gpl,
- gps,
- &gsc,
- mx,
- my,
- radius,
- select,
- &rect,
- gpstroke_iter.diff_mat,
- selectmode,
- scale);
+ changed |= gpencil_stroke_do_circle_sel(gpd,
+ gpl,
+ gps,
+ &gsc,
+ mx,
+ my,
+ radius,
+ select,
+ &rect,
+ gpstroke_iter.diff_mat,
+ selectmode,
+ scale);
}
GP_EVALUATED_STROKES_END(gpstroke_iter);
@@ -1157,7 +1102,7 @@ static int gpencil_generic_select_exec(
}
/* init space conversion stuff */
- gp_point_conversion_init(C, &gsc);
+ gpencil_point_conversion_init(C, &gsc);
/* deselect all strokes first? */
if (SEL_OP_USE_PRE_DESELECT(sel_op) || (GPENCIL_PAINT_MODE(gpd))) {
@@ -1255,7 +1200,7 @@ static int gpencil_generic_select_exec(
/* if paint mode,delete selected points */
if (GPENCIL_PAINT_MODE(gpd)) {
- gp_delete_selected_point_wrap(C);
+ gpencil_delete_selected_point_wrap(C);
changed = true;
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
}
@@ -1292,8 +1237,8 @@ static bool gpencil_test_box(bGPDstroke *gps,
const struct GP_SelectBoxUserData *data = user_data;
bGPDspoint pt2;
int x0, y0;
- gp_point_to_parent_space(pt, diff_mat, &pt2);
- gp_point_to_xy(gsc, gps, &pt2, &x0, &y0);
+ gpencil_point_to_parent_space(pt, diff_mat, &pt2);
+ gpencil_point_to_xy(gsc, gps, &pt2, &x0, &y0);
return ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(&data->rect, x0, y0));
}
@@ -1349,8 +1294,8 @@ static bool gpencil_test_lasso(bGPDstroke *gps,
const struct GP_SelectLassoUserData *data = user_data;
bGPDspoint pt2;
int x0, y0;
- gp_point_to_parent_space(pt, diff_mat, &pt2);
- gp_point_to_xy(gsc, gps, &pt2, &x0, &y0);
+ gpencil_point_to_parent_space(pt, diff_mat, &pt2);
+ gpencil_point_to_xy(gsc, gps, &pt2, &x0, &y0);
/* test if in lasso boundbox + within the lasso noose */
return ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(&data->rect, x0, y0) &&
BLI_lasso_is_point_inside(data->mcoords, data->mcoords_len, x0, y0, INT_MAX));
@@ -1431,7 +1376,6 @@ static int gpencil_select_exec(bContext *C, wmOperator *op)
bGPdata *gpd = ED_gpencil_data_get_active(C);
ToolSettings *ts = CTX_data_tool_settings(C);
const float scale = ts->gp_sculpt.isect_threshold;
- const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
/* "radius" is simply a threshold (screen space) to make it easier to test with a tolerance */
const float radius = 0.4f * U.widget_unit;
@@ -1473,7 +1417,7 @@ static int gpencil_select_exec(bContext *C, wmOperator *op)
}
/* init space conversion stuff */
- gp_point_conversion_init(C, &gsc);
+ gpencil_point_conversion_init(C, &gsc);
/* get mouse location */
RNA_int_get_array(op->ptr, "location", mval);
@@ -1484,24 +1428,13 @@ static int gpencil_select_exec(bContext *C, wmOperator *op)
bGPDspoint *pt;
int i;
- /* Check boundbox to speedup. */
- float fmval[2];
- copy_v2fl_v2i(fmval, mval);
- if (!ED_gpencil_stroke_check_collision(
- &gsc, gps_active, fmval, radius, gpstroke_iter.diff_mat)) {
- continue;
- }
-
/* firstly, check for hit-point */
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
int xy[2];
- if ((!is_multiedit) && (pt->runtime.pt_orig == NULL)) {
- continue;
- }
bGPDspoint pt2;
- gp_point_to_parent_space(pt, gpstroke_iter.diff_mat, &pt2);
- gp_point_to_xy(&gsc, gps, &pt2, &xy[0], &xy[1]);
+ gpencil_point_to_parent_space(pt, gpstroke_iter.diff_mat, &pt2);
+ gpencil_point_to_xy(&gsc, gps, &pt2, &xy[0], &xy[1]);
/* do boundbox check first */
if (!ELEM(V2D_IS_CLIPPED, xy[0], xy[1])) {
@@ -1513,27 +1446,12 @@ static int gpencil_select_exec(bContext *C, wmOperator *op)
if (pt_distance < hit_distance) {
hit_layer = gpl;
hit_stroke = gps_active;
- hit_point = (!is_multiedit) ? pt->runtime.pt_orig : pt;
+ hit_point = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
hit_distance = pt_distance;
}
}
}
}
- if (ELEM(NULL, hit_stroke, hit_point)) {
- /* If nothing hit, check if the mouse is inside any filled stroke.
- * Only check filling materials. */
- MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1);
- if ((gp_style->flag & GP_MATERIAL_FILL_SHOW) == 0) {
- continue;
- }
- bool hit_fill = ED_gpencil_stroke_point_is_inside(gps, &gsc, mval, gpstroke_iter.diff_mat);
- if (hit_fill) {
- hit_stroke = gps_active;
- hit_point = &gps_active->points[0];
- /* Extend selection to all stroke. */
- whole = true;
- }
- }
}
GP_EVALUATED_STROKES_END(gpstroke_iter);
diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c
index 467b7ee86bf..7f77999a17b 100644
--- a/source/blender/editors/gpencil/gpencil_utils.c
+++ b/source/blender/editors/gpencil/gpencil_utils.c
@@ -94,7 +94,7 @@
/* Context Wrangling... */
/**
- * Get pointer to active Grease Pencil datablock,
+ * Get pointer to active Grease Pencil data-block,
* and an RNA-pointer to trace back to whatever owns it,
* when context info is not available.
*/
@@ -107,7 +107,7 @@ bGPdata **ED_gpencil_data_get_pointers_direct(ScrArea *area, Object *ob, Pointer
switch (area->spacetype) {
case SPACE_PROPERTIES: /* properties */
case SPACE_INFO: /* header info */
- case SPACE_TOPBAR: /* Topbar */
+ case SPACE_TOPBAR: /* Top-bar */
case SPACE_VIEW3D: /* 3D-View */
{
if (ob && (ob->type == OB_GPENCIL)) {
@@ -132,7 +132,7 @@ bGPdata **ED_gpencil_data_get_pointers_direct(ScrArea *area, Object *ob, Pointer
}
/**
- * Get pointer to active Grease Pencil datablock for annotations,
+ * Get pointer to active Grease Pencil data-block for annotations,
* and an RNA-pointer to trace back to whatever owns it,
* when context info is not available.
*/
@@ -154,7 +154,7 @@ bGPdata **ED_annotation_data_get_pointers_direct(ID *screen_id,
break;
}
- case SPACE_TOPBAR: /* Topbar */
+ case SPACE_TOPBAR: /* Top-bar */
case SPACE_VIEW3D: /* 3D-View */
{
if (r_ptr) {
@@ -237,8 +237,10 @@ bGPdata **ED_annotation_data_get_pointers_direct(ID *screen_id,
return NULL;
}
-/* Get pointer to active Grease Pencil datablock,
- * and an RNA-pointer to trace back to whatever owns it. */
+/**
+ * Get pointer to active Grease Pencil data-block,
+ * and an RNA-pointer to trace back to whatever owns it.
+ */
bGPdata **ED_gpencil_data_get_pointers(const bContext *C, PointerRNA *r_ptr)
{
ScrArea *area = CTX_wm_area(C);
@@ -247,8 +249,10 @@ bGPdata **ED_gpencil_data_get_pointers(const bContext *C, PointerRNA *r_ptr)
return ED_gpencil_data_get_pointers_direct(area, ob, r_ptr);
}
-/* Get pointer to active Grease Pencil datablock,
- * and an RNA-pointer to trace back to whatever owns it. */
+/**
+ * Get pointer to active Grease Pencil data-block,
+ * and an RNA-pointer to trace back to whatever owns it.
+ */
bGPdata **ED_annotation_data_get_pointers(const bContext *C, PointerRNA *r_ptr)
{
ID *screen_id = (ID *)CTX_wm_screen(C);
@@ -259,14 +263,14 @@ bGPdata **ED_annotation_data_get_pointers(const bContext *C, PointerRNA *r_ptr)
}
/* -------------------------------------------------------- */
-/* Get the active Grease Pencil datablock, when context is not available */
+/* Get the active Grease Pencil data-block, when context is not available */
bGPdata *ED_gpencil_data_get_active_direct(ScrArea *area, Object *ob)
{
bGPdata **gpd_ptr = ED_gpencil_data_get_pointers_direct(area, ob, NULL);
return (gpd_ptr) ? *(gpd_ptr) : NULL;
}
-/* Get the active Grease Pencil datablock, when context is not available */
+/* Get the active Grease Pencil data-block, when context is not available */
bGPdata *ED_annotation_data_get_active_direct(ID *screen_id, ScrArea *area, Scene *scene)
{
bGPdata **gpd_ptr = ED_annotation_data_get_pointers_direct(screen_id, area, scene, NULL);
@@ -274,7 +278,7 @@ bGPdata *ED_annotation_data_get_active_direct(ID *screen_id, ScrArea *area, Scen
}
/**
- * Get the active Grease Pencil datablock
+ * Get the active Grease Pencil data-block
*/
bGPdata *ED_gpencil_data_get_active(const bContext *C)
{
@@ -285,9 +289,10 @@ bGPdata *ED_gpencil_data_get_active(const bContext *C)
return ob->data;
}
-/* Get the active Grease Pencil datablock
- * \note This is the original (bmain) copy of the datablock, stored in files.
- * Do not use for reading evaluated copies of GP Objects data
+/**
+ * Get the active Grease Pencil data-block
+ * \note This is the original (#G.main) copy of the data-block, stored in files.
+ * Do not use for reading evaluated copies of GP Objects data.
*/
bGPdata *ED_annotation_data_get_active(const bContext *C)
{
@@ -295,11 +300,11 @@ bGPdata *ED_annotation_data_get_active(const bContext *C)
return (gpd_ptr) ? *(gpd_ptr) : NULL;
}
/**
- * Get the evaluated copy of the active Grease Pencil datablock (where applicable)
- * - For the 3D View (i.e. "GP Objects"), this gives the evaluated copy of the GP datablock
- * (i.e. a copy of the active GP datablock for the active object, where modifiers have been
+ * Get the evaluated copy of the active Grease Pencil data-block (where applicable)
+ * - For the 3D View (i.e. "GP Objects"), this gives the evaluated copy of the GP data-block
+ * (i.e. a copy of the active GP data-block for the active object, where modifiers have been
* applied). This is needed to correctly work with "Copy-on-Write".
- * - For all other editors (i.e. "GP Annotations"), this just gives the active datablock
+ * - For all other editors (i.e. "GP Annotations"), this just gives the active data-block
* like for #ED_gpencil_data_get_active()
*/
bGPdata *ED_gpencil_data_get_active_evaluated(const bContext *C)
@@ -322,8 +327,7 @@ bGPdata *ED_gpencil_data_get_active_evaluated(const bContext *C)
bool ED_gpencil_data_owner_is_annotation(PointerRNA *owner_ptr)
{
/* Key Assumption: If the pointer is an object, we're dealing with a GP Object's data.
- * Otherwise, the GP datablock is being used for annotations (i.e. everywhere else)
- */
+ * Otherwise, the GP data-block is being used for annotations (i.e. everywhere else). */
return ((owner_ptr) && (owner_ptr->type != &RNA_Object));
}
@@ -354,7 +358,7 @@ bool ED_gpencil_has_keyframe_v3d(Scene *UNUSED(scene), Object *ob, int cfra)
/* Poll Callbacks */
/* poll callback for adding data/layers - special */
-bool gp_add_poll(bContext *C)
+bool gpencil_add_poll(bContext *C)
{
Object *ob = CTX_data_active_object(C);
if (ob == NULL) {
@@ -366,7 +370,7 @@ bool gp_add_poll(bContext *C)
}
/* poll callback for checking if there is an active layer */
-bool gp_active_layer_poll(bContext *C)
+bool gpencil_active_layer_poll(bContext *C)
{
Object *ob = CTX_data_active_object(C);
if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
@@ -379,7 +383,7 @@ bool gp_active_layer_poll(bContext *C)
}
/* poll callback for checking if there is an active brush */
-bool gp_active_brush_poll(bContext *C)
+bool gpencil_active_brush_poll(bContext *C)
{
ToolSettings *ts = CTX_data_tool_settings(C);
Paint *paint = &ts->gp_paint->paint;
@@ -527,13 +531,12 @@ const EnumPropertyItem *ED_gpencil_material_enum_itemf(bContext *C,
* Check whether a given stroke segment is inside a circular brush
*
* \param mval: The current screen-space coordinates (midpoint) of the brush
- * \param mvalo: The previous screen-space coordinates (midpoint) of the brush (NOT CURRENTLY USED)
* \param rad: The radius of the brush
*
* \param x0, y0: The screen-space x and y coordinates of the start of the stroke segment
* \param x1, y1: The screen-space x and y coordinates of the end of the stroke segment
*/
-bool gp_stroke_inside_circle(const float mval[2], int rad, int x0, int y0, int x1, int y1)
+bool gpencil_stroke_inside_circle(const float mval[2], int rad, int x0, int y0, int x1, int y1)
{
/* simple within-radius check for now */
const float screen_co_a[2] = {x0, y0};
@@ -611,7 +614,7 @@ bool ED_gpencil_stroke_color_use(Object *ob, const bGPDlayer *gpl, const bGPDstr
*
* \param r_gsc: [out] The space conversion settings struct, populated with necessary params
*/
-void gp_point_conversion_init(bContext *C, GP_SpaceConversion *r_gsc)
+void gpencil_point_conversion_init(bContext *C, GP_SpaceConversion *r_gsc)
{
ScrArea *area = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
@@ -658,7 +661,9 @@ void gp_point_conversion_init(bContext *C, GP_SpaceConversion *r_gsc)
* \param diff_mat: Matrix with the difference between original parent matrix
* \param[out] r_pt: Pointer to new point after apply matrix
*/
-void gp_point_to_parent_space(const bGPDspoint *pt, const float diff_mat[4][4], bGPDspoint *r_pt)
+void gpencil_point_to_parent_space(const bGPDspoint *pt,
+ const float diff_mat[4][4],
+ bGPDspoint *r_pt)
{
float fpt[3];
@@ -669,7 +674,7 @@ void gp_point_to_parent_space(const bGPDspoint *pt, const float diff_mat[4][4],
/**
* Change position relative to parent object
*/
-void gp_apply_parent(Depsgraph *depsgraph, Object *obact, bGPDlayer *gpl, bGPDstroke *gps)
+void gpencil_apply_parent(Depsgraph *depsgraph, Object *obact, bGPDlayer *gpl, bGPDstroke *gps)
{
bGPDspoint *pt;
int i;
@@ -692,7 +697,10 @@ void gp_apply_parent(Depsgraph *depsgraph, Object *obact, bGPDlayer *gpl, bGPDst
/**
* Change point position relative to parent object
*/
-void gp_apply_parent_point(Depsgraph *depsgraph, Object *obact, bGPDlayer *gpl, bGPDspoint *pt)
+void gpencil_apply_parent_point(Depsgraph *depsgraph,
+ Object *obact,
+ bGPDlayer *gpl,
+ bGPDspoint *pt)
{
/* undo matrix */
float diff_mat[4][4];
@@ -715,7 +723,7 @@ void gp_apply_parent_point(Depsgraph *depsgraph, Object *obact, bGPDlayer *gpl,
* \warning This assumes that the caller has already checked
* whether the stroke in question can be drawn.
*/
-void gp_point_to_xy(
+void gpencil_point_to_xy(
const GP_SpaceConversion *gsc, const bGPDstroke *gps, const bGPDspoint *pt, int *r_x, int *r_y)
{
const ARegion *region = gsc->region;
@@ -760,21 +768,21 @@ void gp_point_to_xy(
/**
* Convert a Grease Pencil coordinate (i.e. can be 2D or 3D) to screenspace (2D).
*
- * Just like #gp_point_to_xy(), except the resulting coordinates are floats not ints.
+ * Just like #gpencil_point_to_xy(), except the resulting coordinates are floats not ints.
* Use this version to solve "stair-step" artifacts which may arise when
* roundtripping the calculations.
*
- * \param r_x[out]: The screen-space x-coordinate of the point.
- * \param r_y[out]: The screen-space y-coordinate of the point.
+ * \param r_x: The screen-space x-coordinate of the point.
+ * \param r_y: The screen-space y-coordinate of the point.
*
* \warning This assumes that the caller has already checked
* whether the stroke in question can be drawn.
*/
-void gp_point_to_xy_fl(const GP_SpaceConversion *gsc,
- const bGPDstroke *gps,
- const bGPDspoint *pt,
- float *r_x,
- float *r_y)
+void gpencil_point_to_xy_fl(const GP_SpaceConversion *gsc,
+ const bGPDstroke *gps,
+ const bGPDspoint *pt,
+ float *r_x,
+ float *r_y)
{
const ARegion *region = gsc->region;
const View2D *v2d = gsc->v2d;
@@ -828,12 +836,12 @@ void gp_point_to_xy_fl(const GP_SpaceConversion *gsc,
}
/**
- * generic based on gp_point_to_xy_fl
+ * generic based on gpencil_point_to_xy_fl
*/
-void gp_point_3d_to_xy(const GP_SpaceConversion *gsc,
- const short flag,
- const float pt[3],
- float xy[2])
+void gpencil_point_3d_to_xy(const GP_SpaceConversion *gsc,
+ const short flag,
+ const float pt[3],
+ float xy[2])
{
const ARegion *region = gsc->region;
const View2D *v2d = gsc->v2d;
@@ -899,10 +907,10 @@ void gp_point_3d_to_xy(const GP_SpaceConversion *gsc,
*
* \warning Assumes that it is getting called in a 3D view only.
*/
-bool gp_point_xy_to_3d(const GP_SpaceConversion *gsc,
- Scene *scene,
- const float screen_co[2],
- float r_out[3])
+bool gpencil_point_xy_to_3d(const GP_SpaceConversion *gsc,
+ Scene *scene,
+ const float screen_co[2],
+ float r_out[3])
{
const RegionView3D *rv3d = gsc->region->regiondata;
float rvec[3];
@@ -935,16 +943,16 @@ bool gp_point_xy_to_3d(const GP_SpaceConversion *gsc,
* Convert tGPspoint (temporary 2D/screenspace point data used by GP modal operators)
* to 3D coordinates.
*
- * \param point2D: The screenspace 2D point data to convert.
+ * \param point2D: The screen-space 2D point data to convert.
* \param depth: Depth array (via #ED_view3d_autodist_depth()).
- * \param[out] r_out: The resulting 2D point data.
+ * \param r_out: The resulting 2D point data.
*/
-void gp_stroke_convertcoords_tpoint(Scene *scene,
- ARegion *region,
- Object *ob,
- const tGPspoint *point2D,
- float *depth,
- float r_out[3])
+void gpencil_stroke_convertcoords_tpoint(Scene *scene,
+ ARegion *region,
+ Object *ob,
+ const tGPspoint *point2D,
+ float *depth,
+ float r_out[3])
{
ToolSettings *ts = scene->toolsettings;
@@ -982,7 +990,7 @@ void gp_stroke_convertcoords_tpoint(Scene *scene,
/**
* Get drawing reference point for conversion or projection of the stroke
- * \param[out] r_vec : Reference point found
+ * \param r_vec: Reference point found
*/
void ED_gpencil_drawing_reference_get(const Scene *scene,
const Object *ob,
@@ -1024,7 +1032,7 @@ void ED_gpencil_project_stroke_to_view(bContext *C, bGPDlayer *gpl, bGPDstroke *
float inverse_diff_mat[4][4];
/* init space conversion stuff */
- gp_point_conversion_init(C, &gsc);
+ gpencil_point_conversion_init(C, &gsc);
BKE_gpencil_parent_matrix_get(depsgraph, ob, gpl, diff_mat);
invert_m4_m4(inverse_diff_mat, diff_mat);
@@ -1034,11 +1042,11 @@ void ED_gpencil_project_stroke_to_view(bContext *C, bGPDlayer *gpl, bGPDstroke *
float xy[2];
bGPDspoint pt2;
- gp_point_to_parent_space(pt, diff_mat, &pt2);
- gp_point_to_xy_fl(&gsc, gps, &pt2, &xy[0], &xy[1]);
+ gpencil_point_to_parent_space(pt, diff_mat, &pt2);
+ gpencil_point_to_xy_fl(&gsc, gps, &pt2, &xy[0], &xy[1]);
/* Planar - All on same plane parallel to the viewplane */
- gp_point_xy_to_3d(&gsc, scene, xy, &pt->x);
+ gpencil_point_xy_to_3d(&gsc, scene, xy, &pt->x);
/* Unapply parent corrections */
mul_m4_v3(inverse_diff_mat, &pt->x);
@@ -1048,12 +1056,12 @@ void ED_gpencil_project_stroke_to_view(bContext *C, bGPDlayer *gpl, bGPDstroke *
/**
* Reproject all points of the stroke to a plane locked to axis to avoid stroke offset
*/
-void ED_gp_project_stroke_to_plane(const Scene *scene,
- const Object *ob,
- const RegionView3D *rv3d,
- bGPDstroke *gps,
- const float origin[3],
- const int axis)
+void ED_gpencil_project_stroke_to_plane(const Scene *scene,
+ const Object *ob,
+ const RegionView3D *rv3d,
+ bGPDstroke *gps,
+ const float origin[3],
+ const int axis)
{
const ToolSettings *ts = scene->toolsettings;
const View3DCursor *cursor = &scene->cursor;
@@ -1165,13 +1173,13 @@ void ED_gpencil_stroke_reproject(Depsgraph *depsgraph,
float xy[2];
/* 3D to Screen-space */
- /* Note: We can't use gp_point_to_xy() here because that uses ints for the screen-space
+ /* Note: We can't use gpencil_point_to_xy() here because that uses ints for the screen-space
* coordinates, resulting in lost precision, which in turn causes stair-stepping
* artifacts in the final points. */
bGPDspoint pt2;
- gp_point_to_parent_space(pt, diff_mat, &pt2);
- gp_point_to_xy_fl(gsc, gps_active, &pt2, &xy[0], &xy[1]);
+ gpencil_point_to_parent_space(pt, diff_mat, &pt2);
+ gpencil_point_to_xy_fl(gsc, gps_active, &pt2, &xy[0], &xy[1]);
/* Project stroke in one axis */
if (ELEM(mode, GP_REPROJECT_FRONT, GP_REPROJECT_SIDE, GP_REPROJECT_TOP, GP_REPROJECT_CURSOR)) {
@@ -1199,18 +1207,18 @@ void ED_gpencil_stroke_reproject(Depsgraph *depsgraph,
}
}
- ED_gp_project_point_to_plane(gsc->scene, gsc->ob, rv3d, origin, axis, &pt2);
+ ED_gpencil_project_point_to_plane(gsc->scene, gsc->ob, rv3d, origin, axis, &pt2);
copy_v3_v3(&pt->x, &pt2.x);
/* apply parent again */
- gp_apply_parent_point(depsgraph, gsc->ob, gpl, pt);
+ gpencil_apply_parent_point(depsgraph, gsc->ob, gpl, pt);
}
/* Project screen-space back to 3D space (from current perspective)
* so that all points have been treated the same way. */
else if (mode == GP_REPROJECT_VIEW) {
/* Planar - All on same plane parallel to the view-plane. */
- gp_point_xy_to_3d(gsc, gsc->scene, xy, &pt->x);
+ gpencil_point_xy_to_3d(gsc, gsc->scene, xy, &pt->x);
}
else {
/* Geometry - Snap to surfaces of visible geometry */
@@ -1238,7 +1246,7 @@ void ED_gpencil_stroke_reproject(Depsgraph *depsgraph,
}
else {
/* Default to planar */
- gp_point_xy_to_3d(gsc, gsc->scene, xy, &pt->x);
+ gpencil_point_xy_to_3d(gsc, gsc->scene, xy, &pt->x);
}
}
@@ -1251,14 +1259,14 @@ void ED_gpencil_stroke_reproject(Depsgraph *depsgraph,
/**
* Reproject given point to a plane locked to axis to avoid stroke offset
- * \param[in,out] pt: Point to affect
+ * \param pt: Point to affect (used for input & output).
*/
-void ED_gp_project_point_to_plane(const Scene *scene,
- const Object *ob,
- const RegionView3D *rv3d,
- const float origin[3],
- const int axis,
- bGPDspoint *pt)
+void ED_gpencil_project_point_to_plane(const Scene *scene,
+ const Object *ob,
+ const RegionView3D *rv3d,
+ const float origin[3],
+ const int axis,
+ bGPDspoint *pt)
{
const ToolSettings *ts = scene->toolsettings;
const View3DCursor *cursor = &scene->cursor;
@@ -1330,7 +1338,7 @@ void ED_gp_project_point_to_plane(const Scene *scene,
* \param gps: Stroke data
* \param subdivide: Number of times to subdivide
*/
-void gp_subdivide_stroke(bGPDstroke *gps, const int subdivide)
+void gpencil_subdivide_stroke(bGPDstroke *gps, const int subdivide)
{
bGPDspoint *temp_points;
int totnewpoints, oldtotpoints;
@@ -1730,7 +1738,7 @@ void ED_gpencil_vgroup_deselect(bContext *C, Object *ob)
/* Cursor drawing */
/* check if cursor is in drawing region */
-static bool gp_check_cursor_region(bContext *C, int mval_i[2])
+static bool gpencil_check_cursor_region(bContext *C, int mval_i[2])
{
ARegion *region = CTX_wm_region(C);
ScrArea *area = CTX_wm_area(C);
@@ -1802,7 +1810,7 @@ void ED_gpencil_brush_draw_eraser(Brush *brush, int x, int y)
GPU_line_smooth(false);
}
-static bool gp_brush_cursor_poll(bContext *C)
+static bool gpencil_brush_cursor_poll(bContext *C)
{
if (WM_toolsystem_active_tool_is_brush(C)) {
return true;
@@ -1810,8 +1818,10 @@ static bool gp_brush_cursor_poll(bContext *C)
return false;
}
-/* Helper callback for drawing the cursor itself */
-static void gp_brush_cursor_draw(bContext *C, int x, int y, void *customdata)
+/**
+ * Helper callback for drawing the cursor itself.
+ */
+static void gpencil_brush_cursor_draw(bContext *C, int x, int y, void *customdata)
{
Scene *scene = CTX_data_scene(C);
Object *ob = CTX_data_active_object(C);
@@ -1830,8 +1840,8 @@ static void gp_brush_cursor_draw(bContext *C, int x, int y, void *customdata)
float radius = 3.0f;
int mval_i[2] = {x, y};
- /* check if cursor is in drawing region and has valid datablock */
- if ((!gp_check_cursor_region(C, mval_i)) || (gpd == NULL)) {
+ /* Check if cursor is in drawing region and has valid data-block. */
+ if ((!gpencil_check_cursor_region(C, mval_i)) || (gpd == NULL)) {
return;
}
@@ -2016,8 +2026,8 @@ void ED_gpencil_toggle_brush_cursor(bContext *C, bool enable, void *customdata)
/* enable cursor */
gset->paintcursor = WM_paint_cursor_activate(SPACE_TYPE_ANY,
RGN_TYPE_ANY,
- gp_brush_cursor_poll,
- gp_brush_cursor_draw,
+ gpencil_brush_cursor_poll,
+ gpencil_brush_cursor_draw,
(lastpost) ? customdata : NULL);
}
}
@@ -2081,7 +2091,9 @@ void ED_gpencil_setup_modes(bContext *C, bGPdata *gpd, int newmode)
}
}
-/* helper to convert 2d to 3d for simple drawing buffer */
+/**
+ * Helper to convert 2d to 3d for simple drawing buffer.
+ */
static void gpencil_stroke_convertcoords(ARegion *region,
const tGPspoint *point2D,
const float origin[3],
@@ -2107,7 +2119,9 @@ static void gpencil_stroke_convertcoords(ARegion *region,
}
}
-/* convert 2d tGPspoint to 3d bGPDspoint */
+/**
+ * Convert 2d #tGPspoint to 3d #bGPDspoint.
+ */
void ED_gpencil_tpoint_to_point(ARegion *region,
float origin[3],
const tGPspoint *tpt,
@@ -2125,7 +2139,9 @@ void ED_gpencil_tpoint_to_point(ARegion *region,
pt->uv_rot = tpt->uv_rot;
}
-/* recalc uv for any stroke using the material */
+/**
+ * Recalculate UV for any stroke using the material.
+ */
void ED_gpencil_update_color_uv(Main *bmain, Material *mat)
{
Material *gps_ma = NULL;
@@ -2212,7 +2228,8 @@ static bool gpencil_check_collision(bGPDstroke *gps,
return hit;
}
-static void gp_copy_points(bGPDstroke *gps, bGPDspoint *pt, bGPDspoint *pt_final, int i, int i2)
+static void gpencil_copy_points(
+ bGPDstroke *gps, bGPDspoint *pt, bGPDspoint *pt_final, int i, int i2)
{
/* don't copy same point */
if (i == i2) {
@@ -2244,7 +2261,7 @@ static void gp_copy_points(bGPDstroke *gps, bGPDspoint *pt, bGPDspoint *pt_final
}
}
-static void gp_insert_point(
+static void gpencil_insert_point(
bGPDstroke *gps, bGPDspoint *a_pt, bGPDspoint *b_pt, const float co_a[3], float co_b[3])
{
bGPDspoint *temp_points;
@@ -2287,16 +2304,16 @@ static void gp_insert_point(
for (int i = 0; i < oldtotpoints; i++) {
bGPDspoint *pt = &temp_points[i];
bGPDspoint *pt_final = &gps->points[i2];
- gp_copy_points(gps, pt, pt_final, i, i2);
+ gpencil_copy_points(gps, pt, pt_final, i, i2);
/* create new point duplicating point and copy location */
if ((i == a_idx) || (i == b_idx)) {
i2++;
pt_final = &gps->points[i2];
- gp_copy_points(gps, pt, pt_final, i, i2);
+ gpencil_copy_points(gps, pt, pt_final, i, i2);
copy_v3_v3(&pt_final->x, (i == a_idx) ? co_a : co_b);
- /* unselect */
+ /* Un-select. */
pt_final->flag &= ~GP_SPOINT_SELECT;
/* tag to avoid more checking with this point */
pt_final->flag |= GP_SPOINT_TAG;
@@ -2304,13 +2321,13 @@ static void gp_insert_point(
i2++;
}
- /* Calc geometry data. */
+ /* Calculate geometry data. */
BKE_gpencil_stroke_geometry_update(gps);
MEM_SAFE_FREE(temp_points);
}
-static float gp_calc_factor(float p2d_a1[2], float p2d_a2[2], float r_hit2d[2])
+static float gpencil_calc_factor(float p2d_a1[2], float p2d_a2[2], float r_hit2d[2])
{
float dist1 = len_squared_v2v2(p2d_a1, p2d_a2);
float dist2 = len_squared_v2v2(p2d_a1, r_hit2d);
@@ -2435,7 +2452,7 @@ int ED_gpencil_select_stroke_segment(bGPDlayer *gpl,
}
if (hit_a) {
- f = gp_calc_factor(p2d_a1, p2d_a2, r_hit2d);
+ f = gpencil_calc_factor(p2d_a1, p2d_a2, r_hit2d);
interp_v3_v3v3(r_hita, &pta1->x, &pta2->x, f);
if (f > min_factor) {
hit_pointa = pta2; /* first point is second (inverted loop) */
@@ -2468,7 +2485,7 @@ int ED_gpencil_select_stroke_segment(bGPDlayer *gpl,
}
if (hit_b) {
- f = gp_calc_factor(p2d_a1, p2d_a2, r_hit2d);
+ f = gpencil_calc_factor(p2d_a1, p2d_a2, r_hit2d);
interp_v3_v3v3(r_hitb, &pta1->x, &pta2->x, f);
if (f > min_factor) {
hit_pointb = pta1;
@@ -2482,7 +2499,7 @@ int ED_gpencil_select_stroke_segment(bGPDlayer *gpl,
/* insert new point in the collision points */
if (insert) {
- gp_insert_point(gps, hit_pointa, hit_pointb, r_hita, r_hitb);
+ gpencil_insert_point(gps, hit_pointa, hit_pointb, r_hita, r_hitb);
}
/* free memory */
@@ -2610,7 +2627,10 @@ void ED_gpencil_select_toggle_all(bContext *C, int action)
}
}
-/* Ensure the SBuffer (while drawing stroke) size is enough to save all points of the stroke */
+/**
+ * Ensure the #tGPspoint buffer (while drawing stroke)
+ * size is enough to save all points of the stroke.
+ */
tGPspoint *ED_gpencil_sbuffer_ensure(tGPspoint *buffer_array,
int *buffer_size,
int *buffer_used,
@@ -2661,10 +2681,12 @@ void ED_gpencil_sbuffer_update_eval(bGPdata *gpd, Object *ob_eval)
gpd_eval->runtime.cp_points = gpd->runtime.cp_points;
}
-/* Tag all scene grease pencil object to update. */
+/**
+ * Tag all scene grease pencil object to update.
+ */
void ED_gpencil_tag_scene_gpencil(Scene *scene)
{
- /* mark all grease pencil datablocks of the scene */
+ /* Mark all grease pencil data-blocks of the scene. */
FOREACH_SCENE_COLLECTION_BEGIN (scene, collection) {
FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (collection, ob) {
if (ob->type == OB_GPENCIL) {
@@ -2748,7 +2770,7 @@ void ED_gpencil_init_random_settings(Brush *brush,
random_settings->pressure = BLI_hash_int_01(BLI_hash_int_2d(ix + iz, iy + iz)) * 2.0f - 1.0f;
}
- /* Randomn to color strength. */
+ /* Random to color strength. */
if (brush_settings->draw_random_strength) {
random_settings->strength = BLI_hash_int_01(BLI_hash_int_2d(ix + iy, iy + iz + ix)) * 2.0f -
1.0f;
@@ -2881,8 +2903,8 @@ void ED_gpencil_sbuffer_vertex_color_set(Depsgraph *depsgraph,
/* Random Color. */
gpencil_sbuffer_vertex_color_random(gpd, brush, tpt, random_color, pen_pressure);
- /* Copy to eval data because paint operators don't tag refresh until end for speedup
- painting. */
+ /* Copy to evaluate data because paint operators don't tag refresh until end for speedup
+ * painting. */
if (gpd_eval != NULL) {
copy_v4_v4(gpd_eval->runtime.vert_color_fill, gpd->runtime.vert_color_fill);
gpd_eval->runtime.matid = gpd->runtime.matid;
@@ -2908,12 +2930,12 @@ bool ED_gpencil_stroke_check_collision(GP_SpaceConversion *gsc,
/* Convert bound box to 2d */
copy_v3_v3(&pt_dummy.x, gps->boundbox_min);
- gp_point_to_parent_space(&pt_dummy, diff_mat, &pt_dummy_ps);
- gp_point_to_xy_fl(gsc, gps, &pt_dummy_ps, &boundbox_min[0], &boundbox_min[1]);
+ gpencil_point_to_parent_space(&pt_dummy, diff_mat, &pt_dummy_ps);
+ gpencil_point_to_xy_fl(gsc, gps, &pt_dummy_ps, &boundbox_min[0], &boundbox_min[1]);
copy_v3_v3(&pt_dummy.x, gps->boundbox_max);
- gp_point_to_parent_space(&pt_dummy, diff_mat, &pt_dummy_ps);
- gp_point_to_xy_fl(gsc, gps, &pt_dummy_ps, &boundbox_max[0], &boundbox_max[1]);
+ gpencil_point_to_parent_space(&pt_dummy, diff_mat, &pt_dummy_ps);
+ gpencil_point_to_xy_fl(gsc, gps, &pt_dummy_ps, &boundbox_max[0], &boundbox_max[1]);
/* Ensure the bounding box is oriented to axis. */
if (boundbox_max[0] < boundbox_min[0]) {
@@ -2925,7 +2947,7 @@ bool ED_gpencil_stroke_check_collision(GP_SpaceConversion *gsc,
rcti rect_stroke = {boundbox_min[0], boundbox_max[0], boundbox_min[1], boundbox_max[1]};
- /* For mouse, add a small offet to avoid false negative in corners. */
+ /* For mouse, add a small offset to avoid false negative in corners. */
rcti rect_mouse = {mouse[0] - offset, mouse[0] + offset, mouse[1] - offset, mouse[1] + offset};
/* Check collision between both rectangles. */
@@ -2933,12 +2955,13 @@ bool ED_gpencil_stroke_check_collision(GP_SpaceConversion *gsc,
}
/**
- * Check if a point is inside of the stroke
- * \param gps: Stroke to check
- * \param gsc: SpaceConversion data
- * \param mouse: Mouse position
- * \param diff_mat: View matrix
- * \return True if the point is inside
+ * Check if a point is inside of the stroke.
+ *
+ * \param gps: Stroke to check.
+ * \param gsc: Space conversion data.
+ * \param mouse: Mouse position.
+ * \param diff_mat: View matrix.
+ * \return True if the point is inside.
*/
bool ED_gpencil_stroke_point_is_inside(bGPDstroke *gps,
GP_SpaceConversion *gsc,
@@ -2959,11 +2982,11 @@ bool ED_gpencil_stroke_point_is_inside(bGPDstroke *gps,
int i;
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
bGPDspoint pt2;
- gp_point_to_parent_space(pt, diff_mat, &pt2);
- gp_point_to_xy(gsc, gps, &pt2, &mcoords[i][0], &mcoords[i][1]);
+ gpencil_point_to_parent_space(pt, diff_mat, &pt2);
+ gpencil_point_to_xy(gsc, gps, &pt2, &mcoords[i][0], &mcoords[i][1]);
}
- /* Compute boundbox of lasso (for faster testing later). */
+ /* Compute bound-box of lasso (for faster testing later). */
rcti rect;
BLI_lasso_boundbox(&rect, mcoords, len);
diff --git a/source/blender/editors/gpencil/gpencil_uv.c b/source/blender/editors/gpencil/gpencil_uv.c
index 0dfc7e0728e..e21b2049028 100644
--- a/source/blender/editors/gpencil/gpencil_uv.c
+++ b/source/blender/editors/gpencil/gpencil_uv.c
@@ -148,7 +148,7 @@ static bool gpencil_uv_transform_init(bContext *C, wmOperator *op)
opdata->ob = CTX_data_active_object(C);
opdata->gpd = (bGPdata *)opdata->ob->data;
- gp_point_conversion_init(C, &opdata->gsc);
+ gpencil_point_conversion_init(C, &opdata->gsc);
opdata->array_loc = NULL;
opdata->array_rot = NULL;
opdata->array_scale = NULL;
@@ -197,7 +197,7 @@ static bool gpencil_uv_transform_init(bContext *C, wmOperator *op)
GP_EDITABLE_STROKES_END(gpstroke_iter);
}
/* Convert to 2D. */
- gp_point_3d_to_xy(&opdata->gsc, GP_STROKE_3DSPACE, center, opdata->mcenter);
+ gpencil_point_3d_to_xy(&opdata->gsc, GP_STROKE_3DSPACE, center, opdata->mcenter);
return true;
}
diff --git a/source/blender/editors/gpencil/gpencil_vertex_ops.c b/source/blender/editors/gpencil/gpencil_vertex_ops.c
index 4db88bd552f..36ce7d3dc47 100644
--- a/source/blender/editors/gpencil/gpencil_vertex_ops.c
+++ b/source/blender/editors/gpencil/gpencil_vertex_ops.c
@@ -74,7 +74,7 @@ static const EnumPropertyItem gpencil_modesEnumPropertyItem_mode[] = {
};
/* Poll callback for stroke vertex paint operator. */
-static bool gp_vertexpaint_mode_poll(bContext *C)
+static bool gpencil_vertexpaint_mode_poll(bContext *C)
{
ToolSettings *ts = CTX_data_tool_settings(C);
Object *ob = CTX_data_active_object(C);
@@ -97,7 +97,7 @@ static bool gp_vertexpaint_mode_poll(bContext *C)
return false;
}
-static int gp_vertexpaint_brightness_contrast_exec(bContext *C, wmOperator *op)
+static int gpencil_vertexpaint_brightness_contrast_exec(bContext *C, wmOperator *op)
{
Object *ob = CTX_data_active_object(C);
bGPdata *gpd = (bGPdata *)ob->data;
@@ -178,8 +178,8 @@ void GPENCIL_OT_vertex_color_brightness_contrast(wmOperatorType *ot)
ot->description = "Adjust vertex color brightness/contrast";
/* api callbacks */
- ot->exec = gp_vertexpaint_brightness_contrast_exec;
- ot->poll = gp_vertexpaint_mode_poll;
+ ot->exec = gpencil_vertexpaint_brightness_contrast_exec;
+ ot->poll = gpencil_vertexpaint_mode_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -192,7 +192,7 @@ void GPENCIL_OT_vertex_color_brightness_contrast(wmOperatorType *ot)
RNA_def_property_ui_range(prop, min, max, 1, 1);
}
-static int gp_vertexpaint_hsv_exec(bContext *C, wmOperator *op)
+static int gpencil_vertexpaint_hsv_exec(bContext *C, wmOperator *op)
{
Object *ob = CTX_data_active_object(C);
bGPdata *gpd = (bGPdata *)ob->data;
@@ -273,8 +273,8 @@ void GPENCIL_OT_vertex_color_hsv(wmOperatorType *ot)
ot->description = "Adjust vertex color HSV values";
/* api callbacks */
- ot->exec = gp_vertexpaint_hsv_exec;
- ot->poll = gp_vertexpaint_mode_poll;
+ ot->exec = gpencil_vertexpaint_hsv_exec;
+ ot->poll = gpencil_vertexpaint_mode_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -286,7 +286,7 @@ void GPENCIL_OT_vertex_color_hsv(wmOperatorType *ot)
RNA_def_float(ot->srna, "v", 1.0f, 0.0f, 2.0f, "Value", "", 0.0f, 2.0f);
}
-static int gp_vertexpaint_invert_exec(bContext *C, wmOperator *op)
+static int gpencil_vertexpaint_invert_exec(bContext *C, wmOperator *op)
{
Object *ob = CTX_data_active_object(C);
bGPdata *gpd = (bGPdata *)ob->data;
@@ -344,8 +344,8 @@ void GPENCIL_OT_vertex_color_invert(wmOperatorType *ot)
ot->description = "Invert RGB values";
/* api callbacks */
- ot->exec = gp_vertexpaint_invert_exec;
- ot->poll = gp_vertexpaint_mode_poll;
+ ot->exec = gpencil_vertexpaint_invert_exec;
+ ot->poll = gpencil_vertexpaint_mode_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -354,7 +354,7 @@ void GPENCIL_OT_vertex_color_invert(wmOperatorType *ot)
ot->prop = RNA_def_enum(ot->srna, "mode", gpencil_modesEnumPropertyItem_mode, 0, "Mode", "");
}
-static int gp_vertexpaint_levels_exec(bContext *C, wmOperator *op)
+static int gpencil_vertexpaint_levels_exec(bContext *C, wmOperator *op)
{
Object *ob = CTX_data_active_object(C);
bGPdata *gpd = (bGPdata *)ob->data;
@@ -413,8 +413,8 @@ void GPENCIL_OT_vertex_color_levels(wmOperatorType *ot)
ot->description = "Adjust levels of vertex colors";
/* api callbacks */
- ot->exec = gp_vertexpaint_levels_exec;
- ot->poll = gp_vertexpaint_mode_poll;
+ ot->exec = gpencil_vertexpaint_levels_exec;
+ ot->poll = gpencil_vertexpaint_mode_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -428,13 +428,13 @@ void GPENCIL_OT_vertex_color_levels(wmOperatorType *ot)
ot->srna, "gain", 1.0f, 0.0f, FLT_MAX, "Gain", "Value to multiply colors by", 0.0f, 10.0f);
}
-static int gp_vertexpaint_set_exec(bContext *C, wmOperator *op)
+static int gpencil_vertexpaint_set_exec(bContext *C, wmOperator *op)
{
ToolSettings *ts = CTX_data_tool_settings(C);
Object *ob = CTX_data_active_object(C);
bGPdata *gpd = (bGPdata *)ob->data;
Paint *paint = &ts->gp_vertexpaint->paint;
- Brush *brush = brush = paint->brush;
+ Brush *brush = paint->brush;
bool changed = false;
int i;
@@ -485,8 +485,8 @@ void GPENCIL_OT_vertex_color_set(wmOperatorType *ot)
ot->description = "Set active color to all selected vertex";
/* api callbacks */
- ot->exec = gp_vertexpaint_set_exec;
- ot->poll = gp_vertexpaint_mode_poll;
+ ot->exec = gpencil_vertexpaint_set_exec;
+ ot->poll = gpencil_vertexpaint_mode_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -497,7 +497,9 @@ void GPENCIL_OT_vertex_color_set(wmOperatorType *ot)
}
/* Helper to extract color from vertex color to create a palette. */
-static bool gp_extract_palette_from_vertex(bContext *C, const bool selected, const int threshold)
+static bool gpencil_extract_palette_from_vertex(bContext *C,
+ const bool selected,
+ const int threshold)
{
Main *bmain = CTX_data_main(C);
Object *ob = CTX_data_active_object(C);
@@ -657,7 +659,7 @@ static uint get_material_type(MaterialGPencilStyle *gp_style,
return r_i;
}
-static bool gp_material_to_vertex_poll(bContext *C)
+static bool gpencil_material_to_vertex_poll(bContext *C)
{
/* only supported with grease pencil objects */
Object *ob = CTX_data_active_object(C);
@@ -668,7 +670,7 @@ static bool gp_material_to_vertex_poll(bContext *C)
return true;
}
-static int gp_material_to_vertex_exec(bContext *C, wmOperator *op)
+static int gpencil_material_to_vertex_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Object *ob = CTX_data_active_object(C);
@@ -814,7 +816,7 @@ static int gp_material_to_vertex_exec(bContext *C, wmOperator *op)
/* Generate a Palette. */
if (palette) {
- gp_extract_palette_from_vertex(C, selected, 1);
+ gpencil_extract_palette_from_vertex(C, selected, 1);
}
/* Clean unused materials. */
@@ -834,8 +836,8 @@ void GPENCIL_OT_material_to_vertex_color(wmOperatorType *ot)
ot->description = "Replace materials in strokes with Vertex Color";
/* api callbacks */
- ot->exec = gp_material_to_vertex_exec;
- ot->poll = gp_material_to_vertex_poll;
+ ot->exec = gpencil_material_to_vertex_exec;
+ ot->poll = gpencil_material_to_vertex_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -852,7 +854,7 @@ void GPENCIL_OT_material_to_vertex_color(wmOperatorType *ot)
}
/* Extract Palette from Vertex Color. */
-static bool gp_extract_palette_vertex_poll(bContext *C)
+static bool gpencil_extract_palette_vertex_poll(bContext *C)
{
/* only supported with grease pencil objects */
Object *ob = CTX_data_active_object(C);
@@ -863,12 +865,12 @@ static bool gp_extract_palette_vertex_poll(bContext *C)
return true;
}
-static int gp_extract_palette_vertex_exec(bContext *C, wmOperator *op)
+static int gpencil_extract_palette_vertex_exec(bContext *C, wmOperator *op)
{
const bool selected = RNA_boolean_get(op->ptr, "selected");
const int threshold = RNA_int_get(op->ptr, "threshold");
- if (gp_extract_palette_from_vertex(C, selected, threshold)) {
+ if (gpencil_extract_palette_from_vertex(C, selected, threshold)) {
BKE_reportf(op->reports, RPT_INFO, "Palette created");
}
else {
@@ -886,8 +888,8 @@ void GPENCIL_OT_extract_palette_vertex(wmOperatorType *ot)
ot->description = "Extract all colors used in Grease Pencil Vertex and create a Palette";
/* api callbacks */
- ot->exec = gp_extract_palette_vertex_exec;
- ot->poll = gp_extract_palette_vertex_poll;
+ ot->exec = gpencil_extract_palette_vertex_exec;
+ ot->poll = gpencil_extract_palette_vertex_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
diff --git a/source/blender/editors/gpencil/gpencil_vertex_paint.c b/source/blender/editors/gpencil/gpencil_vertex_paint.c
index fe3f0871fdc..99d55350527 100644
--- a/source/blender/editors/gpencil/gpencil_vertex_paint.c
+++ b/source/blender/editors/gpencil/gpencil_vertex_paint.c
@@ -263,7 +263,7 @@ static void brush_calc_dvec_2d(tGP_BrushVertexpaintData *gso)
* number of pixels (see: GP_GRID_PIXEL_SIZE)
*/
-static void gp_grid_cells_init(tGP_BrushVertexpaintData *gso)
+static void gpencil_grid_cells_init(tGP_BrushVertexpaintData *gso)
{
tGP_Grid *grid;
float bottom[2];
@@ -298,7 +298,7 @@ static void gp_grid_cells_init(tGP_BrushVertexpaintData *gso)
}
/* Get the index used in the grid base on dvec. */
-static void gp_grid_cell_average_color_idx_get(tGP_BrushVertexpaintData *gso, int r_idx[2])
+static void gpencil_grid_cell_average_color_idx_get(tGP_BrushVertexpaintData *gso, int r_idx[2])
{
/* Lower direction. */
if (gso->dvec[1] < 0.0f) {
@@ -348,7 +348,7 @@ static void gp_grid_cell_average_color_idx_get(tGP_BrushVertexpaintData *gso, in
}
}
-static int gp_grid_cell_index_get(tGP_BrushVertexpaintData *gso, int pc[2])
+static int gpencil_grid_cell_index_get(tGP_BrushVertexpaintData *gso, int pc[2])
{
float bottom[2], top[2];
@@ -366,7 +366,7 @@ static int gp_grid_cell_index_get(tGP_BrushVertexpaintData *gso, int pc[2])
}
/* Fill the grid with the color in each cell and assign point cell index. */
-static void gp_grid_colors_calc(tGP_BrushVertexpaintData *gso)
+static void gpencil_grid_colors_calc(tGP_BrushVertexpaintData *gso)
{
tGP_Selected *selected = NULL;
bGPDstroke *gps_selected = NULL;
@@ -383,7 +383,7 @@ static void gp_grid_colors_calc(tGP_BrushVertexpaintData *gso)
selected = &gso->pbuffer[i];
gps_selected = selected->gps;
pt = &gps_selected->points[selected->pt_index];
- int grid_index = gp_grid_cell_index_get(gso, selected->pc);
+ int grid_index = gpencil_grid_cell_index_get(gso, selected->pc);
if (grid_index > -1) {
grid = &gso->grid[grid_index];
@@ -639,7 +639,7 @@ static bool brush_smear_apply(tGP_BrushVertexpaintData *gso,
/* Need get average colors in the grid. */
if ((!gso->grid_ready) && (gso->pbuffer_used > 0)) {
- gp_grid_colors_calc(gso);
+ gpencil_grid_colors_calc(gso);
}
/* The influence is equal to strength and no decay around brush radius. */
@@ -657,10 +657,10 @@ static bool brush_smear_apply(tGP_BrushVertexpaintData *gso,
inf *= fac;
/* Retry row and col for average color. */
- gp_grid_cell_average_color_idx_get(gso, average_idx);
+ gpencil_grid_cell_average_color_idx_get(gso, average_idx);
/* Retry average color cell. */
- int grid_index = gp_grid_cell_index_get(gso, selected->pc);
+ int grid_index = gpencil_grid_cell_index_get(gso, selected->pc);
if (grid_index > -1) {
int row = grid_index / gso->grid_size;
int col = grid_index - (gso->grid_size * row);
@@ -700,7 +700,7 @@ static bool brush_smear_apply(tGP_BrushVertexpaintData *gso,
/* ************************************************ */
/* Header Info */
-static void gp_vertexpaint_brush_header_set(bContext *C)
+static void gpencil_vertexpaint_brush_header_set(bContext *C)
{
ED_workspace_status_text(C,
TIP_("GPencil Vertex Paint: LMB to paint | RMB/Escape to Exit"
@@ -712,7 +712,7 @@ static void gp_vertexpaint_brush_header_set(bContext *C)
/* Init/Exit ----------------------------------------------- */
-static bool gp_vertexpaint_brush_init(bContext *C, wmOperator *op)
+static bool gpencil_vertexpaint_brush_init(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
ToolSettings *ts = CTX_data_tool_settings(C);
@@ -765,15 +765,15 @@ static bool gp_vertexpaint_brush_init(bContext *C, wmOperator *op)
}
/* Setup space conversions. */
- gp_point_conversion_init(C, &gso->gsc);
+ gpencil_point_conversion_init(C, &gso->gsc);
/* Update header. */
- gp_vertexpaint_brush_header_set(C);
+ gpencil_vertexpaint_brush_header_set(C);
return true;
}
-static void gp_vertexpaint_brush_exit(bContext *C, wmOperator *op)
+static void gpencil_vertexpaint_brush_exit(bContext *C, wmOperator *op)
{
tGP_BrushVertexpaintData *gso = op->customdata;
@@ -791,17 +791,17 @@ static void gp_vertexpaint_brush_exit(bContext *C, wmOperator *op)
}
/* Poll callback for stroke vertex paint operator. */
-static bool gp_vertexpaint_brush_poll(bContext *C)
+static bool gpencil_vertexpaint_brush_poll(bContext *C)
{
/* NOTE: this is a bit slower, but is the most accurate... */
return CTX_DATA_COUNT(C, editable_gpencil_strokes) != 0;
}
/* Helper to save the points selected by the brush. */
-static void gp_save_selected_point(tGP_BrushVertexpaintData *gso,
- bGPDstroke *gps,
- int index,
- int pc[2])
+static void gpencil_save_selected_point(tGP_BrushVertexpaintData *gso,
+ bGPDstroke *gps,
+ int index,
+ int pc[2])
{
tGP_Selected *selected;
bGPDspoint *pt = &gps->points[index];
@@ -822,10 +822,10 @@ static void gp_save_selected_point(tGP_BrushVertexpaintData *gso,
}
/* Select points in this stroke and add to an array to be used later. */
-static void gp_vertexpaint_select_stroke(tGP_BrushVertexpaintData *gso,
- bGPDstroke *gps,
- const char tool,
- const float diff_mat[4][4])
+static void gpencil_vertexpaint_select_stroke(tGP_BrushVertexpaintData *gso,
+ bGPDstroke *gps,
+ const char tool,
+ const float diff_mat[4][4])
{
GP_SpaceConversion *gsc = &gso->gsc;
rcti *rect = &gso->brush_rect;
@@ -851,8 +851,8 @@ static void gp_vertexpaint_select_stroke(tGP_BrushVertexpaintData *gso,
if (gps->totpoints == 1) {
bGPDspoint pt_temp;
pt = &gps->points[0];
- gp_point_to_parent_space(gps->points, diff_mat, &pt_temp);
- gp_point_to_xy(gsc, gps, &pt_temp, &pc1[0], &pc1[1]);
+ gpencil_point_to_parent_space(gps->points, diff_mat, &pt_temp);
+ gpencil_point_to_xy(gsc, gps, &pt_temp, &pc1[0], &pc1[1]);
pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
/* do boundbox check first */
@@ -863,7 +863,7 @@ static void gp_vertexpaint_select_stroke(tGP_BrushVertexpaintData *gso,
if (len_v2v2_int(mval_i, pc1) <= radius) {
/* apply operation to this point */
if (pt_active != NULL) {
- gp_save_selected_point(gso, gps_active, 0, pc1);
+ gpencil_save_selected_point(gso, gps_active, 0, pc1);
}
}
}
@@ -888,11 +888,11 @@ static void gp_vertexpaint_select_stroke(tGP_BrushVertexpaintData *gso,
}
bGPDspoint npt;
- gp_point_to_parent_space(pt1, diff_mat, &npt);
- gp_point_to_xy(gsc, gps, &npt, &pc1[0], &pc1[1]);
+ gpencil_point_to_parent_space(pt1, diff_mat, &npt);
+ gpencil_point_to_xy(gsc, gps, &npt, &pc1[0], &pc1[1]);
- gp_point_to_parent_space(pt2, diff_mat, &npt);
- gp_point_to_xy(gsc, gps, &npt, &pc2[0], &pc2[1]);
+ gpencil_point_to_parent_space(pt2, diff_mat, &npt);
+ gpencil_point_to_xy(gsc, gps, &npt, &pc2[0], &pc2[1]);
/* Check that point segment of the boundbox of the selection stroke */
if (((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) ||
@@ -901,7 +901,7 @@ static void gp_vertexpaint_select_stroke(tGP_BrushVertexpaintData *gso,
* brush region (either within stroke painted, or on its lines)
* - this assumes that linewidth is irrelevant
*/
- if (gp_stroke_inside_circle(gso->mval, radius, pc1[0], pc1[1], pc2[0], pc2[1])) {
+ if (gpencil_stroke_inside_circle(gso->mval, radius, pc1[0], pc1[1], pc2[0], pc2[1])) {
/* To each point individually... */
pt = &gps->points[i];
@@ -914,7 +914,7 @@ static void gp_vertexpaint_select_stroke(tGP_BrushVertexpaintData *gso,
continue;
}
hit = true;
- gp_save_selected_point(gso, gps_active, index, pc1);
+ gpencil_save_selected_point(gso, gps_active, index, pc1);
}
/* Only do the second point if this is the last segment,
@@ -931,7 +931,7 @@ static void gp_vertexpaint_select_stroke(tGP_BrushVertexpaintData *gso,
index = (pt->runtime.pt_orig) ? pt->runtime.idx_orig : i + 1;
if (pt_active != NULL) {
hit = true;
- gp_save_selected_point(gso, gps_active, index, pc2);
+ gpencil_save_selected_point(gso, gps_active, index, pc2);
include_last = false;
}
}
@@ -950,7 +950,7 @@ static void gp_vertexpaint_select_stroke(tGP_BrushVertexpaintData *gso,
index = (pt->runtime.pt_orig) ? pt->runtime.idx_orig : i;
if (pt_active != NULL) {
hit = true;
- gp_save_selected_point(gso, gps_active, index, pc1);
+ gpencil_save_selected_point(gso, gps_active, index, pc1);
include_last = false;
}
@@ -970,7 +970,7 @@ static void gp_vertexpaint_select_stroke(tGP_BrushVertexpaintData *gso,
/* Need repeat the effect because if we don't do that the tint process
* is very slow. */
for (int repeat = 0; repeat < 50; repeat++) {
- gp_save_selected_point(gso, gps_active, -1, NULL);
+ gpencil_save_selected_point(gso, gps_active, -1, NULL);
}
}
}
@@ -979,11 +979,11 @@ static void gp_vertexpaint_select_stroke(tGP_BrushVertexpaintData *gso,
}
/* Apply vertex paint brushes to strokes in the given frame. */
-static bool gp_vertexpaint_brush_do_frame(bContext *C,
- tGP_BrushVertexpaintData *gso,
- bGPDlayer *gpl,
- bGPDframe *gpf,
- const float diff_mat[4][4])
+static bool gpencil_vertexpaint_brush_do_frame(bContext *C,
+ tGP_BrushVertexpaintData *gso,
+ bGPDlayer *gpl,
+ bGPDframe *gpf,
+ const float diff_mat[4][4])
{
Object *ob = CTX_data_active_object(C);
const char tool = ob->mode == OB_MODE_VERTEX_GPENCIL ? gso->brush->gpencil_vertex_tool :
@@ -1010,7 +1010,7 @@ static bool gp_vertexpaint_brush_do_frame(bContext *C,
}
/* Check points below the brush. */
- gp_vertexpaint_select_stroke(gso, gps, tool, diff_mat);
+ gpencil_vertexpaint_select_stroke(gso, gps, tool, diff_mat);
}
/* For Average tool, need calculate the average resulting color from all colors
@@ -1092,7 +1092,7 @@ static bool gp_vertexpaint_brush_do_frame(bContext *C,
}
/* Apply brush effect to all layers. */
-static bool gp_vertexpaint_brush_apply_to_layers(bContext *C, tGP_BrushVertexpaintData *gso)
+static bool gpencil_vertexpaint_brush_apply_to_layers(bContext *C, tGP_BrushVertexpaintData *gso)
{
ToolSettings *ts = CTX_data_tool_settings(C);
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
@@ -1138,7 +1138,7 @@ static bool gp_vertexpaint_brush_apply_to_layers(bContext *C, tGP_BrushVertexpai
}
/* affect strokes in this frame */
- changed |= gp_vertexpaint_brush_do_frame(C, gso, gpl, gpf, diff_mat);
+ changed |= gpencil_vertexpaint_brush_do_frame(C, gso, gpl, gpf, diff_mat);
}
}
}
@@ -1146,7 +1146,7 @@ static bool gp_vertexpaint_brush_apply_to_layers(bContext *C, tGP_BrushVertexpai
/* Apply to active frame's strokes */
if (gpl->actframe != NULL) {
gso->mf_falloff = 1.0f;
- changed |= gp_vertexpaint_brush_do_frame(C, gso, gpl, gpl->actframe, diff_mat);
+ changed |= gpencil_vertexpaint_brush_do_frame(C, gso, gpl, gpl->actframe, diff_mat);
}
}
}
@@ -1155,7 +1155,7 @@ static bool gp_vertexpaint_brush_apply_to_layers(bContext *C, tGP_BrushVertexpai
}
/* Calculate settings for applying brush */
-static void gp_vertexpaint_brush_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
+static void gpencil_vertexpaint_brush_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
{
tGP_BrushVertexpaintData *gso = op->customdata;
Brush *brush = gso->brush;
@@ -1196,9 +1196,9 @@ static void gp_vertexpaint_brush_apply(bContext *C, wmOperator *op, PointerRNA *
brush_calc_dvec_2d(gso);
/* Calc grid for smear tool. */
- gp_grid_cells_init(gso);
+ gpencil_grid_cells_init(gso);
- changed = gp_vertexpaint_brush_apply_to_layers(C, gso);
+ changed = gpencil_vertexpaint_brush_apply_to_layers(C, gso);
/* Updates */
if (changed) {
@@ -1216,7 +1216,9 @@ static void gp_vertexpaint_brush_apply(bContext *C, wmOperator *op, PointerRNA *
/* Running --------------------------------------------- */
/* helper - a record stroke, and apply paint event */
-static void gp_vertexpaint_brush_apply_event(bContext *C, wmOperator *op, const wmEvent *event)
+static void gpencil_vertexpaint_brush_apply_event(bContext *C,
+ wmOperator *op,
+ const wmEvent *event)
{
tGP_BrushVertexpaintData *gso = op->customdata;
PointerRNA itemptr;
@@ -1238,28 +1240,28 @@ static void gp_vertexpaint_brush_apply_event(bContext *C, wmOperator *op, const
RNA_float_set(&itemptr, "pressure", pressure);
/* apply */
- gp_vertexpaint_brush_apply(C, op, &itemptr);
+ gpencil_vertexpaint_brush_apply(C, op, &itemptr);
}
/* reapply */
-static int gp_vertexpaint_brush_exec(bContext *C, wmOperator *op)
+static int gpencil_vertexpaint_brush_exec(bContext *C, wmOperator *op)
{
- if (!gp_vertexpaint_brush_init(C, op)) {
+ if (!gpencil_vertexpaint_brush_init(C, op)) {
return OPERATOR_CANCELLED;
}
RNA_BEGIN (op->ptr, itemptr, "stroke") {
- gp_vertexpaint_brush_apply(C, op, &itemptr);
+ gpencil_vertexpaint_brush_apply(C, op, &itemptr);
}
RNA_END;
- gp_vertexpaint_brush_exit(C, op);
+ gpencil_vertexpaint_brush_exit(C, op);
return OPERATOR_FINISHED;
}
/* start modal painting */
-static int gp_vertexpaint_brush_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+static int gpencil_vertexpaint_brush_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
tGP_BrushVertexpaintData *gso = NULL;
const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input");
@@ -1273,7 +1275,7 @@ static int gp_vertexpaint_brush_invoke(bContext *C, wmOperator *op, const wmEven
}
/* init painting data */
- if (!gp_vertexpaint_brush_init(C, op)) {
+ if (!gpencil_vertexpaint_brush_init(C, op)) {
return OPERATOR_CANCELLED;
}
@@ -1288,7 +1290,7 @@ static int gp_vertexpaint_brush_invoke(bContext *C, wmOperator *op, const wmEven
/* apply first dab... */
gso->is_painting = true;
- gp_vertexpaint_brush_apply_event(C, op, event);
+ gpencil_vertexpaint_brush_apply_event(C, op, event);
/* redraw view with feedback */
ED_region_tag_redraw(region);
@@ -1298,7 +1300,7 @@ static int gp_vertexpaint_brush_invoke(bContext *C, wmOperator *op, const wmEven
}
/* painting - handle events */
-static int gp_vertexpaint_brush_modal(bContext *C, wmOperator *op, const wmEvent *event)
+static int gpencil_vertexpaint_brush_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
tGP_BrushVertexpaintData *gso = op->customdata;
const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input");
@@ -1313,7 +1315,7 @@ static int gp_vertexpaint_brush_modal(bContext *C, wmOperator *op, const wmEvent
case MOUSEMOVE:
case INBETWEEN_MOUSEMOVE:
/* apply brush effect at new position */
- gp_vertexpaint_brush_apply_event(C, op, event);
+ gpencil_vertexpaint_brush_apply_event(C, op, event);
/* force redraw, so that the cursor will at least be valid */
redraw_region = true;
@@ -1329,7 +1331,7 @@ static int gp_vertexpaint_brush_modal(bContext *C, wmOperator *op, const wmEvent
/* end painting, since we're not modal */
gso->is_painting = false;
- gp_vertexpaint_brush_exit(C, op);
+ gpencil_vertexpaint_brush_exit(C, op);
return OPERATOR_FINISHED;
}
break;
@@ -1338,7 +1340,7 @@ static int gp_vertexpaint_brush_modal(bContext *C, wmOperator *op, const wmEvent
case MIDDLEMOUSE:
case RIGHTMOUSE:
case EVT_ESCKEY:
- gp_vertexpaint_brush_exit(C, op);
+ gpencil_vertexpaint_brush_exit(C, op);
return OPERATOR_FINISHED;
}
}
@@ -1353,13 +1355,13 @@ static int gp_vertexpaint_brush_modal(bContext *C, wmOperator *op, const wmEvent
gso->is_painting = true;
gso->first = true;
- gp_vertexpaint_brush_apply_event(C, op, event);
+ gpencil_vertexpaint_brush_apply_event(C, op, event);
break;
/* Exit modal operator, based on the "standard" ops */
case RIGHTMOUSE:
case EVT_ESCKEY:
- gp_vertexpaint_brush_exit(C, op);
+ gpencil_vertexpaint_brush_exit(C, op);
return OPERATOR_FINISHED;
/* MMB is often used for view manipulations */
@@ -1421,11 +1423,11 @@ void GPENCIL_OT_vertex_paint(wmOperatorType *ot)
ot->description = "Paint stroke points with a color";
/* api callbacks */
- ot->exec = gp_vertexpaint_brush_exec;
- ot->invoke = gp_vertexpaint_brush_invoke;
- ot->modal = gp_vertexpaint_brush_modal;
- ot->cancel = gp_vertexpaint_brush_exit;
- ot->poll = gp_vertexpaint_brush_poll;
+ ot->exec = gpencil_vertexpaint_brush_exec;
+ ot->invoke = gpencil_vertexpaint_brush_invoke;
+ ot->modal = gpencil_vertexpaint_brush_modal;
+ ot->cancel = gpencil_vertexpaint_brush_exit;
+ ot->poll = gpencil_vertexpaint_brush_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
diff --git a/source/blender/editors/gpencil/gpencil_weight_paint.c b/source/blender/editors/gpencil/gpencil_weight_paint.c
index 2ebf1aba353..e41146575e4 100644
--- a/source/blender/editors/gpencil/gpencil_weight_paint.c
+++ b/source/blender/editors/gpencil/gpencil_weight_paint.c
@@ -268,7 +268,7 @@ static bool brush_draw_apply(tGP_BrushWeightpaintData *gso,
/* ************************************************ */
/* Header Info */
-static void gp_weightpaint_brush_header_set(bContext *C)
+static void gpencil_weightpaint_brush_header_set(bContext *C)
{
ED_workspace_status_text(C, TIP_("GPencil Weight Paint: LMB to paint | RMB/Escape to Exit"));
}
@@ -278,7 +278,7 @@ static void gp_weightpaint_brush_header_set(bContext *C)
/* Init/Exit ----------------------------------------------- */
-static bool gp_weightpaint_brush_init(bContext *C, wmOperator *op)
+static bool gpencil_weightpaint_brush_init(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
ToolSettings *ts = CTX_data_tool_settings(C);
@@ -330,15 +330,15 @@ static bool gp_weightpaint_brush_init(bContext *C, wmOperator *op)
}
/* Setup space conversions. */
- gp_point_conversion_init(C, &gso->gsc);
+ gpencil_point_conversion_init(C, &gso->gsc);
/* Update header. */
- gp_weightpaint_brush_header_set(C);
+ gpencil_weightpaint_brush_header_set(C);
return true;
}
-static void gp_weightpaint_brush_exit(bContext *C, wmOperator *op)
+static void gpencil_weightpaint_brush_exit(bContext *C, wmOperator *op)
{
tGP_BrushWeightpaintData *gso = op->customdata;
@@ -352,17 +352,17 @@ static void gp_weightpaint_brush_exit(bContext *C, wmOperator *op)
}
/* Poll callback for stroke weight paint operator. */
-static bool gp_weightpaint_brush_poll(bContext *C)
+static bool gpencil_weightpaint_brush_poll(bContext *C)
{
/* NOTE: this is a bit slower, but is the most accurate... */
return CTX_DATA_COUNT(C, editable_gpencil_strokes) != 0;
}
/* Helper to save the points selected by the brush. */
-static void gp_save_selected_point(tGP_BrushWeightpaintData *gso,
- bGPDstroke *gps,
- int index,
- int pc[2])
+static void gpencil_save_selected_point(tGP_BrushWeightpaintData *gso,
+ bGPDstroke *gps,
+ int index,
+ int pc[2])
{
tGP_Selected *selected;
bGPDspoint *pt = &gps->points[index];
@@ -381,9 +381,9 @@ static void gp_save_selected_point(tGP_BrushWeightpaintData *gso,
}
/* Select points in this stroke and add to an array to be used later. */
-static void gp_weightpaint_select_stroke(tGP_BrushWeightpaintData *gso,
- bGPDstroke *gps,
- const float diff_mat[4][4])
+static void gpencil_weightpaint_select_stroke(tGP_BrushWeightpaintData *gso,
+ bGPDstroke *gps,
+ const float diff_mat[4][4])
{
GP_SpaceConversion *gsc = &gso->gsc;
rcti *rect = &gso->brush_rect;
@@ -409,8 +409,8 @@ static void gp_weightpaint_select_stroke(tGP_BrushWeightpaintData *gso,
if (gps->totpoints == 1) {
bGPDspoint pt_temp;
pt = &gps->points[0];
- gp_point_to_parent_space(gps->points, diff_mat, &pt_temp);
- gp_point_to_xy(gsc, gps, &pt_temp, &pc1[0], &pc1[1]);
+ gpencil_point_to_parent_space(gps->points, diff_mat, &pt_temp);
+ gpencil_point_to_xy(gsc, gps, &pt_temp, &pc1[0], &pc1[1]);
pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
/* do boundbox check first */
@@ -421,7 +421,7 @@ static void gp_weightpaint_select_stroke(tGP_BrushWeightpaintData *gso,
if (len_v2v2_int(mval_i, pc1) <= radius) {
/* apply operation to this point */
if (pt_active != NULL) {
- gp_save_selected_point(gso, gps_active, 0, pc1);
+ gpencil_save_selected_point(gso, gps_active, 0, pc1);
}
}
}
@@ -436,11 +436,11 @@ static void gp_weightpaint_select_stroke(tGP_BrushWeightpaintData *gso,
pt2 = gps->points + i + 1;
bGPDspoint npt;
- gp_point_to_parent_space(pt1, diff_mat, &npt);
- gp_point_to_xy(gsc, gps, &npt, &pc1[0], &pc1[1]);
+ gpencil_point_to_parent_space(pt1, diff_mat, &npt);
+ gpencil_point_to_xy(gsc, gps, &npt, &pc1[0], &pc1[1]);
- gp_point_to_parent_space(pt2, diff_mat, &npt);
- gp_point_to_xy(gsc, gps, &npt, &pc2[0], &pc2[1]);
+ gpencil_point_to_parent_space(pt2, diff_mat, &npt);
+ gpencil_point_to_xy(gsc, gps, &npt, &pc2[0], &pc2[1]);
/* Check that point segment of the boundbox of the selection stroke */
if (((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) ||
@@ -449,14 +449,14 @@ static void gp_weightpaint_select_stroke(tGP_BrushWeightpaintData *gso,
* brush region (either within stroke painted, or on its lines)
* - this assumes that linewidth is irrelevant
*/
- if (gp_stroke_inside_circle(gso->mval, radius, pc1[0], pc1[1], pc2[0], pc2[1])) {
+ if (gpencil_stroke_inside_circle(gso->mval, radius, pc1[0], pc1[1], pc2[0], pc2[1])) {
/* To each point individually... */
pt = &gps->points[i];
pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
index = (pt->runtime.pt_orig) ? pt->runtime.idx_orig : i;
if (pt_active != NULL) {
- gp_save_selected_point(gso, gps_active, index, pc1);
+ gpencil_save_selected_point(gso, gps_active, index, pc1);
}
/* Only do the second point if this is the last segment,
@@ -472,7 +472,7 @@ static void gp_weightpaint_select_stroke(tGP_BrushWeightpaintData *gso,
pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
index = (pt->runtime.pt_orig) ? pt->runtime.idx_orig : i + 1;
if (pt_active != NULL) {
- gp_save_selected_point(gso, gps_active, index, pc2);
+ gpencil_save_selected_point(gso, gps_active, index, pc2);
include_last = false;
}
}
@@ -490,7 +490,7 @@ static void gp_weightpaint_select_stroke(tGP_BrushWeightpaintData *gso,
pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
index = (pt->runtime.pt_orig) ? pt->runtime.idx_orig : i;
if (pt_active != NULL) {
- gp_save_selected_point(gso, gps_active, index, pc1);
+ gpencil_save_selected_point(gso, gps_active, index, pc1);
include_last = false;
}
@@ -501,11 +501,11 @@ static void gp_weightpaint_select_stroke(tGP_BrushWeightpaintData *gso,
}
/* Apply weight paint brushes to strokes in the given frame. */
-static bool gp_weightpaint_brush_do_frame(bContext *C,
- tGP_BrushWeightpaintData *gso,
- bGPDlayer *gpl,
- bGPDframe *gpf,
- const float diff_mat[4][4])
+static bool gpencil_weightpaint_brush_do_frame(bContext *C,
+ tGP_BrushWeightpaintData *gso,
+ bGPDlayer *gpl,
+ bGPDframe *gpf,
+ const float diff_mat[4][4])
{
Object *ob = CTX_data_active_object(C);
char tool = gso->brush->gpencil_weight_tool;
@@ -531,7 +531,7 @@ static bool gp_weightpaint_brush_do_frame(bContext *C,
}
/* Check points below the brush. */
- gp_weightpaint_select_stroke(gso, gps, diff_mat);
+ gpencil_weightpaint_select_stroke(gso, gps, diff_mat);
}
/*---------------------------------------------------------------------
@@ -561,7 +561,7 @@ static bool gp_weightpaint_brush_do_frame(bContext *C,
}
/* Apply brush effect to all layers. */
-static bool gp_weightpaint_brush_apply_to_layers(bContext *C, tGP_BrushWeightpaintData *gso)
+static bool gpencil_weightpaint_brush_apply_to_layers(bContext *C, tGP_BrushWeightpaintData *gso)
{
ToolSettings *ts = CTX_data_tool_settings(C);
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
@@ -608,7 +608,7 @@ static bool gp_weightpaint_brush_apply_to_layers(bContext *C, tGP_BrushWeightpai
}
/* affect strokes in this frame */
- changed |= gp_weightpaint_brush_do_frame(C, gso, gpl, gpf, diff_mat);
+ changed |= gpencil_weightpaint_brush_do_frame(C, gso, gpl, gpf, diff_mat);
}
}
}
@@ -616,7 +616,7 @@ static bool gp_weightpaint_brush_apply_to_layers(bContext *C, tGP_BrushWeightpai
if (gpl->actframe != NULL) {
/* Apply to active frame's strokes */
gso->mf_falloff = 1.0f;
- changed |= gp_weightpaint_brush_do_frame(C, gso, gpl, gpl->actframe, diff_mat);
+ changed |= gpencil_weightpaint_brush_do_frame(C, gso, gpl, gpl->actframe, diff_mat);
}
}
}
@@ -625,7 +625,7 @@ static bool gp_weightpaint_brush_apply_to_layers(bContext *C, tGP_BrushWeightpai
}
/* Calculate settings for applying brush */
-static void gp_weightpaint_brush_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
+static void gpencil_weightpaint_brush_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
{
tGP_BrushWeightpaintData *gso = op->customdata;
Brush *brush = gso->brush;
@@ -658,7 +658,7 @@ static void gp_weightpaint_brush_apply(bContext *C, wmOperator *op, PointerRNA *
/* Calculate 2D direction vector and relative angle. */
brush_calc_dvec_2d(gso);
- changed = gp_weightpaint_brush_apply_to_layers(C, gso);
+ changed = gpencil_weightpaint_brush_apply_to_layers(C, gso);
/* Updates */
if (changed) {
@@ -676,7 +676,9 @@ static void gp_weightpaint_brush_apply(bContext *C, wmOperator *op, PointerRNA *
/* Running --------------------------------------------- */
/* helper - a record stroke, and apply paint event */
-static void gp_weightpaint_brush_apply_event(bContext *C, wmOperator *op, const wmEvent *event)
+static void gpencil_weightpaint_brush_apply_event(bContext *C,
+ wmOperator *op,
+ const wmEvent *event)
{
tGP_BrushWeightpaintData *gso = op->customdata;
PointerRNA itemptr;
@@ -698,28 +700,28 @@ static void gp_weightpaint_brush_apply_event(bContext *C, wmOperator *op, const
RNA_float_set(&itemptr, "pressure", pressure);
/* apply */
- gp_weightpaint_brush_apply(C, op, &itemptr);
+ gpencil_weightpaint_brush_apply(C, op, &itemptr);
}
/* reapply */
-static int gp_weightpaint_brush_exec(bContext *C, wmOperator *op)
+static int gpencil_weightpaint_brush_exec(bContext *C, wmOperator *op)
{
- if (!gp_weightpaint_brush_init(C, op)) {
+ if (!gpencil_weightpaint_brush_init(C, op)) {
return OPERATOR_CANCELLED;
}
RNA_BEGIN (op->ptr, itemptr, "stroke") {
- gp_weightpaint_brush_apply(C, op, &itemptr);
+ gpencil_weightpaint_brush_apply(C, op, &itemptr);
}
RNA_END;
- gp_weightpaint_brush_exit(C, op);
+ gpencil_weightpaint_brush_exit(C, op);
return OPERATOR_FINISHED;
}
/* start modal painting */
-static int gp_weightpaint_brush_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+static int gpencil_weightpaint_brush_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
tGP_BrushWeightpaintData *gso = NULL;
const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input");
@@ -733,7 +735,7 @@ static int gp_weightpaint_brush_invoke(bContext *C, wmOperator *op, const wmEven
}
/* init painting data */
- if (!gp_weightpaint_brush_init(C, op)) {
+ if (!gpencil_weightpaint_brush_init(C, op)) {
return OPERATOR_CANCELLED;
}
@@ -748,7 +750,7 @@ static int gp_weightpaint_brush_invoke(bContext *C, wmOperator *op, const wmEven
/* apply first dab... */
gso->is_painting = true;
- gp_weightpaint_brush_apply_event(C, op, event);
+ gpencil_weightpaint_brush_apply_event(C, op, event);
/* redraw view with feedback */
ED_region_tag_redraw(region);
@@ -758,7 +760,7 @@ static int gp_weightpaint_brush_invoke(bContext *C, wmOperator *op, const wmEven
}
/* painting - handle events */
-static int gp_weightpaint_brush_modal(bContext *C, wmOperator *op, const wmEvent *event)
+static int gpencil_weightpaint_brush_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
tGP_BrushWeightpaintData *gso = op->customdata;
const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input");
@@ -773,7 +775,7 @@ static int gp_weightpaint_brush_modal(bContext *C, wmOperator *op, const wmEvent
case MOUSEMOVE:
case INBETWEEN_MOUSEMOVE:
/* apply brush effect at new position */
- gp_weightpaint_brush_apply_event(C, op, event);
+ gpencil_weightpaint_brush_apply_event(C, op, event);
/* force redraw, so that the cursor will at least be valid */
redraw_region = true;
@@ -789,7 +791,7 @@ static int gp_weightpaint_brush_modal(bContext *C, wmOperator *op, const wmEvent
/* end painting, since we're not modal */
gso->is_painting = false;
- gp_weightpaint_brush_exit(C, op);
+ gpencil_weightpaint_brush_exit(C, op);
return OPERATOR_FINISHED;
}
break;
@@ -798,7 +800,7 @@ static int gp_weightpaint_brush_modal(bContext *C, wmOperator *op, const wmEvent
case MIDDLEMOUSE:
case RIGHTMOUSE:
case EVT_ESCKEY:
- gp_weightpaint_brush_exit(C, op);
+ gpencil_weightpaint_brush_exit(C, op);
return OPERATOR_FINISHED;
}
}
@@ -813,13 +815,13 @@ static int gp_weightpaint_brush_modal(bContext *C, wmOperator *op, const wmEvent
gso->is_painting = true;
gso->first = true;
- gp_weightpaint_brush_apply_event(C, op, event);
+ gpencil_weightpaint_brush_apply_event(C, op, event);
break;
/* Exit modal operator, based on the "standard" ops */
case RIGHTMOUSE:
case EVT_ESCKEY:
- gp_weightpaint_brush_exit(C, op);
+ gpencil_weightpaint_brush_exit(C, op);
return OPERATOR_FINISHED;
/* MMB is often used for view manipulations */
@@ -881,11 +883,11 @@ void GPENCIL_OT_weight_paint(wmOperatorType *ot)
ot->description = "Paint stroke points with a color";
/* api callbacks */
- ot->exec = gp_weightpaint_brush_exec;
- ot->invoke = gp_weightpaint_brush_invoke;
- ot->modal = gp_weightpaint_brush_modal;
- ot->cancel = gp_weightpaint_brush_exit;
- ot->poll = gp_weightpaint_brush_poll;
+ ot->exec = gpencil_weightpaint_brush_exec;
+ ot->invoke = gpencil_weightpaint_brush_invoke;
+ ot->modal = gpencil_weightpaint_brush_modal;
+ ot->cancel = gpencil_weightpaint_brush_exit;
+ ot->poll = gpencil_weightpaint_brush_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h
index 426a470b128..3aecec0d6b6 100644
--- a/source/blender/editors/include/ED_anim_api.h
+++ b/source/blender/editors/include/ED_anim_api.h
@@ -281,10 +281,13 @@ typedef enum eAnim_Update_Flags {
/* filtering flags - under what circumstances should a channel be returned */
typedef enum eAnimFilter_Flags {
- /** data which channel represents is fits the dopesheet filters
- * (i.e. scene visibility criteria) */
- /* XXX: it's hard to think of any examples where this *ISN'T* the case...
- * perhaps becomes implicit?. */
+ /**
+ * Data which channel represents is fits the dope-sheet filters
+ * (i.e. scene visibility criteria).
+ *
+ * XXX: it's hard to think of any examples where this *ISN'T* the case...
+ * perhaps becomes implicit?.
+ */
ANIMFILTER_DATA_VISIBLE = (1 << 0),
/** channel is visible within the channel-list hierarchy
* (i.e. F-Curves within Groups in ActEdit) */
@@ -297,7 +300,7 @@ typedef enum eAnimFilter_Flags {
/** for its type, channel should be "active" one */
ANIMFILTER_ACTIVE = (1 << 4),
- /** channel is a child of the active group (* Actions speciality) */
+ /** channel is a child of the active group (* Actions specialty) */
ANIMFILTER_ACTGROUPED = (1 << 5),
/** channel must be selected/not-selected, but both must not be set together */
@@ -310,8 +313,10 @@ typedef enum eAnimFilter_Flags {
* for Graph Editor's option for keys on select curves only */
ANIMFILTER_SELEDIT = (1 << 9),
- /** flags used to enforce certain data types
- * \node the ones for curves and NLA tracks were redundant and have been removed for now...
+ /**
+ * Flags used to enforce certain data types.
+ *
+ * \note The ones for curves and NLA tracks were redundant and have been removed for now.
*/
ANIMFILTER_ANIMDATA = (1 << 10),
@@ -321,7 +326,7 @@ typedef enum eAnimFilter_Flags {
/** for checking if we should keep some collapsed channel around (internal use only!) */
ANIMFILTER_TMP_PEEK = (1 << 30),
- /** ignore ONLYSEL flag from filterflag, (internal use only!) */
+ /** Ignore ONLYSEL flag from #bDopeSheet.filterflag (internal use only!) */
ANIMFILTER_TMP_IGNORE_ONLYSEL = (1u << 31),
} eAnimFilter_Flags;
@@ -640,14 +645,14 @@ bool ANIM_remove_empty_action_from_animdata(struct AnimData *adt);
/* ---------- Current Frame Drawing ---------------- */
/* flags for Current Frame Drawing */
-enum eAnimEditDraw_CurrentFrame {
+typedef enum eAnimEditDraw_CurrentFrame {
/* plain time indicator with no special indicators */
/* DRAWCFRA_PLAIN = 0, */ /* UNUSED */
/* time indication in seconds or frames */
DRAWCFRA_UNIT_SECONDS = (1 << 0),
/* draw indicator extra wide (for timeline) */
DRAWCFRA_WIDE = (1 << 1),
-};
+} eAnimEditDraw_CurrentFrame;
/* main call to draw current-frame indicator in an Animation Editor */
void ANIM_draw_cfra(const struct bContext *C, struct View2D *v2d, short flag);
diff --git a/source/blender/editors/include/ED_armature.h b/source/blender/editors/include/ED_armature.h
index 541e2633512..eef431c40fa 100644
--- a/source/blender/editors/include/ED_armature.h
+++ b/source/blender/editors/include/ED_armature.h
@@ -174,7 +174,7 @@ void ED_operatormacros_armature(void);
void ED_keymap_armature(struct wmKeyConfig *keyconf);
/* armature_relations.c */
-int join_armature_exec(struct bContext *C, struct wmOperator *op);
+int ED_armature_join_objects_exec(struct bContext *C, struct wmOperator *op);
/* armature_select.c */
struct Base *ED_armature_base_and_ebone_from_select_buffer(struct Base **bases,
diff --git a/source/blender/editors/include/ED_curve.h b/source/blender/editors/include/ED_curve.h
index 95c454043da..79f5f62f293 100644
--- a/source/blender/editors/include/ED_curve.h
+++ b/source/blender/editors/include/ED_curve.h
@@ -66,7 +66,7 @@ int ED_curve_nurb_select_count(struct View3D *v3d, struct Nurb *nu);
bool ED_curve_nurb_select_all(const struct Nurb *nu);
bool ED_curve_nurb_deselect_all(const struct Nurb *nu);
-int join_curve_exec(struct bContext *C, struct wmOperator *op);
+int ED_curve_join_objects_exec(struct bContext *C, struct wmOperator *op);
/* editcurve_select.c */
bool ED_curve_select_check(struct View3D *v3d, struct EditNurb *editnurb);
diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h
index 2a1d9c2dd6c..f961f835f12 100644
--- a/source/blender/editors/include/ED_gpencil.h
+++ b/source/blender/editors/include/ED_gpencil.h
@@ -87,15 +87,24 @@ typedef enum eGP_ReprojectModes {
* Used as part of the 'stroke cache' used during drawing of new strokes
*/
typedef struct tGPspoint {
- float x, y; /* x and y coordinates of cursor (in relative to area) */
- float pressure; /* pressure of tablet at this point */
- float strength; /* pressure of tablet at this point for alpha factor */
- float time; /* Time relative to stroke start (used when converting to path) */
- float uv_fac; /* factor of uv along the stroke */
- float uv_rot; /* uv rotation for dor mode */
- float rnd[3]; /* rnd value */
- bool rnd_dirty; /* rnd flag */
- float vert_color[4]; /* Point vertex color. */
+ /** Coordinates x and y of cursor (in relative to area). */
+ float x, y;
+ /** Pressure of tablet at this point. */
+ float pressure;
+ /** Pressure of tablet at this point for alpha factor. */
+ float strength;
+ /** Time relative to stroke start (used when converting to path). */
+ float time;
+ /** Factor of uv along the stroke. */
+ float uv_fac;
+ /** UV rotation for dot mode. */
+ float uv_rot;
+ /** Random value. */
+ float rnd[3];
+ /** Random flag. */
+ bool rnd_dirty;
+ /** Point vertex color. */
+ float vert_color[4];
} tGPspoint;
/* ----------- Grease Pencil Tools/Context ------------- */
@@ -166,28 +175,31 @@ void ED_annotation_draw_ex(struct Scene *scene,
const char spacetype);
/* ----------- Grease-Pencil AnimEdit API ------------------ */
-bool ED_gplayer_frames_looper(struct bGPDlayer *gpl,
- struct Scene *scene,
- short (*gpf_cb)(struct bGPDframe *, struct Scene *));
-void ED_gplayer_make_cfra_list(struct bGPDlayer *gpl, ListBase *elems, bool onlysel);
-
-bool ED_gplayer_frame_select_check(struct bGPDlayer *gpl);
-void ED_gplayer_frame_select_set(struct bGPDlayer *gpl, short mode);
-void ED_gplayer_frames_select_box(struct bGPDlayer *gpl, float min, float max, short select_mode);
-void ED_gplayer_frames_select_region(struct KeyframeEditData *ked,
- struct bGPDlayer *gpl,
- short tool,
- short select_mode);
+bool ED_gpencil_layer_frames_looper(struct bGPDlayer *gpl,
+ struct Scene *scene,
+ short (*gpf_cb)(struct bGPDframe *, struct Scene *));
+void ED_gpencil_layer_make_cfra_list(struct bGPDlayer *gpl, ListBase *elems, bool onlysel);
+
+bool ED_gpencil_layer_frame_select_check(struct bGPDlayer *gpl);
+void ED_gpencil_layer_frame_select_set(struct bGPDlayer *gpl, short mode);
+void ED_gpencil_layer_frames_select_box(struct bGPDlayer *gpl,
+ float min,
+ float max,
+ short select_mode);
+void ED_gpencil_layer_frames_select_region(struct KeyframeEditData *ked,
+ struct bGPDlayer *gpl,
+ short tool,
+ short select_mode);
void ED_gpencil_select_frames(struct bGPDlayer *gpl, short select_mode);
void ED_gpencil_select_frame(struct bGPDlayer *gpl, int selx, short select_mode);
-bool ED_gplayer_frames_delete(struct bGPDlayer *gpl);
-void ED_gplayer_frames_duplicate(struct bGPDlayer *gpl);
+bool ED_gpencil_layer_frames_delete(struct bGPDlayer *gpl);
+void ED_gpencil_layer_frames_duplicate(struct bGPDlayer *gpl);
-void ED_gplayer_frames_keytype_set(struct bGPDlayer *gpl, short type);
+void ED_gpencil_layer_frames_keytype_set(struct bGPDlayer *gpl, short type);
-void ED_gplayer_snap_frames(struct bGPDlayer *gpl, struct Scene *scene, short mode);
-void ED_gplayer_mirror_frames(struct bGPDlayer *gpl, struct Scene *scene, short mode);
+void ED_gpencil_layer_snap_frames(struct bGPDlayer *gpl, struct Scene *scene, short mode);
+void ED_gpencil_layer_mirror_frames(struct bGPDlayer *gpl, struct Scene *scene, short mode);
void ED_gpencil_anim_copybuf_free(void);
bool ED_gpencil_anim_copybuf_copy(struct bAnimContext *ac);
@@ -242,18 +254,18 @@ void ED_gpencil_add_defaults(struct bContext *C, struct Object *ob);
void ED_gpencil_setup_modes(struct bContext *C, struct bGPdata *gpd, int newmode);
bool ED_object_gpencil_exit(struct Main *bmain, struct Object *ob);
-void ED_gp_project_stroke_to_plane(const struct Scene *scene,
- const struct Object *ob,
- const struct RegionView3D *rv3d,
- struct bGPDstroke *gps,
- const float origin[3],
- const int axis);
-void ED_gp_project_point_to_plane(const struct Scene *scene,
- const struct Object *ob,
- const struct RegionView3D *rv3d,
- const float origin[3],
- const int axis,
- struct bGPDspoint *pt);
+void ED_gpencil_project_stroke_to_plane(const struct Scene *scene,
+ const struct Object *ob,
+ const struct RegionView3D *rv3d,
+ struct bGPDstroke *gps,
+ const float origin[3],
+ const int axis);
+void ED_gpencil_project_point_to_plane(const struct Scene *scene,
+ const struct Object *ob,
+ const struct RegionView3D *rv3d,
+ const float origin[3],
+ const int axis,
+ struct bGPDspoint *pt);
void ED_gpencil_drawing_reference_get(const struct Scene *scene,
const struct Object *ob,
char align_flag,
diff --git a/source/blender/editors/include/ED_mball.h b/source/blender/editors/include/ED_mball.h
index 938d1059f90..5c2106b934c 100644
--- a/source/blender/editors/include/ED_mball.h
+++ b/source/blender/editors/include/ED_mball.h
@@ -38,8 +38,12 @@ void ED_operatortypes_metaball(void);
void ED_operatormacros_metaball(void);
void ED_keymap_metaball(struct wmKeyConfig *keyconf);
-struct MetaElem *ED_mball_add_primitive(
- struct bContext *C, struct Object *obedit, float mat[4][4], float dia, int type);
+struct MetaElem *ED_mball_add_primitive(struct bContext *C,
+ struct Object *obedit,
+ bool obedit_is_new,
+ float mat[4][4],
+ float dia,
+ int type);
bool ED_mball_select_pick(
struct bContext *C, const int mval[2], bool extend, bool deselect, bool toggle);
diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h
index 20e54df1ccb..9c49d991182 100644
--- a/source/blender/editors/include/ED_mesh.h
+++ b/source/blender/editors/include/ED_mesh.h
@@ -426,6 +426,15 @@ bool ED_mesh_color_remove_index(struct Mesh *me, const int n);
bool ED_mesh_color_remove_active(struct Mesh *me);
bool ED_mesh_color_remove_named(struct Mesh *me, const char *name);
+bool ED_mesh_sculpt_color_ensure(struct Mesh *me, const char *name);
+int ED_mesh_sculpt_color_add(struct Mesh *me,
+ const char *name,
+ const bool active_set,
+ const bool do_init);
+bool ED_mesh_sculpt_color_remove_index(struct Mesh *me, const int n);
+bool ED_mesh_sculpt_color_remove_active(struct Mesh *me);
+bool ED_mesh_sculpt_color_remove_named(struct Mesh *me, const char *name);
+
void ED_mesh_report_mirror(struct wmOperator *op, int totmirr, int totfail);
void ED_mesh_report_mirror_ex(struct wmOperator *op, int totmirr, int totfail, char selectmode);
@@ -445,8 +454,8 @@ void EDBM_redo_state_restore(struct BMBackup, struct BMEditMesh *em, int recalct
void EDBM_redo_state_free(struct BMBackup *, struct BMEditMesh *em, int recalctess);
/* *** meshtools.c *** */
-int join_mesh_exec(struct bContext *C, struct wmOperator *op);
-int join_mesh_shapes_exec(struct bContext *C, struct wmOperator *op);
+int ED_mesh_join_objects_exec(struct bContext *C, struct wmOperator *op);
+int ED_mesh_shapes_join_objects_exec(struct bContext *C, struct wmOperator *op);
/* mirror lookup api */
/* Spatial Mirror */
diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h
index 5c33513f0a4..73c58753531 100644
--- a/source/blender/editors/include/ED_object.h
+++ b/source/blender/editors/include/ED_object.h
@@ -185,7 +185,7 @@ struct Base *ED_object_add_duplicate(struct Main *bmain,
struct Scene *scene,
struct ViewLayer *view_layer,
struct Base *base,
- int dupflag);
+ const uint dupflag);
void ED_object_parent(struct Object *ob,
struct Object *parent,
@@ -421,6 +421,10 @@ int ED_object_gpencil_modifier_move_down(struct ReportList *reports,
int ED_object_gpencil_modifier_move_up(struct ReportList *reports,
struct Object *ob,
struct GpencilModifierData *md);
+bool ED_object_gpencil_modifier_move_to_index(struct ReportList *reports,
+ struct Object *ob,
+ struct GpencilModifierData *md,
+ const int index);
int ED_object_gpencil_modifier_apply(struct Main *bmain,
struct ReportList *reports,
struct Depsgraph *depsgraph,
@@ -449,6 +453,10 @@ int ED_object_shaderfx_move_down(struct ReportList *reports,
int ED_object_shaderfx_move_up(struct ReportList *reports,
struct Object *ob,
struct ShaderFxData *fx);
+bool ED_object_shaderfx_move_to_index(struct ReportList *reports,
+ struct Object *ob,
+ struct ShaderFxData *fx,
+ const int index);
/* object_select.c */
void ED_object_select_linked_by_id(struct bContext *C, struct ID *id);
diff --git a/source/blender/editors/include/ED_render.h b/source/blender/editors/include/ED_render.h
index f03739c74c4..ba70abcc055 100644
--- a/source/blender/editors/include/ED_render.h
+++ b/source/blender/editors/include/ED_render.h
@@ -47,7 +47,7 @@ void ED_operatortypes_render(void);
/* render_update.c */
-void ED_render_engine_changed(struct Main *bmain);
+void ED_render_engine_changed(struct Main *bmain, const bool update_scene_data);
void ED_render_engine_area_exit(struct Main *bmain, struct ScrArea *area);
void ED_render_view_layer_changed(struct Main *bmain, struct bScreen *screen);
diff --git a/source/blender/editors/include/ED_screen_types.h b/source/blender/editors/include/ED_screen_types.h
index 51f3eea74fa..9826ec8c3b8 100644
--- a/source/blender/editors/include/ED_screen_types.h
+++ b/source/blender/editors/include/ED_screen_types.h
@@ -131,7 +131,7 @@ enum {
*/
AZONE_FULLSCREEN,
/**
- * Hotspot azone around scroll-bars to show/hide them.
+ * Hot-spot #AZone around scroll-bars to show/hide them.
* Only show the scroll-bars when the cursor is close.
*/
AZONE_REGION_SCROLL,
diff --git a/source/blender/editors/include/ED_time_scrub_ui.h b/source/blender/editors/include/ED_time_scrub_ui.h
index d5b9fa2a553..483dce56577 100644
--- a/source/blender/editors/include/ED_time_scrub_ui.h
+++ b/source/blender/editors/include/ED_time_scrub_ui.h
@@ -32,6 +32,11 @@ struct bContext;
struct bDopeSheet;
struct wmEvent;
+void ED_time_scrub_draw_current_frame(const struct ARegion *region,
+ const struct Scene *scene,
+ bool display_seconds,
+ bool draw_vert_line);
+
void ED_time_scrub_draw(const struct ARegion *region,
const struct Scene *scene,
bool display_seconds,
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index e897fd2f689..8fae884747d 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -1726,12 +1726,17 @@ void UI_panel_category_draw_all(struct ARegion *region, const char *category_id_
struct PanelType *UI_paneltype_find(int space_id, int region_id, const char *idname);
+struct PointerRNA *UI_region_panel_custom_data_under_cursor(const struct bContext *C,
+ const struct wmEvent *event);
+void UI_panel_custom_data_set(struct Panel *panel, struct PointerRNA *custom_data);
+
/* Polyinstantiated panels for representing a list of data. */
struct Panel *UI_panel_add_instanced(struct ScrArea *area,
struct ARegion *region,
struct ListBase *panels,
char *panel_idname,
- int list_index);
+ int list_index,
+ struct PointerRNA *custom_data);
void UI_panels_free_instanced(struct bContext *C, struct ARegion *region);
#define LIST_PANEL_UNIQUE_STR_LEN 4
@@ -2024,6 +2029,10 @@ void uiTemplatePathBuilder(uiLayout *layout,
struct PointerRNA *root_ptr,
const char *text);
void uiTemplateModifiers(uiLayout *layout, struct bContext *C);
+void uiTemplateGpencilModifiers(uiLayout *layout, struct bContext *C);
+void uiTemplateShaderFx(uiLayout *layout, struct bContext *C);
+void uiTemplateConstraints(uiLayout *layout, struct bContext *C, bool use_bone_constraints);
+
uiLayout *uiTemplateGpencilModifier(uiLayout *layout, struct bContext *C, struct PointerRNA *ptr);
void uiTemplateGpencilColorPreview(uiLayout *layout,
struct bContext *C,
@@ -2034,11 +2043,9 @@ void uiTemplateGpencilColorPreview(uiLayout *layout,
float scale,
int filter);
-uiLayout *uiTemplateShaderFx(uiLayout *layout, struct bContext *C, struct PointerRNA *ptr);
-
void uiTemplateOperatorRedoProperties(uiLayout *layout, const struct bContext *C);
-uiLayout *uiTemplateConstraint(uiLayout *layout, struct PointerRNA *ptr);
+void uiTemplateConstraintHeader(uiLayout *layout, struct PointerRNA *ptr);
void uiTemplatePreview(uiLayout *layout,
struct bContext *C,
struct ID *id,
diff --git a/source/blender/editors/include/UI_interface_icons.h b/source/blender/editors/include/UI_interface_icons.h
index a304c76bc9d..0529ee08da6 100644
--- a/source/blender/editors/include/UI_interface_icons.h
+++ b/source/blender/editors/include/UI_interface_icons.h
@@ -106,6 +106,7 @@ struct PreviewImage *UI_icon_to_preview(int icon_id);
int UI_rnaptr_icon_get(struct bContext *C, struct PointerRNA *ptr, int rnaicon, const bool big);
int UI_idcode_icon_get(const int idcode);
+int UI_library_icon_get(const struct ID *id);
#ifdef __cplusplus
}
diff --git a/source/blender/editors/include/UI_view2d.h b/source/blender/editors/include/UI_view2d.h
index ffc06e94a90..0ddc45f4878 100644
--- a/source/blender/editors/include/UI_view2d.h
+++ b/source/blender/editors/include/UI_view2d.h
@@ -180,9 +180,10 @@ void UI_view2d_draw_scale_x__frames_or_seconds(const struct ARegion *region,
int colorid);
/* scrollbar drawing */
-View2DScrollers *UI_view2d_scrollers_calc(struct View2D *v2d, const struct rcti *mask_custom);
-void UI_view2d_scrollers_draw(struct View2D *v2d, View2DScrollers *scrollers);
-void UI_view2d_scrollers_free(View2DScrollers *scrollers);
+void UI_view2d_scrollers_calc(struct View2D *v2d,
+ const struct rcti *mask_custom,
+ struct View2DScrollers *r_scrollers);
+void UI_view2d_scrollers_draw(struct View2D *v2d, const struct rcti *mask_custom);
/* list view tools */
void UI_view2d_listview_view_to_cell(float columnwidth,
diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c
index cbd357fd324..d201fcd8684 100644
--- a/source/blender/editors/interface/interface.c
+++ b/source/blender/editors/interface/interface.c
@@ -1504,7 +1504,7 @@ static void ui_menu_block_set_keymaps(const bContext *C, uiBlock *block)
void ui_but_override_flag(uiBut *but)
{
- const int override_status = RNA_property_override_library_status(
+ const uint override_status = RNA_property_override_library_status(
&but->rnapoin, but->rnaprop, but->rnaindex);
if (override_status & RNA_OVERRIDE_STATUS_OVERRIDDEN) {
@@ -6445,7 +6445,7 @@ void UI_but_func_search_set_context_menu(uiBut *but, uiButSearchContextMenuFn co
}
/**
- * \param separator_string: when not NULL, this string is used as a separator,
+ * \param search_sep_string: when not NULL, this string is used as a separator,
* showing the icon and highlighted text after the last instance of this string.
*/
void UI_but_func_search_set_sep_string(uiBut *but, const char *search_sep_string)
diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c
index cc370113422..15d00d8537b 100644
--- a/source/blender/editors/interface/interface_context_menu.c
+++ b/source/blender/editors/interface/interface_context_menu.c
@@ -561,7 +561,7 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but)
const bool is_array_component = (is_array && but->rnaindex != -1);
const bool is_whole_array = (is_array && but->rnaindex == -1);
- const int override_status = RNA_property_override_library_status(ptr, prop, -1);
+ const uint override_status = RNA_property_override_library_status(ptr, prop, -1);
const bool is_overridable = (override_status & RNA_OVERRIDE_STATUS_OVERRIDABLE) != 0;
/* Set the (button_pointer, button_prop)
diff --git a/source/blender/editors/interface/interface_draw.c b/source/blender/editors/interface/interface_draw.c
index 499d2f7f1fa..720a5bcff37 100644
--- a/source/blender/editors/interface/interface_draw.c
+++ b/source/blender/editors/interface/interface_draw.c
@@ -25,6 +25,7 @@
#include <string.h>
#include "DNA_color_types.h"
+#include "DNA_curve_types.h"
#include "DNA_curveprofile_types.h"
#include "DNA_movieclip_types.h"
#include "DNA_screen_types.h"
@@ -118,51 +119,53 @@ void UI_draw_roundbox_aa(
bool filled, float minx, float miny, float maxx, float maxy, float rad, const float color[4])
{
uiWidgetBaseParameters widget_params = {
- .recti.xmin = minx,
- .recti.ymin = miny,
- .recti.xmax = maxx,
- .recti.ymax = maxy,
+ .recti.xmin = minx + U.pixelsize,
+ .recti.ymin = miny + U.pixelsize,
+ .recti.xmax = maxx - U.pixelsize,
+ .recti.ymax = maxy - U.pixelsize,
+ .rect.xmin = minx,
+ .rect.ymin = miny,
+ .rect.xmax = maxx,
+ .rect.ymax = maxy,
.radi = rad,
+ .rad = rad,
.round_corners[0] = (roundboxtype & UI_CNR_BOTTOM_LEFT) ? 1.0f : 0.0f,
.round_corners[1] = (roundboxtype & UI_CNR_BOTTOM_RIGHT) ? 1.0f : 0.0f,
.round_corners[2] = (roundboxtype & UI_CNR_TOP_RIGHT) ? 1.0f : 0.0f,
.round_corners[3] = (roundboxtype & UI_CNR_TOP_LEFT) ? 1.0f : 0.0f,
- .color_inner1[0] = color[0],
- .color_inner2[0] = color[0],
- .color_inner1[1] = color[1],
- .color_inner2[1] = color[1],
- .color_inner1[2] = color[2],
- .color_inner2[2] = color[2],
- .color_inner1[3] = color[3],
- .color_inner2[3] = color[3],
+ .color_inner1[0] = filled ? color[0] : 0.0f,
+ .color_inner1[1] = filled ? color[1] : 0.0f,
+ .color_inner1[2] = filled ? color[2] : 0.0f,
+ .color_inner1[3] = filled ? color[3] : 0.0f,
+ .color_inner2[0] = filled ? color[0] : 0.0f,
+ .color_inner2[1] = filled ? color[1] : 0.0f,
+ .color_inner2[2] = filled ? color[2] : 0.0f,
+ .color_inner2[3] = filled ? color[3] : 0.0f,
+ .color_outline[0] = color[0],
+ .color_outline[1] = color[1],
+ .color_outline[2] = color[2],
+ .color_outline[3] = color[3],
.alpha_discard = 1.0f,
};
- GPU_blend(true);
-
+ /* XXX this is to emulate previous behavior of semitransparent fills but that's was a side effect
+ * of the previous AA method. Better fix the callers. */
if (filled) {
- /* plain antialiased filled box */
- widget_params.color_inner1[3] *= 0.125f;
- widget_params.color_inner2[3] *= 0.125f;
-
- /* WATCH: This is assuming the ModelViewProjectionMatrix is area pixel space.
- * If it has been scaled, then it's no longer valid. */
- GPUBatch *batch = ui_batch_roundbox_get(filled, true);
- GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE);
- GPU_batch_uniform_4fv_array(batch, "parameters", 11, (float *)&widget_params);
- GPU_batch_draw(batch);
+ widget_params.color_inner1[3] *= 0.65f;
+ widget_params.color_inner2[3] *= 0.65f;
+ widget_params.color_outline[3] *= 0.65f;
}
- else {
- /* plain antialiased unfilled box */
- GPU_line_smooth(true);
- GPUBatch *batch = ui_batch_roundbox_get(filled, false);
- GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE);
- GPU_batch_uniform_4fv_array(batch, "parameters", 11, (float *)&widget_params);
- GPU_batch_draw(batch);
+ /* WATCH: This is assuming the ModelViewProjectionMatrix is area pixel space.
+ * If it has been scaled, then it's no longer valid. */
- GPU_line_smooth(false);
- }
+ GPUBatch *batch = ui_batch_roundbox_widget_get();
+ GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE);
+ GPU_batch_uniform_4fv_array(batch, "parameters", 11, (float *)&widget_params);
+
+ GPU_blend(true);
+
+ GPU_batch_draw(batch);
GPU_blend(false);
}
@@ -251,32 +254,49 @@ void UI_draw_roundbox_4fv(
immEnd();
immUnbindProgram();
#endif
-
uiWidgetBaseParameters widget_params = {
- .recti.xmin = minx,
- .recti.ymin = miny,
- .recti.xmax = maxx,
- .recti.ymax = maxy,
+ .recti.xmin = minx + U.pixelsize,
+ .recti.ymin = miny + U.pixelsize,
+ .recti.xmax = maxx - U.pixelsize,
+ .recti.ymax = maxy - U.pixelsize,
+ .rect.xmin = minx,
+ .rect.ymin = miny,
+ .rect.xmax = maxx,
+ .rect.ymax = maxy,
.radi = rad,
+ .rad = rad,
.round_corners[0] = (roundboxtype & UI_CNR_BOTTOM_LEFT) ? 1.0f : 0.0f,
.round_corners[1] = (roundboxtype & UI_CNR_BOTTOM_RIGHT) ? 1.0f : 0.0f,
.round_corners[2] = (roundboxtype & UI_CNR_TOP_RIGHT) ? 1.0f : 0.0f,
.round_corners[3] = (roundboxtype & UI_CNR_TOP_LEFT) ? 1.0f : 0.0f,
- .color_inner1[0] = col[0],
- .color_inner2[0] = col[0],
- .color_inner1[1] = col[1],
- .color_inner2[1] = col[1],
- .color_inner1[2] = col[2],
- .color_inner2[2] = col[2],
- .color_inner1[3] = col[3],
- .color_inner2[3] = col[3],
+ .color_inner1[0] = filled ? col[0] : 0.0f,
+ .color_inner1[1] = filled ? col[1] : 0.0f,
+ .color_inner1[2] = filled ? col[2] : 0.0f,
+ .color_inner1[3] = filled ? col[3] : 0.0f,
+ .color_inner2[0] = filled ? col[0] : 0.0f,
+ .color_inner2[1] = filled ? col[1] : 0.0f,
+ .color_inner2[2] = filled ? col[2] : 0.0f,
+ .color_inner2[3] = filled ? col[3] : 0.0f,
+ .color_outline[0] = col[0],
+ .color_outline[1] = col[1],
+ .color_outline[2] = col[2],
+ .color_outline[3] = col[3],
.alpha_discard = 1.0f,
};
+ /* Exactly the same as UI_draw_roundbox_aa but does not do the legacy transparency. */
- GPUBatch *batch = ui_batch_roundbox_get(filled, false);
+ /* WATCH: This is assuming the ModelViewProjectionMatrix is area pixel space.
+ * If it has been scaled, then it's no longer valid. */
+
+ GPUBatch *batch = ui_batch_roundbox_widget_get();
GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE);
GPU_batch_uniform_4fv_array(batch, "parameters", 11, (float *)&widget_params);
+
+ GPU_blend(true);
+
GPU_batch_draw(batch);
+
+ GPU_blend(false);
}
#if 0
@@ -427,32 +447,46 @@ void UI_draw_roundbox_shade_x(bool filled,
immEnd();
immUnbindProgram();
#endif
-
uiWidgetBaseParameters widget_params = {
- .recti.xmin = minx,
- .recti.ymin = miny,
- .recti.xmax = maxx,
- .recti.ymax = maxy,
+ .recti.xmin = minx + U.pixelsize,
+ .recti.ymin = miny + U.pixelsize,
+ .recti.xmax = maxx - U.pixelsize,
+ .recti.ymax = maxy - U.pixelsize,
+ .rect.xmin = minx,
+ .rect.ymin = miny,
+ .rect.xmax = maxx,
+ .rect.ymax = maxy,
.radi = rad,
+ .rad = rad,
.round_corners[0] = (roundboxtype & UI_CNR_BOTTOM_LEFT) ? 1.0f : 0.0f,
.round_corners[1] = (roundboxtype & UI_CNR_BOTTOM_RIGHT) ? 1.0f : 0.0f,
.round_corners[2] = (roundboxtype & UI_CNR_TOP_RIGHT) ? 1.0f : 0.0f,
.round_corners[3] = (roundboxtype & UI_CNR_TOP_LEFT) ? 1.0f : 0.0f,
- .color_inner1[0] = min_ff(1.0f, col[0] + shadetop),
- .color_inner2[0] = max_ff(0.0f, col[0] + shadedown),
- .color_inner1[1] = min_ff(1.0f, col[1] + shadetop),
- .color_inner2[1] = max_ff(0.0f, col[1] + shadedown),
- .color_inner1[2] = min_ff(1.0f, col[2] + shadetop),
- .color_inner2[2] = max_ff(0.0f, col[2] + shadedown),
- .color_inner1[3] = 1.0f,
- .color_inner2[3] = 1.0f,
+ .color_inner1[0] = !filled ? 0.0f : min_ff(1.0f, col[0] + shadetop),
+ .color_inner1[1] = !filled ? 0.0f : min_ff(1.0f, col[1] + shadetop),
+ .color_inner1[2] = !filled ? 0.0f : min_ff(1.0f, col[2] + shadetop),
+ .color_inner1[3] = !filled ? 0.0f : 1.0f,
+ .color_inner2[0] = !filled ? 0.0f : max_ff(0.0f, col[0] + shadedown),
+ .color_inner2[1] = !filled ? 0.0f : max_ff(0.0f, col[1] + shadedown),
+ .color_inner2[2] = !filled ? 0.0f : max_ff(0.0f, col[2] + shadedown),
+ .color_inner2[3] = !filled ? 0.0f : 1.0f,
+ /* TODO: non-filled box don't have gradients. Just use middle color. */
+ .color_outline[0] = clamp_f(col[0] + shadetop + shadedown, 0.0f, 1.0f),
+ .color_outline[1] = clamp_f(col[1] + shadetop + shadedown, 0.0f, 1.0f),
+ .color_outline[2] = clamp_f(col[2] + shadetop + shadedown, 0.0f, 1.0f),
+ .color_outline[3] = clamp_f(col[3] + shadetop + shadedown, 0.0f, 1.0f),
+ .shade_dir = 1.0f,
.alpha_discard = 1.0f,
};
- GPUBatch *batch = ui_batch_roundbox_get(filled, false);
+ GPU_blend(true);
+
+ GPUBatch *batch = ui_batch_roundbox_widget_get();
GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE);
GPU_batch_uniform_4fv_array(batch, "parameters", 11, (float *)&widget_params);
GPU_batch_draw(batch);
+
+ GPU_blend(false);
}
#if 0 /* unused */
@@ -765,10 +799,8 @@ void ui_draw_but_IMAGE(ARegion *UNUSED(region),
*
* \note This function is to be used with the 2D dashed shader enabled.
*
- * \param pos: is a PRIM_FLOAT, 2, GPU_FETCH_FLOAT vertex attribute.
- * \param line_origin: is a PRIM_FLOAT, 2, GPU_FETCH_FLOAT vertex attribute.
- *
- * The next 4 parameters are the offsets for the view, not the zones.
+ * \param pos: is a #PRIM_FLOAT, 2, #GPU_FETCH_FLOAT vertex attribute.
+ * \param x1, x2, y1, y2: The offsets for the view, not the zones.
*/
void UI_draw_safe_areas(uint pos,
float x1,
@@ -801,7 +833,7 @@ void UI_draw_safe_areas(uint pos,
static void draw_scope_end(const rctf *rect, GLint *scissor)
{
- /* restore scissortest */
+ /* Restore scissor test. */
GPU_scissor(scissor[0], scissor[1], scissor[2], scissor[3]);
GPU_blend_set_func_separate(
@@ -2131,7 +2163,19 @@ void ui_draw_but_CURVE(ARegion *region, uiBut *but, const uiWidgetColors *wcol,
immUnbindProgram();
}
-/** Used to draw a curve profile widget. Somewhat similar to ui_draw_but_CURVE */
+/**
+ * Helper for #ui_draw_but_CURVEPROFILE. Used to tell whether to draw a control point's handles.
+ */
+static bool point_draw_handles(CurveProfilePoint *point)
+{
+ return (point->flag & PROF_SELECT &&
+ (ELEM(point->h1, HD_FREE, HD_ALIGN) || ELEM(point->h2, HD_FREE, HD_ALIGN))) ||
+ ELEM(point->flag, PROF_H1_SELECT, PROF_H2_SELECT);
+}
+
+/**
+ * Draws the curve profile widget. Somewhat similar to ui_draw_but_CURVE.
+ */
void ui_draw_but_CURVEPROFILE(ARegion *region,
uiBut *but,
const uiWidgetColors *wcol,
@@ -2147,18 +2191,18 @@ void ui_draw_but_CURVEPROFILE(ARegion *region,
profile = (CurveProfile *)but->poin;
}
- /* Calculate offset and zoom */
+ /* Calculate offset and zoom. */
float zoomx = (BLI_rcti_size_x(rect) - 2.0f) / BLI_rctf_size_x(&profile->view_rect);
float zoomy = (BLI_rcti_size_y(rect) - 2.0f) / BLI_rctf_size_y(&profile->view_rect);
float offsx = profile->view_rect.xmin - (1.0f / zoomx);
float offsy = profile->view_rect.ymin - (1.0f / zoomy);
- /* Exit early if too narrow */
+ /* Exit early if too narrow. */
if (zoomx == 0.0f) {
return;
}
- /* Test needed because path can draw outside of boundary */
+ /* Test needed because path can draw outside of boundary. */
int scissor[4];
GPU_scissor_get_i(scissor);
rcti scissor_new = {
@@ -2180,7 +2224,7 @@ void ui_draw_but_CURVEPROFILE(ARegion *region,
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
- /* Backdrop */
+ /* Draw the backdrop. */
float color_backdrop[4] = {0, 0, 0, 1};
if (profile->flag & PROF_USE_CLIP) {
gl_shaded_color_get_fl((uchar *)wcol->inner, -20, color_backdrop);
@@ -2199,33 +2243,33 @@ void ui_draw_but_CURVEPROFILE(ARegion *region,
immRectf(pos, rect->xmin, rect->ymin, rect->xmax, rect->ymax);
}
- /* 0.25 step grid */
+ /* 0.25 step grid. */
gl_shaded_color((uchar *)wcol->inner, -16);
ui_draw_but_curve_grid(pos, rect, zoomx, zoomy, offsx, offsy, 0.25f);
- /* 1.0 step grid */
+ /* 1.0 step grid. */
gl_shaded_color((uchar *)wcol->inner, -24);
ui_draw_but_curve_grid(pos, rect, zoomx, zoomy, offsx, offsy, 1.0f);
- /* Draw the path's fill */
+ /* Draw the path's fill. */
if (profile->table == NULL) {
- BKE_curveprofile_update(profile, false);
+ BKE_curveprofile_update(profile, PROF_UPDATE_NONE);
}
CurveProfilePoint *pts = profile->table;
- /* Also add the last points on the right and bottom edges to close off the fill polygon */
+ /* Also add the last points on the right and bottom edges to close off the fill polygon. */
bool add_left_tri = profile->view_rect.xmin < 0.0f;
bool add_bottom_tri = profile->view_rect.ymin < 0.0f;
uint tot_points = (uint)PROF_N_TABLE(profile->path_len) + 1 + add_left_tri + add_bottom_tri;
uint tot_triangles = tot_points - 2;
- /* Create array of the positions of the table's points */
+ /* Create array of the positions of the table's points. */
float(*table_coords)[2] = MEM_mallocN(sizeof(*table_coords) * tot_points, "table x coords");
for (i = 0; i < (uint)PROF_N_TABLE(profile->path_len);
- i++) { /* Only add the points from the table here */
+ i++) { /* Only add the points from the table here. */
table_coords[i][0] = pts[i].x;
table_coords[i][1] = pts[i].y;
}
if (add_left_tri && add_bottom_tri) {
- /* Add left side, bottom left corner, and bottom side points */
+ /* Add left side, bottom left corner, and bottom side points. */
table_coords[tot_points - 3][0] = profile->view_rect.xmin;
table_coords[tot_points - 3][1] = 1.0f;
table_coords[tot_points - 2][0] = profile->view_rect.xmin;
@@ -2234,30 +2278,30 @@ void ui_draw_but_CURVEPROFILE(ARegion *region,
table_coords[tot_points - 1][1] = profile->view_rect.ymin;
}
else if (add_left_tri) {
- /* Add the left side and bottom left corner points */
+ /* Add the left side and bottom left corner points. */
table_coords[tot_points - 2][0] = profile->view_rect.xmin;
table_coords[tot_points - 2][1] = 1.0f;
table_coords[tot_points - 1][0] = profile->view_rect.xmin;
table_coords[tot_points - 1][1] = 0.0f;
}
else if (add_bottom_tri) {
- /* Add the bottom side and bottom left corner points */
+ /* Add the bottom side and bottom left corner points. */
table_coords[tot_points - 2][0] = 0.0f;
table_coords[tot_points - 2][1] = profile->view_rect.ymin;
table_coords[tot_points - 1][0] = 1.0f;
table_coords[tot_points - 1][1] = profile->view_rect.ymin;
}
else {
- /* Just add the bottom corner point. Side points would be redundant anyway */
+ /* Just add the bottom corner point. Side points would be redundant anyway. */
table_coords[tot_points - 1][0] = 0.0f;
table_coords[tot_points - 1][1] = 0.0f;
}
- /* Calculate the table point indices of the triangles for the profile's fill */
+ /* Calculate the table point indices of the triangles for the profile's fill. */
uint(*tri_indices)[3] = MEM_mallocN(sizeof(*tri_indices) * tot_triangles, "return tri indices");
BLI_polyfill_calc(table_coords, tot_points, -1, tri_indices);
- /* Draw the triangles for the profile fill */
+ /* Draw the triangles for the profile fill. */
immUniformColor3ubvAlpha((const uchar *)wcol->item, 128);
GPU_blend(true);
GPU_polygon_smooth(false);
@@ -2273,7 +2317,7 @@ void ui_draw_but_CURVEPROFILE(ARegion *region,
immEnd();
MEM_freeN(tri_indices);
- /* Draw the profile's path so the edge stands out a bit */
+ /* Draw the profile's path so the edge stands out a bit. */
tot_points -= (add_left_tri + add_left_tri);
GPU_line_width(1.0f);
immUniformColor3ubvAlpha((const uchar *)wcol->item, 255);
@@ -2285,9 +2329,44 @@ void ui_draw_but_CURVEPROFILE(ARegion *region,
immVertex2f(pos, fx, fy);
}
immEnd();
- immUnbindProgram();
MEM_freeN(table_coords);
+ /* Draw the handles for the selected control points. */
+ pts = profile->path;
+ tot_points = (uint)profile->path_len;
+ int selected_free_points = 0;
+ for (i = 0; i < tot_points; i++) {
+ if (point_draw_handles(&pts[i])) {
+ selected_free_points++;
+ }
+ }
+ /* Draw the lines to the handles from the points. */
+ if (selected_free_points > 0) {
+ GPU_line_width(1.0f);
+ gl_shaded_color((uchar *)wcol->inner, -24);
+ GPU_line_smooth(true);
+ immBegin(GPU_PRIM_LINES, selected_free_points * 4);
+ float ptx, pty;
+ for (i = 0; i < tot_points; i++) {
+ if (point_draw_handles(&pts[i])) {
+ ptx = rect->xmin + zoomx * (pts[i].x - offsx);
+ pty = rect->ymin + zoomy * (pts[i].y - offsy);
+
+ fx = rect->xmin + zoomx * (pts[i].h1_loc[0] - offsx);
+ fy = rect->ymin + zoomy * (pts[i].h1_loc[1] - offsy);
+ immVertex2f(pos, ptx, pty);
+ immVertex2f(pos, fx, fy);
+
+ fx = rect->xmin + zoomx * (pts[i].h2_loc[0] - offsx);
+ fy = rect->ymin + zoomy * (pts[i].h2_loc[1] - offsy);
+ immVertex2f(pos, ptx, pty);
+ immVertex2f(pos, fx, fy);
+ }
+ }
+ immEnd();
+ }
+ immUnbindProgram();
+
/* New GPU instructions for control points and sampled points. */
format = immVertexFormat();
pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
@@ -2311,8 +2390,6 @@ void ui_draw_but_CURVEPROFILE(ARegion *region,
}
/* Draw the control points. */
- pts = profile->path;
- tot_points = (uint)profile->path_len;
GPU_line_smooth(false);
GPU_blend(false);
GPU_point_size(max_ff(3.0f, min_ff(UI_DPI_FAC / but->block->aspect * 5.0f, 5.0f)));
@@ -2325,6 +2402,28 @@ void ui_draw_but_CURVEPROFILE(ARegion *region,
}
immEnd();
+ /* Draw the handle points. */
+ if (selected_free_points > 0) {
+ GPU_line_smooth(false);
+ GPU_blend(false);
+ GPU_point_size(max_ff(2.0f, min_ff(UI_DPI_FAC / but->block->aspect * 4.0f, 4.0f)));
+ immBegin(GPU_PRIM_POINTS, selected_free_points * 2);
+ for (i = 0; i < tot_points; i++) {
+ if (point_draw_handles(&pts[i])) {
+ fx = rect->xmin + zoomx * (pts[i].h1_loc[0] - offsx);
+ fy = rect->ymin + zoomy * (pts[i].h1_loc[1] - offsy);
+ immAttr4fv(col, (pts[i].flag & PROF_H1_SELECT) ? color_vert_select : color_vert);
+ immVertex2f(pos, fx, fy);
+
+ fx = rect->xmin + zoomx * (pts[i].h2_loc[0] - offsx);
+ fy = rect->ymin + zoomy * (pts[i].h2_loc[1] - offsy);
+ immAttr4fv(col, (pts[i].flag & PROF_H2_SELECT) ? color_vert_select : color_vert);
+ immVertex2f(pos, fx, fy);
+ }
+ }
+ immEnd();
+ }
+
/* Draw the sampled points in addition to the control points if they have been created */
pts = profile->segments;
tot_points = (uint)profile->segments_len;
@@ -2339,7 +2438,6 @@ void ui_draw_but_CURVEPROFILE(ARegion *region,
}
immEnd();
}
-
immUnbindProgram();
/* restore scissortest */
@@ -2678,7 +2776,6 @@ void ui_draw_dropshadow(
GPU_batch_draw(batch);
/* outline emphasis */
- GPU_line_smooth(true);
float color[4] = {0.0f, 0.0f, 0.0f, 0.4f};
UI_draw_roundbox_4fv(false,
rct->xmin - 0.5f,
@@ -2687,7 +2784,6 @@ void ui_draw_dropshadow(
rct->ymax + 0.5f,
radius + 0.5f,
color);
- GPU_line_smooth(false);
GPU_blend(false);
}
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index 42feda0e1bc..f6bfb492c92 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -4872,7 +4872,7 @@ static void ui_numedit_set_active(uiBut *but)
/* Don't change the cursor once pressed. */
if ((but->flag & UI_SELECT) == 0) {
- if ((but->drawflag & (UI_BUT_ACTIVE_LEFT)) || (but->drawflag & (UI_BUT_ACTIVE_RIGHT))) {
+ if ((but->drawflag & UI_BUT_ACTIVE_LEFT) || (but->drawflag & UI_BUT_ACTIVE_RIGHT)) {
if (data->changed_cursor) {
WM_cursor_modal_restore(data->window);
data->changed_cursor = false;
@@ -6944,7 +6944,7 @@ static bool ui_numedit_but_CURVEPROFILE(uiBlock *block,
d[0] = mx - data->dragstartx;
d[1] = my - data->dragstarty;
- if (len_squared_v2(d) < (3.0f * 3.0f)) {
+ if (len_squared_v2(d) < (9.0f * U.dpi_fac)) {
snap = false;
}
}
@@ -6953,32 +6953,38 @@ static bool ui_numedit_but_CURVEPROFILE(uiBlock *block,
fy = (my - dragy) / zoomy;
if (data->dragsel != -1) {
- CurveProfilePoint *point_last = NULL;
+ float last_x, last_y;
const float mval_factor = ui_mouse_scale_warp_factor(shift);
bool moved_point = false; /* for ctrl grid, can't use orig coords because of sorting */
fx *= mval_factor;
fy *= mval_factor;
- /* Move all the points that aren't the last or the first */
- for (a = 1; a < profile->path_len - 1; a++) {
- if (pts[a].flag & PROF_SELECT) {
- float origx = pts[a].x, origy = pts[a].y;
- pts[a].x += fx;
- pts[a].y += fy;
- if (snap) {
- pts[a].x = 0.125f * roundf(8.0f * pts[a].x);
- pts[a].y = 0.125f * roundf(8.0f * pts[a].y);
+ /* Move all selected points. */
+ float delta[2] = {fx, fy};
+ for (a = 0; a < profile->path_len; a++) {
+ /* Don't move the last and first control points. */
+ if ((pts[a].flag & PROF_SELECT) && (a != 0) && (a != profile->path_len)) {
+ moved_point |= BKE_curveprofile_move_point(profile, &pts[a], snap, delta);
+ last_x = pts[a].x;
+ last_y = pts[a].y;
+ }
+ else {
+ /* Move handles when they're selected but the control point isn't. */
+ if (ELEM(pts[a].h2, HD_FREE, HD_ALIGN) && pts[a].flag == PROF_H1_SELECT) {
+ moved_point |= BKE_curveprofile_move_handle(&pts[a], true, snap, delta);
+ last_x = pts[a].h1_loc[0];
+ last_y = pts[a].h1_loc[1];
}
- if (!moved_point && (pts[a].x != origx || pts[a].y != origy)) {
- moved_point = true;
+ if (ELEM(pts[a].h2, HD_FREE, HD_ALIGN) && pts[a].flag == PROF_H2_SELECT) {
+ moved_point |= BKE_curveprofile_move_handle(&pts[a], false, snap, delta);
+ last_x = pts[a].h2_loc[0];
+ last_y = pts[a].h2_loc[1];
}
-
- point_last = &pts[a];
}
}
- BKE_curveprofile_update(profile, false);
+ BKE_curveprofile_update(profile, PROF_UPDATE_NONE);
if (moved_point) {
data->draglastx = evtx;
@@ -6989,10 +6995,8 @@ static bool ui_numedit_but_CURVEPROFILE(uiBlock *block,
* but in practice this isnt really an issue */
if (ui_but_is_cursor_warp(but)) {
/* OK but can go outside bounds */
- data->ungrab_mval[0] = but->rect.xmin +
- ((point_last->x - profile->view_rect.xmin) * zoomx);
- data->ungrab_mval[1] = but->rect.ymin +
- ((point_last->y - profile->view_rect.ymin) * zoomy);
+ data->ungrab_mval[0] = but->rect.xmin + ((last_x - profile->view_rect.xmin) * zoomx);
+ data->ungrab_mval[1] = but->rect.ymin + ((last_y - profile->view_rect.ymin) * zoomy);
BLI_rctf_clamp_pt_v(&but->rect, data->ungrab_mval);
}
#endif
@@ -7000,7 +7004,7 @@ static bool ui_numedit_but_CURVEPROFILE(uiBlock *block,
data->dragchange = true; /* mark for selection */
}
else {
- /* clamp for clip */
+ /* Clamp the view rect when clipping is on. */
if (profile->flag & PROF_USE_CLIP) {
if (profile->view_rect.xmin - fx < profile->clip_rect.xmin) {
fx = profile->view_rect.xmin - profile->clip_rect.xmin;
@@ -7031,16 +7035,26 @@ static bool ui_numedit_but_CURVEPROFILE(uiBlock *block,
}
/**
+ * Helper for #ui_do_but_CURVEPROFILE. Used to tell whether to select a control point's handles.
+ */
+static bool point_draw_handles(CurveProfilePoint *point)
+{
+ return (point->flag & PROF_SELECT &&
+ (ELEM(point->h1, HD_FREE, HD_ALIGN) || ELEM(point->h2, HD_FREE, HD_ALIGN))) ||
+ ELEM(point->flag, PROF_H1_SELECT, PROF_H2_SELECT);
+}
+
+/**
* Interaction for curve profile widget.
* \note Uses hardcoded keys rather than the keymap.
*/
static int ui_do_but_CURVEPROFILE(
bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
{
- int mx, my, i;
+ CurveProfile *profile = (CurveProfile *)but->poin;
+ int mx = event->x;
+ int my = event->y;
- mx = event->x;
- my = event->y;
ui_window_to_block(data->region, block, &mx, &my);
/* Move selected control points. */
@@ -7053,12 +7067,10 @@ static int ui_do_but_CURVEPROFILE(
return WM_UI_HANDLER_BREAK;
}
- CurveProfile *profile = (CurveProfile *)but->poin;
-
/* Delete selected control points. */
if (event->type == EVT_XKEY && event->val == KM_RELEASE) {
BKE_curveprofile_remove_by_flag(profile, PROF_SELECT);
- BKE_curveprofile_update(profile, false);
+ BKE_curveprofile_update(profile, PROF_UPDATE_NONE);
button_activate_state(C, but, BUTTON_STATE_EXIT);
return WM_UI_HANDLER_BREAK;
}
@@ -7066,76 +7078,94 @@ static int ui_do_but_CURVEPROFILE(
/* Selecting, adding, and starting point movements. */
if (data->state == BUTTON_STATE_HIGHLIGHT) {
if (event->type == LEFTMOUSE && event->val == KM_PRESS) {
- CurveProfilePoint *pts; /* Path or table. */
const float m_xy[2] = {mx, my};
- float dist_min_sq;
- int i_selected = -1;
if (event->ctrl) {
float f_xy[2];
BLI_rctf_transform_pt_v(&profile->view_rect, &but->rect, f_xy, m_xy);
BKE_curveprofile_insert(profile, f_xy[0], f_xy[1]);
- BKE_curveprofile_update(profile, false);
+ BKE_curveprofile_update(profile, PROF_UPDATE_CLIP);
}
/* Check for selecting of a point by finding closest point in radius. */
- dist_min_sq = square_f(U.dpi_fac * 14.0f); /* 14 pixels radius for selecting points. */
- pts = profile->path;
- for (i = 0; i < profile->path_len; i++) {
+ CurveProfilePoint *pts = profile->path;
+ float dist_min_sq = square_f(U.dpi_fac * 14.0f); /* 14 pixels radius for selecting points. */
+ int i_selected = -1;
+ short selection_type = 0; /* For handle selection. */
+ for (int i = 0; i < profile->path_len; i++) {
float f_xy[2];
BLI_rctf_transform_pt_v(&but->rect, &profile->view_rect, f_xy, &pts[i].x);
- const float dist_sq = len_squared_v2v2(m_xy, f_xy);
+ float dist_sq = len_squared_v2v2(m_xy, f_xy);
if (dist_sq < dist_min_sq) {
i_selected = i;
+ selection_type = PROF_SELECT;
dist_min_sq = dist_sq;
}
+
+ /* Also select handles if the point is selected and it has the right handle type. */
+ if (point_draw_handles(&pts[i])) {
+ if (ELEM(profile->path[i].h1, HD_FREE, HD_ALIGN)) {
+ BLI_rctf_transform_pt_v(&but->rect, &profile->view_rect, f_xy, pts[i].h1_loc);
+ dist_sq = len_squared_v2v2(m_xy, f_xy);
+ if (dist_sq < dist_min_sq) {
+ i_selected = i;
+ selection_type = PROF_H1_SELECT;
+ dist_min_sq = dist_sq;
+ }
+ }
+ if (ELEM(profile->path[i].h2, HD_FREE, HD_ALIGN)) {
+ BLI_rctf_transform_pt_v(&but->rect, &profile->view_rect, f_xy, pts[i].h2_loc);
+ dist_sq = len_squared_v2v2(m_xy, f_xy);
+ if (dist_sq < dist_min_sq) {
+ i_selected = i;
+ selection_type = PROF_H2_SELECT;
+ dist_min_sq = dist_sq;
+ }
+ }
+ }
}
- /* Add a point if the click was close to the path but not a control point. */
- if (i_selected == -1) { /* No control point selected. */
+ /* Add a point if the click was close to the path but not a control point or handle. */
+ if (i_selected == -1) {
float f_xy[2], f_xy_prev[2];
- pts = profile->table;
- BLI_rctf_transform_pt_v(&but->rect, &profile->view_rect, f_xy, &pts[0].x);
+ CurveProfilePoint *table = profile->table;
+ BLI_rctf_transform_pt_v(&but->rect, &profile->view_rect, f_xy, &table[0].x);
dist_min_sq = square_f(U.dpi_fac * 8.0f); /* 8 pixel radius from each table point. */
/* Loop through the path's high resolution table and find what's near the click. */
- for (i = 1; i <= PROF_N_TABLE(profile->path_len); i++) {
+ for (int i = 1; i <= PROF_N_TABLE(profile->path_len); i++) {
copy_v2_v2(f_xy_prev, f_xy);
- BLI_rctf_transform_pt_v(&but->rect, &profile->view_rect, f_xy, &pts[i].x);
+ BLI_rctf_transform_pt_v(&but->rect, &profile->view_rect, f_xy, &table[i].x);
if (dist_squared_to_line_segment_v2(m_xy, f_xy_prev, f_xy) < dist_min_sq) {
BLI_rctf_transform_pt_v(&profile->view_rect, &but->rect, f_xy, m_xy);
CurveProfilePoint *new_pt = BKE_curveprofile_insert(profile, f_xy[0], f_xy[1]);
- BKE_curveprofile_update(profile, false);
-
- /* reset pts back to the control points. */
- pts = profile->path;
+ BKE_curveprofile_update(profile, PROF_UPDATE_CLIP);
/* Get the index of the newly added point. */
- for (i = 0; i < profile->path_len; i++) {
- if (&pts[i] == new_pt) {
- i_selected = i;
- }
- }
+ i_selected = (int)(new_pt - profile->path);
+ BLI_assert(i_selected >= 0 && i_selected <= profile->path_len);
+ selection_type = PROF_SELECT;
break;
}
}
}
- /* Change the flag for the point(s) if one was selected. */
+ /* Change the flag for the point(s) if one was selected or added. */
if (i_selected != -1) {
/* Deselect all if this one is deselected, except if we hold shift. */
- if (!event->shift) {
- for (i = 0; i < profile->path_len; i++) {
- pts[i].flag &= ~PROF_SELECT;
- }
- pts[i_selected].flag |= PROF_SELECT;
+ if (event->shift) {
+ pts[i_selected].flag ^= selection_type;
}
else {
- pts[i_selected].flag ^= PROF_SELECT;
+ for (int i = 0; i < profile->path_len; i++) {
+ // pts[i].flag &= ~(PROF_SELECT | PROF_H1_SELECT | PROF_H2_SELECT);
+ profile->path[i].flag &= ~(PROF_SELECT | PROF_H1_SELECT | PROF_H2_SELECT);
+ }
+ profile->path[i_selected].flag |= selection_type;
}
}
else {
@@ -7166,19 +7196,13 @@ static int ui_do_but_CURVEPROFILE(
else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
/* Finish move. */
if (data->dragsel != -1) {
- CurveProfilePoint *pts = profile->path;
if (data->dragchange == false) {
/* Deselect all, select one. */
- if (!event->shift) {
- for (i = 0; i < profile->path_len; i++) {
- pts[i].flag &= ~PROF_SELECT;
- }
- pts[data->dragsel].flag |= PROF_SELECT;
- }
}
else {
- BKE_curveprofile_update(profile, true); /* Remove doubles after move. */
+ /* Remove doubles, clip after move. */
+ BKE_curveprofile_update(profile, PROF_UPDATE_REMOVE_DOUBLES | PROF_UPDATE_CLIP);
}
}
button_activate_state(C, but, BUTTON_STATE_EXIT);
@@ -9947,7 +9971,7 @@ static int ui_handle_menu_event(bContext *C,
if (ELEM(event->val, KM_PRESS, KM_DBL_CLICK)) {
if ((is_parent_menu == false) && (U.uiflag & USER_MENUOPENAUTO) == 0) {
/* for root menus, allow clicking to close */
- if (block->flag & (UI_BLOCK_OUT_1)) {
+ if (block->flag & UI_BLOCK_OUT_1) {
menu->menuretval = UI_RETURN_OK;
}
else {
@@ -9955,7 +9979,7 @@ static int ui_handle_menu_event(bContext *C,
}
}
else if (saferct && !BLI_rctf_isect_pt(&saferct->parent, event->x, event->y)) {
- if (block->flag & (UI_BLOCK_OUT_1)) {
+ if (block->flag & UI_BLOCK_OUT_1) {
menu->menuretval = UI_RETURN_OK;
}
else {
@@ -10049,7 +10073,7 @@ static int ui_handle_menu_event(bContext *C,
/* strict check, and include the parent rect */
if (!menu->dotowards && !saferct) {
- if (block->flag & (UI_BLOCK_OUT_1)) {
+ if (block->flag & UI_BLOCK_OUT_1) {
menu->menuretval = UI_RETURN_OK;
}
else {
diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c
index deea3028354..7411639a99f 100644
--- a/source/blender/editors/interface/interface_icons.c
+++ b/source/blender/editors/interface/interface_icons.c
@@ -2192,6 +2192,26 @@ int ui_id_icon_get(const bContext *C, ID *id, const bool big)
return iconid;
}
+int UI_library_icon_get(const ID *id)
+{
+ if (ID_IS_LINKED(id)) {
+ if (id->tag & LIB_TAG_MISSING) {
+ return ICON_LIBRARY_DATA_BROKEN;
+ }
+ else if (id->tag & LIB_TAG_INDIRECT) {
+ return ICON_LIBRARY_DATA_INDIRECT;
+ }
+ else {
+ return ICON_LIBRARY_DATA_DIRECT;
+ }
+ }
+ else if (ID_IS_OVERRIDE_LIBRARY(id)) {
+ return ICON_LIBRARY_DATA_OVERRIDE;
+ }
+
+ return ICON_NONE;
+}
+
int UI_rnaptr_icon_get(bContext *C, PointerRNA *ptr, int rnaicon, const bool big)
{
ID *id = NULL;
diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h
index 6cd990ec2b0..fac78808a9a 100644
--- a/source/blender/editors/interface/interface_intern.h
+++ b/source/blender/editors/interface/interface_intern.h
@@ -848,6 +848,8 @@ typedef struct uiWidgetBaseParameters {
* The absolute value itself is the discard factor.
* Initialize value to 1.0.f if you don't want discard */
float alpha_discard;
+ float tria_type;
+ float _pad[3];
} uiWidgetBaseParameters;
enum {
@@ -861,8 +863,7 @@ enum {
ROUNDBOX_TRIA_MAX, /* don't use */
};
-struct GPUBatch *ui_batch_roundbox_get(bool filled, bool antialiased);
-struct GPUBatch *ui_batch_roundbox_widget_get(int tria);
+struct GPUBatch *ui_batch_roundbox_widget_get(void);
struct GPUBatch *ui_batch_roundbox_shadow_get(void);
void ui_draw_anti_tria_rect(const rctf *rect, char dir, const float color[4]);
diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c
index 6aefef197df..188ff62d923 100644
--- a/source/blender/editors/interface/interface_layout.c
+++ b/source/blender/editors/interface/interface_layout.c
@@ -5576,6 +5576,7 @@ static void ui_paneltype_draw_impl(bContext *C, PanelType *pt, uiLayout *layout,
panel->layout = layout;
pt->draw(C, panel);
panel->layout = NULL;
+ BLI_assert(panel->runtime.custom_data_ptr == NULL);
MEM_freeN(panel);
diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c
index 41c89d2d832..924fc6738e1 100644
--- a/source/blender/editors/interface/interface_ops.c
+++ b/source/blender/editors/interface/interface_ops.c
@@ -513,7 +513,7 @@ static bool override_type_set_button_poll(bContext *C)
UI_context_active_but_prop_get(C, &ptr, &prop, &index);
- const int override_status = RNA_property_override_library_status(&ptr, prop, index);
+ const uint override_status = RNA_property_override_library_status(&ptr, prop, index);
return (ptr.data && prop && (override_status & RNA_OVERRIDE_STATUS_OVERRIDABLE));
}
@@ -613,7 +613,7 @@ static bool override_remove_button_poll(bContext *C)
UI_context_active_but_prop_get(C, &ptr, &prop, &index);
- const int override_status = RNA_property_override_library_status(&ptr, prop, index);
+ const uint override_status = RNA_property_override_library_status(&ptr, prop, index);
return (ptr.data && ptr.owner_id && prop && (override_status & RNA_OVERRIDE_STATUS_OVERRIDDEN));
}
@@ -630,11 +630,11 @@ static int override_remove_button_exec(bContext *C, wmOperator *op)
UI_context_active_but_prop_get(C, &ptr, &prop, &index);
ID *id = ptr.owner_id;
- IDOverrideLibraryProperty *oprop = RNA_property_override_property_find(&ptr, prop);
+ IDOverrideLibraryProperty *oprop = RNA_property_override_property_find(&ptr, prop, &id);
BLI_assert(oprop != NULL);
BLI_assert(id != NULL && id->override_library != NULL);
- const bool is_template = (id->override_library->reference == NULL);
+ const bool is_template = ID_IS_OVERRIDE_LIBRARY_TEMPLATE(id);
/* We need source (i.e. linked data) to restore values of deleted overrides...
* If this is an override template, we obviously do not need to restore anything. */
@@ -1309,7 +1309,7 @@ static int editsource_text_edit(bContext *C,
printf("%s:%d\n", filepath, line);
for (text = bmain->texts.first; text; text = text->id.next) {
- if (text->name && BLI_path_cmp(text->name, filepath) == 0) {
+ if (text->filepath && BLI_path_cmp(text->filepath, filepath) == 0) {
break;
}
}
diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c
index 55657d7297a..2eeab4efbc7 100644
--- a/source/blender/editors/interface/interface_panel.c
+++ b/source/blender/editors/interface/interface_panel.c
@@ -245,20 +245,25 @@ static bool panels_need_realign(ScrArea *area, ARegion *region, Panel **r_panel_
/********* Functions for instanced panels. ***********/
-static Panel *UI_panel_add_instanced_ex(
- ScrArea *area, ARegion *region, ListBase *panels, PanelType *panel_type, int list_index)
+static Panel *UI_panel_add_instanced_ex(ScrArea *area,
+ ARegion *region,
+ ListBase *panels,
+ PanelType *panel_type,
+ int list_index,
+ PointerRNA *custom_data)
{
Panel *panel = MEM_callocN(sizeof(Panel), "instanced panel");
panel->type = panel_type;
BLI_strncpy(panel->panelname, panel_type->idname, sizeof(panel->panelname));
panel->runtime.list_index = list_index;
+ panel->runtime.custom_data_ptr = custom_data;
/* Add the panel's children too. Although they aren't instanced panels, we can still use this
* function to create them, as UI_panel_begin does other things we don't need to do. */
LISTBASE_FOREACH (LinkData *, child, &panel_type->children) {
PanelType *child_type = child->data;
- UI_panel_add_instanced_ex(area, region, &panel->children, child_type, list_index);
+ UI_panel_add_instanced_ex(area, region, &panel->children, child_type, list_index, custom_data);
}
/* Make sure the panel is added to the end of the display-order as well. This is needed for
@@ -283,8 +288,12 @@ static Panel *UI_panel_add_instanced_ex(
* Called in situations where panels need to be added dynamically rather than having only one panel
* corresponding to each PanelType.
*/
-Panel *UI_panel_add_instanced(
- ScrArea *area, ARegion *region, ListBase *panels, char *panel_idname, int list_index)
+Panel *UI_panel_add_instanced(ScrArea *area,
+ ARegion *region,
+ ListBase *panels,
+ char *panel_idname,
+ int list_index,
+ PointerRNA *custom_data)
{
ARegionType *region_type = region->type;
@@ -296,7 +305,7 @@ Panel *UI_panel_add_instanced(
return NULL;
}
- return UI_panel_add_instanced_ex(area, region, panels, panel_type, list_index);
+ return UI_panel_add_instanced_ex(area, region, panels, panel_type, list_index, custom_data);
}
/**
@@ -332,7 +341,8 @@ static void panel_free_block(ARegion *region, Panel *panel)
}
/**
- * Free a panel and it's children.
+ * Free a panel and it's children. Custom data is shared by the panel and its children
+ * and is freed by #UI_panels_free_instanced.
*
* \note The only panels that should need to be deleted at runtime are panels with the
* #PNL_INSTANCED flag set.
@@ -369,6 +379,13 @@ void UI_panels_free_instanced(bContext *C, ARegion *region)
if (C != NULL && panel->activedata != NULL) {
panel_activate_state(C, panel, PANEL_STATE_EXIT);
}
+
+ /* Free panel's custom data. */
+ if (panel->runtime.custom_data_ptr != NULL) {
+ MEM_freeN(panel->runtime.custom_data_ptr);
+ }
+
+ /* Free the panel and its sub-panels. */
panel_delete(region, &region->panels, panel);
}
}
@@ -387,9 +404,19 @@ bool UI_panel_list_matches_data(ARegion *region,
ListBase *data,
uiListPanelIDFromDataFunc panel_idname_func)
{
- int data_len = BLI_listbase_count(data);
+ /* Check for NULL data. */
+ int data_len = 0;
+ Link *data_link = NULL;
+ if (data == NULL) {
+ data_len = 0;
+ data_link = NULL;
+ }
+ else {
+ data_len = BLI_listbase_count(data);
+ data_link = data->first;
+ }
+
int i = 0;
- Link *data_link = data->first;
LISTBASE_FOREACH (Panel *, panel, &region->panels) {
if (panel->type != NULL && panel->type->flag & PNL_INSTANCED) {
/* The panels were reordered by drag and drop. */
@@ -493,7 +520,7 @@ static void reorder_instanced_panel_list(bContext *C, ARegion *region, Panel *dr
/**
* Recursive implementation for #UI_panel_set_expand_from_list_data.
*
- * \return Whether the closed flag for the panel or any subpanels changed.
+ * \return Whether the closed flag for the panel or any sub-panels changed.
*/
static bool panel_set_expand_from_list_data_recursive(Panel *panel, short flag, short *flag_index)
{
@@ -513,7 +540,7 @@ static bool panel_set_expand_from_list_data_recursive(Panel *panel, short flag,
}
/**
- * Set the expansion of the panel and its subpanels from the flag stored by the list data
+ * Set the expansion of the panel and its sub-panels from the flag stored by the list data
* corresponding to this panel. The flag has expansion stored in each bit in depth first
* order.
*/
@@ -583,6 +610,26 @@ static void set_panels_list_data_expand_flag(const bContext *C, ARegion *region)
/****************************** panels ******************************/
+/**
+ * Set flag state for a panel and its sub-panels.
+ *
+ * \return True if this function changed any of the flags, false if it didn't.
+ */
+static bool panel_set_flag_recursive(Panel *panel, int flag, bool value)
+{
+ short flag_original = panel->flag;
+
+ SET_FLAG_FROM_TEST(panel->flag, value, flag);
+
+ bool changed = (flag_original != panel->flag);
+
+ LISTBASE_FOREACH (Panel *, child, &panel->children) {
+ changed |= panel_set_flag_recursive(child, flag, value);
+ }
+
+ return changed;
+}
+
static void panels_collapse_all(const bContext *C,
ScrArea *area,
ARegion *region,
@@ -980,7 +1027,7 @@ void ui_draw_aligned_panel(uiStyle *style,
* can't be dragged. This may be changed in future. */
show_background);
const int panel_col = is_subpanel ? TH_PANEL_SUB_BACK : TH_PANEL_BACK;
- const bool draw_box_style = (panel->type && panel->type->flag & (PNL_DRAW_BOX));
+ const bool draw_box_style = (panel->type && panel->type->flag & PNL_DRAW_BOX);
/* Use the theme for box widgets for box-style panels. */
uiWidgetColors *box_wcol = NULL;
@@ -1160,7 +1207,7 @@ void ui_draw_aligned_panel(uiStyle *style,
/* Draw panel backdrop if it wasn't already been drawn by the single opaque round box earlier.
* Note: Sub-panels blend with panels, so they can't be opaque. */
if (show_background && !(draw_box_style && !is_subpanel)) {
- /* Draw the bottom subpanels . */
+ /* Draw the bottom sub-panels. */
if (draw_box_style) {
if (panel->next) {
immUniformThemeColor(panel_col);
@@ -1461,11 +1508,6 @@ static bool uiAlignPanelStep(ScrArea *area, ARegion *region, const float fac, co
ps->panel->ofsx = 0;
ps->panel->ofsy = -get_panel_size_y(ps->panel);
ps->panel->ofsx += ps->panel->runtime.region_ofsx;
- /* Extra margin if the panel is a box style panel. */
- if (ps->panel->type && ps->panel->type->flag & PNL_DRAW_BOX) {
- ps->panel->ofsx += UI_PANEL_BOX_STYLE_MARGIN;
- ps->panel->ofsy -= UI_PANEL_BOX_STYLE_MARGIN;
- }
for (a = 0; a < tot - 1; a++, ps++) {
psnext = ps + 1;
@@ -1475,16 +1517,12 @@ static bool uiAlignPanelStep(ScrArea *area, ARegion *region, const float fac, co
bool use_box_next = psnext->panel->type && psnext->panel->type->flag & PNL_DRAW_BOX;
psnext->panel->ofsx = ps->panel->ofsx;
psnext->panel->ofsy = get_panel_real_ofsy(ps->panel) - get_panel_size_y(psnext->panel);
+
/* Extra margin for box style panels. */
+ ps->panel->ofsx += (use_box) ? UI_PANEL_BOX_STYLE_MARGIN : 0.0f;
if (use_box || use_box_next) {
psnext->panel->ofsy -= UI_PANEL_BOX_STYLE_MARGIN;
}
- if (use_box && !use_box_next) {
- psnext->panel->ofsx -= UI_PANEL_BOX_STYLE_MARGIN;
- }
- else if (!use_box && use_box_next) {
- psnext->panel->ofsx += UI_PANEL_BOX_STYLE_MARGIN;
- }
}
else {
psnext->panel->ofsx = get_panel_real_ofsx(ps->panel);
@@ -1492,6 +1530,10 @@ static bool uiAlignPanelStep(ScrArea *area, ARegion *region, const float fac, co
get_panel_size_y(psnext->panel);
}
}
+ /* Extra margin for the last panel if it's a box-style panel. */
+ if (panelsort[tot - 1].panel->type && panelsort[tot - 1].panel->type->flag & PNL_DRAW_BOX) {
+ panelsort[tot - 1].panel->ofsx += UI_PANEL_BOX_STYLE_MARGIN;
+ }
/* we interpolate */
done = false;
@@ -2017,14 +2059,28 @@ static void ui_handle_panel_header(
if (button == 2) { /* close */
ED_region_tag_redraw(region);
}
- else { /* collapse */
+ else {
+ /* Collapse and expand panels. */
+
if (ctrl) {
- /* Only collapse all for parent panels. */
+ /* For parent panels, collapse all other panels or toggle children. */
if (block->panel->type != NULL && block->panel->type->parent == NULL) {
- panels_collapse_all(C, area, region, block->panel);
+ if (block->panel->flag & PNL_CLOSED || BLI_listbase_is_empty(&block->panel->children)) {
+ panels_collapse_all(C, area, region, block->panel);
- /* reset the view - we don't want to display a view without content */
- UI_view2d_offset(&region->v2d, 0.0f, 1.0f);
+ /* Reset the view - we don't want to display a view without content. */
+ UI_view2d_offset(&region->v2d, 0.0f, 1.0f);
+ }
+ else {
+ const int closed_flag = (align == BUT_HORIZONTAL) ? PNL_CLOSEDX : PNL_CLOSEDY;
+ /* If a panel has sub-panels and it's open, toggle the expansion
+ * of the sub-panels (based on the expansion of the first subpanel). */
+ Panel *first_child = block->panel->children.first;
+ BLI_assert(first_child != NULL);
+ panel_set_flag_recursive(
+ block->panel, closed_flag, (first_child->flag & PNL_CLOSED) == 0);
+ block->panel->flag |= closed_flag;
+ }
}
}
@@ -2847,6 +2903,56 @@ int ui_handler_panel_region(bContext *C,
return retval;
}
+static void ui_panel_custom_data_set_recursive(Panel *panel, PointerRNA *custom_data)
+{
+ panel->runtime.custom_data_ptr = custom_data;
+
+ LISTBASE_FOREACH (Panel *, child_panel, &panel->children) {
+ ui_panel_custom_data_set_recursive(child_panel, custom_data);
+ }
+}
+
+void UI_panel_custom_data_set(Panel *panel, PointerRNA *custom_data)
+{
+ BLI_assert(panel->type != NULL);
+
+ /* Free the old custom data, which should be shared among all of the panel's sub-panels. */
+ if (panel->runtime.custom_data_ptr != NULL) {
+ MEM_freeN(panel->runtime.custom_data_ptr);
+ }
+
+ ui_panel_custom_data_set_recursive(panel, custom_data);
+}
+
+PointerRNA *UI_region_panel_custom_data_under_cursor(const bContext *C, const wmEvent *event)
+{
+ ARegion *region = CTX_wm_region(C);
+
+ Panel *panel = NULL;
+ LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
+ panel = block->panel;
+ if (panel == NULL) {
+ continue;
+ }
+
+ int mx = event->x;
+ int my = event->y;
+ ui_window_to_block(region, block, &mx, &my);
+ int mouse_state = ui_panel_mouse_state_get(block, panel, mx, my);
+ if (ELEM(mouse_state, PANEL_MOUSE_INSIDE_CONTENT, PANEL_MOUSE_INSIDE_HEADER)) {
+ break;
+ }
+ }
+
+ if (panel == NULL) {
+ return NULL;
+ }
+
+ PointerRNA *customdata = panel->runtime.custom_data_ptr;
+
+ return customdata;
+}
+
/**************** window level modal panel interaction **************/
/* note, this is modal handler and should not swallow events for animation */
@@ -2899,24 +3005,6 @@ static void ui_handler_remove_panel(bContext *C, void *userdata)
panel_activate_state(C, panel, PANEL_STATE_EXIT);
}
-/**
- * Set selection state for a panel and its subpanels. The subpanels need to know they are selected
- * too so they can be drawn above their parent when it is dragged.
- */
-static void set_panel_selection(Panel *panel, bool value)
-{
- if (value) {
- panel->flag |= PNL_SELECT;
- }
- else {
- panel->flag &= ~PNL_SELECT;
- }
-
- LISTBASE_FOREACH (Panel *, child, &panel->children) {
- set_panel_selection(child, value);
- }
-}
-
static void panel_activate_state(const bContext *C, Panel *panel, uiHandlePanelState state)
{
uiHandlePanelData *data = panel->activedata;
@@ -2929,6 +3017,8 @@ static void panel_activate_state(const bContext *C, Panel *panel, uiHandlePanelS
bool was_drag_drop = (data && data->state == PANEL_STATE_DRAG);
+ /* Set selection state for the panel and its sub-panels, which need to know they are selected
+ * too so they can be drawn above their parent when it's dragged. */
if (state == PANEL_STATE_EXIT || state == PANEL_STATE_ANIMATION) {
if (data && data->state != PANEL_STATE_ANIMATION) {
/* XXX:
@@ -2941,10 +3031,10 @@ static void panel_activate_state(const bContext *C, Panel *panel, uiHandlePanelS
check_panel_overlap(region, NULL); /* clears */
}
- set_panel_selection(panel, false);
+ panel_set_flag_recursive(panel, PNL_SELECT, false);
}
else {
- set_panel_selection(panel, true);
+ panel_set_flag_recursive(panel, PNL_SELECT, true);
}
if (data && data->animtimer) {
diff --git a/source/blender/editors/interface/interface_region_menu_popup.c b/source/blender/editors/interface/interface_region_menu_popup.c
index 3e34b7f3f8a..65f5798bdce 100644
--- a/source/blender/editors/interface/interface_region_menu_popup.c
+++ b/source/blender/editors/interface/interface_region_menu_popup.c
@@ -184,7 +184,12 @@ static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, voi
pup->block->handle = NULL;
}
- if (pup->but) {
+ /* Find block minimum width. */
+ if (uiLayoutGetUnitsX(pup->layout) != 0.0f) {
+ /* Use the minimum width from the layout if it's set. */
+ minwidth = uiLayoutGetUnitsX(pup->layout) * UI_UNIT_X;
+ }
+ else if (pup->but) {
/* minimum width to enforece */
if (pup->but->drawstr[0]) {
minwidth = BLI_rctf_size_x(&pup->but->rect);
@@ -193,7 +198,13 @@ static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, voi
/* For buttons with no text, use the minimum (typically icon only). */
minwidth = UI_MENU_WIDTH_MIN;
}
+ }
+ else {
+ minwidth = UI_MENU_WIDTH_MIN;
+ }
+ /* Find block direction. */
+ if (pup->but) {
if (pup->block->direction != 0) {
/* allow overriding the direction from menu_func */
direction = pup->block->direction;
@@ -203,7 +214,6 @@ static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, voi
}
}
else {
- minwidth = UI_MENU_WIDTH_MIN;
direction = UI_DIR_DOWN;
}
diff --git a/source/blender/editors/interface/interface_region_search.c b/source/blender/editors/interface/interface_region_search.c
index 68be08ef4f8..c88dd1954c9 100644
--- a/source/blender/editors/interface/interface_region_search.c
+++ b/source/blender/editors/interface/interface_region_search.c
@@ -77,6 +77,9 @@ struct uiSearchItems {
int *icons;
int *states;
+ /** Is there any item with an icon? */
+ bool has_icon;
+
AutoComplete *autocpl;
void *active;
};
@@ -122,6 +125,10 @@ bool UI_search_item_add(uiSearchItems *items, const char *name, void *poin, int
return true;
}
+ if (iconid) {
+ items->has_icon = true;
+ }
+
/* hijack for finding active item */
if (items->active) {
if (poin == items->active) {
@@ -562,6 +569,10 @@ static void ui_searchbox_region_draw_cb(const bContext *C, ARegion *region)
/* widget itself */
if ((search_sep_len == 0) ||
!(name_sep_test = strstr(data->items.names[a], data->sep_string))) {
+ if (!icon && data->items.has_icon) {
+ /* If there is any icon item, make sure all items line up. */
+ icon = ICON_BLANK1;
+ }
/* Simple menu item. */
ui_draw_menu_item(&data->fstyle, &rect, name, icon, state, use_sep_char, NULL);
diff --git a/source/blender/editors/interface/interface_region_tooltip.c b/source/blender/editors/interface/interface_region_tooltip.c
index 6f49fed675f..9fed98ffda2 100644
--- a/source/blender/editors/interface/interface_region_tooltip.c
+++ b/source/blender/editors/interface/interface_region_tooltip.c
@@ -923,12 +923,7 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but)
.style = UI_TIP_STYLE_NORMAL,
.color_id = UI_TIP_LC_NORMAL,
});
- if (ID_IS_LINKED_DATABLOCK(id)) {
- field->text = BLI_sprintfN(TIP_("Library: %s"), id->lib->name);
- }
- else { /* if (ID_IS_LINKED_DATAPATH(id)) */
- field->text = BLI_sprintfN(TIP_("Using file path as asset"));
- }
+ field->text = BLI_sprintfN(TIP_("Library: %s"), id->lib->filepath);
}
}
}
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index 1ce1e2950d5..baea948e122 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -361,11 +361,16 @@ static bool id_search_add(const bContext *C,
* followed by ID_NAME-2 characters from id->name
*/
char name_ui[MAX_ID_FULL_NAME_UI];
- BKE_id_full_name_ui_prefix_get(name_ui, id, UI_SEP_CHAR);
-
int iconid = ui_id_icon_get(C, id, template_ui->preview);
bool has_sep_char = (id->lib != NULL);
+ /* When using previews, the library hint (linked, overridden, missing) is added with a
+ * character prefix, otherwise we can use a icon. */
+ BKE_id_full_name_ui_prefix_get(name_ui, id, template_ui->preview, UI_SEP_CHAR);
+ if (!template_ui->preview) {
+ iconid = UI_library_icon_get(id);
+ }
+
if (!UI_search_item_add(
items, name_ui, id, iconid, has_sep_char ? UI_BUT_HAS_SEP_CHAR : 0)) {
return false;
@@ -511,6 +516,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
PointerRNA idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop);
ID *id = idptr.data;
int event = POINTER_AS_INT(arg_event);
+ const char *undo_push_label = NULL;
switch (event) {
case UI_ID_BROWSE:
@@ -531,6 +537,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
id_us_clear_real(id);
id_fake_user_clear(id);
id->us = 0;
+ undo_push_label = "Delete Data-Block";
}
break;
@@ -542,6 +549,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
else {
id_us_min(id);
}
+ undo_push_label = "Fake User";
}
else {
return;
@@ -572,15 +580,17 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
}
RNA_property_pointer_set(&template_ui->ptr, template_ui->prop, idptr, NULL);
RNA_property_update(C, &template_ui->ptr, template_ui->prop);
+ undo_push_label = "Make Local";
}
break;
case UI_ID_OVERRIDE:
- if (id && id->override_library) {
+ if (id && ID_IS_OVERRIDE_LIBRARY(id)) {
BKE_lib_override_library_free(&id->override_library, true);
/* reassign to get get proper updates/notifiers */
idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop);
RNA_property_pointer_set(&template_ui->ptr, template_ui->prop, idptr, NULL);
RNA_property_update(C, &template_ui->ptr, template_ui->prop);
+ undo_push_label = "Override Data-Block";
}
break;
case UI_ID_ALONE:
@@ -601,6 +611,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
id_single_user(C, id, &template_ui->ptr, template_ui->prop);
DEG_relations_tag_update(bmain);
}
+ undo_push_label = "Make Single User";
}
break;
#if 0
@@ -608,6 +619,10 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
break;
#endif
}
+
+ if (undo_push_label != NULL) {
+ ED_undo_push(C, undo_push_label);
+ }
}
static const char *template_id_browse_tip(const StructRNA *type)
@@ -1823,6 +1838,20 @@ void uiTemplatePathBuilder(uiLayout *layout,
* Template for building the panel layout for the active object's modifiers.
* \{ */
+/**
+ * Get the active object or the property region's pinned object.
+ */
+static Object *get_context_object(const bContext *C)
+{
+ SpaceProperties *sbuts = CTX_wm_space_properties(C);
+ if (sbuts != NULL && (sbuts->pinid != NULL) && GS(sbuts->pinid->name) == ID_OB) {
+ return (Object *)sbuts->pinid;
+ }
+ else {
+ return CTX_data_active_object(C);
+ }
+}
+
static void modifier_panel_id(void *md_link, char *r_name)
{
ModifierData *md = (ModifierData *)md_link;
@@ -1834,14 +1863,7 @@ void uiTemplateModifiers(uiLayout *UNUSED(layout), bContext *C)
ScrArea *sa = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
- Object *ob;
- SpaceProperties *sbuts = CTX_wm_space_properties(C);
- if (sbuts != NULL && (sbuts->pinid != NULL) && GS(sbuts->pinid->name) == ID_OB) {
- ob = (Object *)sbuts->pinid;
- }
- else {
- ob = CTX_data_active_object(C);
- }
+ Object *ob = get_context_object(C);
ListBase *modifiers = &ob->modifiers;
bool panels_match = UI_panel_list_matches_data(region, modifiers, modifier_panel_id);
@@ -1851,14 +1873,22 @@ void uiTemplateModifiers(uiLayout *UNUSED(layout), bContext *C)
ModifierData *md = modifiers->first;
for (int i = 0; md; i++, md = md->next) {
const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type);
- if (mti->panelRegister) {
- char panel_idname[MAX_NAME];
- modifier_panel_id(md, panel_idname);
+ if (mti->panelRegister == NULL) {
+ continue;
+ }
- Panel *new_panel = UI_panel_add_instanced(sa, region, &region->panels, panel_idname, i);
- if (new_panel != NULL) {
- UI_panel_set_expand_from_list_data(C, new_panel);
- }
+ char panel_idname[MAX_NAME];
+ modifier_panel_id(md, panel_idname);
+
+ /* Create custom data RNA pointer. */
+ PointerRNA *md_ptr = MEM_mallocN(sizeof(PointerRNA), "panel customdata");
+ RNA_pointer_create(&ob->id, &RNA_Modifier, md, md_ptr);
+
+ Panel *new_panel = UI_panel_add_instanced(
+ sa, region, &region->panels, panel_idname, i, md_ptr);
+
+ if (new_panel != NULL) {
+ UI_panel_set_expand_from_list_data(C, new_panel);
}
}
}
@@ -1868,274 +1898,321 @@ void uiTemplateModifiers(uiLayout *UNUSED(layout), bContext *C)
if ((panel->type != NULL) && (panel->type->flag & PNL_INSTANCED))
UI_panel_set_expand_from_list_data(C, panel);
}
+
+ /* Assuming there's only one group of instanced panels, update the custom data pointers. */
+ Panel *panel = region->panels.first;
+ LISTBASE_FOREACH (ModifierData *, md, modifiers) {
+ const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type);
+ if (mti->panelRegister == NULL) {
+ continue;
+ }
+
+ /* Move to the next instanced panel corresponding to the next modifier. */
+ while ((panel->type == NULL) || !(panel->type->flag & PNL_INSTANCED)) {
+ panel = panel->next;
+ BLI_assert(panel != NULL); /* There shouldn't be fewer panels than modifiers with UIs. */
+ }
+
+ PointerRNA *md_ptr = MEM_mallocN(sizeof(PointerRNA), "panel customdata");
+ RNA_pointer_create(&ob->id, &RNA_Modifier, md, md_ptr);
+ UI_panel_custom_data_set(panel, md_ptr);
+
+ panel = panel->next;
+ }
}
}
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Grease Pencil Modifier Template
+/** \name Constraints Template
+ *
+ * Template for building the panel layout for the active object or bone's constraints.
* \{ */
-#define ERROR_LIBDATA_MESSAGE TIP_("Can't edit external library data")
+/** For building the panel UI for constraints. */
+#define CONSTRAINT_TYPE_PANEL_PREFIX "OBJECT_PT_"
+#define CONSTRAINT_BONE_TYPE_PANEL_PREFIX "BONE_PT_"
-static uiLayout *gpencil_draw_modifier(uiLayout *layout, Object *ob, GpencilModifierData *md)
+/**
+ * Check if the panel's ID starts with 'BONE', meaning it is a bone constraint.
+ */
+static bool constraint_panel_is_bone(Panel *panel)
{
- const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(md->type);
- PointerRNA ptr;
- uiBlock *block;
- uiLayout *box, *column, *row, *sub;
- uiLayout *result = NULL;
-
- /* create RNA pointer */
- RNA_pointer_create(&ob->id, &RNA_GpencilModifier, md, &ptr);
-
- column = uiLayoutColumn(layout, true);
- uiLayoutSetContextPointer(column, "modifier", &ptr);
-
- /* rounded header ------------------------------------------------------------------- */
- box = uiLayoutBox(column);
-
- row = uiLayoutRow(box, false);
- block = uiLayoutGetBlock(row);
-
- UI_block_emboss_set(block, UI_EMBOSS_NONE);
- /* Open/Close ................................. */
- uiItemR(row, &ptr, "show_expanded", 0, "", ICON_NONE);
-
- /* modifier-type icon */
- uiItemL(row, "", RNA_struct_ui_icon(ptr.type));
- UI_block_emboss_set(block, UI_EMBOSS);
+ return (panel->panelname[0] == 'B') && (panel->panelname[1] == 'O') &&
+ (panel->panelname[2] == 'N') && (panel->panelname[3] == 'E');
+}
- /* modifier name */
- if (mti->isDisabled && mti->isDisabled(md, 0)) {
- uiLayoutSetRedAlert(row, true);
+/**
+ * Get the constraints for the active pose bone or the active / pinned object.
+ */
+static ListBase *get_constraints(const bContext *C, bool use_bone_constraints)
+{
+ ListBase *constraints = {NULL};
+ if (use_bone_constraints) {
+ bPoseChannel *pose_bone = CTX_data_pointer_get(C, "pose_bone").data;
+ if (pose_bone != NULL) {
+ constraints = &pose_bone->constraints;
+ }
}
- uiItemR(row, &ptr, "name", 0, "", ICON_NONE);
- uiLayoutSetRedAlert(row, false);
-
- /* mode enabling buttons */
- UI_block_align_begin(block);
- uiItemR(row, &ptr, "show_render", 0, "", ICON_NONE);
- uiItemR(row, &ptr, "show_viewport", 0, "", ICON_NONE);
-
- if (mti->flags & eGpencilModifierTypeFlag_SupportsEditmode) {
- sub = uiLayoutRow(row, true);
- uiLayoutSetActive(sub, false);
- uiItemR(sub, &ptr, "show_in_editmode", 0, "", ICON_NONE);
+ else {
+ Object *ob = get_context_object(C);
+ if (ob != NULL) {
+ constraints = &ob->constraints;
+ }
}
+ return constraints;
+}
- UI_block_align_end(block);
-
- /* Up/Down + Delete ........................... */
- UI_block_align_begin(block);
- uiItemO(row, "", ICON_TRIA_UP, "OBJECT_OT_gpencil_modifier_move_up");
- uiItemO(row, "", ICON_TRIA_DOWN, "OBJECT_OT_gpencil_modifier_move_down");
- UI_block_align_end(block);
-
- UI_block_emboss_set(block, UI_EMBOSS_NONE);
- uiItemO(row, "", ICON_X, "OBJECT_OT_gpencil_modifier_remove");
- UI_block_emboss_set(block, UI_EMBOSS);
+/**
+ * Move a constraint to the index it's moved to after a drag and drop.
+ */
+static void constraint_reorder(bContext *C, Panel *panel, int new_index)
+{
+ bool constraint_from_bone = constraint_panel_is_bone(panel);
+ ListBase *lb = get_constraints(C, constraint_from_bone);
- /* modifier settings (under the header) --------------------------------------------------- */
- if (md->mode & eGpencilModifierMode_Expanded) {
- /* apply/convert/copy */
- box = uiLayoutBox(column);
- row = uiLayoutRow(box, false);
+ bConstraint *con = BLI_findlink(lb, panel->runtime.list_index);
+ PointerRNA props_ptr;
+ wmOperatorType *ot = WM_operatortype_find("CONSTRAINT_OT_move_to_index", false);
+ WM_operator_properties_create_ptr(&props_ptr, ot);
+ RNA_string_set(&props_ptr, "constraint", con->name);
+ RNA_int_set(&props_ptr, "index", new_index);
+ /* Set owner to #EDIT_CONSTRAINT_OWNER_OBJECT or #EDIT_CONSTRAINT_OWNER_BONE. */
+ RNA_enum_set(&props_ptr, "owner", constraint_from_bone ? 1 : 0);
+ WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr);
+ WM_operator_properties_free(&props_ptr);
+}
- /* only here obdata, the rest of modifiers is ob level */
- UI_block_lock_set(block, BKE_object_obdata_is_libdata(ob), ERROR_LIBDATA_MESSAGE);
+/**
+ * Get the expand flag from the active constraint to use for the panel.
+ */
+static short get_constraint_expand_flag(const bContext *C, Panel *panel)
+{
+ bool constraint_from_bone = constraint_panel_is_bone(panel);
+ ListBase *lb = get_constraints(C, constraint_from_bone);
- uiLayoutSetOperatorContext(row, WM_OP_INVOKE_DEFAULT);
+ bConstraint *con = BLI_findlink(lb, panel->runtime.list_index);
+ return con->ui_expand_flag;
+}
- sub = uiLayoutRow(row, false);
- if (mti->flags & eGpencilModifierTypeFlag_NoApply) {
- uiLayoutSetEnabled(sub, false);
- }
- uiItemEnumO(sub,
- "OBJECT_OT_gpencil_modifier_apply",
- CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Apply"),
- 0,
- "apply_as",
- MODIFIER_APPLY_DATA);
+/**
+ * Save the expand flag for the panel and sub-panels to the constraint.
+ */
+static void set_constraint_expand_flag(const bContext *C, Panel *panel, short expand_flag)
+{
+ bool constraint_from_bone = constraint_panel_is_bone(panel);
+ ListBase *lb = get_constraints(C, constraint_from_bone);
- UI_block_lock_clear(block);
- UI_block_lock_set(block, ob && ID_IS_LINKED(ob), ERROR_LIBDATA_MESSAGE);
+ bConstraint *con = BLI_findlink(lb, panel->runtime.list_index);
+ con->ui_expand_flag = expand_flag;
+}
- uiItemO(row,
- CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Copy"),
- ICON_NONE,
- "OBJECT_OT_gpencil_modifier_copy");
+/**
+ * Function with void * argument for #uiListPanelIDFromDataFunc.
+ *
+ * \note: Constraint panel types are assumed to be named with the struct name field
+ * concatenated to the defined prefix.
+ */
+static void object_constraint_panel_id(void *md_link, char *r_name)
+{
+ bConstraint *con = (bConstraint *)md_link;
+ const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_from_type(con->type);
- /* result is the layout block inside the box,
- * that we return so that modifier settings can be drawn */
- result = uiLayoutColumn(box, false);
- block = uiLayoutAbsoluteBlock(box);
- }
+ strcpy(r_name, CONSTRAINT_TYPE_PANEL_PREFIX);
+ strcat(r_name, cti->structName);
+}
- /* error messages */
- if (md->error) {
- box = uiLayoutBox(column);
- row = uiLayoutRow(box, false);
- uiItemL(row, md->error, ICON_ERROR);
- }
+static void bone_constraint_panel_id(void *md_link, char *r_name)
+{
+ bConstraint *con = (bConstraint *)md_link;
+ const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_from_type(con->type);
- return result;
+ strcpy(r_name, CONSTRAINT_BONE_TYPE_PANEL_PREFIX);
+ strcat(r_name, cti->structName);
}
-uiLayout *uiTemplateGpencilModifier(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+/**
+ * Check if the constraint panels don't match the data and rebuild the panels if so.
+ */
+void uiTemplateConstraints(uiLayout *UNUSED(layout), bContext *C, bool use_bone_constraints)
{
- Object *ob;
- GpencilModifierData *md, *vmd;
- int i;
-
- /* verify we have valid data */
- if (!RNA_struct_is_a(ptr->type, &RNA_GpencilModifier)) {
- RNA_warning("Expected modifier on object");
- return NULL;
- }
+ ScrArea *sa = CTX_wm_area(C);
+ ARegion *region = CTX_wm_region(C);
- ob = (Object *)ptr->owner_id;
- md = ptr->data;
+ ListBase *constraints = get_constraints(C, use_bone_constraints);
- if (!ob || !(GS(ob->id.name) == ID_OB)) {
- RNA_warning("Expected modifier on object");
- return NULL;
- }
+ /* Switch between the bone panel ID function and the object panel ID function. */
+ uiListPanelIDFromDataFunc panel_id_func = use_bone_constraints ? bone_constraint_panel_id :
+ object_constraint_panel_id;
- UI_block_lock_set(uiLayoutGetBlock(layout), (ob && ID_IS_LINKED(ob)), ERROR_LIBDATA_MESSAGE);
+ bool panels_match = UI_panel_list_matches_data(region, constraints, panel_id_func);
- /* find modifier and draw it */
- vmd = ob->greasepencil_modifiers.first;
- for (i = 0; vmd; i++, vmd = vmd->next) {
- if (md == vmd) {
- return gpencil_draw_modifier(layout, ob, md);
+ if (!panels_match) {
+ UI_panels_free_instanced(C, region);
+ bConstraint *con = (constraints == NULL) ? NULL : constraints->first;
+ for (int i = 0; con; i++, con = con->next) {
+ char panel_idname[MAX_NAME];
+ panel_id_func(con, panel_idname);
+
+ Panel *new_panel = UI_panel_add_instanced(
+ sa, region, &region->panels, panel_idname, i, NULL);
+ if (new_panel) {
+ /* Set the list panel functionality function pointers since we don't do it with
+ * python. */
+ new_panel->type->set_list_data_expand_flag = set_constraint_expand_flag;
+ new_panel->type->get_list_data_expand_flag = get_constraint_expand_flag;
+ new_panel->type->reorder = constraint_reorder;
+
+ UI_panel_set_expand_from_list_data(C, new_panel);
+ }
+ }
+ }
+ else {
+ /* The expansion might have been changed elsewhere, so we still need to set it. */
+ LISTBASE_FOREACH (Panel *, panel, &region->panels) {
+ if ((panel->type != NULL) && (panel->type->flag & PNL_INSTANCED))
+ UI_panel_set_expand_from_list_data(C, panel);
}
}
-
- return NULL;
}
+#undef CONSTRAINT_TYPE_PANEL_PREFIX
+#undef CONSTRAINT_BONE_TYPE_PANEL_PREFIX
+
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Grease Pencil Shader FX Template
+/** \name Grease Pencil Modifiers Template
* \{ */
-static uiLayout *gpencil_draw_shaderfx(uiLayout *layout, Object *ob, ShaderFxData *md)
+/**
+ * Function with void * argument for #uiListPanelIDFromDataFunc.
+ */
+static void gpencil_modifier_panel_id(void *md_link, char *r_name)
{
- const ShaderFxTypeInfo *mti = BKE_shaderfx_get_info(md->type);
- PointerRNA ptr;
- uiBlock *block;
- uiLayout *box, *column, *row, *sub;
- uiLayout *result = NULL;
+ ModifierData *md = (ModifierData *)md_link;
+ BKE_gpencil_modifierType_panel_id(md->type, r_name);
+}
- /* create RNA pointer */
- RNA_pointer_create(&ob->id, &RNA_ShaderFx, md, &ptr);
+void uiTemplateGpencilModifiers(uiLayout *UNUSED(layout), bContext *C)
+{
+ ScrArea *sa = CTX_wm_area(C);
+ ARegion *region = CTX_wm_region(C);
+ Object *ob = get_context_object(C);
+ ListBase *modifiers = &ob->greasepencil_modifiers;
- column = uiLayoutColumn(layout, true);
- uiLayoutSetContextPointer(column, "shaderfx", &ptr);
+ bool panels_match = UI_panel_list_matches_data(region, modifiers, gpencil_modifier_panel_id);
- /* rounded header ------------------------------------------------------------------- */
- box = uiLayoutBox(column);
+ if (!panels_match) {
+ UI_panels_free_instanced(C, region);
+ GpencilModifierData *md = modifiers->first;
+ for (int i = 0; md; i++, md = md->next) {
+ const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(md->type);
+ if (mti->panelRegister == NULL) {
+ continue;
+ }
- row = uiLayoutRow(box, false);
- block = uiLayoutGetBlock(row);
+ char panel_idname[MAX_NAME];
+ gpencil_modifier_panel_id(md, panel_idname);
- UI_block_emboss_set(block, UI_EMBOSS_NONE);
- /* Open/Close ................................. */
- uiItemR(row, &ptr, "show_expanded", 0, "", ICON_NONE);
+ /* Create custom data RNA pointer. */
+ PointerRNA *md_ptr = MEM_mallocN(sizeof(PointerRNA), "panel customdata");
+ RNA_pointer_create(&ob->id, &RNA_GpencilModifier, md, md_ptr);
- /* shader-type icon */
- uiItemL(row, "", RNA_struct_ui_icon(ptr.type));
- UI_block_emboss_set(block, UI_EMBOSS);
+ Panel *new_panel = UI_panel_add_instanced(
+ sa, region, &region->panels, panel_idname, i, md_ptr);
- /* effect name */
- if (mti->isDisabled && mti->isDisabled(md, 0)) {
- uiLayoutSetRedAlert(row, true);
+ if (new_panel != NULL) {
+ UI_panel_set_expand_from_list_data(C, new_panel);
+ }
+ }
}
- uiItemR(row, &ptr, "name", 0, "", ICON_NONE);
- uiLayoutSetRedAlert(row, false);
-
- /* mode enabling buttons */
- UI_block_align_begin(block);
- uiItemR(row, &ptr, "show_render", 0, "", ICON_NONE);
- uiItemR(row, &ptr, "show_viewport", 0, "", ICON_NONE);
+ else {
+ /* The expansion might have been changed elsewhere, so we still need to set it. */
+ LISTBASE_FOREACH (Panel *, panel, &region->panels) {
+ if ((panel->type != NULL) && (panel->type->flag & PNL_INSTANCED))
+ UI_panel_set_expand_from_list_data(C, panel);
+ }
- if (mti->flags & eShaderFxTypeFlag_SupportsEditmode) {
- sub = uiLayoutRow(row, true);
- uiLayoutSetActive(sub, false);
- uiItemR(sub, &ptr, "show_in_editmode", 0, "", ICON_NONE);
- }
+ /* Assuming there's only one group of instanced panels, update the custom data pointers. */
+ Panel *panel = region->panels.first;
+ LISTBASE_FOREACH (ModifierData *, md, modifiers) {
+ const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(md->type);
+ if (mti->panelRegister == NULL) {
+ continue;
+ }
- UI_block_align_end(block);
+ /* Move to the next instanced panel corresponding to the next modifier. */
+ while ((panel->type == NULL) || !(panel->type->flag & PNL_INSTANCED)) {
+ panel = panel->next;
+ BLI_assert(panel != NULL); /* There shouldn't be fewer panels than modifiers with UIs. */
+ }
- /* Up/Down + Delete ........................... */
- UI_block_align_begin(block);
- uiItemO(row, "", ICON_TRIA_UP, "OBJECT_OT_shaderfx_move_up");
- uiItemO(row, "", ICON_TRIA_DOWN, "OBJECT_OT_shaderfx_move_down");
- UI_block_align_end(block);
+ PointerRNA *md_ptr = MEM_mallocN(sizeof(PointerRNA), "panel customdata");
+ RNA_pointer_create(&ob->id, &RNA_GpencilModifier, md, md_ptr);
+ UI_panel_custom_data_set(panel, md_ptr);
- UI_block_emboss_set(block, UI_EMBOSS_NONE);
- uiItemO(row, "", ICON_X, "OBJECT_OT_shaderfx_remove");
- UI_block_emboss_set(block, UI_EMBOSS);
+ panel = panel->next;
+ }
+ }
+}
- /* effect settings (under the header) --------------------------------------------------- */
- if (md->mode & eShaderFxMode_Expanded) {
- /* apply/convert/copy */
- box = uiLayoutBox(column);
- row = uiLayoutRow(box, false);
+/** \} */
- /* only here obdata, the rest of effect is ob level */
- UI_block_lock_set(block, BKE_object_obdata_is_libdata(ob), ERROR_LIBDATA_MESSAGE);
+/** \} */
- /* result is the layout block inside the box,
- * that we return so that effect settings can be drawn */
- result = uiLayoutColumn(box, false);
- block = uiLayoutAbsoluteBlock(box);
- }
+#define ERROR_LIBDATA_MESSAGE TIP_("Can't edit external library data")
- /* error messages */
- if (md->error) {
- box = uiLayoutBox(column);
- row = uiLayoutRow(box, false);
- uiItemL(row, md->error, ICON_ERROR);
- }
+/* -------------------------------------------------------------------- */
+/** \name ShaderFx Template
+ *
+ * Template for building the panel layout for the active object's grease pencil shader
+ * effects.
+ * \{ */
- return result;
+/**
+ * Function with void * argument for #uiListPanelIDFromDataFunc.
+ */
+static void shaderfx_panel_id(void *fx_v, char *r_idname)
+{
+ ShaderFxData *fx = (ShaderFxData *)fx_v;
+ BKE_shaderfxType_panel_id(fx->type, r_idname);
}
-uiLayout *uiTemplateShaderFx(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+/**
+ * Check if the shader effect panels don't match the data and rebuild the panels if so.
+ */
+void uiTemplateShaderFx(uiLayout *UNUSED(layout), bContext *C)
{
- Object *ob;
- ShaderFxData *fx, *vfx;
- int i;
-
- /* verify we have valid data */
- if (!RNA_struct_is_a(ptr->type, &RNA_ShaderFx)) {
- RNA_warning("Expected shader fx on object");
- return NULL;
- }
+ ScrArea *sa = CTX_wm_area(C);
+ ARegion *region = CTX_wm_region(C);
+ Object *ob = get_context_object(C);
+ ListBase *shaderfx = &ob->shader_fx;
- ob = (Object *)ptr->owner_id;
- fx = ptr->data;
+ bool panels_match = UI_panel_list_matches_data(region, shaderfx, shaderfx_panel_id);
- if (!ob || !(GS(ob->id.name) == ID_OB)) {
- RNA_warning("Expected shader fx on object");
- return NULL;
+ if (!panels_match) {
+ UI_panels_free_instanced(C, region);
+ ShaderFxData *fx = shaderfx->first;
+ for (int i = 0; fx; i++, fx = fx->next) {
+ char panel_idname[MAX_NAME];
+ shaderfx_panel_id(fx, panel_idname);
+
+ Panel *new_panel = UI_panel_add_instanced(
+ sa, region, &region->panels, panel_idname, i, NULL);
+ if (new_panel != NULL) {
+ UI_panel_set_expand_from_list_data(C, new_panel);
+ }
+ }
}
-
- UI_block_lock_set(uiLayoutGetBlock(layout), (ob && ID_IS_LINKED(ob)), ERROR_LIBDATA_MESSAGE);
-
- /* find modifier and draw it */
- vfx = ob->shader_fx.first;
- for (i = 0; vfx; i++, vfx = vfx->next) {
- if (fx == vfx) {
- return gpencil_draw_shaderfx(layout, ob, fx);
+ else {
+ /* The expansion might have been changed elsewhere, so we still need to set it. */
+ LISTBASE_FOREACH (Panel *, panel, &region->panels) {
+ if ((panel->type != NULL) && (panel->type->flag & PNL_INSTANCED))
+ UI_panel_set_expand_from_list_data(C, panel);
}
}
-
- return NULL;
}
/** \} */
@@ -2441,38 +2518,25 @@ void uiTemplateOperatorRedoProperties(uiLayout *layout, const bContext *C)
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Constraint Template
+/** \name Constraint Header Template
* \{ */
+#define ERROR_LIBDATA_MESSAGE TIP_("Can't edit external library data")
+
static void constraint_active_func(bContext *UNUSED(C), void *ob_v, void *con_v)
{
ED_object_constraint_active_set(ob_v, con_v);
}
-/* draw panel showing settings for a constraint */
-static uiLayout *draw_constraint(uiLayout *layout, Object *ob, bConstraint *con)
+static void draw_constraint_header(uiLayout *layout, Object *ob, bConstraint *con)
{
bPoseChannel *pchan = BKE_pose_channel_active(ob);
- const bConstraintTypeInfo *cti;
uiBlock *block;
- uiLayout *result = NULL, *col, *box, *row;
+ uiLayout *sub;
PointerRNA ptr;
- char typestr[32];
short proxy_protected, xco = 0, yco = 0;
// int rb_col; // UNUSED
- /* get constraint typeinfo */
- cti = BKE_constraint_typeinfo_get(con);
- if (cti == NULL) {
- /* exception for 'Null' constraint - it doesn't have constraint typeinfo! */
- BLI_strncpy(typestr,
- (con->type == CONSTRAINT_TYPE_NULL) ? IFACE_("Null") : IFACE_("Unknown"),
- sizeof(typestr));
- }
- else {
- BLI_strncpy(typestr, IFACE_(cti->name), sizeof(typestr));
- }
-
/* determine whether constraint is proxy protected or not */
if (BKE_constraints_proxylocked_owner(ob, pchan)) {
proxy_protected = (con->flag & CONSTRAINT_PROXY_LOCAL) == 0;
@@ -2487,36 +2551,23 @@ static uiLayout *draw_constraint(uiLayout *layout, Object *ob, bConstraint *con)
RNA_pointer_create(&ob->id, &RNA_Constraint, con, &ptr);
- col = uiLayoutColumn(layout, true);
- uiLayoutSetContextPointer(col, "constraint", &ptr);
-
- box = uiLayoutBox(col);
- row = uiLayoutRow(box, false);
- block = uiLayoutGetBlock(box);
+ uiLayoutSetContextPointer(layout, "constraint", &ptr);
- /* Draw constraint header */
+ /* Constraint type icon. */
+ sub = uiLayoutRow(layout, false);
+ uiLayoutSetEmboss(sub, false);
+ uiLayoutSetRedAlert(sub, (con->flag & CONSTRAINT_DISABLE));
+ uiItemL(sub, "", RNA_struct_ui_icon(ptr.type));
- /* open/close */
- UI_block_emboss_set(block, UI_EMBOSS_NONE);
- uiItemR(row, &ptr, "show_expanded", 0, "", ICON_NONE);
-
- /* constraint-type icon */
- uiItemL(row, "", RNA_struct_ui_icon(ptr.type));
UI_block_emboss_set(block, UI_EMBOSS);
- if (con->flag & CONSTRAINT_DISABLE) {
- uiLayoutSetRedAlert(row, true);
- }
-
if (proxy_protected == 0) {
- uiItemR(row, &ptr, "name", 0, "", ICON_NONE);
+ uiItemR(layout, &ptr, "name", 0, "", ICON_NONE);
}
else {
- uiItemL(row, con->name, ICON_NONE);
+ uiItemL(layout, con->name, ICON_NONE);
}
- uiLayoutSetRedAlert(row, false);
-
/* proxy-protected constraints cannot be edited, so hide up/down + close buttons */
if (proxy_protected) {
UI_block_emboss_set(block, UI_EMBOSS_NONE);
@@ -2555,54 +2606,20 @@ static uiLayout *draw_constraint(uiLayout *layout, Object *ob, bConstraint *con)
UI_block_emboss_set(block, UI_EMBOSS);
}
else {
- short prev_proxylock, show_upbut, show_downbut;
-
- /* Up/Down buttons:
- * Proxy-constraints are not allowed to occur after local (non-proxy) constraints
- * as that poses problems when restoring them, so disable the "up" button where
- * it may cause this situation.
- *
- * Up/Down buttons should only be shown (or not grayed - todo) if they serve some purpose.
- */
- if (BKE_constraints_proxylocked_owner(ob, pchan)) {
- if (con->prev) {
- prev_proxylock = (con->prev->flag & CONSTRAINT_PROXY_LOCAL) ? 0 : 1;
- }
- else {
- prev_proxylock = 0;
- }
- }
- else {
- prev_proxylock = 0;
- }
-
- show_upbut = ((prev_proxylock == 0) && (con->prev));
- show_downbut = (con->next) ? 1 : 0;
-
/* enabled */
UI_block_emboss_set(block, UI_EMBOSS_NONE);
- uiItemR(row, &ptr, "mute", 0, "", 0);
+ uiItemR(layout, &ptr, "mute", 0, "", 0);
UI_block_emboss_set(block, UI_EMBOSS);
- uiLayoutSetOperatorContext(row, WM_OP_INVOKE_DEFAULT);
-
- /* up/down */
- if (show_upbut || show_downbut) {
- UI_block_align_begin(block);
- if (show_upbut) {
- uiItemO(row, "", ICON_TRIA_UP, "CONSTRAINT_OT_move_up");
- }
-
- if (show_downbut) {
- uiItemO(row, "", ICON_TRIA_DOWN, "CONSTRAINT_OT_move_down");
- }
- UI_block_align_end(block);
- }
+ uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT);
/* Close 'button' - emboss calls here disable drawing of 'button' behind X */
UI_block_emboss_set(block, UI_EMBOSS_NONE);
- uiItemO(row, "", ICON_X, "CONSTRAINT_OT_delete");
+ uiItemO(layout, "", ICON_X, "CONSTRAINT_OT_delete");
UI_block_emboss_set(block, UI_EMBOSS);
+
+ /* Some extra padding at the end, so the 'x' icon isn't too close to drag button. */
+ uiItemS(layout);
}
/* Set but-locks for protected settings (magic numbers are used here!) */
@@ -2610,23 +2627,11 @@ static uiLayout *draw_constraint(uiLayout *layout, Object *ob, bConstraint *con)
UI_block_lock_set(block, true, TIP_("Cannot edit Proxy-Protected Constraint"));
}
- /* Draw constraint data */
- if ((con->flag & CONSTRAINT_EXPAND) == 0) {
- (yco) -= 10.5f * UI_UNIT_Y;
- }
- else {
- box = uiLayoutBox(col);
- block = uiLayoutAbsoluteBlock(box);
- result = box;
- }
-
/* clear any locks set up for proxies/lib-linking */
UI_block_lock_clear(block);
-
- return result;
}
-uiLayout *uiTemplateConstraint(uiLayout *layout, PointerRNA *ptr)
+void uiTemplateConstraintHeader(uiLayout *layout, PointerRNA *ptr)
{
Object *ob;
bConstraint *con;
@@ -2634,7 +2639,7 @@ uiLayout *uiTemplateConstraint(uiLayout *layout, PointerRNA *ptr)
/* verify we have valid data */
if (!RNA_struct_is_a(ptr->type, &RNA_Constraint)) {
RNA_warning("Expected constraint on object");
- return NULL;
+ return;
}
ob = (Object *)ptr->owner_id;
@@ -2642,7 +2647,7 @@ uiLayout *uiTemplateConstraint(uiLayout *layout, PointerRNA *ptr)
if (!ob || !(GS(ob->id.name) == ID_OB)) {
RNA_warning("Expected constraint on object");
- return NULL;
+ return;
}
UI_block_lock_set(uiLayoutGetBlock(layout), (ob && ID_IS_LINKED(ob)), ERROR_LIBDATA_MESSAGE);
@@ -2651,11 +2656,11 @@ uiLayout *uiTemplateConstraint(uiLayout *layout, PointerRNA *ptr)
if (con->type == CONSTRAINT_TYPE_KINEMATIC) {
bKinematicConstraint *data = con->data;
if (data->flag & CONSTRAINT_IK_TEMP) {
- return NULL;
+ return;
}
}
- return draw_constraint(layout, ob, con);
+ draw_constraint_header(layout, ob, con);
}
/** \} */
@@ -4525,7 +4530,7 @@ static void CurveProfile_presets_dofunc(bContext *C, void *profile_v, int event)
profile->preset = event;
BKE_curveprofile_reset(profile);
- BKE_curveprofile_update(profile, false);
+ BKE_curveprofile_update(profile, PROF_UPDATE_NONE);
ED_undo_push(C, "CurveProfile tools");
ED_region_tag_redraw(CTX_wm_region(C));
@@ -4641,7 +4646,7 @@ static void CurveProfile_tools_dofunc(bContext *C, void *profile_v, int event)
switch (event) {
case UIPROFILE_FUNC_RESET: /* reset */
BKE_curveprofile_reset(profile);
- BKE_curveprofile_update(profile, false);
+ BKE_curveprofile_update(profile, PROF_UPDATE_NONE);
break;
case UIPROFILE_FUNC_RESET_VIEW: /* reset view to clipping rect */
profile->view_rect = profile->clip_rect;
@@ -4707,7 +4712,7 @@ static void CurveProfile_buttons_zoom_in(bContext *C, void *profile_v, void *UNU
CurveProfile *profile = profile_v;
float d;
- /* we allow 20 times zoom */
+ /* Allow a 20x zoom. */
if (BLI_rctf_size_x(&profile->view_rect) > 0.04f * BLI_rctf_size_x(&profile->clip_rect)) {
d = 0.1154f * BLI_rctf_size_x(&profile->view_rect);
profile->view_rect.xmin += d;
@@ -4771,7 +4776,7 @@ static void CurveProfile_clipping_toggle(bContext *C, void *cb_v, void *profile_
profile->flag ^= PROF_USE_CLIP;
- BKE_curveprofile_update(profile, false);
+ BKE_curveprofile_update(profile, PROF_UPDATE_NONE);
rna_update_cb(C, cb_v, NULL);
}
@@ -4780,7 +4785,7 @@ static void CurveProfile_buttons_reverse(bContext *C, void *cb_v, void *profile_
CurveProfile *profile = profile_v;
BKE_curveprofile_reverse(profile);
- BKE_curveprofile_update(profile, false);
+ BKE_curveprofile_update(profile, PROF_UPDATE_NONE);
rna_update_cb(C, cb_v, NULL);
}
@@ -4789,35 +4794,23 @@ static void CurveProfile_buttons_delete(bContext *C, void *cb_v, void *profile_v
CurveProfile *profile = profile_v;
BKE_curveprofile_remove_by_flag(profile, SELECT);
- BKE_curveprofile_update(profile, false);
-
- rna_update_cb(C, cb_v, NULL);
-}
-
-static void CurveProfile_buttons_setsharp(bContext *C, void *cb_v, void *profile_v)
-{
- CurveProfile *profile = profile_v;
-
- BKE_curveprofile_selected_handle_set(profile, HD_VECT, HD_VECT);
- BKE_curveprofile_update(profile, false);
+ BKE_curveprofile_update(profile, PROF_UPDATE_NONE);
rna_update_cb(C, cb_v, NULL);
}
-static void CurveProfile_buttons_setcurved(bContext *C, void *cb_v, void *profile_v)
+static void CurveProfile_buttons_update(bContext *C, void *arg1_v, void *profile_v)
{
CurveProfile *profile = profile_v;
-
- BKE_curveprofile_selected_handle_set(profile, HD_AUTO, HD_AUTO);
- BKE_curveprofile_update(profile, false);
-
- rna_update_cb(C, cb_v, NULL);
+ BKE_curveprofile_update(profile, PROF_UPDATE_REMOVE_DOUBLES | PROF_UPDATE_CLIP);
+ rna_update_cb(C, arg1_v, NULL);
}
-static void CurveProfile_buttons_update(bContext *C, void *arg1_v, void *profile_v)
+static void CurveProfile_buttons_reset(bContext *C, void *arg1_v, void *profile_v)
{
CurveProfile *profile = profile_v;
- BKE_curveprofile_update(profile, true);
+ BKE_curveprofile_reset(profile);
+ BKE_curveprofile_update(profile, PROF_UPDATE_NONE);
rna_update_cb(C, arg1_v, NULL);
}
@@ -4836,7 +4829,7 @@ static void CurveProfile_buttons_layout(uiLayout *layout, PointerRNA *ptr, RNAUp
UI_block_emboss_set(block, UI_EMBOSS);
- uiLayoutRow(layout, false);
+ uiLayoutSetPropSep(layout, false);
/* Preset selector */
/* There is probably potential to use simpler "uiItemR" functions here, but automatic updating
@@ -4845,6 +4838,29 @@ static void CurveProfile_buttons_layout(uiLayout *layout, PointerRNA *ptr, RNAUp
block, CurveProfile_buttons_presets, profile, "Preset", 0, 0, UI_UNIT_X, UI_UNIT_X, "");
UI_but_funcN_set(bt, rna_update_cb, MEM_dupallocN(cb), NULL);
+ /* Show a "re-apply" preset button when it has been changed from the preset. */
+ if (profile->flag & PROF_DIRTY_PRESET) {
+ /* Only for dynamic presets. */
+ if (ELEM(profile->preset, PROF_PRESET_STEPS, PROF_PRESET_SUPPORTS)) {
+ bt = uiDefIconTextBut(block,
+ UI_BTYPE_BUT,
+ 0,
+ ICON_NONE,
+ "Apply Preset",
+ 0,
+ 0,
+ UI_UNIT_X,
+ UI_UNIT_X,
+ NULL,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ "Reapply and update the preset, removing changes");
+ UI_but_funcN_set(bt, CurveProfile_buttons_reset, MEM_dupallocN(cb), profile);
+ }
+ }
+
row = uiLayoutRow(layout, false);
/* (Left aligned) */
@@ -4952,11 +4968,24 @@ static void CurveProfile_buttons_layout(uiLayout *layout, PointerRNA *ptr, RNAUp
"");
/* Position sliders for (first) selected point */
+ float *selection_x, *selection_y;
for (i = 0; i < profile->path_len; i++) {
if (profile->path[i].flag & PROF_SELECT) {
point = &profile->path[i];
+ selection_x = &point->x;
+ selection_y = &point->y;
break;
}
+ else if (profile->path[i].flag & PROF_H1_SELECT) {
+ point = &profile->path[i];
+ selection_x = &point->h1_loc[0];
+ selection_y = &point->h1_loc[1];
+ }
+ else if (profile->path[i].flag & PROF_H2_SELECT) {
+ point = &profile->path[i];
+ selection_x = &point->h2_loc[0];
+ selection_y = &point->h2_loc[1];
+ }
}
if (i == 0 || i == profile->path_len - 1) {
point_last_or_first = true;
@@ -4972,46 +5001,19 @@ static void CurveProfile_buttons_layout(uiLayout *layout, PointerRNA *ptr, RNAUp
bounds.xmax = bounds.ymax = 1000.0;
}
- uiLayoutRow(layout, true);
- UI_block_funcN_set(block, CurveProfile_buttons_update, MEM_dupallocN(cb), profile);
+ row = uiLayoutRow(layout, true);
- /* Sharp / Smooth */
- bt = uiDefIconBut(block,
- UI_BTYPE_BUT,
- 0,
- ICON_LINCURVE,
- 0,
- 0,
- UI_UNIT_X,
- UI_UNIT_X,
- NULL,
- 0.0,
- 0.0,
- 0.0,
- 0.0,
- TIP_("Set the point's handle type to sharp"));
- if (point_last_or_first) {
- UI_but_flag_enable(bt, UI_BUT_DISABLED);
- }
- UI_but_funcN_set(bt, CurveProfile_buttons_setsharp, MEM_dupallocN(cb), profile);
- bt = uiDefIconBut(block,
- UI_BTYPE_BUT,
- 0,
- ICON_SMOOTHCURVE,
- 0,
- 0,
- UI_UNIT_X,
- UI_UNIT_X,
- NULL,
- 0.0,
- 0.0,
- 0.0,
- 0.0,
- TIP_("Set the point's handle type to smooth"));
- UI_but_funcN_set(bt, CurveProfile_buttons_setcurved, MEM_dupallocN(cb), profile);
- if (point_last_or_first) {
- UI_but_flag_enable(bt, UI_BUT_DISABLED);
- }
+ PointerRNA point_ptr;
+ RNA_pointer_create(ptr->owner_id, &RNA_CurveProfilePoint, point, &point_ptr);
+ PropertyRNA *prop_handle_type = RNA_struct_find_property(&point_ptr, "handle_type_1");
+ uiItemFullR(row,
+ &point_ptr,
+ prop_handle_type,
+ RNA_NO_INDEX,
+ 0,
+ UI_ITEM_R_EXPAND | UI_ITEM_R_ICON_ONLY,
+ "",
+ ICON_NONE);
/* Position */
bt = uiDefButF(block,
@@ -5022,16 +5024,16 @@ static void CurveProfile_buttons_layout(uiLayout *layout, PointerRNA *ptr, RNAUp
2 * UI_UNIT_Y,
UI_UNIT_X * 10,
UI_UNIT_Y,
- &point->x,
+ selection_x,
bounds.xmin,
bounds.xmax,
1,
5,
"");
+ UI_but_funcN_set(bt, CurveProfile_buttons_update, MEM_dupallocN(cb), profile);
if (point_last_or_first) {
UI_but_flag_enable(bt, UI_BUT_DISABLED);
}
-
bt = uiDefButF(block,
UI_BTYPE_NUM,
0,
@@ -5040,12 +5042,13 @@ static void CurveProfile_buttons_layout(uiLayout *layout, PointerRNA *ptr, RNAUp
1 * UI_UNIT_Y,
UI_UNIT_X * 10,
UI_UNIT_Y,
- &point->y,
+ selection_y,
bounds.ymin,
bounds.ymax,
1,
5,
"");
+ UI_but_funcN_set(bt, CurveProfile_buttons_update, MEM_dupallocN(cb), profile);
if (point_last_or_first) {
UI_but_flag_enable(bt, UI_BUT_DISABLED);
}
@@ -7271,37 +7274,35 @@ void uiTemplateCacheFile(uiLayout *layout,
SpaceProperties *sbuts = CTX_wm_space_properties(C);
- uiLayout *row = uiLayoutRow(layout, false);
- uiBlock *block = uiLayoutGetBlock(row);
- uiDefBut(block, UI_BTYPE_LABEL, 0, IFACE_("File Path:"), 0, 19, 145, 19, NULL, 0, 0, 0, 0, "");
-
- row = uiLayoutRow(layout, false);
- uiLayout *split = uiLayoutSplit(row, 0.0f, false);
- row = uiLayoutRow(split, true);
+ uiLayout *row, *sub, *subsub;
- uiItemR(row, &fileptr, "filepath", 0, "", ICON_NONE);
- uiItemO(row, "", ICON_FILE_REFRESH, "cachefile.reload");
+ uiLayoutSetPropSep(layout, true);
- row = uiLayoutRow(layout, false);
- uiItemR(row, &fileptr, "is_sequence", 0, "Is Sequence", ICON_NONE);
+ row = uiLayoutRow(layout, true);
+ uiItemR(row, &fileptr, "filepath", 0, NULL, ICON_NONE);
+ sub = uiLayoutRow(row, true);
+ uiItemO(sub, "", ICON_FILE_REFRESH, "cachefile.reload");
row = uiLayoutRow(layout, false);
- uiItemR(row, &fileptr, "override_frame", 0, "Override Frame", ICON_NONE);
+ uiItemR(row, &fileptr, "is_sequence", 0, NULL, ICON_NONE);
- row = uiLayoutRow(layout, false);
- uiLayoutSetActive(row, RNA_boolean_get(&fileptr, "override_frame"));
- uiItemR(row, &fileptr, "frame", 0, "Frame", ICON_NONE);
+ row = uiLayoutRowWithHeading(layout, true, IFACE_("Override Frame"));
+ sub = uiLayoutRow(row, true);
+ uiLayoutSetPropDecorate(sub, false);
+ uiItemR(sub, &fileptr, "override_frame", 0, "", ICON_NONE);
+ subsub = uiLayoutRow(sub, true);
+ uiLayoutSetActive(subsub, RNA_boolean_get(&fileptr, "override_frame"));
+ uiItemR(subsub, &fileptr, "frame", 0, "", ICON_NONE);
+ uiItemDecoratorR(row, &fileptr, "frame", 0);
row = uiLayoutRow(layout, false);
- uiItemR(row, &fileptr, "frame_offset", 0, "Frame Offset", ICON_NONE);
+ uiItemR(row, &fileptr, "frame_offset", 0, NULL, ICON_NONE);
uiLayoutSetActive(row, !RNA_boolean_get(&fileptr, "is_sequence"));
- row = uiLayoutRow(layout, false);
- uiItemL(row, IFACE_("Manual Transform:"), ICON_NONE);
-
- row = uiLayoutRow(layout, false);
- uiLayoutSetActive(row, (sbuts->mainb == BCONTEXT_CONSTRAINT));
- uiItemR(row, &fileptr, "scale", 0, "Scale", ICON_NONE);
+ if (sbuts->mainb == BCONTEXT_CONSTRAINT) {
+ row = uiLayoutRow(layout, false);
+ uiItemR(row, &fileptr, "scale", 0, IFACE_("Manual Scale"), ICON_NONE);
+ }
/* TODO: unused for now, so no need to expose. */
#if 0
diff --git a/source/blender/editors/interface/interface_utils.c b/source/blender/editors/interface/interface_utils.c
index 3737b607305..432da3d5dd5 100644
--- a/source/blender/editors/interface/interface_utils.c
+++ b/source/blender/editors/interface/interface_utils.c
@@ -45,6 +45,7 @@
#include "RNA_access.h"
#include "UI_interface.h"
+#include "UI_interface_icons.h"
#include "UI_resources.h"
#include "WM_api.h"
@@ -384,6 +385,7 @@ typedef struct CollItemSearch {
char *name;
int index;
int iconid;
+ bool is_id;
uint has_sep_char : 1;
} CollItemSearch;
@@ -418,6 +420,7 @@ void ui_rna_collection_search_update_fn(const struct bContext *C,
const bool skip_filter = data->search_but && !data->search_but->changed;
char name_buf[UI_MAX_DRAW_STR];
char *name;
+ bool has_id_icon = false;
/* build a temporary list of relevant items first */
RNA_PROP_BEGIN (&data->search_ptr, itemptr, data->search_prop) {
@@ -437,16 +440,20 @@ void ui_rna_collection_search_update_fn(const struct bContext *C,
int iconid = ICON_NONE;
bool has_sep_char = false;
+ bool is_id = itemptr.type && RNA_struct_is_ID(itemptr.type);
- if (itemptr.type && RNA_struct_is_ID(itemptr.type)) {
+ if (is_id) {
iconid = ui_id_icon_get(C, itemptr.data, false);
+ if (!ELEM(iconid, 0, ICON_BLANK1)) {
+ has_id_icon = true;
+ }
if (requires_exact_data_name) {
name = RNA_struct_name_get_alloc(&itemptr, name_buf, sizeof(name_buf), NULL);
}
else {
const ID *id = itemptr.data;
- BKE_id_full_name_ui_prefix_get(name_buf, id, UI_SEP_CHAR);
+ BKE_id_full_name_ui_prefix_get(name_buf, itemptr.data, true, UI_SEP_CHAR);
BLI_STATIC_ASSERT(sizeof(name_buf) >= MAX_ID_FULL_NAME_UI,
"Name string buffer should be big enough to hold full UI ID name");
name = name_buf;
@@ -464,6 +471,7 @@ void ui_rna_collection_search_update_fn(const struct bContext *C,
cis->name = BLI_strdup(name);
cis->index = i;
cis->iconid = iconid;
+ cis->is_id = is_id;
cis->has_sep_char = has_sep_char;
BLI_addtail(items_list, cis);
}
@@ -480,6 +488,17 @@ void ui_rna_collection_search_update_fn(const struct bContext *C,
/* add search items from temporary list */
for (cis = items_list->first; cis; cis = cis->next) {
+ /* If no item has an own icon to display, libraries can use the library icons rather than the
+ * name prefix for showing the library status. */
+ if (!has_id_icon && cis->is_id) {
+ cis->iconid = UI_library_icon_get(cis->data);
+ /* No need to re-allocate, string should be shorter than before (lib status prefix is
+ * removed). */
+ BKE_id_full_name_ui_prefix_get(name_buf, cis->data, false, UI_SEP_CHAR);
+ BLI_assert(strlen(name_buf) <= MEM_allocN_len(cis->name));
+ strcpy(cis->name, name_buf);
+ }
+
if (!UI_search_item_add(items,
cis->name,
cis->data,
diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c
index 0498b312618..16a5a13885b 100644
--- a/source/blender/editors/interface/interface_widgets.c
+++ b/source/blender/editors/interface/interface_widgets.c
@@ -393,35 +393,14 @@ static const uint g_shape_preset_hold_action_face[2][3] = {{2, 0, 1}, {3, 5, 4}}
*
* \{ */
-/* offset in triavec[] in shader per type */
-static const int tria_ofs[ROUNDBOX_TRIA_MAX] = {
- [ROUNDBOX_TRIA_NONE] = 0,
- [ROUNDBOX_TRIA_ARROWS] = 0,
- [ROUNDBOX_TRIA_SCROLL] = 12,
- [ROUNDBOX_TRIA_MENU] = 28,
- [ROUNDBOX_TRIA_CHECK] = 34,
- [ROUNDBOX_TRIA_HOLD_ACTION_ARROW] = 40,
-};
-static const int tria_vcount[ROUNDBOX_TRIA_MAX] = {
- [ROUNDBOX_TRIA_NONE] = 0,
- [ROUNDBOX_TRIA_ARROWS] = 6,
- [ROUNDBOX_TRIA_SCROLL] = 16,
- [ROUNDBOX_TRIA_MENU] = 6,
- [ROUNDBOX_TRIA_CHECK] = 6,
- [ROUNDBOX_TRIA_HOLD_ACTION_ARROW] = 3,
-};
-
static struct {
- GPUBatch *roundbox_widget[ROUNDBOX_TRIA_MAX];
-
- GPUBatch *roundbox_simple;
- GPUBatch *roundbox_simple_aa;
- GPUBatch *roundbox_simple_outline;
+ GPUBatch *roundbox_widget;
GPUBatch *roundbox_shadow;
+ /* TODO remove */
GPUVertFormat format;
uint vflag_id;
-} g_ui_batch_cache = {{0}};
+} g_ui_batch_cache = {0};
static GPUVertFormat *vflag_format(void)
{
@@ -436,7 +415,7 @@ static GPUVertFormat *vflag_format(void)
#define INNER 0
#define OUTLINE 1
#define EMBOSS 2
-#define NO_AA WIDGET_AA_JITTER
+#define NO_AA 0
static void set_roundbox_vertex_data(GPUVertBufRaw *vflag_step, uint32_t d)
{
@@ -462,176 +441,30 @@ static uint32_t set_roundbox_vertex(GPUVertBufRaw *vflag_step,
return *data;
}
-static uint32_t set_tria_vertex(
- GPUVertBufRaw *vflag_step, int tria_type, int tria_v, int tria_id, int jit_v)
-{
- uint32_t *data = GPU_vertbuf_raw_step(vflag_step);
- if (ELEM(tria_type, ROUNDBOX_TRIA_ARROWS)) {
- tria_v += tria_id * tria_vcount[ROUNDBOX_TRIA_ARROWS];
- }
- *data = tria_ofs[tria_type] + tria_v;
- *data |= jit_v << 6;
- *data |= (tria_id == 0) ? (1 << 10) : 0; /* is first tria */
- *data |= 1 << 14; /* is tria vert */
- return *data;
-}
-
-static void roundbox_batch_add_tria(GPUVertBufRaw *vflag_step, int tria, uint32_t last_data)
+GPUBatch *ui_batch_roundbox_widget_get(void)
{
- const int tria_num =
- ELEM(tria, ROUNDBOX_TRIA_CHECK, ROUNDBOX_TRIA_HOLD_ACTION_ARROW, ROUNDBOX_TRIA_MENU) ? 1 : 2;
- /* for each tria */
- for (int t = 0; t < tria_num; t++) {
- for (int j = 0; j < WIDGET_AA_JITTER; j++) {
- /* restart */
- set_roundbox_vertex_data(vflag_step, last_data);
- set_tria_vertex(vflag_step, tria, 0, t, j);
- for (int v = 0; v < tria_vcount[tria]; v++) {
- last_data = set_tria_vertex(vflag_step, tria, v, t, j);
- }
- }
- }
-}
-
-GPUBatch *ui_batch_roundbox_widget_get(int tria)
-{
- if (g_ui_batch_cache.roundbox_widget[tria] == NULL) {
- uint32_t last_data;
- GPUVertBufRaw vflag_step;
+ if (g_ui_batch_cache.roundbox_widget == NULL) {
GPUVertBuf *vbo = GPU_vertbuf_create_with_format(vflag_format());
- int vcount = WIDGET_SIZE_MAX; /* inner */
- vcount += 2; /* restart */
- vcount += ((WIDGET_SIZE_MAX + 1) * 2) * WIDGET_AA_JITTER; /* outline (edges) */
- vcount += 2; /* restart */
- vcount += ((WIDGET_CURVE_RESOLU * 2) * 2) * WIDGET_AA_JITTER; /* emboss */
- if (tria) {
- vcount += (tria_vcount[tria] + 2) * WIDGET_AA_JITTER; /* tria1 */
- if (!ELEM(tria, ROUNDBOX_TRIA_CHECK, ROUNDBOX_TRIA_HOLD_ACTION_ARROW, ROUNDBOX_TRIA_MENU)) {
- vcount += (tria_vcount[tria] + 2) * WIDGET_AA_JITTER; /* tria2 */
- }
- }
- GPU_vertbuf_data_alloc(vbo, vcount);
- GPU_vertbuf_attr_get_raw_data(vbo, g_ui_batch_cache.vflag_id, &vflag_step);
- /* Inner */
- for (int c1 = 0, c2 = 3; c1 < 2; c1++, c2--) {
- for (int a1 = 0, a2 = WIDGET_CURVE_RESOLU - 1; a2 >= 0; a1++, a2--) {
- last_data = set_roundbox_vertex(&vflag_step, c1, a1, NO_AA, true, false, INNER);
- last_data = set_roundbox_vertex(&vflag_step, c2, a2, NO_AA, true, false, INNER);
- }
- }
- /* restart */
- set_roundbox_vertex_data(&vflag_step, last_data);
- set_roundbox_vertex(&vflag_step, 0, 0, 0, true, false, OUTLINE);
- /* Outlines */
- for (int j = 0; j < WIDGET_AA_JITTER; j++) {
- for (int c = 0; c < 4; c++) {
- for (int a = 0; a < WIDGET_CURVE_RESOLU; a++) {
- set_roundbox_vertex(&vflag_step, c, a, j, true, false, OUTLINE);
- set_roundbox_vertex(&vflag_step, c, a, j, false, false, OUTLINE);
- }
- }
- /* Close the loop. */
- set_roundbox_vertex(&vflag_step, 0, 0, j, true, false, OUTLINE);
- last_data = set_roundbox_vertex(&vflag_step, 0, 0, j, false, false, OUTLINE);
- }
- /* restart */
- set_roundbox_vertex_data(&vflag_step, last_data);
- set_roundbox_vertex(&vflag_step, 0, 0, 0, false, false, EMBOSS);
- /* Emboss */
- /* go back and forth : avoid degenerate triangle (but beware of backface cull) */
- bool rev = false;
- for (int j = 0; j < WIDGET_AA_JITTER; j++, rev = !rev) {
- for (int c = (rev) ? 1 : 0; (rev) ? c >= 0 : c < 2; (rev) ? c-- : c++) {
- int sta = (rev) ? WIDGET_CURVE_RESOLU - 1 : 0;
- int end = WIDGET_CURVE_RESOLU;
- for (int a = sta; (rev) ? a >= 0 : a < end; (rev) ? a-- : a++) {
- set_roundbox_vertex(&vflag_step, c, a, j, false, false, EMBOSS);
- last_data = set_roundbox_vertex(&vflag_step, c, a, j, false, true, EMBOSS);
- }
- }
- }
- if (tria) {
- roundbox_batch_add_tria(&vflag_step, tria, last_data);
- }
- g_ui_batch_cache.roundbox_widget[tria] = GPU_batch_create_ex(
- GPU_PRIM_TRI_STRIP, vbo, NULL, GPU_BATCH_OWNS_VBO);
- gpu_batch_presets_register(g_ui_batch_cache.roundbox_widget[tria]);
- }
- return g_ui_batch_cache.roundbox_widget[tria];
-}
-GPUBatch *ui_batch_roundbox_get(bool filled, bool antialiased)
-{
- GPUBatch **batch = NULL;
+ GPU_vertbuf_data_alloc(vbo, 12);
- if (filled) {
- if (antialiased) {
- batch = &g_ui_batch_cache.roundbox_simple_aa;
- }
- else {
- batch = &g_ui_batch_cache.roundbox_simple;
- }
- }
- else {
- if (antialiased) {
- BLI_assert(0); /* Use GL_LINE_SMOOTH instead!!: */
- }
- else {
- batch = &g_ui_batch_cache.roundbox_simple_outline;
- }
- }
+ GPUIndexBufBuilder ibuf;
+ GPU_indexbuf_init(&ibuf, GPU_PRIM_TRIS, 6, 12);
+ /* Widget */
+ GPU_indexbuf_add_tri_verts(&ibuf, 0, 1, 2);
+ GPU_indexbuf_add_tri_verts(&ibuf, 2, 1, 3);
+ /* Trias */
+ GPU_indexbuf_add_tri_verts(&ibuf, 4, 5, 6);
+ GPU_indexbuf_add_tri_verts(&ibuf, 6, 5, 7);
- if (*batch == NULL) {
- uint32_t last_data;
- GPUVertBufRaw vflag_step;
- GPUVertBuf *vbo = GPU_vertbuf_create_with_format(vflag_format());
- int vcount = WIDGET_SIZE_MAX;
- vcount += (filled) ? 2 : 0;
- vcount *= (antialiased) ? WIDGET_AA_JITTER : 1;
- GPU_vertbuf_data_alloc(vbo, vcount);
- GPU_vertbuf_attr_get_raw_data(vbo, g_ui_batch_cache.vflag_id, &vflag_step);
+ GPU_indexbuf_add_tri_verts(&ibuf, 8, 9, 10);
+ GPU_indexbuf_add_tri_verts(&ibuf, 10, 9, 11);
- if (filled) {
- for (int j = 0; j < WIDGET_AA_JITTER; j++) {
- if (!antialiased) {
- j = NO_AA;
- }
- /* restart */
- set_roundbox_vertex(&vflag_step, 0, 0, j, true, false, INNER);
- for (int c1 = 0, c2 = 3; c1 < 2; c1++, c2--) {
- for (int a1 = 0, a2 = WIDGET_CURVE_RESOLU - 1; a2 >= 0; a1++, a2--) {
- last_data = set_roundbox_vertex(&vflag_step, c1, a1, j, true, false, INNER);
- last_data = set_roundbox_vertex(&vflag_step, c2, a2, j, true, false, INNER);
- }
- }
- /* restart */
- set_roundbox_vertex_data(&vflag_step, last_data);
- if (!antialiased) {
- break;
- }
- }
- *batch = GPU_batch_create_ex(GPU_PRIM_TRI_STRIP, vbo, NULL, GPU_BATCH_OWNS_VBO);
- }
- else {
- for (int j = 0; j < WIDGET_AA_JITTER; j++) {
- if (!antialiased) {
- j = NO_AA;
- }
- for (int c = 0; c < 4; c++) {
- for (int a = 0; a < WIDGET_CURVE_RESOLU; a++) {
- set_roundbox_vertex(&vflag_step, c, a, j, true, false, INNER);
- }
- }
- if (!antialiased) {
- break;
- }
- }
- *batch = GPU_batch_create_ex(GPU_PRIM_LINE_LOOP, vbo, NULL, GPU_BATCH_OWNS_VBO);
- }
-
- gpu_batch_presets_register(*batch);
+ g_ui_batch_cache.roundbox_widget = GPU_batch_create_ex(
+ GPU_PRIM_TRIS, vbo, GPU_indexbuf_build(&ibuf), GPU_BATCH_OWNS_INDEX | GPU_BATCH_OWNS_VBO);
+ gpu_batch_presets_register(g_ui_batch_cache.roundbox_widget);
}
- return *batch;
+ return g_ui_batch_cache.roundbox_widget;
}
GPUBatch *ui_batch_roundbox_shadow_get(void)
@@ -1314,14 +1147,13 @@ static void widgetbase_set_uniform_colors_ubv(uiWidgetBase *wtb,
/* keep in sync with shader */
#define MAX_WIDGET_BASE_BATCH 6
-#define MAX_WIDGET_PARAMETERS 11
+#define MAX_WIDGET_PARAMETERS 12
static struct {
- GPUBatch *batch; /* Batch type */
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH];
int count;
bool enabled;
-} g_widget_base_batch = {0};
+} g_widget_base_batch = {{{{0}}}};
void UI_widgetbase_draw_cache_flush(void)
{
@@ -1332,7 +1164,7 @@ void UI_widgetbase_draw_cache_flush(void)
return;
}
- GPUBatch *batch = g_widget_base_batch.batch;
+ GPUBatch *batch = ui_batch_roundbox_widget_get();
if (g_widget_base_batch.count == 1) {
/* draw single */
GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE);
@@ -1376,31 +1208,15 @@ void UI_widgetbase_draw_cache_end(void)
GPU_blend(false);
}
-static void draw_widgetbase_batch(GPUBatch *batch, uiWidgetBase *wtb)
+static void draw_widgetbase_batch(uiWidgetBase *wtb)
{
+ wtb->uniform_params.tria_type = wtb->tria1.type;
wtb->uniform_params.tria1_size = wtb->tria1.size;
wtb->uniform_params.tria2_size = wtb->tria2.size;
copy_v2_v2(wtb->uniform_params.tria1_center, wtb->tria1.center);
copy_v2_v2(wtb->uniform_params.tria2_center, wtb->tria2.center);
if (g_widget_base_batch.enabled) {
- if (g_widget_base_batch.batch == NULL) {
- g_widget_base_batch.batch = ui_batch_roundbox_widget_get(ROUNDBOX_TRIA_ARROWS);
- }
-
- /* draw multi */
- if (batch != g_ui_batch_cache.roundbox_widget[ROUNDBOX_TRIA_NONE] &&
- batch != g_widget_base_batch.batch) {
- /* issue previous calls before changing batch type. */
- UI_widgetbase_draw_cache_flush();
- g_widget_base_batch.batch = batch;
- }
-
- /* No need to change batch if tria is not visible. Just scale it to 0. */
- if (batch == g_ui_batch_cache.roundbox_widget[ROUNDBOX_TRIA_NONE]) {
- wtb->uniform_params.tria1_size = wtb->uniform_params.tria2_size = 0;
- }
-
g_widget_base_batch.params[g_widget_base_batch.count] = wtb->uniform_params;
g_widget_base_batch.count++;
@@ -1412,6 +1228,7 @@ static void draw_widgetbase_batch(GPUBatch *batch, uiWidgetBase *wtb)
const float checker_params[3] = {
UI_ALPHA_CHECKER_DARK / 255.0f, UI_ALPHA_CHECKER_LIGHT / 255.0f, 8.0f};
/* draw single */
+ GPUBatch *batch = ui_batch_roundbox_widget_get();
GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE);
GPU_batch_uniform_4fv_array(
batch, "parameters", MAX_WIDGET_PARAMETERS, (float *)&wtb->uniform_params);
@@ -1434,8 +1251,6 @@ static void widgetbase_draw_ex(uiWidgetBase *wtb,
show_alpha_checkers = false;
}
- GPU_blend(true);
-
/* backdrop non AA */
if (wtb->draw_inner) {
if (wcol->shaded == 0) {
@@ -1455,7 +1270,7 @@ static void widgetbase_draw_ex(uiWidgetBase *wtb,
outline_col[0] = wcol->outline[0];
outline_col[1] = wcol->outline[1];
outline_col[2] = wcol->outline[2];
- outline_col[3] = wcol->outline[3] / WIDGET_AA_JITTER;
+ outline_col[3] = wcol->outline[3];
/* emboss bottom shadow */
if (wtb->draw_emboss) {
@@ -1467,7 +1282,7 @@ static void widgetbase_draw_ex(uiWidgetBase *wtb,
tria_col[0] = wcol->item[0];
tria_col[1] = wcol->item[1];
tria_col[2] = wcol->item[2];
- tria_col[3] = (uchar)((float)wcol->item[3] / WIDGET_AA_JITTER);
+ tria_col[3] = wcol->item[3];
}
/* Draw everything in one drawcall */
@@ -1476,11 +1291,10 @@ static void widgetbase_draw_ex(uiWidgetBase *wtb,
widgetbase_set_uniform_colors_ubv(
wtb, inner_col1, inner_col2, outline_col, emboss_col, tria_col, show_alpha_checkers);
- GPUBatch *roundbox_batch = ui_batch_roundbox_widget_get(wtb->tria1.type);
- draw_widgetbase_batch(roundbox_batch, wtb);
+ GPU_blend(true);
+ draw_widgetbase_batch(wtb);
+ GPU_blend(false);
}
-
- GPU_blend(false);
}
static void widgetbase_draw(uiWidgetBase *wtb, const uiWidgetColors *wcol)
diff --git a/source/blender/editors/interface/view2d.c b/source/blender/editors/interface/view2d.c
index f8419ba3eba..d696c99851b 100644
--- a/source/blender/editors/interface/view2d.c
+++ b/source/blender/editors/interface/view2d.c
@@ -113,10 +113,10 @@ BLI_INLINE void clamp_rctf_to_rcti(rcti *dst, const rctf *src)
static int view2d_scroll_mapped(int scroll)
{
if (scroll & V2D_SCROLL_HORIZONTAL_FULLR) {
- scroll &= ~(V2D_SCROLL_HORIZONTAL);
+ scroll &= ~V2D_SCROLL_HORIZONTAL;
}
if (scroll & V2D_SCROLL_VERTICAL_FULLR) {
- scroll &= ~(V2D_SCROLL_VERTICAL);
+ scroll &= ~V2D_SCROLL_VERTICAL;
}
return scroll;
}
@@ -198,7 +198,7 @@ static void view2d_masks(View2D *v2d, const rcti *mask_scroll)
}
/* horizontal scroller */
- if (scroll & (V2D_SCROLL_BOTTOM)) {
+ if (scroll & V2D_SCROLL_BOTTOM) {
/* on bottom edge of region */
v2d->hor = *mask_scroll;
v2d->hor.ymax = scroll_height;
@@ -211,7 +211,7 @@ static void view2d_masks(View2D *v2d, const rcti *mask_scroll)
/* adjust vertical scroller if there's a horizontal scroller, to leave corner free */
if (scroll & V2D_SCROLL_VERTICAL) {
- if (scroll & (V2D_SCROLL_BOTTOM)) {
+ if (scroll & V2D_SCROLL_BOTTOM) {
/* on bottom edge of region */
v2d->vert.ymin = v2d->hor.ymax;
}
@@ -1408,22 +1408,22 @@ struct View2DScrollers {
int vert_min, vert_max; /* vertical scrollbar */
int hor_min, hor_max; /* horizontal scrollbar */
- rcti hor, vert; /* exact size of slider backdrop */
- int horfull, vertfull; /* set if sliders are full, we don't draw them */
+ /** Exact size of slider backdrop. */
+ rcti hor, vert;
+ /* set if sliders are full, we don't draw them */
+ /* int horfull, vertfull; */ /* UNUSED */
};
/* Calculate relevant scroller properties */
-View2DScrollers *UI_view2d_scrollers_calc(View2D *v2d, const rcti *mask_custom)
+void UI_view2d_scrollers_calc(View2D *v2d,
+ const rcti *mask_custom,
+ struct View2DScrollers *r_scrollers)
{
- View2DScrollers *scrollers;
rcti vert, hor;
float fac1, fac2, totsize, scrollsize;
int scroll = view2d_scroll_mapped(v2d->scroll);
int smaller;
- /* scrollers is allocated here... */
- scrollers = MEM_callocN(sizeof(View2DScrollers), "View2DScrollers");
-
/* Always update before drawing (for dynamically sized scrollers). */
view2d_masks(v2d, mask_custom);
@@ -1452,12 +1452,12 @@ View2DScrollers *UI_view2d_scrollers_calc(View2D *v2d, const rcti *mask_custom)
vert.xmax -= smaller;
}
- CLAMP(vert.ymin, vert.ymin, vert.ymax - V2D_SCROLL_HANDLE_SIZE_HOTSPOT);
- CLAMP(hor.xmin, hor.xmin, hor.xmax - V2D_SCROLL_HANDLE_SIZE_HOTSPOT);
+ CLAMP_MAX(vert.ymin, vert.ymax - V2D_SCROLL_HANDLE_SIZE_HOTSPOT);
+ CLAMP_MAX(hor.xmin, hor.xmax - V2D_SCROLL_HANDLE_SIZE_HOTSPOT);
/* store in scrollers, used for drawing */
- scrollers->vert = vert;
- scrollers->hor = hor;
+ r_scrollers->vert = vert;
+ r_scrollers->hor = hor;
/* scroller 'buttons':
* - These should always remain within the visible region of the scrollbar
@@ -1475,30 +1475,30 @@ View2DScrollers *UI_view2d_scrollers_calc(View2D *v2d, const rcti *mask_custom)
fac1 = (v2d->cur.xmin - v2d->tot.xmin) / totsize;
if (fac1 <= 0.0f) {
- scrollers->hor_min = hor.xmin;
+ r_scrollers->hor_min = hor.xmin;
}
else {
- scrollers->hor_min = (int)(hor.xmin + (fac1 * scrollsize));
+ r_scrollers->hor_min = (int)(hor.xmin + (fac1 * scrollsize));
}
fac2 = (v2d->cur.xmax - v2d->tot.xmin) / totsize;
if (fac2 >= 1.0f) {
- scrollers->hor_max = hor.xmax;
+ r_scrollers->hor_max = hor.xmax;
}
else {
- scrollers->hor_max = (int)(hor.xmin + (fac2 * scrollsize));
+ r_scrollers->hor_max = (int)(hor.xmin + (fac2 * scrollsize));
}
/* prevent inverted sliders */
- if (scrollers->hor_min > scrollers->hor_max) {
- scrollers->hor_min = scrollers->hor_max;
+ if (r_scrollers->hor_min > r_scrollers->hor_max) {
+ r_scrollers->hor_min = r_scrollers->hor_max;
}
/* prevent sliders from being too small to grab */
- if ((scrollers->hor_max - scrollers->hor_min) < V2D_SCROLL_THUMB_SIZE_MIN) {
- scrollers->hor_max = scrollers->hor_min + V2D_SCROLL_THUMB_SIZE_MIN;
+ if ((r_scrollers->hor_max - r_scrollers->hor_min) < V2D_SCROLL_THUMB_SIZE_MIN) {
+ r_scrollers->hor_max = r_scrollers->hor_min + V2D_SCROLL_THUMB_SIZE_MIN;
- CLAMP(scrollers->hor_max, hor.xmin + V2D_SCROLL_THUMB_SIZE_MIN, hor.xmax);
- CLAMP(scrollers->hor_min, hor.xmin, hor.xmax - V2D_SCROLL_THUMB_SIZE_MIN);
+ CLAMP(r_scrollers->hor_max, hor.xmin + V2D_SCROLL_THUMB_SIZE_MIN, hor.xmax);
+ CLAMP(r_scrollers->hor_min, hor.xmin, hor.xmax - V2D_SCROLL_THUMB_SIZE_MIN);
}
}
@@ -1513,39 +1513,39 @@ View2DScrollers *UI_view2d_scrollers_calc(View2D *v2d, const rcti *mask_custom)
fac1 = (v2d->cur.ymin - v2d->tot.ymin) / totsize;
if (fac1 <= 0.0f) {
- scrollers->vert_min = vert.ymin;
+ r_scrollers->vert_min = vert.ymin;
}
else {
- scrollers->vert_min = (int)(vert.ymin + (fac1 * scrollsize));
+ r_scrollers->vert_min = (int)(vert.ymin + (fac1 * scrollsize));
}
fac2 = (v2d->cur.ymax - v2d->tot.ymin) / totsize;
if (fac2 >= 1.0f) {
- scrollers->vert_max = vert.ymax;
+ r_scrollers->vert_max = vert.ymax;
}
else {
- scrollers->vert_max = (int)(vert.ymin + (fac2 * scrollsize));
+ r_scrollers->vert_max = (int)(vert.ymin + (fac2 * scrollsize));
}
/* prevent inverted sliders */
- if (scrollers->vert_min > scrollers->vert_max) {
- scrollers->vert_min = scrollers->vert_max;
+ if (r_scrollers->vert_min > r_scrollers->vert_max) {
+ r_scrollers->vert_min = r_scrollers->vert_max;
}
/* prevent sliders from being too small to grab */
- if ((scrollers->vert_max - scrollers->vert_min) < V2D_SCROLL_THUMB_SIZE_MIN) {
- scrollers->vert_max = scrollers->vert_min + V2D_SCROLL_THUMB_SIZE_MIN;
+ if ((r_scrollers->vert_max - r_scrollers->vert_min) < V2D_SCROLL_THUMB_SIZE_MIN) {
+ r_scrollers->vert_max = r_scrollers->vert_min + V2D_SCROLL_THUMB_SIZE_MIN;
- CLAMP(scrollers->vert_max, vert.ymin + V2D_SCROLL_THUMB_SIZE_MIN, vert.ymax);
- CLAMP(scrollers->vert_min, vert.ymin, vert.ymax - V2D_SCROLL_THUMB_SIZE_MIN);
+ CLAMP(r_scrollers->vert_max, vert.ymin + V2D_SCROLL_THUMB_SIZE_MIN, vert.ymax);
+ CLAMP(r_scrollers->vert_min, vert.ymin, vert.ymax - V2D_SCROLL_THUMB_SIZE_MIN);
}
}
-
- return scrollers;
}
/* Draw scrollbars in the given 2d-region */
-void UI_view2d_scrollers_draw(View2D *v2d, View2DScrollers *vs)
+void UI_view2d_scrollers_draw(View2D *v2d, const rcti *mask_custom)
{
+ View2DScrollers scrollers;
+ UI_view2d_scrollers_calc(v2d, mask_custom, &scrollers);
bTheme *btheme = UI_GetTheme();
rcti vert, hor;
const int scroll = view2d_scroll_mapped(v2d->scroll);
@@ -1556,8 +1556,8 @@ void UI_view2d_scrollers_draw(View2D *v2d, View2DScrollers *vs)
UI_GetThemeColor4ubv(TH_BACK, scrollers_back_color);
/* make copies of rects for less typing */
- vert = vs->vert;
- hor = vs->hor;
+ vert = scrollers.vert;
+ hor = scrollers.hor;
/* horizontal scrollbar */
if (scroll & V2D_SCROLL_HORIZONTAL) {
@@ -1566,8 +1566,8 @@ void UI_view2d_scrollers_draw(View2D *v2d, View2DScrollers *vs)
rcti slider;
int state;
- slider.xmin = vs->hor_min;
- slider.xmax = vs->hor_max;
+ slider.xmin = scrollers.hor_min;
+ slider.xmax = scrollers.hor_max;
slider.ymin = hor.ymin;
slider.ymax = hor.ymax;
@@ -1602,8 +1602,8 @@ void UI_view2d_scrollers_draw(View2D *v2d, View2DScrollers *vs)
slider.xmin = vert.xmin;
slider.xmax = vert.xmax;
- slider.ymin = vs->vert_min;
- slider.ymax = vs->vert_max;
+ slider.ymin = scrollers.vert_min;
+ slider.ymax = scrollers.vert_max;
state = (v2d->scroll_ui & V2D_SCROLL_V_ACTIVE) ? UI_SCROLL_PRESSED : 0;
@@ -1631,12 +1631,6 @@ void UI_view2d_scrollers_draw(View2D *v2d, View2DScrollers *vs)
btheme->tui.widget_emboss[3] = emboss_alpha;
}
-/* free temporary memory used for drawing scrollers */
-void UI_view2d_scrollers_free(View2DScrollers *scrollers)
-{
- MEM_freeN(scrollers);
-}
-
/** \} */
/* -------------------------------------------------------------------- */
diff --git a/source/blender/editors/interface/view2d_ops.c b/source/blender/editors/interface/view2d_ops.c
index 98bbd7af943..0efb49f42bf 100644
--- a/source/blender/editors/interface/view2d_ops.c
+++ b/source/blender/editors/interface/view2d_ops.c
@@ -1922,6 +1922,9 @@ struct View2DScrollers {
/* focus bubbles */
int vert_min, vert_max; /* vertical scrollbar */
int hor_min, hor_max; /* horizontal scrollbar */
+
+ /* These values are written into, even if we don't use them. */
+ rcti _hor, _vert;
};
/* quick enum for vsm->zone (scroller handles) */
@@ -2011,7 +2014,7 @@ static void scroller_activate_init(bContext *C,
const char in_scroller)
{
v2dScrollerMove *vsm;
- View2DScrollers *scrollers;
+ View2DScrollers scrollers;
ARegion *region = CTX_wm_region(C);
View2D *v2d = &region->v2d;
rctf tot_cur_union;
@@ -2032,7 +2035,7 @@ static void scroller_activate_init(bContext *C,
/* 'zone' depends on where mouse is relative to bubble
* - zooming must be allowed on this axis, otherwise, default to pan
*/
- scrollers = UI_view2d_scrollers_calc(v2d, NULL);
+ UI_view2d_scrollers_calc(v2d, NULL, &scrollers);
/* Use a union of 'cur' & 'tot' in case the current view is far outside 'tot'. In this cases
* moving the scroll bars has far too little effect and the view can get stuck T31476. */
@@ -2049,15 +2052,15 @@ static void scroller_activate_init(bContext *C,
/* get 'zone' (i.e. which part of scroller is activated) */
vsm->zone = mouse_in_scroller_handle(
- event->mval[0], v2d->hor.xmin, v2d->hor.xmax, scrollers->hor_min, scrollers->hor_max);
+ event->mval[0], v2d->hor.xmin, v2d->hor.xmax, scrollers.hor_min, scrollers.hor_max);
if ((v2d->keepzoom & V2D_LOCKZOOM_X) && ELEM(vsm->zone, SCROLLHANDLE_MIN, SCROLLHANDLE_MAX)) {
/* default to scroll, as handles not usable */
vsm->zone = SCROLLHANDLE_BAR;
}
- vsm->scrollbarwidth = scrollers->hor_max - scrollers->hor_min;
- vsm->scrollbar_orig = ((scrollers->hor_max + scrollers->hor_min) / 2) + region->winrct.xmin;
+ vsm->scrollbarwidth = scrollers.hor_max - scrollers.hor_min;
+ vsm->scrollbar_orig = ((scrollers.hor_max + scrollers.hor_min) / 2) + region->winrct.xmin;
}
else {
/* vertical scroller - calculate adjustment factor first */
@@ -2069,18 +2072,17 @@ static void scroller_activate_init(bContext *C,
/* get 'zone' (i.e. which part of scroller is activated) */
vsm->zone = mouse_in_scroller_handle(
- event->mval[1], v2d->vert.ymin, v2d->vert.ymax, scrollers->vert_min, scrollers->vert_max);
+ event->mval[1], v2d->vert.ymin, v2d->vert.ymax, scrollers.vert_min, scrollers.vert_max);
if ((v2d->keepzoom & V2D_LOCKZOOM_Y) && ELEM(vsm->zone, SCROLLHANDLE_MIN, SCROLLHANDLE_MAX)) {
/* default to scroll, as handles not usable */
vsm->zone = SCROLLHANDLE_BAR;
}
- vsm->scrollbarwidth = scrollers->vert_max - scrollers->vert_min;
- vsm->scrollbar_orig = ((scrollers->vert_max + scrollers->vert_min) / 2) + region->winrct.ymin;
+ vsm->scrollbarwidth = scrollers.vert_max - scrollers.vert_min;
+ vsm->scrollbar_orig = ((scrollers.vert_max + scrollers.vert_min) / 2) + region->winrct.ymin;
}
- UI_view2d_scrollers_free(scrollers);
ED_region_tag_redraw_no_rebuild(region);
}
@@ -2299,8 +2301,8 @@ static int scroller_activate_invoke(bContext *C, wmOperator *op, const wmEvent *
}
/* zone is also inappropriate if scroller is not visible... */
- if (((vsm->scroller == 'h') && (v2d->scroll & (V2D_SCROLL_HORIZONTAL_FULLR))) ||
- ((vsm->scroller == 'v') && (v2d->scroll & (V2D_SCROLL_VERTICAL_FULLR)))) {
+ if (((vsm->scroller == 'h') && (v2d->scroll & V2D_SCROLL_HORIZONTAL_FULLR)) ||
+ ((vsm->scroller == 'v') && (v2d->scroll & V2D_SCROLL_VERTICAL_FULLR))) {
/* free customdata initialized */
scroller_activate_exit(C, op);
diff --git a/source/blender/editors/io/io_alembic.c b/source/blender/editors/io/io_alembic.c
index fc2c55ffeda..9db1fe31494 100644
--- a/source/blender/editors/io/io_alembic.c
+++ b/source/blender/editors/io/io_alembic.c
@@ -410,8 +410,11 @@ void WM_OT_alembic_export(wmOperatorType *ot)
"Use Subdivision Schema",
"Export meshes using Alembic's subdivision schema");
- RNA_def_boolean(
- ot->srna, "apply_subdiv", 0, "Apply Subdivision Surface", "Export subdivision surfaces as meshes");
+ RNA_def_boolean(ot->srna,
+ "apply_subdiv",
+ 0,
+ "Apply Subdivision Surface",
+ "Export subdivision surfaces as meshes");
RNA_def_boolean(ot->srna,
"curves_as_mesh",
@@ -667,6 +670,7 @@ void WM_OT_alembic_import(wmOperatorType *ot)
ot->name = "Import Alembic";
ot->description = "Load an Alembic archive";
ot->idname = "WM_OT_alembic_import";
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
ot->invoke = wm_alembic_import_invoke;
ot->exec = wm_alembic_import_exec;
diff --git a/source/blender/editors/io/io_collada.c b/source/blender/editors/io/io_collada.c
index 9091e7d8afc..c1a4492994a 100644
--- a/source/blender/editors/io/io_collada.c
+++ b/source/blender/editors/io/io_collada.c
@@ -857,6 +857,7 @@ void WM_OT_collada_import(wmOperatorType *ot)
ot->name = "Import COLLADA";
ot->description = "Load a Collada file";
ot->idname = "WM_OT_collada_import";
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
ot->invoke = WM_operator_filesel;
ot->exec = wm_collada_import_exec;
diff --git a/source/blender/editors/mesh/editmesh_bevel.c b/source/blender/editors/mesh/editmesh_bevel.c
index e94412233ff..d18f5de357f 100644
--- a/source/blender/editors/mesh/editmesh_bevel.c
+++ b/source/blender/editors/mesh/editmesh_bevel.c
@@ -121,7 +121,7 @@ enum {
BEV_MODAL_MARK_SHARP_TOGGLE,
BEV_MODAL_OUTER_MITER_CHANGE,
BEV_MODAL_INNER_MITER_CHANGE,
- BEV_MODAL_CUSTOM_PROFILE_TOGGLE,
+ BEV_MODAL_PROFILE_TYPE_CHANGE,
BEV_MODAL_VERTEX_MESH_CHANGE,
};
@@ -146,7 +146,7 @@ static void edbm_bevel_update_status_text(bContext *C, wmOperator *op)
int available_len = sizeof(buf);
Scene *sce = CTX_data_scene(C);
char offset_str[NUM_STR_REP_LEN];
- const char *mode_str, *omiter_str, *imiter_str, *vmesh_str;
+ const char *mode_str, *omiter_str, *imiter_str, *vmesh_str, *profile_type_str;
PropertyRNA *prop;
#define WM_MODALKEY(_id) \
@@ -170,6 +170,9 @@ static void edbm_bevel_update_status_text(bContext *C, wmOperator *op)
prop = RNA_struct_find_property(op->ptr, "offset_type");
RNA_property_enum_name_gettexted(
C, op->ptr, prop, RNA_property_enum_get(op->ptr, prop), &mode_str);
+ prop = RNA_struct_find_property(op->ptr, "profile_type");
+ RNA_property_enum_name_gettexted(
+ C, op->ptr, prop, RNA_property_enum_get(op->ptr, prop), &profile_type_str);
prop = RNA_struct_find_property(op->ptr, "miter_outer");
RNA_property_enum_name_gettexted(
C, op->ptr, prop, RNA_property_enum_get(op->ptr, prop), &omiter_str);
@@ -195,7 +198,7 @@ static void edbm_bevel_update_status_text(bContext *C, wmOperator *op)
"%s: Harden Normals (%s), "
"%s: Mark Seam (%s), "
"%s: Mark Sharp (%s), "
- "%s: Custom Profile (%s), "
+ "%s: Profile Type (%s), "
"%s: Intersection (%s)"),
WM_MODALKEY(BEV_MODAL_CONFIRM),
WM_MODALKEY(BEV_MODAL_CANCEL),
@@ -221,8 +224,8 @@ static void edbm_bevel_update_status_text(bContext *C, wmOperator *op)
WM_bool_as_string(RNA_boolean_get(op->ptr, "mark_seam")),
WM_MODALKEY(BEV_MODAL_MARK_SHARP_TOGGLE),
WM_bool_as_string(RNA_boolean_get(op->ptr, "mark_sharp")),
- WM_MODALKEY(BEV_MODAL_CUSTOM_PROFILE_TOGGLE),
- WM_bool_as_string(RNA_boolean_get(op->ptr, "use_custom_profile")),
+ WM_MODALKEY(BEV_MODAL_PROFILE_TYPE_CHANGE),
+ profile_type_str,
WM_MODALKEY(BEV_MODAL_VERTEX_MESH_CHANGE),
vmesh_str);
@@ -327,6 +330,7 @@ static bool edbm_bevel_calc(wmOperator *op)
const float offset = get_bevel_offset(op);
const int offset_type = RNA_enum_get(op->ptr, "offset_type");
+ const int profile_type = RNA_enum_get(op->ptr, "profile_type");
const int segments = RNA_int_get(op->ptr, "segments");
const float profile = RNA_float_get(op->ptr, "profile");
const bool vertex_only = RNA_boolean_get(op->ptr, "vertex_only");
@@ -340,7 +344,6 @@ static bool edbm_bevel_calc(wmOperator *op)
const int miter_outer = RNA_enum_get(op->ptr, "miter_outer");
const int miter_inner = RNA_enum_get(op->ptr, "miter_inner");
const float spread = RNA_float_get(op->ptr, "spread");
- const bool use_custom_profile = RNA_boolean_get(op->ptr, "use_custom_profile");
const int vmesh_method = RNA_enum_get(op->ptr, "vmesh_method");
for (uint ob_index = 0; ob_index < opdata->ob_store_len; ob_index++) {
@@ -364,16 +367,17 @@ static bool edbm_bevel_calc(wmOperator *op)
EDBM_op_init(em,
&bmop,
op,
- "bevel geom=%hev offset=%f segments=%i vertex_only=%b offset_type=%i profile=%f "
- "clamp_overlap=%b material=%i loop_slide=%b mark_seam=%b mark_sharp=%b "
- "harden_normals=%b face_strength_mode=%i "
- "miter_outer=%i miter_inner=%i spread=%f smoothresh=%f use_custom_profile=%b "
- "custom_profile=%p vmesh_method=%i",
+ "bevel geom=%hev offset=%f segments=%i vertex_only=%b offset_type=%i "
+ "profile_type=%i profile=%f clamp_overlap=%b material=%i loop_slide=%b "
+ "mark_seam=%b mark_sharp=%b harden_normals=%b face_strength_mode=%i "
+ "miter_outer=%i miter_inner=%i spread=%f smoothresh=%f custom_profile=%p "
+ "vmesh_method=%i",
BM_ELEM_SELECT,
offset,
segments,
vertex_only,
offset_type,
+ profile_type,
profile,
clamp_overlap,
material,
@@ -386,7 +390,6 @@ static bool edbm_bevel_calc(wmOperator *op)
miter_inner,
spread,
me->smoothresh,
- use_custom_profile,
opdata->custom_profile,
vmesh_method);
@@ -681,7 +684,7 @@ wmKeyMap *bevel_modal_keymap(wmKeyConfig *keyconf)
0,
"Change inner miter",
"Cycle through inner miter kinds"},
- {BEV_MODAL_CUSTOM_PROFILE_TOGGLE, "CUSTOM_PROFILE_TOGGLE", 0, "Toggle custom profile", ""},
+ {BEV_MODAL_PROFILE_TYPE_CHANGE, "PROFILE_TYPE_CHANGE", 0, "Cycle through profile types", ""},
{BEV_MODAL_VERTEX_MESH_CHANGE,
"VERTEX_MESH_CHANGE",
0,
@@ -892,9 +895,13 @@ static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event)
break;
}
- case BEV_MODAL_CUSTOM_PROFILE_TOGGLE: {
- bool use_custom_profile = RNA_boolean_get(op->ptr, "use_custom_profile");
- RNA_boolean_set(op->ptr, "use_custom_profile", !use_custom_profile);
+ case BEV_MODAL_PROFILE_TYPE_CHANGE: {
+ int profile_type = RNA_enum_get(op->ptr, "profile_type");
+ profile_type++;
+ if (profile_type > BEVEL_PROFILE_CUSTOM) {
+ profile_type = BEVEL_PROFILE_SUPERELLIPSE;
+ }
+ RNA_enum_set(op->ptr, "profile_type", profile_type);
edbm_bevel_calc(op);
edbm_bevel_update_status_text(C, op);
handled = true;
@@ -938,21 +945,12 @@ static void edbm_bevel_ui(bContext *C, wmOperator *op)
RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
+ int profile_type = RNA_enum_get(&ptr, "profile_type");
+
if (RNA_enum_get(&ptr, "offset_type") == BEVEL_AMT_PERCENT) {
uiItemR(layout, &ptr, "offset_pct", 0, NULL, ICON_NONE);
}
else {
- switch (RNA_enum_get(&ptr, "offset_type")) {
- case BEVEL_AMT_DEPTH:
- offset_name = "Depth";
- break;
- case BEVEL_AMT_WIDTH:
- offset_name = "Width";
- break;
- case BEVEL_AMT_OFFSET:
- offset_name = "Offset";
- break;
- }
prop = RNA_struct_find_property(op->ptr, "offset_type");
RNA_property_enum_name_gettexted(
C, op->ptr, prop, RNA_property_enum_get(op->ptr, prop), &offset_name);
@@ -972,7 +970,14 @@ static void edbm_bevel_ui(bContext *C, wmOperator *op)
uiItemR(col, &ptr, "harden_normals", 0, NULL, ICON_NONE);
uiItemR(layout, &ptr, "segments", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "profile", UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+ if (ELEM(profile_type, BEVEL_PROFILE_SUPERELLIPSE, BEVEL_PROFILE_CUSTOM)) {
+ uiItemR(layout,
+ &ptr,
+ "profile",
+ UI_ITEM_R_SLIDER,
+ (profile_type == BEVEL_PROFILE_SUPERELLIPSE) ? IFACE_("Shape") : IFACE_("Miter Shape"),
+ ICON_NONE);
+ }
uiItemR(layout, &ptr, "material", 0, NULL, ICON_NONE);
uiItemL(layout, "Miter Type:", ICON_NONE);
@@ -990,9 +995,11 @@ static void edbm_bevel_ui(bContext *C, wmOperator *op)
row = uiLayoutRow(layout, true);
uiItemR(row, &ptr, "vmesh_method", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "use_custom_profile", 0, NULL, ICON_NONE);
- if (RNA_boolean_get(&ptr, "use_custom_profile")) {
- /* Get an RNA pointer to ToolSettings to give to the curve profile template code */
+ uiItemL(layout, "Profile Type:", ICON_NONE);
+ row = uiLayoutRow(layout, true);
+ uiItemR(row, &ptr, "profile_type", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ if (profile_type == BEVEL_PROFILE_CUSTOM) {
+ /* Get an RNA pointer to ToolSettings to give to the curve profile template code. */
Scene *scene = CTX_data_scene(C);
RNA_pointer_create(&scene->id, &RNA_ToolSettings, scene->toolsettings, &toolsettings_ptr);
uiTemplateCurveProfile(layout, &toolsettings_ptr, "custom_bevel_profile_preset");
@@ -1012,6 +1019,25 @@ void MESH_OT_bevel(wmOperatorType *ot)
"Depth",
"Amount is perpendicular distance from original edge to bevel face"},
{BEVEL_AMT_PERCENT, "PERCENT", 0, "Percent", "Amount is percent of adjacent edge length"},
+ {BEVEL_AMT_ABSOLUTE,
+ "ABSOLUTE",
+ 0,
+ "Absolute",
+ "Amount is absolute distance along adjacent edge"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ static const EnumPropertyItem prop_profile_type_items[] = {
+ {BEVEL_PROFILE_SUPERELLIPSE,
+ "SUPERELLIPSE",
+ 0,
+ "Superellipse",
+ "The profile can be a concave or convex curve"},
+ {BEVEL_PROFILE_CUSTOM,
+ "CUSTOM",
+ 0,
+ "Custom",
+ "The profile can be any arbitrary path between its endpoints"},
{0, NULL, 0, NULL, NULL},
};
@@ -1068,13 +1094,24 @@ void MESH_OT_bevel(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_GRAB_CURSOR_XY | OPTYPE_BLOCKING;
/* properties */
- RNA_def_enum(
- ot->srna, "offset_type", offset_type_items, 0, "Width Type", "What distance Width measures");
+ RNA_def_enum(ot->srna,
+ "offset_type",
+ offset_type_items,
+ 0,
+ "Width Type",
+ "The method for determining the size of the bevel");
prop = RNA_def_property(ot->srna, "offset", PROP_FLOAT, PROP_DISTANCE);
RNA_def_property_range(prop, 0.0, 1e6);
RNA_def_property_ui_range(prop, 0.0, 100.0, 1, 3);
RNA_def_property_ui_text(prop, "Width", "Bevel amount");
+ RNA_def_enum(ot->srna,
+ "profile_type",
+ prop_profile_type_items,
+ 0,
+ "Profile Type",
+ "The type of shape used to rebuild a beveled section");
+
prop = RNA_def_property(ot->srna, "offset_pct", PROP_FLOAT, PROP_PERCENTAGE);
RNA_def_property_range(prop, 0.0, 100);
RNA_def_property_ui_text(prop, "Width Percent", "Bevel amount for percentage method");
@@ -1161,12 +1198,6 @@ void MESH_OT_bevel(wmOperatorType *ot)
0.0f,
100.0f);
- RNA_def_boolean(ot->srna,
- "use_custom_profile",
- false,
- "Custom Profile",
- "Use a custom profile for the bevel");
-
RNA_def_enum(ot->srna,
"vmesh_method",
vmesh_method_items,
diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c
index 5f5599b53df..369c7735d20 100644
--- a/source/blender/editors/mesh/editmesh_knife.c
+++ b/source/blender/editors/mesh/editmesh_knife.c
@@ -1895,12 +1895,12 @@ static BMFace *knife_find_closest_face(KnifeTool_OpData *kcd,
if (!f) {
if (kcd->is_interactive) {
- /* try to use backbuffer selection method if ray casting failed */
+ /* Try to use back-buffer selection method if ray casting failed. */
f = EDBM_face_find_nearest(&kcd->vc, &dist);
/* cheat for now; just put in the origin instead
* of a true coordinate on the face.
- * This just puts a point 1.0f infront of the view. */
+ * This just puts a point 1.0f in front of the view. */
add_v3_v3v3(co, origin, ray);
}
}
@@ -1908,8 +1908,10 @@ static BMFace *knife_find_closest_face(KnifeTool_OpData *kcd,
return f;
}
-/* find the 2d screen space density of vertices within a radius. used to scale snapping
- * distance for picking edges/verts.*/
+/**
+ * Find the 2d screen space density of vertices within a radius.
+ * Used to scale snapping distance for picking edges/verts.
+ */
static int knife_sample_screen_density(KnifeTool_OpData *kcd, const float radius)
{
BMFace *f;
diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c
index a0a3d24e49a..2cffb3ecdec 100644
--- a/source/blender/editors/mesh/editmesh_tools.c
+++ b/source/blender/editors/mesh/editmesh_tools.c
@@ -4161,7 +4161,7 @@ static Base *mesh_separate_tagged(
CustomData_bmesh_init_pool(&bm_new->pdata, bm_mesh_allocsize_default.totface, BM_FACE);
/* Take into account user preferences for duplicating actions. */
- short dupflag = USER_DUP_MESH | (U.dupflag & USER_DUP_ACT);
+ const eDupli_ID_Flags dupflag = USER_DUP_MESH | (U.dupflag & USER_DUP_ACT);
base_new = ED_object_add_duplicate(bmain, scene, view_layer, base_old, dupflag);
/* normally would call directly after but in this case delay recalc */
@@ -4233,7 +4233,7 @@ static Base *mesh_separate_arrays(Main *bmain,
CustomData_bmesh_init_pool(&bm_new->pdata, faces_len, BM_FACE);
/* Take into account user preferences for duplicating actions. */
- short dupflag = USER_DUP_MESH | (U.dupflag & USER_DUP_ACT);
+ const eDupli_ID_Flags dupflag = USER_DUP_MESH | (U.dupflag & USER_DUP_ACT);
base_new = ED_object_add_duplicate(bmain, scene, view_layer, base_old, dupflag);
/* normally would call directly after but in this case delay recalc */
@@ -7240,8 +7240,11 @@ void MESH_OT_wireframe(wmOperatorType *ot)
/* use 1 rather then 10 for max else dragging the button moves too far */
RNA_def_property_ui_range(prop, 0.0, 1.0, 0.01, 4);
RNA_def_float_distance(ot->srna, "offset", 0.01f, 0.0f, 1e4f, "Offset", "", 0.0f, 10.0f);
- RNA_def_boolean(
- ot->srna, "use_crease", false, "Crease", "Crease hub edges for an improved subdivision surface");
+ RNA_def_boolean(ot->srna,
+ "use_crease",
+ false,
+ "Crease",
+ "Crease hub edges for an improved subdivision surface");
prop = RNA_def_float(
ot->srna, "crease_weight", 0.01f, 0.0f, 1e3f, "Crease weight", "", 0.0f, 1.0f);
RNA_def_property_ui_range(prop, 0.0, 1.0, 0.1, 2);
diff --git a/source/blender/editors/mesh/mesh_data.c b/source/blender/editors/mesh/mesh_data.c
index 51b699acd63..8fce726eff5 100644
--- a/source/blender/editors/mesh/mesh_data.c
+++ b/source/blender/editors/mesh/mesh_data.c
@@ -121,8 +121,15 @@ static void delete_customdata_layer(Mesh *me, CustomDataLayer *layer)
CustomData *data;
int layer_index, tot, n;
- data = mesh_customdata_get_type(
- me, (ELEM(type, CD_MLOOPUV, CD_MLOOPCOL)) ? BM_LOOP : BM_FACE, &tot);
+ char htype = BM_FACE;
+ if (ELEM(type, CD_MLOOPCOL, CD_MLOOPUV)) {
+ htype = BM_LOOP;
+ }
+ else if (ELEM(type, CD_PROP_COLOR)) {
+ htype = BM_VERT;
+ }
+
+ data = mesh_customdata_get_type(me, htype, &tot);
layer_index = CustomData_get_layer_index(data, type);
n = (layer - &data->layers[layer_index]);
BLI_assert(n >= 0 && (n + layer_index) < data->totlayer);
@@ -488,6 +495,117 @@ bool ED_mesh_color_remove_named(Mesh *me, const char *name)
}
}
+/*********************** Sculpt Vertex colors operators ************************/
+
+/* note: keep in sync with ED_mesh_uv_texture_add */
+int ED_mesh_sculpt_color_add(Mesh *me, const char *name, const bool active_set, const bool do_init)
+{
+ BMEditMesh *em;
+ int layernum;
+
+ if (me->edit_mesh) {
+ em = me->edit_mesh;
+
+ layernum = CustomData_number_of_layers(&em->bm->vdata, CD_PROP_COLOR);
+ if (layernum >= MAX_MCOL) {
+ return -1;
+ }
+
+ /* CD_PROP_COLOR */
+ BM_data_layer_add_named(em->bm, &em->bm->vdata, CD_PROP_COLOR, name);
+ /* copy data from active vertex color layer */
+ if (layernum && do_init) {
+ const int layernum_dst = CustomData_get_active_layer(&em->bm->vdata, CD_PROP_COLOR);
+ BM_data_layer_copy(em->bm, &em->bm->vdata, CD_PROP_COLOR, layernum_dst, layernum);
+ }
+ if (active_set || layernum == 0) {
+ CustomData_set_layer_active(&em->bm->vdata, CD_PROP_COLOR, layernum);
+ }
+ }
+ else {
+ layernum = CustomData_number_of_layers(&me->vdata, CD_PROP_COLOR);
+ if (layernum >= MAX_MCOL) {
+ return -1;
+ }
+
+ if (CustomData_has_layer(&me->vdata, CD_PROP_COLOR) && do_init) {
+ MPropCol *color_data = CustomData_get_layer(&me->vdata, CD_PROP_COLOR);
+ CustomData_add_layer_named(
+ &me->vdata, CD_PROP_COLOR, CD_DUPLICATE, color_data, me->totvert, name);
+ }
+ else {
+ CustomData_add_layer_named(&me->vdata, CD_PROP_COLOR, CD_DEFAULT, NULL, me->totvert, name);
+ }
+
+ if (active_set || layernum == 0) {
+ CustomData_set_layer_active(&me->vdata, CD_PROP_COLOR, layernum);
+ }
+
+ BKE_mesh_update_customdata_pointers(me, true);
+ }
+
+ DEG_id_tag_update(&me->id, 0);
+ WM_main_add_notifier(NC_GEOM | ND_DATA, me);
+
+ return layernum;
+}
+
+bool ED_mesh_sculpt_color_ensure(struct Mesh *me, const char *name)
+{
+ BLI_assert(me->edit_mesh == NULL);
+
+ if (me->totvert && !CustomData_has_layer(&me->vdata, CD_PROP_COLOR)) {
+ CustomData_add_layer_named(&me->vdata, CD_PROP_COLOR, CD_DEFAULT, NULL, me->totvert, name);
+ BKE_mesh_update_customdata_pointers(me, true);
+ }
+
+ DEG_id_tag_update(&me->id, 0);
+
+ return (me->mloopcol != NULL);
+}
+
+bool ED_mesh_sculpt_color_remove_index(Mesh *me, const int n)
+{
+ CustomData *vdata = GET_CD_DATA(me, vdata);
+ CustomDataLayer *cdl;
+ int index;
+
+ index = CustomData_get_layer_index_n(vdata, CD_PROP_COLOR, n);
+ cdl = (index == -1) ? NULL : &vdata->layers[index];
+
+ if (!cdl) {
+ return false;
+ }
+
+ delete_customdata_layer(me, cdl);
+ DEG_id_tag_update(&me->id, 0);
+ WM_main_add_notifier(NC_GEOM | ND_DATA, me);
+
+ return true;
+}
+bool ED_mesh_sculpt_color_remove_active(Mesh *me)
+{
+ CustomData *vdata = GET_CD_DATA(me, vdata);
+ const int n = CustomData_get_active_layer(vdata, CD_PROP_COLOR);
+ if (n != -1) {
+ return ED_mesh_sculpt_color_remove_index(me, n);
+ }
+ else {
+ return false;
+ }
+}
+bool ED_mesh_sculpt_color_remove_named(Mesh *me, const char *name)
+{
+ CustomData *vdata = GET_CD_DATA(me, vdata);
+ const int n = CustomData_get_named_layer(vdata, CD_PROP_COLOR, name);
+ if (n != -1) {
+ return ED_mesh_sculpt_color_remove_index(me, n);
+ }
+ else {
+ return false;
+ }
+}
+
/*********************** UV texture operators ************************/
static bool layers_poll(bContext *C)
@@ -619,6 +737,62 @@ void MESH_OT_vertex_color_remove(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
+/*********************** Sculpt Vertex Color Operators ************************/
+
+static int mesh_sculpt_vertex_color_add_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Object *ob = ED_object_context(C);
+ Mesh *me = ob->data;
+
+ if (ED_mesh_sculpt_color_add(me, NULL, true, true) == -1) {
+ return OPERATOR_CANCELLED;
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void MESH_OT_sculpt_vertex_color_add(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Add Sculpt Vertex Color";
+ ot->description = "Add vertex color layer";
+ ot->idname = "MESH_OT_sculpt_vertex_color_add";
+
+ /* api callbacks */
+ ot->poll = layers_poll;
+ ot->exec = mesh_sculpt_vertex_color_add_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+static int mesh_sculpt_vertex_color_remove_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Object *ob = ED_object_context(C);
+ Mesh *me = ob->data;
+
+ if (!ED_mesh_sculpt_color_remove_active(me)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void MESH_OT_sculpt_vertex_color_remove(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Remove Sculpt Vertex Color";
+ ot->description = "Remove vertex color layer";
+ ot->idname = "MESH_OT_sculpt_vertex_color_remove";
+
+ /* api callbacks */
+ ot->exec = mesh_sculpt_vertex_color_remove_exec;
+ ot->poll = layers_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
/* *** CustomData clear functions, we need an operator for each *** */
static int mesh_customdata_clear_exec__internal(bContext *C, char htype, int type)
diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h
index 5e70069456b..bebad312454 100644
--- a/source/blender/editors/mesh/mesh_intern.h
+++ b/source/blender/editors/mesh/mesh_intern.h
@@ -267,6 +267,8 @@ void MESH_OT_uv_texture_add(struct wmOperatorType *ot);
void MESH_OT_uv_texture_remove(struct wmOperatorType *ot);
void MESH_OT_vertex_color_add(struct wmOperatorType *ot);
void MESH_OT_vertex_color_remove(struct wmOperatorType *ot);
+void MESH_OT_sculpt_vertex_color_add(struct wmOperatorType *ot);
+void MESH_OT_sculpt_vertex_color_remove(struct wmOperatorType *ot);
/* no create_mask yet */
void MESH_OT_customdata_mask_clear(struct wmOperatorType *ot);
void MESH_OT_customdata_skin_add(struct wmOperatorType *ot);
diff --git a/source/blender/editors/mesh/mesh_ops.c b/source/blender/editors/mesh/mesh_ops.c
index c52a5956ac4..ad1e91a57c0 100644
--- a/source/blender/editors/mesh/mesh_ops.c
+++ b/source/blender/editors/mesh/mesh_ops.c
@@ -155,6 +155,8 @@ void ED_operatortypes_mesh(void)
WM_operatortype_append(MESH_OT_uv_texture_remove);
WM_operatortype_append(MESH_OT_vertex_color_add);
WM_operatortype_append(MESH_OT_vertex_color_remove);
+ WM_operatortype_append(MESH_OT_sculpt_vertex_color_add);
+ WM_operatortype_append(MESH_OT_sculpt_vertex_color_remove);
WM_operatortype_append(MESH_OT_customdata_mask_clear);
WM_operatortype_append(MESH_OT_customdata_skin_add);
WM_operatortype_append(MESH_OT_customdata_skin_clear);
@@ -278,8 +280,8 @@ void ED_operatormacros_mesh(void)
RNA_boolean_set(otmacro->ptr, "mirror", false);
ot = WM_operatortype_append_macro(
- "MESH_OT_extrude_region_dissolve_move_intersect",
- "Extrude, Dissolve, Move and Intersect",
+ "MESH_OT_extrude_manifold",
+ "Extrude Manifold",
"Extrude, dissolves edges whose faces form a flat surface and intersect new edges",
OPTYPE_UNDO | OPTYPE_REGISTER);
otmacro = WM_operatortype_macro_define(ot, "MESH_OT_extrude_region");
diff --git a/source/blender/editors/mesh/meshtools.c b/source/blender/editors/mesh/meshtools.c
index 917bbe61e3d..1bdf2ede22a 100644
--- a/source/blender/editors/mesh/meshtools.c
+++ b/source/blender/editors/mesh/meshtools.c
@@ -297,7 +297,7 @@ static void join_mesh_single(Depsgraph *depsgraph,
*mpoly_pp += me->totpoly;
}
-int join_mesh_exec(bContext *C, wmOperator *op)
+int ED_mesh_join_objects_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
@@ -706,12 +706,14 @@ int join_mesh_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
-/*********************** JOIN AS SHAPES ***************************/
+/* -------------------------------------------------------------------- */
+/** \name Join as Shapes
+ * \{ */
/* Append selected meshes vertex locations as shapes of the active mesh,
* return 0 if no join is made (error) and 1 of the join is done */
-int join_mesh_shapes_exec(bContext *C, wmOperator *op)
+int ED_mesh_shapes_join_objects_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
@@ -796,6 +798,8 @@ int join_mesh_shapes_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
+/** \} */
+
/* -------------------------------------------------------------------- */
/** \name Mesh Topology Mirror API
* \{ */
diff --git a/source/blender/editors/metaball/mball_edit.c b/source/blender/editors/metaball/mball_edit.c
index a25175510cd..094011ebef1 100644
--- a/source/blender/editors/metaball/mball_edit.c
+++ b/source/blender/editors/metaball/mball_edit.c
@@ -58,6 +58,10 @@
#include "mball_intern.h"
+/* -------------------------------------------------------------------- */
+/** \name Edit Mode Functions
+ * \{ */
+
/* This function is used to free all MetaElems from MetaBall */
void ED_mball_editmball_free(Object *obedit)
{
@@ -93,9 +97,36 @@ void ED_mball_editmball_load(Object *UNUSED(obedit))
{
}
-/* Add metaelem primitive to metaball object (which is in edit mode) */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Selection
+ * \{ */
+
+bool ED_mball_deselect_all_multi(bContext *C)
+{
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ ViewContext vc;
+ ED_view3d_viewcontext_init(C, &vc, depsgraph);
+ uint bases_len = 0;
+ Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data(
+ vc.view_layer, vc.v3d, &bases_len);
+ bool changed_multi = BKE_mball_deselect_all_multi_ex(bases, bases_len);
+ MEM_freeN(bases);
+ return changed_multi;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Add Meta Primitive Utility
+ * \{ */
+
+/**
+ * Add meta-element primitive to meta-ball object (which is in edit mode).
+ */
MetaElem *ED_mball_add_primitive(
- bContext *UNUSED(C), Object *obedit, float mat[4][4], float dia, int type)
+ bContext *UNUSED(C), Object *obedit, bool obedit_is_new, float mat[4][4], float dia, int type)
{
MetaBall *mball = (MetaBall *)obedit->data;
MetaElem *ml;
@@ -109,16 +140,28 @@ MetaElem *ED_mball_add_primitive(
ml = BKE_mball_element_add(mball, type);
ml->rad *= dia;
- mball->wiresize *= dia;
- mball->rendersize *= dia;
+
+ if (obedit_is_new) {
+ mball->wiresize *= dia;
+ mball->rendersize *= dia;
+ }
copy_v3_v3(&ml->x, mat[3]);
+ /* MB_ELIPSOID works differently (intentional?). Whatever the case,
+ * on testing this needs to be skipped otherwise it doesn't behave like other types. */
+ if (type != MB_ELIPSOID) {
+ mul_v3_fl(&ml->expx, dia);
+ }
ml->flag |= SELECT;
mball->lastelem = ml;
return ml;
}
-/***************************** Select/Deselect operator *****************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Select/Deselect Operator
+ * \{ */
/* Select or deselect all MetaElements */
static int mball_select_all_exec(bContext *C, wmOperator *op)
@@ -175,8 +218,11 @@ void MBALL_OT_select_all(wmOperatorType *ot)
WM_operator_properties_select_all(ot);
}
+/** \} */
+
/* -------------------------------------------------------------------- */
-/* Select Similar */
+/** \name Select Similar Operator
+ * \{ */
enum {
SIMMBALL_TYPE = 1,
@@ -428,9 +474,12 @@ void MBALL_OT_select_similar(wmOperatorType *ot)
RNA_def_float(ot->srna, "threshold", 0.1, 0.0, FLT_MAX, "Threshold", "", 0.01, 1.0);
}
-/***************************** Select random operator *****************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Select Random Operator
+ * \{ */
-/* Random metaball selection */
static int select_random_metaelems_exec(bContext *C, wmOperator *op)
{
const bool select = (RNA_enum_get(op->ptr, "action") == SEL_SELECT);
@@ -494,7 +543,11 @@ void MBALL_OT_select_random_metaelems(struct wmOperatorType *ot)
WM_operator_properties_select_random(ot);
}
-/***************************** Duplicate operator *****************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Duplicate Meta-Ball Operator
+ * \{ */
/* Duplicate selected MetaElements */
static int duplicate_metaelems_exec(bContext *C, wmOperator *UNUSED(op))
@@ -546,9 +599,14 @@ void MBALL_OT_duplicate_metaelems(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/***************************** Delete operator *****************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Delete Meta-Ball Operator
+ *
+ * Delete all selected MetaElems (not MetaBall).
+ * \{ */
-/* Delete all selected MetaElems (not MetaBall) */
static int delete_metaelems_exec(bContext *C, wmOperator *UNUSED(op))
{
ViewLayer *view_layer = CTX_data_view_layer(C);
@@ -601,9 +659,12 @@ void MBALL_OT_delete_metaelems(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/***************************** Hide operator *****************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Hide Meta-Elements Operator
+ * \{ */
-/* Hide selected MetaElems */
static int hide_metaelems_exec(bContext *C, wmOperator *op)
{
Object *obedit = CTX_data_edit_object(C);
@@ -646,9 +707,12 @@ void MBALL_OT_hide_metaelems(wmOperatorType *ot)
ot->srna, "unselected", false, "Unselected", "Hide unselected rather than selected");
}
-/***************************** Unhide operator *****************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Un-Hide Meta-Elements Operator
+ * \{ */
-/* Unhide all edited MetaElems */
static int reveal_metaelems_exec(bContext *C, wmOperator *op)
{
Object *obedit = CTX_data_edit_object(C);
@@ -689,6 +753,12 @@ void MBALL_OT_reveal_metaelems(wmOperatorType *ot)
RNA_def_boolean(ot->srna, "select", true, "Select", "");
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Select Pick Utility
+ * \{ */
+
/* Select MetaElement with mouse click (user can select radius circle or
* stiffness circle) */
bool ED_mball_select_pick(bContext *C, const int mval[2], bool extend, bool deselect, bool toggle)
@@ -740,7 +810,7 @@ bool ED_mball_select_pick(bContext *C, const int mval[2], bool extend, bool dese
continue;
}
- if (metaelem_id != (hitresult & 0xFFFF0000 & ~(MBALLSEL_ANY))) {
+ if (metaelem_id != (hitresult & 0xFFFF0000 & ~MBALLSEL_ANY)) {
continue;
}
@@ -831,15 +901,4 @@ bool ED_mball_select_pick(bContext *C, const int mval[2], bool extend, bool dese
return false;
}
-bool ED_mball_deselect_all_multi(bContext *C)
-{
- Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- ViewContext vc;
- ED_view3d_viewcontext_init(C, &vc, depsgraph);
- uint bases_len = 0;
- Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data(
- vc.view_layer, vc.v3d, &bases_len);
- bool changed_multi = BKE_mball_deselect_all_multi_ex(bases, bases_len);
- MEM_freeN(bases);
- return changed_multi;
-}
+/** \} */
diff --git a/source/blender/editors/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt
index fb273cf49a8..953ef8114f9 100644
--- a/source/blender/editors/object/CMakeLists.txt
+++ b/source/blender/editors/object/CMakeLists.txt
@@ -91,8 +91,4 @@ if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
-if(WITH_NEW_OBJECT_TYPES)
- add_definitions(-DWITH_NEW_OBJECT_TYPES)
-endif()
-
blender_add_lib(bf_editor_object "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c
index 8289f52b0c8..8073d87080f 100644
--- a/source/blender/editors/object/object_add.c
+++ b/source/blender/editors/object/object_add.c
@@ -65,6 +65,7 @@
#include "BKE_effect.h"
#include "BKE_font.h"
#include "BKE_gpencil_curve.h"
+#include "BKE_gpencil_geom.h"
#include "BKE_hair.h"
#include "BKE_key.h"
#include "BKE_lattice.h"
@@ -96,6 +97,8 @@
#include "RNA_define.h"
#include "RNA_enum_types.h"
+#include "UI_interface.h"
+
#include "WM_api.h"
#include "WM_types.h"
@@ -857,7 +860,7 @@ static int object_metaball_add_exec(bContext *C, wmOperator *op)
* we want to pass in 1 so other values such as resolution are scaled by 1.0. */
dia = RNA_float_get(op->ptr, "radius") / 2;
- ED_mball_add_primitive(C, obedit, mat, dia, RNA_enum_get(op->ptr, "type"));
+ ED_mball_add_primitive(C, obedit, newob, mat, dia, RNA_enum_get(op->ptr, "type"));
/* userdef */
if (newob && !enter_editmode) {
@@ -1508,6 +1511,14 @@ void OBJECT_OT_speaker_add(wmOperatorType *ot)
/** \name Add Hair Operator
* \{ */
+static bool object_hair_add_poll(bContext *C)
+{
+ if (!U.experimental.use_new_hair_type) {
+ return false;
+ }
+ return ED_operator_objectmode(C);
+}
+
static int object_hair_add_exec(bContext *C, wmOperator *op)
{
ushort local_view_bits;
@@ -1531,7 +1542,7 @@ void OBJECT_OT_hair_add(wmOperatorType *ot)
/* api callbacks */
ot->exec = object_hair_add_exec;
- ot->poll = ED_operator_objectmode;
+ ot->poll = object_hair_add_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -1545,6 +1556,14 @@ void OBJECT_OT_hair_add(wmOperatorType *ot)
/** \name Add Point Cloud Operator
* \{ */
+static bool object_pointcloud_add_poll(bContext *C)
+{
+ if (!U.experimental.use_new_particle_system) {
+ return false;
+ }
+ return ED_operator_objectmode(C);
+}
+
static int object_pointcloud_add_exec(bContext *C, wmOperator *op)
{
ushort local_view_bits;
@@ -1568,7 +1587,7 @@ void OBJECT_OT_pointcloud_add(wmOperatorType *ot)
/* api callbacks */
ot->exec = object_pointcloud_add_exec;
- ot->poll = ED_operator_objectmode;
+ ot->poll = object_pointcloud_add_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -2129,11 +2148,11 @@ void OBJECT_OT_duplicates_make_real(wmOperatorType *ot)
static const EnumPropertyItem convert_target_items[] = {
{OB_CURVE, "CURVE", ICON_OUTLINER_OB_CURVE, "Curve from Mesh/Text", ""},
{OB_MESH, "MESH", ICON_OUTLINER_OB_MESH, "Mesh from Curve/Meta/Surf/Text", ""},
- {OB_GPENCIL, "GPENCIL", ICON_OUTLINER_OB_GREASEPENCIL, "Grease Pencil from Curve", ""},
+ {OB_GPENCIL, "GPENCIL", ICON_OUTLINER_OB_GREASEPENCIL, "Grease Pencil from Curve/Mesh", ""},
{0, NULL, 0, NULL, NULL},
};
-static void convert_ensure_curve_cache(Depsgraph *depsgraph, Scene *scene, Object *ob)
+static void object_data_convert_ensure_curve_cache(Depsgraph *depsgraph, Scene *scene, Object *ob)
{
if (ob->runtime.curve_cache == NULL) {
/* Force creation. This is normally not needed but on operator
@@ -2152,7 +2171,7 @@ static void convert_ensure_curve_cache(Depsgraph *depsgraph, Scene *scene, Objec
}
}
-static void curvetomesh(Main *bmain, Depsgraph *depsgraph, Object *ob)
+static void object_data_convert_curve_to_mesh(Main *bmain, Depsgraph *depsgraph, Object *ob)
{
Object *object_eval = DEG_get_evaluated_object(depsgraph, ob);
Curve *curve = ob->data;
@@ -2185,17 +2204,22 @@ static void curvetomesh(Main *bmain, Depsgraph *depsgraph, Object *ob)
}
}
-static bool convert_poll(bContext *C)
+static bool object_convert_poll(bContext *C)
{
Scene *scene = CTX_data_scene(C);
Base *base_act = CTX_data_active_base(C);
Object *obact = base_act ? base_act->object : NULL;
- return (!ID_IS_LINKED(scene) && obact && (BKE_object_is_in_editmode(obact) == false) &&
- (base_act->flag & BASE_SELECTED) && !ID_IS_LINKED(obact));
+ if (obact == NULL || obact->data == NULL || ID_IS_LINKED(obact) ||
+ ID_IS_OVERRIDE_LIBRARY(obact) || ID_IS_OVERRIDE_LIBRARY(obact->data)) {
+ return false;
+ }
+
+ return (!ID_IS_LINKED(scene) && (BKE_object_is_in_editmode(obact) == false) &&
+ (base_act->flag & BASE_SELECTED));
}
-/* Helper for convert_exec */
+/* Helper for object_convert_exec */
static Base *duplibase_for_convert(
Main *bmain, Depsgraph *depsgraph, Scene *scene, ViewLayer *view_layer, Base *base, Object *ob)
{
@@ -2230,7 +2254,7 @@ static Base *duplibase_for_convert(
* time we need to duplicate an object to convert it. Even worse, this is not 100% correct, since
* we do not yet have duplicated obdata.
* However, that is a safe solution for now. Proper, longer-term solution is to refactor
- * convert_exec to:
+ * object_convert_exec to:
* - duplicate all data it needs to in a first loop.
* - do a single update.
* - convert data in a second loop. */
@@ -2248,7 +2272,7 @@ static Base *duplibase_for_convert(
return basen;
}
-static int convert_exec(bContext *C, wmOperator *op)
+static int object_convert_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
@@ -2261,9 +2285,16 @@ static int convert_exec(bContext *C, wmOperator *op)
Nurb *nu;
MetaBall *mb;
Mesh *me;
- Object *gpencil_ob = NULL;
+ Object *ob_gpencil = NULL;
const short target = RNA_enum_get(op->ptr, "target");
bool keep_original = RNA_boolean_get(op->ptr, "keep_original");
+
+ const float angle = RNA_float_get(op->ptr, "angle");
+ const int thickness = RNA_int_get(op->ptr, "thickness");
+ const bool use_seams = RNA_boolean_get(op->ptr, "seams");
+ const bool use_faces = RNA_boolean_get(op->ptr, "faces");
+ const float offset = RNA_float_get(op->ptr, "offset");
+
int a, mballConverted = 0;
bool gpencilConverted = false;
@@ -2375,6 +2406,54 @@ static int convert_exec(bContext *C, wmOperator *op)
ED_rigidbody_object_remove(bmain, scene, newob);
}
}
+ else if (ob->type == OB_MESH && target == OB_GPENCIL) {
+ ob->flag |= OB_DONE;
+
+ /* Create a new grease pencil object and copy transformations. */
+ ushort local_view_bits = (v3d && v3d->localvd) ? v3d->local_view_uuid : 0;
+ float loc[3], size[3], rot[3][3], eul[3];
+ float matrix[4][4];
+ mat4_to_loc_rot_size(loc, rot, size, ob->obmat);
+ mat3_to_eul(eul, rot);
+
+ ob_gpencil = ED_gpencil_add_object(C, loc, local_view_bits);
+ copy_v3_v3(ob_gpencil->loc, loc);
+ copy_v3_v3(ob_gpencil->rot, eul);
+ copy_v3_v3(ob_gpencil->scale, size);
+ unit_m4(matrix);
+ /* Set object in 3D mode. */
+ bGPdata *gpd = (bGPdata *)ob_gpencil->data;
+ gpd->draw_mode = GP_DRAWMODE_3D;
+
+ BKE_gpencil_convert_mesh(bmain,
+ depsgraph,
+ scene,
+ ob_gpencil,
+ ob,
+ angle,
+ thickness,
+ offset,
+ matrix,
+ 0,
+ use_seams,
+ use_faces);
+ gpencilConverted = true;
+
+ /* Remove unused materials. */
+ int actcol = ob_gpencil->actcol;
+ for (int slot = 1; slot <= ob_gpencil->totcol; slot++) {
+ while (slot <= ob_gpencil->totcol &&
+ !BKE_object_material_slot_used(ob_gpencil->data, slot)) {
+ ob_gpencil->actcol = slot;
+ BKE_object_material_slot_remove(CTX_data_main(C), ob_gpencil);
+
+ if (actcol >= slot) {
+ actcol--;
+ }
+ }
+ }
+ ob_gpencil->actcol = actcol;
+ }
else if (ob->type == OB_MESH) {
ob->flag |= OB_DONE;
@@ -2470,7 +2549,7 @@ static int convert_exec(bContext *C, wmOperator *op)
if (target == OB_MESH) {
/* No assumption should be made that the resulting objects is a mesh, as conversion can
* fail. */
- curvetomesh(bmain, depsgraph, newob);
+ object_data_convert_curve_to_mesh(bmain, depsgraph, newob);
/* meshes doesn't use displist */
BKE_object_free_curve_cache(newob);
}
@@ -2495,7 +2574,7 @@ static int convert_exec(bContext *C, wmOperator *op)
/* No assumption should be made that the resulting objects is a mesh, as conversion can
* fail. */
- curvetomesh(bmain, depsgraph, newob);
+ object_data_convert_curve_to_mesh(bmain, depsgraph, newob);
/* meshes doesn't use displist */
BKE_object_free_curve_cache(newob);
}
@@ -2509,10 +2588,10 @@ static int convert_exec(bContext *C, wmOperator *op)
* Nurbs Surface are not supported.
*/
ushort local_view_bits = (v3d && v3d->localvd) ? v3d->local_view_uuid : 0;
- gpencil_ob = ED_gpencil_add_object(C, ob->loc, local_view_bits);
- copy_v3_v3(gpencil_ob->rot, ob->rot);
- copy_v3_v3(gpencil_ob->scale, ob->scale);
- BKE_gpencil_convert_curve(bmain, scene, gpencil_ob, ob, false, false, true);
+ ob_gpencil = ED_gpencil_add_object(C, ob->loc, local_view_bits);
+ copy_v3_v3(ob_gpencil->rot, ob->rot);
+ copy_v3_v3(ob_gpencil->scale, ob->scale);
+ BKE_gpencil_convert_curve(bmain, scene, ob_gpencil, ob, false, false, true);
gpencilConverted = true;
}
}
@@ -2549,7 +2628,7 @@ static int convert_exec(bContext *C, wmOperator *op)
}
}
- convert_ensure_curve_cache(depsgraph, scene, baseob);
+ object_data_convert_ensure_curve_cache(depsgraph, scene, baseob);
BKE_mesh_from_metaball(&baseob->runtime.curve_cache->disp, newob->data);
if (obact->type == OB_MBALL) {
@@ -2618,12 +2697,12 @@ static int convert_exec(bContext *C, wmOperator *op)
}
FOREACH_SCENE_OBJECT_END;
}
- /* Remove curves converted to Grease Pencil object. */
+ /* Remove curves and meshes converted to Grease Pencil object. */
if (gpencilConverted) {
- FOREACH_SCENE_OBJECT_BEGIN (scene, ob_curve) {
- if (ob_curve->type == OB_CURVE) {
- if (ob_curve->flag & OB_DONE) {
- ED_object_base_free_and_unlink(bmain, scene, ob_curve);
+ FOREACH_SCENE_OBJECT_BEGIN (scene, ob_delete) {
+ if ((ob_delete->type == OB_CURVE) || (ob_delete->type == OB_MESH)) {
+ if (ob_delete->flag & OB_DONE) {
+ ED_object_base_free_and_unlink(bmain, scene, ob_delete);
}
}
}
@@ -2652,8 +2731,28 @@ static int convert_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
+static void object_convert_ui(bContext *UNUSED(C), wmOperator *op)
+{
+ uiLayout *layout = op->layout;
+ PointerRNA ptr;
+
+ RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
+ uiItemR(layout, &ptr, "target", 0, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "keep_original", 0, NULL, ICON_NONE);
+
+ if (RNA_enum_get(&ptr, "target") == OB_GPENCIL) {
+ uiItemR(layout, &ptr, "thickness", 0, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "angle", 0, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "offset", 0, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "seams", 0, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "faces", 0, NULL, ICON_NONE);
+ }
+}
+
void OBJECT_OT_convert(wmOperatorType *ot)
{
+ PropertyRNA *prop;
+
/* identifiers */
ot->name = "Convert to";
ot->description = "Convert selected objects to another type";
@@ -2661,8 +2760,9 @@ void OBJECT_OT_convert(wmOperatorType *ot)
/* api callbacks */
ot->invoke = WM_menu_invoke;
- ot->exec = convert_exec;
- ot->poll = convert_poll;
+ ot->exec = object_convert_exec;
+ ot->poll = object_convert_poll;
+ ot->ui = object_convert_ui;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -2675,6 +2775,31 @@ void OBJECT_OT_convert(wmOperatorType *ot)
0,
"Keep Original",
"Keep original objects instead of replacing them");
+
+ prop = RNA_def_float_rotation(ot->srna,
+ "angle",
+ 0,
+ NULL,
+ DEG2RADF(0.0f),
+ DEG2RADF(180.0f),
+ "Threshold Angle",
+ "Threshold to determine ends of the strokes",
+ DEG2RADF(0.0f),
+ DEG2RADF(180.0f));
+ RNA_def_property_float_default(prop, DEG2RADF(70.0f));
+
+ RNA_def_int(ot->srna, "thickness", 5, 1, 100, "Thickness", "", 1, 100);
+ RNA_def_boolean(ot->srna, "seams", 0, "Only Seam Edges", "Convert only seam edges");
+ RNA_def_boolean(ot->srna, "faces", 1, "Export Faces", "Export faces as filled strokes");
+ RNA_def_float_distance(ot->srna,
+ "offset",
+ 0.01f,
+ 0.0,
+ OBJECT_ADD_SIZE_MAXF,
+ "Stroke Offset",
+ "Offset strokes from fill",
+ 0.0,
+ 100.00);
}
/** \} */
@@ -2693,8 +2818,12 @@ void OBJECT_OT_convert(wmOperatorType *ot)
/* used below, assumes id.new is correct */
/* leaves selection of base/object unaltered */
/* Does set ID->newid pointers. */
-static Base *object_add_duplicate_internal(
- Main *bmain, Scene *scene, ViewLayer *view_layer, Object *ob, int dupflag)
+static Base *object_add_duplicate_internal(Main *bmain,
+ Scene *scene,
+ ViewLayer *view_layer,
+ Object *ob,
+ const eDupli_ID_Flags dupflag,
+ const eLibIDDuplicateFlags duplicate_options)
{
Base *base, *basen = NULL;
Object *obn;
@@ -2703,7 +2832,7 @@ static Base *object_add_duplicate_internal(
/* nothing? */
}
else {
- obn = ID_NEW_SET(ob, BKE_object_duplicate(bmain, ob, dupflag));
+ obn = ID_NEW_SET(ob, BKE_object_duplicate(bmain, ob, dupflag, duplicate_options));
DEG_id_tag_update(&obn->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
base = BKE_view_layer_base_find(view_layer, ob);
@@ -2742,12 +2871,13 @@ static Base *object_add_duplicate_internal(
* note: caller must do DAG_relations_tag_update(bmain);
* this is not done automatic since we may duplicate many objects in a batch */
Base *ED_object_add_duplicate(
- Main *bmain, Scene *scene, ViewLayer *view_layer, Base *base, int dupflag)
+ Main *bmain, Scene *scene, ViewLayer *view_layer, Base *base, const eDupli_ID_Flags dupflag)
{
Base *basen;
Object *ob;
- basen = object_add_duplicate_internal(bmain, scene, view_layer, base->object, dupflag);
+ basen = object_add_duplicate_internal(
+ bmain, scene, view_layer, base->object, dupflag, LIB_ID_DUPLICATE_IS_SUBPROCESS);
if (basen == NULL) {
return NULL;
}
@@ -2775,10 +2905,11 @@ static int duplicate_exec(bContext *C, wmOperator *op)
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
const bool linked = RNA_boolean_get(op->ptr, "linked");
- int dupflag = (linked) ? 0 : U.dupflag;
+ const eDupli_ID_Flags dupflag = (linked) ? 0 : (eDupli_ID_Flags)U.dupflag;
CTX_DATA_BEGIN (C, Base *, base, selected_bases) {
- Base *basen = object_add_duplicate_internal(bmain, scene, view_layer, base->object, dupflag);
+ Base *basen = object_add_duplicate_internal(
+ bmain, scene, view_layer, base->object, dupflag, 0);
/* note that this is safe to do with this context iterator,
* the list is made in advance */
@@ -2849,7 +2980,7 @@ void OBJECT_OT_duplicate(wmOperatorType *ot)
* Use for drag & drop.
* \{ */
-static int add_named_exec(bContext *C, wmOperator *op)
+static int object_add_named_exec(bContext *C, wmOperator *op)
{
wmWindow *win = CTX_wm_window(C);
const wmEvent *event = win ? win->eventstate : NULL;
@@ -2859,7 +2990,7 @@ static int add_named_exec(bContext *C, wmOperator *op)
Base *basen;
Object *ob;
const bool linked = RNA_boolean_get(op->ptr, "linked");
- int dupflag = (linked) ? 0 : U.dupflag;
+ const eDupli_ID_Flags dupflag = (linked) ? 0 : (eDupli_ID_Flags)U.dupflag;
char name[MAX_ID_NAME - 2];
/* find object, create fake base */
@@ -2872,7 +3003,7 @@ static int add_named_exec(bContext *C, wmOperator *op)
}
/* prepare dupli */
- basen = object_add_duplicate_internal(bmain, scene, view_layer, ob, dupflag);
+ basen = object_add_duplicate_internal(bmain, scene, view_layer, ob, dupflag, 0);
if (basen == NULL) {
BKE_report(op->reports, RPT_ERROR, "Object could not be duplicated");
@@ -2912,7 +3043,7 @@ void OBJECT_OT_add_named(wmOperatorType *ot)
ot->idname = "OBJECT_OT_add_named";
/* api callbacks */
- ot->exec = add_named_exec;
+ ot->exec = object_add_named_exec;
ot->poll = ED_operator_objectmode;
/* flags */
@@ -2933,23 +3064,24 @@ void OBJECT_OT_add_named(wmOperatorType *ot)
*
* \{ */
-static bool join_poll(bContext *C)
+static bool object_join_poll(bContext *C)
{
Object *ob = CTX_data_active_object(C);
- if (!ob || ID_IS_LINKED(ob)) {
- return 0;
+ if (ob == NULL || ob->data == NULL || ID_IS_LINKED(ob) || ID_IS_OVERRIDE_LIBRARY(ob) ||
+ ID_IS_OVERRIDE_LIBRARY(ob->data)) {
+ return false;
}
if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_ARMATURE, OB_GPENCIL)) {
return ED_operator_screenactive(C);
}
else {
- return 0;
+ return false;
}
}
-static int join_exec(bContext *C, wmOperator *op)
+static int object_join_exec(bContext *C, wmOperator *op)
{
Object *ob = CTX_data_active_object(C);
@@ -2970,13 +3102,13 @@ static int join_exec(bContext *C, wmOperator *op)
}
if (ob->type == OB_MESH) {
- return join_mesh_exec(C, op);
+ return ED_mesh_join_objects_exec(C, op);
}
else if (ELEM(ob->type, OB_CURVE, OB_SURF)) {
- return join_curve_exec(C, op);
+ return ED_curve_join_objects_exec(C, op);
}
else if (ob->type == OB_ARMATURE) {
- return join_armature_exec(C, op);
+ return ED_armature_join_objects_exec(C, op);
}
else if (ob->type == OB_GPENCIL) {
return ED_gpencil_join_objects_exec(C, op);
@@ -2993,8 +3125,8 @@ void OBJECT_OT_join(wmOperatorType *ot)
ot->idname = "OBJECT_OT_join";
/* api callbacks */
- ot->exec = join_exec;
- ot->poll = join_poll;
+ ot->exec = object_join_exec;
+ ot->poll = object_join_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -3010,8 +3142,9 @@ static bool join_shapes_poll(bContext *C)
{
Object *ob = CTX_data_active_object(C);
- if (!ob || ID_IS_LINKED(ob)) {
- return 0;
+ if (ob == NULL || ob->data == NULL || ID_IS_LINKED(ob) || ID_IS_OVERRIDE_LIBRARY(ob) ||
+ ID_IS_OVERRIDE_LIBRARY(ob->data)) {
+ return false;
}
/* only meshes supported at the moment */
@@ -3019,7 +3152,7 @@ static bool join_shapes_poll(bContext *C)
return ED_operator_screenactive(C);
}
else {
- return 0;
+ return false;
}
}
@@ -3037,7 +3170,7 @@ static int join_shapes_exec(bContext *C, wmOperator *op)
}
if (ob->type == OB_MESH) {
- return join_mesh_shapes_exec(C, op);
+ return ED_mesh_shapes_join_objects_exec(C, op);
}
return OPERATOR_CANCELLED;
diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c
index a2d33ffe413..5746480e3f8 100644
--- a/source/blender/editors/object/object_constraint.c
+++ b/source/blender/editors/object/object_constraint.c
@@ -1557,6 +1557,77 @@ void CONSTRAINT_OT_move_up(wmOperatorType *ot)
/** \} */
/* ------------------------------------------------------------------- */
+/** \name Move Constraint To Index Operator
+ * \{ */
+
+static int constraint_move_to_index_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = ED_object_active_context(C);
+ bConstraint *con = edit_constraint_property_get(op, ob, 0);
+
+ int new_index = RNA_int_get(op->ptr, "index");
+ if (new_index < 0) {
+ new_index = 0;
+ }
+
+ if (con) {
+ ListBase *conlist = ED_object_constraint_list_from_constraint(ob, con, NULL);
+ int current_index = BLI_findindex(conlist, con);
+ BLI_assert(current_index >= 0);
+
+ BLI_listbase_link_move(conlist, con, new_index - current_index);
+
+ WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, ob);
+
+ return OPERATOR_FINISHED;
+ }
+
+ return OPERATOR_CANCELLED;
+}
+
+static int constraint_move_to_index_invoke(bContext *C,
+ wmOperator *op,
+ const wmEvent *UNUSED(event))
+{
+ if (edit_constraint_invoke_properties(C, op)) {
+ return constraint_move_to_index_exec(C, op);
+ }
+ else {
+ return OPERATOR_CANCELLED;
+ }
+}
+
+void CONSTRAINT_OT_move_to_index(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Move Constraint To Index";
+ ot->idname = "CONSTRAINT_OT_move_to_index";
+ ot->description =
+ "Change the constraint's position in the list so it evaluates after the set number of "
+ "others";
+
+ /* callbacks */
+ ot->exec = constraint_move_to_index_exec;
+ ot->invoke = constraint_move_to_index_invoke;
+ ot->poll = edit_constraint_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+ edit_constraint_properties(ot);
+ RNA_def_int(ot->srna,
+ "index",
+ 0,
+ 0,
+ INT_MAX,
+ "Index",
+ "The index to move the constraint to",
+ 0,
+ INT_MAX);
+}
+
+/** \} */
+
+/* ------------------------------------------------------------------- */
/** \name Clear Pose Constraints Operator
* \{ */
diff --git a/source/blender/editors/object/object_data_transfer.c b/source/blender/editors/object/object_data_transfer.c
index 274cd31406c..0df33255c34 100644
--- a/source/blender/editors/object/object_data_transfer.c
+++ b/source/blender/editors/object/object_data_transfer.c
@@ -381,7 +381,7 @@ static bool data_transfer_exec_is_object_valid(wmOperator *op,
me->id.tag &= ~LIB_TAG_DOIT;
return true;
}
- else if (!ID_IS_LINKED(me)) {
+ else if (!ID_IS_LINKED(me) && !ID_IS_OVERRIDE_LIBRARY(me)) {
/* Do not apply transfer operation more than once. */
/* XXX This is not nice regarding vgroups, which are half-Object data... :/ */
BKE_reportf(
@@ -446,8 +446,8 @@ static int data_transfer_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
- if (reverse_transfer && ID_IS_LINKED(ob_src->data)) {
- /* Do not transfer to linked data, not supported. */
+ if (reverse_transfer && (ID_IS_LINKED(ob_src->data) || ID_IS_OVERRIDE_LIBRARY(ob_src->data))) {
+ /* Do not transfer to linked or override data, not supported. */
return OPERATOR_CANCELLED;
}
@@ -530,7 +530,7 @@ static bool data_transfer_poll(bContext *C)
{
Object *ob = ED_object_active_context(C);
ID *data = (ob) ? ob->data : NULL;
- return (ob && ob->type == OB_MESH && data);
+ return (ob != NULL && ob->type == OB_MESH && data != NULL);
}
/* Used by both OBJECT_OT_data_transfer and OBJECT_OT_datalayout_transfer */
@@ -786,7 +786,7 @@ static int datalayout_transfer_exec(bContext *C, wmOperator *op)
const bool use_delete = false; /* Never when used from modifier, for now. */
- if (!ob_src) {
+ if (!ob_src || ID_IS_LINKED(ob_dst) || ID_IS_OVERRIDE_LIBRARY(ob_dst)) {
return OPERATOR_CANCELLED;
}
@@ -854,7 +854,7 @@ static int datalayout_transfer_exec(bContext *C, wmOperator *op)
static int datalayout_transfer_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- if (edit_modifier_invoke_properties(C, op)) {
+ if (edit_modifier_invoke_properties(C, op, NULL, NULL)) {
return datalayout_transfer_exec(C, op);
}
else {
diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c
index d522dcabae3..283aaec85ef 100644
--- a/source/blender/editors/object/object_edit.c
+++ b/source/blender/editors/object/object_edit.c
@@ -600,7 +600,8 @@ bool ED_object_editmode_enter_ex(Main *bmain, Scene *scene, Object *ob, int flag
{
bool ok = false;
- if (ELEM(NULL, ob, ob->data) || ID_IS_LINKED(ob)) {
+ if (ELEM(NULL, ob, ob->data) || ID_IS_LINKED(ob) || ID_IS_OVERRIDE_LIBRARY(ob) ||
+ ID_IS_OVERRIDE_LIBRARY(ob->data)) {
return false;
}
@@ -695,14 +696,10 @@ bool ED_object_editmode_enter(bContext *C, int flag)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
- Object *ob;
/* Active layer checked here for view3d,
* callers that don't want view context can call the extended version. */
- ob = CTX_data_active_object(C);
- if ((ob == NULL) || ID_IS_LINKED(ob)) {
- return false;
- }
+ Object *ob = CTX_data_active_object(C);
return ED_object_editmode_enter_ex(bmain, scene, ob, flag);
}
@@ -760,7 +757,8 @@ static bool editmode_toggle_poll(bContext *C)
Object *ob = CTX_data_active_object(C);
/* covers proxies too */
- if (ELEM(NULL, ob, ob->data) || ID_IS_LINKED(ob->data)) {
+ if (ELEM(NULL, ob, ob->data) || ID_IS_LINKED(ob->data) || ID_IS_OVERRIDE_LIBRARY(ob) ||
+ ID_IS_OVERRIDE_LIBRARY(ob->data)) {
return 0;
}
@@ -1368,7 +1366,8 @@ static bool shade_poll(bContext *C)
Object *obact = OBACT(view_layer);
if (obact != NULL) {
/* Doesn't handle edit-data, sculpt dynamic-topology, or their undo systems. */
- if (obact->mode & (OB_MODE_EDIT | OB_MODE_SCULPT)) {
+ if (obact->mode & (OB_MODE_EDIT | OB_MODE_SCULPT) || obact->data == NULL ||
+ ID_IS_OVERRIDE_LIBRARY(obact) || ID_IS_OVERRIDE_LIBRARY(obact->data)) {
return false;
}
}
@@ -1771,15 +1770,6 @@ static void move_to_collection_menu_create(bContext *UNUSED(C), uiLayout *layout
const char *name = BKE_collection_ui_name_get(menu->collection);
UI_block_flag_enable(uiLayoutGetBlock(layout), UI_BLOCK_IS_FLIP);
- uiItemIntO(layout, name, ICON_NONE, menu->ot->idname, "collection_index", menu->index);
- uiItemS(layout);
-
- for (MoveToCollectionData *submenu = menu->submenus.first; submenu != NULL;
- submenu = submenu->next) {
- move_to_collection_menus_items(layout, submenu);
- }
-
- uiItemS(layout);
WM_operator_properties_create_ptr(&menu->ptr, menu->ot);
RNA_int_set(&menu->ptr, "collection_index", menu->index);
@@ -1787,6 +1777,15 @@ static void move_to_collection_menu_create(bContext *UNUSED(C), uiLayout *layout
uiItemFullO_ptr(
layout, menu->ot, "New Collection", ICON_ADD, menu->ptr.data, WM_OP_INVOKE_DEFAULT, 0, NULL);
+
+ uiItemS(layout);
+
+ uiItemIntO(layout, name, ICON_SCENE_DATA, menu->ot->idname, "collection_index", menu->index);
+
+ for (MoveToCollectionData *submenu = menu->submenus.first; submenu != NULL;
+ submenu = submenu->next) {
+ move_to_collection_menus_items(layout, submenu);
+ }
}
static void move_to_collection_menus_items(uiLayout *layout, MoveToCollectionData *menu)
diff --git a/source/blender/editors/object/object_facemap_ops.c b/source/blender/editors/object/object_facemap_ops.c
index 147bd3d7871..6e0376358bb 100644
--- a/source/blender/editors/object/object_facemap_ops.c
+++ b/source/blender/editors/object/object_facemap_ops.c
@@ -171,14 +171,15 @@ static bool face_map_supported_poll(bContext *C)
{
Object *ob = ED_object_context(C);
ID *data = (ob) ? ob->data : NULL;
- return (ob && !ob->id.lib && ob->type == OB_MESH && data && !data->lib);
+ return (ob && !ID_IS_LINKED(ob) && !ID_IS_OVERRIDE_LIBRARY(ob) && ob->type == OB_MESH && data &&
+ !ID_IS_LINKED(data) && !ID_IS_OVERRIDE_LIBRARY(data));
}
static bool face_map_supported_edit_mode_poll(bContext *C)
{
Object *ob = ED_object_context(C);
- ID *data = (ob) ? ob->data : NULL;
- if (ob && !ob->id.lib && ob->type == OB_MESH && data && !data->lib) {
+
+ if (face_map_supported_poll(C)) {
if (ob->mode == OB_MODE_EDIT) {
return true;
}
diff --git a/source/blender/editors/object/object_gpencil_modifier.c b/source/blender/editors/object/object_gpencil_modifier.c
index 6d0f53cfa1e..cfdb6fea52d 100644
--- a/source/blender/editors/object/object_gpencil_modifier.c
+++ b/source/blender/editors/object/object_gpencil_modifier.c
@@ -24,6 +24,7 @@
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include "MEM_guardedalloc.h"
@@ -54,6 +55,8 @@
#include "ED_object.h"
#include "ED_screen.h"
+#include "UI_interface.h"
+
#include "WM_api.h"
#include "WM_types.h"
@@ -105,23 +108,6 @@ GpencilModifierData *ED_object_gpencil_modifier_add(
return new_md;
}
-/* Return true if the object has a modifier of type 'type' other than
- * the modifier pointed to be 'exclude', otherwise returns false. */
-static bool UNUSED_FUNCTION(gpencil_object_has_modifier)(const Object *ob,
- const GpencilModifierData *exclude,
- GpencilModifierType type)
-{
- GpencilModifierData *md;
-
- for (md = ob->greasepencil_modifiers.first; md; md = md->next) {
- if ((md != exclude) && (md->type == type)) {
- return true;
- }
- }
-
- return false;
-}
-
static bool gpencil_object_modifier_remove(Main *bmain,
Object *ob,
GpencilModifierData *md,
@@ -211,6 +197,40 @@ int ED_object_gpencil_modifier_move_down(ReportList *UNUSED(reports),
return 1;
}
+bool ED_object_gpencil_modifier_move_to_index(ReportList *reports,
+ Object *ob,
+ GpencilModifierData *md,
+ const int index)
+{
+ BLI_assert(md != NULL);
+ BLI_assert(index >= 0);
+ if (index >= BLI_listbase_count(&ob->greasepencil_modifiers)) {
+ BKE_report(reports, RPT_WARNING, "Cannot move modifier beyond the end of the stack");
+ return false;
+ }
+
+ int md_index = BLI_findindex(&ob->greasepencil_modifiers, md);
+ BLI_assert(md_index != -1);
+ if (md_index < index) {
+ /* Move modifier down in list. */
+ for (; md_index < index; md_index++) {
+ if (!ED_object_gpencil_modifier_move_down(reports, ob, md)) {
+ break;
+ }
+ }
+ }
+ else {
+ /* Move modifier up in list. */
+ for (; md_index > index; md_index--) {
+ if (!ED_object_gpencil_modifier_move_up(reports, ob, md)) {
+ break;
+ }
+ }
+ }
+
+ return true;
+}
+
static int gpencil_modifier_apply_obdata(
ReportList *reports, Main *bmain, Depsgraph *depsgraph, Object *ob, GpencilModifierData *md)
{
@@ -428,22 +448,58 @@ static void gpencil_edit_modifier_properties(wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_HIDDEN);
}
-static int gpencil_edit_modifier_invoke_properties(bContext *C, wmOperator *op)
+static void gpencil_edit_modifier_report_property(wmOperatorType *ot)
{
- GpencilModifierData *md;
+ PropertyRNA *prop = RNA_def_boolean(
+ ot->srna, "report", false, "Report", "Create a notification after the operation");
+ RNA_def_property_flag(prop, PROP_HIDDEN);
+}
+/**
+ * \param event: If this isn't NULL, the operator will also look for panels underneath
+ * the cursor with customdata set to a modifier.
+ * \param r_retval: This should be used if #event is used in order to to return
+ * #OPERATOR_PASS_THROUGH to check other operators with the same key set.
+ */
+static bool gpencil_edit_modifier_invoke_properties(bContext *C,
+ wmOperator *op,
+ const wmEvent *event,
+ int *r_retval)
+{
if (RNA_struct_property_is_set(op->ptr, "modifier")) {
return true;
}
- else {
- PointerRNA ptr = CTX_data_pointer_get_type(C, "modifier", &RNA_GpencilModifier);
- if (ptr.data) {
- md = ptr.data;
- RNA_string_set(op->ptr, "modifier", md->name);
- return true;
+
+ PointerRNA ctx_ptr = CTX_data_pointer_get_type(C, "modifier", &RNA_GpencilModifier);
+ if (ctx_ptr.data != NULL) {
+ GpencilModifierData *md = ctx_ptr.data;
+ RNA_string_set(op->ptr, "modifier", md->name);
+ return true;
+ }
+
+ /* Check the custom data of panels under the mouse for a modifier. */
+ if (event != NULL) {
+ PointerRNA *panel_ptr = UI_region_panel_custom_data_under_cursor(C, event);
+
+ if (!(panel_ptr == NULL || RNA_pointer_is_null(panel_ptr))) {
+ if (RNA_struct_is_a(panel_ptr->type, &RNA_GpencilModifier)) {
+ GpencilModifierData *md = panel_ptr->data;
+ RNA_string_set(op->ptr, "modifier", md->name);
+ return true;
+ }
+ else {
+ BLI_assert(r_retval != NULL); /* We need the return value in this case. */
+ if (r_retval != NULL) {
+ *r_retval = (OPERATOR_PASS_THROUGH | OPERATOR_CANCELLED);
+ }
+ return false;
+ }
}
}
+ if (r_retval != NULL) {
+ *r_retval = OPERATOR_CANCELLED;
+ }
return false;
}
@@ -472,24 +528,35 @@ static int gpencil_modifier_remove_exec(bContext *C, wmOperator *op)
Object *ob = ED_object_active_context(C);
GpencilModifierData *md = gpencil_edit_modifier_property_get(op, ob, 0);
- if (!md || !ED_object_gpencil_modifier_remove(op->reports, bmain, ob, md)) {
+ if (md == NULL) {
+ return OPERATOR_CANCELLED;
+ }
+
+ /* Store name temporarily for report. */
+ char name[MAX_NAME];
+ strcpy(name, md->name);
+
+ if (!ED_object_gpencil_modifier_remove(op->reports, bmain, ob, md)) {
return OPERATOR_CANCELLED;
}
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
+ if (RNA_boolean_get(op->ptr, "report")) {
+ BKE_reportf(op->reports, RPT_INFO, "Removed modifier: %s", name);
+ }
+
return OPERATOR_FINISHED;
}
-static int gpencil_modifier_remove_invoke(bContext *C,
- wmOperator *op,
- const wmEvent *UNUSED(event))
+static int gpencil_modifier_remove_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- if (gpencil_edit_modifier_invoke_properties(C, op)) {
+ int retval;
+ if (gpencil_edit_modifier_invoke_properties(C, op, event, &retval)) {
return gpencil_modifier_remove_exec(C, op);
}
else {
- return OPERATOR_CANCELLED;
+ return retval;
}
}
@@ -506,6 +573,7 @@ void OBJECT_OT_gpencil_modifier_remove(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
gpencil_edit_modifier_properties(ot);
+ gpencil_edit_modifier_report_property(ot);
}
/************************ move up modifier operator *********************/
@@ -525,15 +593,14 @@ static int gpencil_modifier_move_up_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
-static int gpencil_modifier_move_up_invoke(bContext *C,
- wmOperator *op,
- const wmEvent *UNUSED(event))
+static int gpencil_modifier_move_up_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- if (gpencil_edit_modifier_invoke_properties(C, op)) {
+ int retval;
+ if (gpencil_edit_modifier_invoke_properties(C, op, event, &retval)) {
return gpencil_modifier_move_up_exec(C, op);
}
else {
- return OPERATOR_CANCELLED;
+ return retval;
}
}
@@ -569,15 +636,14 @@ static int gpencil_modifier_move_down_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
-static int gpencil_modifier_move_down_invoke(bContext *C,
- wmOperator *op,
- const wmEvent *UNUSED(event))
+static int gpencil_modifier_move_down_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- if (gpencil_edit_modifier_invoke_properties(C, op)) {
+ int retval;
+ if (gpencil_edit_modifier_invoke_properties(C, op, event, &retval)) {
return gpencil_modifier_move_down_exec(C, op);
}
else {
- return OPERATOR_CANCELLED;
+ return retval;
}
}
@@ -596,6 +662,59 @@ void OBJECT_OT_gpencil_modifier_move_down(wmOperatorType *ot)
gpencil_edit_modifier_properties(ot);
}
+/* ************************* Move to Index Gpencil Modifier Operator ************************* */
+
+static bool gpencil_modifier_move_to_index_poll(bContext *C)
+{
+ return gpencil_edit_modifier_poll(C);
+}
+
+static int gpencil_modifier_move_to_index_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = ED_object_active_context(C);
+ GpencilModifierData *md = gpencil_edit_modifier_property_get(op, ob, 0);
+ int index = RNA_int_get(op->ptr, "index");
+
+ if (!ED_object_gpencil_modifier_move_to_index(op->reports, ob, md, index)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+static int gpencil_modifier_move_to_index_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ int retval;
+ if (gpencil_edit_modifier_invoke_properties(C, op, event, &retval)) {
+ return gpencil_modifier_move_to_index_exec(C, op);
+ }
+ else {
+ return retval;
+ }
+}
+
+void OBJECT_OT_gpencil_modifier_move_to_index(wmOperatorType *ot)
+{
+ ot->name = "Move Active Modifier to Index";
+ ot->idname = "OBJECT_OT_gpencil_modifier_move_to_index";
+ ot->description =
+ "Change the modifier's position in the list so it evaluates after the set number of "
+ "others";
+
+ ot->invoke = gpencil_modifier_move_to_index_invoke;
+ ot->exec = gpencil_modifier_move_to_index_exec;
+ ot->poll = gpencil_modifier_move_to_index_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
+ edit_modifier_properties(ot);
+ RNA_def_int(
+ ot->srna, "index", 0, 0, INT_MAX, "Index", "The index to move the modifier to", 0, INT_MAX);
+}
+
/************************ apply modifier operator *********************/
static int gpencil_modifier_apply_exec(bContext *C, wmOperator *op)
@@ -606,23 +725,36 @@ static int gpencil_modifier_apply_exec(bContext *C, wmOperator *op)
GpencilModifierData *md = gpencil_edit_modifier_property_get(op, ob, 0);
int apply_as = RNA_enum_get(op->ptr, "apply_as");
- if (!md || !ED_object_gpencil_modifier_apply(bmain, op->reports, depsgraph, ob, md, apply_as)) {
+ if (md == NULL) {
+ return OPERATOR_CANCELLED;
+ }
+
+ /* Store name temporarily for report. */
+ char name[MAX_NAME];
+ strcpy(name, md->name);
+
+ if (!ED_object_gpencil_modifier_apply(bmain, op->reports, depsgraph, ob, md, apply_as)) {
return OPERATOR_CANCELLED;
}
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
+ if (RNA_boolean_get(op->ptr, "report")) {
+ BKE_reportf(op->reports, RPT_INFO, "Applied modifier: %s", name);
+ }
+
return OPERATOR_FINISHED;
}
-static int gpencil_modifier_apply_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+static int gpencil_modifier_apply_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- if (gpencil_edit_modifier_invoke_properties(C, op)) {
+ int retval;
+ if (gpencil_edit_modifier_invoke_properties(C, op, event, &retval)) {
return gpencil_modifier_apply_exec(C, op);
}
else {
- return OPERATOR_CANCELLED;
+ return retval;
}
}
@@ -656,6 +788,7 @@ void OBJECT_OT_gpencil_modifier_apply(wmOperatorType *ot)
"Apply as",
"How to apply the modifier to the geometry");
gpencil_edit_modifier_properties(ot);
+ gpencil_edit_modifier_report_property(ot);
}
/************************ copy modifier operator *********************/
@@ -675,13 +808,14 @@ static int gpencil_modifier_copy_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
-static int gpencil_modifier_copy_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+static int gpencil_modifier_copy_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- if (gpencil_edit_modifier_invoke_properties(C, op)) {
+ int retval;
+ if (gpencil_edit_modifier_invoke_properties(C, op, event, &retval)) {
return gpencil_modifier_copy_exec(C, op);
}
else {
- return OPERATOR_CANCELLED;
+ return retval;
}
}
diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h
index c5a6e38fbcb..afc87c0caba 100644
--- a/source/blender/editors/object/object_intern.h
+++ b/source/blender/editors/object/object_intern.h
@@ -153,7 +153,10 @@ bool edit_modifier_poll_generic(struct bContext *C,
const bool is_editmode_allowed);
bool edit_modifier_poll(struct bContext *C);
void edit_modifier_properties(struct wmOperatorType *ot);
-int edit_modifier_invoke_properties(struct bContext *C, struct wmOperator *op);
+bool edit_modifier_invoke_properties(struct bContext *C,
+ struct wmOperator *op,
+ const struct wmEvent *event,
+ int *r_retval);
struct ModifierData *edit_modifier_property_get(struct wmOperator *op,
struct Object *ob,
int type);
@@ -190,6 +193,7 @@ void OBJECT_OT_gpencil_modifier_add(struct wmOperatorType *ot);
void OBJECT_OT_gpencil_modifier_remove(struct wmOperatorType *ot);
void OBJECT_OT_gpencil_modifier_move_up(struct wmOperatorType *ot);
void OBJECT_OT_gpencil_modifier_move_down(struct wmOperatorType *ot);
+void OBJECT_OT_gpencil_modifier_move_to_index(struct wmOperatorType *ot);
void OBJECT_OT_gpencil_modifier_apply(struct wmOperatorType *ot);
void OBJECT_OT_gpencil_modifier_copy(struct wmOperatorType *ot);
@@ -198,6 +202,7 @@ void OBJECT_OT_shaderfx_add(struct wmOperatorType *ot);
void OBJECT_OT_shaderfx_remove(struct wmOperatorType *ot);
void OBJECT_OT_shaderfx_move_up(struct wmOperatorType *ot);
void OBJECT_OT_shaderfx_move_down(struct wmOperatorType *ot);
+void OBJECT_OT_shaderfx_move_to_index(struct wmOperatorType *ot);
/* object_constraint.c */
void OBJECT_OT_constraint_add(struct wmOperatorType *ot);
@@ -217,6 +222,7 @@ void POSE_OT_ik_clear(struct wmOperatorType *ot);
void CONSTRAINT_OT_delete(struct wmOperatorType *ot);
void CONSTRAINT_OT_move_up(struct wmOperatorType *ot);
+void CONSTRAINT_OT_move_to_index(struct wmOperatorType *ot);
void CONSTRAINT_OT_move_down(struct wmOperatorType *ot);
void CONSTRAINT_OT_stretchto_reset(struct wmOperatorType *ot);
diff --git a/source/blender/editors/object/object_modes.c b/source/blender/editors/object/object_modes.c
index c518fd32c7f..5d4476ecb8c 100644
--- a/source/blender/editors/object/object_modes.c
+++ b/source/blender/editors/object/object_modes.c
@@ -116,7 +116,7 @@ bool ED_object_mode_compat_test(const Object *ob, eObjectMode mode)
case OB_SURF:
case OB_FONT:
case OB_MBALL:
- if (mode & (OB_MODE_EDIT)) {
+ if (mode & OB_MODE_EDIT) {
return true;
}
break;
diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c
index a6c5814e88a..6b0eff5b6e5 100644
--- a/source/blender/editors/object/object_modifier.c
+++ b/source/blender/editors/object/object_modifier.c
@@ -88,6 +88,8 @@
#include "ED_screen.h"
#include "ED_sculpt.h"
+#include "UI_interface.h"
+
#include "WM_api.h"
#include "WM_types.h"
@@ -1037,22 +1039,58 @@ void edit_modifier_properties(wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_HIDDEN);
}
-int edit_modifier_invoke_properties(bContext *C, wmOperator *op)
+static void edit_modifier_report_property(wmOperatorType *ot)
{
- ModifierData *md;
+ PropertyRNA *prop = RNA_def_boolean(
+ ot->srna, "report", false, "Report", "Create a notification after the operation");
+ RNA_def_property_flag(prop, PROP_HIDDEN);
+}
+/**
+ * \param event: If this isn't NULL, the operator will also look for panels underneath
+ * the cursor with customdata set to a modifier.
+ * \param r_retval: This should be used if #event is used in order to to return
+ * #OPERATOR_PASS_THROUGH to check other operators with the same key set.
+ */
+bool edit_modifier_invoke_properties(bContext *C,
+ wmOperator *op,
+ const wmEvent *event,
+ int *r_retval)
+{
if (RNA_struct_property_is_set(op->ptr, "modifier")) {
return true;
}
- else {
- PointerRNA ptr = CTX_data_pointer_get_type(C, "modifier", &RNA_Modifier);
- if (ptr.data) {
- md = ptr.data;
- RNA_string_set(op->ptr, "modifier", md->name);
- return true;
+
+ PointerRNA ctx_ptr = CTX_data_pointer_get_type(C, "modifier", &RNA_Modifier);
+ if (ctx_ptr.data != NULL) {
+ ModifierData *md = ctx_ptr.data;
+ RNA_string_set(op->ptr, "modifier", md->name);
+ return true;
+ }
+
+ /* Check the custom data of panels under the mouse for a modifier. */
+ if (event != NULL) {
+ PointerRNA *panel_ptr = UI_region_panel_custom_data_under_cursor(C, event);
+
+ if (!(panel_ptr == NULL || RNA_pointer_is_null(panel_ptr))) {
+ if (RNA_struct_is_a(panel_ptr->type, &RNA_Modifier)) {
+ ModifierData *md = panel_ptr->data;
+ RNA_string_set(op->ptr, "modifier", md->name);
+ return true;
+ }
+ else {
+ BLI_assert(r_retval != NULL); /* We need the return value in this case. */
+ if (r_retval != NULL) {
+ *r_retval = (OPERATOR_PASS_THROUGH | OPERATOR_CANCELLED);
+ }
+ return false;
+ }
}
}
+ if (r_retval != NULL) {
+ *r_retval = OPERATOR_CANCELLED;
+ }
return false;
}
@@ -1085,7 +1123,15 @@ static int modifier_remove_exec(bContext *C, wmOperator *op)
ModifierData *md = edit_modifier_property_get(op, ob, 0);
int mode_orig = ob->mode;
- if (!md || !ED_object_modifier_remove(op->reports, bmain, ob, md)) {
+ if (md == NULL) {
+ return OPERATOR_CANCELLED;
+ }
+
+ /* Store name temporarily for report. */
+ char name[MAX_NAME];
+ strcpy(name, md->name);
+
+ if (!ED_object_modifier_remove(op->reports, bmain, ob, md)) {
return OPERATOR_CANCELLED;
}
@@ -1099,16 +1145,22 @@ static int modifier_remove_exec(bContext *C, wmOperator *op)
}
}
}
+
+ if (RNA_boolean_get(op->ptr, "report")) {
+ BKE_reportf(op->reports, RPT_INFO, "Removed modifier: %s", name);
+ }
+
return OPERATOR_FINISHED;
}
-static int modifier_remove_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+static int modifier_remove_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- if (edit_modifier_invoke_properties(C, op)) {
+ int retval;
+ if (edit_modifier_invoke_properties(C, op, event, &retval)) {
return modifier_remove_exec(C, op);
}
else {
- return OPERATOR_CANCELLED;
+ return retval;
}
}
@@ -1125,6 +1177,7 @@ void OBJECT_OT_modifier_remove(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
edit_modifier_properties(ot);
+ edit_modifier_report_property(ot);
}
/** \} */
@@ -1148,13 +1201,14 @@ static int modifier_move_up_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
-static int modifier_move_up_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+static int modifier_move_up_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- if (edit_modifier_invoke_properties(C, op)) {
+ int retval;
+ if (edit_modifier_invoke_properties(C, op, event, &retval)) {
return modifier_move_up_exec(C, op);
}
else {
- return OPERATOR_CANCELLED;
+ return retval;
}
}
@@ -1194,13 +1248,14 @@ static int modifier_move_down_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
-static int modifier_move_down_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+static int modifier_move_down_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- if (edit_modifier_invoke_properties(C, op)) {
+ int retval;
+ if (edit_modifier_invoke_properties(C, op, event, &retval)) {
return modifier_move_down_exec(C, op);
}
else {
- return OPERATOR_CANCELLED;
+ return retval;
}
}
@@ -1246,13 +1301,14 @@ static int modifier_move_to_index_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
-static int modifier_move_to_index_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+static int modifier_move_to_index_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- if (edit_modifier_invoke_properties(C, op)) {
+ int retval;
+ if (edit_modifier_invoke_properties(C, op, event, &retval)) {
return modifier_move_to_index_exec(C, op);
}
else {
- return OPERATOR_CANCELLED;
+ return retval;
}
}
@@ -1291,6 +1347,10 @@ static bool modifier_apply_poll(bContext *C)
Object *ob = (ptr.owner_id != NULL) ? (Object *)ptr.owner_id : ED_object_active_context(C);
ModifierData *md = ptr.data; /* May be NULL. */
+ if (ID_IS_OVERRIDE_LIBRARY(ob) || ID_IS_OVERRIDE_LIBRARY(ob->data)) {
+ CTX_wm_operator_poll_msg_set(C, "Modifiers cannot be applied on override data");
+ return false;
+ }
if ((ob->data != NULL) && ID_REAL_USERS(ob->data) > 1) {
CTX_wm_operator_poll_msg_set(C, "Modifiers cannot be applied to multi-user data");
return false;
@@ -1315,7 +1375,15 @@ static int modifier_apply_exec(bContext *C, wmOperator *op)
ModifierData *md = edit_modifier_property_get(op, ob, 0);
int apply_as = RNA_enum_get(op->ptr, "apply_as");
- if (!md || !ED_object_modifier_apply(bmain, op->reports, depsgraph, scene, ob, md, apply_as)) {
+ if (md == NULL) {
+ return OPERATOR_CANCELLED;
+ }
+
+ /* Store name temporarily for report. */
+ char name[MAX_NAME];
+ strcpy(name, md->name);
+
+ if (!ED_object_modifier_apply(bmain, op->reports, depsgraph, scene, ob, md, apply_as)) {
return OPERATOR_CANCELLED;
}
@@ -1323,16 +1391,21 @@ static int modifier_apply_exec(bContext *C, wmOperator *op)
DEG_relations_tag_update(bmain);
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
+ if (RNA_boolean_get(op->ptr, "report")) {
+ BKE_reportf(op->reports, RPT_INFO, "Applied modifier: %s", name);
+ }
+
return OPERATOR_FINISHED;
}
-static int modifier_apply_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+static int modifier_apply_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- if (edit_modifier_invoke_properties(C, op)) {
+ int retval;
+ if (edit_modifier_invoke_properties(C, op, event, &retval)) {
return modifier_apply_exec(C, op);
}
else {
- return OPERATOR_CANCELLED;
+ return retval;
}
}
@@ -1366,6 +1439,7 @@ void OBJECT_OT_modifier_apply(wmOperatorType *ot)
"Apply as",
"How to apply the modifier to the geometry");
edit_modifier_properties(ot);
+ edit_modifier_report_property(ot);
}
/** \} */
@@ -1396,7 +1470,7 @@ static int modifier_convert_exec(bContext *C, wmOperator *op)
static int modifier_convert_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
- if (edit_modifier_invoke_properties(C, op)) {
+ if (edit_modifier_invoke_properties(C, op, NULL, NULL)) {
return modifier_convert_exec(C, op);
}
else {
@@ -1440,13 +1514,14 @@ static int modifier_copy_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
-static int modifier_copy_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+static int modifier_copy_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- if (edit_modifier_invoke_properties(C, op)) {
+ int retval;
+ if (edit_modifier_invoke_properties(C, op, event, &retval)) {
return modifier_copy_exec(C, op);
}
else {
- return OPERATOR_CANCELLED;
+ return retval;
}
}
@@ -1501,7 +1576,7 @@ static int multires_higher_levels_delete_invoke(bContext *C,
wmOperator *op,
const wmEvent *UNUSED(event))
{
- if (edit_modifier_invoke_properties(C, op)) {
+ if (edit_modifier_invoke_properties(C, op, NULL, NULL)) {
return multires_higher_levels_delete_exec(C, op);
}
else {
@@ -1579,7 +1654,7 @@ static int multires_subdivide_exec(bContext *C, wmOperator *op)
static int multires_subdivide_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
- if (edit_modifier_invoke_properties(C, op)) {
+ if (edit_modifier_invoke_properties(C, op, NULL, NULL)) {
return multires_subdivide_exec(C, op);
}
else {
@@ -1656,7 +1731,7 @@ static int multires_reshape_exec(bContext *C, wmOperator *op)
static int multires_reshape_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
- if (edit_modifier_invoke_properties(C, op)) {
+ if (edit_modifier_invoke_properties(C, op, NULL, NULL)) {
return multires_reshape_exec(C, op);
}
else {
@@ -1720,7 +1795,7 @@ static int multires_external_save_invoke(bContext *C, wmOperator *op, const wmEv
Mesh *me = ob->data;
char path[FILE_MAX];
- if (!edit_modifier_invoke_properties(C, op)) {
+ if (!edit_modifier_invoke_properties(C, op, NULL, NULL)) {
return OPERATOR_CANCELLED;
}
@@ -1837,7 +1912,7 @@ static int multires_base_apply_exec(bContext *C, wmOperator *op)
static int multires_base_apply_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
- if (edit_modifier_invoke_properties(C, op)) {
+ if (edit_modifier_invoke_properties(C, op, NULL, NULL)) {
return multires_base_apply_exec(C, op);
}
else {
@@ -1891,7 +1966,7 @@ static int multires_unsubdivide_exec(bContext *C, wmOperator *op)
static int multires_unsubdivide_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
- if (edit_modifier_invoke_properties(C, op)) {
+ if (edit_modifier_invoke_properties(C, op, NULL, NULL)) {
return multires_unsubdivide_exec(C, op);
}
else {
@@ -1949,7 +2024,7 @@ static int multires_rebuild_subdiv_invoke(bContext *C,
wmOperator *op,
const wmEvent *UNUSED(event))
{
- if (edit_modifier_invoke_properties(C, op)) {
+ if (edit_modifier_invoke_properties(C, op, NULL, NULL)) {
return multires_rebuild_subdiv_exec(C, op);
}
else {
@@ -1999,8 +2074,9 @@ static bool skin_poll(bContext *C)
static bool skin_edit_poll(bContext *C)
{
- return (CTX_data_edit_object(C) &&
- edit_modifier_poll_generic(C, &RNA_SkinModifier, (1 << OB_MESH), true));
+ Object *ob = CTX_data_edit_object(C);
+ return (ob != NULL && edit_modifier_poll_generic(C, &RNA_SkinModifier, (1 << OB_MESH), true) &&
+ !ID_IS_OVERRIDE_LIBRARY(ob) && !ID_IS_OVERRIDE_LIBRARY(ob->data));
}
static void skin_root_clear(BMVert *bm_vert, GSet *visited, const int cd_vert_skin_offset)
@@ -2327,7 +2403,7 @@ static int skin_armature_create_exec(bContext *C, wmOperator *op)
static int skin_armature_create_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
- if (edit_modifier_invoke_properties(C, op)) {
+ if (edit_modifier_invoke_properties(C, op, NULL, NULL)) {
return skin_armature_create_exec(C, op);
}
else {
@@ -2406,7 +2482,7 @@ static int correctivesmooth_bind_exec(bContext *C, wmOperator *op)
static int correctivesmooth_bind_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
- if (edit_modifier_invoke_properties(C, op)) {
+ if (edit_modifier_invoke_properties(C, op, NULL, NULL)) {
return correctivesmooth_bind_exec(C, op);
}
else {
@@ -2483,7 +2559,7 @@ static int meshdeform_bind_exec(bContext *C, wmOperator *op)
static int meshdeform_bind_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
- if (edit_modifier_invoke_properties(C, op)) {
+ if (edit_modifier_invoke_properties(C, op, NULL, NULL)) {
return meshdeform_bind_exec(C, op);
}
else {
@@ -2539,7 +2615,7 @@ static int explode_refresh_exec(bContext *C, wmOperator *op)
static int explode_refresh_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
- if (edit_modifier_invoke_properties(C, op)) {
+ if (edit_modifier_invoke_properties(C, op, NULL, NULL)) {
return explode_refresh_exec(C, op);
}
else {
@@ -2743,7 +2819,7 @@ static int ocean_bake_exec(bContext *C, wmOperator *op)
static int ocean_bake_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
- if (edit_modifier_invoke_properties(C, op)) {
+ if (edit_modifier_invoke_properties(C, op, NULL, NULL)) {
return ocean_bake_exec(C, op);
}
else {
@@ -2822,7 +2898,7 @@ static int laplaciandeform_bind_exec(bContext *C, wmOperator *op)
static int laplaciandeform_bind_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
- if (edit_modifier_invoke_properties(C, op)) {
+ if (edit_modifier_invoke_properties(C, op, NULL, NULL)) {
return laplaciandeform_bind_exec(C, op);
}
else {
@@ -2891,7 +2967,7 @@ static int surfacedeform_bind_exec(bContext *C, wmOperator *op)
static int surfacedeform_bind_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
- if (edit_modifier_invoke_properties(C, op)) {
+ if (edit_modifier_invoke_properties(C, op, NULL, NULL)) {
return surfacedeform_bind_exec(C, op);
}
else {
diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c
index 05f1ced8615..e28bbb3fb1c 100644
--- a/source/blender/editors/object/object_ops.c
+++ b/source/blender/editors/object/object_ops.c
@@ -109,10 +109,8 @@ void ED_operatortypes_object(void)
WM_operatortype_append(OBJECT_OT_light_add);
WM_operatortype_append(OBJECT_OT_camera_add);
WM_operatortype_append(OBJECT_OT_speaker_add);
-#ifdef WITH_NEW_OBJECT_TYPES
WM_operatortype_append(OBJECT_OT_hair_add);
WM_operatortype_append(OBJECT_OT_pointcloud_add);
-#endif
WM_operatortype_append(OBJECT_OT_volume_add);
WM_operatortype_append(OBJECT_OT_volume_import);
WM_operatortype_append(OBJECT_OT_add);
@@ -152,6 +150,7 @@ void ED_operatortypes_object(void)
WM_operatortype_append(OBJECT_OT_gpencil_modifier_remove);
WM_operatortype_append(OBJECT_OT_gpencil_modifier_move_up);
WM_operatortype_append(OBJECT_OT_gpencil_modifier_move_down);
+ WM_operatortype_append(OBJECT_OT_gpencil_modifier_move_to_index);
WM_operatortype_append(OBJECT_OT_gpencil_modifier_apply);
WM_operatortype_append(OBJECT_OT_gpencil_modifier_copy);
@@ -160,6 +159,7 @@ void ED_operatortypes_object(void)
WM_operatortype_append(OBJECT_OT_shaderfx_remove);
WM_operatortype_append(OBJECT_OT_shaderfx_move_up);
WM_operatortype_append(OBJECT_OT_shaderfx_move_down);
+ WM_operatortype_append(OBJECT_OT_shaderfx_move_to_index);
WM_operatortype_append(OBJECT_OT_correctivesmooth_bind);
WM_operatortype_append(OBJECT_OT_meshdeform_bind);
@@ -179,6 +179,7 @@ void ED_operatortypes_object(void)
WM_operatortype_append(CONSTRAINT_OT_delete);
WM_operatortype_append(CONSTRAINT_OT_move_up);
WM_operatortype_append(CONSTRAINT_OT_move_down);
+ WM_operatortype_append(CONSTRAINT_OT_move_to_index);
WM_operatortype_append(CONSTRAINT_OT_stretchto_reset);
WM_operatortype_append(CONSTRAINT_OT_limitdistance_reset);
WM_operatortype_append(CONSTRAINT_OT_childof_set_inverse);
diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c
index fd2fcb11635..509b70f849e 100644
--- a/source/blender/editors/object/object_relations.c
+++ b/source/blender/editors/object/object_relations.c
@@ -33,6 +33,7 @@
#include "DNA_collection_types.h"
#include "DNA_constraint_types.h"
#include "DNA_gpencil_types.h"
+#include "DNA_key_types.h"
#include "DNA_lattice_types.h"
#include "DNA_light_types.h"
#include "DNA_material_types.h"
@@ -68,6 +69,7 @@
#include "BKE_gpencil.h"
#include "BKE_hair.h"
#include "BKE_idprop.h"
+#include "BKE_idtype.h"
#include "BKE_lattice.h"
#include "BKE_layer.h"
#include "BKE_lib_id.h"
@@ -1771,7 +1773,10 @@ static Collection *single_object_users_collection(Main *bmain,
/* Generate new copies for objects in given collection and all its children,
* and optionally also copy collections themselves. */
if (copy_collections && !is_master_collection) {
- collection = ID_NEW_SET(collection, BKE_collection_copy(bmain, NULL, collection));
+ Collection *collection_new;
+ BKE_id_copy(bmain, &collection->id, (ID **)&collection_new);
+ id_us_min(&collection_new->id);
+ collection = ID_NEW_SET(collection, collection_new);
}
/* We do not remap to new objects here, this is done in separate step. */
@@ -2246,47 +2251,66 @@ void OBJECT_OT_make_local(wmOperatorType *ot)
/** \name Make Library Override Operator
* \{ */
-static void make_override_library_tag_object(Object *obact, Object *ob)
+static bool make_override_hierarchy_recursive_tag(Main *bmain, ID *id)
{
- if (ob == obact) {
- return;
+ MainIDRelationsEntry *entry = BLI_ghash_lookup(bmain->relations->id_user_to_used, id);
+
+ /* This way we won't process again that ID should we encounter it again through another
+ * relationship hierarchy.
+ * Note that this does not free any memory from relations, so we can still use the entries.
+ */
+ BKE_main_relations_ID_remove(bmain, id);
+
+ for (; entry != NULL; entry = entry->next) {
+ /* We only consider IDs from the same library. */
+ if (entry->id_pointer != NULL && (*entry->id_pointer)->lib == id->lib) {
+ if (make_override_hierarchy_recursive_tag(bmain, *entry->id_pointer)) {
+ id->tag |= LIB_TAG_DOIT;
+ }
+ }
}
- if (!ID_IS_LINKED(ob)) {
- return;
+ return (id->tag & LIB_TAG_DOIT) != 0;
+}
+
+static int make_override_tag_ids_cb(LibraryIDLinkCallbackData *cb_data)
+{
+ if (cb_data->cb_flag & (IDWALK_CB_EMBEDDED | IDWALK_CB_LOOPBACK)) {
+ return IDWALK_RET_STOP_RECURSION;
}
- /* Note: all this is very case-by-case bad handling, ultimately we'll want a real full
- * 'automatic', generic handling of all this,
- * will probably require adding some override-aware stuff to library_query code... */
+ ID *id_root = cb_data->user_data;
+ Library *library_root = id_root->lib;
+ ID *id = *cb_data->id_pointer;
+ ID *id_owner = cb_data->id_owner;
- if (obact->type == OB_ARMATURE && ob->modifiers.first != NULL) {
- for (ModifierData *md = ob->modifiers.first; md != NULL; md = md->next) {
- if (md->type == eModifierType_Armature) {
- ArmatureModifierData *amd = (ArmatureModifierData *)md;
- if (amd->object == obact) {
- ob->id.tag |= LIB_TAG_DOIT;
- break;
- }
- }
- }
+ BLI_assert(id_owner == cb_data->id_self);
+
+ if (ELEM(id, NULL, id_owner)) {
+ return IDWALK_RET_NOP;
}
- else if (ob->parent == obact) {
- ob->id.tag |= LIB_TAG_DOIT;
+
+ BLI_assert(id->lib != NULL);
+ BLI_assert(id_owner->lib == library_root);
+
+ if (id->tag & LIB_TAG_DOIT) {
+ /* Already processed, but maybe not with the same chain of dependency, so we need to check that
+ * one nonetheless. */
+ return IDWALK_RET_STOP_RECURSION;
}
- if (ob->id.tag & LIB_TAG_DOIT) {
- printf("Indirectly overriding %s for %s\n", ob->id.name, obact->id.name);
+ if (id->lib != library_root) {
+ /* We do not override data-blocks from other libraries, nor do we process them. */
+ return IDWALK_RET_STOP_RECURSION;
}
-}
-static void make_override_library_tag_collections(Collection *collection)
-{
- collection->id.tag |= LIB_TAG_DOIT;
- for (CollectionChild *coll_child = collection->children.first; coll_child != NULL;
- coll_child = coll_child->next) {
- make_override_library_tag_collections(coll_child->collection);
+ /* We tag all collections and objects for override. And we also tag all other data-blocks which
+ * would user one of those. */
+ if (ELEM(GS(id->name), ID_OB, ID_GR)) {
+ id->tag |= LIB_TAG_DOIT;
}
+
+ return IDWALK_RET_NOP;
}
/* Set the object to override. */
@@ -2335,6 +2359,7 @@ static int make_override_library_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Object *obact = CTX_data_active_object(C);
+ ID *id_root = NULL;
bool success = false;
@@ -2348,111 +2373,156 @@ static int make_override_library_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
+ id_root = &obact->instance_collection->id;
+ }
+ else if (!ID_IS_OVERRIDABLE_LIBRARY(obact)) {
+ BKE_reportf(op->reports,
+ RPT_ERROR_INVALID_INPUT,
+ "Active object '%s' is not overridable",
+ obact->id.name + 2);
+ return OPERATOR_CANCELLED;
+ }
+ /* Else, poll func ensures us that ID_IS_LINKED(obact) is true. */
+ else {
+ id_root = &obact->id;
+ }
- Object *obcollection = obact;
- Collection *collection = obcollection->instance_collection;
+ BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
- const ListBase dup_collection_objects = BKE_collection_object_cache_get(collection);
- Base *base = BLI_findlink(&dup_collection_objects, RNA_enum_get(op->ptr, "object"));
- obact = base->object;
+ /* Tag all collections and objects, as well as other IDs using them. */
+ id_root->tag |= LIB_TAG_DOIT;
- /* First, we make a library override of the linked collection itself, and all its children. */
- make_override_library_tag_collections(collection);
+ BKE_main_relations_create(bmain, 0);
- /* Then, we make library override of the whole set of objects in the Collection. */
- FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (collection, ob) {
- ob->id.tag |= LIB_TAG_DOIT;
- }
- FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
-
- /* Then, we remove (untag) bone shape objects, you shall never want to override those
- * (hopefully)... */
- FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (collection, ob) {
- if (ob->type == OB_ARMATURE && ob->pose != NULL) {
- for (bPoseChannel *pchan = ob->pose->chanbase.first; pchan != NULL; pchan = pchan->next) {
- if (pchan->custom != NULL) {
- pchan->custom->id.tag &= ~LIB_TAG_DOIT;
- }
+ BKE_library_foreach_ID_link(
+ bmain, id_root, make_override_tag_ids_cb, id_root, IDWALK_READONLY | IDWALK_RECURSE);
+
+ /* Then, we remove (untag) bone shape objects, you shall never want to override those
+ * (hopefully)... */
+ LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
+ if (ob->type == OB_ARMATURE && ob->pose != NULL && (ob->id.tag & LIB_TAG_DOIT)) {
+ for (bPoseChannel *pchan = ob->pose->chanbase.first; pchan != NULL; pchan = pchan->next) {
+ if (pchan->custom != NULL) {
+ pchan->custom->id.tag &= ~LIB_TAG_DOIT;
}
}
}
- FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
+ }
- success = BKE_lib_override_library_create_from_tag(bmain);
+ /* The we tag all intermediary data-blocks in-between to overridden ones (e.g. if a shapekey has
+ * a driver using an armature object's bone, we need to override the shapekey/obdata, the objects
+ * using them, etc.) */
+ make_override_hierarchy_recursive_tag(bmain, id_root);
+
+ BKE_main_relations_free(bmain);
+
+ ID *id;
+ FOREACH_MAIN_ID_BEGIN (bmain, id) {
+ if (id->tag & LIB_TAG_DOIT && id->lib != NULL) {
+ printf("ID %s tagged for override\n", id->name);
+ }
+ }
+ FOREACH_MAIN_ID_END;
- /* Instantiate our newly overridden objects in scene, if not yet done. */
+ success = BKE_lib_override_library_create_from_tag(bmain);
+
+ if (success) {
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Collection *new_collection = (Collection *)collection->id.newid;
- BKE_collection_add_from_object(bmain, scene, obcollection, new_collection);
+ BKE_main_collection_sync(bmain);
+
+ switch (GS(id_root->name)) {
+ case ID_GR: {
+ Collection *collection_new = ((Collection *)id_root->newid);
+ BKE_collection_add_from_object(bmain, scene, obact, collection_new);
+
+ FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (collection_new, ob_new) {
+ if (ob_new != NULL && ob_new->id.override_library != NULL) {
+ Base *base;
+ if ((base = BKE_view_layer_base_find(view_layer, ob_new)) == NULL) {
+ BKE_collection_object_add_from(bmain, scene, obact, ob_new);
+ base = BKE_view_layer_base_find(view_layer, ob_new);
+ DEG_id_tag_update_ex(bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS);
+ }
- FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (new_collection, new_ob) {
- if (new_ob != NULL && new_ob->id.override_library != NULL) {
- if ((base = BKE_view_layer_base_find(view_layer, new_ob)) == NULL) {
- BKE_collection_object_add_from(bmain, scene, obcollection, new_ob);
- base = BKE_view_layer_base_find(view_layer, new_ob);
- DEG_id_tag_update_ex(bmain, &new_ob->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS);
+ if (ob_new == (Object *)obact->id.newid) {
+ /* TODO: is setting active needed? */
+ BKE_view_layer_base_select_and_set_active(view_layer, base);
+ }
+ }
}
+ FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
+ break;
+ }
+ case ID_OB: {
+ BKE_collection_object_add_from(bmain, scene, obact, ((Object *)id_root->newid));
+ break;
+ }
+ default:
+ BLI_assert(0);
+ }
- if (new_ob == (Object *)obact->id.newid) {
- /* TODO: is setting active needed? */
- BKE_view_layer_base_select_and_set_active(view_layer, base);
+ /* We need to ensure all new overrides of objects are properly instantiated. */
+ LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
+ Object *ob_new = (Object *)ob->id.newid;
+ if (ob_new != NULL) {
+ BLI_assert(ob_new->id.override_library != NULL &&
+ ob_new->id.override_library->reference == &ob->id);
+
+ Collection *default_instantiating_collection = NULL;
+ Base *base;
+ if ((base = BKE_view_layer_base_find(view_layer, ob_new)) == NULL) {
+ if (default_instantiating_collection == NULL) {
+ switch (GS(id_root->name)) {
+ case ID_GR: {
+ default_instantiating_collection = BKE_collection_add(
+ bmain, (Collection *)id_root, "OVERRIDE_HIDDEN");
+ break;
+ }
+ case ID_OB: {
+ /* Add the new container collection to one of the collections instantiating the
+ * root object, or scene's master collection if none found. */
+ Object *ob_root = (Object *)id_root;
+ LISTBASE_FOREACH (Collection *, collection, &bmain->collections) {
+ if (BKE_collection_has_object(collection, ob_root) &&
+ BKE_view_layer_has_collection(view_layer, collection)) {
+ default_instantiating_collection = BKE_collection_add(
+ bmain, collection, "OVERRIDE_HIDDEN");
+ }
+ }
+ if (default_instantiating_collection == NULL) {
+ default_instantiating_collection = BKE_collection_add(
+ bmain, scene->master_collection, "OVERRIDE_HIDDEN");
+ }
+ break;
+ }
+ default:
+ BLI_assert(0);
+ }
+ /* Hide the collection from viewport and render. */
+ default_instantiating_collection->flag |= COLLECTION_RESTRICT_VIEWPORT |
+ COLLECTION_RESTRICT_RENDER;
+ }
+
+ BKE_collection_object_add(bmain, default_instantiating_collection, ob_new);
+ DEG_id_tag_update_ex(bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS);
}
- /* We still want to store all objects' current override status (i.e. change of parent). */
- BKE_lib_override_library_operations_create(bmain, &new_ob->id, true);
}
}
- FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
/* Remove the instance empty from this scene, the items now have an overridden collection
* instead. */
- ED_object_base_free_and_unlink(bmain, scene, obcollection);
-
- /* Also, we'd likely want to lock by default things like
- * transformations of implicitly overridden objects? */
-
- DEG_id_tag_update(&scene->id, 0);
-
- /* Cleanup. */
- BKE_main_id_clear_newpoins(bmain);
- BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
- }
- else if (!ID_IS_OVERRIDABLE_LIBRARY(obact)) {
- BKE_reportf(op->reports,
- RPT_ERROR_INVALID_INPUT,
- "Active object '%s' is not overridable",
- obact->id.name + 2);
- return OPERATOR_CANCELLED;
- }
- /* Else, poll func ensures us that ID_IS_LINKED(obact) is true. */
- else if (obact->type == OB_ARMATURE) {
- BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
-
- obact->id.tag |= LIB_TAG_DOIT;
-
- for (Object *ob = bmain->objects.first; ob != NULL; ob = ob->id.next) {
- make_override_library_tag_object(obact, ob);
+ if (id_root != &obact->id) {
+ ED_object_base_free_and_unlink(bmain, scene, obact);
}
-
- success = BKE_lib_override_library_create_from_tag(bmain);
-
- /* Also, we'd likely want to lock by default things like
- * transformations of implicitly overridden objects? */
-
- /* Cleanup. */
- BKE_main_id_clear_newpoins(bmain);
- BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
- }
- /* TODO: probably more cases where we want to do automated smart things in the future! */
- else {
- /* For now, remapp all local usages of linked ID to local override one here. */
- BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, true);
- success = (BKE_lib_override_library_create_from_id(bmain, &obact->id, true) != NULL);
- BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
}
+ /* Cleanup. */
+ BKE_main_id_clear_newpoins(bmain);
+ BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
+
+ DEG_id_tag_update(&CTX_data_scene(C)->id, ID_RECALC_BASE_FLAGS | ID_RECALC_COPY_ON_WRITE);
WM_event_add_notifier(C, NC_WINDOW, NULL);
return success ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
diff --git a/source/blender/editors/object/object_remesh.c b/source/blender/editors/object/object_remesh.c
index 1d7920b9991..664d4219686 100644
--- a/source/blender/editors/object/object_remesh.c
+++ b/source/blender/editors/object/object_remesh.c
@@ -100,7 +100,12 @@ static bool object_remesh_poll(bContext *C)
{
Object *ob = CTX_data_active_object(C);
- if (ob == NULL) {
+ if (ob == NULL || ob->data == NULL) {
+ return false;
+ }
+
+ if (ID_IS_LINKED(ob) || ID_IS_LINKED(ob->data) || ID_IS_OVERRIDE_LIBRARY(ob->data)) {
+ CTX_wm_operator_poll_msg_set(C, "The remesher cannot worked on linked or override data");
return false;
}
@@ -174,6 +179,11 @@ static int voxel_remesh_exec(bContext *C, wmOperator *op)
BKE_remesh_reproject_sculpt_face_sets(new_mesh, mesh);
}
+ if (mesh->flag & ME_REMESH_REPROJECT_VERTEX_COLORS) {
+ BKE_mesh_runtime_clear_geometry(mesh);
+ BKE_remesh_reproject_vertex_paint(new_mesh, mesh);
+ }
+
BKE_mesh_nomain_to_mesh(new_mesh, mesh, ob, &CD_MASK_MESH, true);
if (mesh->flag & ME_REMESH_SMOOTH_NORMALS) {
@@ -521,7 +531,9 @@ static int voxel_size_edit_invoke(bContext *C, wmOperator *op, const wmEvent *ev
/* Project the selected face in the previous step of the Bounding Box. */
for (int i = 0; i < 4; i++) {
- ED_view3d_project(ar, cd->preview_plane[i], preview_plane_proj[i]);
+ float preview_plane_world_space[3];
+ mul_v3_m4v3(preview_plane_world_space, active_object->obmat, cd->preview_plane[i]);
+ ED_view3d_project(ar, preview_plane_world_space, preview_plane_proj[i]);
}
/* Get the initial X and Y axis of the basis from the edges of the Bounding Box face. */
@@ -569,7 +581,9 @@ static int voxel_size_edit_invoke(bContext *C, wmOperator *op, const wmEvent *ev
copy_v3_v3(cd->text_mat[3], text_pos);
/* Scale the text. */
- const float pixelsize = ED_view3d_pixel_size(rv3d, text_pos);
+ float text_pos_word_space[3];
+ mul_v3_m4v3(text_pos_word_space, active_object->obmat, text_pos);
+ const float pixelsize = ED_view3d_pixel_size(rv3d, text_pos_word_space);
scale_m4_fl(scale_mat, pixelsize * 0.5f);
mul_m4_m4_post(cd->text_mat, scale_mat);
diff --git a/source/blender/editors/object/object_shader_fx.c b/source/blender/editors/object/object_shader_fx.c
index 5db4a5a4f57..d9f37b8e38b 100644
--- a/source/blender/editors/object/object_shader_fx.c
+++ b/source/blender/editors/object/object_shader_fx.c
@@ -200,6 +200,40 @@ int ED_object_shaderfx_move_down(ReportList *UNUSED(reports), Object *ob, Shader
return 1;
}
+bool ED_object_shaderfx_move_to_index(ReportList *reports,
+ Object *ob,
+ ShaderFxData *fx,
+ const int index)
+{
+ BLI_assert(fx != NULL);
+ BLI_assert(index >= 0);
+ if (index >= BLI_listbase_count(&ob->shader_fx)) {
+ BKE_report(reports, RPT_WARNING, "Cannot move effect beyond the end of the stack");
+ return false;
+ }
+
+ int fx_index = BLI_findindex(&ob->shader_fx, fx);
+ BLI_assert(fx_index != -1);
+ if (fx_index < index) {
+ /* Move shaderfx down in list. */
+ for (; fx_index < index; fx_index++) {
+ if (!ED_object_shaderfx_move_down(reports, ob, fx)) {
+ break;
+ }
+ }
+ }
+ else {
+ /* Move shaderfx up in list. */
+ for (; fx_index > index; fx_index--) {
+ if (!ED_object_shaderfx_move_up(reports, ob, fx)) {
+ break;
+ }
+ }
+ }
+
+ return true;
+}
+
/************************ add effect operator *********************/
static int shaderfx_add_exec(bContext *C, wmOperator *op)
@@ -391,8 +425,8 @@ static int shaderfx_remove_invoke(bContext *C, wmOperator *op, const wmEvent *UN
void OBJECT_OT_shaderfx_remove(wmOperatorType *ot)
{
- ot->name = "Remove Grease Pencil Modifier";
- ot->description = "Remove a shaderfx from the active grease pencil object";
+ ot->name = "Remove Grease Pencil Effect";
+ ot->description = "Remove a effect from the active grease pencil object";
ot->idname = "OBJECT_OT_shaderfx_remove";
ot->invoke = shaderfx_remove_invoke;
@@ -433,8 +467,8 @@ static int shaderfx_move_up_invoke(bContext *C, wmOperator *op, const wmEvent *U
void OBJECT_OT_shaderfx_move_up(wmOperatorType *ot)
{
- ot->name = "Move Up Modifier";
- ot->description = "Move shaderfx up in the stack";
+ ot->name = "Move Up Effect";
+ ot->description = "Move effect up in the stack";
ot->idname = "OBJECT_OT_shaderfx_move_up";
ot->invoke = shaderfx_move_up_invoke;
@@ -475,8 +509,8 @@ static int shaderfx_move_down_invoke(bContext *C, wmOperator *op, const wmEvent
void OBJECT_OT_shaderfx_move_down(wmOperatorType *ot)
{
- ot->name = "Move Down Modifier";
- ot->description = "Move shaderfx down in the stack";
+ ot->name = "Move Down Effect";
+ ot->description = "Move effect down in the stack";
ot->idname = "OBJECT_OT_shaderfx_move_down";
ot->invoke = shaderfx_move_down_invoke;
@@ -487,3 +521,55 @@ void OBJECT_OT_shaderfx_move_down(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
edit_shaderfx_properties(ot);
}
+
+/************************ move shaderfx to index operator *********************/
+
+static bool shaderfx_move_to_index_poll(bContext *C)
+{
+ return edit_shaderfx_poll_generic(C, &RNA_ShaderFx, 0);
+}
+
+static int shaderfx_move_to_index_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = ED_object_active_context(C);
+ ShaderFxData *fx = edit_shaderfx_property_get(op, ob, 0);
+ int index = RNA_int_get(op->ptr, "index");
+
+ if (!fx || !ED_object_shaderfx_move_to_index(op->reports, ob, fx, index)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+static int shaderfx_move_to_index_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+{
+ if (edit_shaderfx_invoke_properties(C, op)) {
+ return shaderfx_move_to_index_exec(C, op);
+ }
+ else {
+ return OPERATOR_CANCELLED;
+ }
+}
+
+void OBJECT_OT_shaderfx_move_to_index(wmOperatorType *ot)
+{
+ ot->name = "Move Effect to Index";
+ ot->idname = "OBJECT_OT_shaderfx_move_to_index";
+ ot->description =
+ "Change the effect's position in the list so it evaluates after the set number of "
+ "others";
+
+ ot->invoke = shaderfx_move_to_index_invoke;
+ ot->exec = shaderfx_move_to_index_exec;
+ ot->poll = shaderfx_move_to_index_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
+ edit_shaderfx_properties(ot);
+ RNA_def_int(
+ ot->srna, "index", 0, 0, INT_MAX, "Index", "The index to move the effect to", 0, INT_MAX);
+}
diff --git a/source/blender/editors/object/object_shapekey.c b/source/blender/editors/object/object_shapekey.c
index 26d33bbc375..71778f92349 100644
--- a/source/blender/editors/object/object_shapekey.c
+++ b/source/blender/editors/object/object_shapekey.c
@@ -216,40 +216,38 @@ static bool object_shape_key_mirror(
/********************** shape key operators *********************/
-static bool shape_key_mode_poll(bContext *C)
+static bool shape_key_poll(bContext *C)
{
Object *ob = ED_object_context(C);
ID *data = (ob) ? ob->data : NULL;
- return (ob && !ID_IS_LINKED(ob) && data && !ID_IS_LINKED(data) && ob->mode != OB_MODE_EDIT);
+
+ return (ob != NULL && !ID_IS_LINKED(ob) && !ID_IS_OVERRIDE_LIBRARY(ob) && data != NULL &&
+ !ID_IS_LINKED(data) && !ID_IS_OVERRIDE_LIBRARY(data));
}
-static bool shape_key_mode_exists_poll(bContext *C)
+static bool shape_key_mode_poll(bContext *C)
{
Object *ob = ED_object_context(C);
- ID *data = (ob) ? ob->data : NULL;
- /* same as shape_key_mode_poll */
- return (ob && !ID_IS_LINKED(ob) && data && !ID_IS_LINKED(data) && ob->mode != OB_MODE_EDIT) &&
- /* check a keyblock exists */
- (BKE_keyblock_from_object(ob) != NULL);
+ return (shape_key_poll(C) && ob->mode != OB_MODE_EDIT);
}
-static bool shape_key_move_poll(bContext *C)
+static bool shape_key_mode_exists_poll(bContext *C)
{
- /* Same as shape_key_mode_exists_poll above, but ensure we have at least two shapes! */
Object *ob = ED_object_context(C);
- ID *data = (ob) ? ob->data : NULL;
- Key *key = BKE_key_from_object(ob);
- return (ob && !ID_IS_LINKED(ob) && data && !ID_IS_LINKED(data) && ob->mode != OB_MODE_EDIT &&
- key && key->totkey > 1);
+ return (shape_key_mode_poll(C) &&
+ /* check a keyblock exists */
+ (BKE_keyblock_from_object(ob) != NULL));
}
-static bool shape_key_poll(bContext *C)
+static bool shape_key_move_poll(bContext *C)
{
+ /* Same as shape_key_mode_exists_poll above, but ensure we have at least two shapes! */
Object *ob = ED_object_context(C);
- ID *data = (ob) ? ob->data : NULL;
- return (ob && !ID_IS_LINKED(ob) && data && !ID_IS_LINKED(data));
+ Key *key = BKE_key_from_object(ob);
+
+ return (shape_key_mode_poll(C) && key != NULL && key->totkey > 1);
}
static int shape_key_add_exec(bContext *C, wmOperator *op)
diff --git a/source/blender/editors/object/object_transform.c b/source/blender/editors/object/object_transform.c
index 132b530455e..161611d59c9 100644
--- a/source/blender/editors/object/object_transform.c
+++ b/source/blender/editors/object/object_transform.c
@@ -1098,15 +1098,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
mul_m4_v3(obedit->imat, cent);
}
else {
- if (around == V3D_AROUND_CENTER_MEDIAN) {
- if (em->bm->totvert) {
- const float total_div = 1.0f / (float)em->bm->totvert;
- BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
- madd_v3_v3fl(cent, eve->co, total_div);
- }
- }
- }
- else {
+ if (around == V3D_AROUND_CENTER_BOUNDS) {
float min[3], max[3];
INIT_MINMAX(min, max);
BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
@@ -1114,6 +1106,14 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
}
mid_v3_v3v3(cent, min, max);
}
+ else { /* #V3D_AROUND_CENTER_MEDIAN. */
+ if (em->bm->totvert) {
+ const float total_div = 1.0f / (float)em->bm->totvert;
+ BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
+ madd_v3_v3fl(cent, eve->co, total_div);
+ }
+ }
+ }
}
BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
@@ -1211,12 +1211,12 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
else if (centermode == ORIGIN_TO_CENTER_OF_MASS_VOLUME) {
BKE_mesh_center_of_volume(me, cent);
}
- else if (around == V3D_AROUND_CENTER_MEDIAN) {
- BKE_mesh_center_median(me, cent);
- }
- else {
+ else if (around == V3D_AROUND_CENTER_BOUNDS) {
BKE_mesh_center_bounds(me, cent);
}
+ else { /* #V3D_AROUND_CENTER_MEDIAN. */
+ BKE_mesh_center_median(me, cent);
+ }
negate_v3_v3(cent_neg, cent);
BKE_mesh_translate(me, cent_neg, 1);
@@ -1231,12 +1231,12 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
if (centermode == ORIGIN_TO_CURSOR) {
/* done */
}
- else if (around == V3D_AROUND_CENTER_MEDIAN) {
- BKE_curve_center_median(cu, cent);
- }
- else {
+ else if (around == V3D_AROUND_CENTER_BOUNDS) {
BKE_curve_center_bounds(cu, cent);
}
+ else { /* #V3D_AROUND_CENTER_MEDIAN. */
+ BKE_curve_center_median(cu, cent);
+ }
/* don't allow Z change if curve is 2D */
if ((ob->type == OB_CURVE) && !(cu->flag & CU_3D)) {
@@ -1324,12 +1324,12 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
if (centermode == ORIGIN_TO_CURSOR) {
/* done */
}
- else if (around == V3D_AROUND_CENTER_MEDIAN) {
- BKE_mball_center_median(mb, cent);
- }
- else {
+ else if (around == V3D_AROUND_CENTER_BOUNDS) {
BKE_mball_center_bounds(mb, cent);
}
+ else { /* #V3D_AROUND_CENTER_MEDIAN. */
+ BKE_mball_center_median(mb, cent);
+ }
negate_v3_v3(cent_neg, cent);
BKE_mball_translate(mb, cent_neg);
@@ -1351,12 +1351,12 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
if (centermode == ORIGIN_TO_CURSOR) {
/* done */
}
- else if (around == V3D_AROUND_CENTER_MEDIAN) {
- BKE_lattice_center_median(lt, cent);
- }
- else {
+ else if (around == V3D_AROUND_CENTER_BOUNDS) {
BKE_lattice_center_bounds(lt, cent);
}
+ else { /* #V3D_AROUND_CENTER_MEDIAN. */
+ BKE_lattice_center_median(lt, cent);
+ }
negate_v3_v3(cent_neg, cent);
BKE_lattice_translate(lt, cent_neg, 1);
diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.c
index fb79cfb910e..7ca2a89f61d 100644
--- a/source/blender/editors/object/object_vgroup.c
+++ b/source/blender/editors/object/object_vgroup.c
@@ -77,7 +77,10 @@
#include "object_intern.h"
-/************************ Exported Functions **********************/
+/* -------------------------------------------------------------------- */
+/** \name Public Utility Functions
+ * \{ */
+
static bool vertex_group_use_vert_sel(Object *ob)
{
if (ob->mode == OB_MODE_EDIT) {
@@ -682,7 +685,11 @@ static void vgroup_copy_active_to_sel(Object *ob, eVGroupSelect subset_type)
MEM_freeN((void *)vgroup_validmap);
}
-/***********************Start weight transfer (WT)*********************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Shared Weight Transfer Operator Properties
+ * \{ */
static const EnumPropertyItem WT_vertex_group_select_item[] = {
{WT_VGROUP_ACTIVE, "ACTIVE", 0, "Active Group", "The active Vertex Group"},
@@ -779,7 +786,15 @@ static void vgroup_operator_subset_select_props(wmOperatorType *ot, bool use_act
ot->prop = prop;
}
-/***********************End weight transfer (WT)***********************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name High Level Vertex Group Add/Remove
+ *
+ * Wrap lower level `BKE` functions.
+ *
+ * \note that operations on many vertices should use #ED_vgroup_parray_alloc.
+ * \{ */
/* for Mesh in Object mode */
/* allows editmode for Lattice */
@@ -976,7 +991,11 @@ void ED_vgroup_select_by_name(Object *ob, const char *name)
ob->actdef = BKE_object_defgroup_name_index(ob, name) + 1;
}
-/********************** Operator Implementations *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Operator Function Implementations
+ * \{ */
/* only in editmode */
static void vgroup_select_verts(Object *ob, int select)
@@ -2634,48 +2653,49 @@ static void vgroup_assign_verts(Object *ob, const float weight)
}
}
-/********************** vertex group operators *********************/
+/** \} */
-static bool vertex_group_poll(bContext *C)
+/* -------------------------------------------------------------------- */
+/** \name Shared Operator Poll Functions
+ * \{ */
+
+static bool vertex_group_supported_poll(bContext *C)
{
Object *ob = ED_object_context(C);
ID *data = (ob) ? ob->data : NULL;
- return (ob && !ID_IS_LINKED(ob) && data && !ID_IS_LINKED(data) &&
- OB_TYPE_SUPPORT_VGROUP(ob->type) && ob->defbase.first);
+ return (ob && !ID_IS_LINKED(ob) && OB_TYPE_SUPPORT_VGROUP(ob->type) &&
+ !ID_IS_OVERRIDE_LIBRARY(ob) && data && !ID_IS_LINKED(data) &&
+ !ID_IS_OVERRIDE_LIBRARY(data));
}
-static bool vertex_group_supported_poll(bContext *C)
+static bool vertex_group_poll(bContext *C)
{
Object *ob = ED_object_context(C);
- ID *data = (ob) ? ob->data : NULL;
- return (ob && !ID_IS_LINKED(ob) && OB_TYPE_SUPPORT_VGROUP(ob->type) && data &&
- !ID_IS_LINKED(data));
+
+ return (vertex_group_supported_poll(C) && ob->defbase.first);
}
static bool vertex_group_mesh_poll(bContext *C)
{
Object *ob = ED_object_context(C);
- ID *data = (ob) ? ob->data : NULL;
- return (ob && !ID_IS_LINKED(ob) && data && !ID_IS_LINKED(data) && ob->type == OB_MESH &&
- ob->defbase.first);
+ return (vertex_group_poll(C) && ob->type == OB_MESH);
}
static bool UNUSED_FUNCTION(vertex_group_mesh_supported_poll)(bContext *C)
{
Object *ob = ED_object_context(C);
- ID *data = (ob) ? ob->data : NULL;
- return (ob && !ID_IS_LINKED(ob) && ob->type == OB_MESH && data && !ID_IS_LINKED(data));
+
+ return (vertex_group_supported_poll(C) && ob->type == OB_MESH);
}
static bool UNUSED_FUNCTION(vertex_group_poll_edit)(bContext *C)
{
Object *ob = ED_object_context(C);
- ID *data = (ob) ? ob->data : NULL;
- if (!(ob && !ID_IS_LINKED(ob) && data && !ID_IS_LINKED(data))) {
- return 0;
+ if (!vertex_group_supported_poll(C)) {
+ return false;
}
return BKE_object_is_in_editmode_vgroup(ob);
@@ -2687,9 +2707,8 @@ static bool vertex_group_vert_poll_ex(bContext *C,
const short ob_type_flag)
{
Object *ob = ED_object_context(C);
- ID *data = (ob) ? ob->data : NULL;
- if (!(ob && !ID_IS_LINKED(ob) && data && !ID_IS_LINKED(data))) {
+ if (!vertex_group_supported_poll(C)) {
return false;
}
@@ -2747,14 +2766,13 @@ static bool vertex_group_mesh_vert_select_poll(bContext *C)
static bool vertex_group_vert_select_unlocked_poll(bContext *C)
{
Object *ob = ED_object_context(C);
- ID *data = (ob) ? ob->data : NULL;
- if (!(ob && !ID_IS_LINKED(ob) && data && !ID_IS_LINKED(data))) {
- return 0;
+ if (!vertex_group_supported_poll(C)) {
+ return false;
}
if (!(BKE_object_is_in_editmode_vgroup(ob) || BKE_object_is_in_wpaint_select_vert(ob))) {
- return 0;
+ return false;
}
if (ob->actdef != 0) {
@@ -2763,26 +2781,31 @@ static bool vertex_group_vert_select_unlocked_poll(bContext *C)
return !(dg->flag & DG_LOCK_WEIGHT);
}
}
- return 1;
+ return true;
}
static bool vertex_group_vert_select_mesh_poll(bContext *C)
{
Object *ob = ED_object_context(C);
- ID *data = (ob) ? ob->data : NULL;
- if (!(ob && !ID_IS_LINKED(ob) && data && !ID_IS_LINKED(data))) {
- return 0;
+ if (!vertex_group_supported_poll(C)) {
+ return false;
}
/* only difference to #vertex_group_vert_select_poll */
if (ob->type != OB_MESH) {
- return 0;
+ return false;
}
return (BKE_object_is_in_editmode_vgroup(ob) || BKE_object_is_in_wpaint_select_vert(ob));
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Vertex Group Add Operator
+ * \{ */
+
static int vertex_group_add_exec(bContext *C, wmOperator *UNUSED(op))
{
Object *ob = ED_object_context(C);
@@ -2811,6 +2834,12 @@ void OBJECT_OT_vertex_group_add(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Vertex Group Remove Operator
+ * \{ */
+
static int vertex_group_remove_exec(bContext *C, wmOperator *op)
{
Object *ob = ED_object_context(C);
@@ -2858,6 +2887,12 @@ void OBJECT_OT_vertex_group_remove(wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Vertex Group Assign Operator
+ * \{ */
+
static int vertex_group_assign_exec(bContext *C, wmOperator *UNUSED(op))
{
ToolSettings *ts = CTX_data_tool_settings(C);
@@ -2888,6 +2923,12 @@ void OBJECT_OT_vertex_group_assign(wmOperatorType *ot)
ot->flag = /*OPTYPE_REGISTER|*/ OPTYPE_UNDO;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Vertex Group Assign New Operator
+ * \{ */
+
/* NOTE: just a wrapper around vertex_group_assign_exec(), except we add these to a new group */
static int vertex_group_assign_new_exec(bContext *C, wmOperator *op)
{
@@ -2917,6 +2958,12 @@ void OBJECT_OT_vertex_group_assign_new(wmOperatorType *ot)
ot->flag = /*OPTYPE_REGISTER|*/ OPTYPE_UNDO;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Vertex Group Remove From Operator
+ * \{ */
+
static int vertex_group_remove_from_exec(bContext *C, wmOperator *op)
{
const bool use_all_groups = RNA_boolean_get(op->ptr, "use_all_groups");
@@ -2968,6 +3015,12 @@ void OBJECT_OT_vertex_group_remove_from(wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Vertex Group Select Operator
+ * \{ */
+
static int vertex_group_select_exec(bContext *C, wmOperator *UNUSED(op))
{
Object *ob = ED_object_context(C);
@@ -2998,6 +3051,12 @@ void OBJECT_OT_vertex_group_select(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Vertex Group Deselect Operator
+ * \{ */
+
static int vertex_group_deselect_exec(bContext *C, wmOperator *UNUSED(op))
{
Object *ob = ED_object_context(C);
@@ -3037,6 +3096,12 @@ static int vertex_group_copy_exec(bContext *C, wmOperator *UNUSED(op))
return OPERATOR_FINISHED;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Vertex Group Copy Operator
+ * \{ */
+
void OBJECT_OT_vertex_group_copy(wmOperatorType *ot)
{
/* identifiers */
@@ -3074,6 +3139,12 @@ static int vertex_group_levels_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Vertex Group Levels Operator
+ * \{ */
+
void OBJECT_OT_vertex_group_levels(wmOperatorType *ot)
{
/* identifiers */
@@ -3115,6 +3186,12 @@ static int vertex_group_normalize_exec(bContext *C, wmOperator *UNUSED(op))
}
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Vertex Group Normalize Operator
+ * \{ */
+
void OBJECT_OT_vertex_group_normalize(wmOperatorType *ot)
{
/* identifiers */
@@ -3158,6 +3235,12 @@ static int vertex_group_normalize_all_exec(bContext *C, wmOperator *op)
}
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Vertex Group Normalize All Operator
+ * \{ */
+
void OBJECT_OT_vertex_group_normalize_all(wmOperatorType *ot)
{
/* identifiers */
@@ -3182,6 +3265,12 @@ void OBJECT_OT_vertex_group_normalize_all(wmOperatorType *ot)
"Keep the values of the active group while normalizing others");
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Vertex Group Fix Position Operator
+ * \{ */
+
static int vertex_group_fix_exec(bContext *C, wmOperator *op)
{
Object *ob = CTX_data_active_object(C);
@@ -3259,6 +3348,12 @@ void OBJECT_OT_vertex_group_fix(wmOperatorType *ot)
1.f);
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Vertex Group Lock Operator
+ * \{ */
+
static int vertex_group_lock_exec(bContext *C, wmOperator *op)
{
Object *ob = CTX_data_active_object(C);
@@ -3361,6 +3456,12 @@ void OBJECT_OT_vertex_group_lock(wmOperatorType *ot)
"Apply the action based on vertex group selection");
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Vertex Group Invert Operator
+ * \{ */
+
static int vertex_group_invert_exec(bContext *C, wmOperator *op)
{
Object *ob = ED_object_context(C);
@@ -3410,6 +3511,12 @@ void OBJECT_OT_vertex_group_invert(wmOperatorType *ot)
"Remove verts from groups that have zero weight after inverting");
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Vertex Group Invert Operator
+ * \{ */
+
static int vertex_group_smooth_exec(bContext *C, wmOperator *op)
{
const float fac = RNA_float_get(op->ptr, "factor");
@@ -3482,6 +3589,12 @@ void OBJECT_OT_vertex_group_smooth(wmOperatorType *ot)
1.0f);
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Vertex Group Clean Operator
+ * \{ */
+
static int vertex_group_clean_exec(bContext *C, wmOperator *op)
{
Object *ob = ED_object_context(C);
@@ -3535,6 +3648,12 @@ void OBJECT_OT_vertex_group_clean(wmOperatorType *ot)
"Keep verts assigned to at least one group when cleaning");
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Vertex Group Quantize Operator
+ * \{ */
+
static int vertex_group_quantize_exec(bContext *C, wmOperator *op)
{
Object *ob = ED_object_context(C);
@@ -3574,6 +3693,12 @@ void OBJECT_OT_vertex_group_quantize(wmOperatorType *ot)
RNA_def_int(ot->srna, "steps", 4, 1, 1000, "Steps", "Number of steps between 0 and 1", 1, 100);
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Vertex Group Limit Total Operator
+ * \{ */
+
static int vertex_group_limit_total_exec(bContext *C, wmOperator *op)
{
Object *ob = ED_object_context(C);
@@ -3625,6 +3750,12 @@ void OBJECT_OT_vertex_group_limit_total(wmOperatorType *ot)
RNA_def_int(ot->srna, "limit", 4, 1, 32, "Limit", "Maximum number of deform weights", 1, 32);
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Vertex Group Mirror Operator
+ * \{ */
+
static int vertex_group_mirror_exec(bContext *C, wmOperator *op)
{
Object *ob = ED_object_context(C);
@@ -3677,6 +3808,12 @@ void OBJECT_OT_vertex_group_mirror(wmOperatorType *ot)
"Use topology based mirroring (for when both sides of mesh have matching, unique topology)");
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Vertex Group Copy to Linked Operator
+ * \{ */
+
static int vertex_group_copy_to_linked_exec(bContext *C, wmOperator *UNUSED(op))
{
Scene *scene = CTX_data_scene(C);
@@ -3720,6 +3857,12 @@ void OBJECT_OT_vertex_group_copy_to_linked(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Vertex Group Copy to Selected Operator
+ * \{ */
+
static int vertex_group_copy_to_selected_exec(bContext *C, wmOperator *op)
{
Object *obact = ED_object_context(C);
@@ -3768,6 +3911,12 @@ void OBJECT_OT_vertex_group_copy_to_selected(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Vertex Group Set Active Operator
+ * \{ */
+
static int set_active_group_exec(bContext *C, wmOperator *op)
{
Object *ob = ED_object_context(C);
@@ -3836,6 +3985,12 @@ void OBJECT_OT_vertex_group_set_active(wmOperatorType *ot)
ot->prop = prop;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Vertex Group Sort Operator
+ * \{ */
+
/* creates the name_array parameter for vgroup_do_remap, call this before fiddling
* with the order of vgroups then call vgroup_do_remap after */
static char *vgroup_init_remap(Object *ob)
@@ -4030,6 +4185,12 @@ void OBJECT_OT_vertex_group_sort(wmOperatorType *ot)
RNA_def_enum(ot->srna, "sort_type", vgroup_sort_type, SORT_TYPE_NAME, "Sort type", "Sort type");
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Vertex Group Move Operator
+ * \{ */
+
static int vgroup_move_exec(bContext *C, wmOperator *op)
{
Object *ob = ED_object_context(C);
@@ -4089,6 +4250,12 @@ void OBJECT_OT_vertex_group_move(wmOperatorType *ot)
"Direction to move the active vertex group towards");
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Vertex Group Weight Paste Operator
+ * \{ */
+
static void vgroup_copy_active_to_sel_single(Object *ob, const int def_nr)
{
MDeformVert *dvert_act;
@@ -4218,6 +4385,12 @@ void OBJECT_OT_vertex_weight_paste(wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN);
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Vertex Group Weight Delete Operator
+ * \{ */
+
static int vertex_weight_delete_exec(bContext *C, wmOperator *op)
{
Object *ob = ED_object_context(C);
@@ -4262,6 +4435,12 @@ void OBJECT_OT_vertex_weight_delete(wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN);
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Vertex Group Set Active by Weight Operator
+ * \{ */
+
static int vertex_weight_set_active_exec(bContext *C, wmOperator *op)
{
Object *ob = ED_object_context(C);
@@ -4303,6 +4482,12 @@ void OBJECT_OT_vertex_weight_set_active(wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN);
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Vertex Group Normalize Active Vertex Operator
+ * \{ */
+
static int vertex_weight_normalize_active_vertex_exec(bContext *C, wmOperator *UNUSED(op))
{
Object *ob = ED_object_context(C);
@@ -4338,6 +4523,12 @@ void OBJECT_OT_vertex_weight_normalize_active_vertex(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Vertex Group Copy Weights from Active Operator
+ * \{ */
+
static int vertex_weight_copy_exec(bContext *C, wmOperator *UNUSED(op))
{
Object *ob = ED_object_context(C);
@@ -4366,3 +4557,5 @@ void OBJECT_OT_vertex_weight_copy(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
+
+/** \} */
diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c
index ef5ed806c1e..457c8ba30e6 100644
--- a/source/blender/editors/physics/particle_edit.c
+++ b/source/blender/editors/physics/particle_edit.c
@@ -4103,7 +4103,7 @@ static void brush_add_count_iter(void *__restrict iter_data_v,
BrushAddCountIterTLSData *tls = tls_v->userdata_chunk;
const int number = iter_data->number;
const short size = iter_data->size;
- const short size2 = size * size;
+ const int size2 = size * size;
float dmx, dmy;
if (number > 1) {
dmx = size;
diff --git a/source/blender/editors/physics/physics_fluid.c b/source/blender/editors/physics/physics_fluid.c
index 8524870c15e..8f6b5aa8bc3 100644
--- a/source/blender/editors/physics/physics_fluid.c
+++ b/source/blender/editors/physics/physics_fluid.c
@@ -270,31 +270,31 @@ static void fluid_bake_sequence(FluidJob *job)
*(job->do_update) = true;
}
- /* Get current pause frame (pointer) - depending on bake type */
+ /* Get current pause frame (pointer) - depending on bake type. */
pause_frame = job->pause_frame;
- /* Set frame to start point (depending on current pause frame value) */
+ /* Set frame to start point (depending on current pause frame value). */
is_first_frame = ((*pause_frame) == 0);
frame = is_first_frame ? mds->cache_frame_start : (*pause_frame);
- /* Save orig frame and update scene frame */
+ /* Save orig frame and update scene frame. */
orig_frame = CFRA;
CFRA = frame;
- /* Loop through selected frames */
+ /* Loop through selected frames. */
for (; frame <= mds->cache_frame_end; frame++) {
const float progress = (frame - mds->cache_frame_start) / (float)frames;
- /* Keep track of pause frame - needed to init future loop */
+ /* Keep track of pause frame - needed to init future loop. */
(*pause_frame) = frame;
- /* If user requested stop, quit baking */
+ /* If user requested stop, quit baking. */
if (G.is_break) {
job->success = 0;
return;
}
- /* Update progress bar */
+ /* Update progress bar. */
if (job->do_update) {
*(job->do_update) = true;
}
@@ -304,17 +304,17 @@ static void fluid_bake_sequence(FluidJob *job)
CFRA = frame;
- /* Update animation system */
+ /* Update animation system. */
ED_update_for_newframe(job->bmain, job->depsgraph);
- /* If user requested stop, quit baking */
+ /* If user requested stop, quit baking. */
if (G.is_break) {
job->success = 0;
return;
}
}
- /* Restore frame position that we were on before bake */
+ /* Restore frame position that we were on before bake. */
CFRA = orig_frame;
}
@@ -355,9 +355,9 @@ static void fluid_bake_endjob(void *customdata)
WM_set_locked_interface(G_MAIN->wm.first, false);
/* Bake was successful:
- * Report for ended bake and how long it took */
+ * Report for ended bake and how long it took. */
if (job->success) {
- /* Show bake info */
+ /* Show bake info. */
WM_reportf(
RPT_INFO, "Fluid: %s complete! (%.2f)", job->name, PIL_check_seconds_timer() - job->start);
}
@@ -365,7 +365,7 @@ static void fluid_bake_endjob(void *customdata)
if (mds->error[0] != '\0') {
WM_reportf(RPT_ERROR, "Fluid: %s failed: %s", job->name, mds->error);
}
- else { /* User canceled the bake */
+ else { /* User canceled the bake. */
WM_reportf(RPT_WARNING, "Fluid: %s canceled!", job->name);
}
}
diff --git a/source/blender/editors/physics/physics_pointcache.c b/source/blender/editors/physics/physics_pointcache.c
index 700a94e4f93..5bd56baf41e 100644
--- a/source/blender/editors/physics/physics_pointcache.c
+++ b/source/blender/editors/physics/physics_pointcache.c
@@ -132,7 +132,7 @@ static void ptcache_job_endjob(void *customdata)
WM_set_locked_interface(job->wm, false);
WM_main_add_notifier(NC_SCENE | ND_FRAME, scene);
- WM_main_add_notifier(NC_OBJECT | ND_POINTCACHE, job->baker->pid.ob);
+ WM_main_add_notifier(NC_OBJECT | ND_POINTCACHE, job->baker->pid.owner_id);
}
static void ptcache_free_bake(PointCache *cache)
diff --git a/source/blender/editors/render/render_update.c b/source/blender/editors/render/render_update.c
index 10f69f3fe9d..7d0ad42c703 100644
--- a/source/blender/editors/render/render_update.c
+++ b/source/blender/editors/render/render_update.c
@@ -178,7 +178,7 @@ void ED_render_engine_area_exit(Main *bmain, ScrArea *area)
}
}
-void ED_render_engine_changed(Main *bmain)
+void ED_render_engine_changed(Main *bmain, const bool update_scene_data)
{
/* on changing the render engine type, clear all running render engines */
for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
@@ -198,7 +198,7 @@ void ED_render_engine_changed(Main *bmain)
update_ctx.view_layer = view_layer;
ED_render_id_flush_update(&update_ctx, &scene->id);
}
- if (scene->nodetree) {
+ if (scene->nodetree && update_scene_data) {
ntreeCompositUpdateRLayers(scene->nodetree);
}
}
diff --git a/source/blender/editors/scene/scene_edit.c b/source/blender/editors/scene/scene_edit.c
index c32bb69db4e..a1fa5e2d655 100644
--- a/source/blender/editors/scene/scene_edit.c
+++ b/source/blender/editors/scene/scene_edit.c
@@ -122,7 +122,7 @@ void ED_scene_change_update(Main *bmain, Scene *scene, ViewLayer *layer)
DEG_graph_relations_update(depsgraph, bmain, scene, layer);
DEG_on_visible_update(bmain, false);
- ED_render_engine_changed(bmain);
+ ED_render_engine_changed(bmain, false);
ED_update_for_newframe(bmain, depsgraph);
}
diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c
index c04122edd36..a182dd662af 100644
--- a/source/blender/editors/screen/area.c
+++ b/source/blender/editors/screen/area.c
@@ -646,7 +646,7 @@ void ED_region_tag_redraw(ARegion *region)
void ED_region_tag_redraw_cursor(ARegion *region)
{
if (region) {
- region->do_draw_overlay = RGN_DRAW;
+ region->do_draw_paintcursor = RGN_DRAW;
}
}
@@ -1145,7 +1145,7 @@ static void region_overlap_fix(ScrArea *area, ARegion *region)
/* find overlapping previous region on same place */
for (ar1 = region->prev; ar1; ar1 = ar1->prev) {
- if (ar1->flag & (RGN_FLAG_HIDDEN)) {
+ if (ar1->flag & RGN_FLAG_HIDDEN) {
continue;
}
@@ -1194,7 +1194,7 @@ static void region_overlap_fix(ScrArea *area, ARegion *region)
/* At this point, 'region' is in its final position and still open.
* Make a final check it does not overlap any previous 'other side' region. */
for (ar1 = region->prev; ar1; ar1 = ar1->prev) {
- if (ar1->flag & (RGN_FLAG_HIDDEN)) {
+ if (ar1->flag & RGN_FLAG_HIDDEN) {
continue;
}
if (ELEM(ar1->alignment, RGN_ALIGN_FLOAT)) {
@@ -1556,7 +1556,14 @@ static void region_rect_recursive(
/* Tag for redraw if size changes. */
if (region->winx != prev_winx || region->winy != prev_winy) {
- ED_region_tag_redraw(region);
+ /* 3D View needs a full rebuild in case a progressive render runs. Rest can live with
+ * no-rebuild (e.g. Outliner) */
+ if (area->spacetype == SPACE_VIEW3D) {
+ ED_region_tag_redraw(region);
+ }
+ else {
+ ED_region_tag_redraw_no_rebuild(region);
+ }
}
/* Clear, initialize on demand. */
@@ -2348,9 +2355,9 @@ BLI_INLINE bool streq_array_any(const char *s, const char *arr[])
/**
* Builds the panel layout for the input \a panel or type \a pt.
*
- * \param panel The panel to draw. Can be null, in which case a panel with the type of \a pt will
- * be created.
- * \param unique_panel_str A unique identifier for the name of the \a uiBlock associated with the
+ * \param panel: The panel to draw. Can be null,
+ * in which case a panel with the type of \a pt will be created.
+ * \param unique_panel_str: A unique identifier for the name of the \a uiBlock associated with the
* panel. Used when the panel is an instanced panel so a unique identifier is needed to find the
* correct old \a uiBlock, and NULL otherwise.
*/
@@ -2566,16 +2573,16 @@ void ED_region_panels_layout_ex(const bContext *C,
/* only allow scrolling in vertical direction */
v2d->keepofs |= V2D_LOCKOFS_X | V2D_KEEPOFS_Y;
v2d->keepofs &= ~(V2D_LOCKOFS_Y | V2D_KEEPOFS_X);
- v2d->scroll &= ~(V2D_SCROLL_BOTTOM);
- v2d->scroll |= (V2D_SCROLL_RIGHT);
+ v2d->scroll &= ~V2D_SCROLL_BOTTOM;
+ v2d->scroll |= V2D_SCROLL_RIGHT;
}
else {
/* for now, allow scrolling in both directions (since layouts are optimized for vertical,
* they often don't fit in horizontal layout)
*/
v2d->keepofs &= ~(V2D_LOCKOFS_X | V2D_LOCKOFS_Y | V2D_KEEPOFS_X | V2D_KEEPOFS_Y);
- v2d->scroll |= (V2D_SCROLL_BOTTOM);
- v2d->scroll &= ~(V2D_SCROLL_RIGHT);
+ v2d->scroll |= V2D_SCROLL_BOTTOM;
+ v2d->scroll &= ~V2D_SCROLL_RIGHT;
}
/* collect categories */
@@ -2788,9 +2795,7 @@ void ED_region_panels_draw(const bContext *C, ARegion *region)
mask_buf.xmax -= UI_PANEL_CATEGORY_MARGIN_WIDTH;
mask = &mask_buf;
}
- View2DScrollers *scrollers = UI_view2d_scrollers_calc(v2d, mask);
- UI_view2d_scrollers_draw(v2d, scrollers);
- UI_view2d_scrollers_free(scrollers);
+ UI_view2d_scrollers_draw(v2d, mask);
}
void ED_region_panels_ex(
diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c
index 6f004238522..b6f210d7f13 100644
--- a/source/blender/editors/screen/screen_edit.c
+++ b/source/blender/editors/screen/screen_edit.c
@@ -1331,6 +1331,7 @@ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *area, const
oldscreen->animtimer = NULL;
ED_screen_change(C, screen);
+ ED_area_tag_refresh(fullsa);
BKE_workspace_layout_remove(CTX_data_main(C), workspace, layout_old);
diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c
index b2243f2ccb9..5de8ccd404d 100644
--- a/source/blender/editors/screen/screen_ops.c
+++ b/source/blender/editors/screen/screen_ops.c
@@ -366,19 +366,20 @@ bool ED_operator_object_active_editable_mesh(bContext *C)
{
Object *ob = ED_object_active_context(C);
return ((ob != NULL) && !ID_IS_LINKED(ob) && !ed_object_hidden(ob) && (ob->type == OB_MESH) &&
- !ID_IS_LINKED(ob->data));
+ !ID_IS_LINKED(ob->data) && !ID_IS_OVERRIDE_LIBRARY(ob->data));
}
bool ED_operator_object_active_editable_font(bContext *C)
{
Object *ob = ED_object_active_context(C);
- return ((ob != NULL) && !ID_IS_LINKED(ob) && !ed_object_hidden(ob) && (ob->type == OB_FONT));
+ return ((ob != NULL) && !ID_IS_LINKED(ob) && !ed_object_hidden(ob) && (ob->type == OB_FONT) &&
+ !ID_IS_LINKED(ob->data) && !ID_IS_OVERRIDE_LIBRARY(ob->data));
}
bool ED_operator_editable_mesh(bContext *C)
{
Mesh *mesh = ED_mesh_context(C);
- return (mesh != NULL) && !ID_IS_LINKED(mesh);
+ return (mesh != NULL) && !ID_IS_LINKED(mesh) && !ID_IS_OVERRIDE_LIBRARY(mesh);
}
bool ED_operator_editmesh(bContext *C)
@@ -1761,7 +1762,7 @@ static void area_move_apply_do(const bContext *C,
screen->do_refresh = true;
redraw_all = true;
}
- ED_area_tag_redraw(area);
+ ED_area_tag_redraw_no_rebuild(area);
}
}
if (redraw_all) {
@@ -4273,68 +4274,67 @@ static void SCREEN_OT_region_context_menu(wmOperatorType *ot)
*
* Animation Step.
* \{ */
+static bool screen_animation_region_supports_time_follow(eSpace_Type spacetype,
+ eRegionType regiontype)
+{
+ return (regiontype == RGN_TYPE_WINDOW &&
+ ELEM(spacetype, SPACE_SEQ, SPACE_GRAPH, SPACE_ACTION, SPACE_NLA)) ||
+ (spacetype == SPACE_CLIP && regiontype == RGN_TYPE_PREVIEW);
+}
-static int match_region_with_redraws(int spacetype,
- int regiontype,
- int redraws,
- bool from_anim_edit)
+static bool match_region_with_redraws(const ScrArea *area,
+ eRegionType regiontype,
+ eScreen_Redraws_Flag redraws,
+ bool from_anim_edit)
{
+ const eSpace_Type spacetype = area->spacetype;
if (regiontype == RGN_TYPE_WINDOW) {
switch (spacetype) {
case SPACE_VIEW3D:
if ((redraws & TIME_ALL_3D_WIN) || from_anim_edit) {
- return 1;
+ return true;
}
break;
case SPACE_GRAPH:
case SPACE_NLA:
if ((redraws & TIME_ALL_ANIM_WIN) || from_anim_edit) {
- return 1;
+ return true;
}
break;
case SPACE_ACTION:
/* if only 1 window or 3d windows, we do timeline too
* NOTE: Now we do action editor in all these cases, since timeline is here. */
if ((redraws & (TIME_ALL_ANIM_WIN | TIME_REGION | TIME_ALL_3D_WIN)) || from_anim_edit) {
- return 1;
+ return true;
}
break;
case SPACE_PROPERTIES:
if (redraws & TIME_ALL_BUTS_WIN) {
- return 1;
+ return true;
}
break;
case SPACE_SEQ:
if ((redraws & (TIME_SEQ | TIME_ALL_ANIM_WIN)) || from_anim_edit) {
- return 1;
+ return true;
}
break;
case SPACE_NODE:
- if (redraws & (TIME_NODES)) {
- return 1;
+ if (redraws & TIME_NODES) {
+ return true;
}
break;
case SPACE_IMAGE:
if ((redraws & TIME_ALL_IMAGE_WIN) || from_anim_edit) {
- return 1;
+ return true;
}
break;
case SPACE_CLIP:
if ((redraws & TIME_CLIPS) || from_anim_edit) {
- return 1;
+ return true;
}
break;
- }
- }
- else if (regiontype == RGN_TYPE_CHANNELS) {
- switch (spacetype) {
- case SPACE_GRAPH:
- case SPACE_ACTION:
- case SPACE_NLA:
- if (redraws & TIME_ALL_ANIM_WIN) {
- return 1;
- }
+ default:
break;
}
}
@@ -4345,30 +4345,68 @@ static int match_region_with_redraws(int spacetype,
* during playback, so asking people to enable special option
* for this is a bit tricky, so add exception here for refreshing
* Properties Editor for SpaceClip always */
- return 1;
+ return true;
}
if (redraws & TIME_ALL_BUTS_WIN) {
- return 1;
+ return true;
}
}
- else if (ELEM(regiontype, RGN_TYPE_HEADER, RGN_TYPE_TOOL_HEADER)) {
+ else if (regiontype == RGN_TYPE_HEADER) {
if (spacetype == SPACE_ACTION) {
- return 1;
+ /* The timeline shows the current frame in the header. Other headers
+ * don't need to be updated. */
+ SpaceAction *saction = (SpaceAction *)area->spacedata.first;
+ return saction->mode == SACTCONT_TIMELINE;
}
}
else if (regiontype == RGN_TYPE_PREVIEW) {
switch (spacetype) {
case SPACE_SEQ:
if (redraws & (TIME_SEQ | TIME_ALL_ANIM_WIN)) {
- return 1;
+ return true;
}
break;
case SPACE_CLIP:
- return 1;
+ return true;
+ default:
+ break;
}
}
- return 0;
+ return false;
+}
+
+static void screen_animation_region_tag_redraw(ScrArea *area,
+ ARegion *region,
+ const Scene *scene,
+ eScreen_Redraws_Flag redraws)
+{
+ /* Do follow time here if editor type supports it */
+ if ((redraws & TIME_FOLLOW) &&
+ (screen_animation_region_supports_time_follow(area->spacetype, region->regiontype))) {
+ float w = BLI_rctf_size_x(&region->v2d.cur);
+ if (scene->r.cfra < region->v2d.cur.xmin) {
+ region->v2d.cur.xmax = scene->r.cfra;
+ region->v2d.cur.xmin = region->v2d.cur.xmax - w;
+ ED_region_tag_redraw(region);
+ return;
+ }
+ else if (scene->r.cfra > region->v2d.cur.xmax) {
+ region->v2d.cur.xmin = scene->r.cfra;
+ region->v2d.cur.xmax = region->v2d.cur.xmin + w;
+ ED_region_tag_redraw(region);
+ return;
+ }
+ }
+
+ /* No need to do a full redraw as the current frame indicator is only updated.
+ * We do need to redraw when this area is in full screen as no other areas
+ * will be tagged for redrawing. */
+ if ((region->regiontype == RGN_TYPE_WINDOW) &&
+ (ELEM(area->spacetype, SPACE_GRAPH, SPACE_NLA, SPACE_ACTION, SPACE_SEQ)) && !area->full) {
+ return;
+ }
+ ED_region_tag_redraw(region);
}
//#define PROFILE_AUDIO_SYNCH
@@ -4409,7 +4447,8 @@ static int screen_animation_step(bContext *C, wmOperator *UNUSED(op), const wmEv
}
if (scene_eval == NULL) {
- /* Happens when undo/redo system is used during playback, nothing meaningful we can do here.
+ /* Happens when undo/redo system is used during playback, nothing meaningful we can do
+ * here.
*/
}
else if (scene_eval->id.recalc & ID_RECALC_AUDIO_SEEK) {
@@ -4549,28 +4588,12 @@ static int screen_animation_step(bContext *C, wmOperator *UNUSED(op), const wmEv
redraw = true;
}
else if (match_region_with_redraws(
- area->spacetype, region->regiontype, sad->redraws, sad->from_anim_edit)) {
+ area, region->regiontype, sad->redraws, sad->from_anim_edit)) {
redraw = true;
}
if (redraw) {
- ED_region_tag_redraw(region);
- /* do follow here if editor type supports it */
- if ((sad->redraws & TIME_FOLLOW)) {
- if ((region->regiontype == RGN_TYPE_WINDOW &&
- ELEM(area->spacetype, SPACE_SEQ, SPACE_GRAPH, SPACE_ACTION, SPACE_NLA)) ||
- (area->spacetype == SPACE_CLIP && region->regiontype == RGN_TYPE_PREVIEW)) {
- float w = BLI_rctf_size_x(&region->v2d.cur);
- if (scene->r.cfra < region->v2d.cur.xmin) {
- region->v2d.cur.xmax = scene->r.cfra;
- region->v2d.cur.xmin = region->v2d.cur.xmax - w;
- }
- else if (scene->r.cfra > region->v2d.cur.xmax) {
- region->v2d.cur.xmin = scene->r.cfra;
- region->v2d.cur.xmax = region->v2d.cur.xmin + w;
- }
- }
- }
+ screen_animation_region_tag_redraw(area, region, scene, sad->redraws);
}
}
}
diff --git a/source/blender/editors/screen/workspace_layout_edit.c b/source/blender/editors/screen/workspace_layout_edit.c
index 7ce92bc3e4d..0af81e0db21 100644
--- a/source/blender/editors/screen/workspace_layout_edit.c
+++ b/source/blender/editors/screen/workspace_layout_edit.c
@@ -38,7 +38,7 @@
#include "screen_intern.h"
/**
- * Empty screen, with 1 dummy area without spacedata. Uses window size.
+ * Empty screen, with 1 dummy area without space-data. Uses window size.
*/
WorkSpaceLayout *ED_workspace_layout_add(Main *bmain,
WorkSpace *workspace,
diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt
index b8754953741..ed87d524627 100644
--- a/source/blender/editors/sculpt_paint/CMakeLists.txt
+++ b/source/blender/editors/sculpt_paint/CMakeLists.txt
@@ -64,10 +64,12 @@ set(SRC
sculpt_detail.c
sculpt_dyntopo.c
sculpt_face_set.c
+ sculpt_filter_color.c
sculpt_filter_mask.c
sculpt_filter_mesh.c
sculpt_mask_expand.c
sculpt_multiplane_scrape.c
+ sculpt_paint_color.c
sculpt_pose.c
sculpt_smooth.c
sculpt_transform.c
diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c
index 4222a466a7b..1291de04634 100644
--- a/source/blender/editors/sculpt_paint/paint_cursor.c
+++ b/source/blender/editors/sculpt_paint/paint_cursor.c
@@ -1441,7 +1441,7 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused))
* cursor won't be tagged to update, so always initialize the preview chain if it is
* null before drawing it. */
if (update_previews || !ss->pose_ik_chain_preview) {
- BKE_sculpt_update_object_for_edit(depsgraph, vc.obact, true, false);
+ BKE_sculpt_update_object_for_edit(depsgraph, vc.obact, true, false, false);
/* Free the previous pose brush preview. */
if (ss->pose_ik_chain_preview) {
diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c
index 08af3bdd16c..7f64fdf3501 100644
--- a/source/blender/editors/sculpt_paint/paint_image.c
+++ b/source/blender/editors/sculpt_paint/paint_image.c
@@ -1301,7 +1301,7 @@ static bool brush_colors_flip_poll(bContext *C)
else {
Object *ob = CTX_data_active_object(C);
if (ob != NULL) {
- if (ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_TEXTURE_PAINT)) {
+ if (ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_TEXTURE_PAINT | OB_MODE_SCULPT)) {
return true;
}
}
diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c
index 5e3204b6d5a..dfb8f03fa6e 100644
--- a/source/blender/editors/sculpt_paint/paint_image_proj.c
+++ b/source/blender/editors/sculpt_paint/paint_image_proj.c
@@ -811,7 +811,7 @@ static bool project_paint_PickColor(
}
/**
- * Check if 'pt' is infront of the 3 verts on the Z axis (used for screenspace occlusion test)
+ * Check if 'pt' is in front of the 3 verts on the Z axis (used for screen-space occlusion test)
* \return
* - `0`: no occlusion
* - `-1`: no occlusion but 2D intersection is true
@@ -836,7 +836,7 @@ static int project_paint_occlude_ptv(const float pt[3],
}
/* From here on we know there IS an intersection */
- /* if ALL of the verts are infront of us then we know it intersects ? */
+ /* if ALL of the verts are in front of us then we know it intersects ? */
if (v1[2] < pt[2] && v2[2] < pt[2] && v3[2] < pt[2]) {
return 1;
}
diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c
index c32e496f4f5..6e0402fc6e0 100644
--- a/source/blender/editors/sculpt_paint/paint_mask.c
+++ b/source/blender/editors/sculpt_paint/paint_mask.c
@@ -146,12 +146,11 @@ static int mask_flood_fill_exec(bContext *C, wmOperator *op)
PBVHNode **nodes;
int totnode;
bool multires;
- Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
mode = RNA_enum_get(op->ptr, "mode");
value = RNA_float_get(op->ptr, "value");
- BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true, false);
pbvh = ob->sculpt->pbvh;
multires = (BKE_pbvh_type(pbvh) == PBVH_GRIDS);
@@ -169,7 +168,7 @@ static int mask_flood_fill_exec(bContext *C, wmOperator *op)
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, mask_flood_fill_task_cb, &settings);
if (multires) {
@@ -313,7 +312,7 @@ bool ED_sculpt_mask_box_select(struct bContext *C, ViewContext *vc, const rcti *
/* Transform the clip planes in object space. */
ED_view3d_clipping_calc(&bb, clip_planes, vc->region, vc->obact, rect);
- BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true, false);
pbvh = ob->sculpt->pbvh;
multires = (BKE_pbvh_type(pbvh) == PBVH_GRIDS);
@@ -344,7 +343,7 @@ bool ED_sculpt_mask_box_select(struct bContext *C, ViewContext *vc, const rcti *
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, mask_box_select_task_cb, &settings);
if (nodes) {
@@ -500,7 +499,7 @@ static int paint_mask_gesture_lasso_exec(bContext *C, wmOperator *op)
ED_view3d_clipping_calc(&bb, clip_planes, vc.region, vc.obact, &data.rect);
- BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true, false);
pbvh = ob->sculpt->pbvh;
multires = (BKE_pbvh_type(pbvh) == PBVH_GRIDS);
@@ -533,7 +532,7 @@ static int paint_mask_gesture_lasso_exec(bContext *C, wmOperator *op)
data.task_data.value = value;
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, mask_gesture_lasso_task_cb, &settings);
if (nodes) {
diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c
index 0f54d5e0821..191ae1da343 100644
--- a/source/blender/editors/sculpt_paint/paint_ops.c
+++ b/source/blender/editors/sculpt_paint/paint_ops.c
@@ -304,7 +304,10 @@ static bool palette_extract_img_poll(bContext *C)
{
SpaceLink *sl = CTX_wm_space_data(C);
if ((sl != NULL) && (sl->spacetype == SPACE_IMAGE)) {
- return true;
+ SpaceImage *sima = CTX_wm_space_image(C);
+ Image *image = sima->image;
+ ImageUser iuser = sima->iuser;
+ return BKE_image_has_ibuf(image, &iuser);
}
return false;
@@ -326,16 +329,16 @@ static int palette_extract_img_exec(bContext *C, wmOperator *op)
ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock);
- if (ibuf->rect) {
+ if (ibuf && ibuf->rect) {
/* Extract all colors. */
+ const int range = (int)pow(10.0f, threshold);
for (int row = 0; row < ibuf->y; row++) {
for (int col = 0; col < ibuf->x; col++) {
float color[4];
IMB_sampleImageAtLocation(ibuf, (float)col, (float)row, false, color);
- const float range = pow(10.0f, threshold);
- color[0] = truncf(color[0] * range) / range;
- color[1] = truncf(color[1] * range) / range;
- color[2] = truncf(color[2] * range) / range;
+ for (int i = 0; i < 3; i++) {
+ color[i] = truncf(color[i] * range) / range;
+ }
uint key = rgb_to_cpack(color[0], color[1], color[2]);
if (!BLI_ghash_haskey(color_table, POINTER_FROM_INT(key))) {
@@ -360,6 +363,8 @@ static int palette_extract_img_exec(bContext *C, wmOperator *op)
static void PALETTE_OT_extract_from_image(wmOperatorType *ot)
{
+ PropertyRNA *prop;
+
/* identifiers */
ot->name = "Extract Palette from Image";
ot->idname = "PALETTE_OT_extract_from_image";
@@ -373,7 +378,8 @@ static void PALETTE_OT_extract_from_image(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
- RNA_def_int(ot->srna, "threshold", 1, 1, 4, "Threshold", "", 1, 4);
+ prop = RNA_def_int(ot->srna, "threshold", 1, 1, 1, "Threshold", "", 1, 1);
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
}
/* Sort Palette color by Hue and Saturation. */
diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c
index 2c6f708d82a..447d5373a48 100644
--- a/source/blender/editors/sculpt_paint/paint_stroke.c
+++ b/source/blender/editors/sculpt_paint/paint_stroke.c
@@ -909,7 +909,7 @@ PaintStroke *paint_stroke_new(bContext *C,
stroke->zoom_2d = max_ff(zoomx, zoomy);
if (stroke->stroke_mode == BRUSH_STROKE_INVERT) {
- if (br->flag & (BRUSH_CURVE)) {
+ if (br->flag & BRUSH_CURVE) {
RNA_enum_set(op->ptr, "mode", BRUSH_STROKE_NORMAL);
}
}
@@ -1467,8 +1467,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
}
else if (first_modal ||
/* regular dabs */
- (!(br->flag & (BRUSH_AIRBRUSH)) &&
- (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE))) ||
+ (!(br->flag & BRUSH_AIRBRUSH) && (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE))) ||
/* airbrush */
((br->flag & BRUSH_AIRBRUSH) && event->type == TIMER &&
event->customdata == stroke->timer)) {
diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c
index 6de54b3ae6a..7ac778630ac 100644
--- a/source/blender/editors/sculpt_paint/paint_vertex.c
+++ b/source/blender/editors/sculpt_paint/paint_vertex.c
@@ -1101,12 +1101,12 @@ static void vertex_paint_init_session(Depsgraph *depsgraph,
BLI_assert(ob->sculpt == NULL);
ob->sculpt = MEM_callocN(sizeof(SculptSession), "sculpt session");
ob->sculpt->mode_type = object_mode;
- BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
}
static void vertex_paint_init_stroke(Depsgraph *depsgraph, Object *ob)
{
- BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
}
static void vertex_paint_init_session_data(const ToolSettings *ts, Object *ob)
@@ -1425,10 +1425,10 @@ static int wpaint_mode_toggle_exec(bContext *C, wmOperator *op)
}
}
- /* Weightpaint works by overriding colors in mesh,
- * so need to make sure we recalc on enter and
+ /* Weight-paint works by overriding colors in mesh,
+ * so need to make sure we recalculate on enter and
* exit (exit needs doing regardless because we
- * should redeform).
+ * should re-deform).
*/
DEG_id_tag_update(&me->id, 0);
@@ -2171,7 +2171,7 @@ static void calculate_average_weight(SculptThreadedTaskData *data,
data->custom_data = accum;
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (data->sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, data, do_wpaint_brush_calc_average_weight_cb_ex, &settings);
uint accum_len = 0;
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index 75c88047914..fb7ae4d017d 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -30,6 +30,7 @@
#include "BLI_gsqueue.h"
#include "BLI_hash.h"
#include "BLI_math.h"
+#include "BLI_math_color_blend.h"
#include "BLI_task.h"
#include "BLI_utildefines.h"
@@ -73,6 +74,8 @@
#include "DEG_depsgraph.h"
+#include "IMB_colormanagement.h"
+
#include "WM_api.h"
#include "WM_message.h"
#include "WM_toolsystem.h"
@@ -153,6 +156,21 @@ const float *SCULPT_vertex_co_get(SculptSession *ss, int index)
return NULL;
}
+const float *SCULPT_vertex_color_get(SculptSession *ss, int index)
+{
+ switch (BKE_pbvh_type(ss->pbvh)) {
+ case PBVH_FACES:
+ if (ss->vcol) {
+ return ss->vcol[index].color;
+ }
+ break;
+ case PBVH_BMESH:
+ case PBVH_GRIDS:
+ break;
+ }
+ return NULL;
+}
+
void SCULPT_vertex_normal_get(SculptSession *ss, int index, float no[3])
{
switch (BKE_pbvh_type(ss->pbvh)) {
@@ -180,6 +198,23 @@ void SCULPT_vertex_normal_get(SculptSession *ss, int index, float no[3])
}
}
+static const float *sculpt_vertex_persistent_co_get(SculptSession *ss, int index)
+{
+ if (ss->persistent_base) {
+ return ss->persistent_base[index].co;
+ }
+ return SCULPT_vertex_co_get(ss, index);
+}
+
+static void sculpt_vertex_persistent_normal_get(SculptSession *ss, int index, float no[3])
+{
+ if (ss->persistent_base) {
+ copy_v3_v3(no, ss->persistent_base[index].no);
+ return;
+ }
+ SCULPT_vertex_normal_get(ss, index, no);
+}
+
float SCULPT_vertex_mask_get(SculptSession *ss, int index)
{
BMVert *v;
@@ -633,6 +668,13 @@ static void sculpt_vertex_neighbors_get_faces(SculptSession *ss,
}
}
}
+
+ if (ss->fake_neighbors.use_fake_neighbors) {
+ BLI_assert(ss->fake_neighbors.fake_neighbor_index != NULL);
+ if (ss->fake_neighbors.fake_neighbor_index[index] != FAKE_NEIGHBOR_NONE) {
+ sculpt_vertex_neighbor_add(iter, ss->fake_neighbors.fake_neighbor_index[index]);
+ }
+ }
}
static void sculpt_vertex_neighbors_get_grids(SculptSession *ss,
@@ -665,6 +707,13 @@ static void sculpt_vertex_neighbors_get_grids(SculptSession *ss,
neighbors.coords[i].y * key->grid_size + neighbors.coords[i].x);
}
+ if (ss->fake_neighbors.use_fake_neighbors) {
+ BLI_assert(ss->fake_neighbors.fake_neighbor_index != NULL);
+ if (ss->fake_neighbors.fake_neighbor_index[index] != FAKE_NEIGHBOR_NONE) {
+ sculpt_vertex_neighbor_add(iter, ss->fake_neighbors.fake_neighbor_index[index]);
+ }
+ }
+
if (neighbors.coords != neighbors.coords_fixed) {
MEM_freeN(neighbors.coords);
}
@@ -821,7 +870,7 @@ int SCULPT_nearest_vertex_get(
nvtd.nearest_vertex_distance_squared = FLT_MAX;
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
settings.func_reduce = nearest_vertex_get_reduce;
settings.userdata_chunk = &nvtd;
settings.userdata_chunk_size = sizeof(NearestVertexTLSData);
@@ -998,6 +1047,8 @@ static bool sculpt_tool_is_proxy_used(const char sculpt_tool)
SCULPT_TOOL_LAYER,
SCULPT_TOOL_POSE,
SCULPT_TOOL_CLOTH,
+ SCULPT_TOOL_PAINT,
+ SCULPT_TOOL_SMEAR,
SCULPT_TOOL_DRAW_FACE_SETS);
}
@@ -1045,9 +1096,7 @@ typedef enum StrokeFlags {
/* Initialize a SculptOrigVertData for accessing original vertex data;
* handles BMesh, mesh, and multires. */
-static void sculpt_orig_vert_data_unode_init(SculptOrigVertData *data,
- Object *ob,
- SculptUndoNode *unode)
+void SCULPT_orig_vert_data_unode_init(SculptOrigVertData *data, Object *ob, SculptUndoNode *unode)
{
SculptSession *ss = ob->sculpt;
BMesh *bm = ss->bm;
@@ -1062,6 +1111,7 @@ static void sculpt_orig_vert_data_unode_init(SculptOrigVertData *data,
data->coords = data->unode->co;
data->normals = data->unode->no;
data->vmasks = data->unode->mask;
+ data->colors = data->unode->col;
}
}
@@ -1071,7 +1121,7 @@ void SCULPT_orig_vert_data_init(SculptOrigVertData *data, Object *ob, PBVHNode *
{
SculptUndoNode *unode;
unode = SCULPT_undo_push_node(ob, node, SCULPT_UNDO_COORDS);
- sculpt_orig_vert_data_unode_init(data, ob, unode);
+ SCULPT_orig_vert_data_unode_init(data, ob, unode);
}
/* Update a SculptOrigVertData for a particular vertex from the PBVH
@@ -1087,6 +1137,9 @@ void SCULPT_orig_vert_data_update(SculptOrigVertData *orig_data, PBVHVertexIter
orig_data->no = orig_data->normals[iter->i];
}
}
+ else if (orig_data->unode->type == SCULPT_UNDO_COLOR) {
+ orig_data->col = orig_data->colors[iter->i];
+ }
else if (orig_data->unode->type == SCULPT_UNDO_MASK) {
if (orig_data->bm_log) {
orig_data->mask = BM_log_original_mask(orig_data->bm_log, iter->bm_vert);
@@ -1247,7 +1300,7 @@ static void paint_mesh_restore_co_task_cb(void *__restrict userdata,
PBVHVertexIter vd;
SculptOrigVertData orig_data;
- sculpt_orig_vert_data_unode_init(&orig_data, data->ob, unode);
+ SCULPT_orig_vert_data_unode_init(&orig_data, data->ob, unode);
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
@@ -1265,6 +1318,9 @@ static void paint_mesh_restore_co_task_cb(void *__restrict userdata,
else if (orig_data.unode->type == SCULPT_UNDO_MASK) {
*vd.mask = orig_data.mask;
}
+ else if (orig_data.unode->type == SCULPT_UNDO_COLOR) {
+ copy_v4_v4(vd.col, orig_data.col);
+ }
if (vd.mvert) {
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
@@ -1288,7 +1344,7 @@ static void paint_mesh_restore_co(Sculpt *sd, Object *ob)
/**
* Disable OpenMP when dynamic-topology is enabled. Otherwise, new entries might be inserted by
- * #sculpt_undo_push_node() into the GHash used internally by #BM_log_original_vert_co()
+ * #SCULPT_undo_push_node() into the #GHash used internally by #BM_log_original_vert_co()
* by a different thread. See T33787. */
SculptThreadedTaskData data = {
.sd = sd,
@@ -1298,9 +1354,11 @@ static void paint_mesh_restore_co(Sculpt *sd, Object *ob)
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP) && !ss->bm, totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true && !ss->bm, totnode);
BLI_task_parallel_range(0, totnode, &data, paint_mesh_restore_co_task_cb, &settings);
+ BKE_pbvh_node_color_buffer_free(ss->pbvh);
+
MEM_SAFE_FREE(nodes);
}
@@ -1478,6 +1536,9 @@ bool SCULPT_brush_test_cube(SculptBrushTest *test,
{
float side = M_SQRT1_2;
float local_co[3];
+ float i_local[4][4];
+
+ invert_m4_m4(i_local, local);
if (sculpt_brush_test_clipping(test, co)) {
return false;
@@ -1924,7 +1985,7 @@ static void calc_area_center(
AreaNormalCenterTLSData anctd = {{{0}}};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
settings.func_reduce = calc_area_normal_and_center_reduce;
settings.userdata_chunk = &anctd;
settings.userdata_chunk_size = sizeof(AreaNormalCenterTLSData);
@@ -1953,8 +2014,7 @@ void SCULPT_calc_area_normal(
Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float r_area_no[3])
{
const Brush *brush = BKE_paint_brush(&sd->paint);
- bool use_threading = (sd->flags & SCULPT_USE_OPENMP);
- SCULPT_pbvh_calc_area_normal(brush, ob, nodes, totnode, use_threading, r_area_no);
+ SCULPT_pbvh_calc_area_normal(brush, ob, nodes, totnode, true, r_area_no);
}
/* Expose 'calc_area_normal' externally. */
@@ -2024,7 +2084,7 @@ static void calc_area_normal_and_center(
AreaNormalCenterTLSData anctd = {{{0}}};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
settings.func_reduce = calc_area_normal_and_center_reduce;
settings.userdata_chunk = &anctd;
settings.userdata_chunk_size = sizeof(AreaNormalCenterTLSData);
@@ -2116,6 +2176,11 @@ static float brush_strength(const Sculpt *sd,
return alpha * pressure * overlap * feather;
case SCULPT_TOOL_SLIDE_RELAX:
return alpha * pressure * overlap * feather * 2.0f;
+ case SCULPT_TOOL_PAINT:
+ final_pressure = pressure * pressure;
+ return final_pressure * overlap * feather;
+ case SCULPT_TOOL_SMEAR:
+ return pressure * overlap * feather;
case SCULPT_TOOL_CLAY_STRIPS:
/* Clay Strips needs less strength to compensate the curve. */
final_pressure = powf(pressure, 1.5f);
@@ -2577,11 +2642,6 @@ static void update_brush_local_mat(Sculpt *sd, Object *ob)
}
}
-/* Note: uses after-struct allocated mem to store actual cache... */
-typedef struct SculptDoBrushSmoothGridDataChunk {
- size_t tmpgrid_size;
-} SculptDoBrushSmoothGridDataChunk;
-
typedef struct {
SculptSession *ss;
const float *ray_start;
@@ -2689,7 +2749,7 @@ static void bmesh_topology_rake(
.strength = factor,
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_topology_rake_bmesh_task_cb_ex, &settings);
}
@@ -2746,7 +2806,7 @@ static void do_mask_brush_draw(Sculpt *sd, Object *ob, PBVHNode **nodes, int tot
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_mask_brush_draw_task_cb_ex, &settings);
}
@@ -2834,7 +2894,7 @@ static void do_draw_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_draw_brush_task_cb_ex, &settings);
}
@@ -2911,7 +2971,7 @@ static void do_draw_sharp_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int to
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_draw_sharp_brush_task_cb_ex, &settings);
}
@@ -3098,7 +3158,7 @@ static void do_slide_relax_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int t
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
if (ss->cache->alt_smooth) {
for (int i = 0; i < 4; i++) {
BLI_task_parallel_range(0, totnode, &data, do_topology_relax_task_cb_ex, &settings);
@@ -3308,7 +3368,7 @@ static void do_crease_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_crease_brush_task_cb_ex, &settings);
}
@@ -3422,7 +3482,7 @@ static void do_pinch_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_pinch_brush_task_cb_ex, &settings);
}
@@ -3495,7 +3555,7 @@ static void do_grab_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_grab_brush_task_cb_ex, &settings);
}
@@ -3604,7 +3664,7 @@ static void do_elastic_deform_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, in
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_elastic_deform_brush_task_cb_ex, &settings);
}
@@ -3811,7 +3871,7 @@ static void do_nudge_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_nudge_brush_task_cb_ex, &settings);
}
@@ -3933,7 +3993,7 @@ static void do_snake_hook_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int to
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_snake_hook_brush_task_cb_ex, &settings);
}
@@ -4006,7 +4066,7 @@ static void do_thumb_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_thumb_brush_task_cb_ex, &settings);
}
@@ -4080,7 +4140,7 @@ static void do_rotate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_rotate_brush_task_cb_ex, &settings);
}
@@ -4093,7 +4153,7 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata,
Sculpt *sd = data->sd;
const Brush *brush = data->brush;
- const bool use_persistent_base = ss->layer_base && brush->flag & BRUSH_PERSISTENT;
+ const bool use_persistent_base = ss->persistent_base && brush->flag & BRUSH_PERSISTENT;
PBVHVertexIter vd;
SculptOrigVertData orig_data;
@@ -4123,7 +4183,7 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata,
const int vi = vd.index;
float *disp_factor;
if (use_persistent_base) {
- disp_factor = &ss->layer_base[vi].disp;
+ disp_factor = &ss->persistent_base[vi].disp;
}
else {
disp_factor = &ss->cache->layer_displacement_factor[vi];
@@ -4153,9 +4213,9 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata,
float normal[3];
if (use_persistent_base) {
- copy_v3_v3(normal, ss->layer_base[vi].no);
+ sculpt_vertex_persistent_normal_get(ss, vi, normal);
mul_v3_fl(normal, brush->height);
- madd_v3_v3v3fl(final_co, ss->layer_base[vi].co, normal, *disp_factor);
+ madd_v3_v3v3fl(final_co, sculpt_vertex_persistent_co_get(ss, vi), normal, *disp_factor);
}
else {
normal_short_to_float_v3(normal, orig_data.no);
@@ -4196,7 +4256,7 @@ static void do_layer_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_layer_brush_task_cb_ex, &settings);
}
@@ -4263,7 +4323,7 @@ static void do_inflate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totno
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_inflate_brush_task_cb_ex, &settings);
}
@@ -4388,7 +4448,7 @@ static void do_flatten_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totno
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_flatten_brush_task_cb_ex, &settings);
}
@@ -4539,7 +4599,7 @@ static void do_clay_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
ClaySampleData csd = {{0}};
TaskParallelSettings sample_settings;
- BKE_pbvh_parallel_range_settings(&sample_settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&sample_settings, true, totnode);
sample_settings.func_reduce = calc_clay_surface_reduce;
sample_settings.userdata_chunk = &csd;
sample_settings.userdata_chunk_size = sizeof(ClaySampleData);
@@ -4570,7 +4630,7 @@ static void do_clay_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_clay_brush_task_cb_ex, &settings);
}
@@ -4677,6 +4737,17 @@ static void do_clay_strips_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int t
mul_v3_fl(temp, displace);
add_v3_v3(area_co, temp);
+ /* Clay Strips uses a cube test with falloff in the XY axis (not in Z) and a plane to deform the
+ * vertices. When in Add mode, vertices that are below the plane and inside the cube are move
+ * towards the plane. In this situation, there may be cases where a vertex is outside the cube
+ * but below the plane, so won't be deformed, causing artifacts. In order to prevent these
+ * artifacts, this displaces the test cube space in relation to the plane in order to
+ * deform more vertices that may be below it. */
+ /* The 0.7 and 1.25 factors are arbitrary and don't have any relation between them, they were set
+ * by doing multiple tests using the default "Clay Strips" brush preset. */
+ float area_co_displaced[3];
+ madd_v3_v3v3fl(area_co_displaced, area_co, area_no, -radius * 0.7f);
+
/* Init brush local space matrix. */
cross_v3_v3v3(mat[0], area_no, ss->cache->grab_delta_symmetry);
mat[0][3] = 0.0f;
@@ -4684,13 +4755,19 @@ static void do_clay_strips_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int t
mat[1][3] = 0.0f;
copy_v3_v3(mat[2], area_no);
mat[2][3] = 0.0f;
- copy_v3_v3(mat[3], ss->cache->location);
+ copy_v3_v3(mat[3], area_co_displaced);
mat[3][3] = 1.0f;
normalize_m4(mat);
/* Scale brush local space matrix. */
scale_m4_fl(scale, ss->cache->radius);
mul_m4_m4m4(tmat, mat, scale);
+
+ /* Deform the local space in Z to scale the test cube. As the test cube does not have falloff in
+ * Z this does not produce artifacts in the falloff cube and allows to deform extra vertices
+ * during big deformation while keeping the surface as uniform as possible. */
+ mul_v3_fl(tmat[2], 1.25f);
+
invert_m4_m4(mat, tmat);
SculptThreadedTaskData data = {
@@ -4704,7 +4781,7 @@ static void do_clay_strips_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int t
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_clay_strips_brush_task_cb_ex, &settings);
}
@@ -4798,7 +4875,7 @@ static void do_fill_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_fill_brush_task_cb_ex, &settings);
}
@@ -4891,7 +4968,7 @@ static void do_scrape_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_scrape_brush_task_cb_ex, &settings);
}
@@ -5064,7 +5141,7 @@ static void do_clay_thumb_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int to
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_clay_thumb_brush_task_cb_ex, &settings);
}
@@ -5136,7 +5213,7 @@ static void do_gravity(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, fl
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_gravity_task_cb_ex, &settings);
}
@@ -5255,23 +5332,23 @@ static void do_brush_action_task_cb(void *__restrict userdata,
/* Face Sets modifications do a single undo push */
if (data->brush->sculpt_tool == SCULPT_TOOL_DRAW_FACE_SETS) {
+ BKE_pbvh_node_mark_redraw(data->nodes[n]);
/* Draw face sets in smooth mode moves the vertices. */
if (ss->cache->alt_smooth) {
SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
BKE_pbvh_node_mark_update(data->nodes[n]);
}
}
- else {
- SCULPT_undo_push_node(data->ob,
- data->nodes[n],
- data->brush->sculpt_tool == SCULPT_TOOL_MASK ? SCULPT_UNDO_MASK :
- SCULPT_UNDO_COORDS);
- }
-
- if (data->brush->sculpt_tool == SCULPT_TOOL_MASK) {
+ else if (data->brush->sculpt_tool == SCULPT_TOOL_MASK) {
+ SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_MASK);
BKE_pbvh_node_mark_update_mask(data->nodes[n]);
}
+ else if (ELEM(data->brush->sculpt_tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR)) {
+ SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COLOR);
+ BKE_pbvh_node_mark_update_color(data->nodes[n]);
+ }
else {
+ SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
BKE_pbvh_node_mark_update(data->nodes[n]);
}
}
@@ -5282,7 +5359,17 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
int totnode;
PBVHNode **nodes;
- /* Build a list of all nodes that are potentially within the brush's area of influence. */
+ /* Check for unsupported features. */
+ PBVHType type = BKE_pbvh_type(ss->pbvh);
+ if (brush->sculpt_tool == SCULPT_TOOL_PAINT && type != PBVH_FACES) {
+ return;
+ }
+
+ if (brush->sculpt_tool == SCULPT_TOOL_SMEAR && type != PBVH_FACES) {
+ return;
+ }
+
+ /* Build a list of all nodes that are potentially within the brush's area of influence */
/* These brushes need to update all nodes as they are not constrained by the brush radius */
/* Elastic deform needs all nodes to avoid artifacts as the effect of the brush is not
@@ -5322,7 +5409,14 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
* and the number of nodes under the brush influence. */
if (brush->sculpt_tool == SCULPT_TOOL_DRAW_FACE_SETS && ss->cache->first_time &&
ss->cache->mirror_symmetry_pass == 0 && !ss->cache->alt_smooth) {
- SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_FACE_SETS);
+
+ /* Dyntopo does not support Face Sets data, so it can't store/restore it from undo. */
+ /* TODO (pablodp606): This check should be done in the undo code and not here, but the rest of
+ * the sculpt code is not checking for unsupported undo types that may return a null node. */
+ if (BKE_pbvh_type(ss->pbvh) != PBVH_BMESH) {
+ SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_FACE_SETS);
+ }
+
if (ss->cache->invert) {
/* When inverting the brush, pick the paint face mask ID from the mesh. */
ss->cache->paint_face_set = SCULPT_active_face_set_get(ss);
@@ -5345,7 +5439,7 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &task_data, do_brush_action_task_cb, &settings);
if (sculpt_brush_needs_normal(ss, brush)) {
@@ -5464,6 +5558,12 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
case SCULPT_TOOL_DRAW_FACE_SETS:
SCULPT_do_draw_face_sets_brush(sd, ob, nodes, totnode);
break;
+ case SCULPT_TOOL_PAINT:
+ SCULPT_do_paint_brush(sd, ob, nodes, totnode);
+ break;
+ case SCULPT_TOOL_SMEAR:
+ SCULPT_do_smear_brush(sd, ob, nodes, totnode);
+ break;
}
if (!ELEM(brush->sculpt_tool, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_MASK) &&
@@ -5602,7 +5702,7 @@ static void sculpt_combine_proxies(Sculpt *sd, Object *ob)
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, sculpt_combine_proxies_task_cb, &settings);
}
@@ -5690,7 +5790,7 @@ void SCULPT_flush_stroke_deform(Sculpt *sd, Object *ob, bool is_proxy_used)
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, SCULPT_flush_stroke_deform_task_cb, &settings);
if (vertCos) {
@@ -5991,6 +6091,10 @@ static const char *sculpt_tool_name(Sculpt *sd)
return "Cloth Brush";
case SCULPT_TOOL_DRAW_FACE_SETS:
return "Draw Face Sets";
+ case SCULPT_TOOL_PAINT:
+ return "Paint Brush";
+ case SCULPT_TOOL_SMEAR:
+ return "Smear Brush";
}
return "Sculpting";
@@ -6005,6 +6109,7 @@ void SCULPT_cache_free(StrokeCache *cache)
MEM_SAFE_FREE(cache->dial);
MEM_SAFE_FREE(cache->surface_smooth_laplacian_disp);
MEM_SAFE_FREE(cache->layer_displacement_factor);
+ MEM_SAFE_FREE(cache->prev_colors);
if (cache->pose_ik_chain) {
SCULPT_pose_ik_chain_free(cache->pose_ik_chain);
@@ -6119,7 +6224,11 @@ static void sculpt_update_cache_invariants(
cache->saved_mask_brush_tool = brush->mask_tool;
brush->mask_tool = BRUSH_MASK_SMOOTH;
}
- else if (ELEM(brush->sculpt_tool, SCULPT_TOOL_SLIDE_RELAX, SCULPT_TOOL_DRAW_FACE_SETS)) {
+ else if (ELEM(brush->sculpt_tool,
+ SCULPT_TOOL_SLIDE_RELAX,
+ SCULPT_TOOL_DRAW_FACE_SETS,
+ SCULPT_TOOL_PAINT,
+ SCULPT_TOOL_SMEAR)) {
/* Do nothing, this tool has its own smooth mode. */
}
else {
@@ -6266,6 +6375,7 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru
int tool = brush->sculpt_tool;
if (ELEM(tool,
+ SCULPT_TOOL_PAINT,
SCULPT_TOOL_GRAB,
SCULPT_TOOL_ELASTIC_DEFORM,
SCULPT_TOOL_CLOTH,
@@ -6519,7 +6629,7 @@ static bool sculpt_needs_connectivity_info(const Sculpt *sd,
((brush->sculpt_tool == SCULPT_TOOL_MASK) && (brush->mask_tool == BRUSH_MASK_SMOOTH)) ||
(brush->sculpt_tool == SCULPT_TOOL_POSE) ||
(brush->sculpt_tool == SCULPT_TOOL_SLIDE_RELAX) ||
- (brush->sculpt_tool == SCULPT_TOOL_CLOTH) ||
+ (brush->sculpt_tool == SCULPT_TOOL_CLOTH) || (brush->sculpt_tool == SCULPT_TOOL_SMEAR) ||
(brush->sculpt_tool == SCULPT_TOOL_DRAW_FACE_SETS));
}
@@ -6533,7 +6643,7 @@ void SCULPT_stroke_modifiers_check(const bContext *C, Object *ob, const Brush *b
if (ss->shapekey_active || ss->deform_modifiers_active ||
(!BKE_sculptsession_use_pbvh_draw(ob, v3d) && need_pmap)) {
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
- BKE_sculpt_update_object_for_edit(depsgraph, ob, need_pmap, false);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, need_pmap, false, false);
}
}
@@ -6877,7 +6987,7 @@ static void sculpt_brush_stroke_init(bContext *C, wmOperator *op)
SculptSession *ss = CTX_data_active_object(C)->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
int mode = RNA_enum_get(op->ptr, "mode");
- bool is_smooth;
+ bool is_smooth, needs_colors;
bool need_mask = false;
if (brush->sculpt_tool == SCULPT_TOOL_MASK) {
@@ -6892,7 +7002,8 @@ static void sculpt_brush_stroke_init(bContext *C, wmOperator *op)
sculpt_brush_init_tex(scene, sd, ss);
is_smooth = sculpt_needs_connectivity_info(sd, brush, ss, mode);
- BKE_sculpt_update_object_for_edit(depsgraph, ob, is_smooth, need_mask);
+ needs_colors = ELEM(brush->sculpt_tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, is_smooth, need_mask, needs_colors);
}
static void sculpt_restore_mesh(Sculpt *sd, Object *ob)
@@ -7035,12 +7146,19 @@ void SCULPT_flush_update_done(const bContext *C, Object *ob, SculptUpdateType up
if (update_flags & SCULPT_UPDATE_COORDS) {
BKE_pbvh_update_bounds(ss->pbvh, PBVH_UpdateOriginalBB);
+
+ /* Coordinates were modified, so fake neighbors are not longer valid. */
+ SCULPT_fake_neighbors_free(ob);
}
if (update_flags & SCULPT_UPDATE_MASK) {
BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateMask);
}
+ if (update_flags & SCULPT_UPDATE_COLOR) {
+ BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateColor);
+ }
+
if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
BKE_pbvh_bmesh_after_stroke(ss->pbvh);
}
@@ -7155,6 +7273,9 @@ static void sculpt_stroke_update_step(bContext *C,
if (brush->sculpt_tool == SCULPT_TOOL_MASK) {
SCULPT_flush_update_step(C, SCULPT_UPDATE_MASK);
}
+ else if (ELEM(brush->sculpt_tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR)) {
+ SCULPT_flush_update_step(C, SCULPT_UPDATE_COLOR);
+ }
else {
SCULPT_flush_update_step(C, SCULPT_UPDATE_COORDS);
}
@@ -7192,7 +7313,11 @@ static void sculpt_stroke_done(const bContext *C, struct PaintStroke *UNUSED(str
if (brush->sculpt_tool == SCULPT_TOOL_MASK) {
brush->mask_tool = ss->cache->saved_mask_brush_tool;
}
- else if (ELEM(brush->sculpt_tool, SCULPT_TOOL_SLIDE_RELAX, SCULPT_TOOL_DRAW_FACE_SETS)) {
+ else if (ELEM(brush->sculpt_tool,
+ SCULPT_TOOL_SLIDE_RELAX,
+ SCULPT_TOOL_DRAW_FACE_SETS,
+ SCULPT_TOOL_PAINT,
+ SCULPT_TOOL_SMEAR)) {
/* Do nothing. */
}
else {
@@ -7208,6 +7333,7 @@ static void sculpt_stroke_done(const bContext *C, struct PaintStroke *UNUSED(str
SCULPT_automasking_end(ob);
}
+ BKE_pbvh_node_color_buffer_free(ss->pbvh);
SCULPT_cache_free(ss->cache);
ss->cache = NULL;
@@ -7346,18 +7472,18 @@ static int sculpt_set_persistent_base_exec(bContext *C, wmOperator *UNUSED(op))
if (ss) {
SCULPT_vertex_random_access_init(ss);
- BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
- MEM_SAFE_FREE(ss->layer_base);
+ MEM_SAFE_FREE(ss->persistent_base);
const int totvert = SCULPT_vertex_count_get(ss);
- ss->layer_base = MEM_mallocN(sizeof(SculptLayerPersistentBase) * totvert,
- "layer persistent base");
+ ss->persistent_base = MEM_mallocN(sizeof(SculptPersistentBase) * totvert,
+ "layer persistent base");
for (int i = 0; i < totvert; i++) {
- copy_v3_v3(ss->layer_base[i].co, SCULPT_vertex_co_get(ss, i));
- SCULPT_vertex_normal_get(ss, i, ss->layer_base[i].no);
- ss->layer_base[i].disp = 0.0f;
+ copy_v3_v3(ss->persistent_base[i].co, SCULPT_vertex_co_get(ss, i));
+ SCULPT_vertex_normal_get(ss, i, ss->persistent_base[i].no);
+ ss->persistent_base[i].disp = 0.0f;
}
}
@@ -7548,7 +7674,7 @@ static void sculpt_init_session(Depsgraph *depsgraph, Scene *scene, Object *ob)
ob->sculpt = MEM_callocN(sizeof(SculptSession), "sculpt session");
ob->sculpt->mode_type = OB_MODE_SCULPT;
- BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
/* Here we can detect geometry that was just added to Sculpt Mode as it has the
* SCULPT_FACE_SET_NONE assigned, so we can create a new Face Set for it. */
@@ -7834,7 +7960,7 @@ void SCULPT_geometry_preview_lines_update(bContext *C, SculptSession *ss, float
return;
}
- BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
if (!ss->pmap) {
return;
@@ -7886,6 +8012,417 @@ void SCULPT_geometry_preview_lines_update(bContext *C, SculptSession *ss, float
ss->preview_vert_index_count = totpoints;
}
+static int vertex_to_loop_colors_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Object *ob = CTX_data_active_object(C);
+
+ ID *data;
+ data = ob->data;
+ if (data && ID_IS_LINKED(data)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ if (ob->type != OB_MESH) {
+ return OPERATOR_CANCELLED;
+ }
+
+ Mesh *mesh = ob->data;
+
+ const int mloopcol_layer_n = CustomData_get_active_layer(&mesh->ldata, CD_MLOOPCOL);
+ if (mloopcol_layer_n == -1) {
+ return OPERATOR_CANCELLED;
+ }
+ MLoopCol *loopcols = CustomData_get_layer_n(&mesh->ldata, CD_MLOOPCOL, mloopcol_layer_n);
+
+ const int MPropCol_layer_n = CustomData_get_active_layer(&mesh->vdata, CD_PROP_COLOR);
+ if (MPropCol_layer_n == -1) {
+ return OPERATOR_CANCELLED;
+ }
+ MPropCol *vertcols = CustomData_get_layer_n(&mesh->vdata, CD_PROP_COLOR, MPropCol_layer_n);
+
+ MLoop *loops = CustomData_get_layer(&mesh->ldata, CD_MLOOP);
+ MPoly *polys = CustomData_get_layer(&mesh->pdata, CD_MPOLY);
+
+ for (int i = 0; i < mesh->totpoly; i++) {
+ MPoly *c_poly = &polys[i];
+ for (int j = 0; j < c_poly->totloop; j++) {
+ int loop_index = c_poly->loopstart + j;
+ MLoop *c_loop = &loops[c_poly->loopstart + j];
+ loopcols[loop_index].r = (char)(vertcols[c_loop->v].color[0] * 255);
+ loopcols[loop_index].g = (char)(vertcols[c_loop->v].color[1] * 255);
+ loopcols[loop_index].b = (char)(vertcols[c_loop->v].color[2] * 255);
+ loopcols[loop_index].a = (char)(vertcols[c_loop->v].color[3] * 255);
+ }
+ }
+
+ DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
+
+ return OPERATOR_FINISHED;
+}
+
+static void SCULPT_OT_vertex_to_loop_colors(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Sculpt Vertex Color to Vertex Color";
+ ot->description = "Copy the Sculpt Vertex Color to a regular color layer";
+ ot->idname = "SCULPT_OT_vertex_to_loop_colors";
+
+ /* api callbacks */
+ ot->poll = SCULPT_mode_poll;
+ ot->exec = vertex_to_loop_colors_exec;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+static int loop_to_vertex_colors_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Object *ob = CTX_data_active_object(C);
+
+ ID *data;
+ data = ob->data;
+ if (data && ID_IS_LINKED(data)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ if (ob->type != OB_MESH) {
+ return OPERATOR_CANCELLED;
+ }
+
+ Mesh *mesh = ob->data;
+
+ const int mloopcol_layer_n = CustomData_get_active_layer(&mesh->ldata, CD_MLOOPCOL);
+ if (mloopcol_layer_n == -1) {
+ return OPERATOR_CANCELLED;
+ }
+ MLoopCol *loopcols = CustomData_get_layer_n(&mesh->ldata, CD_MLOOPCOL, mloopcol_layer_n);
+
+ const int MPropCol_layer_n = CustomData_get_active_layer(&mesh->vdata, CD_PROP_COLOR);
+ if (MPropCol_layer_n == -1) {
+ return OPERATOR_CANCELLED;
+ }
+ MPropCol *vertcols = CustomData_get_layer_n(&mesh->vdata, CD_PROP_COLOR, MPropCol_layer_n);
+
+ MLoop *loops = CustomData_get_layer(&mesh->ldata, CD_MLOOP);
+ MPoly *polys = CustomData_get_layer(&mesh->pdata, CD_MPOLY);
+
+ for (int i = 0; i < mesh->totpoly; i++) {
+ MPoly *c_poly = &polys[i];
+ for (int j = 0; j < c_poly->totloop; j++) {
+ int loop_index = c_poly->loopstart + j;
+ MLoop *c_loop = &loops[c_poly->loopstart + j];
+ vertcols[c_loop->v].color[0] = (loopcols[loop_index].r / 255.0f);
+ vertcols[c_loop->v].color[1] = (loopcols[loop_index].g / 255.0f);
+ vertcols[c_loop->v].color[2] = (loopcols[loop_index].b / 255.0f);
+ vertcols[c_loop->v].color[3] = (loopcols[loop_index].a / 255.0f);
+ }
+ }
+
+ DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
+
+ return OPERATOR_FINISHED;
+}
+
+static void SCULPT_OT_loop_to_vertex_colors(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Vertex Color to Sculpt Vertex Color";
+ ot->description = "Copy the active loop color layer to the vertex color";
+ ot->idname = "SCULPT_OT_loop_to_vertex_colors";
+
+ /* api callbacks */
+ ot->poll = SCULPT_mode_poll;
+ ot->exec = loop_to_vertex_colors_exec;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+static int sculpt_sample_color_invoke(bContext *C,
+ wmOperator *UNUSED(op),
+ const wmEvent *UNUSED(e))
+{
+ Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
+ Scene *scene = CTX_data_scene(C);
+ Object *ob = CTX_data_active_object(C);
+ Brush *brush = BKE_paint_brush(&sd->paint);
+ SculptSession *ss = ob->sculpt;
+ int active_vertex = SCULPT_active_vertex_get(ss);
+ const float *active_vertex_color = SCULPT_vertex_color_get(ss, active_vertex);
+ if (!active_vertex_color) {
+ return OPERATOR_CANCELLED;
+ }
+
+ float color_srgb[3];
+ copy_v3_v3(color_srgb, active_vertex_color);
+ IMB_colormanagement_scene_linear_to_srgb_v3(color_srgb);
+ BKE_brush_color_set(scene, brush, color_srgb);
+ BKE_brush_alpha_set(scene, brush, active_vertex_color[3]);
+
+ WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, brush);
+
+ return OPERATOR_FINISHED;
+}
+
+static void SCULPT_OT_sample_color(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Sample color";
+ ot->idname = "SCULPT_OT_sample_color";
+ ot->description = "Sample the vertex color of the active vertex";
+
+ /* api callbacks */
+ ot->invoke = sculpt_sample_color_invoke;
+ ot->poll = SCULPT_mode_poll;
+
+ ot->flag = OPTYPE_REGISTER;
+}
+
+/* Fake Neighbors. */
+/* This allows the sculpt tools to work on meshes with multiple connected components as they had
+ * only one connected component. When initialized and enabled, the sculpt API will return extra
+ * connectivity neighbors that are not in the real mesh. These neighbors are calculated for each
+ * vertex using the minimun distance to a vertex that is in a different connected component. */
+
+/* The fake neighbors first need to be ensured to be initialized.
+ * After that tools which needs fake neighbors functionality need to
+ * temporarily enable it:
+ *
+ * void my_awesome_sculpt_tool() {
+ * SCULPT_fake_neighbors_ensure(sd, object, brush->disconnected_distance_max);
+ * SCULPT_fake_neighbors_enable(ob);
+ *
+ * ... Logic of the tool ...
+ * SCULPT_fake_neighbors_disable(ob);
+ * }
+ *
+ * Such approach allows to keep all the connectivity information ready for reuse
+ * (withouy having lag prior to every stroke), but also makes it so the affect
+ * is localized to a specific brushes and tools only. */
+
+enum {
+ SCULPT_TOPOLOGY_ID_NONE,
+ SCULPT_TOPOLOGY_ID_DEFAULT,
+};
+
+static int SCULPT_vertex_get_connected_component(SculptSession *ss, int index)
+{
+ if (ss->vertex_info.connected_component) {
+ return ss->vertex_info.connected_component[index];
+ }
+ return SCULPT_TOPOLOGY_ID_DEFAULT;
+}
+
+static void SCULPT_fake_neighbor_init(SculptSession *ss, const float max_dist)
+{
+ const int totvert = SCULPT_vertex_count_get(ss);
+ ss->fake_neighbors.fake_neighbor_index = MEM_malloc_arrayN(
+ totvert, sizeof(int), "fake neighbor");
+ for (int i = 0; i < totvert; i++) {
+ ss->fake_neighbors.fake_neighbor_index[i] = FAKE_NEIGHBOR_NONE;
+ }
+
+ ss->fake_neighbors.current_max_distance = max_dist;
+}
+
+static void SCULPT_fake_neighbor_add(SculptSession *ss, int v_index_a, int v_index_b)
+{
+ if (ss->fake_neighbors.fake_neighbor_index[v_index_a] == FAKE_NEIGHBOR_NONE) {
+ ss->fake_neighbors.fake_neighbor_index[v_index_a] = v_index_b;
+ ss->fake_neighbors.fake_neighbor_index[v_index_b] = v_index_a;
+ }
+}
+
+static void sculpt_pose_fake_neighbors_free(SculptSession *ss)
+{
+ MEM_SAFE_FREE(ss->fake_neighbors.fake_neighbor_index);
+}
+
+typedef struct NearestVertexFakeNeighborTLSData {
+ int nearest_vertex_index;
+ float nearest_vertex_distance_squared;
+ int current_topology_id;
+} NearestVertexFakeNeighborTLSData;
+
+static void do_fake_neighbor_search_task_cb(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict tls)
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ NearestVertexFakeNeighborTLSData *nvtd = tls->userdata_chunk;
+ PBVHVertexIter vd;
+
+ BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
+ {
+ int vd_topology_id = SCULPT_vertex_get_connected_component(ss, vd.index);
+ if (vd_topology_id != nvtd->current_topology_id &&
+ ss->fake_neighbors.fake_neighbor_index[vd.index] == FAKE_NEIGHBOR_NONE) {
+ float distance_squared = len_squared_v3v3(vd.co, data->nearest_vertex_search_co);
+ if (distance_squared < nvtd->nearest_vertex_distance_squared &&
+ distance_squared < data->max_distance_squared) {
+ nvtd->nearest_vertex_index = vd.index;
+ nvtd->nearest_vertex_distance_squared = distance_squared;
+ }
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+}
+
+static void fake_neighbor_search_reduce(const void *__restrict UNUSED(userdata),
+ void *__restrict chunk_join,
+ void *__restrict chunk)
+{
+ NearestVertexFakeNeighborTLSData *join = chunk_join;
+ NearestVertexFakeNeighborTLSData *nvtd = chunk;
+ if (join->nearest_vertex_index == -1) {
+ join->nearest_vertex_index = nvtd->nearest_vertex_index;
+ join->nearest_vertex_distance_squared = nvtd->nearest_vertex_distance_squared;
+ }
+ else if (nvtd->nearest_vertex_distance_squared < join->nearest_vertex_distance_squared) {
+ join->nearest_vertex_index = nvtd->nearest_vertex_index;
+ join->nearest_vertex_distance_squared = nvtd->nearest_vertex_distance_squared;
+ }
+}
+
+static int SCULPT_fake_neighbor_search(Sculpt *sd, Object *ob, const int index, float max_distance)
+{
+ SculptSession *ss = ob->sculpt;
+ PBVHNode **nodes = NULL;
+ int totnode;
+ SculptSearchSphereData data = {
+ .ss = ss,
+ .sd = sd,
+ .radius_squared = max_distance * max_distance,
+ .original = false,
+ .center = SCULPT_vertex_co_get(ss, index),
+ };
+ BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, &totnode);
+
+ if (totnode == 0) {
+ return -1;
+ }
+
+ SculptThreadedTaskData task_data = {
+ .sd = sd,
+ .ob = ob,
+ .nodes = nodes,
+ .max_distance_squared = max_distance * max_distance,
+ };
+
+ copy_v3_v3(task_data.nearest_vertex_search_co, SCULPT_vertex_co_get(ss, index));
+
+ NearestVertexFakeNeighborTLSData nvtd;
+ nvtd.nearest_vertex_index = -1;
+ nvtd.nearest_vertex_distance_squared = FLT_MAX;
+ nvtd.current_topology_id = SCULPT_vertex_get_connected_component(ss, index);
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ settings.func_reduce = fake_neighbor_search_reduce;
+ settings.userdata_chunk = &nvtd;
+ settings.userdata_chunk_size = sizeof(NearestVertexFakeNeighborTLSData);
+ BLI_task_parallel_range(0, totnode, &task_data, do_fake_neighbor_search_task_cb, &settings);
+
+ MEM_SAFE_FREE(nodes);
+
+ return nvtd.nearest_vertex_index;
+}
+
+typedef struct SculptTopologyIDFloodFillData {
+ int next_id;
+} SculptTopologyIDFloodFillData;
+
+static bool SCULPT_connected_components_floodfill_cb(
+ SculptSession *ss, int from_v, int to_v, bool UNUSED(is_duplicate), void *userdata)
+{
+ SculptTopologyIDFloodFillData *data = userdata;
+ ss->vertex_info.connected_component[from_v] = data->next_id;
+ ss->vertex_info.connected_component[to_v] = data->next_id;
+ return true;
+}
+
+static void sculpt_connected_components_ensure(Object *ob)
+{
+ SculptSession *ss = ob->sculpt;
+
+ /* Topology IDs already initialized. They only need to be recalculated when the PBVH is rebuild.
+ */
+ if (ss->vertex_info.connected_component) {
+ return;
+ }
+
+ const int totvert = SCULPT_vertex_count_get(ss);
+ ss->vertex_info.connected_component = MEM_malloc_arrayN(totvert, sizeof(int), "topology ID");
+
+ for (int i = 0; i < totvert; i++) {
+ ss->vertex_info.connected_component[i] = SCULPT_TOPOLOGY_ID_NONE;
+ }
+
+ int next_id = 0;
+ for (int i = 0; i < totvert; i++) {
+ if (ss->vertex_info.connected_component[i] == SCULPT_TOPOLOGY_ID_NONE) {
+ SculptFloodFill flood;
+ SCULPT_floodfill_init(ss, &flood);
+ SCULPT_floodfill_add_initial(&flood, i);
+ SculptTopologyIDFloodFillData data;
+ data.next_id = next_id;
+ SCULPT_floodfill_execute(ss, &flood, SCULPT_connected_components_floodfill_cb, &data);
+ SCULPT_floodfill_free(&flood);
+ next_id++;
+ }
+ }
+}
+
+void SCULPT_fake_neighbors_ensure(Sculpt *sd, Object *ob, const float max_dist)
+{
+ SculptSession *ss = ob->sculpt;
+ const int totvert = SCULPT_vertex_count_get(ss);
+
+ /* Fake neighbors were already initialized with the same distance, so no need to be recalculated.
+ */
+ if (ss->fake_neighbors.fake_neighbor_index &&
+ ss->fake_neighbors.current_max_distance == max_dist) {
+ return;
+ }
+
+ sculpt_connected_components_ensure(ob);
+ SCULPT_fake_neighbor_init(ss, max_dist);
+
+ for (int i = 0; i < totvert; i++) {
+ const int from_v = i;
+
+ /* This vertex does not have a fake neighbor yet, seach one for it. */
+ if (ss->fake_neighbors.fake_neighbor_index[from_v] == FAKE_NEIGHBOR_NONE) {
+ const int to_v = SCULPT_fake_neighbor_search(sd, ob, from_v, max_dist);
+ if (to_v != -1) {
+ /* Add the fake neighbor if available. */
+ SCULPT_fake_neighbor_add(ss, from_v, to_v);
+ }
+ }
+ }
+}
+
+void SCULPT_fake_neighbors_enable(Object *ob)
+{
+ SculptSession *ss = ob->sculpt;
+ BLI_assert(ss->fake_neighbors.fake_neighbor_index != NULL);
+ ss->fake_neighbors.use_fake_neighbors = true;
+}
+
+void SCULPT_fake_neighbors_disable(Object *ob)
+{
+ SculptSession *ss = ob->sculpt;
+ BLI_assert(ss->fake_neighbors.fake_neighbor_index != NULL);
+ ss->fake_neighbors.use_fake_neighbors = false;
+}
+
+void SCULPT_fake_neighbors_free(Object *ob)
+{
+ SculptSession *ss = ob->sculpt;
+ sculpt_pose_fake_neighbors_free(ss);
+}
+
void ED_operatortypes_sculpt(void)
{
WM_operatortype_append(SCULPT_OT_brush_stroke);
@@ -7908,4 +8445,8 @@ void ED_operatortypes_sculpt(void)
WM_operatortype_append(SCULPT_OT_face_sets_init);
WM_operatortype_append(SCULPT_OT_cloth_filter);
WM_operatortype_append(SCULPT_OT_face_sets_edit);
+ WM_operatortype_append(SCULPT_OT_sample_color);
+ WM_operatortype_append(SCULPT_OT_loop_to_vertex_colors);
+ WM_operatortype_append(SCULPT_OT_vertex_to_loop_colors);
+ WM_operatortype_append(SCULPT_OT_color_filter);
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.c b/source/blender/editors/sculpt_paint/sculpt_cloth.c
index 7776af11a77..c821685e8b1 100644
--- a/source/blender/editors/sculpt_paint/sculpt_cloth.c
+++ b/source/blender/editors/sculpt_paint/sculpt_cloth.c
@@ -25,7 +25,7 @@
#include "BLI_blenlib.h"
#include "BLI_dial_2d.h"
-#include "BLI_ghash.h"
+#include "BLI_edgehash.h"
#include "BLI_gsqueue.h"
#include "BLI_hash.h"
#include "BLI_math.h"
@@ -106,25 +106,11 @@
#define CLOTH_MAX_CONSTRAINTS_PER_VERTEX 1024
#define CLOTH_SIMULATION_TIME_STEP 0.01f
-static void cloth_brush_constraint_key_get(int r_key[2], const int v1, const int v2)
-{
- if (v1 < v2) {
- r_key[0] = v1;
- r_key[1] = v2;
- }
- else {
- r_key[0] = v2;
- r_key[1] = v1;
- }
-}
-
static bool cloth_brush_sim_has_length_constraint(SculptClothSimulation *cloth_sim,
const int v1,
const int v2)
{
- int constraint[2];
- cloth_brush_constraint_key_get(constraint, v1, v2);
- return BLI_gset_haskey(cloth_sim->created_length_constraints, constraint);
+ return BLI_edgeset_haskey(cloth_sim->created_length_constraints, v1, v2);
}
static void cloth_brush_add_length_constraint(SculptSession *ss,
@@ -149,9 +135,7 @@ static void cloth_brush_add_length_constraint(SculptSession *ss,
}
/* Add the constraint to the GSet to avoid creating it again. */
- int constraint[2];
- cloth_brush_constraint_key_get(constraint, v1, v2);
- BLI_gset_add(cloth_sim->created_length_constraints, constraint);
+ BLI_edgeset_add(cloth_sim->created_length_constraints, v1, v2);
}
static void do_cloth_brush_build_constraints_task_cb_ex(
@@ -472,8 +456,7 @@ static void cloth_brush_build_nodes_constraints(Sculpt *sd,
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, false, totnode);
- cloth_sim->created_length_constraints = BLI_gset_new(
- BLI_ghashutil_inthash_v2_p, BLI_ghashutil_inthash_v2_cmp, "created length constraints");
+ cloth_sim->created_length_constraints = BLI_edgeset_new("created length constraints");
SculptThreadedTaskData build_constraints_data = {
.sd = sd,
@@ -487,7 +470,7 @@ static void cloth_brush_build_nodes_constraints(Sculpt *sd,
BLI_task_parallel_range(
0, totnode, &build_constraints_data, do_cloth_brush_build_constraints_task_cb_ex, &settings);
- BLI_gset_free(cloth_sim->created_length_constraints, NULL);
+ BLI_edgeset_free(cloth_sim->created_length_constraints);
}
static void cloth_brush_satisfy_constraints(SculptSession *ss,
@@ -564,7 +547,7 @@ static void cloth_brush_do_simulation_step(
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(
0, totnode, &solve_simulation_data, do_cloth_brush_solve_simulation_task_cb_ex, &settings);
}
@@ -639,7 +622,7 @@ static void cloth_brush_apply_brush_foces(Sculpt *sd, Object *ob, PBVHNode **nod
}
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(
0, totnode, &apply_forces_data, do_cloth_brush_apply_forces_task_cb_ex, &settings);
}
@@ -873,7 +856,7 @@ static int sculpt_cloth_filter_modal(bContext *C, wmOperator *op, const wmEvent
SCULPT_vertex_random_access_init(ss);
- BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
const int totverts = SCULPT_vertex_count_get(ss);
for (int i = 0; i < totverts; i++) {
@@ -889,8 +872,7 @@ static int sculpt_cloth_filter_modal(bContext *C, wmOperator *op, const wmEvent
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(
- &settings, (sd->flags & SCULPT_USE_OPENMP), ss->filter_cache->totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->totnode);
BLI_task_parallel_range(
0, ss->filter_cache->totnode, &data, cloth_filter_apply_forces_task_cb, &settings);
@@ -922,10 +904,10 @@ static int sculpt_cloth_filter_invoke(bContext *C, wmOperator *op, const wmEvent
SCULPT_vertex_random_access_init(ss);
/* Needs mask data to be available as it is used when solving the constraints. */
- BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
SCULPT_undo_push_begin("Cloth filter");
- SCULPT_filter_cache_init(ob, sd);
+ SCULPT_filter_cache_init(ob, sd, SCULPT_UNDO_COORDS);
const float cloth_mass = RNA_float_get(op->ptr, "cloth_mass");
const float cloth_damping = RNA_float_get(op->ptr, "cloth_damping");
diff --git a/source/blender/editors/sculpt_paint/sculpt_detail.c b/source/blender/editors/sculpt_paint/sculpt_detail.c
index f071deaa219..463233fd6fb 100644
--- a/source/blender/editors/sculpt_paint/sculpt_detail.c
+++ b/source/blender/editors/sculpt_paint/sculpt_detail.c
@@ -181,7 +181,7 @@ static void sample_detail_voxel(bContext *C, ViewContext *vc, int mx, int my)
/* Update the active vertex. */
float mouse[2] = {mx, my};
SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false);
- BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false);
/* Average the edge length of the connected edges to the active vertex. */
int active_vertex = SCULPT_active_vertex_get(ss);
diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.c b/source/blender/editors/sculpt_paint/sculpt_face_set.c
index 7bb54366204..031b4f8731d 100644
--- a/source/blender/editors/sculpt_paint/sculpt_face_set.c
+++ b/source/blender/editors/sculpt_paint/sculpt_face_set.c
@@ -203,7 +203,7 @@ void SCULPT_do_draw_face_sets_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, in
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
if (ss->cache->alt_smooth) {
for (int i = 0; i < 4; i++) {
BLI_task_parallel_range(0, totnode, &data, do_relax_face_sets_brush_task_cb_ex, &settings);
@@ -269,7 +269,7 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- BKE_sculpt_update_object_for_edit(depsgraph, ob, true, mode == SCULPT_FACE_SET_MASKED);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, true, mode == SCULPT_FACE_SET_MASKED, false);
const int tot_vert = SCULPT_vertex_count_get(ss);
float threshold = 0.5f;
@@ -630,7 +630,7 @@ static int sculpt_face_set_init_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false);
PBVH *pbvh = ob->sculpt->pbvh;
PBVHNode **nodes;
@@ -787,7 +787,7 @@ static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
const int tot_vert = SCULPT_vertex_count_get(ss);
const int mode = RNA_enum_get(op->ptr, "mode");
@@ -1083,7 +1083,7 @@ static int sculpt_face_set_edit_invoke(bContext *C, wmOperator *op, const wmEven
return OPERATOR_CANCELLED;
}
- BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false);
PBVH *pbvh = ob->sculpt->pbvh;
PBVHNode **nodes;
diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.c b/source/blender/editors/sculpt_paint/sculpt_filter_color.c
new file mode 100644
index 00000000000..5f7805af347
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.c
@@ -0,0 +1,323 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup edsculpt
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_hash.h"
+#include "BLI_math.h"
+#include "BLI_math_color_blend.h"
+#include "BLI_task.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_brush.h"
+#include "BKE_colortools.h"
+#include "BKE_context.h"
+#include "BKE_mesh.h"
+#include "BKE_mesh_mapping.h"
+#include "BKE_object.h"
+#include "BKE_paint.h"
+#include "BKE_pbvh.h"
+#include "BKE_scene.h"
+
+#include "IMB_colormanagement.h"
+
+#include "DEG_depsgraph.h"
+
+#include "WM_api.h"
+#include "WM_message.h"
+#include "WM_toolsystem.h"
+#include "WM_types.h"
+
+#include "ED_object.h"
+#include "ED_screen.h"
+#include "ED_sculpt.h"
+#include "paint_intern.h"
+#include "sculpt_intern.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "UI_interface.h"
+
+#include "bmesh.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+typedef enum eSculptColorFilterTypes {
+ COLOR_FILTER_FILL,
+ COLOR_FILTER_HUE,
+ COLOR_FILTER_SATURATION,
+ COLOR_FILTER_VALUE,
+ COLOR_FILTER_BRIGHTNESS,
+ COLOR_FILTER_CONTRAST,
+ COLOR_FILTER_RED,
+ COLOR_FILTER_GREEN,
+ COLOR_FILTER_BLUE,
+ COLOR_FILTER_SMOOTH,
+} eSculptColorFilterTypes;
+
+EnumPropertyItem prop_color_filter_types[] = {
+ {COLOR_FILTER_FILL, "FILL", 0, "Fill", "Fill with a specific color"},
+ {COLOR_FILTER_HUE, "HUE", 0, "Hue", "Change hue"},
+ {COLOR_FILTER_SATURATION, "SATURATION", 0, "Saturation", "Change saturation"},
+ {COLOR_FILTER_VALUE, "VALUE", 0, "Value", "Change value"},
+
+ {COLOR_FILTER_BRIGHTNESS, "BRIGTHNESS", 0, "Brightness", "Change brightness"},
+ {COLOR_FILTER_CONTRAST, "CONTRAST", 0, "Contrast", "Change contrast"},
+
+ {COLOR_FILTER_SMOOTH, "SMOOTH", 0, "Smooth", "Smooth colors"},
+
+ {COLOR_FILTER_RED, "RED", 0, "Red", "Change red channel"},
+ {COLOR_FILTER_GREEN, "GREEN", 0, "Green", "Change green channel"},
+ {COLOR_FILTER_BLUE, "BLUE", 0, "Blue", "Change blue channel"},
+ {0, NULL, 0, NULL, NULL},
+};
+
+static void color_filter_task_cb(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+
+ const int mode = data->filter_type;
+
+ SculptOrigVertData orig_data;
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+
+ PBVHVertexIter vd;
+ BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
+ {
+ SCULPT_orig_vert_data_update(&orig_data, &vd);
+ float orig_color[3], final_color[4], hsv_color[3];
+ int hue;
+ float brightness, contrast, gain, delta, offset;
+ float fade = vd.mask ? *vd.mask : 0.0f;
+ fade = 1.0f - fade;
+ fade *= data->filter_strength;
+
+ copy_v3_v3(orig_color, orig_data.col);
+
+ switch (mode) {
+ case COLOR_FILTER_FILL: {
+ float fill_color_rgba[4];
+ copy_v3_v3(fill_color_rgba, data->filter_fill_color);
+ fill_color_rgba[3] = 1.0f;
+ CLAMP(fade, 0.0f, 1.0f);
+ mul_v4_fl(fill_color_rgba, fade);
+ blend_color_mix_float(final_color, orig_data.col, fill_color_rgba);
+ break;
+ }
+ case COLOR_FILTER_HUE:
+ rgb_to_hsv_v(orig_color, hsv_color);
+ hue = hsv_color[0] + fade;
+ hsv_color[0] = fabs((hsv_color[0] + fade) - hue);
+ hsv_to_rgb_v(hsv_color, final_color);
+ break;
+ case COLOR_FILTER_SATURATION:
+ rgb_to_hsv_v(orig_color, hsv_color);
+ hsv_color[1] = hsv_color[1] + fade;
+ CLAMP(hsv_color[1], 0.0f, 1.0f);
+ hsv_to_rgb_v(hsv_color, final_color);
+ break;
+ case COLOR_FILTER_VALUE:
+ rgb_to_hsv_v(orig_color, hsv_color);
+ hsv_color[2] = hsv_color[2] + fade;
+ CLAMP(hsv_color[2], 0.0f, 1.0f);
+ hsv_to_rgb_v(hsv_color, final_color);
+ break;
+ case COLOR_FILTER_RED:
+ orig_color[0] = orig_color[0] + fade;
+ CLAMP(orig_color[0], 0.0f, 1.0f);
+ copy_v3_v3(final_color, orig_color);
+ break;
+ case COLOR_FILTER_GREEN:
+ orig_color[1] = orig_color[1] + fade;
+ CLAMP(orig_color[1], 0.0f, 1.0f);
+ copy_v3_v3(final_color, orig_color);
+ break;
+ case COLOR_FILTER_BLUE:
+ orig_color[2] = orig_color[2] + fade;
+ CLAMP(orig_color[2], 0.0f, 1.0f);
+ copy_v3_v3(final_color, orig_color);
+ break;
+ case COLOR_FILTER_BRIGHTNESS:
+ CLAMP(fade, -1.0f, 1.0f);
+ brightness = fade;
+ contrast = 0;
+ delta = contrast / 2.0f;
+ gain = 1.0f - delta * 2.0f;
+ delta *= -1;
+ offset = gain * (brightness + delta);
+ for (int i = 0; i < 3; i++) {
+ final_color[i] = gain * orig_color[i] + offset;
+ CLAMP(final_color[i], 0.0f, 1.0f);
+ }
+ break;
+ case COLOR_FILTER_CONTRAST:
+ CLAMP(fade, -1.0f, 1.0f);
+ brightness = 0;
+ contrast = fade;
+ delta = contrast / 2.0f;
+ gain = 1.0f - delta * 2.0f;
+ if (contrast > 0) {
+ gain = 1.0f / ((gain != 0.0f) ? gain : FLT_EPSILON);
+ offset = gain * (brightness - delta);
+ }
+ else {
+ delta *= -1;
+ offset = gain * (brightness + delta);
+ }
+ for (int i = 0; i < 3; i++) {
+ final_color[i] = gain * orig_color[i] + offset;
+ CLAMP(final_color[i], 0.0f, 1.0f);
+ }
+ break;
+ case COLOR_FILTER_SMOOTH: {
+ CLAMP(fade, -1.0f, 1.0f);
+ float smooth_color[4];
+ SCULPT_neighbor_color_average(ss, smooth_color, vd.index);
+ blend_color_interpolate_float(final_color, vd.col, smooth_color, fade);
+ break;
+ }
+ }
+
+ copy_v3_v3(vd.col, final_color);
+
+ if (vd.mvert) {
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+ BKE_pbvh_node_mark_update_color(data->nodes[n]);
+}
+
+static int sculpt_color_filter_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ Object *ob = CTX_data_active_object(C);
+ SculptSession *ss = ob->sculpt;
+ Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
+ const int mode = RNA_enum_get(op->ptr, "type");
+ float filter_strength = RNA_float_get(op->ptr, "strength");
+
+ if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
+ SCULPT_undo_push_end();
+ SCULPT_filter_cache_free(ss);
+ SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_COLOR);
+ return OPERATOR_FINISHED;
+ }
+
+ if (event->type != MOUSEMOVE) {
+ return OPERATOR_RUNNING_MODAL;
+ }
+
+ float len = event->prevclickx - event->mval[0];
+ filter_strength = filter_strength * -len * 0.001f;
+
+ float fill_color[3];
+ RNA_float_get_array(op->ptr, "fill_color", fill_color);
+ IMB_colormanagement_srgb_to_scene_linear_v3(fill_color);
+
+ SculptThreadedTaskData data = {
+ .sd = sd,
+ .ob = ob,
+ .nodes = ss->filter_cache->nodes,
+ .filter_type = mode,
+ .filter_strength = filter_strength,
+ .filter_fill_color = fill_color,
+ };
+
+ TaskParallelSettings settings;
+ BLI_parallel_range_settings_defaults(&settings);
+
+ BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->totnode);
+ BLI_task_parallel_range(0, ss->filter_cache->totnode, &data, color_filter_task_cb, &settings);
+
+ SCULPT_flush_update_step(C, SCULPT_UPDATE_COLOR);
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static int sculpt_color_filter_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+{
+ Object *ob = CTX_data_active_object(C);
+ Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
+ Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
+ SculptSession *ss = ob->sculpt;
+ int mode = RNA_enum_get(op->ptr, "type");
+ PBVH *pbvh = ob->sculpt->pbvh;
+
+ /* Disable for multires and dyntopo for now */
+ if (!ss->pbvh) {
+ return OPERATOR_CANCELLED;
+ }
+ if (BKE_pbvh_type(pbvh) != PBVH_FACES) {
+ return OPERATOR_CANCELLED;
+ }
+
+ if (!ss->vcol) {
+ return OPERATOR_CANCELLED;
+ }
+
+ SCULPT_undo_push_begin("color filter");
+
+ bool needs_pmap = mode == COLOR_FILTER_SMOOTH;
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_pmap, false, true);
+
+ if (BKE_pbvh_type(pbvh) == PBVH_FACES && needs_pmap && !ob->sculpt->pmap) {
+ return OPERATOR_CANCELLED;
+ }
+
+ SCULPT_filter_cache_init(ob, sd, SCULPT_UNDO_COLOR);
+
+ WM_event_add_modal_handler(C, op);
+ return OPERATOR_RUNNING_MODAL;
+}
+
+void SCULPT_OT_color_filter(struct wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Filter color";
+ ot->idname = "SCULPT_OT_color_filter";
+ ot->description = "Applies a filter to modify the current sculpt vertex colors";
+
+ /* api callbacks */
+ ot->invoke = sculpt_color_filter_invoke;
+ ot->modal = sculpt_color_filter_modal;
+ ot->poll = SCULPT_mode_poll;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* rna */
+ RNA_def_enum(ot->srna, "type", prop_color_filter_types, COLOR_FILTER_HUE, "Filter type", "");
+ RNA_def_float(
+ ot->srna, "strength", 1.0f, -10.0f, 10.0f, "Strength", "Filter Strength", -10.0f, 10.0f);
+
+ PropertyRNA *prop = RNA_def_float_color(
+ ot->srna, "fill_color", 3, NULL, 0.0f, FLT_MAX, "Fill Color", "fill color", 0.0f, 1.0f);
+ RNA_def_property_subtype(prop, PROP_COLOR_GAMMA);
+}
diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mask.c b/source/blender/editors/sculpt_paint/sculpt_filter_mask.c
index d2a683461a7..83145f5600f 100644
--- a/source/blender/editors/sculpt_paint/sculpt_filter_mask.c
+++ b/source/blender/editors/sculpt_paint/sculpt_filter_mask.c
@@ -202,7 +202,7 @@ static int sculpt_mask_filter_exec(bContext *C, wmOperator *op)
int totnode;
int filter_type = RNA_enum_get(op->ptr, "filter_type");
- BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
SCULPT_vertex_random_access_init(ss);
@@ -247,7 +247,7 @@ static int sculpt_mask_filter_exec(bContext *C, wmOperator *op)
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, mask_filter_task_cb, &settings);
if (ELEM(filter_type, MASK_FILTER_GROW, MASK_FILTER_SHRINK)) {
@@ -276,7 +276,7 @@ void SCULPT_mask_filter_smooth_apply(
for (int i = 0; i < smooth_iterations; i++) {
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, mask_filter_task_cb, &settings);
}
}
@@ -432,7 +432,7 @@ static int sculpt_dirty_mask_exec(bContext *C, wmOperator *op)
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
int totnode;
- BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
SCULPT_vertex_random_access_init(ss);
@@ -459,7 +459,7 @@ static int sculpt_dirty_mask_exec(bContext *C, wmOperator *op)
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
settings.func_reduce = dirty_mask_compute_range_reduce;
settings.userdata_chunk = &range;
diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
index fd0f67f040a..494588d0996 100644
--- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
+++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
@@ -70,10 +70,10 @@ static void filter_cache_init_task_cb(void *__restrict userdata,
SculptThreadedTaskData *data = userdata;
PBVHNode *node = data->nodes[i];
- SCULPT_undo_push_node(data->ob, node, SCULPT_UNDO_COORDS);
+ SCULPT_undo_push_node(data->ob, node, data->filter_undo_type);
}
-void SCULPT_filter_cache_init(Object *ob, Sculpt *sd)
+void SCULPT_filter_cache_init(Object *ob, Sculpt *sd, const int undo_type)
{
SculptSession *ss = ob->sculpt;
PBVH *pbvh = ob->sculpt->pbvh;
@@ -110,11 +110,11 @@ void SCULPT_filter_cache_init(Object *ob, Sculpt *sd)
.sd = sd,
.ob = ob,
.nodes = ss->filter_cache->nodes,
+ .filter_undo_type = undo_type,
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(
- &settings, (sd->flags & SCULPT_USE_OPENMP), ss->filter_cache->totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->totnode);
BLI_task_parallel_range(
0, ss->filter_cache->totnode, &data, filter_cache_init_task_cb, &settings);
}
@@ -475,7 +475,7 @@ static int sculpt_mesh_filter_modal(bContext *C, wmOperator *op, const wmEvent *
SCULPT_vertex_random_access_init(ss);
bool needs_pmap = sculpt_mesh_filter_needs_pmap(filter_type, use_face_sets);
- BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_pmap, false);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_pmap, false, false);
SculptThreadedTaskData data = {
.sd = sd,
@@ -486,8 +486,7 @@ static int sculpt_mesh_filter_modal(bContext *C, wmOperator *op, const wmEvent *
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(
- &settings, (sd->flags & SCULPT_USE_OPENMP), ss->filter_cache->totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->totnode);
BLI_task_parallel_range(0, ss->filter_cache->totnode, &data, mesh_filter_task_cb, &settings);
if (filter_type == MESH_FILTER_SURFACE_SMOOTH) {
@@ -542,7 +541,7 @@ static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent
SCULPT_vertex_random_access_init(ss);
bool needs_pmap = sculpt_mesh_filter_needs_pmap(filter_type, use_face_sets);
- BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_pmap, false);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_pmap, false, false);
const int totvert = SCULPT_vertex_count_get(ss);
if (BKE_pbvh_type(pbvh) == PBVH_FACES && needs_pmap && !ob->sculpt->pmap) {
@@ -551,7 +550,7 @@ static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent
SCULPT_undo_push_begin("Mesh filter");
- SCULPT_filter_cache_init(ob, sd);
+ SCULPT_filter_cache_init(ob, sd, SCULPT_UNDO_COORDS);
if (use_face_sets) {
ss->filter_cache->active_face_set = SCULPT_active_face_set_get(ss);
diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h
index 0e27658e848..c981f89ada7 100644
--- a/source/blender/editors/sculpt_paint/sculpt_intern.h
+++ b/source/blender/editors/sculpt_paint/sculpt_intern.h
@@ -27,6 +27,7 @@
#include "DNA_brush_types.h"
#include "DNA_key_types.h"
#include "DNA_listBase.h"
+#include "DNA_meshdata_types.h"
#include "DNA_vec_types.h"
#include "BLI_bitmap.h"
@@ -56,6 +57,7 @@ typedef enum SculptUpdateType {
SCULPT_UPDATE_COORDS = 1 << 0,
SCULPT_UPDATE_MASK = 1 << 1,
SCULPT_UPDATE_VISIBILITY = 1 << 2,
+ SCULPT_UPDATE_COLOR = 1 << 3,
} SculptUpdateType;
void SCULPT_flush_update_step(bContext *C, SculptUpdateType update_flags);
@@ -92,6 +94,7 @@ int SCULPT_vertex_count_get(struct SculptSession *ss);
const float *SCULPT_vertex_co_get(struct SculptSession *ss, int index);
void SCULPT_vertex_normal_get(SculptSession *ss, int index, float no[3]);
float SCULPT_vertex_mask_get(struct SculptSession *ss, int index);
+const float *SCULPT_vertex_color_get(SculptSession *ss, int index);
#define SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY 256
typedef struct SculptVertexNeighborIter {
@@ -145,6 +148,15 @@ void SCULPT_active_vertex_normal_get(SculptSession *ss, float normal[3]);
bool SCULPT_vertex_is_boundary(SculptSession *ss, const int index);
+/* Fake Neighbors */
+
+#define FAKE_NEIGHBOR_NONE -1
+
+void SCULPT_fake_neighbors_ensure(struct Sculpt *sd, Object *ob, const float max_dist);
+void SCULPT_fake_neighbors_enable(Object *ob);
+void SCULPT_fake_neighbors_disable(Object *ob);
+void SCULPT_fake_neighbors_free(struct Object *ob);
+
/* Sculpt Visibility API */
void SCULPT_vertex_visible_set(SculptSession *ss, int index, bool visible);
@@ -179,15 +191,20 @@ typedef struct {
float (*coords)[3];
short (*normals)[3];
const float *vmasks;
+ float (*colors)[4];
/* Original coordinate, normal, and mask. */
const float *co;
const short *no;
float mask;
+ const float *col;
} SculptOrigVertData;
void SCULPT_orig_vert_data_init(SculptOrigVertData *data, Object *ob, PBVHNode *node);
void SCULPT_orig_vert_data_update(SculptOrigVertData *orig_data, PBVHVertexIter *iter);
+void SCULPT_orig_vert_data_unode_init(SculptOrigVertData *data,
+ Object *ob,
+ struct SculptUndoNode *unode);
/* Utils. */
void SCULPT_calc_brush_plane(struct Sculpt *sd,
@@ -305,7 +322,7 @@ float *SCULPT_boundary_automasking_init(Object *ob,
float *automask_factor);
/* Filters. */
-void SCULPT_filter_cache_init(Object *ob, Sculpt *sd);
+void SCULPT_filter_cache_init(Object *ob, Sculpt *sd, const int undo_type);
void SCULPT_filter_cache_free(SculptSession *ss);
void SCULPT_mask_filter_smooth_apply(
@@ -374,6 +391,12 @@ void SCULPT_multiplane_scrape_preview_draw(const uint gpuattr,
/* Draw Face Sets Brush. */
void SCULPT_do_draw_face_sets_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode);
+/* Paint Brush. */
+void SCULPT_do_paint_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode);
+
+/* Smear Brush. */
+void SCULPT_do_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode);
+
/* Smooth Brush. */
void SCULPT_neighbor_average(SculptSession *ss, float avg[3], uint vert);
@@ -383,6 +406,7 @@ void SCULPT_bmesh_four_neighbor_average(float avg[3], float direction[3], struct
void SCULPT_neighbor_coords_average(SculptSession *ss, float result[3], int index);
float SCULPT_neighbor_mask_average(SculptSession *ss, int index);
+void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], int index);
void SCULPT_smooth(Sculpt *sd,
Object *ob,
@@ -427,6 +451,7 @@ typedef enum {
SCULPT_UNDO_DYNTOPO_SYMMETRIZE,
SCULPT_UNDO_GEOMETRY,
SCULPT_UNDO_FACE_SETS,
+ SCULPT_UNDO_COLOR,
} SculptUndoType;
/* Storage of geometry for the undo node.
@@ -457,6 +482,7 @@ typedef struct SculptUndoNode {
float (*co)[3];
float (*orig_co)[3];
short (*no)[3];
+ float (*col)[4];
float *mask;
int totvert;
@@ -556,6 +582,7 @@ typedef struct SculptThreadedTaskData {
int filter_type;
float filter_strength;
+ float *filter_fill_color;
bool use_area_cos;
bool use_area_nos;
@@ -568,6 +595,8 @@ typedef struct SculptThreadedTaskData {
bool any_vertex_sampled;
+ float *wet_mix_sampled_color;
+
float *prev_mask;
float *pose_factor;
@@ -601,6 +630,7 @@ typedef struct SculptThreadedTaskData {
bool dirty_mask_dirty_only;
int face_set;
+ int filter_undo_type;
ThreadMutex mutex;
@@ -720,6 +750,8 @@ typedef struct StrokeCache {
float bstrength;
float normal_weight; /* from brush (with optional override) */
+ float (*prev_colors)[4];
+
/* The rest is temporary storage that isn't saved as a property */
bool first_time; /* Beginning of stroke may do some things special */
@@ -813,6 +845,9 @@ typedef struct StrokeCache {
float stroke_local_mat[4][4];
float multiplane_scrape_angle;
+ float wet_mix_prev_color[4];
+ float density_seed;
+
rcti previous_r; /* previous redraw rectangle */
rcti current_r; /* current redraw rectangle */
@@ -901,6 +936,9 @@ void SCULPT_OT_mesh_filter(struct wmOperatorType *ot);
/* Cloth Filter. */
void SCULPT_OT_cloth_filter(struct wmOperatorType *ot);
+/* Color Filter. */
+void SCULPT_OT_color_filter(struct wmOperatorType *ot);
+
/* Mask filter and Dirty Mask. */
void SCULPT_OT_mask_filter(struct wmOperatorType *ot);
void SCULPT_OT_dirty_mask(struct wmOperatorType *ot);
diff --git a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c
index cbb198e14a3..60483cc168d 100644
--- a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c
+++ b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c
@@ -206,7 +206,7 @@ static int sculpt_mask_expand_modal(bContext *C, wmOperator *op, const wmEvent *
(event->type == EVT_PADENTER && event->val == KM_PRESS)) {
/* Smooth iterations. */
- BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false);
const int smooth_iterations = RNA_int_get(op->ptr, "smooth_iterations");
SCULPT_mask_filter_smooth_apply(
sd, ob, ss->filter_cache->nodes, ss->filter_cache->totnode, smooth_iterations);
@@ -289,8 +289,7 @@ static int sculpt_mask_expand_modal(bContext *C, wmOperator *op, const wmEvent *
.mask_expand_create_face_set = RNA_boolean_get(op->ptr, "create_face_set"),
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(
- &settings, (sd->flags & SCULPT_USE_OPENMP), ss->filter_cache->totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->totnode);
BLI_task_parallel_range(0, ss->filter_cache->totnode, &data, sculpt_expand_task_cb, &settings);
ss->filter_cache->mask_update_current_it = mask_expand_update_it;
}
@@ -365,7 +364,7 @@ static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent
SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false);
- BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
int vertex_count = SCULPT_vertex_count_get(ss);
@@ -459,8 +458,7 @@ static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent
.mask_expand_create_face_set = RNA_boolean_get(op->ptr, "create_face_set"),
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(
- &settings, (sd->flags & SCULPT_USE_OPENMP), ss->filter_cache->totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->totnode);
BLI_task_parallel_range(0, ss->filter_cache->totnode, &data, sculpt_expand_task_cb, &settings);
const char *status_str = TIP_(
diff --git a/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c b/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c
index f3327706102..503f9429a50 100644
--- a/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c
+++ b/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c
@@ -304,7 +304,7 @@ void SCULPT_do_multiplane_scrape_brush(Sculpt *sd, Object *ob, PBVHNode **nodes,
MultiplaneScrapeSampleData mssd = {{{0}}};
TaskParallelSettings sample_settings;
- BKE_pbvh_parallel_range_settings(&sample_settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&sample_settings, true, totnode);
sample_settings.func_reduce = calc_multiplane_scrape_surface_reduce;
sample_settings.userdata_chunk = &mssd;
sample_settings.userdata_chunk_size = sizeof(MultiplaneScrapeSampleData);
@@ -395,7 +395,7 @@ void SCULPT_do_multiplane_scrape_brush(Sculpt *sd, Object *ob, PBVHNode **nodes,
plane_from_point_normal_v3(data.multiplane_scrape_planes[0], area_co, plane_no);
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_multiplane_scrape_brush_task_cb_ex, &settings);
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_color.c b/source/blender/editors/sculpt_paint/sculpt_paint_color.c
new file mode 100644
index 00000000000..9b38def6b7b
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/sculpt_paint_color.c
@@ -0,0 +1,477 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup edsculpt
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_hash.h"
+#include "BLI_math.h"
+#include "BLI_math_color_blend.h"
+#include "BLI_task.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_brush.h"
+#include "BKE_colortools.h"
+#include "BKE_context.h"
+#include "BKE_mesh.h"
+#include "BKE_mesh_mapping.h"
+#include "BKE_object.h"
+#include "BKE_paint.h"
+#include "BKE_pbvh.h"
+#include "BKE_scene.h"
+
+#include "DEG_depsgraph.h"
+
+#include "IMB_colormanagement.h"
+
+#include "WM_api.h"
+#include "WM_message.h"
+#include "WM_toolsystem.h"
+#include "WM_types.h"
+
+#include "ED_object.h"
+#include "ED_screen.h"
+#include "ED_sculpt.h"
+#include "paint_intern.h"
+#include "sculpt_intern.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "UI_interface.h"
+
+#include "IMB_imbuf.h"
+
+#include "bmesh.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+static void do_color_smooth_task_cb_exec(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict tls)
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ const Brush *brush = data->brush;
+ const float bstrength = ss->cache->bstrength;
+
+ PBVHVertexIter vd;
+
+ SculptBrushTest test;
+ SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
+ ss, &test, data->brush->falloff_shape);
+ const int thread_id = BLI_task_parallel_thread_id(tls);
+
+ BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
+ {
+ if (sculpt_brush_test_sq_fn(&test, vd.co)) {
+ const float fade = bstrength * SCULPT_brush_strength_factor(ss,
+ brush,
+ vd.co,
+ sqrtf(test.dist),
+ vd.no,
+ vd.fno,
+ vd.mask ? *vd.mask : 0.0f,
+ vd.index,
+ thread_id);
+
+ float smooth_color[4];
+ SCULPT_neighbor_color_average(ss, smooth_color, vd.index);
+ blend_color_interpolate_float(vd.col, vd.col, smooth_color, fade);
+
+ if (vd.mvert) {
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+}
+
+static void do_paint_brush_task_cb_ex(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict tls)
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ const Brush *brush = data->brush;
+ const float bstrength = fabsf(ss->cache->bstrength);
+
+ PBVHVertexIter vd;
+ PBVHColorBufferNode *color_buffer;
+
+ SculptOrigVertData orig_data;
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+
+ color_buffer = BKE_pbvh_node_color_buffer_get(data->nodes[n]);
+
+ SculptBrushTest test;
+ SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
+ ss, &test, data->brush->falloff_shape);
+ const int thread_id = BLI_task_parallel_thread_id(tls);
+
+ float brush_color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
+ copy_v3_v3(brush_color, BKE_brush_color_get(ss->scene, brush));
+ IMB_colormanagement_srgb_to_scene_linear_v3(brush_color);
+
+ BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
+ {
+ SCULPT_orig_vert_data_update(&orig_data, &vd);
+
+ bool affect_vertex = false;
+ float distance_to_stroke_location = 0.0f;
+ if (brush->tip_roundness < 1.0f) {
+ affect_vertex = SCULPT_brush_test_cube(&test, vd.co, data->mat, brush->tip_roundness);
+ distance_to_stroke_location = ss->cache->radius * test.dist;
+ }
+ else {
+ affect_vertex = sculpt_brush_test_sq_fn(&test, vd.co);
+ distance_to_stroke_location = sqrtf(test.dist);
+ }
+
+ if (affect_vertex) {
+ float fade = bstrength * SCULPT_brush_strength_factor(ss,
+ brush,
+ vd.co,
+ distance_to_stroke_location,
+ vd.no,
+ vd.fno,
+ vd.mask ? *vd.mask : 0.0f,
+ vd.index,
+ thread_id);
+
+ /* Density. */
+ float noise = 1.0f;
+ const float density = brush->density;
+ if (density < 1.0f) {
+ const float hash_noise = BLI_hash_int_01(ss->cache->density_seed * 1000 * vd.index);
+ if (hash_noise > density) {
+ noise = density * hash_noise;
+ fade = fade * noise;
+ }
+ }
+
+ /* Brush paint color, brush test falloff and flow. */
+ float paint_color[4];
+ float wet_mix_color[4];
+ float buffer_color[4];
+
+ mul_v4_v4fl(paint_color, brush_color, fade * brush->flow);
+ mul_v4_v4fl(wet_mix_color, data->wet_mix_sampled_color, fade * brush->flow);
+
+ /* Interpolate with the wet_mix color for wet paint mixing. */
+ blend_color_interpolate_float(paint_color, paint_color, wet_mix_color, brush->wet_mix);
+ blend_color_mix_float(color_buffer->color[vd.i], color_buffer->color[vd.i], paint_color);
+
+ /* Final mix over the original color using brush alpha. */
+ mul_v4_v4fl(buffer_color, color_buffer->color[vd.i], brush->alpha);
+
+ IMB_blend_color_float(vd.col, orig_data.col, buffer_color, brush->blend);
+ }
+ CLAMP4(vd.col, 0.0f, 1.0f);
+
+ if (vd.mvert) {
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+}
+
+typedef struct SampleWetPaintTLSData {
+ int tot_samples;
+ float color[4];
+} SampleWetPaintTLSData;
+
+static void do_sample_wet_paint_task_cb(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict tls)
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ SampleWetPaintTLSData *swptd = tls->userdata_chunk;
+ PBVHVertexIter vd;
+
+ SculptBrushTest test;
+ SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
+ ss, &test, data->brush->falloff_shape);
+
+ BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
+ {
+ if (sculpt_brush_test_sq_fn(&test, vd.co)) {
+ add_v4_v4(swptd->color, vd.col);
+ swptd->tot_samples++;
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+}
+
+static void sample_wet_paint_reduce(const void *__restrict UNUSED(userdata),
+ void *__restrict chunk_join,
+ void *__restrict chunk)
+{
+ SampleWetPaintTLSData *join = chunk_join;
+ SampleWetPaintTLSData *swptd = chunk;
+
+ join->tot_samples += swptd->tot_samples;
+ add_v4_v4(join->color, swptd->color);
+}
+
+void SCULPT_do_paint_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
+{
+ Brush *brush = BKE_paint_brush(&sd->paint);
+ SculptSession *ss = ob->sculpt;
+
+ if (!ss->vcol) {
+ return;
+ }
+
+ if (ss->cache->first_time && ss->cache->mirror_symmetry_pass == 0) {
+ ss->cache->density_seed = BLI_hash_int_01(ss->cache->location[0] * 1000);
+ return;
+ }
+
+ BKE_curvemapping_initialize(brush->curve);
+
+ float area_no[3];
+ float mat[4][4];
+ float scale[4][4];
+ float tmat[4][4];
+
+ /* If the brush is round the tip does not need to be aligned to the surface, so this saves a
+ * whole iteration over the affected nodes. */
+ if (brush->tip_roundness < 1.0f) {
+ SCULPT_calc_area_normal(sd, ob, nodes, totnode, area_no);
+
+ cross_v3_v3v3(mat[0], area_no, ss->cache->grab_delta_symmetry);
+ mat[0][3] = 0;
+ cross_v3_v3v3(mat[1], area_no, mat[0]);
+ mat[1][3] = 0;
+ copy_v3_v3(mat[2], area_no);
+ mat[2][3] = 0;
+ copy_v3_v3(mat[3], ss->cache->location);
+ mat[3][3] = 1;
+ normalize_m4(mat);
+
+ scale_m4_fl(scale, ss->cache->radius);
+ mul_m4_m4m4(tmat, mat, scale);
+ mul_v3_fl(tmat[1], brush->tip_scale_x);
+ invert_m4_m4(mat, tmat);
+ if (is_zero_m4(mat)) {
+ return;
+ }
+ }
+
+ /* Smooth colors mode. */
+ if (ss->cache->alt_smooth) {
+ SculptThreadedTaskData data = {
+ .sd = sd,
+ .ob = ob,
+ .brush = brush,
+ .nodes = nodes,
+ .mat = mat,
+ };
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ BLI_task_parallel_range(0, totnode, &data, do_color_smooth_task_cb_exec, &settings);
+ return;
+ }
+
+ /* Regular Paint mode. */
+
+ /* Wet paint color sampling. */
+ float wet_color[4] = {0.0f};
+ if (brush->wet_mix > 0.0f) {
+ SculptThreadedTaskData task_data = {
+ .sd = sd,
+ .ob = ob,
+ .nodes = nodes,
+ .brush = brush,
+ };
+
+ SampleWetPaintTLSData swptd;
+ swptd.tot_samples = 0;
+ zero_v4(swptd.color);
+
+ TaskParallelSettings settings_sample;
+ BKE_pbvh_parallel_range_settings(&settings_sample, true, totnode);
+ settings_sample.func_reduce = sample_wet_paint_reduce;
+ settings_sample.userdata_chunk = &swptd;
+ settings_sample.userdata_chunk_size = sizeof(SampleWetPaintTLSData);
+ BLI_task_parallel_range(0, totnode, &task_data, do_sample_wet_paint_task_cb, &settings_sample);
+
+ if (swptd.tot_samples > 0 && is_finite_v4(swptd.color)) {
+ copy_v4_v4(wet_color, swptd.color);
+ mul_v4_fl(wet_color, 1.0f / (float)swptd.tot_samples);
+ CLAMP4(wet_color, 0.0f, 1.0f);
+
+ if (ss->cache->first_time) {
+ copy_v4_v4(ss->cache->wet_mix_prev_color, wet_color);
+ }
+ blend_color_interpolate_float(
+ wet_color, wet_color, ss->cache->wet_mix_prev_color, brush->wet_persistence);
+ copy_v4_v4(ss->cache->wet_mix_prev_color, wet_color);
+ CLAMP4(ss->cache->wet_mix_prev_color, 0.0f, 1.0f);
+ }
+ }
+
+ /* Threaded loop over nodes. */
+ SculptThreadedTaskData data = {
+ .sd = sd,
+ .ob = ob,
+ .brush = brush,
+ .nodes = nodes,
+ .wet_mix_sampled_color = wet_color,
+ .mat = mat,
+ };
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ BLI_task_parallel_range(0, totnode, &data, do_paint_brush_task_cb_ex, &settings);
+}
+
+static void do_smear_brush_task_cb_exec(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict tls)
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ const Brush *brush = data->brush;
+ const float bstrength = ss->cache->bstrength;
+
+ PBVHVertexIter vd;
+
+ SculptBrushTest test;
+ SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
+ ss, &test, data->brush->falloff_shape);
+ const int thread_id = BLI_task_parallel_thread_id(tls);
+
+ BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
+ {
+ if (sculpt_brush_test_sq_fn(&test, vd.co)) {
+ const float fade = bstrength * SCULPT_brush_strength_factor(ss,
+ brush,
+ vd.co,
+ sqrtf(test.dist),
+ vd.no,
+ vd.fno,
+ vd.mask ? *vd.mask : 0.0f,
+ vd.index,
+ thread_id);
+
+ float current_disp[3];
+ float current_disp_norm[3];
+ float interp_color[4];
+ copy_v4_v4(interp_color, ss->cache->prev_colors[vd.index]);
+
+ sub_v3_v3v3(current_disp, ss->cache->location, ss->cache->last_location);
+ normalize_v3_v3(current_disp_norm, current_disp);
+ mul_v3_v3fl(current_disp, current_disp_norm, ss->cache->bstrength);
+
+ SculptVertexNeighborIter ni;
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) {
+ float vertex_disp[3];
+ float vertex_disp_norm[3];
+ sub_v3_v3v3(vertex_disp, SCULPT_vertex_co_get(ss, ni.index), vd.co);
+ const float *neighbor_color = ss->cache->prev_colors[ni.index];
+ normalize_v3_v3(vertex_disp_norm, vertex_disp);
+ if (dot_v3v3(current_disp_norm, vertex_disp_norm) < 0.0f) {
+ const float color_interp = clamp_f(
+ -dot_v3v3(current_disp_norm, vertex_disp_norm), 0.0f, 1.0f);
+ float color_mix[4];
+ copy_v4_v4(color_mix, neighbor_color);
+ mul_v4_fl(color_mix, color_interp * fade);
+ blend_color_mix_float(interp_color, interp_color, color_mix);
+ }
+ }
+ SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
+
+ blend_color_interpolate_float(vd.col, ss->cache->prev_colors[vd.index], interp_color, fade);
+
+ if (vd.mvert) {
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+}
+
+static void do_smear_store_prev_colors_task_cb_exec(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+
+ PBVHVertexIter vd;
+ BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
+ {
+ copy_v4_v4(ss->cache->prev_colors[vd.index], SCULPT_vertex_color_get(ss, vd.index));
+ }
+ BKE_pbvh_vertex_iter_end;
+}
+
+void SCULPT_do_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
+{
+ Brush *brush = BKE_paint_brush(&sd->paint);
+ SculptSession *ss = ob->sculpt;
+
+ if (!ss->vcol) {
+ return;
+ }
+
+ const int totvert = SCULPT_vertex_count_get(ss);
+
+ if (ss->cache->first_time && ss->cache->mirror_symmetry_pass == 0) {
+ if (!ss->cache->prev_colors) {
+ ss->cache->prev_colors = MEM_callocN(sizeof(float) * 4 * totvert, "prev colors");
+ for (int i = 0; i < totvert; i++) {
+ copy_v4_v4(ss->cache->prev_colors[i], SCULPT_vertex_color_get(ss, i));
+ }
+ }
+ }
+
+ BKE_curvemapping_initialize(brush->curve);
+
+ SculptThreadedTaskData data = {
+ .sd = sd,
+ .ob = ob,
+ .brush = brush,
+ .nodes = nodes,
+ };
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+
+ /* Smooth colors mode. */
+ if (ss->cache->alt_smooth) {
+ BLI_task_parallel_range(0, totnode, &data, do_color_smooth_task_cb_exec, &settings);
+ }
+ else {
+ /* Smear mode. */
+ BLI_task_parallel_range(0, totnode, &data, do_smear_store_prev_colors_task_cb_exec, &settings);
+ BLI_task_parallel_range(0, totnode, &data, do_smear_brush_task_cb_exec, &settings);
+ }
+}
diff --git a/source/blender/editors/sculpt_paint/sculpt_pose.c b/source/blender/editors/sculpt_paint/sculpt_pose.c
index a4a87051e03..c9e2b7318d6 100644
--- a/source/blender/editors/sculpt_paint/sculpt_pose.c
+++ b/source/blender/editors/sculpt_paint/sculpt_pose.c
@@ -293,7 +293,7 @@ static void sculpt_pose_grow_pose_factor(Sculpt *sd,
PoseGrowFactorTLSData gftd;
gftd.pos_count = 0;
zero_v3(gftd.pos_avg);
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
settings.func_reduce = pose_brush_grow_factor_reduce;
settings.userdata_chunk = &gftd;
settings.userdata_chunk_size = sizeof(PoseGrowFactorTLSData);
@@ -937,18 +937,32 @@ SculptPoseIKChain *SCULPT_pose_ik_chain_init(Sculpt *sd,
const float initial_location[3],
const float radius)
{
+ SculptPoseIKChain *ik_chain = NULL;
+
+ const bool use_fake_neighbors = !(br->flag2 & BRUSH_USE_CONNECTED_ONLY);
+
+ if (use_fake_neighbors) {
+ SCULPT_fake_neighbors_ensure(sd, ob, br->disconnected_distance_max);
+ SCULPT_fake_neighbors_enable(ob);
+ }
+
switch (br->pose_origin_type) {
case BRUSH_POSE_ORIGIN_TOPOLOGY:
- return pose_ik_chain_init_topology(sd, ob, ss, br, initial_location, radius);
+ ik_chain = pose_ik_chain_init_topology(sd, ob, ss, br, initial_location, radius);
break;
case BRUSH_POSE_ORIGIN_FACE_SETS:
- return pose_ik_chain_init_face_sets(sd, ob, ss, br, radius);
+ ik_chain = pose_ik_chain_init_face_sets(sd, ob, ss, br, radius);
break;
case BRUSH_POSE_ORIGIN_FACE_SETS_FK:
return pose_ik_chain_init_face_sets_fk(sd, ob, ss, radius, initial_location);
break;
}
- return NULL;
+
+ if (use_fake_neighbors) {
+ SCULPT_fake_neighbors_disable(ob);
+ }
+
+ return ik_chain;
}
void SCULPT_pose_brush_init(Sculpt *sd, Object *ob, SculptSession *ss, Brush *br)
@@ -975,7 +989,7 @@ void SCULPT_pose_brush_init(Sculpt *sd, Object *ob, SculptSession *ss, Brush *br
data.pose_factor = ss->cache->pose_ik_chain->segments[ik].weights;
for (int i = 0; i < br->pose_smooth_iterations; i++) {
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, pose_brush_init_task_cb_ex, &settings);
}
}
@@ -1198,7 +1212,7 @@ void SCULPT_do_pose_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_pose_brush_task_cb_ex, &settings);
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.c b/source/blender/editors/sculpt_paint/sculpt_smooth.c
index 17451cb40ae..aa82ae1d7ad 100644
--- a/source/blender/editors/sculpt_paint/sculpt_smooth.c
+++ b/source/blender/editors/sculpt_paint/sculpt_smooth.c
@@ -226,6 +226,26 @@ float SCULPT_neighbor_mask_average(SculptSession *ss, int index)
}
}
+void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], int index)
+{
+ float avg[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+ int total = 0;
+
+ SculptVertexNeighborIter ni;
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) {
+ add_v4_v4(avg, SCULPT_vertex_color_get(ss, ni.index));
+ total++;
+ }
+ SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
+
+ if (total > 0) {
+ mul_v4_v4fl(result, avg, 1.0f / (float)total);
+ }
+ else {
+ copy_v4_v4(result, SCULPT_vertex_color_get(ss, index));
+ }
+}
+
static void do_smooth_brush_mesh_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict tls)
@@ -433,7 +453,7 @@ void SCULPT_smooth(Sculpt *sd,
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
switch (type) {
case PBVH_GRIDS:
@@ -525,10 +545,15 @@ static void SCULPT_do_surface_smooth_brush_laplacian_task_cb_ex(
{
SCULPT_orig_vert_data_update(&orig_data, &vd);
if (sculpt_brush_test_sq_fn(&test, vd.co)) {
- const float fade =
- bstrength *
- SCULPT_brush_strength_factor(
- ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, 0.0f, vd.index, thread_id);
+ const float fade = bstrength * SCULPT_brush_strength_factor(ss,
+ brush,
+ vd.co,
+ sqrtf(test.dist),
+ vd.no,
+ vd.fno,
+ vd.mask ? *vd.mask : 0.0f,
+ vd.index,
+ thread_id);
float disp[3];
SCULPT_surface_smooth_laplacian_step(ss,
@@ -566,10 +591,15 @@ static void SCULPT_do_surface_smooth_brush_displace_task_cb_ex(
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
if (sculpt_brush_test_sq_fn(&test, vd.co)) {
- const float fade =
- bstrength *
- SCULPT_brush_strength_factor(
- ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, 0.0f, vd.index, thread_id);
+ const float fade = bstrength * SCULPT_brush_strength_factor(ss,
+ brush,
+ vd.co,
+ sqrtf(test.dist),
+ vd.no,
+ vd.fno,
+ vd.mask ? *vd.mask : 0.0f,
+ vd.index,
+ thread_id);
SCULPT_surface_smooth_displace_step(
ss, vd.co, ss->cache->surface_smooth_laplacian_disp, vd.index, beta, fade);
}
@@ -598,7 +628,7 @@ void SCULPT_do_surface_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, in
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
for (int i = 0; i < brush->surface_smooth_iterations; i++) {
BLI_task_parallel_range(
0, totnode, &data, SCULPT_do_surface_smooth_brush_laplacian_task_cb_ex, &settings);
diff --git a/source/blender/editors/sculpt_paint/sculpt_transform.c b/source/blender/editors/sculpt_paint/sculpt_transform.c
index 59a4695ce18..f616817c330 100644
--- a/source/blender/editors/sculpt_paint/sculpt_transform.c
+++ b/source/blender/editors/sculpt_paint/sculpt_transform.c
@@ -71,12 +71,12 @@ void ED_sculpt_init_transform(struct bContext *C)
copy_v4_v4(ss->init_pivot_rot, ss->pivot_rot);
SCULPT_undo_push_begin("Transform");
- BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
ss->pivot_rot[3] = 1.0f;
SCULPT_vertex_random_access_init(ss);
- SCULPT_filter_cache_init(ob, sd);
+ SCULPT_filter_cache_init(ob, sd, SCULPT_UNDO_COORDS);
}
static void sculpt_transform_task_cb(void *__restrict userdata,
@@ -127,7 +127,7 @@ void ED_sculpt_update_modal_transform(struct bContext *C)
const char symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL;
SCULPT_vertex_random_access_init(ss);
- BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
SculptThreadedTaskData data = {
.sd = sd,
@@ -178,8 +178,7 @@ void ED_sculpt_update_modal_transform(struct bContext *C)
}
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(
- &settings, (sd->flags & SCULPT_USE_OPENMP), ss->filter_cache->totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->totnode);
BLI_task_parallel_range(
0, ss->filter_cache->totnode, &data, sculpt_transform_task_cb, &settings);
@@ -253,7 +252,7 @@ static int sculpt_set_pivot_position_exec(bContext *C, wmOperator *op)
int mode = RNA_enum_get(op->ptr, "mode");
- BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true, false);
/* Pivot to center. */
if (mode == SCULPT_PIVOT_POSITION_ORIGIN) {
diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c
index d21552efafe..6ede631eb11 100644
--- a/source/blender/editors/sculpt_paint/sculpt_undo.c
+++ b/source/blender/editors/sculpt_paint/sculpt_undo.c
@@ -205,7 +205,7 @@ static bool sculpt_undo_restore_coords(bContext *C, Depsgraph *depsgraph, Sculpt
if (kb) {
ob->shapenr = BLI_findindex(&key->block, kb) + 1;
- BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
WM_event_add_notifier(C, NC_OBJECT | ND_DATA, ob);
}
else {
@@ -326,6 +326,29 @@ static bool sculpt_undo_restore_hidden(bContext *C, SculptUndoNode *unode)
return true;
}
+static bool sculpt_undo_restore_color(bContext *C, SculptUndoNode *unode)
+{
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+ Object *ob = OBACT(view_layer);
+ SculptSession *ss = ob->sculpt;
+ MVert *mvert;
+ MPropCol *vcol;
+ int *index, i;
+
+ if (unode->maxvert) {
+ /* regular mesh restore */
+ index = unode->index;
+ mvert = ss->mvert;
+ vcol = ss->vcol;
+
+ for (i = 0; i < unode->totvert; i++) {
+ copy_v4_v4(vcol[index[i]].color, unode->col[i]);
+ mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ return true;
+}
+
static bool sculpt_undo_restore_mask(bContext *C, SculptUndoNode *unode)
{
ViewLayer *view_layer = CTX_data_view_layer(C);
@@ -394,10 +417,7 @@ static void sculpt_undo_bmesh_restore_generic_task_cb(
BKE_pbvh_node_mark_redraw(nodes[n]);
}
-static void sculpt_undo_bmesh_restore_generic(bContext *C,
- SculptUndoNode *unode,
- Object *ob,
- SculptSession *ss)
+static void sculpt_undo_bmesh_restore_generic(SculptUndoNode *unode, Object *ob, SculptSession *ss)
{
if (unode->applied) {
BM_log_undo(ss->bm, ss->bm_log);
@@ -411,12 +431,11 @@ static void sculpt_undo_bmesh_restore_generic(bContext *C,
if (unode->type == SCULPT_UNDO_MASK) {
int totnode;
PBVHNode **nodes;
- Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode);
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(
0, totnode, nodes, sculpt_undo_bmesh_restore_generic_task_cb, &settings);
@@ -588,7 +607,7 @@ static int sculpt_undo_bmesh_restore(bContext *C,
return true;
default:
if (ss->bm_log) {
- sculpt_undo_bmesh_restore_generic(C, unode, ob, ss);
+ sculpt_undo_bmesh_restore_generic(unode, ob, ss);
return true;
}
break;
@@ -633,7 +652,7 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase
rebuild = true;
BKE_pbvh_search_callback(ss->pbvh, NULL, NULL, update_cb, &rebuild);
- BKE_sculpt_update_object_for_edit(depsgraph, ob, true, need_mask);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, true, need_mask, false);
SCULPT_visibility_sync_all_face_sets_to_vertices(ss);
@@ -659,7 +678,7 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase
* ensure object is updated after the node is handled. */
const SculptUndoNode *first_unode = (const SculptUndoNode *)lb->first;
if (first_unode->type != SCULPT_UNDO_GEOMETRY) {
- BKE_sculpt_update_object_for_edit(depsgraph, ob, false, need_mask);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, false, need_mask, false);
}
if (sculpt_undo_bmesh_restore(C, lb->first, ob, ss)) {
@@ -712,10 +731,15 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase
break;
case SCULPT_UNDO_FACE_SETS:
break;
+ case SCULPT_UNDO_COLOR:
+ if (sculpt_undo_restore_color(C, unode)) {
+ update = true;
+ }
+ break;
case SCULPT_UNDO_GEOMETRY:
sculpt_undo_geometry_restore(unode, ob);
- BKE_sculpt_update_object_for_edit(depsgraph, ob, false, need_mask);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, false, need_mask, false);
break;
case SCULPT_UNDO_DYNTOPO_BEGIN:
@@ -998,6 +1022,12 @@ static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node, Sculpt
usculpt->undo_size += (sizeof(float) * sizeof(int)) * allvert;
break;
+ case SCULPT_UNDO_COLOR:
+ unode->col = MEM_callocN(sizeof(MPropCol) * allvert, "SculptUndoNode.col");
+
+ usculpt->undo_size += (sizeof(MPropCol) * sizeof(int)) * allvert;
+
+ break;
case SCULPT_UNDO_DYNTOPO_BEGIN:
case SCULPT_UNDO_DYNTOPO_END:
case SCULPT_UNDO_DYNTOPO_SYMMETRIZE:
@@ -1083,6 +1113,18 @@ static void sculpt_undo_store_mask(Object *ob, SculptUndoNode *unode)
BKE_pbvh_vertex_iter_end;
}
+static void sculpt_undo_store_color(Object *ob, SculptUndoNode *unode)
+{
+ SculptSession *ss = ob->sculpt;
+ PBVHVertexIter vd;
+
+ BKE_pbvh_vertex_iter_begin(ss->pbvh, unode->node, vd, PBVH_ITER_ALL)
+ {
+ copy_v4_v4(unode->col[vd.i], vd.col);
+ }
+ BKE_pbvh_vertex_iter_end;
+}
+
static SculptUndoNodeGeometry *sculpt_undo_geometry_get(SculptUndoNode *unode)
{
if (!unode->geometry_original.is_initialized) {
@@ -1203,6 +1245,7 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, Sculpt
case SCULPT_UNDO_DYNTOPO_SYMMETRIZE:
case SCULPT_UNDO_GEOMETRY:
case SCULPT_UNDO_FACE_SETS:
+ case SCULPT_UNDO_COLOR:
break;
}
}
@@ -1272,6 +1315,9 @@ SculptUndoNode *SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType
case SCULPT_UNDO_MASK:
sculpt_undo_store_mask(ob, unode);
break;
+ case SCULPT_UNDO_COLOR:
+ sculpt_undo_store_color(ob, unode);
+ break;
case SCULPT_UNDO_DYNTOPO_BEGIN:
case SCULPT_UNDO_DYNTOPO_END:
case SCULPT_UNDO_DYNTOPO_SYMMETRIZE:
diff --git a/source/blender/editors/sound/sound_ops.c b/source/blender/editors/sound/sound_ops.c
index 81ac8a16d8a..2e52f3aa8a8 100644
--- a/source/blender/editors/sound/sound_ops.c
+++ b/source/blender/editors/sound/sound_ops.c
@@ -771,7 +771,7 @@ static int sound_pack_exec(bContext *C, wmOperator *op)
}
sound->packedfile = BKE_packedfile_new(
- op->reports, sound->name, ID_BLEND_PATH(bmain, &sound->id));
+ op->reports, sound->filepath, ID_BLEND_PATH(bmain, &sound->id));
BKE_sound_load(bmain, sound);
return OPERATOR_FINISHED;
@@ -847,7 +847,8 @@ static int sound_unpack_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSE
"AutoPack is enabled, so image will be packed again on file save");
}
- unpack_menu(C, "SOUND_OT_unpack", sound->id.name + 2, sound->name, "sounds", sound->packedfile);
+ unpack_menu(
+ C, "SOUND_OT_unpack", sound->id.name + 2, sound->filepath, "sounds", sound->packedfile);
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c
index b390e4b56d6..aa784800be0 100644
--- a/source/blender/editors/space_action/action_edit.c
+++ b/source/blender/editors/space_action/action_edit.c
@@ -800,10 +800,17 @@ static void insert_gpencil_keys(bAnimContext *ac, short mode)
add_frame_mode = GP_GETFRAME_ADD_NEW;
}
- /* insert gp frames */
+ /* Insert gp frames. */
+ bGPdata *gpd_old = NULL;
for (ale = anim_data.first; ale; ale = ale->next) {
+ bGPdata *gpd = (bGPdata *)ale->id;
bGPDlayer *gpl = (bGPDlayer *)ale->data;
BKE_gpencil_layer_frame_get(gpl, CFRA, add_frame_mode);
+ /* Check if the gpd changes to tag only once. */
+ if (gpd != gpd_old) {
+ BKE_gpencil_tag(gpd);
+ gpd_old = gpd;
+ }
}
ANIM_animdata_update(ac, &anim_data);
@@ -839,6 +846,9 @@ static int actkeys_insertkey_exec(bContext *C, wmOperator *op)
}
/* set notifier that keyframes have changed */
+ if (ac.datatype == ANIMCONT_GPENCIL) {
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+ }
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_ADDED, NULL);
return OPERATOR_FINISHED;
@@ -888,7 +898,7 @@ static void duplicate_action_keys(bAnimContext *ac)
duplicate_fcurve_keys((FCurve *)ale->key_data);
}
else if (ale->type == ANIMTYPE_GPLAYER) {
- ED_gplayer_frames_duplicate((bGPDlayer *)ale->data);
+ ED_gpencil_layer_frames_duplicate((bGPDlayer *)ale->data);
}
else if (ale->type == ANIMTYPE_MASKLAYER) {
ED_masklayer_frames_duplicate((MaskLayer *)ale->data);
@@ -964,7 +974,7 @@ static bool delete_action_keys(bAnimContext *ac)
bool changed = false;
if (ale->type == ANIMTYPE_GPLAYER) {
- changed = ED_gplayer_frames_delete((bGPDlayer *)ale->data);
+ changed = ED_gpencil_layer_frames_delete((bGPDlayer *)ale->data);
}
else if (ale->type == ANIMTYPE_MASKLAYER) {
changed = ED_masklayer_frames_delete((MaskLayer *)ale->data);
@@ -1539,7 +1549,7 @@ static void setkeytype_gpencil_keys(bAnimContext *ac, short mode)
/* loop through each layer */
for (ale = anim_data.first; ale; ale = ale->next) {
if (ale->type == ANIMTYPE_GPLAYER) {
- ED_gplayer_frames_keytype_set(ale->data, mode);
+ ED_gpencil_layer_frames_keytype_set(ale->data, mode);
ale->update |= ANIM_UPDATE_DEPS;
}
}
@@ -1740,7 +1750,7 @@ static void snap_action_keys(bAnimContext *ac, short mode)
AnimData *adt = ANIM_nla_mapping_get(ac, ale);
if (ale->type == ANIMTYPE_GPLAYER) {
- ED_gplayer_snap_frames(ale->data, ac->scene, mode);
+ ED_gpencil_layer_snap_frames(ale->data, ac->scene, mode);
}
else if (ale->type == ANIMTYPE_MASKLAYER) {
ED_masklayer_snap_frames(ale->data, ac->scene, mode);
@@ -1870,7 +1880,7 @@ static void mirror_action_keys(bAnimContext *ac, short mode)
AnimData *adt = ANIM_nla_mapping_get(ac, ale);
if (ale->type == ANIMTYPE_GPLAYER) {
- ED_gplayer_mirror_frames(ale->data, ac->scene, mode);
+ ED_gpencil_layer_mirror_frames(ale->data, ac->scene, mode);
}
else if (ale->type == ANIMTYPE_MASKLAYER) {
/* TODO */
diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c
index bbb68f632fb..f40b792269b 100644
--- a/source/blender/editors/space_action/action_select.c
+++ b/source/blender/editors/space_action/action_select.c
@@ -270,7 +270,7 @@ static void deselect_action_keys(bAnimContext *ac, short test, short sel)
if (test) {
for (ale = anim_data.first; ale; ale = ale->next) {
if (ale->type == ANIMTYPE_GPLAYER) {
- if (ED_gplayer_frame_select_check(ale->data)) {
+ if (ED_gpencil_layer_frame_select_check(ale->data)) {
sel = SELECT_SUBTRACT;
break;
}
@@ -296,7 +296,7 @@ static void deselect_action_keys(bAnimContext *ac, short test, short sel)
/* Now set the flags */
for (ale = anim_data.first; ale; ale = ale->next) {
if (ale->type == ANIMTYPE_GPLAYER) {
- ED_gplayer_frame_select_set(ale->data, sel);
+ ED_gpencil_layer_frame_select_set(ale->data, sel);
ale->update |= ANIM_UPDATE_DEPS;
}
else if (ale->type == ANIMTYPE_MASKLAYER) {
@@ -405,14 +405,14 @@ static void box_select_elem(
bGPdata *gpd = ale->data;
bGPDlayer *gpl;
for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
- ED_gplayer_frames_select_box(gpl, xmin, xmax, data->selectmode);
+ ED_gpencil_layer_frames_select_box(gpl, xmin, xmax, data->selectmode);
}
ale->update |= ANIM_UPDATE_DEPS;
break;
}
#endif
case ANIMTYPE_GPLAYER: {
- ED_gplayer_frames_select_box(ale->data, xmin, xmax, sel_data->selectmode);
+ ED_gpencil_layer_frames_select_box(ale->data, xmin, xmax, sel_data->selectmode);
ale->update |= ANIM_UPDATE_DEPS;
break;
}
@@ -641,13 +641,13 @@ static void region_select_elem(RegionSelectData *sel_data, bAnimListElem *ale, b
bGPdata *gpd = ale->data;
bGPDlayer *gpl;
for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
- ED_gplayer_frames_select_region(&rdata->ked, ale->data, rdata->mode, rdata->selectmode);
+ ED_gpencil_layer_frames_select_region(&rdata->ked, ale->data, rdata->mode, rdata->selectmode);
}
break;
}
#endif
case ANIMTYPE_GPLAYER: {
- ED_gplayer_frames_select_region(
+ ED_gpencil_layer_frames_select_region(
&sel_data->ked, ale->data, sel_data->mode, sel_data->selectmode);
ale->update |= ANIM_UPDATE_DEPS;
break;
@@ -972,7 +972,7 @@ static void markers_selectkeys_between(bAnimContext *ac)
ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1);
}
else if (ale->type == ANIMTYPE_GPLAYER) {
- ED_gplayer_frames_select_box(ale->data, min, max, SELECT_ADD);
+ ED_gpencil_layer_frames_select_box(ale->data, min, max, SELECT_ADD);
ale->update |= ANIM_UPDATE_DEPS;
}
else if (ale->type == ANIMTYPE_MASKLAYER) {
@@ -1008,7 +1008,7 @@ static void columnselect_action_keys(bAnimContext *ac, short mode)
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
for (ale = anim_data.first; ale; ale = ale->next) {
- ED_gplayer_make_cfra_list(ale->data, &ked.list, 1);
+ ED_gpencil_layer_make_cfra_list(ale->data, &ked.list, 1);
}
}
else {
@@ -1385,7 +1385,7 @@ static void actkeys_select_leftright(bAnimContext *ac, short leftright, short se
ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1);
}
else if (ale->type == ANIMTYPE_GPLAYER) {
- ED_gplayer_frames_select_box(ale->data, ked.f1, ked.f2, select_mode);
+ ED_gpencil_layer_frames_select_box(ale->data, ked.f1, ked.f2, select_mode);
ale->update |= ANIM_UPDATE_DEPS;
}
else if (ale->type == ANIMTYPE_MASKLAYER) {
diff --git a/source/blender/editors/space_action/space_action.c b/source/blender/editors/space_action/space_action.c
index e92ea906237..079cee290ae 100644
--- a/source/blender/editors/space_action/space_action.c
+++ b/source/blender/editors/space_action/space_action.c
@@ -128,7 +128,7 @@ static SpaceLink *action_new(const ScrArea *area, const Scene *scene)
region->v2d.minzoom = 0.01f;
region->v2d.maxzoom = 50;
region->v2d.scroll = (V2D_SCROLL_BOTTOM | V2D_SCROLL_HORIZONTAL_HANDLES);
- region->v2d.scroll |= (V2D_SCROLL_RIGHT);
+ region->v2d.scroll |= V2D_SCROLL_RIGHT;
region->v2d.keepzoom = V2D_LOCKZOOM_Y;
region->v2d.keepofs = V2D_KEEPOFS_Y;
region->v2d.align = V2D_ALIGN_NO_POS_Y;
@@ -181,10 +181,14 @@ static void action_main_region_draw(const bContext *C, ARegion *region)
Object *obact = CTX_data_active_object(C);
bAnimContext ac;
View2D *v2d = &region->v2d;
- View2DScrollers *scrollers;
short marker_flag = 0;
short cfra_flag = 0;
+ UI_view2d_view_ortho(v2d);
+ if (saction->flag & SACTION_DRAWTIME) {
+ cfra_flag |= DRAWCFRA_UNIT_SECONDS;
+ }
+
/* clear and setup matrix */
UI_ThemeClearColor(TH_BACK);
GPU_clear(GPU_COLOR_BIT);
@@ -204,12 +208,6 @@ static void action_main_region_draw(const bContext *C, ARegion *region)
draw_channel_strips(&ac, saction, region);
}
- /* current frame */
- if (saction->flag & SACTION_DRAWTIME) {
- cfra_flag |= DRAWCFRA_UNIT_SECONDS;
- }
- ANIM_draw_cfra(C, v2d, cfra_flag);
-
/* markers */
UI_view2d_view_orthoSpecial(region, v2d, 1);
@@ -238,11 +236,20 @@ static void action_main_region_draw(const bContext *C, ARegion *region)
/* scrubbing region */
ED_time_scrub_draw(region, scene, saction->flag & SACTION_DRAWTIME, true);
+}
+
+static void action_main_region_draw_overlay(const bContext *C, ARegion *region)
+{
+ /* draw entirely, view changes should be handled here */
+ const SpaceAction *saction = CTX_wm_space_action(C);
+ const Scene *scene = CTX_data_scene(C);
+ View2D *v2d = &region->v2d;
+
+ /* scrubbing region */
+ ED_time_scrub_draw_current_frame(region, scene, saction->flag & SACTION_DRAWTIME, true);
/* scrollers */
- scrollers = UI_view2d_scrollers_calc(v2d, NULL);
- UI_view2d_scrollers_draw(v2d, scrollers);
- UI_view2d_scrollers_free(scrollers);
+ UI_view2d_scrollers_draw(v2d, NULL);
}
/* add handlers, stuff you only do once or on area/region changes */
@@ -874,6 +881,7 @@ void ED_spacetype_action(void)
art->regionid = RGN_TYPE_WINDOW;
art->init = action_main_region_init;
art->draw = action_main_region_draw;
+ art->draw_overlay = action_main_region_draw_overlay;
art->listener = action_main_region_listener;
art->message_subscribe = saction_main_region_message_subscribe;
art->keymapflag = ED_KEYMAP_VIEW2D | ED_KEYMAP_ANIMATION | ED_KEYMAP_FRAMES;
diff --git a/source/blender/editors/space_buttons/CMakeLists.txt b/source/blender/editors/space_buttons/CMakeLists.txt
index c3b7d65689f..25ff6bbd098 100644
--- a/source/blender/editors/space_buttons/CMakeLists.txt
+++ b/source/blender/editors/space_buttons/CMakeLists.txt
@@ -54,8 +54,4 @@ if(WITH_FREESTYLE)
add_definitions(-DWITH_FREESTYLE)
endif()
-if(WITH_NEW_OBJECT_TYPES)
- add_definitions(-DWITH_NEW_OBJECT_TYPES)
-endif()
-
blender_add_lib(bf_editor_space_buttons "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c
index 7e6088bc3cc..ba618083620 100644
--- a/source/blender/editors/space_buttons/buttons_context.c
+++ b/source/blender/editors/space_buttons/buttons_context.c
@@ -251,14 +251,12 @@ static int buttons_context_path_data(ButsContextPath *path, int type)
else if (RNA_struct_is_a(ptr->type, &RNA_GreasePencil) && (type == -1 || type == OB_GPENCIL)) {
return 1;
}
-#ifdef WITH_NEW_OBJECT_TYPES
else if (RNA_struct_is_a(ptr->type, &RNA_Hair) && (type == -1 || type == OB_HAIR)) {
return 1;
}
else if (RNA_struct_is_a(ptr->type, &RNA_PointCloud) && (type == -1 || type == OB_POINTCLOUD)) {
return 1;
}
-#endif
else if (RNA_struct_is_a(ptr->type, &RNA_Volume) && (type == -1 || type == OB_VOLUME)) {
return 1;
}
@@ -796,10 +794,8 @@ const char *buttons_context_dir[] = {
"line_style",
"collection",
"gpencil",
-#ifdef WITH_NEW_OBJECT_TYPES
"hair",
"pointcloud",
-#endif
"volume",
NULL,
};
@@ -878,7 +874,6 @@ int buttons_context(const bContext *C, const char *member, bContextDataResult *r
set_pointer_type(path, result, &RNA_LightProbe);
return 1;
}
-#ifdef WITH_NEW_OBJECT_TYPES
else if (CTX_data_equals(member, "hair")) {
set_pointer_type(path, result, &RNA_Hair);
return 1;
@@ -887,7 +882,6 @@ int buttons_context(const bContext *C, const char *member, bContextDataResult *r
set_pointer_type(path, result, &RNA_PointCloud);
return 1;
}
-#endif
else if (CTX_data_equals(member, "volume")) {
set_pointer_type(path, result, &RNA_Volume);
return 1;
diff --git a/source/blender/editors/space_buttons/space_buttons.c b/source/blender/editors/space_buttons/space_buttons.c
index 187334a5c34..71b86996989 100644
--- a/source/blender/editors/space_buttons/space_buttons.c
+++ b/source/blender/editors/space_buttons/space_buttons.c
@@ -30,10 +30,10 @@
#include "BLI_utildefines.h"
#include "BKE_context.h"
+#include "BKE_gpencil_modifier.h" /* Types for registering panels. */
#include "BKE_modifier.h"
#include "BKE_screen.h"
-
-#include "DNA_modifier_types.h"
+#include "BKE_shader_fx.h"
#include "ED_screen.h"
#include "ED_space_api.h"
@@ -643,6 +643,21 @@ void ED_spacetype_buttons(void)
mti->panelRegister(art);
}
}
+ for (int i = 0; i < NUM_GREASEPENCIL_MODIFIER_TYPES; i++) {
+ const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(i);
+ if (mti != NULL && mti->panelRegister != NULL) {
+ mti->panelRegister(art);
+ }
+ }
+ for (int i = 0; i < NUM_SHADER_FX_TYPES; i++) {
+ if (i == eShaderFxType_Light_deprecated) {
+ continue;
+ }
+ const ShaderFxTypeInfo *fxti = BKE_shaderfx_get_info(i);
+ if (fxti != NULL && fxti->panelRegister != NULL) {
+ fxti->panelRegister(art);
+ }
+ }
/* regions: header */
art = MEM_callocN(sizeof(ARegionType), "spacetype buttons region");
diff --git a/source/blender/editors/space_clip/clip_dopesheet_draw.c b/source/blender/editors/space_clip/clip_dopesheet_draw.c
index 84ab5e6524b..c3aca95910b 100644
--- a/source/blender/editors/space_clip/clip_dopesheet_draw.c
+++ b/source/blender/editors/space_clip/clip_dopesheet_draw.c
@@ -118,7 +118,7 @@ void clip_draw_dopesheet_main(SpaceClip *sc, ARegion *region, Scene *scene)
MovieTrackingDopesheet *dopesheet = &tracking->dopesheet;
MovieTrackingDopesheetChannel *channel;
float strip[4], selected_strip[4];
- float height = (dopesheet->tot_channel * CHANNEL_STEP) + (CHANNEL_HEIGHT);
+ float height = (dopesheet->tot_channel * CHANNEL_STEP) + CHANNEL_HEIGHT;
uint keyframe_len = 0;
@@ -305,7 +305,7 @@ void clip_draw_dopesheet_channels(const bContext *C, ARegion *region)
MovieTracking *tracking = &clip->tracking;
MovieTrackingDopesheet *dopesheet = &tracking->dopesheet;
- int height = (dopesheet->tot_channel * CHANNEL_STEP) + (CHANNEL_HEIGHT);
+ int height = (dopesheet->tot_channel * CHANNEL_STEP) + CHANNEL_HEIGHT;
if (height > BLI_rcti_size_y(&v2d->mask)) {
/* don't use totrect set, as the width stays the same
diff --git a/source/blender/editors/space_clip/clip_ops.c b/source/blender/editors/space_clip/clip_ops.c
index 984aa0a63ad..22707b97afa 100644
--- a/source/blender/editors/space_clip/clip_ops.c
+++ b/source/blender/editors/space_clip/clip_ops.c
@@ -283,7 +283,7 @@ static int open_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)
}
if (clip) {
- BLI_strncpy(path, clip->name, sizeof(path));
+ BLI_strncpy(path, clip->filepath, sizeof(path));
BLI_path_abs(path, CTX_data_main(C)->name);
BLI_path_parent_dir(path);
diff --git a/source/blender/editors/space_clip/space_clip.c b/source/blender/editors/space_clip/space_clip.c
index a68e06951f7..54da00a132d 100644
--- a/source/blender/editors/space_clip/space_clip.c
+++ b/source/blender/editors/space_clip/space_clip.c
@@ -96,7 +96,7 @@ static void init_preview_region(const Scene *scene,
region->v2d.minzoom = 0.01f;
region->v2d.maxzoom = 50;
region->v2d.scroll = (V2D_SCROLL_BOTTOM | V2D_SCROLL_HORIZONTAL_HANDLES);
- region->v2d.scroll |= (V2D_SCROLL_RIGHT);
+ region->v2d.scroll |= V2D_SCROLL_RIGHT;
region->v2d.keepzoom = V2D_LOCKZOOM_Y;
region->v2d.keepofs = V2D_KEEPOFS_Y;
region->v2d.align = V2D_ALIGN_NO_POS_Y;
@@ -1045,7 +1045,6 @@ static void clip_preview_region_init(wmWindowManager *wm, ARegion *region)
static void graph_region_draw(const bContext *C, ARegion *region)
{
View2D *v2d = &region->v2d;
- View2DScrollers *scrollers;
SpaceClip *sc = CTX_wm_space_clip(C);
Scene *scene = CTX_data_scene(C);
short cfra_flag = 0;
@@ -1076,9 +1075,7 @@ static void graph_region_draw(const bContext *C, ARegion *region)
ED_time_scrub_draw(region, scene, sc->flag & SC_SHOW_SECONDS, true);
/* scrollers */
- scrollers = UI_view2d_scrollers_calc(v2d, NULL);
- UI_view2d_scrollers_draw(v2d, scrollers);
- UI_view2d_scrollers_free(scrollers);
+ UI_view2d_scrollers_draw(v2d, NULL);
/* scale indicators */
{
@@ -1095,7 +1092,6 @@ static void dopesheet_region_draw(const bContext *C, ARegion *region)
SpaceClip *sc = CTX_wm_space_clip(C);
MovieClip *clip = ED_space_clip_get_clip(sc);
View2D *v2d = &region->v2d;
- View2DScrollers *scrollers;
short cfra_flag = 0;
if (clip) {
@@ -1127,9 +1123,7 @@ static void dopesheet_region_draw(const bContext *C, ARegion *region)
ED_time_scrub_draw(region, scene, sc->flag & SC_SHOW_SECONDS, true);
/* scrollers */
- scrollers = UI_view2d_scrollers_calc(v2d, NULL);
- UI_view2d_scrollers_draw(v2d, scrollers);
- UI_view2d_scrollers_free(scrollers);
+ UI_view2d_scrollers_draw(v2d, NULL);
}
static void clip_preview_region_draw(const bContext *C, ARegion *region)
diff --git a/source/blender/editors/space_console/space_console.c b/source/blender/editors/space_console/space_console.c
index d8c097cad37..3c62aeb1759 100644
--- a/source/blender/editors/space_console/space_console.c
+++ b/source/blender/editors/space_console/space_console.c
@@ -70,7 +70,7 @@ static SpaceLink *console_new(const ScrArea *UNUSED(area), const Scene *UNUSED(s
region->regiontype = RGN_TYPE_WINDOW;
/* keep in sync with info */
- region->v2d.scroll |= (V2D_SCROLL_RIGHT);
+ region->v2d.scroll |= V2D_SCROLL_RIGHT;
region->v2d.align |= V2D_ALIGN_NO_NEG_X | V2D_ALIGN_NO_NEG_Y; /* align bottom left */
region->v2d.keepofs |= V2D_LOCKOFS_X;
region->v2d.keepzoom = (V2D_LOCKZOOM_X | V2D_LOCKZOOM_Y | V2D_LIMITZOOM | V2D_KEEPASPECT);
@@ -208,7 +208,6 @@ static void console_main_region_draw(const bContext *C, ARegion *region)
/* draw entirely, view changes should be handled here */
SpaceConsole *sc = CTX_wm_space_console(C);
View2D *v2d = &region->v2d;
- View2DScrollers *scrollers;
if (BLI_listbase_is_empty(&sc->scrollback)) {
WM_operator_name_call((bContext *)C, "CONSOLE_OT_banner", WM_OP_EXEC_DEFAULT, NULL);
@@ -230,9 +229,7 @@ static void console_main_region_draw(const bContext *C, ARegion *region)
UI_view2d_view_restore(C);
/* scrollers */
- scrollers = UI_view2d_scrollers_calc(v2d, NULL);
- UI_view2d_scrollers_draw(v2d, scrollers);
- UI_view2d_scrollers_free(scrollers);
+ UI_view2d_scrollers_draw(v2d, NULL);
}
static void console_operatortypes(void)
diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c
index 79635da3ede..b2fd3366473 100644
--- a/source/blender/editors/space_file/filelist.c
+++ b/source/blender/editors/space_file/filelist.c
@@ -3128,7 +3128,7 @@ static void filelist_readjob_main_recursive(Main *bmain, FileList *filelist)
}
else {
char relname[FILE_MAX + (MAX_ID_NAME - 2) + 3];
- BLI_snprintf(relname, sizeof(relname), "%s | %s", id->lib->name, id->name + 2);
+ BLI_snprintf(relname, sizeof(relname), "%s | %s", id->lib->filepath, id->name + 2);
files->entry->relpath = BLI_strdup(relname);
}
// files->type |= S_IFREG;
diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c
index 620d28a0c7d..1bf0b2b0a9a 100644
--- a/source/blender/editors/space_file/space_file.c
+++ b/source/blender/editors/space_file/space_file.c
@@ -465,7 +465,6 @@ static void file_main_region_draw(const bContext *C, ARegion *region)
FileSelectParams *params = ED_fileselect_get_params(sfile);
View2D *v2d = &region->v2d;
- View2DScrollers *scrollers;
float col[3];
/* Needed, because filelist is not initialized on loading */
@@ -525,9 +524,7 @@ static void file_main_region_draw(const bContext *C, ARegion *region)
/* scrollers */
rcti view_rect;
ED_fileselect_layout_maskrect(sfile->layout, v2d, &view_rect);
- scrollers = UI_view2d_scrollers_calc(v2d, &view_rect);
- UI_view2d_scrollers_draw(v2d, scrollers);
- UI_view2d_scrollers_free(scrollers);
+ UI_view2d_scrollers_draw(v2d, &view_rect);
}
static void file_operatortypes(void)
diff --git a/source/blender/editors/space_graph/space_graph.c b/source/blender/editors/space_graph/space_graph.c
index b9c7c529620..052e089942c 100644
--- a/source/blender/editors/space_graph/space_graph.c
+++ b/source/blender/editors/space_graph/space_graph.c
@@ -200,9 +200,7 @@ static void graph_main_region_draw(const bContext *C, ARegion *region)
Scene *scene = CTX_data_scene(C);
bAnimContext ac;
View2D *v2d = &region->v2d;
- View2DScrollers *scrollers;
float col[3];
- short cfra_flag = 0;
/* clear and setup matrix */
UI_GetThemeColor3fv(TH_BACK, col);
@@ -284,14 +282,6 @@ static void graph_main_region_draw(const bContext *C, ARegion *region)
immUnbindProgram();
}
- if (sipo->mode != SIPO_MODE_DRIVERS) {
- /* current frame */
- if (sipo->flag & SIPO_DRAWTIME) {
- cfra_flag |= DRAWCFRA_UNIT_SECONDS;
- }
- ANIM_draw_cfra(C, v2d, cfra_flag);
- }
-
/* markers */
if (sipo->mode != SIPO_MODE_DRIVERS) {
UI_view2d_view_orthoSpecial(region, v2d, 1);
@@ -316,12 +306,22 @@ static void graph_main_region_draw(const bContext *C, ARegion *region)
/* time-scrubbing */
ED_time_scrub_draw(region, scene, display_seconds, false);
+}
+
+static void graph_main_region_draw_overlay(const bContext *C, ARegion *region)
+{
+ /* draw entirely, view changes should be handled here */
+ const SpaceGraph *sipo = CTX_wm_space_graph(C);
+ const Scene *scene = CTX_data_scene(C);
+ const bool draw_vert_line = sipo->mode != SIPO_MODE_DRIVERS;
+ View2D *v2d = &region->v2d;
+
+ /* scrubbing region */
+ ED_time_scrub_draw_current_frame(region, scene, sipo->flag & SIPO_DRAWTIME, draw_vert_line);
/* scrollers */
// FIXME: args for scrollers depend on the type of data being shown...
- scrollers = UI_view2d_scrollers_calc(v2d, NULL);
- UI_view2d_scrollers_draw(v2d, scrollers);
- UI_view2d_scrollers_free(scrollers);
+ UI_view2d_scrollers_draw(v2d, NULL);
/* scale numbers */
{
@@ -358,7 +358,6 @@ static void graph_channel_region_draw(const bContext *C, ARegion *region)
{
bAnimContext ac;
View2D *v2d = &region->v2d;
- View2DScrollers *scrollers;
float col[3];
/* clear and setup matrix */
@@ -380,9 +379,7 @@ static void graph_channel_region_draw(const bContext *C, ARegion *region)
UI_view2d_view_restore(C);
/* scrollers */
- scrollers = UI_view2d_scrollers_calc(v2d, NULL);
- UI_view2d_scrollers_draw(v2d, scrollers);
- UI_view2d_scrollers_free(scrollers);
+ UI_view2d_scrollers_draw(v2d, NULL);
}
/* add handlers, stuff you only do once or on area/region changes */
@@ -859,6 +856,7 @@ void ED_spacetype_ipo(void)
art->regionid = RGN_TYPE_WINDOW;
art->init = graph_main_region_init;
art->draw = graph_main_region_draw;
+ art->draw_overlay = graph_main_region_draw_overlay;
art->listener = graph_region_listener;
art->message_subscribe = graph_region_message_subscribe;
art->keymapflag = ED_KEYMAP_VIEW2D | ED_KEYMAP_ANIMATION | ED_KEYMAP_FRAMES;
diff --git a/source/blender/editors/space_image/image_edit.c b/source/blender/editors/space_image/image_edit.c
index c9f2ec38354..cb0fdcf23ca 100644
--- a/source/blender/editors/space_image/image_edit.c
+++ b/source/blender/editors/space_image/image_edit.c
@@ -191,7 +191,7 @@ int ED_space_image_get_display_channel_mask(ImBuf *ibuf)
result &= ~(SI_USE_ALPHA | SI_SHOW_ALPHA);
}
if (!zbuf) {
- result &= ~(SI_SHOW_ZBUF);
+ result &= ~SI_SHOW_ZBUF;
}
if (!color) {
result &= ~(SI_SHOW_R | SI_SHOW_G | SI_SHOW_B);
diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c
index 8cb85ce9800..f71c92b4c1f 100644
--- a/source/blender/editors/space_image/image_ops.c
+++ b/source/blender/editors/space_image/image_ops.c
@@ -1237,7 +1237,7 @@ static Image *image_open_single(Main *bmain,
if (!exists) {
/* only image path after save, never ibuf */
if (is_relative_path) {
- BLI_path_rel(ima->name, relbase);
+ BLI_path_rel(ima->filepath, relbase);
}
/* handle multiview images */
@@ -1417,7 +1417,7 @@ static int image_open_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(
}
if (ima) {
- path = ima->name;
+ path = ima->filepath;
}
if (RNA_struct_property_is_set(op->ptr, "filepath")) {
@@ -1577,7 +1577,7 @@ static int image_replace_exec(bContext *C, wmOperator *op)
RNA_string_get(op->ptr, "filepath", str);
/* we cant do much if the str is longer then FILE_MAX :/ */
- BLI_strncpy(sima->image->name, str, sizeof(sima->image->name));
+ BLI_strncpy(sima->image->filepath, str, sizeof(sima->image->filepath));
if (sima->image->source == IMA_SRC_GENERATED) {
sima->image->source = IMA_SRC_FILE;
@@ -1614,10 +1614,10 @@ static int image_replace_invoke(bContext *C, wmOperator *op, const wmEvent *UNUS
}
if (!RNA_struct_property_is_set(op->ptr, "relative_path")) {
- RNA_boolean_set(op->ptr, "relative_path", BLI_path_is_rel(sima->image->name));
+ RNA_boolean_set(op->ptr, "relative_path", BLI_path_is_rel(sima->image->filepath));
}
- image_filesel(C, op, sima->image->name);
+ image_filesel(C, op, sima->image->filepath);
return OPERATOR_RUNNING_MODAL;
}
@@ -1777,7 +1777,7 @@ static int image_save_options_init(Main *bmain,
/* append UDIM numbering if not present */
if (ima->source == IMA_SRC_TILED &&
- (BLI_path_sequence_decode(ima->name, NULL, NULL, NULL) != 1001)) {
+ (BLI_path_sequence_decode(ima->filepath, NULL, NULL, NULL) != 1001)) {
int len = strlen(opts->filepath);
STR_CONCAT(opts->filepath, len, ".1001");
}
@@ -2301,7 +2301,7 @@ static bool image_should_be_saved(Image *ima, bool *is_format_writable)
static bool image_has_valid_path(Image *ima)
{
- return strchr(ima->name, '\\') || strchr(ima->name, '/');
+ return strchr(ima->filepath, '\\') || strchr(ima->filepath, '/');
}
bool ED_image_should_save_modified(const Main *bmain)
@@ -2336,7 +2336,7 @@ int ED_image_save_all_modified_info(const Main *bmain, ReportList *reports)
RPT_WARNING,
"Packed library image can't be saved: \"%s\" from \"%s\"",
ima->id.name + 2,
- ima->id.lib->name);
+ ima->id.lib->filepath);
}
}
else if (!is_format_writable) {
@@ -2348,19 +2348,21 @@ int ED_image_save_all_modified_info(const Main *bmain, ReportList *reports)
else {
if (image_has_valid_path(ima)) {
num_saveable_images++;
- if (BLI_gset_haskey(unique_paths, ima->name)) {
+ if (BLI_gset_haskey(unique_paths, ima->filepath)) {
BKE_reportf(reports,
RPT_WARNING,
"Multiple images can't be saved to an identical path: \"%s\"",
- ima->name);
+ ima->filepath);
}
else {
- BLI_gset_insert(unique_paths, BLI_strdup(ima->name));
+ BLI_gset_insert(unique_paths, BLI_strdup(ima->filepath));
}
}
else {
- BKE_reportf(
- reports, RPT_WARNING, "Image can't be saved, no valid file path: \"%s\"", ima->name);
+ BKE_reportf(reports,
+ RPT_WARNING,
+ "Image can't be saved, no valid file path: \"%s\"",
+ ima->filepath);
}
}
}
@@ -3044,7 +3046,7 @@ static int image_unpack_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSE
unpack_menu(C,
"IMAGE_OT_unpack",
ima->id.name + 2,
- ima->name,
+ ima->filepath,
"textures",
BKE_image_has_packedfile(ima) ?
((ImagePackedFile *)ima->packedfiles.first)->packedfile :
diff --git a/source/blender/editors/space_info/space_info.c b/source/blender/editors/space_info/space_info.c
index 04df0f0d4f0..836830916ed 100644
--- a/source/blender/editors/space_info/space_info.c
+++ b/source/blender/editors/space_info/space_info.c
@@ -77,7 +77,7 @@ static SpaceLink *info_new(const ScrArea *UNUSED(area), const Scene *UNUSED(scen
region->regiontype = RGN_TYPE_WINDOW;
/* keep in sync with console */
- region->v2d.scroll |= (V2D_SCROLL_RIGHT);
+ region->v2d.scroll |= V2D_SCROLL_RIGHT;
region->v2d.align |= V2D_ALIGN_NO_NEG_X | V2D_ALIGN_NO_NEG_Y; /* align bottom left */
region->v2d.keepofs |= V2D_LOCKOFS_X;
region->v2d.keepzoom = (V2D_LOCKZOOM_X | V2D_LOCKZOOM_Y | V2D_LIMITZOOM | V2D_KEEPASPECT);
@@ -139,7 +139,6 @@ static void info_main_region_draw(const bContext *C, ARegion *region)
/* draw entirely, view changes should be handled here */
SpaceInfo *sinfo = CTX_wm_space_info(C);
View2D *v2d = &region->v2d;
- View2DScrollers *scrollers;
/* clear and setup matrix */
UI_ThemeClearColor(TH_BACK);
@@ -161,9 +160,7 @@ static void info_main_region_draw(const bContext *C, ARegion *region)
UI_view2d_view_restore(C);
/* scrollers */
- scrollers = UI_view2d_scrollers_calc(v2d, NULL);
- UI_view2d_scrollers_draw(v2d, scrollers);
- UI_view2d_scrollers_free(scrollers);
+ UI_view2d_scrollers_draw(v2d, NULL);
}
static void info_operatortypes(void)
diff --git a/source/blender/editors/space_nla/nla_buttons.c b/source/blender/editors/space_nla/nla_buttons.c
index 307b6d9bc21..d0d9f2f57bb 100644
--- a/source/blender/editors/space_nla/nla_buttons.c
+++ b/source/blender/editors/space_nla/nla_buttons.c
@@ -407,7 +407,7 @@ static void nla_panel_properties(const bContext *C, Panel *panel)
uiItemR(row, &strip_ptr, "use_auto_blend", 0, NULL, ICON_NONE); // XXX as toggle?
/* settings */
- column = uiLayoutColumnWithHeading(layout, true, "Playback");
+ column = uiLayoutColumnWithHeading(layout, true, IFACE_("Playback"));
row = uiLayoutRow(column, true);
uiLayoutSetActive(row,
!(RNA_boolean_get(&strip_ptr, "use_animated_influence") ||
@@ -446,7 +446,7 @@ static void nla_panel_actclip(const bContext *C, Panel *panel)
uiItemR(column, &strip_ptr, "action_frame_start", 0, IFACE_("Frame Start"), ICON_NONE);
uiItemR(column, &strip_ptr, "action_frame_end", 0, IFACE_("End"), ICON_NONE);
- row = uiLayoutRowWithHeading(layout, false, "Sync Length");
+ row = uiLayoutRowWithHeading(layout, false, IFACE_("Sync Length"));
uiItemR(row, &strip_ptr, "use_sync_length", 0, "", ICON_NONE);
uiItemO(row, IFACE_("Now"), ICON_FILE_REFRESH, "NLA_OT_action_sync_length");
diff --git a/source/blender/editors/space_nla/space_nla.c b/source/blender/editors/space_nla/space_nla.c
index f060693d9f4..b09536e0621 100644
--- a/source/blender/editors/space_nla/space_nla.c
+++ b/source/blender/editors/space_nla/space_nla.c
@@ -120,7 +120,7 @@ static SpaceLink *nla_new(const ScrArea *area, const Scene *scene)
region->v2d.minzoom = 0.01f;
region->v2d.maxzoom = 50;
region->v2d.scroll = (V2D_SCROLL_BOTTOM | V2D_SCROLL_HORIZONTAL_HANDLES);
- region->v2d.scroll |= (V2D_SCROLL_RIGHT);
+ region->v2d.scroll |= V2D_SCROLL_RIGHT;
region->v2d.keepzoom = V2D_LOCKZOOM_Y;
region->v2d.keepofs = V2D_KEEPOFS_Y;
region->v2d.align = V2D_ALIGN_NO_POS_Y;
@@ -191,7 +191,6 @@ static void nla_channel_region_draw(const bContext *C, ARegion *region)
{
bAnimContext ac;
View2D *v2d = &region->v2d;
- View2DScrollers *scrollers;
/* clear and setup matrix */
UI_ThemeClearColor(TH_BACK);
@@ -211,9 +210,7 @@ static void nla_channel_region_draw(const bContext *C, ARegion *region)
UI_view2d_view_restore(C);
/* scrollers */
- scrollers = UI_view2d_scrollers_calc(v2d, NULL);
- UI_view2d_scrollers_draw(v2d, scrollers);
- UI_view2d_scrollers_free(scrollers);
+ UI_view2d_scrollers_draw(v2d, NULL);
}
/* add handlers, stuff you only do once or on area/region changes */
@@ -237,7 +234,6 @@ static void nla_main_region_draw(const bContext *C, ARegion *region)
Scene *scene = CTX_data_scene(C);
bAnimContext ac;
View2D *v2d = &region->v2d;
- View2DScrollers *scrollers;
short cfra_flag = 0;
/* clear and setup matrix */
@@ -263,13 +259,10 @@ static void nla_main_region_draw(const bContext *C, ARegion *region)
UI_view2d_text_cache_draw(region);
}
- UI_view2d_view_ortho(v2d);
-
/* current frame */
if (snla->flag & SNLA_DRAWTIME) {
cfra_flag |= DRAWCFRA_UNIT_SECONDS;
}
- ANIM_draw_cfra(C, v2d, cfra_flag);
/* markers */
UI_view2d_view_orthoSpecial(region, v2d, 1);
@@ -290,11 +283,20 @@ static void nla_main_region_draw(const bContext *C, ARegion *region)
UI_view2d_view_restore(C);
ED_time_scrub_draw(region, scene, snla->flag & SNLA_DRAWTIME, true);
+}
+
+static void nla_main_region_draw_overlay(const bContext *C, ARegion *region)
+{
+ /* draw entirely, view changes should be handled here */
+ const SpaceNla *snla = CTX_wm_space_nla(C);
+ const Scene *scene = CTX_data_scene(C);
+ View2D *v2d = &region->v2d;
+
+ /* scrubbing region */
+ ED_time_scrub_draw_current_frame(region, scene, snla->flag & SNLA_DRAWTIME, true);
/* scrollers */
- scrollers = UI_view2d_scrollers_calc(v2d, NULL);
- UI_view2d_scrollers_draw(v2d, scrollers);
- UI_view2d_scrollers_free(scrollers);
+ UI_view2d_scrollers_draw(v2d, NULL);
}
/* add handlers, stuff you only do once or on area/region changes */
@@ -620,6 +622,7 @@ void ED_spacetype_nla(void)
art->regionid = RGN_TYPE_WINDOW;
art->init = nla_main_region_init;
art->draw = nla_main_region_draw;
+ art->draw_overlay = nla_main_region_draw_overlay;
art->listener = nla_main_region_listener;
art->message_subscribe = nla_main_region_message_subscribe;
art->keymapflag = ED_KEYMAP_VIEW2D | ED_KEYMAP_ANIMATION | ED_KEYMAP_FRAMES;
diff --git a/source/blender/editors/space_node/CMakeLists.txt b/source/blender/editors/space_node/CMakeLists.txt
index 4e21cdc9d16..f8c30f9a688 100644
--- a/source/blender/editors/space_node/CMakeLists.txt
+++ b/source/blender/editors/space_node/CMakeLists.txt
@@ -75,10 +75,6 @@ if(WITH_OPENIMAGEDENOISE)
add_definitions(-DWITH_OPENIMAGEDENOISE)
endif()
-if (WITH_NEW_SIMULATION_TYPE)
- add_definitions(-DWITH_NEW_SIMULATION_TYPE)
-endif()
-
add_definitions(${GL_DEFINITIONS})
blender_add_lib(bf_editor_space_node "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c
index 01ac3a80871..234ca5d5ce6 100644
--- a/source/blender/editors/space_node/drawnode.c
+++ b/source/blender/editors/space_node/drawnode.c
@@ -849,12 +849,35 @@ static void node_shader_buts_tex_environment_ex(uiLayout *layout, bContext *C, P
static void node_shader_buts_tex_sky(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "sky_type", DEFAULT_FLAGS, "", ICON_NONE);
- uiItemR(layout, ptr, "sun_direction", DEFAULT_FLAGS, "", ICON_NONE);
- uiItemR(layout, ptr, "turbidity", DEFAULT_FLAGS, NULL, ICON_NONE);
- if (RNA_enum_get(ptr, "sky_type") == SHD_SKY_NEW) {
+ if (RNA_enum_get(ptr, "sky_type") == SHD_SKY_PREETHAM) {
+ uiItemR(layout, ptr, "sun_direction", DEFAULT_FLAGS, "", ICON_NONE);
+ uiItemR(layout, ptr, "turbidity", DEFAULT_FLAGS, NULL, ICON_NONE);
+ }
+ if (RNA_enum_get(ptr, "sky_type") == SHD_SKY_HOSEK) {
+ uiItemR(layout, ptr, "sun_direction", DEFAULT_FLAGS, "", ICON_NONE);
+ uiItemR(layout, ptr, "turbidity", DEFAULT_FLAGS, NULL, ICON_NONE);
uiItemR(layout, ptr, "ground_albedo", DEFAULT_FLAGS, NULL, ICON_NONE);
}
+ if (RNA_enum_get(ptr, "sky_type") == SHD_SKY_NISHITA) {
+ uiItemR(layout, ptr, "sun_disc", DEFAULT_FLAGS, NULL, 0);
+
+ if (RNA_boolean_get(ptr, "sun_disc")) {
+ uiItemR(layout, ptr, "sun_size", DEFAULT_FLAGS, NULL, ICON_NONE);
+ }
+
+ uiLayout *col;
+ col = uiLayoutColumn(layout, true);
+ uiItemR(col, ptr, "sun_elevation", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "sun_rotation", DEFAULT_FLAGS, NULL, ICON_NONE);
+
+ uiItemR(layout, ptr, "altitude", DEFAULT_FLAGS, NULL, ICON_NONE);
+
+ col = uiLayoutColumn(layout, true);
+ uiItemR(col, ptr, "air_density", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "dust_density", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "ozone_density", DEFAULT_FLAGS, NULL, ICON_NONE);
+ }
}
static void node_shader_buts_tex_gradient(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -991,7 +1014,14 @@ static void node_shader_buts_vertex_color(uiLayout *layout, bContext *C, Pointer
PointerRNA obptr = CTX_data_pointer_get(C, "active_object");
if (obptr.data && RNA_enum_get(&obptr, "type") == OB_MESH) {
PointerRNA dataptr = RNA_pointer_get(&obptr, "data");
- uiItemPointerR(layout, ptr, "layer_name", &dataptr, "vertex_colors", "", ICON_GROUP_VCOL);
+
+ if (RNA_collection_length(&dataptr, "sculpt_vertex_colors")) {
+ uiItemPointerR(
+ layout, ptr, "layer_name", &dataptr, "sculpt_vertex_colors", "", ICON_GROUP_VCOL);
+ }
+ else {
+ uiItemPointerR(layout, ptr, "layer_name", &dataptr, "vertex_colors", "", ICON_GROUP_VCOL);
+ }
}
else {
uiItemL(layout, "No mesh in active object.", ICON_ERROR);
diff --git a/source/blender/editors/space_node/node_draw.c b/source/blender/editors/space_node/node_draw.c
index bd8950c5085..c3823d8eb27 100644
--- a/source/blender/editors/space_node/node_draw.c
+++ b/source/blender/editors/space_node/node_draw.c
@@ -1323,7 +1323,7 @@ static void node_draw_basis(const bContext *C,
UI_BTYPE_LABEL,
0,
showname,
- (int)(rct->xmin + (NODE_MARGIN_X)),
+ (int)(rct->xmin + NODE_MARGIN_X),
(int)(rct->ymax - NODE_DY),
(short)(iconofs - rct->xmin - 18.0f),
(short)NODE_DY,
@@ -1772,7 +1772,6 @@ static void draw_group_overlay(const bContext *C, ARegion *region)
void drawnodespace(const bContext *C, ARegion *region)
{
wmWindow *win = CTX_wm_window(C);
- View2DScrollers *scrollers;
SpaceNode *snode = CTX_wm_space_node(C);
View2D *v2d = &region->v2d;
@@ -1921,7 +1920,5 @@ void drawnodespace(const bContext *C, ARegion *region)
draw_tree_path(snode);
/* scrollers */
- scrollers = UI_view2d_scrollers_calc(v2d, NULL);
- UI_view2d_scrollers_draw(v2d, scrollers);
- UI_view2d_scrollers_free(scrollers);
+ UI_view2d_scrollers_draw(v2d, NULL);
}
diff --git a/source/blender/editors/space_node/node_edit.c b/source/blender/editors/space_node/node_edit.c
index ac58ec1e636..11d87148713 100644
--- a/source/blender/editors/space_node/node_edit.c
+++ b/source/blender/editors/space_node/node_edit.c
@@ -1428,11 +1428,15 @@ int node_render_changed_exec(bContext *C, wmOperator *UNUSED(op))
Scene *sce = CTX_data_scene(C);
bNode *node;
+ /* This is actually a test whether scene is used by the compositor or not.
+ * All the nodes are using same render result, so there is no need to do
+ * anything smart about check how exactly scene is used. */
for (node = sce->nodetree->nodes.first; node; node = node->next) {
- if (node->id == (ID *)sce && node->need_exec) {
+ if (node->id == (ID *)sce) {
break;
}
}
+
if (node) {
ViewLayer *view_layer = BLI_findlink(&sce->view_layers, node->custom1);
diff --git a/source/blender/editors/space_node/space_node.c b/source/blender/editors/space_node/space_node.c
index f339d11b842..562aa6b078c 100644
--- a/source/blender/editors/space_node/space_node.c
+++ b/source/blender/editors/space_node/space_node.c
@@ -936,11 +936,10 @@ static void node_space_subtype_item_extend(bContext *C, EnumPropertyItem **item,
bool free;
const EnumPropertyItem *item_src = RNA_enum_node_tree_types_itemf_impl(C, &free);
for (const EnumPropertyItem *item_iter = item_src; item_iter->identifier; item_iter++) {
-#ifndef WITH_NEW_SIMULATION_TYPE
- if (STREQ(item_iter->identifier, "SimulationNodeTree")) {
+ if (!U.experimental.use_new_particle_system &&
+ STREQ(item_iter->identifier, "SimulationNodeTree")) {
continue;
}
-#endif
RNA_enum_item_add(item, totitem, item_iter);
}
if (free) {
diff --git a/source/blender/editors/space_outliner/outliner_collections.c b/source/blender/editors/space_outliner/outliner_collections.c
index 82ff9e06194..131491fcc40 100644
--- a/source/blender/editors/space_outliner/outliner_collections.c
+++ b/source/blender/editors/space_outliner/outliner_collections.c
@@ -581,7 +581,8 @@ static int collection_duplicate_exec(bContext *C, wmOperator *op)
"it won't be linked to any view layer");
}
- BKE_collection_duplicate(bmain, parent, collection, true, true, !linked);
+ const eDupli_ID_Flags dupli_flags = USER_DUP_OBJECT | (linked ? 0 : U.dupflag);
+ BKE_collection_duplicate(bmain, parent, collection, dupli_flags, 0);
DEG_relations_tag_update(bmain);
WM_main_add_notifier(NC_SCENE | ND_LAYER, CTX_data_scene(C));
diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.c b/source/blender/editors/space_outliner/outliner_dragdrop.c
index c2c9f3a5bfb..70a628eead0 100644
--- a/source/blender/editors/space_outliner/outliner_dragdrop.c
+++ b/source/blender/editors/space_outliner/outliner_dragdrop.c
@@ -907,6 +907,9 @@ static int outliner_item_drag_drop_invoke(bContext *C,
return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH);
}
+ /* Scroll view when dragging near edges */
+ WM_operator_name_call(C, "VIEW2D_OT_edge_pan", WM_OP_INVOKE_DEFAULT, NULL);
+
wmDrag *drag = WM_event_start_drag(C, data.icon, WM_DRAG_ID, NULL, 0.0, WM_DRAG_NOP);
if (ELEM(GS(data.drag_id->name), ID_OB, ID_GR)) {
diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c
index 9a9bd610322..5eee97e3fee 100644
--- a/source/blender/editors/space_outliner/outliner_draw.c
+++ b/source/blender/editors/space_outliner/outliner_draw.c
@@ -709,9 +709,9 @@ static void namebutton_cb(bContext *C, void *tsep, char *oldname)
Library *lib = (Library *)tselem->id;
char expanded[FILE_MAX];
- BKE_library_filepath_set(bmain, lib, lib->name);
+ BKE_library_filepath_set(bmain, lib, lib->filepath);
- BLI_strncpy(expanded, lib->name, sizeof(expanded));
+ BLI_strncpy(expanded, lib->filepath, sizeof(expanded));
BLI_path_abs(expanded, BKE_main_blendfile_path(bmain));
if (!BLI_exists(expanded)) {
BKE_reportf(CTX_wm_reports(C),
@@ -1657,7 +1657,6 @@ static void outliner_draw_userbuts(uiBlock *block,
uiBut *bt;
ID *id = tselem->id;
const char *tip = NULL;
- int icon = ICON_NONE;
char buf[16] = "";
int but_flag = UI_BUT_DRAG_LOCK;
@@ -1683,18 +1682,16 @@ static void outliner_draw_userbuts(uiBlock *block,
UI_but_flag_enable(bt, but_flag);
if (id->flag & LIB_FAKEUSER) {
- icon = ICON_FILE_TICK;
tip = TIP_("Data-block will be retained using a fake user");
}
else {
- icon = ICON_X;
tip = TIP_("Data-block has no users and will be deleted");
}
bt = uiDefIconButBitS(block,
UI_BTYPE_ICON_TOGGLE,
LIB_FAKEUSER,
1,
- icon,
+ ICON_FAKE_USER_OFF,
(int)(region->v2d.cur.xmax - OL_TOG_USER_BUTS_STATUS),
te->ys,
UI_UNIT_X,
@@ -1707,25 +1704,6 @@ static void outliner_draw_userbuts(uiBlock *block,
tip);
UI_but_func_set(bt, restrictbutton_id_user_toggle, id, NULL);
UI_but_flag_enable(bt, but_flag);
-
- bt = uiDefButBitS(block,
- UI_BTYPE_ICON_TOGGLE,
- LIB_FAKEUSER,
- 1,
- (id->flag & LIB_FAKEUSER) ? "F" : " ",
- (int)(region->v2d.cur.xmax - OL_TOG_USER_BUTS_FAKEUSER),
- te->ys,
- UI_UNIT_X,
- UI_UNIT_Y,
- &id->flag,
- 0,
- 0,
- 0,
- 0,
- TIP_("Data-block has a 'fake' user which will keep it in the file "
- "even if nothing else uses it"));
- UI_but_func_set(bt, restrictbutton_id_user_toggle, id, NULL);
- UI_but_flag_enable(bt, but_flag);
}
}
@@ -1864,7 +1842,7 @@ static void outliner_buttons(const bContext *C,
len = sizeof(((ModifierData *)0)->name);
}
else if (tselem->id && GS(tselem->id->name) == ID_LI) {
- len = sizeof(((Library *)0)->name);
+ len = sizeof(((Library *)0)->filepath);
}
else {
len = MAX_ID_NAME - 2;
@@ -2288,6 +2266,7 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te)
data.icon = ICON_MATERIAL_DATA;
break;
case TSE_POSEGRP_BASE:
+ case TSE_POSEGRP:
data.icon = ICON_GROUP_BONE;
break;
case TSE_SEQUENCE:
@@ -2702,7 +2681,7 @@ static void tselem_draw_icon(uiBlock *block,
0.0,
1.0,
alpha,
- (data.drag_id && ID_IS_LINKED(data.drag_id)) ? data.drag_id->lib->name : "");
+ (data.drag_id && ID_IS_LINKED(data.drag_id)) ? data.drag_id->lib->filepath : "");
}
}
@@ -3129,18 +3108,13 @@ static void outliner_draw_tree_element(bContext *C,
offsx += 2 * ufac;
}
- if (ELEM(tselem->type, 0, TSE_LAYER_COLLECTION) && ID_IS_LINKED(tselem->id)) {
- if (tselem->id->tag & LIB_TAG_MISSING) {
- UI_icon_draw_alpha((float)startx + offsx + 2 * ufac,
- (float)*starty + 2 * ufac,
- ICON_LIBRARY_DATA_BROKEN,
- alpha_fac);
- }
- else if (tselem->id->tag & LIB_TAG_INDIRECT) {
- UI_icon_draw_alpha((float)startx + offsx + 2 * ufac,
- (float)*starty + 2 * ufac,
- ICON_LIBRARY_DATA_INDIRECT,
- alpha_fac);
+ if (ELEM(tselem->type, 0, TSE_LAYER_COLLECTION) ||
+ ((tselem->type == TSE_RNA_STRUCT) && RNA_struct_is_ID(te->rnaptr.type))) {
+ const BIFIconID lib_icon = UI_library_icon_get(tselem->id);
+ if (lib_icon != ICON_NONE) {
+ UI_icon_draw_alpha(
+ (float)startx + offsx + 2 * ufac, (float)*starty + 2 * ufac, lib_icon, alpha_fac);
+ offsx += UI_UNIT_X + 4 * ufac;
}
else {
UI_icon_draw_alpha((float)startx + offsx + 2 * ufac,
@@ -3171,13 +3145,6 @@ static void outliner_draw_tree_element(bContext *C,
}
offsx += UI_UNIT_X + 4 * ufac;
}
- else if (ELEM(tselem->type, 0, TSE_LAYER_COLLECTION) && ID_IS_OVERRIDE_LIBRARY(tselem->id)) {
- UI_icon_draw_alpha((float)startx + offsx + 2 * ufac,
- (float)*starty + 2 * ufac,
- ICON_LIBRARY_DATA_OVERRIDE,
- alpha_fac);
- offsx += UI_UNIT_X + 4 * ufac;
- }
GPU_blend(false);
/* name */
diff --git a/source/blender/editors/space_outliner/outliner_edit.c b/source/blender/editors/space_outliner/outliner_edit.c
index 3db75d9288b..bee8b28e658 100644
--- a/source/blender/editors/space_outliner/outliner_edit.c
+++ b/source/blender/editors/space_outliner/outliner_edit.c
@@ -191,7 +191,14 @@ static int outliner_item_openclose_modal(bContext *C, wmOperator *op, const wmEv
/* Only toggle openclose on the same level as the first clicked element */
if (te->xs == data->x_location) {
outliner_item_openclose(te, data->open, false);
- ED_region_tag_redraw(region);
+
+ /* Avoid rebuild if possible. */
+ if (outliner_element_needs_rebuild_on_open_change(TREESTORE(te))) {
+ ED_region_tag_redraw(region);
+ }
+ else {
+ ED_region_tag_redraw_no_rebuild(region);
+ }
}
}
@@ -231,7 +238,13 @@ static int outliner_item_openclose_invoke(bContext *C, wmOperator *op, const wmE
(toggle_all && (outliner_flag_is_any_test(&te->subtree, TSE_CLOSED, 1)));
outliner_item_openclose(te, open, toggle_all);
- ED_region_tag_redraw(region);
+ /* Avoid rebuild if possible. */
+ if (outliner_element_needs_rebuild_on_open_change(TREESTORE(te))) {
+ ED_region_tag_redraw(region);
+ }
+ else {
+ ED_region_tag_redraw_no_rebuild(region);
+ }
/* Only toggle once for single click toggling */
if (event->type == LEFTMOUSE) {
@@ -532,7 +545,7 @@ static int outliner_id_delete_invoke_do(bContext *C,
BKE_reportf(reports,
RPT_ERROR_INVALID_INPUT,
"Cannot delete indirectly linked library '%s'",
- ((Library *)tselem->id)->filepath);
+ ((Library *)tselem->id)->filepath_abs);
return OPERATOR_CANCELLED;
}
id_delete(C, reports, te, tselem);
@@ -898,12 +911,13 @@ static int lib_relocate(
Library *lib = (Library *)tselem->id;
char dir[FILE_MAXDIR], filename[FILE_MAX];
- BLI_split_dirfile(lib->filepath, dir, filename, sizeof(dir), sizeof(filename));
+ BLI_split_dirfile(lib->filepath_abs, dir, filename, sizeof(dir), sizeof(filename));
- printf("%s, %s\n", tselem->id->name, lib->filepath);
+ printf("%s, %s\n", tselem->id->name, lib->filepath_abs);
- /* We assume if both paths in lib are not the same then lib->name was relative... */
- RNA_boolean_set(&op_props, "relative_path", BLI_path_cmp(lib->filepath, lib->name) != 0);
+ /* We assume if both paths in lib are not the same then `lib->filepath` was relative. */
+ RNA_boolean_set(
+ &op_props, "relative_path", BLI_path_cmp(lib->filepath_abs, lib->filepath) != 0);
RNA_string_set(&op_props, "directory", dir);
RNA_string_set(&op_props, "filename", filename);
@@ -930,7 +944,7 @@ static int outliner_lib_relocate_invoke_do(
BKE_reportf(reports,
RPT_ERROR_INVALID_INPUT,
"Cannot relocate indirectly linked library '%s'",
- ((Library *)tselem->id)->filepath);
+ ((Library *)tselem->id)->filepath_abs);
return OPERATOR_CANCELLED;
}
else {
diff --git a/source/blender/editors/space_outliner/outliner_intern.h b/source/blender/editors/space_outliner/outliner_intern.h
index f2b64bc2a4b..b590eb75407 100644
--- a/source/blender/editors/space_outliner/outliner_intern.h
+++ b/source/blender/editors/space_outliner/outliner_intern.h
@@ -163,8 +163,7 @@ typedef enum {
#define OL_Y_OFFSET 2
#define OL_TOG_USER_BUTS_USERS (UI_UNIT_X * 2.0f + V2D_SCROLL_WIDTH)
-#define OL_TOG_USER_BUTS_STATUS (UI_UNIT_X * 3.0f + V2D_SCROLL_WIDTH)
-#define OL_TOG_USER_BUTS_FAKEUSER (UI_UNIT_X + V2D_SCROLL_WIDTH)
+#define OL_TOG_USER_BUTS_STATUS (UI_UNIT_X + V2D_SCROLL_WIDTH)
#define OL_RNA_COLX (UI_UNIT_X * 15)
#define OL_RNA_COL_SIZEX (UI_UNIT_X * 7.5f)
@@ -216,6 +215,16 @@ typedef struct TreeViewContext {
Object *ob_pose;
} TreeViewContext;
+typedef enum TreeItemSelectAction {
+ OL_ITEM_DESELECT = 0, /* Deselect the item */
+ OL_ITEM_SELECT = (1 << 0), /* Select the item */
+ OL_ITEM_SELECT_DATA = (1 << 1), /* Select object data */
+ OL_ITEM_ACTIVATE = (1 << 2), /* Activate the item */
+ OL_ITEM_EXTEND = (1 << 3), /* Extend the current selection */
+ OL_ITEM_RECURSIVE = (1 << 4), /* Select recursively */
+ OL_ITEM_TOGGLE_MODE = (1 << 5) /* Temporary */
+} TreeItemSelectAction;
+
/* outliner_tree.c ----------------------------------------------- */
void outliner_free_tree(ListBase *tree);
@@ -228,6 +237,8 @@ void outliner_build_tree(struct Main *mainvar,
struct SpaceOutliner *soops,
struct ARegion *region);
+bool outliner_element_needs_rebuild_on_open_change(const TreeStoreElem *tselem);
+
typedef struct IDsSelectedData {
struct ListBase selected_array;
} IDsSelectedData;
@@ -266,21 +277,16 @@ eOLDrawState tree_element_active(struct bContext *C,
const eOLSetState set,
const bool handle_all_types);
-void outliner_item_do_activate_from_tree_element(
- struct bContext *C, TreeElement *te, TreeStoreElem *tselem, bool extend, bool recursive);
-
-void outliner_item_select(struct SpaceOutliner *soops,
- const struct TreeElement *te,
- const bool extend,
- const bool toggle);
+void outliner_item_select(struct bContext *C,
+ struct SpaceOutliner *soops,
+ struct TreeElement *te,
+ const short select_flag);
void outliner_object_mode_toggle(struct bContext *C,
Scene *scene,
ViewLayer *view_layer,
Base *base);
-void outliner_element_activate(struct SpaceOutliner *soops, struct TreeStoreElem *tselem);
-
bool outliner_item_is_co_over_name_icons(const TreeElement *te, float view_co_x);
bool outliner_item_is_co_within_close_toggle(const TreeElement *te, float view_co_x);
@@ -495,6 +501,7 @@ TreeElement *outliner_find_parent_element(ListBase *lb,
TreeElement *outliner_find_id(struct SpaceOutliner *soops, ListBase *lb, const struct ID *id);
TreeElement *outliner_find_posechannel(ListBase *lb, const struct bPoseChannel *pchan);
TreeElement *outliner_find_editbone(ListBase *lb, const struct EditBone *ebone);
+TreeElement *outliner_search_back_te(TreeElement *te, short idcode);
struct ID *outliner_search_back(TreeElement *te, short idcode);
bool outliner_tree_traverse(const SpaceOutliner *soops,
ListBase *tree,
diff --git a/source/blender/editors/space_outliner/outliner_select.c b/source/blender/editors/space_outliner/outliner_select.c
index fa8422573ab..8a408a41897 100644
--- a/source/blender/editors/space_outliner/outliner_select.c
+++ b/source/blender/editors/space_outliner/outliner_select.c
@@ -111,7 +111,7 @@ static bool do_outliner_activate_common(bContext *C,
* If extend is used, we try to have the other compatible selected objects in the new mode as well.
* Otherwise only the new object will be active, selected and in the edit mode.
*/
-static void do_outliner_activate_obdata(
+static void do_outliner_item_editmode_toggle(
bContext *C, Scene *scene, ViewLayer *view_layer, Base *base, const bool extend)
{
Main *bmain = CTX_data_main(C);
@@ -159,7 +159,7 @@ static void do_outliner_activate_obdata(
}
}
-static void do_outliner_activate_pose(
+static void do_outliner_item_posemode_toggle(
bContext *C, Scene *scene, ViewLayer *view_layer, Base *base, const bool extend)
{
Main *bmain = CTX_data_main(C);
@@ -214,10 +214,42 @@ void outliner_object_mode_toggle(bContext *C, Scene *scene, ViewLayer *view_laye
{
Object *obact = OBACT(view_layer);
if (obact->mode & OB_MODE_EDIT) {
- do_outliner_activate_obdata(C, scene, view_layer, base, true);
+ do_outliner_item_editmode_toggle(C, scene, view_layer, base, true);
}
else if (obact->mode & OB_MODE_POSE) {
- do_outliner_activate_pose(C, scene, view_layer, base, true);
+ do_outliner_item_posemode_toggle(C, scene, view_layer, base, true);
+ }
+}
+
+/* Toggle the item's interaction mode if supported */
+static void outliner_item_mode_toggle(bContext *C,
+ TreeViewContext *tvc,
+ TreeElement *te,
+ const bool extend)
+{
+ TreeStoreElem *tselem = TREESTORE(te);
+
+ if (tselem->type == 0) {
+ if (OB_DATA_SUPPORT_EDITMODE(te->idcode)) {
+ Object *ob = (Object *)outliner_search_back(te, ID_OB);
+ if ((ob != NULL) && (ob->data == tselem->id)) {
+ Base *base = BKE_view_layer_base_find(tvc->view_layer, ob);
+ if ((base != NULL) && (base->flag & BASE_VISIBLE_DEPSGRAPH)) {
+ do_outliner_item_editmode_toggle(C, tvc->scene, tvc->view_layer, base, extend);
+ }
+ }
+ }
+ else if (ELEM(te->idcode, ID_GD)) {
+ /* set grease pencil to object mode */
+ WM_operator_name_call(C, "GPENCIL_OT_editmode_toggle", WM_OP_INVOKE_REGION_WIN, NULL);
+ }
+ }
+ else if (tselem->type == TSE_POSE_BASE) {
+ Object *ob = (Object *)tselem->id;
+ Base *base = BKE_view_layer_base_find(tvc->view_layer, ob);
+ if (base != NULL) {
+ do_outliner_item_posemode_toggle(C, tvc->scene, tvc->view_layer, base, extend);
+ }
}
}
@@ -302,28 +334,32 @@ static void do_outliner_ebone_select_recursive(bArmature *arm, EditBone *ebone_p
static eOLDrawState tree_element_set_active_object(bContext *C,
Scene *scene,
ViewLayer *view_layer,
- SpaceOutliner *soops,
+ SpaceOutliner *UNUSED(soops),
TreeElement *te,
const eOLSetState set,
bool recursive)
{
TreeStoreElem *tselem = TREESTORE(te);
TreeStoreElem *parent_tselem = NULL;
+ TreeElement *parent_te = NULL;
Scene *sce;
Base *base;
Object *ob = NULL;
- TreeElement *te_ob = NULL;
/* if id is not object, we search back */
- if (te->idcode == ID_OB) {
+ if (tselem->type == 0 && te->idcode == ID_OB) {
ob = (Object *)tselem->id;
}
else {
- ob = (Object *)outliner_search_back(te, ID_OB);
-
- /* Don't return when activating children of the previous active object. */
- if (ob == OBACT(view_layer) && set == OL_SETSEL_NONE) {
- return OL_DRAWSEL_NONE;
+ parent_te = outliner_search_back_te(te, ID_OB);
+ if (parent_te) {
+ parent_tselem = TREESTORE(parent_te);
+ ob = (Object *)parent_tselem->id;
+
+ /* Don't return when activating children of the previous active object. */
+ if (ob == OBACT(view_layer) && set == OL_SETSEL_NONE) {
+ return OL_DRAWSEL_NONE;
+ }
}
}
if (ob == NULL) {
@@ -356,11 +392,6 @@ static eOLDrawState tree_element_set_active_object(bContext *C,
}
}
- te_ob = outliner_find_id(soops, &soops->tree, (ID *)ob);
- if (te_ob != NULL && te_ob != te) {
- parent_tselem = TREESTORE(te_ob);
- }
-
if (base) {
if (set == OL_SETSEL_EXTEND) {
/* swap select */
@@ -863,8 +894,8 @@ static eOLDrawState tree_element_active_text(bContext *UNUSED(C),
return OL_DRAWSEL_NONE;
}
-static eOLDrawState tree_element_active_pose(bContext *C,
- Scene *scene,
+static eOLDrawState tree_element_active_pose(bContext *UNUSED(C),
+ Scene *UNUSED(scene),
ViewLayer *view_layer,
TreeElement *UNUSED(te),
TreeStoreElem *tselem,
@@ -879,7 +910,6 @@ static eOLDrawState tree_element_active_pose(bContext *C,
}
if (set != OL_SETSEL_NONE) {
- do_outliner_activate_pose(C, scene, view_layer, base, (set == OL_SETSEL_EXTEND));
}
else {
if (ob->mode & OB_MODE_POSE) {
@@ -1113,13 +1143,6 @@ eOLDrawState tree_element_type_active(bContext *C,
/* ================================================ */
-/* Activate a tree store element and set the walk navigation start element */
-void outliner_element_activate(SpaceOutliner *soops, TreeStoreElem *tselem)
-{
- outliner_flag_set(&soops->tree, TSE_ACTIVE | TSE_ACTIVE_WALK, false);
- tselem->flag |= TSE_ACTIVE | TSE_ACTIVE_WALK;
-}
-
/**
* Action when clicking to activate an item (typically under the mouse cursor),
* but don't do any cursor intersection checks.
@@ -1133,9 +1156,8 @@ static void do_outliner_item_activate_tree_element(bContext *C,
TreeStoreElem *tselem,
const bool extend,
const bool recursive,
- const bool is_over_name_icons)
+ const bool do_activate_data)
{
- bool do_activate_data = soops->flag & SO_SYNC_SELECT || is_over_name_icons;
/* Always makes active object, except for some specific types. */
if (ELEM(tselem->type,
TSE_SEQUENCE,
@@ -1153,7 +1175,6 @@ static void do_outliner_item_activate_tree_element(bContext *C,
/* Support pose mode toggle, keeping the active object as is. */
}
else if (do_activate_data) {
- /* Only activate when synced selection is enabled */
tree_element_set_active_object(C,
tvc->scene,
tvc->view_layer,
@@ -1164,9 +1185,6 @@ static void do_outliner_item_activate_tree_element(bContext *C,
recursive && tselem->type == 0);
}
- /* Mark as active in the outliner */
- outliner_element_activate(soops, tselem);
-
if (tselem->type == 0) { // the lib blocks
if (do_activate_data == false) {
/* Only select in outliner. */
@@ -1216,19 +1234,6 @@ static void do_outliner_item_activate_tree_element(bContext *C,
DEG_id_tag_update(&tvc->scene->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, tvc->scene);
}
- else if (OB_DATA_SUPPORT_EDITMODE(te->idcode)) {
- Object *ob = (Object *)outliner_search_back(te, ID_OB);
- if ((ob != NULL) && (ob->data == tselem->id)) {
- Base *base = BKE_view_layer_base_find(tvc->view_layer, ob);
- if ((base != NULL) && (base->flag & BASE_VISIBLE_DEPSGRAPH)) {
- do_outliner_activate_obdata(C, tvc->scene, tvc->view_layer, base, extend);
- }
- }
- }
- else if (ELEM(te->idcode, ID_GD)) {
- /* set grease pencil to object mode */
- WM_operator_name_call(C, "GPENCIL_OT_editmode_toggle", WM_OP_INVOKE_REGION_WIN, NULL);
- }
else { // rest of types
tree_element_active(C, tvc, soops, te, OL_SETSEL_NORMAL, false);
}
@@ -1239,23 +1244,49 @@ static void do_outliner_item_activate_tree_element(bContext *C,
}
}
-/**
- * \param extend: Don't deselect other items, only modify \a te.
- * \param toggle: Select \a te when not selected, deselect when selected.
- */
-void outliner_item_select(SpaceOutliner *soops,
- const TreeElement *te,
- const bool extend,
- const bool toggle)
+/* Select the item using the set flags */
+void outliner_item_select(bContext *C,
+ SpaceOutliner *soops,
+ TreeElement *te,
+ const short select_flag)
{
TreeStoreElem *tselem = TREESTORE(te);
- const short new_flag = (toggle && (tselem->flag & TSE_ACTIVE)) ? (tselem->flag ^ TSE_SELECTED) :
- (tselem->flag | TSE_SELECTED);
+ const bool activate = select_flag & OL_ITEM_ACTIVATE;
+ const bool extend = select_flag & OL_ITEM_EXTEND;
+ const bool activate_data = select_flag & OL_ITEM_SELECT_DATA;
- if (extend == false) {
- outliner_flag_set(&soops->tree, TSE_SELECTED, false);
+ /* Clear previous active when activating and clear selection when not extending selection */
+ const short clear_flag = (activate ? TSE_ACTIVE : 0) | (extend ? 0 : TSE_SELECTED);
+ if (clear_flag) {
+ outliner_flag_set(&soops->tree, clear_flag, false);
+ }
+
+ if (select_flag & OL_ITEM_SELECT) {
+ tselem->flag |= TSE_SELECTED;
+ }
+ else {
+ tselem->flag &= ~TSE_SELECTED;
+ }
+
+ if (activate) {
+ TreeViewContext tvc;
+ outliner_viewcontext_init(C, &tvc);
+
+ tselem->flag |= TSE_ACTIVE;
+ do_outliner_item_activate_tree_element(C,
+ &tvc,
+ soops,
+ te,
+ tselem,
+ extend,
+ select_flag & OL_ITEM_RECURSIVE,
+ activate_data || soops->flag & SO_SYNC_SELECT);
+
+ /* Mode toggle on data activate for now, but move later */
+ if (select_flag & OL_ITEM_TOGGLE_MODE) {
+ outliner_item_mode_toggle(C, &tvc, te, extend);
+ }
}
- tselem->flag = new_flag;
}
static bool do_outliner_range_select_recursive(ListBase *lb,
@@ -1298,8 +1329,7 @@ static void do_outliner_range_select(bContext *C,
/* If no active element exists, activate the element under the cursor */
if (!active) {
- outliner_item_select(soops, cursor, false, false);
- outliner_item_do_activate_from_tree_element(C, cursor, TREESTORE(cursor), false, false);
+ outliner_item_select(C, soops, cursor, OL_ITEM_SELECT | OL_ITEM_ACTIVATE);
return;
}
@@ -1312,14 +1342,13 @@ static void do_outliner_range_select(bContext *C,
/* Select active if under cursor */
if (active == cursor) {
- TREESTORE(cursor)->flag |= TSE_SELECTED;
+ outliner_item_select(C, soops, cursor, OL_ITEM_SELECT);
return;
}
- /* If active is not selected, select the element under the cursor */
+ /* If active is not selected or visible, select and activate the element under the cursor */
if (!active_selected || !outliner_is_element_visible(active)) {
- outliner_item_select(soops, cursor, false, false);
- outliner_item_do_activate_from_tree_element(C, cursor, TREESTORE(cursor), false, false);
+ outliner_item_select(C, soops, cursor, OL_ITEM_SELECT | OL_ITEM_ACTIVATE);
return;
}
@@ -1334,23 +1363,6 @@ static bool outliner_is_co_within_restrict_columns(const SpaceOutliner *soops,
}
/**
- * A version of #outliner_item_do_activate_from_cursor that takes the tree element directly.
- * and doesn't depend on the pointer position.
- *
- * This allows us to simulate clicking on an item without dealing with the mouse cursor.
- */
-void outliner_item_do_activate_from_tree_element(
- bContext *C, TreeElement *te, TreeStoreElem *tselem, bool extend, bool recursive)
-{
- SpaceOutliner *soops = CTX_wm_space_outliner(C);
-
- TreeViewContext tvc;
- outliner_viewcontext_init(C, &tvc);
-
- do_outliner_item_activate_tree_element(C, &tvc, soops, te, tselem, extend, recursive, false);
-}
-
-/**
* Action to run when clicking in the outliner,
*
* May expend/collapse branches or activate items.
@@ -1402,14 +1414,17 @@ static int outliner_item_do_activate_from_cursor(bContext *C,
do_outliner_range_select(C, soops, activate_te, extend);
}
else {
- TreeViewContext tvc;
- outliner_viewcontext_init(C, &tvc);
-
const bool is_over_name_icons = outliner_item_is_co_over_name_icons(activate_te,
view_mval[0]);
- outliner_item_select(soops, activate_te, extend, extend);
- do_outliner_item_activate_tree_element(
- C, &tvc, soops, activate_te, activate_tselem, extend, false, is_over_name_icons);
+ /* Always select unless already active and selected */
+ const bool select = !extend || !(activate_tselem->flag & TSE_ACTIVE &&
+ activate_tselem->flag & TSE_SELECTED);
+
+ const short select_flag = OL_ITEM_ACTIVATE | (select ? OL_ITEM_SELECT : OL_ITEM_DESELECT) |
+ (is_over_name_icons ? OL_ITEM_SELECT_DATA : 0) |
+ (extend ? OL_ITEM_EXTEND : 0) | OL_ITEM_TOGGLE_MODE;
+
+ outliner_item_select(C, soops, activate_te, select_flag);
}
changed = true;
@@ -1468,23 +1483,19 @@ void OUTLINER_OT_item_activate(wmOperatorType *ot)
/* **************** Box Select Tool ****************** */
static void outliner_item_box_select(
- SpaceOutliner *soops, Scene *scene, rctf *rectf, TreeElement *te, bool select)
+ bContext *C, SpaceOutliner *soops, Scene *scene, rctf *rectf, TreeElement *te, bool select)
{
TreeStoreElem *tselem = TREESTORE(te);
if (te->ys <= rectf->ymax && te->ys + UI_UNIT_Y >= rectf->ymin) {
- if (select) {
- tselem->flag |= TSE_SELECTED;
- }
- else {
- tselem->flag &= ~TSE_SELECTED;
- }
+ outliner_item_select(
+ C, soops, te, (select ? OL_ITEM_SELECT : OL_ITEM_DESELECT) | OL_ITEM_EXTEND);
}
/* Look at its children. */
if (TSELEM_OPEN(tselem, soops)) {
for (te = te->subtree.first; te; te = te->next) {
- outliner_item_box_select(soops, scene, rectf, te, select);
+ outliner_item_box_select(C, soops, scene, rectf, te, select);
}
}
}
@@ -1506,7 +1517,7 @@ static int outliner_box_select_exec(bContext *C, wmOperator *op)
UI_view2d_region_to_view_rctf(&region->v2d, &rectf, &rectf);
LISTBASE_FOREACH (TreeElement *, te, &soops->tree) {
- outliner_item_box_select(soops, scene, &rectf, te, select);
+ outliner_item_box_select(C, soops, scene, &rectf, te, select);
}
DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
@@ -1587,17 +1598,17 @@ static TreeElement *outliner_find_rightmost_visible_child(SpaceOutliner *soops,
}
/* Find previous visible element in the tree */
-static TreeElement *outliner_find_previous_element(SpaceOutliner *soops, TreeElement *walk_element)
+static TreeElement *outliner_find_previous_element(SpaceOutliner *soops, TreeElement *te)
{
- if (walk_element->prev) {
- walk_element = outliner_find_rightmost_visible_child(soops, walk_element->prev);
+ if (te->prev) {
+ te = outliner_find_rightmost_visible_child(soops, te->prev);
}
- else if (walk_element->parent) {
+ else if (te->parent) {
/* Use parent if at beginning of list */
- walk_element = walk_element->parent;
+ te = te->parent;
}
- return walk_element;
+ return te;
}
/* Recursively search up the tree until a successor to a given element is found */
@@ -1618,98 +1629,78 @@ static TreeElement *outliner_element_find_successor_in_parents(TreeElement *te)
}
/* Find next visible element in the tree */
-static TreeElement *outliner_find_next_element(SpaceOutliner *soops, TreeElement *walk_element)
+static TreeElement *outliner_find_next_element(SpaceOutliner *soops, TreeElement *te)
{
- TreeStoreElem *tselem = TREESTORE(walk_element);
+ TreeStoreElem *tselem = TREESTORE(te);
- if (TSELEM_OPEN(tselem, soops) && walk_element->subtree.first) {
- walk_element = walk_element->subtree.first;
+ if (TSELEM_OPEN(tselem, soops) && te->subtree.first) {
+ te = te->subtree.first;
}
- else if (walk_element->next) {
- walk_element = walk_element->next;
+ else if (te->next) {
+ te = te->next;
}
else {
- walk_element = outliner_element_find_successor_in_parents(walk_element);
+ te = outliner_element_find_successor_in_parents(te);
}
- return walk_element;
+ return te;
}
static TreeElement *do_outliner_select_walk(SpaceOutliner *soops,
- TreeElement *walk_element,
+ TreeElement *te,
const int direction,
const bool extend,
const bool toggle_all)
{
- TreeStoreElem *tselem = TREESTORE(walk_element);
-
- if (!extend) {
- outliner_flag_set(&soops->tree, TSE_SELECTED, false);
- }
- tselem->flag &= ~TSE_ACTIVE_WALK;
+ TreeStoreElem *tselem = TREESTORE(te);
switch (direction) {
case UI_SELECT_WALK_UP:
- walk_element = outliner_find_previous_element(soops, walk_element);
+ te = outliner_find_previous_element(soops, te);
break;
case UI_SELECT_WALK_DOWN:
- walk_element = outliner_find_next_element(soops, walk_element);
+ te = outliner_find_next_element(soops, te);
break;
case UI_SELECT_WALK_LEFT:
- outliner_item_openclose(walk_element, false, toggle_all);
+ outliner_item_openclose(te, false, toggle_all);
break;
case UI_SELECT_WALK_RIGHT:
- outliner_item_openclose(walk_element, true, toggle_all);
+ outliner_item_openclose(te, true, toggle_all);
break;
}
- TreeStoreElem *tselem_new = TREESTORE(walk_element);
-
/* If new element is already selected, deselect the previous element */
+ TreeStoreElem *tselem_new = TREESTORE(te);
if (extend) {
tselem->flag = (tselem_new->flag & TSE_SELECTED) ? (tselem->flag & ~TSE_SELECTED) :
(tselem->flag | TSE_SELECTED);
}
- tselem_new->flag |= TSE_SELECTED | TSE_ACTIVE_WALK;
-
- return walk_element;
+ return te;
}
-/* Find walk select element, or set it if it does not exist.
- * Changed is set to true if walk element is found, false if it was set */
+/* Find the active element to walk from, or set one if none exists.
+ * Changed is set to true if the active element is found, or false if it was set */
static TreeElement *find_walk_select_start_element(SpaceOutliner *soops, bool *changed)
{
- TreeElement *walk_element = outliner_find_element_with_flag(&soops->tree, TSE_ACTIVE_WALK);
-
+ TreeElement *active_te = outliner_find_element_with_flag(&soops->tree, TSE_ACTIVE);
*changed = false;
- /* If no walk element exists, start from active */
- if (!walk_element) {
- TreeElement *active_element = outliner_find_element_with_flag(&soops->tree, TSE_ACTIVE);
-
- /* If no active element exists, use the first element in the tree */
- if (!active_element) {
- walk_element = soops->tree.first;
- }
- else {
- walk_element = active_element;
- }
-
+ /* If no active element exists, use the first element in the tree */
+ if (!active_te) {
+ active_te = soops->tree.first;
*changed = true;
}
- /* If walk element is not visible, set that element's first visible parent as walk element */
- if (!outliner_is_element_visible(walk_element)) {
- TREESTORE(walk_element)->flag &= ~TSE_ACTIVE_WALK;
-
- while (!outliner_is_element_visible(walk_element)) {
- walk_element = walk_element->parent;
+ /* If the active element is not visible, activate the first visible parent element */
+ if (!outliner_is_element_visible(active_te)) {
+ while (!outliner_is_element_visible(active_te)) {
+ active_te = active_te->parent;
}
*changed = true;
}
- return walk_element;
+ return active_te;
}
/* Scroll the outliner when the walk element reaches the top or bottom boundary */
@@ -1738,18 +1729,18 @@ static int outliner_walk_select_invoke(bContext *C, wmOperator *op, const wmEven
const bool toggle_all = RNA_boolean_get(op->ptr, "toggle_all");
bool changed;
- TreeElement *walk_element = find_walk_select_start_element(soops, &changed);
+ TreeElement *active_te = find_walk_select_start_element(soops, &changed);
- /* If finding the starting walk select element did not move the element, proceed to walk */
+ /* If finding the active element did not modify the selection, proceed to walk */
if (!changed) {
- walk_element = do_outliner_select_walk(soops, walk_element, direction, extend, toggle_all);
- }
- else {
- TREESTORE(walk_element)->flag |= TSE_SELECTED | TSE_ACTIVE_WALK;
+ active_te = do_outliner_select_walk(soops, active_te, direction, extend, toggle_all);
}
+ outliner_item_select(
+ C, soops, active_te, OL_ITEM_SELECT | OL_ITEM_ACTIVATE | (extend ? OL_ITEM_EXTEND : 0));
+
/* Scroll outliner to focus on walk element */
- outliner_walk_scroll(region, walk_element);
+ outliner_walk_scroll(region, active_te);
ED_outliner_select_sync_from_outliner(C, soops);
ED_region_tag_redraw(region);
diff --git a/source/blender/editors/space_outliner/outliner_sync.c b/source/blender/editors/space_outliner/outliner_sync.c
index 852773d3979..a4be4062746 100644
--- a/source/blender/editors/space_outliner/outliner_sync.c
+++ b/source/blender/editors/space_outliner/outliner_sync.c
@@ -392,7 +392,6 @@ void ED_outliner_select_sync_from_outliner(bContext *C, SpaceOutliner *soops)
}
static void outliner_select_sync_from_object(ViewLayer *view_layer,
- SpaceOutliner *soops,
Object *obact,
TreeElement *te,
TreeStoreElem *tselem)
@@ -403,7 +402,7 @@ static void outliner_select_sync_from_object(ViewLayer *view_layer,
const bool is_selected = (base != NULL) && ((base->flag & BASE_SELECTED) != 0);
if (base && (ob == obact)) {
- outliner_element_activate(soops, tselem);
+ tselem->flag |= TSE_ACTIVE;
}
else {
tselem->flag &= ~TSE_ACTIVE;
@@ -417,15 +416,14 @@ static void outliner_select_sync_from_object(ViewLayer *view_layer,
}
}
-static void outliner_select_sync_from_edit_bone(SpaceOutliner *soops,
- EditBone *ebone_active,
+static void outliner_select_sync_from_edit_bone(EditBone *ebone_active,
TreeElement *te,
TreeStoreElem *tselem)
{
EditBone *ebone = (EditBone *)te->directdata;
if (ebone == ebone_active) {
- outliner_element_activate(soops, tselem);
+ tselem->flag |= TSE_ACTIVE;
}
else {
tselem->flag &= ~TSE_ACTIVE;
@@ -439,8 +437,7 @@ static void outliner_select_sync_from_edit_bone(SpaceOutliner *soops,
}
}
-static void outliner_select_sync_from_pose_bone(SpaceOutliner *soops,
- bPoseChannel *pchan_active,
+static void outliner_select_sync_from_pose_bone(bPoseChannel *pchan_active,
TreeElement *te,
TreeStoreElem *tselem)
{
@@ -448,7 +445,7 @@ static void outliner_select_sync_from_pose_bone(SpaceOutliner *soops,
Bone *bone = pchan->bone;
if (pchan == pchan_active) {
- outliner_element_activate(soops, tselem);
+ tselem->flag |= TSE_ACTIVE;
}
else {
tselem->flag &= ~TSE_ACTIVE;
@@ -462,14 +459,12 @@ static void outliner_select_sync_from_pose_bone(SpaceOutliner *soops,
}
}
-static void outliner_select_sync_from_sequence(SpaceOutliner *soops,
- Sequence *sequence_active,
- TreeStoreElem *tselem)
+static void outliner_select_sync_from_sequence(Sequence *sequence_active, TreeStoreElem *tselem)
{
Sequence *seq = (Sequence *)tselem->id;
if (seq == sequence_active) {
- outliner_element_activate(soops, tselem);
+ tselem->flag |= TSE_ACTIVE;
}
else {
tselem->flag &= ~TSE_ACTIVE;
@@ -506,26 +501,26 @@ static void outliner_sync_selection_to_outliner(ViewLayer *view_layer,
if (tselem->type == 0 && te->idcode == ID_OB) {
if (sync_types->object) {
- outliner_select_sync_from_object(view_layer, soops, active_data->object, te, tselem);
+ outliner_select_sync_from_object(view_layer, active_data->object, te, tselem);
}
}
else if (tselem->type == TSE_EBONE) {
if (sync_types->edit_bone) {
- outliner_select_sync_from_edit_bone(soops, active_data->edit_bone, te, tselem);
+ outliner_select_sync_from_edit_bone(active_data->edit_bone, te, tselem);
}
}
else if (tselem->type == TSE_POSE_CHANNEL) {
if (sync_types->pose_bone) {
- outliner_select_sync_from_pose_bone(soops, active_data->pose_channel, te, tselem);
+ outliner_select_sync_from_pose_bone(active_data->pose_channel, te, tselem);
}
}
else if (tselem->type == TSE_SEQUENCE) {
if (sync_types->sequence) {
- outliner_select_sync_from_sequence(soops, active_data->sequence, tselem);
+ outliner_select_sync_from_sequence(active_data->sequence, tselem);
}
}
else {
- tselem->flag &= ~TSE_SELECTED;
+ tselem->flag &= ~(TSE_SELECTED | TSE_ACTIVE);
}
/* Sync subtree elements */
diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c
index b4c5dd8813a..51b919cd0dc 100644
--- a/source/blender/editors/space_outliner/outliner_tools.c
+++ b/source/blender/editors/space_outliner/outliner_tools.c
@@ -44,6 +44,7 @@
#include "DNA_world_types.h"
#include "BLI_blenlib.h"
+#include "BLI_ghash.h"
#include "BLI_utildefines.h"
#include "BKE_anim_data.h"
@@ -573,8 +574,7 @@ static void merged_element_search_exec_fn(struct bContext *C, void *UNUSED(arg1)
SpaceOutliner *soops = CTX_wm_space_outliner(C);
TreeElement *te = (TreeElement *)element;
- outliner_item_select(soops, te, false, false);
- outliner_item_do_activate_from_tree_element(C, te, te->store_elem, false, false);
+ outliner_item_select(C, soops, te, OL_ITEM_SELECT | OL_ITEM_ACTIVATE);
ED_outliner_select_sync_from_outliner(C, soops);
}
@@ -664,12 +664,13 @@ static void object_select_hierarchy_cb(bContext *C,
Scene *UNUSED(scene),
TreeElement *te,
TreeStoreElem *UNUSED(tsep),
- TreeStoreElem *tselem,
+ TreeStoreElem *UNUSED(tselem),
void *UNUSED(user_data))
{
/* Don't extend because this toggles, which is nice for Ctrl-Click but not for a menu item.
* it's especially confusing when multiple items are selected since some toggle on/off. */
- outliner_item_do_activate_from_tree_element(C, te, tselem, false, true);
+ SpaceOutliner *soops = CTX_wm_space_outliner(C);
+ outliner_item_select(C, soops, te, OL_ITEM_SELECT | OL_ITEM_ACTIVATE | OL_ITEM_RECURSIVE);
}
static void object_deselect_cb(bContext *C,
@@ -689,12 +690,8 @@ static void object_deselect_cb(bContext *C,
}
}
-static void outliner_object_delete(bContext *C,
- ReportList *reports,
- Scene *scene,
- TreeStoreElem *tselem)
+static void outliner_object_delete_fn(bContext *C, ReportList *reports, Scene *scene, Object *ob)
{
- Object *ob = (Object *)tselem->id;
if (ob) {
Main *bmain = CTX_data_main(C);
if (ob->id.tag & LIB_TAG_INDIRECT) {
@@ -862,7 +859,6 @@ void outliner_do_object_operation_ex(bContext *C,
bool select_recurse)
{
TreeElement *te;
-
for (te = lb->first; te; te = te->next) {
TreeStoreElem *tselem = TREESTORE(te);
bool select_handled = false;
@@ -1177,82 +1173,6 @@ static void outliner_do_data_operation(
}
}
-static Base *outline_delete_hierarchy(bContext *C, ReportList *reports, Scene *scene, Base *base)
-{
- Base *child_base, *base_next;
- Object *parent;
- ViewLayer *view_layer = CTX_data_view_layer(C);
-
- if (!base) {
- return NULL;
- }
-
- for (child_base = view_layer->object_bases.first; child_base; child_base = base_next) {
- base_next = child_base->next;
- for (parent = child_base->object->parent; parent && (parent != base->object);
- parent = parent->parent) {
- /* pass */
- }
- if (parent) {
- base_next = outline_delete_hierarchy(C, reports, scene, child_base);
- }
- }
-
- base_next = base->next;
-
- Main *bmain = CTX_data_main(C);
- if (base->object->id.tag & LIB_TAG_INDIRECT) {
- BKE_reportf(reports,
- RPT_WARNING,
- "Cannot delete indirectly linked object '%s'",
- base->object->id.name + 2);
- return base_next;
- }
- else if (BKE_library_ID_is_indirectly_used(bmain, base->object) &&
- ID_REAL_USERS(base->object) <= 1 && ID_EXTRA_USERS(base->object) == 0) {
- BKE_reportf(reports,
- RPT_WARNING,
- "Cannot delete object '%s' from scene '%s', indirectly used objects need at least "
- "one user",
- base->object->id.name + 2,
- scene->id.name + 2);
- return base_next;
- }
- ED_object_base_free_and_unlink(CTX_data_main(C), scene, base->object);
- return base_next;
-}
-
-static void object_delete_hierarchy_cb(bContext *C,
- ReportList *reports,
- Scene *scene,
- TreeElement *te,
- TreeStoreElem *UNUSED(tsep),
- TreeStoreElem *tselem,
- void *UNUSED(user_data))
-{
- ViewLayer *view_layer = CTX_data_view_layer(C);
- Base *base = (Base *)te->directdata;
- Object *obedit = CTX_data_edit_object(C);
-
- if (!base) {
- base = BKE_view_layer_base_find(view_layer, (Object *)tselem->id);
- }
- if (base) {
- /* Check also library later. */
- for (; obedit && (obedit != base->object); obedit = obedit->parent) {
- /* pass */
- }
- if (obedit == base->object) {
- ED_object_editmode_exit(C, EM_FREEDATA);
- }
-
- outline_delete_hierarchy(C, reports, scene, base);
- }
-
- DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
- WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene);
-}
-
static Base *outline_batch_delete_hierarchy(
ReportList *reports, Main *bmain, ViewLayer *view_layer, Scene *scene, Base *base)
{
@@ -1305,21 +1225,16 @@ static Base *outline_batch_delete_hierarchy(
return base_next;
}
-static void object_batch_delete_hierarchy_cb(bContext *C,
+static void object_batch_delete_hierarchy_fn(bContext *C,
ReportList *reports,
Scene *scene,
- TreeElement *te,
- TreeStoreElem *UNUSED(tsep),
- TreeStoreElem *tselem,
- void *UNUSED(user_data))
+ Object *ob)
{
ViewLayer *view_layer = CTX_data_view_layer(C);
- Base *base = (Base *)te->directdata;
Object *obedit = CTX_data_edit_object(C);
- if (!base) {
- base = BKE_view_layer_base_find(view_layer, (Object *)tselem->id);
- }
+ Base *base = BKE_view_layer_base_find(view_layer, ob);
+
if (base) {
/* Check also library later. */
for (; obedit && (obedit != base->object); obedit = obedit->parent) {
@@ -1343,7 +1258,6 @@ enum {
OL_OP_SELECT = 1,
OL_OP_DESELECT,
OL_OP_SELECT_HIERARCHY,
- OL_OP_DELETE_HIERARCHY,
OL_OP_REMAP,
OL_OP_LOCALIZED, /* disabled, see below */
OL_OP_TOGVIS,
@@ -1358,7 +1272,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_DELETE_HIERARCHY, "DELETE_HIERARCHY", 0, "Delete Hierarchy", ""},
{OL_OP_REMAP,
"REMAP",
0,
@@ -1372,7 +1285,6 @@ static const EnumPropertyItem prop_object_op_types[] = {
static int outliner_object_operation_exec(bContext *C, wmOperator *op)
{
- struct wmMsgBus *mbus = CTX_wm_message_bus(C);
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
wmWindow *win = CTX_wm_window(C);
@@ -1413,43 +1325,6 @@ static int outliner_object_operation_exec(bContext *C, wmOperator *op)
str = "Deselect Objects";
selection_changed = true;
}
- else if (event == OL_OP_DELETE_HIERARCHY) {
- ViewLayer *view_layer = CTX_data_view_layer(C);
- const Base *basact_prev = BASACT(view_layer);
-
- /* Keeping old 'safe and slow' code for a bit (new one enabled on 28/01/2019). */
- if (G.debug_value == 666) {
- outliner_do_object_operation_ex(
- C, op->reports, scene, soops, &soops->tree, object_delete_hierarchy_cb, NULL, false);
- }
- else {
- BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
-
- outliner_do_object_operation_ex(C,
- op->reports,
- scene,
- soops,
- &soops->tree,
- object_batch_delete_hierarchy_cb,
- NULL,
- false);
-
- BKE_id_multi_tagged_delete(bmain);
- }
-
- /* XXX: See outliner_delete_exec comment below. */
- outliner_cleanup_tree(soops);
-
- DEG_relations_tag_update(bmain);
- str = "Delete Object Hierarchy";
- DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
- WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
- if (basact_prev != BASACT(view_layer)) {
- WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene);
- WM_msg_publish_rna_prop(mbus, &scene->id, view_layer, LayerObjects, active);
- }
- selection_changed = true;
- }
else if (event == OL_OP_REMAP) {
outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, id_remap_cb, NULL);
/* No undo push here, operator does it itself (since it's a modal one, the op_undo_depth trick
@@ -1513,22 +1388,38 @@ void OUTLINER_OT_object_operation(wmOperatorType *ot)
/** \name Delete Object/Collection Operator
* \{ */
-static void outliner_objects_delete(
- bContext *C, Scene *scene, SpaceOutliner *soops, ReportList *reports, ListBase *lb)
+typedef void (*OutlinerDeleteFunc)(bContext *C, ReportList *reports, Scene *scene, Object *ob);
+
+static void outliner_do_object_delete(bContext *C,
+ ReportList *reports,
+ Scene *scene,
+ GSet *objects_to_delete,
+ OutlinerDeleteFunc delete_fn)
{
- LISTBASE_FOREACH (TreeElement *, te, lb) {
- TreeStoreElem *tselem = TREESTORE(te);
+ GSetIterator objects_to_delete_iter;
+ GSET_ITER (objects_to_delete_iter, objects_to_delete) {
+ Object *ob = (Object *)BLI_gsetIterator_getKey(&objects_to_delete_iter);
- if (tselem->flag & TSE_SELECTED) {
- if (tselem->type == 0 && te->idcode == ID_OB) {
- outliner_object_delete(C, reports, scene, tselem);
- }
- }
+ delete_fn(C, reports, scene, ob);
+ }
+}
- if (TSELEM_OPEN(tselem, soops)) {
- outliner_objects_delete(C, scene, soops, reports, &te->subtree);
- }
+static TreeTraversalAction outliner_find_objects_to_delete(TreeElement *te, void *customdata)
+{
+ GSet *objects_to_delete = (GSet *)customdata;
+ TreeStoreElem *tselem = TREESTORE(te);
+
+ if (outliner_is_collection_tree_element(te)) {
+ return TRAVERSE_CONTINUE;
}
+
+ if (tselem->type || (tselem->id == NULL) || (GS(tselem->id->name) != ID_OB)) {
+ return TRAVERSE_SKIP_CHILDS;
+ }
+
+ BLI_gset_add(objects_to_delete, tselem->id);
+
+ return TRAVERSE_CONTINUE;
}
static int outliner_delete_exec(bContext *C, wmOperator *op)
@@ -1537,12 +1428,32 @@ static int outliner_delete_exec(bContext *C, wmOperator *op)
Scene *scene = CTX_data_scene(C);
SpaceOutliner *soops = CTX_wm_space_outliner(C);
struct wmMsgBus *mbus = CTX_wm_message_bus(C);
-
ViewLayer *view_layer = CTX_data_view_layer(C);
const Base *basact_prev = BASACT(view_layer);
- outliner_collection_delete(C, bmain, scene, op->reports, false);
- outliner_objects_delete(C, scene, soops, op->reports, &soops->tree);
+ const bool delete_hierarchy = RNA_boolean_get(op->ptr, "hierarchy");
+
+ /* Get selected objects skipping duplicates to prevent deleting objects linked to multiple
+ * collections twice */
+ GSet *objects_to_delete = BLI_gset_ptr_new(__func__);
+ outliner_tree_traverse(
+ soops, &soops->tree, 0, TSE_SELECTED, outliner_find_objects_to_delete, objects_to_delete);
+
+ if (delete_hierarchy) {
+ BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
+
+ outliner_do_object_delete(
+ C, op->reports, scene, objects_to_delete, object_batch_delete_hierarchy_fn);
+
+ BKE_id_multi_tagged_delete(bmain);
+ }
+ else {
+ outliner_do_object_delete(C, op->reports, scene, objects_to_delete, outliner_object_delete_fn);
+ }
+
+ BLI_gset_free(objects_to_delete, NULL);
+
+ outliner_collection_delete(C, bmain, scene, op->reports, delete_hierarchy);
/* Tree management normally happens from draw_outliner(), but when
* you're clicking too fast on Delete object from context menu in
@@ -1579,6 +1490,11 @@ void OUTLINER_OT_delete(wmOperatorType *ot)
/* flags */
ot->flag |= OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ PropertyRNA *prop = RNA_def_boolean(
+ ot->srna, "hierarchy", false, "Hierarchy", "Delete child objects and collections");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
/** \} */
diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c
index d6efe683673..bc272431e3a 100644
--- a/source/blender/editors/space_outliner/outliner_tree.c
+++ b/source/blender/editors/space_outliner/outliner_tree.c
@@ -235,6 +235,17 @@ static TreeElement *outliner_add_element(
/* -------------------------------------------------------- */
+/**
+ * Check if an element type needs a full rebuild if the open/collapsed state changes.
+ * These element types don't add children if collapsed.
+ *
+ * This current check isn't great really. A per element-type flag would be preferable.
+ */
+bool outliner_element_needs_rebuild_on_open_change(const TreeStoreElem *tselem)
+{
+ return ELEM(tselem->type, TSE_RNA_STRUCT, TSE_RNA_PROPERTY, TSE_KEYMAP);
+}
+
/* special handling of hierarchical non-lib data */
static void outliner_add_bone(
SpaceOutliner *soops, ListBase *lb, ID *id, Bone *curBone, TreeElement *parent, int *a)
@@ -538,7 +549,7 @@ static void outliner_add_id_contents(SpaceOutliner *soops,
/* expand specific data always */
switch (GS(id->name)) {
case ID_LI: {
- te->name = ((Library *)id)->name;
+ te->name = ((Library *)id)->filepath;
break;
}
case ID_SCE: {
@@ -786,8 +797,13 @@ static void outliner_add_id_contents(SpaceOutliner *soops,
}
}
-// TODO: this function needs to be split up! It's getting a bit too large...
-// Note: "ID" is not always a real ID
+/**
+ * TODO: this function needs to be split up! It's getting a bit too large...
+ *
+ * \note: "ID" is not always a real ID
+ * \note: If child items are only added to the tree if the item is open, the TSE_ type _must_ be
+ * added to #outliner_element_needs_rebuild_on_open_change().
+ */
static TreeElement *outliner_add_element(
SpaceOutliner *soops, ListBase *lb, void *idv, TreeElement *parent, short type, short index)
{
@@ -854,7 +870,7 @@ static TreeElement *outliner_add_element(
else {
/* do here too, for blend file viewer, own ID_LI then shows file name */
if (GS(id->name) == ID_LI) {
- te->name = ((Library *)id)->name;
+ te->name = ((Library *)id)->filepath;
}
else {
te->name = id->name + 2; // default, can be overridden by Library or non-ID data
@@ -2340,8 +2356,10 @@ void outliner_build_tree(
for (lib = mainvar->libraries.first; lib; lib = lib->id.next) {
ten = outliner_add_library_contents(mainvar, soops, &soops->tree, lib);
- BLI_assert(ten != NULL);
- lib->id.newid = (ID *)ten;
+ /* NULL-check matters, due to filtering there may not be a new element. */
+ if (ten) {
+ lib->id.newid = (ID *)ten;
+ }
}
/* make hierarchy */
ten = soops->tree.first;
diff --git a/source/blender/editors/space_outliner/outliner_utils.c b/source/blender/editors/space_outliner/outliner_utils.c
index a058c30cef2..a120718e36b 100644
--- a/source/blender/editors/space_outliner/outliner_utils.c
+++ b/source/blender/editors/space_outliner/outliner_utils.c
@@ -84,12 +84,24 @@ TreeElement *outliner_find_item_at_y(const SpaceOutliner *soops,
/* co_y is inside this element */
return te_iter;
}
- else if (TSELEM_OPEN(te_iter->store_elem, soops)) {
- /* co_y is lower than current element, possibly inside children */
- TreeElement *te_sub = outliner_find_item_at_y(soops, &te_iter->subtree, view_co_y);
- if (te_sub) {
- return te_sub;
- }
+
+ if (BLI_listbase_is_empty(&te_iter->subtree) || !TSELEM_OPEN(TREESTORE(te_iter), soops)) {
+ /* No need for recursion. */
+ continue;
+ }
+
+ /* If the coordinate is lower than the next element, we can continue with that one and skip
+ * recursion too. */
+ const TreeElement *te_next = te_iter->next;
+ if (te_next && (view_co_y < (te_next->ys + UI_UNIT_Y))) {
+ continue;
+ }
+
+ /* co_y is lower than current element (but not lower than the next one), possibly inside
+ * children */
+ TreeElement *te_sub = outliner_find_item_at_y(soops, &te_iter->subtree, view_co_y);
+ if (te_sub) {
+ return te_sub;
}
}
}
@@ -256,7 +268,7 @@ TreeElement *outliner_find_editbone(ListBase *lb, const EditBone *ebone)
return NULL;
}
-ID *outliner_search_back(TreeElement *te, short idcode)
+TreeElement *outliner_search_back_te(TreeElement *te, short idcode)
{
TreeStoreElem *tselem;
te = te->parent;
@@ -264,13 +276,26 @@ ID *outliner_search_back(TreeElement *te, short idcode)
while (te) {
tselem = TREESTORE(te);
if (tselem->type == 0 && te->idcode == idcode) {
- return tselem->id;
+ return te;
}
te = te->parent;
}
return NULL;
}
+ID *outliner_search_back(TreeElement *te, short idcode)
+{
+ TreeElement *search_te;
+ TreeStoreElem *tselem;
+
+ search_te = outliner_search_back_te(te, idcode);
+ if (search_te) {
+ tselem = TREESTORE(search_te);
+ return tselem->id;
+ }
+ return NULL;
+}
+
/**
* Iterate over all tree elements (pre-order traversal), executing \a func callback for
* each tree element matching the optional filters.
diff --git a/source/blender/editors/space_outliner/space_outliner.c b/source/blender/editors/space_outliner/space_outliner.c
index 9450136b6a6..aa1663dff01 100644
--- a/source/blender/editors/space_outliner/space_outliner.c
+++ b/source/blender/editors/space_outliner/space_outliner.c
@@ -84,7 +84,6 @@ static void outliner_main_region_init(wmWindowManager *wm, ARegion *region)
static void outliner_main_region_draw(const bContext *C, ARegion *region)
{
View2D *v2d = &region->v2d;
- View2DScrollers *scrollers;
/* clear */
UI_ThemeClearColor(TH_BACK);
@@ -96,9 +95,7 @@ static void outliner_main_region_draw(const bContext *C, ARegion *region)
UI_view2d_view_restore(C);
/* scrollers */
- scrollers = UI_view2d_scrollers_calc(v2d, NULL);
- UI_view2d_scrollers_draw(v2d, scrollers);
- UI_view2d_scrollers_free(scrollers);
+ UI_view2d_scrollers_draw(v2d, NULL);
}
static void outliner_main_region_free(ARegion *UNUSED(region))
@@ -215,7 +212,7 @@ static void outliner_main_region_listener(wmWindow *UNUSED(win),
}
break;
case NC_SCREEN:
- if (ELEM(wmn->data, ND_LAYER)) {
+ if (ELEM(wmn->data, ND_LAYOUTDELETE, ND_LAYER)) {
ED_region_tag_redraw(region);
}
break;
@@ -398,7 +395,7 @@ static void outliner_deactivate(struct ScrArea *area)
/* Remove hover highlights */
SpaceOutliner *soops = area->spacedata.first;
outliner_flag_set(&soops->tree, TSE_HIGHLIGHTED, false);
- ED_region_tag_redraw(BKE_area_find_region_type(area, RGN_TYPE_WINDOW));
+ ED_region_tag_redraw_no_rebuild(BKE_area_find_region_type(area, RGN_TYPE_WINDOW));
}
/* only called once, from space_api/spacetypes.c */
diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c
index 1f06ab68516..1af552680a3 100644
--- a/source/blender/editors/space_sequencer/sequencer_draw.c
+++ b/source/blender/editors/space_sequencer/sequencer_draw.c
@@ -697,7 +697,8 @@ static void draw_seq_text(View2D *v2d,
str_len = 0;
}
else if (seq->sound) {
- str_len = BLI_snprintf(str, sizeof(str), "%s: %s | %d", name, seq->sound->name, seq->len);
+ str_len = BLI_snprintf(
+ str, sizeof(str), "%s: %s | %d", name, seq->sound->filepath, seq->len);
}
else {
str_len = BLI_snprintf(str, sizeof(str), "%s | %d", name, seq->len);
@@ -2257,7 +2258,6 @@ void draw_timeline_seq(const bContext *C, ARegion *region)
Editing *ed = BKE_sequencer_editing_get(scene, false);
SpaceSeq *sseq = CTX_wm_space_seq(C);
View2D *v2d = &region->v2d;
- View2DScrollers *scrollers;
short cfra_flag = 0;
float col[3];
@@ -2299,9 +2299,6 @@ void draw_timeline_seq(const bContext *C, ARegion *region)
cfra_flag |= DRAWCFRA_UNIT_SECONDS;
}
- /* Draw the current frame indicator. */
- ANIM_draw_cfra(C, v2d, cfra_flag);
-
/* Draw overlap frame frame indicator. */
if (scene->ed && scene->ed->over_flag & SEQ_EDIT_OVERLAY_SHOW) {
int cfra_over = (scene->ed->over_flag & SEQ_EDIT_OVERLAY_ABS) ?
@@ -2345,9 +2342,6 @@ void draw_timeline_seq(const bContext *C, ARegion *region)
ED_region_draw_cb_draw(C, region, REGION_DRAW_POST_VIEW);
UI_view2d_view_restore(C);
ED_time_scrub_draw(region, scene, !(sseq->flag & SEQ_DRAWFRAMES), true);
- scrollers = UI_view2d_scrollers_calc(v2d, NULL);
- UI_view2d_scrollers_draw(v2d, scrollers);
- UI_view2d_scrollers_free(scrollers);
/* Draw channel numbers. */
{
@@ -2357,3 +2351,13 @@ void draw_timeline_seq(const bContext *C, ARegion *region)
UI_view2d_draw_scale_y__block(region, v2d, &rect, TH_SCROLL_TEXT);
}
}
+
+void draw_timeline_seq_display(const bContext *C, ARegion *region)
+{
+ const Scene *scene = CTX_data_scene(C);
+ const SpaceSeq *sseq = CTX_wm_space_seq(C);
+ View2D *v2d = &region->v2d;
+
+ ED_time_scrub_draw_current_frame(region, scene, !(sseq->flag & SEQ_DRAWFRAMES), true);
+ UI_view2d_scrollers_draw(v2d, NULL);
+}
diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c
index 9311cbed265..f74a4eae3bb 100644
--- a/source/blender/editors/space_sequencer/sequencer_edit.c
+++ b/source/blender/editors/space_sequencer/sequencer_edit.c
@@ -673,6 +673,14 @@ int seq_effect_find_selected(Scene *scene,
*r_selseq2 = seq2;
*r_selseq3 = seq3;
+ /* TODO(Richard): This function needs some refactoring, this is just quick hack for T73828. */
+ if (BKE_sequence_effect_get_num_inputs(type) < 3) {
+ *r_selseq3 = NULL;
+ }
+ if (BKE_sequence_effect_get_num_inputs(type) < 2) {
+ *r_selseq2 = NULL;
+ }
+
return 1;
}
@@ -1403,14 +1411,21 @@ static int sequencer_snap_exec(bContext *C, wmOperator *op)
BKE_sequence_base_shuffle(ed->seqbasep, seq, scene);
}
}
- else if (seq->type & SEQ_TYPE_EFFECT) {
+ }
+
+ /* Recalculate bounds of effect strips. */
+ for (seq = ed->seqbasep->first; seq; seq = seq->next) {
+ if (seq->type & SEQ_TYPE_EFFECT) {
if (seq->seq1 && (seq->seq1->flag & SELECT)) {
+ BKE_sequencer_offset_animdata(scene, seq, (snap_frame - seq->startdisp));
BKE_sequence_calc(scene, seq);
}
else if (seq->seq2 && (seq->seq2->flag & SELECT)) {
+ BKE_sequencer_offset_animdata(scene, seq, (snap_frame - seq->startdisp));
BKE_sequence_calc(scene, seq);
}
else if (seq->seq3 && (seq->seq3->flag & SELECT)) {
+ BKE_sequencer_offset_animdata(scene, seq, (snap_frame - seq->startdisp));
BKE_sequence_calc(scene, seq);
}
}
@@ -2218,6 +2233,11 @@ static int sequencer_reassign_inputs_exec(bContext *C, wmOperator *op)
Sequence *seq1, *seq2, *seq3, *last_seq = BKE_sequencer_active_get(scene);
const char *error_msg;
+ if (BKE_sequence_effect_get_num_inputs(last_seq->type) != 0) {
+ BKE_report(op->reports, RPT_ERROR, "Cannot reassign inputs: strip has no inputs");
+ return OPERATOR_CANCELLED;
+ }
+
if (!seq_effect_find_selected(
scene, last_seq, last_seq->type, &seq1, &seq2, &seq3, &error_msg)) {
BKE_report(op->reports, RPT_ERROR, error_msg);
@@ -3433,6 +3453,7 @@ static int sequencer_paste_exec(bContext *C, wmOperator *UNUSED(op))
* on the actual data-blocks. */
BKE_sequencer_base_clipboard_pointers_restore(&seqbase_clipboard, bmain);
BKE_sequence_base_dupli_recursive(scene, scene, &nseqbase, &seqbase_clipboard, 0, 0);
+ BKE_sequencer_base_clipboard_pointers_store(bmain, &seqbase_clipboard);
iseq_first = nseqbase.first;
@@ -3451,6 +3472,7 @@ static int sequencer_paste_exec(bContext *C, wmOperator *UNUSED(op))
}
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
+ DEG_relations_tag_update(bmain);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
ED_outliner_select_sync_from_sequence_tag(C);
@@ -3925,7 +3947,7 @@ static int sequencer_change_path_exec(bContext *C, wmOperator *op)
}
char filepath[FILE_MAX];
RNA_string_get(op->ptr, "filepath", filepath);
- BLI_strncpy(sound->name, filepath, sizeof(sound->name));
+ BLI_strncpy(sound->filepath, filepath, sizeof(sound->filepath));
BKE_sound_load(bmain, sound);
}
else {
diff --git a/source/blender/editors/space_sequencer/sequencer_intern.h b/source/blender/editors/space_sequencer/sequencer_intern.h
index 12533dbc74f..fee07e8941d 100644
--- a/source/blender/editors/space_sequencer/sequencer_intern.h
+++ b/source/blender/editors/space_sequencer/sequencer_intern.h
@@ -43,6 +43,7 @@ struct wmOperator;
/* sequencer_draw.c */
void draw_timeline_seq(const struct bContext *C, struct ARegion *region);
+void draw_timeline_seq_display(const struct bContext *C, struct ARegion *region);
void sequencer_draw_preview(const struct bContext *C,
struct Scene *scene,
struct ARegion *region,
diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c
index 2686edd58a5..85b70354ab3 100644
--- a/source/blender/editors/space_sequencer/sequencer_select.c
+++ b/source/blender/editors/space_sequencer/sequencer_select.c
@@ -409,32 +409,15 @@ static int sequencer_select_exec(bContext *C, wmOperator *op)
/* Select left, right or overlapping the current frame. */
if (side_of_frame) {
/* Use different logic for this. */
- float x;
if (extend == false) {
ED_sequencer_deselect_all(scene);
}
- /* 10px margin around current frame to select under the current frame with mouse. */
- float margin = BLI_rctf_size_x(&v2d->cur) / BLI_rcti_size_x(&v2d->mask) * 10;
- x = UI_view2d_region_to_view_x(v2d, mval[0]);
- if (x >= CFRA - margin && x <= CFRA + margin) {
- x = CFRA;
- }
+ const float x = UI_view2d_region_to_view_x(v2d, mval[0]);
SEQP_BEGIN (ed, seq) {
- bool test = false;
- /* FIXME(campbell): this functionality is only in the sequencer,
- * either we should support this for all timeline views or remove it. */
- if ((x == CFRA) && (seq->startdisp <= CFRA) && (seq->enddisp >= CFRA)) {
- /* Select overlapping the current frame. */
- test = true;
- }
- else if ((x < CFRA && seq->enddisp <= CFRA) || (x > CFRA && seq->startdisp >= CFRA)) {
+ if (((x < CFRA) && (seq->enddisp <= CFRA)) || ((x >= CFRA) && (seq->startdisp >= CFRA))) {
/* Select left or right. */
- test = true;
- }
-
- if (test) {
seq->flag |= SELECT;
recurs_sel_seq(seq);
}
@@ -1023,7 +1006,6 @@ static int sequencer_select_side_of_frame_exec(bContext *C, wmOperator *op)
void SEQUENCER_OT_select_side_of_frame(wmOperatorType *ot)
{
static const EnumPropertyItem sequencer_select_left_right_types[] = {
- {0, "OVERLAP", 0, "Overlap", "Select overlapping the current frame"},
{-1, "LEFT", 0, "Left", "Select to the left of the current frame"},
{1, "RIGHT", 0, "Right", "Select to the right of the current frame"},
{0, NULL, 0, NULL, NULL},
diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c
index f52cfdd34c0..368f9c1af19 100644
--- a/source/blender/editors/space_sequencer/space_sequencer.c
+++ b/source/blender/editors/space_sequencer/space_sequencer.c
@@ -521,6 +521,12 @@ static void sequencer_main_region_draw(const bContext *C, ARegion *region)
draw_timeline_seq(C, region);
}
+/* Strip editing timeline. */
+static void sequencer_main_region_draw_overlay(const bContext *C, ARegion *region)
+{
+ draw_timeline_seq_display(C, region);
+}
+
static void sequencer_main_region_listener(wmWindow *UNUSED(win),
ScrArea *UNUSED(area),
ARegion *region,
@@ -865,6 +871,7 @@ void ED_spacetype_sequencer(void)
art->regionid = RGN_TYPE_WINDOW;
art->init = sequencer_main_region_init;
art->draw = sequencer_main_region_draw;
+ art->draw_overlay = sequencer_main_region_draw_overlay;
art->listener = sequencer_main_region_listener;
art->message_subscribe = sequencer_main_region_message_subscribe;
art->keymapflag = ED_KEYMAP_TOOL | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES | ED_KEYMAP_ANIMATION;
diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c
index e50b25de412..c0343900f8b 100644
--- a/source/blender/editors/space_text/text_ops.c
+++ b/source/blender/editors/space_text/text_ops.c
@@ -85,7 +85,7 @@ static void test_line_start(char c, bool *r_last_state)
/**
* This function converts the indentation tabs from a buffer to spaces.
- * \param buf: A pointer to a cstring.
+ * \param in_buf: A pointer to a cstring.
* \param tab_size: The size, in spaces, of the tab character.
*/
static char *buf_tabs_to_spaces(const char *in_buf, const int tab_size)
@@ -370,7 +370,7 @@ static int text_open_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(e
{
Main *bmain = CTX_data_main(C);
Text *text = CTX_data_edit_text(C);
- const char *path = (text && text->name) ? text->name : BKE_main_blendfile_path(bmain);
+ const char *path = (text && text->filepath) ? text->filepath : BKE_main_blendfile_path(bmain);
if (RNA_struct_property_is_set(op->ptr, "filepath")) {
return text_open_exec(C, op);
@@ -428,6 +428,13 @@ static int text_reload_exec(bContext *C, wmOperator *op)
const int orig_curl = BLI_findindex(&text->lines, text->curl);
const int orig_curc = text->curc;
+ /* Don't make this part of 'poll', since 'Alt-R' will type 'R',
+ * if poll checks for the filename. */
+ if (text->filepath == NULL) {
+ BKE_report(op->reports, RPT_ERROR, "This text has not been saved");
+ return OPERATOR_CANCELLED;
+ }
+
if (!BKE_text_reload(text)) {
BKE_report(op->reports, RPT_ERROR, "Could not reopen file");
return OPERATOR_CANCELLED;
@@ -536,10 +543,7 @@ static int text_make_internal_exec(bContext *C, wmOperator *UNUSED(op))
text->flags |= TXT_ISMEM | TXT_ISDIRTY;
- if (text->name) {
- MEM_freeN(text->name);
- text->name = NULL;
- }
+ MEM_SAFE_FREE(text->filepath);
text_update_cursor_moved(C);
WM_event_add_notifier(C, NC_TEXT | NA_EDITED, text);
@@ -576,7 +580,7 @@ static bool text_save_poll(bContext *C)
return 0;
}
- return (text->name != NULL && !(text->flags & TXT_ISMEM));
+ return (text->filepath != NULL && !(text->flags & TXT_ISMEM));
}
static void txt_write_file(Main *bmain, Text *text, ReportList *reports)
@@ -586,7 +590,7 @@ static void txt_write_file(Main *bmain, Text *text, ReportList *reports)
BLI_stat_t st;
char filepath[FILE_MAX];
- BLI_strncpy(filepath, text->name, FILE_MAX);
+ BLI_strncpy(filepath, text->filepath, FILE_MAX);
BLI_path_abs(filepath, BKE_main_blendfile_path(bmain));
fp = BLI_fopen(filepath, "w");
@@ -669,10 +673,10 @@ static int text_save_as_exec(bContext *C, wmOperator *op)
RNA_string_get(op->ptr, "filepath", str);
- if (text->name) {
- MEM_freeN(text->name);
+ if (text->filepath) {
+ MEM_freeN(text->filepath);
}
- text->name = BLI_strdup(str);
+ text->filepath = BLI_strdup(str);
text->flags &= ~TXT_ISMEM;
txt_write_file(bmain, text, op->reports);
@@ -693,8 +697,8 @@ static int text_save_as_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSE
return text_save_as_exec(C, op);
}
- if (text->name) {
- str = text->name;
+ if (text->filepath) {
+ str = text->filepath;
}
else if (text->flags & TXT_ISMEM) {
str = text->id.name + 2;
@@ -3621,9 +3625,57 @@ void TEXT_OT_find(wmOperatorType *ot)
/** \name Replace Operator
* \{ */
+static int text_replace_all(bContext *C)
+{
+ SpaceText *st = CTX_wm_space_text(C);
+ Text *text = st->text;
+ const int flags = st->flags;
+ int found = 0;
+
+ if (!st->findstr[0]) {
+ return OPERATOR_CANCELLED;
+ }
+
+ const int orig_curl = BLI_findindex(&text->lines, text->curl);
+ const int orig_curc = text->curc;
+ bool has_sel = txt_has_sel(text);
+
+ txt_move_toline(text, 0, false);
+
+ found = txt_find_string(text, st->findstr, 0, flags & ST_MATCH_CASE);
+ if (found) {
+ ED_text_undo_push_init(C);
+
+ do {
+ txt_insert_buf(text, st->replacestr);
+ if (text->curl && text->curl->format) {
+ MEM_freeN(text->curl->format);
+ text->curl->format = NULL;
+ }
+ found = txt_find_string(text, st->findstr, 0, flags & ST_MATCH_CASE);
+ } while (found);
+
+ WM_event_add_notifier(C, NC_TEXT | NA_EDITED, text);
+ text_drawcache_tag_update(CTX_wm_space_text(C), 1);
+ }
+ else {
+ /* Restore position */
+ txt_move_to(text, orig_curl, orig_curc, has_sel);
+ return OPERATOR_CANCELLED;
+ }
+
+ return OPERATOR_FINISHED;
+}
+
static int text_replace_exec(bContext *C, wmOperator *op)
{
- return text_find_and_replace(C, op, TEXT_REPLACE);
+ bool replace_all = RNA_boolean_get(op->ptr, "all");
+ if (replace_all) {
+ return text_replace_all(C);
+ }
+ else {
+ return text_find_and_replace(C, op, TEXT_REPLACE);
+ }
}
void TEXT_OT_replace(wmOperatorType *ot)
@@ -3639,6 +3691,11 @@ void TEXT_OT_replace(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_UNDO;
+
+ /* properties */
+ PropertyRNA *prop;
+ prop = RNA_def_boolean(ot->srna, "all", false, "Replace all", "Replace all occurrences");
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
}
/** \} */
diff --git a/source/blender/editors/space_view3d/view3d_camera_control.c b/source/blender/editors/space_view3d/view3d_camera_control.c
index daa3f641404..aeabe68c2d0 100644
--- a/source/blender/editors/space_view3d/view3d_camera_control.c
+++ b/source/blender/editors/space_view3d/view3d_camera_control.c
@@ -182,7 +182,7 @@ struct View3DCameraControl *ED_view3d_cameracontrol_acquire(Depsgraph *depsgraph
copy_qt_qt(vctrl->rot_backup, rv3d->viewquat);
copy_v3_v3(vctrl->ofs_backup, rv3d->ofs);
- /* the dist defines a vector that is infront of the offset
+ /* The dist defines a vector that is in front of the offset
* to rotate the view about.
* this is no good for fly mode because we
* want to rotate about the viewers center.
diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c
index fac378ae104..b3165c9fc78 100644
--- a/source/blender/editors/space_view3d/view3d_draw.c
+++ b/source/blender/editors/space_view3d/view3d_draw.c
@@ -2376,7 +2376,7 @@ void ED_view3d_datamask(const bContext *C,
{
if (ELEM(v3d->shading.type, OB_TEXTURE, OB_MATERIAL, OB_RENDER)) {
r_cddata_masks->lmask |= CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL;
- r_cddata_masks->vmask |= CD_MASK_ORCO;
+ r_cddata_masks->vmask |= CD_MASK_ORCO | CD_MASK_PROP_COLOR;
}
else if (v3d->shading.type == OB_SOLID) {
if (v3d->shading.color_type == V3D_SHADING_TEXTURE_COLOR) {
@@ -2384,6 +2384,7 @@ void ED_view3d_datamask(const bContext *C,
}
if (v3d->shading.color_type == V3D_SHADING_VERTEX_COLOR) {
r_cddata_masks->lmask |= CD_MASK_MLOOPCOL;
+ r_cddata_masks->vmask |= CD_MASK_ORCO | CD_MASK_PROP_COLOR;
}
}
diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c
index edd75d8e561..3c3dea1509d 100644
--- a/source/blender/editors/space_view3d/view3d_edit.c
+++ b/source/blender/editors/space_view3d/view3d_edit.c
@@ -4988,7 +4988,7 @@ void ED_view3d_cursor3d_position(bContext *C,
ED_view3d_calc_zfac(rv3d, cursor_co, &flip);
- /* reset the depth based on the view offset (we _know_ the offset is infront of us) */
+ /* Reset the depth based on the view offset (we _know_ the offset is in front of us). */
if (flip) {
negate_v3_v3(cursor_co, rv3d->ofs);
/* re initialize, no need to check flip again */
diff --git a/source/blender/editors/space_view3d/view3d_fly.c b/source/blender/editors/space_view3d/view3d_fly.c
index 06d1a033a0d..506969443fd 100644
--- a/source/blender/editors/space_view3d/view3d_fly.c
+++ b/source/blender/editors/space_view3d/view3d_fly.c
@@ -55,6 +55,10 @@
#include "view3d_intern.h" /* own include */
+/* -------------------------------------------------------------------- */
+/** \name Modal Key-map
+ * \{ */
+
/* NOTE: these defines are saved in keymap files,
* do not change values but just add new ones */
enum {
@@ -138,6 +142,12 @@ void fly_modal_keymap(wmKeyConfig *keyconf)
WM_modalkeymap_assign(keymap, "VIEW3D_OT_fly");
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Internal Fly Structs
+ * \{ */
+
typedef struct FlyInfo {
/* context stuff */
RegionView3D *rv3d;
@@ -205,6 +215,12 @@ typedef struct FlyInfo {
} FlyInfo;
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Internal Fly Drawing
+ * \{ */
+
/* prototypes */
#ifdef WITH_INPUT_NDOF
static void flyApply_ndof(bContext *C, FlyInfo *fly, bool is_confirm);
@@ -278,6 +294,12 @@ static void drawFlyPixel(const struct bContext *UNUSED(C), ARegion *UNUSED(regio
immUnbindProgram();
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Internal Fly Logic
+ * \{ */
+
/* FlyInfo->state */
enum {
FLY_RUNNING = 0,
@@ -1034,6 +1056,12 @@ static void flyApply_ndof(bContext *C, FlyInfo *fly, bool is_confirm)
}
#endif /* WITH_INPUT_NDOF */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Fly Operator
+ * \{ */
+
static int fly_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
RegionView3D *rv3d = CTX_wm_region_view3d(C);
@@ -1128,3 +1156,5 @@ void VIEW3D_OT_fly(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_BLOCKING;
}
+
+/** \} */
diff --git a/source/blender/editors/space_view3d/view3d_header.c b/source/blender/editors/space_view3d/view3d_header.c
index 829d793333e..f2e42cd1725 100644
--- a/source/blender/editors/space_view3d/view3d_header.c
+++ b/source/blender/editors/space_view3d/view3d_header.c
@@ -209,7 +209,7 @@ static void uiTemplatePaintModeSelection(uiLayout *layout, struct bContext *C)
PointerRNA meshptr;
RNA_pointer_create(ob->data, &RNA_Mesh, ob->data, &meshptr);
- if (ob->mode & (OB_MODE_TEXTURE_PAINT)) {
+ if (ob->mode & OB_MODE_TEXTURE_PAINT) {
uiItemR(layout, &meshptr, "use_paint_mask", UI_ITEM_R_ICON_ONLY, "", ICON_NONE);
}
else {
diff --git a/source/blender/editors/space_view3d/view3d_placement.c b/source/blender/editors/space_view3d/view3d_placement.c
index f2b78bc2aaf..131fbbc02ee 100644
--- a/source/blender/editors/space_view3d/view3d_placement.c
+++ b/source/blender/editors/space_view3d/view3d_placement.c
@@ -936,6 +936,8 @@ static int view3d_interactive_add_modal(bContext *C, wmOperator *op, const wmEve
RNA_float_set_array(&op_props, "rotation", rotation);
RNA_float_set_array(&op_props, "location", location);
RNA_float_set_array(&op_props, "scale", scale);
+ /* Always use default size here. */
+ RNA_float_set(&op_props, "size", 2.0f);
WM_operator_name_call_ptr(C, ot, WM_OP_EXEC_DEFAULT, &op_props);
WM_operator_properties_free(&op_props);
}
diff --git a/source/blender/editors/space_view3d/view3d_project.c b/source/blender/editors/space_view3d/view3d_project.c
index 3a0f0f468fc..f4ec9a22520 100644
--- a/source/blender/editors/space_view3d/view3d_project.c
+++ b/source/blender/editors/space_view3d/view3d_project.c
@@ -557,9 +557,9 @@ void ED_view3d_win_to_3d(const View3D *v3d,
copy_v3_v3(ray_origin, rv3d->viewinv[3]);
ED_view3d_win_to_vector(region, mval, ray_direction);
- /* note, we could use isect_line_plane_v3()
- * however we want the intersection to be infront of the view no matter what,
- * so apply the unsigned factor instead */
+ /* Note: we could use #isect_line_plane_v3()
+ * however we want the intersection to be in front of the view no matter what,
+ * so apply the unsigned factor instead. */
plane_from_point_normal_v3(plane, depth_pt, rv3d->viewinv[2]);
isect_ray_plane_v3(ray_origin, ray_direction, plane, &lambda, false);
diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c
index 1bdb8268c23..9e235d72f26 100644
--- a/source/blender/editors/space_view3d/view3d_select.c
+++ b/source/blender/editors/space_view3d/view3d_select.c
@@ -3007,7 +3007,7 @@ static bool do_meta_box_select(ViewContext *vc, const rcti *rect, const eSelectO
continue;
}
- if (metaelem_id != (hitresult & 0xFFFF0000 & ~(MBALLSEL_ANY))) {
+ if (metaelem_id != (hitresult & 0xFFFF0000 & ~MBALLSEL_ANY)) {
continue;
}
diff --git a/source/blender/editors/space_view3d/view3d_snap.c b/source/blender/editors/space_view3d/view3d_snap.c
index 437c0dd4035..91b2971585d 100644
--- a/source/blender/editors/space_view3d/view3d_snap.c
+++ b/source/blender/editors/space_view3d/view3d_snap.c
@@ -850,12 +850,12 @@ static bool snap_curs_to_sel_ex(bContext *C, float cursor[3])
return false;
}
- if (scene->toolsettings->transform_pivot_point == V3D_AROUND_CENTER_MEDIAN) {
- mul_v3_fl(centroid, 1.0f / (float)count);
- copy_v3_v3(cursor, centroid);
+ if (scene->toolsettings->transform_pivot_point == V3D_AROUND_CENTER_BOUNDS) {
+ mid_v3_v3v3(cursor, min, max);
}
else {
- mid_v3_v3v3(cursor, min, max);
+ mul_v3_fl(centroid, 1.0f / (float)count);
+ copy_v3_v3(cursor, centroid);
}
return true;
}
diff --git a/source/blender/editors/space_view3d/view3d_utils.c b/source/blender/editors/space_view3d/view3d_utils.c
index 377e8c58ba3..15d6a43d105 100644
--- a/source/blender/editors/space_view3d/view3d_utils.c
+++ b/source/blender/editors/space_view3d/view3d_utils.c
@@ -94,7 +94,7 @@ bool ED_view3d_has_workbench_in_texture_color(const Scene *scene,
if (v3d->shading.color_type == V3D_SHADING_TEXTURE_COLOR) {
return true;
}
- if (ob->mode == OB_MODE_TEXTURE_PAINT) {
+ if (ob && ob->mode == OB_MODE_TEXTURE_PAINT) {
return true;
}
}
diff --git a/source/blender/editors/space_view3d/view3d_walk.c b/source/blender/editors/space_view3d/view3d_walk.c
index 7aefd173953..50fa573423a 100644
--- a/source/blender/editors/space_view3d/view3d_walk.c
+++ b/source/blender/editors/space_view3d/view3d_walk.c
@@ -65,6 +65,10 @@
/* ensure the target position is one we can reach, see: T45771 */
#define USE_PIXELSIZE_NATIVE_SUPPORT
+/* -------------------------------------------------------------------- */
+/** \name Modal Key-map
+ * \{ */
+
/* NOTE: these defines are saved in keymap files,
* do not change values but just add new ones */
enum {
@@ -173,6 +177,12 @@ void walk_modal_keymap(wmKeyConfig *keyconf)
WM_modalkeymap_assign(keymap, "VIEW3D_OT_walk");
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Internal Walk Structs
+ * \{ */
+
typedef struct WalkTeleport {
eWalkTeleportState state;
float duration; /* from user preferences */
@@ -283,6 +293,12 @@ typedef struct WalkInfo {
} WalkInfo;
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Internal Walk Drawing
+ * \{ */
+
/* prototypes */
#ifdef WITH_INPUT_NDOF
static void walkApply_ndof(bContext *C, WalkInfo *walk, bool is_confirm);
@@ -340,6 +356,12 @@ static void drawWalkPixel(const struct bContext *UNUSED(C), ARegion *region, voi
immUnbindProgram();
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Internal Walk Logic
+ * \{ */
+
static void walk_navigation_mode_set(WalkInfo *walk, eWalkMethod mode)
{
if (mode == WALK_MODE_FREE) {
@@ -1343,7 +1365,12 @@ static void walkApply_ndof(bContext *C, WalkInfo *walk, bool is_confirm)
}
#endif /* WITH_INPUT_NDOF */
-/****** walk operator ******/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Walk Operator
+ * \{ */
+
static int walk_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
RegionView3D *rv3d = CTX_wm_region_view3d(C);
@@ -1438,3 +1465,5 @@ void VIEW3D_OT_walk(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_BLOCKING;
}
+
+/** \} */
diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c
index b02b1814c67..eb60273fc79 100644
--- a/source/blender/editors/transform/transform.c
+++ b/source/blender/editors/transform/transform.c
@@ -526,8 +526,11 @@ static void viewRedrawPost(bContext *C, TransInfo *t)
}
/* redraw UV editor */
- if (ELEM(t->mode, TFM_VERT_SLIDE, TFM_EDGE_SLIDE) &&
- (t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT)) {
+ const char uvcalc_correct_flag = ELEM(t->mode, TFM_VERT_SLIDE, TFM_EDGE_SLIDE) ?
+ UVCALC_TRANSFORM_CORRECT_SLIDE :
+ UVCALC_TRANSFORM_CORRECT;
+
+ if ((t->data_type == TC_MESH_VERTS) && (t->settings->uvcalc_flag & uvcalc_correct_flag)) {
WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL);
}
@@ -1591,17 +1594,24 @@ static void drawTransformPixel(const struct bContext *C, ARegion *region, void *
void saveTransform(bContext *C, TransInfo *t, wmOperator *op)
{
ToolSettings *ts = CTX_data_tool_settings(C);
- int proportional = 0;
PropertyRNA *prop;
if (!(t->con.mode & CON_APPLY) && (t->flag & T_MODAL) &&
ELEM(t->mode, TFM_TRANSLATION, TFM_RESIZE)) {
/* When redoing these modes the first time, it's more convenient to save
- * the Global orientation. */
- mul_m3_v3(t->spacemtx, t->values_final);
- unit_m3(t->spacemtx);
+ * in the Global orientation. */
+ if (t->mode == TFM_TRANSLATION) {
+ mul_m3_v3(t->spacemtx, t->values_final);
+ }
+ else {
+ float tmat[3][3], sizemat[3][3];
+ size_to_mat3(sizemat, t->values_final);
+ mul_m3_m3m3(tmat, t->spacemtx, sizemat);
+ mat3_to_size(t->values_final, tmat);
+ }
BLI_assert(t->orient_curr == 0);
+ unit_m3(t->spacemtx);
t->orient[0].type = V3D_ORIENT_GLOBAL;
}
@@ -1619,15 +1629,17 @@ void saveTransform(bContext *C, TransInfo *t, wmOperator *op)
}
}
+ bool use_prop_edit = false;
+ int prop_edit_flag = 0;
if (t->flag & T_PROP_EDIT_ALL) {
if (t->flag & T_PROP_EDIT) {
- proportional |= PROP_EDIT_USE;
+ use_prop_edit = true;
}
if (t->flag & T_PROP_CONNECTED) {
- proportional |= PROP_EDIT_CONNECTED;
+ prop_edit_flag |= PROP_EDIT_CONNECTED;
}
if (t->flag & T_PROP_PROJECTED) {
- proportional |= PROP_EDIT_PROJECTED;
+ prop_edit_flag |= PROP_EDIT_PROJECTED;
}
}
@@ -1639,20 +1651,27 @@ void saveTransform(bContext *C, TransInfo *t, wmOperator *op)
if (!(t->options & CTX_NO_PET)) {
if ((prop = RNA_struct_find_property(op->ptr, "use_proportional_edit")) &&
!RNA_property_is_set(op->ptr, prop)) {
+ const Object *obact = OBACT(t->view_layer);
+
if (t->spacetype == SPACE_GRAPH) {
- ts->proportional_fcurve = proportional;
+ ts->proportional_fcurve = use_prop_edit;
}
else if (t->spacetype == SPACE_ACTION) {
- ts->proportional_action = proportional;
- }
- else if (t->obedit_type != -1) {
- ts->proportional_edit = proportional;
+ ts->proportional_action = use_prop_edit;
}
else if (t->options & CTX_MASK) {
- ts->proportional_mask = proportional != 0;
+ ts->proportional_mask = use_prop_edit;
}
- else if ((t->options & CTX_CURSOR) == 0) {
- ts->proportional_objects = proportional != 0;
+ else if (obact && obact->mode == OB_MODE_OBJECT) {
+ ts->proportional_objects = use_prop_edit;
+ }
+ else {
+ if (use_prop_edit) {
+ ts->proportional_edit |= PROP_EDIT_USE;
+ }
+ else {
+ ts->proportional_edit &= ~PROP_EDIT_USE;
+ }
}
}
@@ -1685,9 +1704,9 @@ void saveTransform(bContext *C, TransInfo *t, wmOperator *op)
}
if ((prop = RNA_struct_find_property(op->ptr, "use_proportional_edit"))) {
- RNA_property_boolean_set(op->ptr, prop, proportional & PROP_EDIT_USE);
- RNA_boolean_set(op->ptr, "use_proportional_connected", proportional & PROP_EDIT_CONNECTED);
- RNA_boolean_set(op->ptr, "use_proportional_projected", proportional & PROP_EDIT_PROJECTED);
+ RNA_property_boolean_set(op->ptr, prop, use_prop_edit);
+ RNA_boolean_set(op->ptr, "use_proportional_connected", prop_edit_flag & PROP_EDIT_CONNECTED);
+ RNA_boolean_set(op->ptr, "use_proportional_projected", prop_edit_flag & PROP_EDIT_PROJECTED);
RNA_enum_set(op->ptr, "proportional_edit_falloff", t->prop_mode);
RNA_float_set(op->ptr, "proportional_size", t->prop_size);
}
@@ -1768,7 +1787,7 @@ void saveTransform(bContext *C, TransInfo *t, wmOperator *op)
if ((prop = RNA_struct_find_property(op->ptr, "correct_uv"))) {
RNA_property_boolean_set(
- op->ptr, prop, (t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT) != 0);
+ op->ptr, prop, (t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT_SLIDE) != 0);
}
}
@@ -1819,6 +1838,8 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
int options = 0;
PropertyRNA *prop;
+ mode = transform_mode_really_used(C, mode);
+
t->context = C;
/* added initialize, for external calls to set stuff in TransInfo, like undo string */
diff --git a/source/blender/editors/transform/transform_constraints.c b/source/blender/editors/transform/transform_constraints.c
index 838c1880881..66b90eb159f 100644
--- a/source/blender/editors/transform/transform_constraints.c
+++ b/source/blender/editors/transform/transform_constraints.c
@@ -79,6 +79,27 @@ static void projection_matrix_calc(const TransInfo *t, float r_pmtx[3][3])
}
/* ************************** CONSTRAINTS ************************* */
+#define CONSTRAIN_EPSILON 0.0001f
+
+static void constraint_plane_calc(TransInfo *t, float r_plane[4])
+{
+ const float *constraint_vector[2];
+ int n = 0;
+ for (int i = 0; i < 3; i++) {
+ if (t->con.mode & (CON_AXIS0 << i)) {
+ constraint_vector[n++] = t->spacemtx[i];
+ if (n == 2) {
+ break;
+ }
+ }
+ }
+ BLI_assert(n == 2);
+
+ cross_v3_v3v3(r_plane, constraint_vector[0], constraint_vector[1]);
+ normalize_v3(r_plane);
+ r_plane[3] = -dot_v3v3(r_plane, t->center_global);
+}
+
static void constraintValuesFinal(TransInfo *t, float vec[3])
{
int mode = t->con.mode;
@@ -297,32 +318,83 @@ static void axisProjection(const TransInfo *t,
}
/**
- * Return true if the 2x axis are both aligned when projected into the view.
- * In this case, we can't usefully project the cursor onto the plane.
+ * Snap to the intersection between the edge direction and the constraint plane.
*/
-static bool isPlaneProjectionViewAligned(const TransInfo *t)
+static void constraint_snap_plane_to_edge(const TransInfo *t, const float plane[4], float r_out[3])
{
- const float eps = 0.001f;
- const float *constraint_vector[2];
- int n = 0;
- for (int i = 0; i < 3; i++) {
- if (t->con.mode & (CON_AXIS0 << i)) {
- constraint_vector[n++] = t->spacemtx[i];
- if (n == 2) {
- break;
- }
- }
+ float lambda;
+ const float *edge_snap_point = t->tsnap.snapPoint;
+ const float *edge_dir = t->tsnap.snapNormal;
+ bool is_aligned = fabsf(dot_v3v3(edge_dir, plane)) < CONSTRAIN_EPSILON;
+ if (!is_aligned && isect_ray_plane_v3(edge_snap_point, edge_dir, plane, &lambda, false)) {
+ madd_v3_v3v3fl(r_out, edge_snap_point, edge_dir, lambda);
+ sub_v3_v3(r_out, t->tsnap.snapTarget);
}
- BLI_assert(n == 2);
+}
+
+/**
+ * Snap to the nearest point between the snap point and the line that
+ * intersects the face plane with the constraint plane.
+ */
+static void constraint_snap_plane_to_face(const TransInfo *t, const float plane[4], float r_out[3])
+{
+ float face_plane[4], isect_orig[3], isect_dir[3];
+ const float *face_snap_point = t->tsnap.snapPoint;
+ const float *face_normal = t->tsnap.snapNormal;
+ plane_from_point_normal_v3(face_plane, face_snap_point, face_normal);
+ bool is_aligned = fabsf(dot_v3v3(plane, face_plane)) > (1.0f - CONSTRAIN_EPSILON);
+ if (!is_aligned && isect_plane_plane_v3(plane, face_plane, isect_orig, isect_dir)) {
+ closest_to_ray_v3(r_out, face_snap_point, isect_orig, isect_dir);
+ sub_v3_v3(r_out, t->tsnap.snapTarget);
+ }
+}
- float view_to_plane[3], plane_normal[3];
+/**
+ * Snap to the nearest point on the axis to the edge/line element.
+ */
+void transform_constraint_snap_axis_to_edge(const TransInfo *t,
+ const float axis[3],
+ float r_out[3])
+{
+ float lambda;
+ const float *edge_snap_point = t->tsnap.snapPoint;
+ const float *edge_dir = t->tsnap.snapNormal;
+ bool is_aligned = fabsf(dot_v3v3(axis, edge_dir)) > (1.0f - CONSTRAIN_EPSILON);
+ if (!is_aligned &&
+ isect_ray_ray_v3(t->tsnap.snapTarget, axis, edge_snap_point, edge_dir, &lambda, NULL)) {
+ mul_v3_v3fl(r_out, axis, lambda);
+ }
+}
- getViewVector(t, t->center_global, view_to_plane);
+/**
+ * Snap to the intersection of the axis and the plane defined by the face.
+ */
+void transform_constraint_snap_axis_to_face(const TransInfo *t,
+ const float axis[3],
+ float r_out[3])
+{
+ float lambda;
+ float face_plane[4];
+ const float *face_snap_point = t->tsnap.snapPoint;
+ const float *face_normal = t->tsnap.snapNormal;
+ plane_from_point_normal_v3(face_plane, face_snap_point, face_normal);
+ bool is_aligned = fabsf(dot_v3v3(axis, face_plane)) < CONSTRAIN_EPSILON;
+ if (!is_aligned && isect_ray_plane_v3(t->tsnap.snapTarget, axis, face_plane, &lambda, false)) {
+ mul_v3_v3fl(r_out, axis, lambda);
+ }
+}
- cross_v3_v3v3(plane_normal, constraint_vector[0], constraint_vector[1]);
- normalize_v3(plane_normal);
+/**
+ * Return true if the 2x axis are both aligned when projected into the view.
+ * In this case, we can't usefully project the cursor onto the plane.
+ */
+static bool isPlaneProjectionViewAligned(const TransInfo *t, float plane[4])
+{
+ const float eps = 0.001f;
+ float view_to_plane[3];
+ getViewVector(t, t->center_global, view_to_plane);
- float factor = dot_v3v3(plane_normal, view_to_plane);
+ float factor = dot_v3v3(plane, view_to_plane);
return fabsf(factor) < eps;
}
@@ -361,14 +433,31 @@ static void applyAxisConstraintVec(
copy_v3_v3(out, in);
if (!td && t->con.mode & CON_APPLY) {
mul_m3_v3(t->con.pmtx, out);
+ bool is_snap_to_edge = false, is_snap_to_face = false;
+ if (activeSnap(t)) {
+ is_snap_to_edge = (t->tsnap.snapElem & SCE_SNAP_MODE_EDGE) != 0;
+ is_snap_to_face = (t->tsnap.snapElem & SCE_SNAP_MODE_FACE) != 0;
+ }
- // With snap, a projection is alright, no need to correct for view alignment
- if (!validSnap(t)) {
+ /* With snap points, a projection is alright, no adjustments needed. */
+ if (!validSnap(t) || is_snap_to_edge || is_snap_to_face) {
const int dims = getConstraintSpaceDimension(t);
if (dims == 2) {
if (!is_zero_v3(out)) {
- if (!isPlaneProjectionViewAligned(t)) {
- planeProjection(t, in, out);
+ float plane[4];
+ constraint_plane_calc(t, plane);
+
+ if (is_snap_to_edge) {
+ constraint_snap_plane_to_edge(t, plane, out);
+ }
+ else if (is_snap_to_face) {
+ constraint_snap_plane_to_face(t, plane, out);
+ }
+ else {
+ /* View alignment correction. */
+ if (!isPlaneProjectionViewAligned(t, plane)) {
+ planeProjection(t, in, out);
+ }
}
}
}
@@ -384,7 +473,17 @@ static void applyAxisConstraintVec(
else if (t->con.mode & CON_AXIS2) {
copy_v3_v3(c, t->spacemtx[2]);
}
- axisProjection(t, c, in, out);
+
+ if (is_snap_to_edge) {
+ transform_constraint_snap_axis_to_edge(t, c, out);
+ }
+ else if (is_snap_to_face) {
+ transform_constraint_snap_axis_to_face(t, c, out);
+ }
+ else {
+ /* View alignment correction. */
+ axisProjection(t, c, in, out);
+ }
}
}
postConstraintChecks(t, out);
@@ -779,12 +878,21 @@ void drawPropCircle(const struct bContext *C, TransInfo *t)
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
- immUniformThemeColor(TH_GRID);
+ immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR);
+
+ float viewport[4];
+ GPU_viewport_size_get_f(viewport);
+ GPU_blend(true);
+
+ immUniform2fv("viewportSize", &viewport[2]);
+ immUniform1f("lineWidth", 3.0f * U.pixelsize);
+
+ immUniformThemeColorShadeAlpha(TH_GRID, -20, 255);
+ imm_drawcircball(t->center_global, t->prop_size, imat, pos);
- GPU_logic_op_invert_set(true);
+ immUniform1f("lineWidth", 1.0f * U.pixelsize);
+ immUniformThemeColorShadeAlpha(TH_GRID, 20, 255);
imm_drawcircball(t->center_global, t->prop_size, imat, pos);
- GPU_logic_op_invert_set(false);
immUnbindProgram();
diff --git a/source/blender/editors/transform/transform_constraints.h b/source/blender/editors/transform/transform_constraints.h
index c41b9361ca4..282060af2c3 100644
--- a/source/blender/editors/transform/transform_constraints.h
+++ b/source/blender/editors/transform/transform_constraints.h
@@ -27,6 +27,12 @@
struct TransInfo;
void constraintNumInput(TransInfo *t, float vec[3]);
+void transform_constraint_snap_axis_to_edge(const TransInfo *t,
+ const float axis[3],
+ float r_out[3]);
+void transform_constraint_snap_axis_to_face(const TransInfo *t,
+ const float axis[3],
+ float r_out[3]);
void setConstraint(TransInfo *t, int mode, const char text[]);
void setAxisMatrixConstraint(TransInfo *t, int mode, const char text[]);
void setLocalConstraint(TransInfo *t, int mode, const char text[]);
diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c
index 064057990e0..5d1fd1543df 100644
--- a/source/blender/editors/transform/transform_convert.c
+++ b/source/blender/editors/transform/transform_convert.c
@@ -774,7 +774,7 @@ bool constraints_list_needinv(TransInfo *t, ListBase *list)
bConstraint *con;
/* loop through constraints, checking if there's one of the mentioned
- * constraints needing special crazyspace corrections
+ * constraints needing special crazy-space corrections
*/
if (list) {
for (con = list->first; con; con = con->next) {
@@ -1148,21 +1148,6 @@ void createTransData(bContext *C, TransInfo *t)
}
t->flag |= T_OBJECT;
-
- /* Check if we're transforming the camera from the camera */
- if ((t->spacetype == SPACE_VIEW3D) && (t->region->regiontype == RGN_TYPE_WINDOW)) {
- View3D *v3d = t->view;
- RegionView3D *rv3d = t->region->regiondata;
- if ((rv3d->persp == RV3D_CAMOB) && v3d->camera) {
- /* we could have a flag to easily check an object is being transformed */
- if (v3d->camera->id.tag & LIB_TAG_DOIT) {
- t->flag |= T_CAMERA;
- }
- }
- else if (v3d->ob_center && v3d->ob_center->id.tag & LIB_TAG_DOIT) {
- t->flag |= T_CAMERA;
- }
- }
convert_type = TC_OBJECT;
}
@@ -1175,6 +1160,7 @@ void createTransData(bContext *C, TransInfo *t)
break;
case TC_POSE:
createTransPose(t);
+ /* Disable PET, its not usable in pose mode yet [#32444] */
init_prop_edit = false;
break;
case TC_ARMATURE_VERTS:
@@ -1224,6 +1210,20 @@ void createTransData(bContext *C, TransInfo *t)
break;
case TC_OBJECT:
createTransObject(C, t);
+ /* Check if we're transforming the camera from the camera */
+ if ((t->spacetype == SPACE_VIEW3D) && (t->region->regiontype == RGN_TYPE_WINDOW)) {
+ View3D *v3d = t->view;
+ RegionView3D *rv3d = t->region->regiondata;
+ if ((rv3d->persp == RV3D_CAMOB) && v3d->camera) {
+ /* we could have a flag to easily check an object is being transformed */
+ if (v3d->camera->id.tag & LIB_TAG_DOIT) {
+ t->flag |= T_CAMERA;
+ }
+ }
+ else if (v3d->ob_center && v3d->ob_center->id.tag & LIB_TAG_DOIT) {
+ t->flag |= T_CAMERA;
+ }
+ }
break;
case TC_OBJECT_TEXSPACE:
createTransTexspace(t);
@@ -1292,18 +1292,9 @@ void createTransData(bContext *C, TransInfo *t)
* and are still added into transform data. */
sort_trans_data_selected_first(t);
}
- }
-
- /* exception... hackish, we want bonesize to use bone orientation matrix (ton) */
- if (t->mode == TFM_BONESIZE) {
- t->flag &= ~(T_EDIT | T_POINTS);
- t->flag |= T_POSE;
- t->obedit_type = -1;
- t->data_type = TC_NONE;
- FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- tc->poseobj = tc->obedit;
- tc->obedit = NULL;
+ if (!init_prop_edit) {
+ t->flag &= ~T_PROP_EDIT_ALL;
}
}
diff --git a/source/blender/editors/transform/transform_convert.h b/source/blender/editors/transform/transform_convert.h
index a94bd609d94..f7eea286983 100644
--- a/source/blender/editors/transform/transform_convert.h
+++ b/source/blender/editors/transform/transform_convert.h
@@ -47,7 +47,7 @@ bool clipUVTransform(TransInfo *t, float vec[2], const bool resize);
void clipUVData(TransInfo *t);
/* transform_convert_mesh.c */
-void trans_mesh_customdata_correction_init(TransInfo *t, struct TransDataContainer *tc);
+void trans_mesh_customdata_correction_init(TransInfo *t);
/* transform_convert_sequencer.c */
int transform_convert_sequencer_get_snap_bound(TransInfo *t);
diff --git a/source/blender/editors/transform/transform_convert_armature.c b/source/blender/editors/transform/transform_convert_armature.c
index 75b51b3d2c4..f721ed0b866 100644
--- a/source/blender/editors/transform/transform_convert_armature.c
+++ b/source/blender/editors/transform/transform_convert_armature.c
@@ -553,8 +553,7 @@ static void pose_mirror_info_init(PoseInitData_Mirror *pid,
/** \name Convert Armature
* \{ */
-static void add_pose_transdata(
- TransInfo *t, bPoseChannel *pchan, Object *ob, TransDataContainer *tc, TransData *td)
+static void add_pose_transdata(TransInfo *t, bPoseChannel *pchan, Object *ob, TransData *td)
{
Bone *bone = pchan->bone;
float pmat[3][3], omat[3][3];
@@ -667,20 +666,16 @@ static void add_pose_transdata(
mul_m3_m3m3(td->axismtx, omat, pmat);
normalize_m3(td->axismtx);
- if (ELEM(t->mode, TFM_BONESIZE, TFM_BONE_ENVELOPE_DIST)) {
- bArmature *arm = tc->poseobj->data;
-
- if ((t->mode == TFM_BONE_ENVELOPE_DIST) || (arm->drawtype == ARM_ENVELOPE)) {
- td->loc = NULL;
- td->val = &bone->dist;
- td->ival = bone->dist;
- }
- else {
- // abusive storage of scale in the loc pointer :)
- td->loc = &bone->xwidth;
- copy_v3_v3(td->iloc, td->loc);
- td->val = NULL;
- }
+ if (t->mode == TFM_BONE_ENVELOPE_DIST) {
+ td->loc = NULL;
+ td->val = &bone->dist;
+ td->ival = bone->dist;
+ }
+ else if (t->mode == TFM_BONESIZE) {
+ // abusive storage of scale in the loc pointer :)
+ td->loc = &bone->xwidth;
+ copy_v3_v3(td->iloc, td->loc);
+ td->val = NULL;
}
/* in this case we can do target-less IK grabbing */
@@ -836,7 +831,7 @@ void createTransPose(TransInfo *t)
td = tc->data;
LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) {
if (pchan->bone->flag & BONE_TRANSFORM) {
- add_pose_transdata(t, pchan, ob, tc, td);
+ add_pose_transdata(t, pchan, ob, td);
td++;
}
}
@@ -862,8 +857,6 @@ void createTransPose(TransInfo *t)
}
t->flag |= T_POSE;
- /* disable PET, its not usable in pose mode yet [#32444] */
- t->flag &= ~T_PROP_EDIT_ALL;
}
void createTransArmatureVerts(TransInfo *t)
@@ -988,7 +981,7 @@ void createTransArmatureVerts(TransInfo *t)
}
else if (ELEM(t->mode, TFM_BONESIZE, TFM_BONE_ENVELOPE_DIST)) {
if (ebo->flag & BONE_SELECTED) {
- if ((t->mode == TFM_BONE_ENVELOPE_DIST) || (arm->drawtype == ARM_ENVELOPE)) {
+ if (t->mode == TFM_BONE_ENVELOPE_DIST) {
td->loc = NULL;
td->val = &ebo->dist;
td->ival = ebo->dist;
diff --git a/source/blender/editors/transform/transform_convert_curve.c b/source/blender/editors/transform/transform_convert_curve.c
index 6c03f86f883..37e37072ed7 100644
--- a/source/blender/editors/transform/transform_convert_curve.c
+++ b/source/blender/editors/transform/transform_convert_curve.c
@@ -87,7 +87,7 @@ void createTransCurveVerts(TransInfo *t)
t->data_len_all = 0;
- /* Count control points (one per bez-triple) if any number of handles are selected.
+ /* Count control points (one per #BezTriple) if any number of handles are selected.
* Needed for #transform_around_single_fallback_ex. */
int data_len_all_pt = 0;
diff --git a/source/blender/editors/transform/transform_convert_mask.c b/source/blender/editors/transform/transform_convert_mask.c
index 66c27bca207..6f34c49bdac 100644
--- a/source/blender/editors/transform/transform_convert_mask.c
+++ b/source/blender/editors/transform/transform_convert_mask.c
@@ -149,7 +149,7 @@ static void MaskPointToTransData(Scene *scene,
/* CV coords are scaled by aspects. this is needed for rotations and
* proportional editing to be consistent with the stretched CV coords
- * that are displayed. this also means that for display and numinput,
+ * that are displayed. this also means that for display and number-input,
* and when the CV coords are flushed, these are converted each time */
mul_v2_m3v2(td2d->loc, parent_matrix, bezt->vec[i]);
td2d->loc[0] *= asp[0];
diff --git a/source/blender/editors/transform/transform_convert_mesh.c b/source/blender/editors/transform/transform_convert_mesh.c
index 27ddad262f7..1c286415ea6 100644
--- a/source/blender/editors/transform/transform_convert_mesh.c
+++ b/source/blender/editors/transform/transform_convert_mesh.c
@@ -50,10 +50,11 @@
/* Own include. */
#include "transform_convert.h"
-/* Used for both mirror epsilon and TD_MIRROR_EDGE_ */
-#define TRANSFORM_MAXDIST_MIRROR 0.00002f
+/* -------------------------------------------------------------------- */
+/** \name Island Creation
+ *
+ * \{ */
-/* when transforming islands */
struct TransIslandData {
float (*center)[3];
float (*axismtx)[3][3];
@@ -61,8 +62,186 @@ struct TransIslandData {
int *island_vert_map;
};
+static void editmesh_islands_info_calc(BMEditMesh *em,
+ const bool calc_single_islands,
+ const bool calc_island_center,
+ const bool calc_island_axismtx,
+ struct TransIslandData *r_island_data)
+{
+ BMesh *bm = em->bm;
+ char htype;
+ char itype;
+ int i;
+
+ /* group vars */
+ float(*center)[3] = NULL;
+ float(*axismtx)[3][3] = NULL;
+ int *groups_array;
+ int(*group_index)[2];
+ int group_tot;
+ void **ele_array;
+
+ int *vert_map;
+
+ if (em->selectmode & (SCE_SELECT_VERTEX | SCE_SELECT_EDGE)) {
+ groups_array = MEM_mallocN(sizeof(*groups_array) * bm->totedgesel, __func__);
+ group_tot = BM_mesh_calc_edge_groups(
+ bm, groups_array, &group_index, NULL, NULL, BM_ELEM_SELECT);
+
+ htype = BM_EDGE;
+ itype = BM_VERTS_OF_EDGE;
+ }
+ else { /* (bm->selectmode & SCE_SELECT_FACE) */
+ groups_array = MEM_mallocN(sizeof(*groups_array) * bm->totfacesel, __func__);
+ group_tot = BM_mesh_calc_face_groups(
+ bm, groups_array, &group_index, NULL, NULL, BM_ELEM_SELECT, BM_VERT);
+
+ htype = BM_FACE;
+ itype = BM_VERTS_OF_FACE;
+ }
+
+ if (calc_island_center) {
+ center = MEM_mallocN(sizeof(*center) * group_tot, __func__);
+ }
+
+ if (calc_island_axismtx) {
+ axismtx = MEM_mallocN(sizeof(*axismtx) * group_tot, __func__);
+ }
+
+ vert_map = MEM_mallocN(sizeof(*vert_map) * bm->totvert, __func__);
+ /* we shouldn't need this, but with incorrect selection flushing
+ * its possible we have a selected vertex that's not in a face,
+ * for now best not crash in that case. */
+ copy_vn_i(vert_map, bm->totvert, -1);
+
+ BM_mesh_elem_table_ensure(bm, htype);
+ ele_array = (htype == BM_FACE) ? (void **)bm->ftable : (void **)bm->etable;
+
+ BM_mesh_elem_index_ensure(bm, BM_VERT);
+
+ /* may be an edge OR a face array */
+ for (i = 0; i < group_tot; i++) {
+ BMEditSelection ese = {NULL};
+
+ const int fg_sta = group_index[i][0];
+ const int fg_len = group_index[i][1];
+ float co[3], no[3], tangent[3];
+ int j;
+
+ zero_v3(co);
+ zero_v3(no);
+ zero_v3(tangent);
+
+ ese.htype = htype;
+
+ /* loop on each face or edge in this group:
+ * - assign r_vert_map
+ * - calculate (co, no)
+ */
+ for (j = 0; j < fg_len; j++) {
+ ese.ele = ele_array[groups_array[fg_sta + j]];
+
+ if (center) {
+ float tmp_co[3];
+ BM_editselection_center(&ese, tmp_co);
+ add_v3_v3(co, tmp_co);
+ }
+
+ if (axismtx) {
+ float tmp_no[3], tmp_tangent[3];
+ BM_editselection_normal(&ese, tmp_no);
+ BM_editselection_plane(&ese, tmp_tangent);
+ add_v3_v3(no, tmp_no);
+ add_v3_v3(tangent, tmp_tangent);
+ }
+
+ {
+ /* setup vertex map */
+ BMIter iter;
+ BMVert *v;
+
+ /* connected edge-verts */
+ BM_ITER_ELEM (v, &iter, ese.ele, itype) {
+ vert_map[BM_elem_index_get(v)] = i;
+ }
+ }
+ }
+
+ if (center) {
+ mul_v3_v3fl(center[i], co, 1.0f / (float)fg_len);
+ }
+
+ if (axismtx) {
+ if (createSpaceNormalTangent(axismtx[i], no, tangent)) {
+ /* pass */
+ }
+ else {
+ if (normalize_v3(no) != 0.0f) {
+ axis_dominant_v3_to_m3(axismtx[i], no);
+ invert_m3(axismtx[i]);
+ }
+ else {
+ unit_m3(axismtx[i]);
+ }
+ }
+ }
+ }
+
+ MEM_freeN(groups_array);
+ MEM_freeN(group_index);
+
+ /* for PET we need islands of 1 so connected vertices can use it with V3D_AROUND_LOCAL_ORIGINS */
+ if (calc_single_islands) {
+ BMIter viter;
+ BMVert *v;
+ int group_tot_single = 0;
+
+ BM_ITER_MESH_INDEX (v, &viter, bm, BM_VERTS_OF_MESH, i) {
+ if (BM_elem_flag_test(v, BM_ELEM_SELECT) && (vert_map[i] == -1)) {
+ group_tot_single += 1;
+ }
+ }
+
+ if (group_tot_single != 0) {
+ if (center) {
+ center = MEM_reallocN(center, sizeof(*center) * (group_tot + group_tot_single));
+ }
+ if (axismtx) {
+ axismtx = MEM_reallocN(axismtx, sizeof(*axismtx) * (group_tot + group_tot_single));
+ }
+
+ BM_ITER_MESH_INDEX (v, &viter, bm, BM_VERTS_OF_MESH, i) {
+ if (BM_elem_flag_test(v, BM_ELEM_SELECT) && (vert_map[i] == -1)) {
+ vert_map[i] = group_tot;
+ if (center) {
+ copy_v3_v3(center[group_tot], v->co);
+ }
+ if (axismtx) {
+ if (is_zero_v3(v->no) != 0.0f) {
+ axis_dominant_v3_to_m3(axismtx[group_tot], v->no);
+ invert_m3(axismtx[group_tot]);
+ }
+ else {
+ unit_m3(axismtx[group_tot]);
+ }
+ }
+
+ group_tot += 1;
+ }
+ }
+ }
+ }
+
+ r_island_data->axismtx = axismtx;
+ r_island_data->center = center;
+ r_island_data->island_tot = group_tot;
+ r_island_data->island_vert_map = vert_map;
+}
+
+/** \} */
+
/* -------------------------------------------------------------------- */
-/** \name Edit Mesh Verts Transform Creation
+/** \name Connectivity Distance for Proportional Editing
*
* \{ */
@@ -243,171 +422,25 @@ static void editmesh_set_connectivity_distance(BMesh *bm,
}
}
-static void editmesh_islands_info_calc(BMEditMesh *em,
- const bool calc_single_islands,
- const bool calc_island_axismtx,
- struct TransIslandData *r_island_data)
-{
- BMesh *bm = em->bm;
- char htype;
- char itype;
- int i;
-
- /* group vars */
- float(*center)[3];
- float(*axismtx)[3][3] = NULL;
- int *groups_array;
- int(*group_index)[2];
- int group_tot;
- void **ele_array;
-
- int *vert_map;
-
- if (em->selectmode & (SCE_SELECT_VERTEX | SCE_SELECT_EDGE)) {
- groups_array = MEM_mallocN(sizeof(*groups_array) * bm->totedgesel, __func__);
- group_tot = BM_mesh_calc_edge_groups(
- bm, groups_array, &group_index, NULL, NULL, BM_ELEM_SELECT);
-
- htype = BM_EDGE;
- itype = BM_VERTS_OF_EDGE;
- }
- else { /* (bm->selectmode & SCE_SELECT_FACE) */
- groups_array = MEM_mallocN(sizeof(*groups_array) * bm->totfacesel, __func__);
- group_tot = BM_mesh_calc_face_groups(
- bm, groups_array, &group_index, NULL, NULL, BM_ELEM_SELECT, BM_VERT);
-
- htype = BM_FACE;
- itype = BM_VERTS_OF_FACE;
- }
-
- center = MEM_mallocN(sizeof(*center) * group_tot, __func__);
-
- if (calc_island_axismtx) {
- axismtx = MEM_mallocN(sizeof(*axismtx) * group_tot, __func__);
- }
-
- vert_map = MEM_mallocN(sizeof(*vert_map) * bm->totvert, __func__);
- /* we shouldn't need this, but with incorrect selection flushing
- * its possible we have a selected vertex that's not in a face,
- * for now best not crash in that case. */
- copy_vn_i(vert_map, bm->totvert, -1);
-
- BM_mesh_elem_table_ensure(bm, htype);
- ele_array = (htype == BM_FACE) ? (void **)bm->ftable : (void **)bm->etable;
-
- BM_mesh_elem_index_ensure(bm, BM_VERT);
-
- /* may be an edge OR a face array */
- for (i = 0; i < group_tot; i++) {
- BMEditSelection ese = {NULL};
-
- const int fg_sta = group_index[i][0];
- const int fg_len = group_index[i][1];
- float co[3], no[3], tangent[3];
- int j;
-
- zero_v3(co);
- zero_v3(no);
- zero_v3(tangent);
-
- ese.htype = htype;
-
- /* loop on each face or edge in this group:
- * - assign r_vert_map
- * - calculate (co, no)
- */
- for (j = 0; j < fg_len; j++) {
- float tmp_co[3], tmp_no[3], tmp_tangent[3];
-
- ese.ele = ele_array[groups_array[fg_sta + j]];
-
- BM_editselection_center(&ese, tmp_co);
- add_v3_v3(co, tmp_co);
-
- if (axismtx) {
- BM_editselection_normal(&ese, tmp_no);
- BM_editselection_plane(&ese, tmp_tangent);
- add_v3_v3(no, tmp_no);
- add_v3_v3(tangent, tmp_tangent);
- }
-
- {
- /* setup vertex map */
- BMIter iter;
- BMVert *v;
-
- /* connected edge-verts */
- BM_ITER_ELEM (v, &iter, ese.ele, itype) {
- vert_map[BM_elem_index_get(v)] = i;
- }
- }
- }
-
- mul_v3_v3fl(center[i], co, 1.0f / (float)fg_len);
-
- if (axismtx) {
- if (createSpaceNormalTangent(axismtx[i], no, tangent)) {
- /* pass */
- }
- else {
- if (normalize_v3(no) != 0.0f) {
- axis_dominant_v3_to_m3(axismtx[i], no);
- invert_m3(axismtx[i]);
- }
- else {
- unit_m3(axismtx[i]);
- }
- }
- }
- }
-
- MEM_freeN(groups_array);
- MEM_freeN(group_index);
-
- /* for PET we need islands of 1 so connected vertices can use it with V3D_AROUND_LOCAL_ORIGINS */
- if (calc_single_islands) {
- BMIter viter;
- BMVert *v;
- int group_tot_single = 0;
-
- BM_ITER_MESH_INDEX (v, &viter, bm, BM_VERTS_OF_MESH, i) {
- if (BM_elem_flag_test(v, BM_ELEM_SELECT) && (vert_map[i] == -1)) {
- group_tot_single += 1;
- }
- }
-
- if (group_tot_single != 0) {
- center = MEM_reallocN(center, sizeof(*center) * (group_tot + group_tot_single));
- if (axismtx) {
- axismtx = MEM_reallocN(axismtx, sizeof(*axismtx) * (group_tot + group_tot_single));
- }
+/** \} */
- BM_ITER_MESH_INDEX (v, &viter, bm, BM_VERTS_OF_MESH, i) {
- if (BM_elem_flag_test(v, BM_ELEM_SELECT) && (vert_map[i] == -1)) {
- vert_map[i] = group_tot;
- copy_v3_v3(center[group_tot], v->co);
+/* -------------------------------------------------------------------- */
+/** \name TransDataMirror Creation
+ *
+ * \{ */
- if (axismtx) {
- if (is_zero_v3(v->no) != 0.0f) {
- axis_dominant_v3_to_m3(axismtx[group_tot], v->no);
- invert_m3(axismtx[group_tot]);
- }
- else {
- unit_m3(axismtx[group_tot]);
- }
- }
+/* Used for both mirror epsilon and TD_MIRROR_EDGE_ */
+#define TRANSFORM_MAXDIST_MIRROR 0.00002f
- group_tot += 1;
- }
- }
- }
- }
+struct MirrorDataVert {
+ int index;
+ int flag;
+};
- r_island_data->axismtx = axismtx;
- r_island_data->center = center;
- r_island_data->island_tot = group_tot;
- r_island_data->island_vert_map = vert_map;
-}
+struct TransMirrorData {
+ struct MirrorDataVert *vert_map;
+ int mirror_elem_len;
+};
static bool is_in_quadrant_v3(const float co[3], const int quadrant[3], const float epsilon)
{
@@ -423,172 +456,124 @@ static bool is_in_quadrant_v3(const float co[3], const int quadrant[3], const fl
return true;
}
-static TransDataMirror *editmesh_mirror_data_calc(BMEditMesh *em,
- bool use_select,
- const bool use_topology,
- const bool mirror_axis[3],
- int *r_mirror_data_len,
- BLI_bitmap **r_mirror_bitmap)
+static void editmesh_mirror_data_calc(BMEditMesh *em,
+ const bool use_select,
+ const bool use_topology,
+ const bool mirror_axis[3],
+ struct TransMirrorData *r_mirror_data)
{
- BMesh *bm = em->bm;
- int *index[3] = {NULL};
- int i;
-
- bool test_selected_only = use_select && (mirror_axis[0] + mirror_axis[1] + mirror_axis[2]) == 1;
- for (i = 0; i < 3; i++) {
- if (mirror_axis[i]) {
- index[i] = MEM_mallocN(bm->totvert * sizeof(int), __func__);
- EDBM_verts_mirror_cache_begin_ex(
- em, i, false, test_selected_only, use_topology, TRANSFORM_MAXDIST_MIRROR, index[i]);
- }
- }
+ struct MirrorDataVert *vert_map;
+ BMesh *bm = em->bm;
BMVert *eve;
BMIter iter;
+ int i, flag, totvert = bm->totvert;
- int quadrant[3];
- {
- float select_sum[3] = {0};
- BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
- if (BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
- continue;
- }
- if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
- add_v3_v3(select_sum, eve->co);
- }
- }
-
- for (i = 0; i < 3; i++) {
- if (mirror_axis[i]) {
- quadrant[i] = select_sum[i] >= 0.0f ? 1 : -1;
- }
- else {
- quadrant[i] = 0;
- }
- }
- }
+ vert_map = MEM_mallocN(totvert * sizeof(*vert_map), __func__);
- /* Tag only elements that will be transformed within the quadrant. */
+ float select_sum[3] = {0};
BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
+ vert_map[i] = (struct MirrorDataVert){-1, 0};
if (BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
continue;
}
- if ((!use_select || BM_elem_flag_test(eve, BM_ELEM_SELECT)) &&
- is_in_quadrant_v3(eve->co, quadrant, TRANSFORM_MAXDIST_MIRROR)) {
- BM_elem_flag_enable(eve, BM_ELEM_TAG);
- BM_elem_index_set(eve, i);
+ if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
+ add_v3_v3(select_sum, eve->co);
+ }
+ }
+
+ /* Tag only elements that will be transformed within the quadrant. */
+ int quadrant[3];
+ for (int a = 0; a < 3; a++) {
+ if (mirror_axis[a]) {
+ quadrant[a] = select_sum[a] >= 0.0f ? 1 : -1;
}
else {
- BM_elem_flag_disable(eve, BM_ELEM_TAG);
- BM_elem_index_set(eve, -1);
+ quadrant[a] = 0;
}
}
+ uint mirror_elem_len = 0;
+ int *index[3] = {NULL, NULL, NULL};
+ bool test_selected_only = use_select && (mirror_axis[0] + mirror_axis[1] + mirror_axis[2]) == 1;
for (int a = 0; a < 3; a++) {
- int *index_iter = index[a];
- if (index_iter == NULL) {
+ if (!mirror_axis[a]) {
continue;
}
+
+ index[a] = MEM_mallocN(totvert * sizeof(*index[a]), __func__);
+ EDBM_verts_mirror_cache_begin_ex(
+ em, a, false, test_selected_only, use_topology, TRANSFORM_MAXDIST_MIRROR, index[a]);
+
+ flag = TD_MIRROR_X << a;
BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
+ int i_mirr = index[a][i];
+ if (i_mirr < 0) {
+ continue;
+ }
if (BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
continue;
}
- if (test_selected_only && !BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
+ if (use_select && !BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
continue;
}
- int elem_index = BM_elem_index_get(eve);
- if (elem_index != -1) {
- int i_mirr = index_iter[i];
- if (i_mirr >= 0) {
- BMVert *vmir = BM_vert_at_index(bm, i_mirr);
- BM_elem_index_set(vmir, elem_index);
-
- /* The slot of this element in the index array no longer needs to be read.
- * Use to set the mirror sign. */
- if (index[0] && a > 0) {
- index[0][i_mirr] = index[0][i];
- }
- if (index[1] && a > 1) {
- index[1][i_mirr] = index[1][i];
- }
- /* Use -2 to differ from -1, but both can work. */
- index_iter[i_mirr] = -2;
- }
+ if (!is_in_quadrant_v3(eve->co, quadrant, TRANSFORM_MAXDIST_MIRROR)) {
+ continue;
}
- }
- }
- /* Count mirror elements. */
- uint mirror_elem_len = 0;
- BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
- if (BM_elem_flag_test(eve, BM_ELEM_HIDDEN | BM_ELEM_TAG)) {
- /* Not a mirror element. */
- BM_elem_index_set(eve, -1);
- continue;
- }
- int elem_index = BM_elem_index_get(eve);
- if (elem_index != -1) {
+ vert_map[i_mirr] = (struct MirrorDataVert){i, flag};
mirror_elem_len++;
}
}
- TransDataMirror *td_mirror_iter, *td_mirror = NULL;
- if (mirror_elem_len != 0) {
- td_mirror = MEM_mallocN(mirror_elem_len * sizeof(*td_mirror), __func__);
- td_mirror_iter = &td_mirror[0];
-
- *r_mirror_bitmap = BLI_BITMAP_NEW(bm->totvert, __func__);
-
- BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
- int elem_index = BM_elem_index_get(eve);
- if (elem_index != -1) {
- BMVert *v_src = BM_vert_at_index(bm, elem_index);
+ if (mirror_elem_len) {
+ for (int a = 0; a < 3; a++) {
+ if (!mirror_axis[a]) {
+ continue;
+ }
- int flag = 0;
- if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
- flag |= TD_SELECTED;
- }
- if (index[0] && index[0][i] == -2) {
- flag |= TD_MIRROR_X;
- }
- if (index[1] && index[1][i] == -2) {
- flag |= TD_MIRROR_Y;
+ flag = TD_MIRROR_X << a;
+ for (i = 0; i < totvert; i++) {
+ int i_mirr = index[a][i];
+ if (i_mirr < 0) {
+ continue;
}
- if (index[2] && index[2][i] == -2) {
- flag |= TD_MIRROR_Z;
+ if (vert_map[i].index != -1 && !(vert_map[i].flag & flag)) {
+ if (vert_map[i_mirr].index == -1) {
+ mirror_elem_len++;
+ }
+ vert_map[i_mirr].index = vert_map[i].index;
+ vert_map[i_mirr].flag |= vert_map[i].flag | flag;
}
-
- td_mirror_iter->extra = eve;
- td_mirror_iter->loc = eve->co;
- copy_v3_v3(td_mirror_iter->iloc, eve->co);
- td_mirror_iter->flag = flag;
- td_mirror_iter->loc_src = v_src->co;
- /** `center` will be set in the main createTransEditVerts loop.
- * copy_v3_v3(td_mirror_iter->center, eve->co); */
-
- td_mirror_iter++;
-
- BLI_BITMAP_ENABLE(*r_mirror_bitmap, i);
}
}
}
+ else {
+ MEM_freeN(vert_map);
+ vert_map = NULL;
+ }
MEM_SAFE_FREE(index[0]);
MEM_SAFE_FREE(index[1]);
MEM_SAFE_FREE(index[2]);
- bm->elem_index_dirty |= BM_VERT;
- *r_mirror_data_len = mirror_elem_len;
- return td_mirror;
+ r_mirror_data->vert_map = vert_map;
+ r_mirror_data->mirror_elem_len = mirror_elem_len;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Edit Mesh Verts Transform Creation
+ *
+ * \{ */
+
static void transdata_center_get(const struct TransIslandData *island_data,
const int island_index,
- const bool no_island_center,
const float iloc[3],
float r_center[3])
{
- if (island_index != -1 && !no_island_center) {
+ if (island_data->center && island_index != -1) {
copy_v3_v3(r_center, island_data->center[island_index]);
}
else {
@@ -604,8 +589,7 @@ static void VertsToTransData(TransInfo *t,
BMVert *eve,
float *bweight,
const struct TransIslandData *island_data,
- const int island_index,
- const bool no_island_center)
+ const int island_index)
{
float *no, _no[3];
BLI_assert(BM_elem_flag_test(eve, BM_ELEM_HIDDEN) == 0);
@@ -626,7 +610,7 @@ static void VertsToTransData(TransInfo *t,
no = eve->no;
}
- transdata_center_get(island_data, island_index, no_island_center, td->iloc, td->center);
+ transdata_center_get(island_data, island_index, td->iloc, td->center);
if ((island_index != -1) && island_data->axismtx) {
copy_m3_m3(td->axismtx, island_data->axismtx[island_index]);
@@ -676,31 +660,12 @@ void createTransEditVerts(TransInfo *t)
BMesh *bm = em->bm;
BMVert *eve;
BMIter iter;
- float(*mappedcos)[3] = NULL, (*quats)[4] = NULL;
- float mtx[3][3], smtx[3][3], (*defmats)[3][3] = NULL, (*defcos)[3] = NULL;
- float *dists = NULL;
+ float mtx[3][3], smtx[3][3];
int a;
const int prop_mode = (t->flag & T_PROP_EDIT) ? (t->flag & T_PROP_EDIT_ALL) : 0;
- int cd_vert_bweight_offset = -1;
struct TransIslandData island_data = {NULL};
-
- /* Snap rotation along normal needs a common axis for whole islands,
- * otherwise one get random crazy results, see T59104.
- * However, we do not want to use the island center for the pivot/translation reference. */
- const bool is_snap_rotate = ((t->mode == TFM_TRANSLATION) &&
- /* There is not guarantee that snapping
- * is initialized yet at this point... */
- (usingSnappingNormal(t) ||
- (t->settings->snap_flag & SCE_SNAP_ROTATE) != 0) &&
- (t->around != V3D_AROUND_LOCAL_ORIGINS));
- /* Even for translation this is needed because of island-orientation, see: T51651. */
- const bool is_island_center = (t->around == V3D_AROUND_LOCAL_ORIGINS) || is_snap_rotate;
- /* Original index of our connected vertex when connected distances are calculated.
- * Optional, allocate if needed. */
- int *dists_index = NULL;
-
- BLI_bitmap *mirror_bitmap = NULL;
+ struct TransMirrorData mirror_data = {NULL};
/**
* Quick check if we can transform.
@@ -711,20 +676,7 @@ void createTransEditVerts(TransInfo *t)
/* Support other objects using PET to adjust these, unless connected is enabled. */
if ((!prop_mode || (prop_mode & T_PROP_CONNECTED)) && (bm->totvertsel == 0)) {
- goto cleanup;
- }
-
- if (t->mode == TFM_BWEIGHT) {
- BM_mesh_cd_flag_ensure(bm, BKE_mesh_from_object(tc->obedit), ME_CDFLAG_VERT_BWEIGHT);
- cd_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT);
- }
-
- if (tc->use_mirror_axis_any) {
- bool use_topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0;
- bool use_select = (t->flag & T_PROP_EDIT) == 0;
- bool mirror_axis[3] = {tc->use_mirror_axis_x, tc->use_mirror_axis_y, tc->use_mirror_axis_z};
- tc->data_mirror = editmesh_mirror_data_calc(
- em, use_select, use_topology, mirror_axis, &tc->data_mirror_len, &mirror_bitmap);
+ continue;
}
int data_len = 0;
@@ -734,44 +686,41 @@ void createTransEditVerts(TransInfo *t)
data_len++;
}
}
-
- if (data_len == 0) {
- goto cleanup;
- }
-
- /* allocating scratch arrays */
- if (prop_mode & T_PROP_CONNECTED) {
- dists = MEM_mallocN(em->bm->totvert * sizeof(float), __func__);
- if (is_island_center) {
- dists_index = MEM_mallocN(em->bm->totvert * sizeof(int), __func__);
- }
- }
}
else {
data_len = bm->totvertsel;
}
- if (mirror_bitmap) {
- BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, a) {
- if (prop_mode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
- if (BLI_BITMAP_TEST(mirror_bitmap, a)) {
- data_len--;
- }
- }
- }
+ if (data_len == 0) {
+ continue;
}
- BLI_assert(data_len != 0);
+ /* Snap rotation along normal needs a common axis for whole islands,
+ * otherwise one get random crazy results, see T59104.
+ * However, we do not want to use the island center for the pivot/translation reference. */
+ const bool is_snap_rotate = ((t->mode == TFM_TRANSLATION) &&
+ /* There is not guarantee that snapping
+ * is initialized yet at this point... */
+ (usingSnappingNormal(t) ||
+ (t->settings->snap_flag & SCE_SNAP_ROTATE) != 0) &&
+ (t->around != V3D_AROUND_LOCAL_ORIGINS));
- tc->data_len = data_len;
- tc->data = MEM_callocN(data_len * sizeof(TransData), "TransObData(Mesh EditMode)");
- if (ELEM(t->mode, TFM_SKIN_RESIZE, TFM_SHRINKFATTEN)) {
- /* warning, this is overkill, we only need 2 extra floats,
- * but this stores loads of extra stuff, for TFM_SHRINKFATTEN its even more overkill
- * since we may not use the 'alt' transform mode to maintain shell thickness,
- * but with generic transform code its hard to lazy init vars */
- tx = tc->data_ext = MEM_callocN(tc->data_len * sizeof(TransDataExtension),
- "TransObData ext");
+ /* Even for translation this is needed because of island-orientation, see: T51651. */
+ const bool is_island_center = (t->around == V3D_AROUND_LOCAL_ORIGINS) || is_snap_rotate;
+ if (is_island_center) {
+ /* In this specific case, near-by vertices will need to know
+ * the island of the nearest connected vertex. */
+ const bool calc_single_islands = ((prop_mode & T_PROP_CONNECTED) &&
+ (t->around == V3D_AROUND_LOCAL_ORIGINS) &&
+ (em->selectmode & SCE_SELECT_VERTEX));
+
+ const bool calc_island_center = !is_snap_rotate;
+ /* The island axismtx is only necessary in some modes.
+ * TODO(Germano): Extend the list to exclude other modes. */
+ const bool calc_island_axismtx = !ELEM(t->mode, TFM_SHRINKFATTEN);
+
+ editmesh_islands_info_calc(
+ em, calc_single_islands, calc_island_center, calc_island_axismtx, &island_data);
}
copy_m3_m4(mtx, tc->obedit->obmat);
@@ -779,26 +728,58 @@ void createTransEditVerts(TransInfo *t)
* matrix inversion still works and we can still moving along the other */
pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON);
+ /* Original index of our connected vertex when connected distances are calculated.
+ * Optional, allocate if needed. */
+ int *dists_index = NULL;
+ float *dists = NULL;
if (prop_mode & T_PROP_CONNECTED) {
+ dists = MEM_mallocN(bm->totvert * sizeof(float), __func__);
+ if (is_island_center) {
+ dists_index = MEM_mallocN(bm->totvert * sizeof(int), __func__);
+ }
editmesh_set_connectivity_distance(em->bm, mtx, dists, dists_index);
}
- if (is_island_center) {
- /* In this specific case, near-by vertices will need to know
- * the island of the nearest connected vertex. */
- const bool calc_single_islands = ((prop_mode & T_PROP_CONNECTED) &&
- (t->around == V3D_AROUND_LOCAL_ORIGINS) &&
- (em->selectmode & SCE_SELECT_VERTEX));
+ /* Create TransDataMirror. */
+ if (tc->use_mirror_axis_any) {
+ bool use_topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0;
+ bool use_select = (t->flag & T_PROP_EDIT) == 0;
+ bool mirror_axis[3] = {tc->use_mirror_axis_x, tc->use_mirror_axis_y, tc->use_mirror_axis_z};
+ editmesh_mirror_data_calc(em, use_select, use_topology, mirror_axis, &mirror_data);
- /* The island axismtx is only necessary in some modes.
- * TODO(Germano): Extend the list to exclude other modes. */
- const bool calc_island_axismtx = !ELEM(t->mode, TFM_SHRINKFATTEN);
+ if (mirror_data.vert_map) {
+ tc->data_mirror_len = mirror_data.mirror_elem_len;
+ tc->data_mirror = MEM_mallocN(mirror_data.mirror_elem_len * sizeof(*tc->data_mirror),
+ __func__);
+
+ BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, a) {
+ if (prop_mode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
+ if (mirror_data.vert_map[a].index != -1) {
+ data_len--;
+ }
+ }
+ }
+ }
+ }
- editmesh_islands_info_calc(em, calc_single_islands, calc_island_axismtx, &island_data);
+ /* Create TransData. */
+ BLI_assert(data_len >= 1);
+ tc->data_len = data_len;
+ tc->data = MEM_callocN(data_len * sizeof(TransData), "TransObData(Mesh EditMode)");
+ if (ELEM(t->mode, TFM_SKIN_RESIZE, TFM_SHRINKFATTEN)) {
+ /* warning, this is overkill, we only need 2 extra floats,
+ * but this stores loads of extra stuff, for TFM_SHRINKFATTEN its even more overkill
+ * since we may not use the 'alt' transform mode to maintain shell thickness,
+ * but with generic transform code its hard to lazy init vars */
+ tx = tc->data_ext = MEM_callocN(tc->data_len * sizeof(TransDataExtension),
+ "TransObData ext");
}
/* detect CrazySpace [tm] */
+ float(*quats)[4] = NULL;
+ float(*defmats)[3][3] = NULL;
if (BKE_modifiers_get_cage_index(t->scene, tc->obedit, NULL, 1) != -1) {
+ float(*defcos)[3] = NULL;
int totleft = -1;
if (BKE_modifiers_is_correctable_deformed(t->scene, tc->obedit)) {
BKE_scene_graph_evaluated_ensure(t->depsgraph, CTX_data_main(t->context));
@@ -813,16 +794,17 @@ void createTransEditVerts(TransInfo *t)
t->depsgraph, scene_eval, obedit_eval, em_eval, &defmats, &defcos);
}
- /* if we still have more modifiers, also do crazyspace
- * correction with quats, relative to the coordinates after
- * the modifiers that support deform matrices (defcos) */
+ /* If we still have more modifiers, also do crazy-space
+ * correction with \a quats, relative to the coordinates after
+ * the modifiers that support deform matrices \a defcos. */
-#if 0 /* TODO, fix crazyspace+extrude so it can be enabled for general use - campbell */
+#if 0 /* TODO, fix crazy-space & extrude so it can be enabled for general use - campbell */
if ((totleft > 0) || (totleft == -1))
#else
if (totleft > 0)
#endif
{
+ float(*mappedcos)[3] = NULL;
mappedcos = BKE_crazyspace_get_mapped_editverts(t->depsgraph, tc->obedit);
quats = MEM_mallocN(em->bm->totvert * sizeof(*quats), "crazy quats");
BKE_crazyspace_set_quats_editmesh(em, defcos, mappedcos, quats, !prop_mode);
@@ -836,6 +818,12 @@ void createTransEditVerts(TransInfo *t)
}
}
+ int cd_vert_bweight_offset = -1;
+ if (t->mode == TFM_BWEIGHT) {
+ BM_mesh_cd_flag_ensure(bm, BKE_mesh_from_object(tc->obedit), ME_CDFLAG_VERT_BWEIGHT);
+ cd_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT);
+ }
+
TransData *tob = tc->data;
TransDataMirror *td_mirror = tc->data_mirror;
BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, a) {
@@ -849,9 +837,20 @@ void createTransEditVerts(TransInfo *t)
island_index = island_data.island_vert_map[connected_index];
}
- if (mirror_bitmap && BLI_BITMAP_TEST(mirror_bitmap, a)) {
- transdata_center_get(
- &island_data, island_index, is_snap_rotate, td_mirror->iloc, td_mirror->center);
+ if (mirror_data.vert_map && mirror_data.vert_map[a].index != -1) {
+ int elem_index = mirror_data.vert_map[a].index;
+ BMVert *v_src = BM_vert_at_index(bm, elem_index);
+
+ if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
+ mirror_data.vert_map[a].flag |= TD_SELECTED;
+ }
+
+ td_mirror->extra = eve;
+ td_mirror->loc = eve->co;
+ copy_v3_v3(td_mirror->iloc, eve->co);
+ td_mirror->flag = mirror_data.vert_map[a].flag;
+ td_mirror->loc_src = v_src->co;
+ transdata_center_get(&island_data, island_index, td_mirror->iloc, td_mirror->center);
td_mirror++;
}
@@ -862,8 +861,7 @@ void createTransEditVerts(TransInfo *t)
/* Do not use the island center in case we are using islands
* only to get axis for snap/rotate to normal... */
- VertsToTransData(
- t, tob, tx, em, eve, bweight, &island_data, island_index, is_snap_rotate);
+ VertsToTransData(t, tob, tx, em, eve, bweight, &island_data, island_index);
if (tx) {
tx++;
}
@@ -933,17 +931,15 @@ void createTransEditVerts(TransInfo *t)
if (island_data.center) {
MEM_freeN(island_data.center);
}
-
if (island_data.axismtx) {
MEM_freeN(island_data.axismtx);
}
-
if (island_data.island_vert_map) {
MEM_freeN(island_data.island_vert_map);
}
-
- cleanup:
- /* crazy space free */
+ if (mirror_data.vert_map) {
+ MEM_freeN(mirror_data.vert_map);
+ }
if (quats) {
MEM_freeN(quats);
}
@@ -956,9 +952,6 @@ void createTransEditVerts(TransInfo *t)
if (dists_index) {
MEM_freeN(dists_index);
}
- if (mirror_bitmap) {
- MEM_freeN(mirror_bitmap);
- }
}
}
@@ -1075,99 +1068,118 @@ static void create_trans_vert_customdata_layer(BMVert *v,
BLI_ghash_insert(tcld->origverts, v, r_tcld_vert);
}
-void trans_mesh_customdata_correction_init(TransInfo *t, TransDataContainer *tc)
+static void trans_mesh_customdata_correction_init_container(TransInfo *t, TransDataContainer *tc)
{
if (tc->custom.type.data) {
/* Custom data correction has initiated before. */
BLI_assert(tc->custom.type.free_cb == trans_mesh_customdata_free_cb);
return;
}
- int i;
+
+ if (!ELEM(t->mode,
+ TFM_TRANSLATION,
+ TFM_ROTATION,
+ TFM_RESIZE,
+ TFM_TOSPHERE,
+ TFM_SHEAR,
+ TFM_BEND,
+ TFM_SHRINKFATTEN,
+ TFM_TRACKBALL,
+ TFM_PUSHPULL,
+ TFM_ALIGN)) {
+ /* Currently only modes that change the position of vertices are supported. */
+ return;
+ }
BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
BMesh *bm = em->bm;
- bool use_origfaces;
- int cd_loop_mdisp_offset;
- {
- const bool has_layer_math = CustomData_has_math(&bm->ldata);
- cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS);
- if ((t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT) &&
- /* don't do this at all for non-basis shape keys, too easy to
- * accidentally break uv maps or vertex colors then */
- (bm->shapenr <= 1) && (has_layer_math || (cd_loop_mdisp_offset != -1))) {
- use_origfaces = true;
- }
- else {
- use_origfaces = false;
- cd_loop_mdisp_offset = -1;
- }
+ if (bm->shapenr > 1) {
+ /* Don't do this at all for non-basis shape keys, too easy to
+ * accidentally break uv maps or vertex colors then */
+ /* create copies of faces for customdata projection. */
+ return;
}
- if (use_origfaces) {
- /* create copies of faces for customdata projection */
- bmesh_edit_begin(bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES);
-
- struct GHash *origfaces = BLI_ghash_ptr_new(__func__);
- struct BMesh *bm_origfaces = BM_mesh_create(&bm_mesh_allocsize_default,
- &((struct BMeshCreateParams){
- .use_toolflags = false,
- }));
-
- /* we need to have matching customdata */
- BM_mesh_copy_init_customdata(bm_origfaces, bm, NULL);
-
- int *layer_math_map = NULL;
- int layer_index_dst = 0;
- {
- /* TODO: We don't need `sod->layer_math_map` when there are no loops linked
- * to one of the sliding vertices. */
- if (CustomData_has_math(&bm->ldata)) {
- /* over alloc, only 'math' layers are indexed */
- layer_math_map = MEM_mallocN(bm->ldata.totlayer * sizeof(int), __func__);
- for (i = 0; i < bm->ldata.totlayer; i++) {
- if (CustomData_layer_has_math(&bm->ldata, i)) {
- layer_math_map[layer_index_dst++] = i;
- }
+ const bool has_layer_math = CustomData_has_math(&bm->ldata);
+ const int cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS);
+ if (!has_layer_math && (cd_loop_mdisp_offset == -1)) {
+ return;
+ }
+
+ bmesh_edit_begin(bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES);
+
+ struct GHash *origfaces = BLI_ghash_ptr_new(__func__);
+ struct BMesh *bm_origfaces = BM_mesh_create(&bm_mesh_allocsize_default,
+ &((struct BMeshCreateParams){
+ .use_toolflags = false,
+ }));
+
+ /* we need to have matching customdata */
+ BM_mesh_copy_init_customdata(bm_origfaces, bm, NULL);
+
+ int *layer_math_map = NULL;
+ int layer_math_map_len = 0;
+ {
+ /* TODO: We don't need `sod->layer_math_map` when there are no loops linked
+ * to one of the sliding vertices. */
+ if (has_layer_math) {
+ /* over alloc, only 'math' layers are indexed */
+ layer_math_map = MEM_mallocN(bm->ldata.totlayer * sizeof(int), __func__);
+ for (int i = 0; i < bm->ldata.totlayer; i++) {
+ if (CustomData_layer_has_math(&bm->ldata, i)) {
+ layer_math_map[layer_math_map_len++] = i;
}
- BLI_assert(layer_index_dst != 0);
}
+ BLI_assert(layer_math_map_len != 0);
}
+ }
- struct TransCustomDataLayer *tcld;
- tc->custom.type.data = tcld = MEM_mallocN(sizeof(*tcld), __func__);
- tc->custom.type.free_cb = trans_mesh_customdata_free_cb;
-
- tcld->bm = bm;
- tcld->origfaces = origfaces;
- tcld->bm_origfaces = bm_origfaces;
- tcld->cd_loop_mdisp_offset = cd_loop_mdisp_offset;
- tcld->layer_math_map = layer_math_map;
- tcld->layer_math_map_num = layer_index_dst;
- tcld->arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
+ struct TransCustomDataLayer *tcld = MEM_mallocN(sizeof(*tcld), __func__);
+ int data_len = tc->data_len + tc->data_mirror_len;
- int data_len = tc->data_len + tc->data_mirror_len;
- struct GHash *origverts = BLI_ghash_ptr_new_ex(__func__, data_len);
- tcld->origverts = origverts;
+ tcld->bm = bm;
+ tcld->origfaces = origfaces;
+ tcld->bm_origfaces = bm_origfaces;
+ tcld->cd_loop_mdisp_offset = cd_loop_mdisp_offset;
+ tcld->layer_math_map = layer_math_map;
+ tcld->layer_math_map_num = layer_math_map_len;
+ tcld->arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
+ tcld->origverts = BLI_ghash_ptr_new_ex(__func__, data_len);
+ tcld->data = BLI_memarena_alloc(tcld->arena, data_len * sizeof(*tcld->data));
+ tcld->data_len = data_len;
- struct TransCustomDataLayerVert *tcld_vert, *tcld_vert_iter;
- tcld_vert = BLI_memarena_alloc(tcld->arena, data_len * sizeof(*tcld_vert));
- tcld_vert_iter = &tcld_vert[0];
+ {
+ /* Setup Verts. */
+ struct TransCustomDataLayerVert *tcld_vert_iter = &tcld->data[0];
- TransData *tob;
- for (i = tc->data_len, tob = tc->data; i--; tob++, tcld_vert_iter++) {
+ TransData *tob = tc->data;
+ for (int i = tc->data_len; i--; tob++, tcld_vert_iter++) {
BMVert *v = tob->extra;
create_trans_vert_customdata_layer(v, tcld, tcld_vert_iter);
}
TransDataMirror *td_mirror = tc->data_mirror;
- for (i = tc->data_mirror_len; i--; td_mirror++, tcld_vert_iter++) {
+ for (int i = tc->data_mirror_len; i--; td_mirror++, tcld_vert_iter++) {
BMVert *v = td_mirror->extra;
create_trans_vert_customdata_layer(v, tcld, tcld_vert_iter);
}
+ }
+
+ tc->custom.type.data = tcld;
+ tc->custom.type.free_cb = trans_mesh_customdata_free_cb;
+}
+
+void trans_mesh_customdata_correction_init(TransInfo *t)
+{
+ const char uvcalc_correct_flag = ELEM(t->mode, TFM_VERT_SLIDE, TFM_EDGE_SLIDE) ?
+ UVCALC_TRANSFORM_CORRECT_SLIDE :
+ UVCALC_TRANSFORM_CORRECT;
- tcld->data = tcld_vert;
- tcld->data_len = data_len;
+ if (t->settings->uvcalc_flag & uvcalc_correct_flag) {
+ FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+ trans_mesh_customdata_correction_init_container(t, tc);
+ }
}
}
@@ -1187,19 +1199,17 @@ static void trans_mesh_customdata_correction_apply_vert(struct TransCustomDataLa
BMesh *bm = tcld->bm;
BMVert *v = tcld_vert->v;
const float *co_orig_3d = tcld_vert->co_orig_3d;
- struct LinkNode **cd_loop_groups = tcld_vert->cd_loop_groups;
BMIter liter;
int j, l_num;
float *loop_weights;
const bool is_moved = (len_squared_v3v3(v->co, co_orig_3d) > FLT_EPSILON);
- const bool do_loop_weight = tcld->layer_math_map_num && is_moved;
- const bool do_loop_mdisps = is_final && is_moved && (tcld->cd_loop_mdisp_offset != -1);
+ const bool do_loop_weight = is_moved && tcld->layer_math_map_num;
const float *v_proj_axis = v->no;
/* original (l->prev, l, l->next) projections for each loop ('l' remains unchanged) */
float v_proj[3][3];
- if (do_loop_weight || do_loop_mdisps) {
+ if (do_loop_weight) {
project_plane_normalized_v3_v3v3(v_proj[1], co_orig_3d, v_proj_axis);
}
@@ -1264,6 +1274,7 @@ static void trans_mesh_customdata_correction_apply_vert(struct TransCustomDataLa
}
}
+ struct LinkNode **cd_loop_groups = tcld_vert->cd_loop_groups;
if (tcld->layer_math_map_num && cd_loop_groups) {
if (do_loop_weight) {
for (j = 0; j < tcld->layer_math_map_num; j++) {
@@ -1283,6 +1294,7 @@ static void trans_mesh_customdata_correction_apply_vert(struct TransCustomDataLa
* Interpolate from every other loop (not ideal)
* However values will only be taken from loops which overlap other mdisps.
* */
+ const bool do_loop_mdisps = is_moved && is_final && (tcld->cd_loop_mdisp_offset != -1);
if (do_loop_mdisps) {
float(*faces_center)[3] = BLI_array_alloca(faces_center, l_num);
BMLoop *l;
@@ -1386,13 +1398,9 @@ void recalcData_mesh(TransInfo *t)
}
}
- if (ELEM(t->mode, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) {
- FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- trans_mesh_customdata_correction_apply(tc, false);
- }
- }
-
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+ trans_mesh_customdata_correction_apply(tc, false);
+
DEG_id_tag_update(tc->obedit->data, 0); /* sets recalc flags */
BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
EDBM_mesh_normals_update(em);
@@ -1408,18 +1416,9 @@ void recalcData_mesh(TransInfo *t)
void special_aftertrans_update__mesh(bContext *UNUSED(C), TransInfo *t)
{
const bool canceled = (t->state == TRANS_CANCEL);
+ const bool use_automerge = !canceled && (t->flag & (T_AUTOMERGE | T_AUTOSPLIT)) != 0;
- if (canceled) {
- /* Exception, edge slide transformed UVs too. */
- if (t->mode == TFM_EDGE_SLIDE) {
- doEdgeSlide(t, 0.0f);
- }
- else if (t->mode == TFM_VERT_SLIDE) {
- doVertSlide(t, 0.0f);
- }
- }
-
- if (ELEM(t->mode, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) {
+ if (TRANS_DATA_CONTAINER_FIRST_OK(t)->custom.type.data != NULL) {
/* Handle multires re-projection, done
* on transform completion since it's
* really slow -joeedh. */
@@ -1428,10 +1427,8 @@ void special_aftertrans_update__mesh(bContext *UNUSED(C), TransInfo *t)
}
}
- bool use_automerge = !canceled && (t->flag & (T_AUTOMERGE | T_AUTOSPLIT)) != 0;
if (use_automerge) {
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
-
BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
BMesh *bm = em->bm;
char hflag;
diff --git a/source/blender/editors/transform/transform_data.h b/source/blender/editors/transform/transform_data.h
index bc95fdad59c..48ed9ecf34b 100644
--- a/source/blender/editors/transform/transform_data.h
+++ b/source/blender/editors/transform/transform_data.h
@@ -88,8 +88,8 @@ typedef struct TransDataExtension {
* It is the same but without the #Bone.bone_mat, see #TD_PBONE_LOCAL_MTX_C. */
float l_smtx[3][3];
/** The rotscale matrix of pose bone, to allow using snap-align in translation mode,
- * when td->mtx is the loc pose bone matrix (and hence can't be used to apply
- * rotation in some cases, namely when a bone is in "NoLocal" or "Hinge" mode)... */
+ * when #TransData.mtx is the loc pose bone matrix (and hence can't be used to apply
+ * rotation in some cases, namely when a bone is in "No-Local" or "Hinge" mode)... */
float r_mtx[3][3];
/** Inverse of previous one. */
float r_smtx[3][3];
diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c
index 78c47014d62..cce6ef1f3bd 100644
--- a/source/blender/editors/transform/transform_generics.c
+++ b/source/blender/editors/transform/transform_generics.c
@@ -131,21 +131,6 @@ void resetTransRestrictions(TransInfo *t)
t->flag &= ~T_ALL_RESTRICTIONS;
}
-static int initTransInfo_edit_pet_to_flag(const int proportional)
-{
- int flag = 0;
- if (proportional & PROP_EDIT_USE) {
- flag |= T_PROP_EDIT;
- }
- if (proportional & PROP_EDIT_CONNECTED) {
- flag |= T_PROP_CONNECTED;
- }
- if (proportional & PROP_EDIT_PROJECTED) {
- flag |= T_PROP_PROJECTED;
- }
- return flag;
-}
-
void initTransDataContainers_FromObjectData(TransInfo *t,
Object *obact,
Object **objects,
@@ -390,15 +375,15 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
if (op && ((prop = RNA_struct_find_property(op->ptr, "correct_uv")))) {
if (RNA_property_is_set(op->ptr, prop)) {
if (RNA_property_boolean_get(op->ptr, prop)) {
- t->settings->uvcalc_flag |= UVCALC_TRANSFORM_CORRECT;
+ t->settings->uvcalc_flag |= UVCALC_TRANSFORM_CORRECT_SLIDE;
}
else {
- t->settings->uvcalc_flag &= ~UVCALC_TRANSFORM_CORRECT;
+ t->settings->uvcalc_flag &= ~UVCALC_TRANSFORM_CORRECT_SLIDE;
}
}
else {
RNA_property_boolean_set(
- op->ptr, prop, (t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT) != 0);
+ op->ptr, prop, (t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT_SLIDE) != 0);
}
}
}
@@ -550,7 +535,8 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
orient_types[2] = orient_type_scene;
}
else {
- if ((t->flag & T_MODAL) && (use_orient_axis || transform_mode_is_changeable(t->mode))) {
+ if ((t->flag & T_MODAL) && (use_orient_axis || transform_mode_is_changeable(t->mode)) &&
+ (t->mode != TFM_ALIGN)) {
orient_types[0] = V3D_ORIENT_VIEW;
}
else {
@@ -636,45 +622,45 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
/* setting PET flag only if property exist in operator. Otherwise, assume it's not supported */
if (op && (prop = RNA_struct_find_property(op->ptr, "use_proportional_edit"))) {
if (RNA_property_is_set(op->ptr, prop)) {
- int proportional = 0;
if (RNA_property_boolean_get(op->ptr, prop)) {
- proportional |= PROP_EDIT_USE;
+ t->flag |= T_PROP_EDIT;
if (RNA_boolean_get(op->ptr, "use_proportional_connected")) {
- proportional |= PROP_EDIT_CONNECTED;
+ t->flag |= T_PROP_CONNECTED;
}
if (RNA_boolean_get(op->ptr, "use_proportional_projected")) {
- proportional |= PROP_EDIT_PROJECTED;
+ t->flag |= T_PROP_PROJECTED;
}
}
- t->flag |= initTransInfo_edit_pet_to_flag(proportional);
}
else {
/* use settings from scene only if modal */
if (t->flag & T_MODAL) {
if ((t->options & CTX_NO_PET) == 0) {
+ bool use_prop_edit = false;
if (t->spacetype == SPACE_GRAPH) {
- t->flag |= initTransInfo_edit_pet_to_flag(ts->proportional_fcurve);
+ use_prop_edit = ts->proportional_fcurve;
}
else if (t->spacetype == SPACE_ACTION) {
- t->flag |= initTransInfo_edit_pet_to_flag(ts->proportional_action);
+ use_prop_edit = ts->proportional_action;
}
- else if (t->obedit_type != -1) {
- t->flag |= initTransInfo_edit_pet_to_flag(ts->proportional_edit);
+ else if (t->options & CTX_MASK) {
+ use_prop_edit = ts->proportional_mask;
}
- else if (t->options & CTX_GPENCIL_STROKES) {
- t->flag |= initTransInfo_edit_pet_to_flag(ts->proportional_edit);
+ else if (obact && obact->mode == OB_MODE_OBJECT) {
+ use_prop_edit = ts->proportional_objects;
}
- else if (t->options & CTX_MASK) {
- if (ts->proportional_mask) {
- t->flag |= T_PROP_EDIT;
-
- if (ts->proportional_edit & PROP_EDIT_CONNECTED) {
- t->flag |= T_PROP_CONNECTED;
- }
- }
+ else {
+ use_prop_edit = (ts->proportional_edit & PROP_EDIT_USE) != 0;
}
- else if (!(t->options & CTX_CURSOR) && ts->proportional_objects) {
+
+ if (use_prop_edit) {
t->flag |= T_PROP_EDIT;
+ if (ts->proportional_edit & PROP_EDIT_CONNECTED) {
+ t->flag |= T_PROP_CONNECTED;
+ }
+ if (ts->proportional_edit & PROP_EDIT_PROJECTED) {
+ t->flag |= T_PROP_PROJECTED;
+ }
}
}
}
diff --git a/source/blender/editors/transform/transform_mode.c b/source/blender/editors/transform/transform_mode.c
index f028044809f..831ea90b4e4 100644
--- a/source/blender/editors/transform/transform_mode.c
+++ b/source/blender/editors/transform/transform_mode.c
@@ -50,6 +50,23 @@
/* Own include. */
#include "transform_mode.h"
+int transform_mode_really_used(bContext *C, int mode)
+{
+ if (mode == TFM_BONESIZE) {
+ Object *ob = CTX_data_active_object(C);
+ BLI_assert(ob);
+ if (ob->type != OB_ARMATURE) {
+ return TFM_RESIZE;
+ }
+ bArmature *arm = ob->data;
+ if (arm->drawtype == ARM_ENVELOPE) {
+ return TFM_BONE_ENVELOPE_DIST;
+ }
+ }
+
+ return mode;
+}
+
bool transdata_check_local_center(TransInfo *t, short around)
{
return ((around == V3D_AROUND_LOCAL_ORIGINS) &&
@@ -1174,25 +1191,12 @@ void transform_mode_init(TransInfo *t, wmOperator *op, const int mode)
case TFM_CREASE:
initCrease(t);
break;
- case TFM_BONESIZE: { /* used for both B-Bone width (bonesize) as for deform-dist (envelope) */
- /* Note: we have to pick one, use the active object. */
- TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_OK(t);
- bArmature *arm = tc->poseobj->data;
- if (arm->drawtype == ARM_ENVELOPE) {
- initBoneEnvelope(t);
- t->mode = TFM_BONE_ENVELOPE_DIST;
- }
- else {
- initBoneSize(t);
- }
+ case TFM_BONESIZE:
+ initBoneSize(t);
break;
- }
case TFM_BONE_ENVELOPE:
- initBoneEnvelope(t);
- break;
case TFM_BONE_ENVELOPE_DIST:
initBoneEnvelope(t);
- t->mode = TFM_BONE_ENVELOPE_DIST;
break;
case TFM_EDGE_SLIDE:
case TFM_VERT_SLIDE: {
@@ -1267,6 +1271,12 @@ void transform_mode_init(TransInfo *t, wmOperator *op, const int mode)
break;
}
+ if (t->data_type == TC_MESH_VERTS) {
+ /* Init Custom Data correction.
+ * Ideally this should be called when creating the TransData. */
+ trans_mesh_customdata_correction_init(t);
+ }
+
/* TODO(germano): Some of these operations change the `t->mode`.
* This can be bad for Redo.
* BLI_assert(t->mode == mode); */
diff --git a/source/blender/editors/transform/transform_mode.h b/source/blender/editors/transform/transform_mode.h
index 464deff983b..5cda8e3063a 100644
--- a/source/blender/editors/transform/transform_mode.h
+++ b/source/blender/editors/transform/transform_mode.h
@@ -26,6 +26,7 @@
#define __TRANSFORM_MODE_H__
struct AnimData;
+struct bContext;
struct LinkNode;
struct TransData;
struct TransDataContainer;
@@ -40,6 +41,7 @@ typedef struct TransDataGenericSlideVert {
} TransDataGenericSlideVert;
/* transform_mode.c */
+int transform_mode_really_used(struct bContext *C, int mode);
bool transdata_check_local_center(TransInfo *t, short around);
bool transform_mode_is_changeable(const int mode);
void protectedTransBits(short protectflag, float vec[3]);
@@ -95,7 +97,6 @@ void initSeqSlide(TransInfo *t);
/* transform_mode_edge_slide.c */
void drawEdgeSlide(TransInfo *t);
-void doEdgeSlide(TransInfo *t, float perc);
void initEdgeSlide_ex(
TransInfo *t, bool use_double_side, bool use_even, bool flipped, bool use_clamp);
void initEdgeSlide(TransInfo *t);
@@ -153,7 +154,6 @@ void initTranslation(TransInfo *t);
/* transform_mode_vert_slide.c */
void drawVertSlide(TransInfo *t);
-void doVertSlide(TransInfo *t, float perc);
void initVertSlide_ex(TransInfo *t, bool use_even, bool flipped, bool use_clamp);
void initVertSlide(TransInfo *t);
#endif
diff --git a/source/blender/editors/transform/transform_mode_bbone_resize.c b/source/blender/editors/transform/transform_mode_bbone_resize.c
index 77850e74785..2c2253630c0 100644
--- a/source/blender/editors/transform/transform_mode_bbone_resize.c
+++ b/source/blender/editors/transform/transform_mode_bbone_resize.c
@@ -132,6 +132,11 @@ static void applyBoneSize(TransInfo *t, const int UNUSED(mval[2]))
if (t->con.applySize) {
t->con.applySize(t, NULL, NULL, mat);
+ for (i = 0; i < 3; i++) {
+ if (!(t->con.mode & (CON_AXIS0 << i))) {
+ t->values_final[i] = 1.0f;
+ }
+ }
}
copy_m3_m3(t->mat, mat); // used in gizmo
diff --git a/source/blender/editors/transform/transform_mode_boneenvelope.c b/source/blender/editors/transform/transform_mode_boneenvelope.c
index 7045d190478..b7a34769f5a 100644
--- a/source/blender/editors/transform/transform_mode_boneenvelope.c
+++ b/source/blender/editors/transform/transform_mode_boneenvelope.c
@@ -96,7 +96,6 @@ static void applyBoneEnvelope(TransInfo *t, const int UNUSED(mval[2]))
void initBoneEnvelope(TransInfo *t)
{
- t->mode = TFM_BONE_ENVELOPE;
t->transform = applyBoneEnvelope;
initMouseInputMode(t, &t->mouse, INPUT_SPRING);
diff --git a/source/blender/editors/transform/transform_mode_edge_slide.c b/source/blender/editors/transform/transform_mode_edge_slide.c
index 42f75f041b9..ed22afc404e 100644
--- a/source/blender/editors/transform/transform_mode_edge_slide.c
+++ b/source/blender/editors/transform/transform_mode_edge_slide.c
@@ -50,6 +50,7 @@
#include "BLT_translation.h"
#include "transform.h"
+#include "transform_constraints.h"
#include "transform_convert.h"
#include "transform_mode.h"
#include "transform_snap.h"
@@ -96,24 +97,28 @@ typedef struct EdgeSlideParams {
} EdgeSlideParams;
/**
- * Get the first valid EdgeSlideData.
+ * Get the first valid TransDataContainer *.
*
* Note we cannot trust TRANS_DATA_CONTAINER_FIRST_OK because of multi-object that
* may leave items with invalid custom data in the transform data container.
*/
-static EdgeSlideData *edgeSlideFirstGet(TransInfo *t)
+static TransDataContainer *edge_slide_container_first_ok(TransInfo *t)
{
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- EdgeSlideData *sld = tc->custom.mode.data;
- if (sld == NULL) {
- continue;
+ if (tc->custom.mode.data) {
+ return tc;
}
- return sld;
}
BLI_assert(!"Should never happen, at least one EdgeSlideData should be valid");
return NULL;
}
+static EdgeSlideData *edgeSlideFirstGet(TransInfo *t)
+{
+ TransDataContainer *tc = edge_slide_container_first_ok(t);
+ return tc->custom.mode.data;
+}
+
static void calcEdgeSlideCustomPoints(struct TransInfo *t)
{
EdgeSlideData *sld = edgeSlideFirstGet(t);
@@ -1129,132 +1134,226 @@ static eRedrawFlag handleEventEdgeSlide(struct TransInfo *t, const struct wmEven
void drawEdgeSlide(TransInfo *t)
{
- if ((t->mode == TFM_EDGE_SLIDE) && edgeSlideFirstGet(t)) {
- const EdgeSlideParams *slp = t->custom.mode.data;
- EdgeSlideData *sld = edgeSlideFirstGet(t);
- const bool is_clamp = !(t->flag & T_ALT_TRANSFORM);
+ if (t->mode != TFM_EDGE_SLIDE) {
+ return;
+ }
- /* Even mode */
- if ((slp->use_even == true) || (is_clamp == false)) {
- const float line_size = UI_GetThemeValuef(TH_OUTLINE_WIDTH) + 0.5f;
+ EdgeSlideData *sld = edgeSlideFirstGet(t);
+ if (sld == NULL) {
+ return;
+ }
- GPU_depth_test(false);
+ const EdgeSlideParams *slp = t->custom.mode.data;
+ const bool is_clamp = !(t->flag & T_ALT_TRANSFORM);
- GPU_blend(true);
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ const float line_size = UI_GetThemeValuef(TH_OUTLINE_WIDTH) + 0.5f;
- GPU_matrix_push();
- GPU_matrix_mul(TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->obmat);
+ GPU_depth_test(false);
- uint pos = GPU_vertformat_attr_add(
- immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+ GPU_blend(true);
+ GPU_blend_set_func_separate(
+ GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
- immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
+ GPU_matrix_push();
+ GPU_matrix_mul(TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->obmat);
- if (slp->use_even == true) {
- float co_a[3], co_b[3], co_mark[3];
- TransDataEdgeSlideVert *curr_sv = &sld->sv[sld->curr_sv_index];
- const float fac = (slp->perc + 1.0f) / 2.0f;
- const float ctrl_size = UI_GetThemeValuef(TH_FACEDOT_SIZE) + 1.5f;
- const float guide_size = ctrl_size - 0.5f;
- const int alpha_shade = -30;
+ uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
- add_v3_v3v3(co_a, curr_sv->v_co_orig, curr_sv->dir_side[0]);
- add_v3_v3v3(co_b, curr_sv->v_co_orig, curr_sv->dir_side[1]);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
- GPU_line_width(line_size);
- immUniformThemeColorShadeAlpha(TH_EDGE_SELECT, 80, alpha_shade);
- immBeginAtMost(GPU_PRIM_LINES, 4);
- if (curr_sv->v_side[0]) {
- immVertex3fv(pos, curr_sv->v_side[0]->co);
- immVertex3fv(pos, curr_sv->v_co_orig);
- }
+ if (slp->use_even == true) {
+ /* Even mode */
+ float co_a[3], co_b[3], co_mark[3];
+ TransDataEdgeSlideVert *curr_sv = &sld->sv[sld->curr_sv_index];
+ const float fac = (slp->perc + 1.0f) / 2.0f;
+ const float ctrl_size = UI_GetThemeValuef(TH_FACEDOT_SIZE) + 1.5f;
+ const float guide_size = ctrl_size - 0.5f;
+ const int alpha_shade = -30;
+
+ add_v3_v3v3(co_a, curr_sv->v_co_orig, curr_sv->dir_side[0]);
+ add_v3_v3v3(co_b, curr_sv->v_co_orig, curr_sv->dir_side[1]);
+
+ GPU_line_width(line_size);
+ immUniformThemeColorShadeAlpha(TH_EDGE_SELECT, 80, alpha_shade);
+ immBeginAtMost(GPU_PRIM_LINES, 4);
+ if (curr_sv->v_side[0]) {
+ immVertex3fv(pos, curr_sv->v_side[0]->co);
+ immVertex3fv(pos, curr_sv->v_co_orig);
+ }
+ if (curr_sv->v_side[1]) {
+ immVertex3fv(pos, curr_sv->v_side[1]->co);
+ immVertex3fv(pos, curr_sv->v_co_orig);
+ }
+ immEnd();
+
+ {
+ float *co_test = NULL;
+ if (slp->flipped) {
if (curr_sv->v_side[1]) {
- immVertex3fv(pos, curr_sv->v_side[1]->co);
- immVertex3fv(pos, curr_sv->v_co_orig);
+ co_test = curr_sv->v_side[1]->co;
}
- immEnd();
-
- {
- float *co_test = NULL;
- if (slp->flipped) {
- if (curr_sv->v_side[1]) {
- co_test = curr_sv->v_side[1]->co;
- }
- }
- else {
- if (curr_sv->v_side[0]) {
- co_test = curr_sv->v_side[0]->co;
- }
- }
-
- if (co_test != NULL) {
- immUniformThemeColorShadeAlpha(TH_SELECT, -30, alpha_shade);
- GPU_point_size(ctrl_size);
- immBegin(GPU_PRIM_POINTS, 1);
- immVertex3fv(pos, co_test);
- immEnd();
- }
+ }
+ else {
+ if (curr_sv->v_side[0]) {
+ co_test = curr_sv->v_side[0]->co;
}
+ }
- immUniformThemeColorShadeAlpha(TH_SELECT, 255, alpha_shade);
- GPU_point_size(guide_size);
+ if (co_test != NULL) {
+ immUniformThemeColorShadeAlpha(TH_SELECT, -30, alpha_shade);
+ GPU_point_size(ctrl_size);
immBegin(GPU_PRIM_POINTS, 1);
- interp_line_v3_v3v3v3(co_mark, co_b, curr_sv->v_co_orig, co_a, fac);
- immVertex3fv(pos, co_mark);
+ immVertex3fv(pos, co_test);
immEnd();
}
+ }
+
+ immUniformThemeColorShadeAlpha(TH_SELECT, 255, alpha_shade);
+ GPU_point_size(guide_size);
+ immBegin(GPU_PRIM_POINTS, 1);
+ interp_line_v3_v3v3v3(co_mark, co_b, curr_sv->v_co_orig, co_a, fac);
+ immVertex3fv(pos, co_mark);
+ immEnd();
+ }
+ else if (is_clamp == false) {
+ const int side_index = sld->curr_side_unclamp;
+ TransDataEdgeSlideVert *sv;
+ int i;
+ const int alpha_shade = -160;
+
+ GPU_line_width(line_size);
+ immUniformThemeColorShadeAlpha(TH_EDGE_SELECT, 80, alpha_shade);
+ immBegin(GPU_PRIM_LINES, sld->totsv * 2);
+
+ /* TODO(campbell): Loop over all verts */
+ sv = sld->sv;
+ for (i = 0; i < sld->totsv; i++, sv++) {
+ float a[3], b[3];
+
+ if (!is_zero_v3(sv->dir_side[side_index])) {
+ copy_v3_v3(a, sv->dir_side[side_index]);
+ }
else {
- if (is_clamp == false) {
- const int side_index = sld->curr_side_unclamp;
- TransDataEdgeSlideVert *sv;
- int i;
- const int alpha_shade = -160;
+ copy_v3_v3(a, sv->dir_side[!side_index]);
+ }
- GPU_line_width(line_size);
- immUniformThemeColorShadeAlpha(TH_EDGE_SELECT, 80, alpha_shade);
- immBegin(GPU_PRIM_LINES, sld->totsv * 2);
+ mul_v3_fl(a, 100.0f);
+ negate_v3_v3(b, a);
+ add_v3_v3(a, sv->v_co_orig);
+ add_v3_v3(b, sv->v_co_orig);
- /* TODO(campbell): Loop over all verts */
- sv = sld->sv;
- for (i = 0; i < sld->totsv; i++, sv++) {
- float a[3], b[3];
+ immVertex3fv(pos, a);
+ immVertex3fv(pos, b);
+ }
+ immEnd();
+ }
+ else {
+ /* Common case. */
+ TransDataEdgeSlideVert *curr_sv = &sld->sv[sld->curr_sv_index];
+ const int alpha_shade = -160;
+
+ float co_dir[3];
+ add_v3_v3v3(co_dir, curr_sv->v_co_orig, curr_sv->dir_side[sld->curr_side_unclamp]);
+
+ GPU_line_width(line_size);
+ immUniformThemeColorShadeAlpha(TH_EDGE_SELECT, 80, alpha_shade);
+ immBeginAtMost(GPU_PRIM_LINES, 2);
+ immVertex3fv(pos, curr_sv->v_co_orig);
+ immVertex3fv(pos, co_dir);
+ immEnd();
+ }
- if (!is_zero_v3(sv->dir_side[side_index])) {
- copy_v3_v3(a, sv->dir_side[side_index]);
- }
- else {
- copy_v3_v3(a, sv->dir_side[!side_index]);
- }
+ immUnbindProgram();
- mul_v3_fl(a, 100.0f);
- negate_v3_v3(b, a);
- add_v3_v3(a, sv->v_co_orig);
- add_v3_v3(b, sv->v_co_orig);
+ GPU_matrix_pop();
- immVertex3fv(pos, a);
- immVertex3fv(pos, b);
- }
- immEnd();
- }
- else {
- BLI_assert(0);
- }
- }
+ GPU_blend(false);
- immUnbindProgram();
+ GPU_depth_test(true);
+}
- GPU_matrix_pop();
+static void edge_slide_snap_apply(TransInfo *t, float *value)
+{
+ TransDataContainer *tc = edge_slide_container_first_ok(t);
+ EdgeSlideParams *slp = t->custom.mode.data;
+ EdgeSlideData *sld_active = tc->custom.mode.data;
+ TransDataEdgeSlideVert *sv = &sld_active->sv[sld_active->curr_sv_index];
+ float snap_point[3], co_orig[3], co_dest[2][3], dvec[3];
+
+ copy_v3_v3(co_orig, sv->v_co_orig);
+ add_v3_v3v3(co_dest[0], co_orig, sv->dir_side[0]);
+ add_v3_v3v3(co_dest[1], co_orig, sv->dir_side[1]);
+ if (tc->use_local_mat) {
+ mul_m4_v3(tc->mat, co_orig);
+ mul_m4_v3(tc->mat, co_dest[0]);
+ mul_m4_v3(tc->mat, co_dest[1]);
+ }
- GPU_blend(false);
+ getSnapPoint(t, dvec);
+ sub_v3_v3(dvec, t->tsnap.snapTarget);
+ add_v3_v3v3(snap_point, co_orig, dvec);
- GPU_depth_test(true);
+ float perc = *value;
+ int side_index;
+ float t_mid;
+ if (slp->use_even == false) {
+ const bool is_clamp = !(t->flag & T_ALT_TRANSFORM);
+ if (is_clamp) {
+ side_index = perc < 0.0f;
+ }
+ else {
+ side_index = sld_active->curr_side_unclamp;
}
}
+ else {
+ /* Could be pre-calculated. */
+ t_mid = line_point_factor_v3((float[3]){0.0f, 0.0f, 0.0f}, sv->dir_side[0], sv->dir_side[1]);
+
+ float t_snap = line_point_factor_v3(snap_point, co_dest[0], co_dest[1]);
+ side_index = t_snap >= t_mid;
+ }
+
+ if (t->tsnap.snapElem & (SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE)) {
+ float co_dir[3];
+ sub_v3_v3v3(co_dir, co_dest[side_index], co_orig);
+ if (t->tsnap.snapElem & SCE_SNAP_MODE_EDGE) {
+ transform_constraint_snap_axis_to_edge(t, co_dir, dvec);
+ }
+ else {
+ transform_constraint_snap_axis_to_face(t, co_dir, dvec);
+ }
+ add_v3_v3v3(snap_point, co_orig, dvec);
+ }
+
+ perc = line_point_factor_v3(snap_point, co_orig, co_dest[side_index]);
+ if (slp->use_even == false) {
+ if (side_index) {
+ perc *= -1;
+ }
+ }
+ else {
+ if (!side_index) {
+ perc = (1.0f - perc) * t_mid;
+ }
+ else {
+ perc = perc * (1.0f - t_mid) + t_mid;
+ }
+
+ if (slp->flipped) {
+ perc = 1.0f - perc;
+ }
+
+ perc = (2 * perc) - 1.0f;
+
+ if (!slp->flipped) {
+ perc *= -1;
+ }
+ }
+
+ *value = perc;
}
-void doEdgeSlide(TransInfo *t, float perc)
+static void doEdgeSlide(TransInfo *t, float perc)
{
EdgeSlideParams *slp = t->custom.mode.data;
EdgeSlideData *sld_active = edgeSlideFirstGet(t);
@@ -1365,6 +1464,7 @@ static void applyEdgeSlide(TransInfo *t, const int UNUSED(mval[2]))
final = t->values[0];
+ applySnapping(t, &final);
snapGridIncrement(t, &final);
/* only do this so out of range values are not displayed */
@@ -1413,6 +1513,8 @@ void initEdgeSlide_ex(
t->mode = TFM_EDGE_SLIDE;
t->transform = applyEdgeSlide;
t->handleEvent = handleEventEdgeSlide;
+ t->tsnap.applySnap = edge_slide_snap_apply;
+ t->tsnap.distance = transform_snap_distance_len_squared_fn;
{
EdgeSlideParams *slp = MEM_callocN(sizeof(*slp), __func__);
@@ -1432,16 +1534,13 @@ void initEdgeSlide_ex(
t->custom.mode.use_free = true;
}
- if (use_double_side) {
- FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- sld = use_double_side ? createEdgeSlideVerts_double_side(t, tc) :
- createEdgeSlideVerts_single_side(t, tc);
- if (sld) {
- tc->custom.mode.data = sld;
- tc->custom.mode.free_cb = freeEdgeSlideVerts;
- trans_mesh_customdata_correction_init(t, tc);
- ok = true;
- }
+ FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+ sld = use_double_side ? createEdgeSlideVerts_double_side(t, tc) :
+ createEdgeSlideVerts_single_side(t, tc);
+ if (sld) {
+ tc->custom.mode.data = sld;
+ tc->custom.mode.free_cb = freeEdgeSlideVerts;
+ ok = true;
}
}
diff --git a/source/blender/editors/transform/transform_mode_resize.c b/source/blender/editors/transform/transform_mode_resize.c
index ebce32a598e..5fb46b30e0d 100644
--- a/source/blender/editors/transform/transform_mode_resize.c
+++ b/source/blender/editors/transform/transform_mode_resize.c
@@ -44,6 +44,39 @@
/** \name Transform Resize
* \{ */
+static float ResizeBetween(TransInfo *t, const float p1[3], const float p2[3])
+{
+ float d1[3], d2[3], len_d1;
+
+ sub_v3_v3v3(d1, p1, t->center_global);
+ sub_v3_v3v3(d2, p2, t->center_global);
+
+ if (t->con.applyRot != NULL && (t->con.mode & CON_APPLY)) {
+ mul_m3_v3(t->con.pmtx, d1);
+ mul_m3_v3(t->con.pmtx, d2);
+ }
+
+ project_v3_v3v3(d1, d1, d2);
+
+ len_d1 = len_v3(d1);
+
+ /* Use 'invalid' dist when `center == p1` (after projecting),
+ * in this case scale will _never_ move the point in relation to the center,
+ * so it makes no sense to take it into account when scaling. see: T46503 */
+ return len_d1 != 0.0f ? len_v3(d2) / len_d1 : TRANSFORM_DIST_INVALID;
+}
+
+static void ApplySnapResize(TransInfo *t, float vec[3])
+{
+ float point[3];
+ getSnapPoint(t, point);
+
+ float dist = ResizeBetween(t, t->tsnap.snapTarget, point);
+ if (dist != TRANSFORM_DIST_INVALID) {
+ copy_v3_fl(vec, dist);
+ }
+}
+
static void applyResize(TransInfo *t, const int UNUSED(mval[2]))
{
float mat[3][3];
@@ -134,6 +167,8 @@ void initResize(TransInfo *t)
{
t->mode = TFM_RESIZE;
t->transform = applyResize;
+ t->tsnap.applySnap = ApplySnapResize;
+ t->tsnap.distance = ResizeBetween;
initMouseInputMode(t, &t->mouse, INPUT_SPRING_FLIP);
diff --git a/source/blender/editors/transform/transform_mode_rotate.c b/source/blender/editors/transform/transform_mode_rotate.c
index 6480cb6c30e..4fa5dffc473 100644
--- a/source/blender/editors/transform/transform_mode_rotate.c
+++ b/source/blender/editors/transform/transform_mode_rotate.c
@@ -42,6 +42,67 @@
/** \name Transform Rotation
* \{ */
+static float RotationBetween(TransInfo *t, const float p1[3], const float p2[3])
+{
+ float angle, start[3], end[3];
+
+ sub_v3_v3v3(start, p1, t->center_global);
+ sub_v3_v3v3(end, p2, t->center_global);
+
+ // Angle around a constraint axis (error prone, will need debug)
+ if (t->con.applyRot != NULL && (t->con.mode & CON_APPLY)) {
+ float axis[3], tmp[3];
+
+ t->con.applyRot(t, NULL, NULL, axis, NULL);
+
+ project_v3_v3v3(tmp, end, axis);
+ sub_v3_v3v3(end, end, tmp);
+
+ project_v3_v3v3(tmp, start, axis);
+ sub_v3_v3v3(start, start, tmp);
+
+ normalize_v3(end);
+ normalize_v3(start);
+
+ cross_v3_v3v3(tmp, start, end);
+
+ if (dot_v3v3(tmp, axis) < 0.0f) {
+ angle = -acosf(dot_v3v3(start, end));
+ }
+ else {
+ angle = acosf(dot_v3v3(start, end));
+ }
+ }
+ else {
+ float mtx[3][3];
+
+ copy_m3_m4(mtx, t->viewmat);
+
+ mul_m3_v3(mtx, end);
+ mul_m3_v3(mtx, start);
+
+ angle = atan2f(start[1], start[0]) - atan2f(end[1], end[0]);
+ }
+
+ if (angle > (float)M_PI) {
+ angle = angle - 2 * (float)M_PI;
+ }
+ else if (angle < -((float)M_PI)) {
+ angle = 2.0f * (float)M_PI + angle;
+ }
+
+ return angle;
+}
+
+static void ApplySnapRotation(TransInfo *t, float *value)
+{
+ float point[3];
+ getSnapPoint(t, point);
+
+ float dist = RotationBetween(t, t->tsnap.snapTarget, point);
+ *value = dist;
+}
+
static float large_rotation_limit(float angle)
{
/* Limit rotation to 1001 turns max
@@ -173,6 +234,8 @@ void initRotation(TransInfo *t)
{
t->mode = TFM_ROTATION;
t->transform = applyRotation;
+ t->tsnap.applySnap = ApplySnapRotation;
+ t->tsnap.distance = RotationBetween;
setInputPostFct(&t->mouse, postInputRotation);
initMouseInputMode(t, &t->mouse, INPUT_ANGLE);
diff --git a/source/blender/editors/transform/transform_mode_translate.c b/source/blender/editors/transform/transform_mode_translate.c
index 60caa257a40..aee05197f10 100644
--- a/source/blender/editors/transform/transform_mode_translate.c
+++ b/source/blender/editors/transform/transform_mode_translate.c
@@ -34,6 +34,7 @@
#include "BKE_report.h"
#include "BKE_unit.h"
+#include "ED_node.h"
#include "ED_screen.h"
#include "WM_api.h"
@@ -214,6 +215,34 @@ static void headerTranslation(TransInfo *t, const float vec[3], char str[UI_MAX_
}
}
+static void ApplySnapTranslation(TransInfo *t, float vec[3])
+{
+ float point[3];
+ getSnapPoint(t, point);
+
+ if (t->spacetype == SPACE_NODE) {
+ char border = t->tsnap.snapNodeBorder;
+ if (border & (NODE_LEFT | NODE_RIGHT)) {
+ vec[0] = point[0] - t->tsnap.snapTarget[0];
+ }
+ if (border & (NODE_BOTTOM | NODE_TOP)) {
+ vec[1] = point[1] - t->tsnap.snapTarget[1];
+ }
+ }
+ else {
+ if (t->spacetype == SPACE_VIEW3D) {
+ if (t->options & CTX_PAINT_CURVE) {
+ if (ED_view3d_project_float_global(t->region, point, point, V3D_PROJ_TEST_NOP) !=
+ V3D_PROJ_RET_OK) {
+ zero_v3(point); /* no good answer here... */
+ }
+ }
+ }
+
+ sub_v3_v3v3(vec, point, t->tsnap.snapTarget);
+ }
+}
+
static void applyTranslationValue(TransInfo *t, const float vec[3])
{
const bool apply_snap_align_rotation = usingSnappingNormal(
@@ -376,6 +405,8 @@ void initTranslation(TransInfo *t)
}
t->transform = applyTranslation;
+ t->tsnap.applySnap = ApplySnapTranslation;
+ t->tsnap.distance = transform_snap_distance_len_squared_fn;
initMouseInputMode(t, &t->mouse, INPUT_VECTOR);
diff --git a/source/blender/editors/transform/transform_mode_vert_slide.c b/source/blender/editors/transform/transform_mode_vert_slide.c
index 62415b1ddc6..4b75c362da9 100644
--- a/source/blender/editors/transform/transform_mode_vert_slide.c
+++ b/source/blender/editors/transform/transform_mode_vert_slide.c
@@ -47,6 +47,7 @@
#include "BLT_translation.h"
#include "transform.h"
+#include "transform_constraints.h"
#include "transform_convert.h"
#include "transform_mode.h"
#include "transform_snap.h"
@@ -491,7 +492,7 @@ void drawVertSlide(TransInfo *t)
}
}
-void doVertSlide(TransInfo *t, float perc)
+static void doVertSlide(TransInfo *t, float perc)
{
VertSlideParams *slp = t->custom.mode.data;
@@ -538,6 +539,37 @@ void doVertSlide(TransInfo *t, float perc)
}
}
+static void vert_slide_snap_apply(TransInfo *t, float *value)
+{
+ TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_OK(t);
+ VertSlideData *sld = tc->custom.mode.data;
+ TransDataVertSlideVert *sv = &sld->sv[sld->curr_sv_index];
+
+ float snap_point[3], co_orig_3d[3], co_curr_3d[3], dvec[3];
+ copy_v3_v3(co_orig_3d, sv->co_orig_3d);
+ copy_v3_v3(co_curr_3d, sv->co_link_orig_3d[sv->co_link_curr]);
+ if (tc->use_local_mat) {
+ mul_m4_v3(tc->mat, co_orig_3d);
+ mul_m4_v3(tc->mat, co_curr_3d);
+ }
+
+ getSnapPoint(t, dvec);
+ sub_v3_v3(dvec, t->tsnap.snapTarget);
+ if (t->tsnap.snapElem & (SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE)) {
+ float co_dir_3d[3];
+ sub_v3_v3v3(co_dir_3d, co_curr_3d, co_orig_3d);
+ if (t->tsnap.snapElem & SCE_SNAP_MODE_EDGE) {
+ transform_constraint_snap_axis_to_edge(t, co_dir_3d, dvec);
+ }
+ else {
+ transform_constraint_snap_axis_to_face(t, co_dir_3d, dvec);
+ }
+ }
+
+ add_v3_v3v3(snap_point, co_orig_3d, dvec);
+ *value = line_point_factor_v3(snap_point, co_orig_3d, co_curr_3d);
+}
+
static void applyVertSlide(TransInfo *t, const int UNUSED(mval[2]))
{
char str[UI_MAX_DRAW_STR];
@@ -551,6 +583,7 @@ static void applyVertSlide(TransInfo *t, const int UNUSED(mval[2]))
final = t->values[0];
+ applySnapping(t, &final);
snapGridIncrement(t, &final);
/* only do this so out of range values are not displayed */
@@ -596,6 +629,8 @@ void initVertSlide_ex(TransInfo *t, bool use_even, bool flipped, bool use_clamp)
t->mode = TFM_VERT_SLIDE;
t->transform = applyVertSlide;
t->handleEvent = handleEventVertSlide;
+ t->tsnap.applySnap = vert_slide_snap_apply;
+ t->tsnap.distance = transform_snap_distance_len_squared_fn;
{
VertSlideParams *slp = MEM_callocN(sizeof(*slp), __func__);
@@ -617,7 +652,6 @@ void initVertSlide_ex(TransInfo *t, bool use_even, bool flipped, bool use_clamp)
if (sld) {
tc->custom.mode.data = sld;
tc->custom.mode.free_cb = freeVertSlideVerts;
- trans_mesh_customdata_correction_init(t, tc);
ok = true;
}
}
diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c
index 95249f4d17b..f86bcc41bee 100644
--- a/source/blender/editors/transform/transform_ops.c
+++ b/source/blender/editors/transform/transform_ops.c
@@ -1049,11 +1049,11 @@ static void TRANSFORM_OT_bbone_resize(struct wmOperatorType *ot)
ot->exec = transform_exec;
ot->modal = transform_modal;
ot->cancel = transform_cancel;
- ot->poll = ED_operator_editarmature;
+ ot->poll = ED_operator_object_active;
ot->poll_property = transform_poll_property;
RNA_def_float_translation(
- ot->srna, "value", 2, VecOne, -FLT_MAX, FLT_MAX, "Display Size", "", -FLT_MAX, FLT_MAX);
+ ot->srna, "value", 3, VecOne, -FLT_MAX, FLT_MAX, "Display Size", "", -FLT_MAX, FLT_MAX);
WM_operatortype_props_advanced_begin(ot);
diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c
index b508507cd4e..2943c3cb8ea 100644
--- a/source/blender/editors/transform/transform_snap.c
+++ b/source/blender/editors/transform/transform_snap.c
@@ -22,23 +22,13 @@
*/
#include <float.h>
-#include <math.h>
-#include <stdio.h>
-#include <stdlib.h>
#include "PIL_time.h"
-#include "DNA_node_types.h"
-#include "DNA_object_types.h"
-#include "DNA_scene_types.h"
-#include "DNA_screen_types.h"
-#include "DNA_space_types.h"
-#include "DNA_view3d_types.h"
#include "DNA_windowmanager_types.h"
#include "BLI_blenlib.h"
#include "BLI_math.h"
-#include "BLI_utildefines.h"
#include "GPU_immediate.h"
#include "GPU_state.h"
@@ -58,7 +48,6 @@
#include "ED_node.h"
#include "ED_transform_snap_object_context.h"
#include "ED_uvedit.h"
-#include "ED_view3d.h"
#include "UI_resources.h"
#include "UI_view2d.h"
@@ -80,10 +69,6 @@
static void setSnappingCallback(TransInfo *t);
-static void ApplySnapTranslation(TransInfo *t, float vec[3]);
-static void ApplySnapRotation(TransInfo *t, float *vec);
-static void ApplySnapResize(TransInfo *t, float vec[2]);
-
/* static void CalcSnapGrid(TransInfo *t, float *vec); */
static void CalcSnapGeometry(TransInfo *t, float *vec);
@@ -92,10 +77,6 @@ static void TargetSnapCenter(TransInfo *t);
static void TargetSnapClosest(TransInfo *t);
static void TargetSnapActive(TransInfo *t);
-static float RotationBetween(TransInfo *t, const float p1[3], const float p2[3]);
-static float TranslationBetween(TransInfo *t, const float p1[3], const float p2[3]);
-static float ResizeBetween(TransInfo *t, const float p1[3], const float p2[3]);
-
/** \} */
/* -------------------------------------------------------------------- */
@@ -143,6 +124,12 @@ bool transformModeUseSnap(const TransInfo *t)
if (t->mode == TFM_RESIZE) {
return (ts->snap_transform_mode_flag & SCE_SNAP_TRANSFORM_MODE_SCALE) != 0;
}
+ if (t->mode == TFM_VERT_SLIDE) {
+ return true;
+ }
+ if (t->mode == TFM_EDGE_SLIDE) {
+ return true;
+ }
return false;
}
@@ -476,7 +463,6 @@ void resetSnapping(TransInfo *t)
t->tsnap.modeSelect = 0;
t->tsnap.target = 0;
t->tsnap.last = 0;
- t->tsnap.applySnap = NULL;
t->tsnap.snapNormal[0] = 0;
t->tsnap.snapNormal[1] = 0;
@@ -557,35 +543,32 @@ static void initSnappingMode(TransInfo *t)
t->tsnap.mode = ts->snap_mode;
}
- if ((t->spacetype == SPACE_VIEW3D || t->spacetype == SPACE_IMAGE) && /* Only 3D view or UV */
- (t->flag & T_CAMERA) == 0) /* Not with camera selected in camera view */
- {
+ if ((t->spacetype == SPACE_VIEW3D || t->spacetype == SPACE_IMAGE) && (t->flag & T_CAMERA) == 0) {
+ /* Only 3D view or UV. */
+ /* Not with camera selected in camera view. */
+
setSnappingCallback(t);
- /* Edit mode */
- if (t->tsnap.applySnap != NULL && // A snapping function actually exist
- ((obedit_type != -1) &&
- /* Temporary limited to edit mode meshes, armature, curves, metaballs. */
- ELEM(obedit_type, OB_MESH, OB_ARMATURE, OB_CURVE, OB_LATTICE, OB_MBALL))) {
- /* Exclude editmesh if using proportional edit */
+ if ((obedit_type != -1) &&
+ ELEM(obedit_type, OB_MESH, OB_ARMATURE, OB_CURVE, OB_LATTICE, OB_MBALL)) {
+ /* Edit mode */
+ /* Temporary limited to edit mode meshes, armature, curves, metaballs. */
+
if ((obedit_type == OB_MESH) && (t->flag & T_PROP_EDIT)) {
+ /* Exclude editmesh if using proportional edit */
t->tsnap.modeSelect = SNAP_NOT_ACTIVE;
}
else {
t->tsnap.modeSelect = t->tsnap.snap_self ? SNAP_ALL : SNAP_NOT_ACTIVE;
}
}
- /* Particles edit mode*/
- else if (t->tsnap.applySnap != NULL && // A snapping function actually exist
- ((obedit_type == -1) && base_act && base_act->object &&
- base_act->object->mode & OB_MODE_PARTICLE_EDIT)) {
+ else if ((obedit_type == -1) && base_act && base_act->object &&
+ (base_act->object->mode & OB_MODE_PARTICLE_EDIT)) {
+ /* Particles edit mode. */
t->tsnap.modeSelect = SNAP_ALL;
}
- /* Object mode */
- else if (t->tsnap.applySnap != NULL && // A snapping function actually exist
- (obedit_type == -1)) // Object Mode
- {
-
+ else if (obedit_type == -1) {
+ /* Object mode */
if (t->options & (CTX_GPENCIL_STROKES | CTX_CURSOR | CTX_OBMODE_XFORM_OBDATA)) {
/* In "Edit Strokes" mode,
* snap tool can perform snap to selected or active objects (see T49632)
@@ -605,14 +588,7 @@ static void initSnappingMode(TransInfo *t)
}
else if (t->spacetype == SPACE_NODE) {
setSnappingCallback(t);
-
- if (t->tsnap.applySnap != NULL) {
- t->tsnap.modeSelect = SNAP_NOT_SELECTED;
- }
- else {
- /* Grid if snap is not possible */
- t->tsnap.mode = SCE_SNAP_MODE_INCREMENT;
- }
+ t->tsnap.modeSelect = SNAP_NOT_SELECTED;
}
else if (t->spacetype == SPACE_SEQ) {
/* We do our own snapping currently, so nothing here */
@@ -720,8 +696,13 @@ static void setSnappingCallback(TransInfo *t)
t->tsnap.targetSnap = TargetSnapClosest;
break;
case SCE_SNAP_TARGET_CENTER:
- t->tsnap.targetSnap = TargetSnapCenter;
- break;
+ if (!ELEM(t->mode, TFM_ROTATION, TFM_RESIZE)) {
+ t->tsnap.targetSnap = TargetSnapCenter;
+ break;
+ }
+ /* Can't do TARGET_CENTER with these modes,
+ * use TARGET_MEDIAN instead. */
+ ATTR_FALLTHROUGH;
case SCE_SNAP_TARGET_MEDIAN:
t->tsnap.targetSnap = TargetSnapMedian;
break;
@@ -729,36 +710,6 @@ static void setSnappingCallback(TransInfo *t)
t->tsnap.targetSnap = TargetSnapActive;
break;
}
-
- switch (t->mode) {
- case TFM_TRANSLATION:
- t->tsnap.applySnap = ApplySnapTranslation;
- t->tsnap.distance = TranslationBetween;
- break;
- case TFM_ROTATION:
- t->tsnap.applySnap = ApplySnapRotation;
- t->tsnap.distance = RotationBetween;
-
- // Can't do TARGET_CENTER with rotation, use TARGET_MEDIAN instead
- if (t->tsnap.target == SCE_SNAP_TARGET_CENTER) {
- t->tsnap.target = SCE_SNAP_TARGET_MEDIAN;
- t->tsnap.targetSnap = TargetSnapMedian;
- }
- break;
- case TFM_RESIZE:
- t->tsnap.applySnap = ApplySnapResize;
- t->tsnap.distance = ResizeBetween;
-
- // Can't do TARGET_CENTER with resize, use TARGET_MEDIAN instead
- if (t->tsnap.target == SCE_SNAP_TARGET_CENTER) {
- t->tsnap.target = SCE_SNAP_TARGET_MEDIAN;
- t->tsnap.targetSnap = TargetSnapMedian;
- }
- break;
- default:
- t->tsnap.applySnap = NULL;
- break;
- }
}
void addSnapPoint(TransInfo *t)
@@ -859,145 +810,6 @@ void getSnapPoint(const TransInfo *t, float vec[3])
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Apply Snap
- * \{ */
-
-static void ApplySnapTranslation(TransInfo *t, float vec[3])
-{
- float point[3];
- getSnapPoint(t, point);
-
- if (t->spacetype == SPACE_NODE) {
- char border = t->tsnap.snapNodeBorder;
- if (border & (NODE_LEFT | NODE_RIGHT)) {
- vec[0] = point[0] - t->tsnap.snapTarget[0];
- }
- if (border & (NODE_BOTTOM | NODE_TOP)) {
- vec[1] = point[1] - t->tsnap.snapTarget[1];
- }
- }
- else {
- if (t->spacetype == SPACE_VIEW3D) {
- if (t->options & CTX_PAINT_CURVE) {
- if (ED_view3d_project_float_global(t->region, point, point, V3D_PROJ_TEST_NOP) !=
- V3D_PROJ_RET_OK) {
- zero_v3(point); /* no good answer here... */
- }
- }
- }
-
- sub_v3_v3v3(vec, point, t->tsnap.snapTarget);
- }
-}
-
-static void ApplySnapRotation(TransInfo *t, float *value)
-{
- float point[3];
- getSnapPoint(t, point);
-
- float dist = RotationBetween(t, t->tsnap.snapTarget, point);
- *value = dist;
-}
-
-static void ApplySnapResize(TransInfo *t, float vec[3])
-{
- float point[3];
- getSnapPoint(t, point);
-
- float dist = ResizeBetween(t, t->tsnap.snapTarget, point);
- if (dist != TRANSFORM_DIST_INVALID) {
- copy_v3_fl(vec, dist);
- }
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Distance
- * \{ */
-
-static float TranslationBetween(TransInfo *UNUSED(t), const float p1[3], const float p2[3])
-{
- return len_squared_v3v3(p1, p2);
-}
-
-static float RotationBetween(TransInfo *t, const float p1[3], const float p2[3])
-{
- float angle, start[3], end[3];
-
- sub_v3_v3v3(start, p1, t->center_global);
- sub_v3_v3v3(end, p2, t->center_global);
-
- // Angle around a constraint axis (error prone, will need debug)
- if (t->con.applyRot != NULL && (t->con.mode & CON_APPLY)) {
- float axis[3], tmp[3];
-
- t->con.applyRot(t, NULL, NULL, axis, NULL);
-
- project_v3_v3v3(tmp, end, axis);
- sub_v3_v3v3(end, end, tmp);
-
- project_v3_v3v3(tmp, start, axis);
- sub_v3_v3v3(start, start, tmp);
-
- normalize_v3(end);
- normalize_v3(start);
-
- cross_v3_v3v3(tmp, start, end);
-
- if (dot_v3v3(tmp, axis) < 0.0f) {
- angle = -acosf(dot_v3v3(start, end));
- }
- else {
- angle = acosf(dot_v3v3(start, end));
- }
- }
- else {
- float mtx[3][3];
-
- copy_m3_m4(mtx, t->viewmat);
-
- mul_m3_v3(mtx, end);
- mul_m3_v3(mtx, start);
-
- angle = atan2f(start[1], start[0]) - atan2f(end[1], end[0]);
- }
-
- if (angle > (float)M_PI) {
- angle = angle - 2 * (float)M_PI;
- }
- else if (angle < -((float)M_PI)) {
- angle = 2.0f * (float)M_PI + angle;
- }
-
- return angle;
-}
-
-static float ResizeBetween(TransInfo *t, const float p1[3], const float p2[3])
-{
- float d1[3], d2[3], len_d1;
-
- sub_v3_v3v3(d1, p1, t->center_global);
- sub_v3_v3v3(d2, p2, t->center_global);
-
- if (t->con.applyRot != NULL && (t->con.mode & CON_APPLY)) {
- mul_m3_v3(t->con.pmtx, d1);
- mul_m3_v3(t->con.pmtx, d2);
- }
-
- project_v3_v3v3(d1, d1, d2);
-
- len_d1 = len_v3(d1);
-
- /* Use 'invalid' dist when `center == p1` (after projecting),
- * in this case scale will _never_ move the point in relation to the center,
- * so it makes no sense to take it into account when scaling. see: T46503 */
- return len_d1 != 0.0f ? len_v3(d2) / len_d1 : TRANSFORM_DIST_INVALID;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
/** \name Calc Snap (Generic)
* \{ */
@@ -1734,3 +1546,16 @@ static void applyGridIncrement(
}
/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Generic callbacks
+ * \{ */
+
+float transform_snap_distance_len_squared_fn(TransInfo *UNUSED(t),
+ const float p1[3],
+ const float p2[3])
+{
+ return len_squared_v3v3(p1, p2);
+}
+
+/** \} */
diff --git a/source/blender/editors/transform/transform_snap.h b/source/blender/editors/transform/transform_snap.h
index c088cf80f0d..688661bc2cb 100644
--- a/source/blender/editors/transform/transform_snap.h
+++ b/source/blender/editors/transform/transform_snap.h
@@ -87,4 +87,6 @@ void addSnapPoint(TransInfo *t);
eRedrawFlag updateSelectedSnapPoint(TransInfo *t);
void removeSnapPoint(TransInfo *t);
+float transform_snap_distance_len_squared_fn(TransInfo *t, const float p1[3], const float p2[3]);
+
#endif /* __TRANSFORM_SNAP_H__ */
diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c
index 08ef5109a74..4198b4c02a3 100644
--- a/source/blender/editors/transform/transform_snap_object.c
+++ b/source/blender/editors/transform/transform_snap_object.c
@@ -367,17 +367,15 @@ static SnapObjectData *snap_object_data_editmesh_get(SnapObjectContext *sctx,
* \{ */
typedef void (*IterSnapObjsCallback)(SnapObjectContext *sctx,
- bool use_obedit,
- bool use_backface_culling,
Object *ob,
float obmat[4][4],
+ bool use_obedit,
+ bool use_backface_culling,
+ bool is_object_active,
void *data);
/**
* Walks through all objects in the scene to create the list of objects to snap.
- *
- * \param sctx: Snap context to store data.
- * \param snap_select: from enum #eSnapSelect.
*/
static void iter_snap_objects(SnapObjectContext *sctx,
Depsgraph *depsgraph,
@@ -393,7 +391,6 @@ static void iter_snap_objects(SnapObjectContext *sctx,
Base *base_act = view_layer->basact;
for (Base *base = view_layer->object_bases.first; base != NULL; base = base->next) {
-
if (!BASE_VISIBLE(v3d, base)) {
continue;
}
@@ -405,13 +402,14 @@ static void iter_snap_objects(SnapObjectContext *sctx,
continue;
}
+ const bool is_object_active = (base == base_act);
if (snap_select == SNAP_NOT_SELECTED) {
if ((base->flag & BASE_SELECTED) || (base->flag_legacy & BA_WAS_SEL)) {
continue;
}
}
else if (snap_select == SNAP_NOT_ACTIVE) {
- if (base == base_act) {
+ if (is_object_active) {
continue;
}
}
@@ -421,14 +419,24 @@ static void iter_snap_objects(SnapObjectContext *sctx,
DupliObject *dupli_ob;
ListBase *lb = object_duplilist(depsgraph, sctx->scene, obj_eval);
for (dupli_ob = lb->first; dupli_ob; dupli_ob = dupli_ob->next) {
- sob_callback(
- sctx, use_object_edit_cage, use_backface_culling, dupli_ob->ob, dupli_ob->mat, data);
+ sob_callback(sctx,
+ dupli_ob->ob,
+ dupli_ob->mat,
+ use_object_edit_cage,
+ use_backface_culling,
+ is_object_active,
+ data);
}
free_object_duplilist(lb);
}
- sob_callback(
- sctx, use_object_edit_cage, use_backface_culling, obj_eval, obj_eval->obmat, data);
+ sob_callback(sctx,
+ obj_eval,
+ obj_eval->obmat,
+ use_object_edit_cage,
+ use_backface_culling,
+ is_object_active,
+ data);
}
}
@@ -956,10 +964,11 @@ struct RaycastObjUserData {
* \note Duplicate args here are documented at #snapObjectsRay
*/
static void raycast_obj_fn(SnapObjectContext *sctx,
- bool use_obedit,
- bool use_backface_culling,
Object *ob,
float obmat[4][4],
+ bool use_obedit,
+ bool use_backface_culling,
+ bool is_object_active,
void *data)
{
struct RaycastObjUserData *dt = data;
@@ -1032,24 +1041,26 @@ static void raycast_obj_fn(SnapObjectContext *sctx,
case OB_CURVE:
case OB_SURF:
case OB_FONT: {
- Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob);
- if (mesh_eval) {
- retval = raycastMesh(sctx,
- dt->ray_start,
- dt->ray_dir,
- ob,
- mesh_eval,
- obmat,
- ob_index,
- false,
- use_backface_culling,
- ray_depth,
- dt->r_loc,
- dt->r_no,
- dt->r_index,
- dt->r_hit_list);
- break;
+ if (!is_object_active) {
+ Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob);
+ if (mesh_eval) {
+ retval = raycastMesh(sctx,
+ dt->ray_start,
+ dt->ray_dir,
+ ob,
+ mesh_eval,
+ obmat,
+ ob_index,
+ false,
+ use_backface_culling,
+ ray_depth,
+ dt->r_loc,
+ dt->r_no,
+ dt->r_index,
+ dt->r_hit_list);
+ }
}
+ break;
}
}
@@ -1071,9 +1082,7 @@ static void raycast_obj_fn(SnapObjectContext *sctx,
* Walks through all objects in the scene to find the `hit` on object surface.
*
* \param sctx: Snap context to store data.
- * \param snap_select: from enum eSnapSelect.
- * \param use_object_edit_cage: Uses the coordinates of BMesh(if any) to do the snapping.
- * \param obj_list: List with objects to snap (created in `create_object_list`).
+ * \param params: Snapping behavior.
*
* Read/Write Args
* ---------------
@@ -2054,15 +2063,15 @@ static short snapCurve(SnapData *snapdata,
}
/* may extend later (for now just snaps to empty center) */
-static short snapEmpty(SnapData *snapdata,
- Object *ob,
- const float obmat[4][4],
- /* read/write args */
- float *dist_px,
- /* return args */
- float r_loc[3],
- float *UNUSED(r_no),
- int *r_index)
+static short snap_object_center(SnapData *snapdata,
+ Object *ob,
+ const float obmat[4][4],
+ /* read/write args */
+ float *dist_px,
+ /* return args */
+ float r_loc[3],
+ float *UNUSED(r_no),
+ int *r_index)
{
short retval = 0;
@@ -2117,7 +2126,7 @@ static short snapCamera(const SnapObjectContext *sctx,
float *dist_px,
/* return args */
float r_loc[3],
- float *UNUSED(r_no),
+ float *r_no,
int *r_index)
{
short retval = 0;
@@ -2132,7 +2141,7 @@ static short snapCamera(const SnapObjectContext *sctx,
MovieTracking *tracking;
if (clip == NULL) {
- return retval;
+ return snap_object_center(snapdata, object, obmat, dist_px, r_loc, r_no, r_index);
}
if (object->transflag & OB_DUPLI) {
return retval;
@@ -2650,11 +2659,12 @@ struct SnapObjUserData {
*
* \note Duplicate args here are documented at #snapObjectsRay
*/
-static void sanp_obj_fn(SnapObjectContext *sctx,
- bool use_obedit,
- bool use_backface_culling,
+static void snap_obj_fn(SnapObjectContext *sctx,
Object *ob,
float obmat[4][4],
+ bool use_obedit,
+ bool use_backface_culling,
+ bool UNUSED(is_object_active),
void *data)
{
struct SnapObjUserData *dt = data;
@@ -2729,10 +2739,10 @@ static void sanp_obj_fn(SnapObjectContext *sctx,
break;
}
case OB_EMPTY:
- retval = snapEmpty(dt->snapdata, ob, obmat, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index);
- break;
case OB_GPENCIL:
- retval = snapEmpty(dt->snapdata, ob, obmat, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index);
+ case OB_LAMP:
+ retval = snap_object_center(
+ dt->snapdata, ob, obmat, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index);
break;
case OB_CAMERA:
retval = snapCamera(
@@ -2800,7 +2810,7 @@ static short snapObjectsRay(SnapObjectContext *sctx,
.ret = 0,
};
- iter_snap_objects(sctx, depsgraph, params, sanp_obj_fn, &data);
+ iter_snap_objects(sctx, depsgraph, params, snap_obj_fn, &data);
return data.ret;
}
@@ -3205,7 +3215,7 @@ short ED_transform_snap_object_project_view3d_ex(SnapObjectContext *sctx,
* \param mval: Screenspace coordinate.
* \param prev_co: Coordinate for perpendicular point calculation (optional).
* \param dist_px: Maximum distance to snap (in pixels).
- * \param r_co: hit location.
+ * \param r_loc: hit location.
* \param r_no: hit normal (optional).
* \return Snap success
*/
diff --git a/source/blender/editors/uvedit/uvedit_draw.c b/source/blender/editors/uvedit/uvedit_draw.c
index 897e2f13774..df6e4f70e99 100644
--- a/source/blender/editors/uvedit/uvedit_draw.c
+++ b/source/blender/editors/uvedit/uvedit_draw.c
@@ -409,70 +409,49 @@ static void draw_uvs(SpaceImage *sima,
GPU_blend(true);
}
- switch (sima->dt_uv) {
- case SI_UVDT_DASH: {
- float dash_colors[2][4] = {
- {0.56f, 0.56f, 0.56f, overlay_alpha},
- {0.07f, 0.07f, 0.07f, overlay_alpha},
- };
- float viewport_size[4];
- GPU_viewport_size_get_f(viewport_size);
-
- GPU_line_width(1.0f);
- GPU_batch_program_set_builtin(batch->edges, GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
- GPU_batch_uniform_4fv_array(batch->edges, "colors", 2, (float *)dash_colors);
- GPU_batch_uniform_2f(batch->edges,
- "viewport_size",
- viewport_size[2] / UI_DPI_FAC,
- viewport_size[3] / UI_DPI_FAC);
- GPU_batch_uniform_1i(batch->edges, "colors_len", 2); /* "advanced" mode */
- GPU_batch_uniform_1f(batch->edges, "dash_width", 4.0f);
- GPU_batch_uniform_1f(batch->edges, "dash_factor", 0.5f);
- GPU_batch_draw(batch->edges);
- break;
- }
- case SI_UVDT_BLACK:
- case SI_UVDT_WHITE:
- case SI_UVDT_OUTLINE: {
- /* We could modify the vbo's data filling
- * instead of modifying the provoking vert. */
- glProvokingVertex(GL_FIRST_VERTEX_CONVENTION);
-
- UI_GetThemeColor3fv(TH_EDGE_SELECT, col2);
- col2[3] = overlay_alpha;
-
- GPU_batch_program_set_builtin(
- batch->edges, (interpedges) ? GPU_SHADER_2D_UV_EDGES_SMOOTH : GPU_SHADER_2D_UV_EDGES);
-
- if (sima->dt_uv == SI_UVDT_OUTLINE) {
- /* Black Outline. */
- GPU_line_width(3.0f);
- GPU_batch_uniform_4f(batch->edges, "edgeColor", 0.0f, 0.0f, 0.0f, overlay_alpha);
- GPU_batch_uniform_4f(batch->edges, "selectColor", 0.0f, 0.0f, 0.0f, overlay_alpha);
- GPU_batch_draw(batch->edges);
-
- UI_GetThemeColor3fv(TH_WIRE_EDIT, col1);
- }
- else if (sima->dt_uv == SI_UVDT_WHITE) {
- copy_v3_fl3(col1, 1.0f, 1.0f, 1.0f);
- }
- else {
- copy_v3_fl3(col1, 0.0f, 0.0f, 0.0f);
- }
- col1[3] = overlay_alpha;
+ {
+ /* We could modify the vbo's data filling
+ * instead of modifying the provoking vert. */
+ glProvokingVertex(GL_FIRST_VERTEX_CONVENTION);
+
+ UI_GetThemeColor3fv(TH_EDGE_SELECT, col2);
+ col2[3] = overlay_alpha;
- /* Inner Line. Use depth test to insure selection is drawn on top. */
- GPU_depth_test(true);
- GPU_line_width(1.0f);
- GPU_batch_uniform_4fv(batch->edges, "edgeColor", col1);
- GPU_batch_uniform_4fv(batch->edges, "selectColor", col2);
+ float dash_width = (sima->dt_uv & SI_UVDT_DASH) ? (4.0f * UI_DPI_FAC) : 9999.0f;
+
+ GPU_batch_program_set_builtin(
+ batch->edges, (interpedges) ? GPU_SHADER_2D_UV_EDGES_SMOOTH : GPU_SHADER_2D_UV_EDGES);
+
+ if (sima->dt_uv == SI_UVDT_OUTLINE) {
+ /* Black Outline. */
+ GPU_line_width(3.0f);
+ GPU_batch_uniform_4f(batch->edges, "edgeColor", 0.0f, 0.0f, 0.0f, overlay_alpha);
+ GPU_batch_uniform_4f(batch->edges, "selectColor", 0.0f, 0.0f, 0.0f, overlay_alpha);
+ GPU_batch_uniform_1f(batch->edges, "dashWidth", dash_width);
GPU_batch_draw(batch->edges);
- GPU_depth_test(false);
- glProvokingVertex(GL_LAST_VERTEX_CONVENTION);
- break;
+ UI_GetThemeColor3fv(TH_WIRE_EDIT, col1);
+ }
+ else if (sima->dt_uv == SI_UVDT_BLACK) {
+ copy_v3_fl3(col1, 0.0f, 0.0f, 0.0f);
}
+ else {
+ copy_v3_fl3(col1, 1.0f, 1.0f, 1.0f);
+ }
+ col1[3] = overlay_alpha;
+
+ /* Inner Line. Use depth test to insure selection is drawn on top. */
+ GPU_depth_test(true);
+ GPU_line_width(1.0f);
+ GPU_batch_uniform_4fv(batch->edges, "edgeColor", col1);
+ GPU_batch_uniform_4fv(batch->edges, "selectColor", col2);
+ GPU_batch_uniform_1f(batch->edges, "dashWidth", dash_width);
+ GPU_batch_draw(batch->edges);
+ GPU_depth_test(false);
+
+ glProvokingVertex(GL_LAST_VERTEX_CONVENTION);
}
+
if (sima->flag & SI_SMOOTH_UV) {
GPU_line_smooth(false);
GPU_blend(false);
@@ -481,6 +460,7 @@ static void draw_uvs(SpaceImage *sima,
GPU_blend(false);
}
}
+
if (batch->verts || batch->facedots) {
UI_GetThemeColor4fv(TH_VERTEX_SELECT, col2);
if (batch->verts) {
diff --git a/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp b/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp
index 7bceb036846..996906826e9 100644
--- a/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp
+++ b/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp
@@ -795,7 +795,7 @@ void BlenderFileLoader::insertShapeNode(Object *ob, Mesh *me, int id)
// sets the id of the rep
rep->setId(Id(id, 0));
rep->setName(ob->id.name + 2);
- rep->setLibraryPath(ob->id.lib ? ob->id.lib->name : "");
+ rep->setLibraryPath(ob->id.lib ? ob->id.lib->filepath : "");
const BBox<Vec3r> bbox = BBox<Vec3r>(Vec3r(ls.minBBox[0], ls.minBBox[1], ls.minBBox[2]),
Vec3r(ls.maxBBox[0], ls.maxBBox[1], ls.maxBBox[2]));
diff --git a/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp b/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp
index 0f4263162a6..26070a88e5d 100644
--- a/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp
+++ b/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp
@@ -341,8 +341,8 @@ static void prepare(Render *re, ViewLayer *view_layer, Depsgraph *depsgraph)
const char *id_name = module_conf->script->id.name + 2;
if (G.debug & G_DEBUG_FREESTYLE) {
cout << " " << layer_count + 1 << ": " << id_name;
- if (module_conf->script->name) {
- cout << " (" << module_conf->script->name << ")";
+ if (module_conf->script->filepath) {
+ cout << " (" << module_conf->script->filepath << ")";
}
cout << endl;
}
diff --git a/source/blender/freestyle/intern/python/BPy_Freestyle.cpp b/source/blender/freestyle/intern/python/BPy_Freestyle.cpp
index e7fe660cf93..33078e6ba6a 100644
--- a/source/blender/freestyle/intern/python/BPy_Freestyle.cpp
+++ b/source/blender/freestyle/intern/python/BPy_Freestyle.cpp
@@ -267,7 +267,7 @@ static PyObject *Freestyle_evaluateCurveMappingF(PyObject * /*self*/, PyObject *
BKE_curvemapping_initialize(cumap);
/* disable extrapolation if enabled */
if ((cumap->flag & CUMA_EXTEND_EXTRAPOLATE)) {
- cumap->flag &= ~(CUMA_EXTEND_EXTRAPOLATE);
+ cumap->flag &= ~CUMA_EXTEND_EXTRAPOLATE;
BKE_curvemapping_changed(cumap, 0);
}
return PyFloat_FromDouble(BKE_curvemapping_evaluateF(cumap, cur, value));
diff --git a/source/blender/freestyle/intern/view_map/AutoPtrHelper.h b/source/blender/freestyle/intern/view_map/AutoPtrHelper.h
index 9da109ab2a6..fb2a9d73d13 100644
--- a/source/blender/freestyle/intern/view_map/AutoPtrHelper.h
+++ b/source/blender/freestyle/intern/view_map/AutoPtrHelper.h
@@ -26,7 +26,6 @@
namespace Freestyle {
-#if __cplusplus > 199711L
template<typename T> class AutoPtr : public std::unique_ptr<T> {
public:
AutoPtr() : std::unique_ptr<T>()
@@ -36,26 +35,16 @@ template<typename T> class AutoPtr : public std::unique_ptr<T> {
{
}
- /* TODO(sergey): Is there more clear way to do this? */
+ /* Mimic behavior of legacy auto_ptr.
+ * Keep implementation as small as possible, hens delete assignment operator. */
+
template<typename X> AutoPtr(AutoPtr<X> &other) : std::unique_ptr<T>(other.get())
{
other.release();
}
+
+ template<typename X> AutoPtr &operator=(AutoPtr<X> &other) = delete;
};
-#else
-template<typename T> class AutoPtr : public std::auto_ptr<T> {
- public:
- AutoPtr() : std::auto_ptr<T>()
- {
- }
- AutoPtr(T *ptr) : std::auto_ptr<T>(ptr)
- {
- }
- AutoPtr(std::auto_ptr_ref<T> ref) : std::auto_ptr<T>(ref)
- {
- }
-};
-#endif
} /* namespace Freestyle */
diff --git a/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp b/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp
index 40ab3ada777..c3c43ce6722 100644
--- a/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp
+++ b/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp
@@ -2273,10 +2273,10 @@ void ViewMapBuilder::ComputeIntersections(ViewMap *ioViewMap,
#endif
}
-struct less_SVertex2D : public binary_function<SVertex *, SVertex *, bool> {
+struct less_SVertex2D {
real epsilon;
- less_SVertex2D(real eps) : binary_function<SVertex *, SVertex *, bool>()
+ less_SVertex2D(real eps)
{
epsilon = eps;
}
@@ -2303,10 +2303,10 @@ struct less_SVertex2D : public binary_function<SVertex *, SVertex *, bool> {
typedef Segment<FEdge *, Vec3r> segment;
typedef Intersection<segment> intersection;
-struct less_Intersection : public binary_function<intersection *, intersection *, bool> {
+struct less_Intersection {
segment *edge;
- less_Intersection(segment *iEdge) : binary_function<intersection *, intersection *, bool>()
+ less_Intersection(segment *iEdge)
{
edge = iEdge;
}
diff --git a/source/blender/functions/CMakeLists.txt b/source/blender/functions/CMakeLists.txt
index 9ce1d3ac2fe..703d3c393e8 100644
--- a/source/blender/functions/CMakeLists.txt
+++ b/source/blender/functions/CMakeLists.txt
@@ -27,10 +27,26 @@ set(INC_SYS
)
set(SRC
+ intern/attributes_ref.cc
intern/cpp_types.cc
+ intern/multi_function.cc
+ intern/multi_function_network.cc
+ intern/multi_function_network_evaluation.cc
+ FN_array_spans.hh
+ FN_attributes_ref.hh
FN_cpp_type.hh
FN_cpp_types.hh
+ FN_multi_function.hh
+ FN_multi_function_builder.hh
+ FN_multi_function_context.hh
+ FN_multi_function_data_type.hh
+ FN_multi_function_network.hh
+ FN_multi_function_network_evaluation.hh
+ FN_multi_function_param_type.hh
+ FN_multi_function_params.hh
+ FN_multi_function_signature.hh
+ FN_spans.hh
)
set(LIB
diff --git a/source/blender/functions/FN_array_spans.hh b/source/blender/functions/FN_array_spans.hh
new file mode 100644
index 00000000000..acd3e921b50
--- /dev/null
+++ b/source/blender/functions/FN_array_spans.hh
@@ -0,0 +1,209 @@
+/*
+ * 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.
+ */
+
+#ifndef __FN_ARRAY_SPANS_HH__
+#define __FN_ARRAY_SPANS_HH__
+
+/** \file
+ * \ingroup fn
+ *
+ * An ArraySpan is a span where every element contains an array (instead of a single element as is
+ * the case in a normal span). It's main use case is to reference many small arrays.
+ */
+
+#include "FN_spans.hh"
+
+namespace blender {
+namespace fn {
+
+/**
+ * Depending on the use case, the referenced data might have a different structure. More
+ * categories can be added when necessary.
+ */
+enum class VArraySpanCategory {
+ SingleArray,
+ StartsAndSizes,
+};
+
+template<typename T> class VArraySpanBase {
+ protected:
+ uint m_virtual_size;
+ VArraySpanCategory m_category;
+
+ union {
+ struct {
+ const T *start;
+ uint size;
+ } single_array;
+ struct {
+ const T *const *starts;
+ const uint *sizes;
+ } starts_and_sizes;
+ } m_data;
+
+ public:
+ bool is_single_array() const
+ {
+ switch (m_category) {
+ case VArraySpanCategory::SingleArray:
+ return true;
+ case VArraySpanCategory::StartsAndSizes:
+ return m_virtual_size == 1;
+ }
+ BLI_assert(false);
+ return false;
+ }
+
+ bool is_empty() const
+ {
+ return this->m_virtual_size == 0;
+ }
+
+ uint size() const
+ {
+ return this->m_virtual_size;
+ }
+};
+
+/**
+ * A virtual array span. Every element of this span contains a virtual span. So it behaves like
+ * a blender::Span, but might not be backed up by an actual array.
+ */
+template<typename T> class VArraySpan : public VArraySpanBase<T> {
+ private:
+ friend class GVArraySpan;
+
+ VArraySpan(const VArraySpanBase<void> &other)
+ {
+ memcpy(this, &other, sizeof(VArraySpanBase<void>));
+ }
+
+ public:
+ VArraySpan()
+ {
+ this->m_virtual_size = 0;
+ this->m_category = VArraySpanCategory::StartsAndSizes;
+ this->m_data.starts_and_sizes.starts = nullptr;
+ this->m_data.starts_and_sizes.sizes = nullptr;
+ }
+
+ VArraySpan(Span<T> span, uint virtual_size)
+ {
+ this->m_virtual_size = virtual_size;
+ this->m_category = VArraySpanCategory::SingleArray;
+ this->m_data.single_array.start = span.data();
+ this->m_data.single_array.size = span.size();
+ }
+
+ VArraySpan(Span<const T *> starts, Span<uint> sizes)
+ {
+ BLI_assert(starts.size() == sizes.size());
+ this->m_virtual_size = starts.size();
+ this->m_category = VArraySpanCategory::StartsAndSizes;
+ this->m_data.starts_and_sizes.starts = starts.begin();
+ this->m_data.starts_and_sizes.sizes = sizes.begin();
+ }
+
+ VSpan<T> operator[](uint index) const
+ {
+ BLI_assert(index < this->m_virtual_size);
+ switch (this->m_category) {
+ case VArraySpanCategory::SingleArray:
+ return VSpan<T>(Span<T>(this->m_data.single_array.start, this->m_data.single_array.size));
+ case VArraySpanCategory::StartsAndSizes:
+ return VSpan<T>(Span<T>(this->m_data.starts_and_sizes.starts[index],
+ this->m_data.starts_and_sizes.sizes[index]));
+ }
+ BLI_assert(false);
+ return {};
+ }
+};
+
+/**
+ * A generic virtual array span. It's just like a VArraySpan, but the type is only known at
+ * run-time.
+ */
+class GVArraySpan : public VArraySpanBase<void> {
+ private:
+ const CPPType *m_type;
+
+ GVArraySpan() = default;
+
+ public:
+ GVArraySpan(const CPPType &type)
+ {
+ this->m_type = &type;
+ this->m_virtual_size = 0;
+ this->m_category = VArraySpanCategory::StartsAndSizes;
+ this->m_data.starts_and_sizes.starts = nullptr;
+ this->m_data.starts_and_sizes.sizes = nullptr;
+ }
+
+ GVArraySpan(GSpan array, uint virtual_size)
+ {
+ this->m_type = &array.type();
+ this->m_virtual_size = virtual_size;
+ this->m_category = VArraySpanCategory::SingleArray;
+ this->m_data.single_array.start = array.buffer();
+ this->m_data.single_array.size = array.size();
+ }
+
+ GVArraySpan(const CPPType &type, Span<const void *> starts, Span<uint> sizes)
+ {
+ BLI_assert(starts.size() == sizes.size());
+ this->m_type = &type;
+ this->m_virtual_size = starts.size();
+ this->m_category = VArraySpanCategory::StartsAndSizes;
+ this->m_data.starts_and_sizes.starts = (void **)starts.begin();
+ this->m_data.starts_and_sizes.sizes = sizes.begin();
+ }
+
+ template<typename T> GVArraySpan(VArraySpan<T> other)
+ {
+ this->m_type = &CPPType::get<T>();
+ memcpy(this, &other, sizeof(VArraySpanBase<void>));
+ }
+
+ const CPPType &type() const
+ {
+ return *this->m_type;
+ }
+
+ template<typename T> VArraySpan<T> typed() const
+ {
+ BLI_assert(m_type->is<T>());
+ return VArraySpan<T>(*this);
+ }
+
+ GVSpan operator[](uint index) const
+ {
+ BLI_assert(index < m_virtual_size);
+ switch (m_category) {
+ case VArraySpanCategory::SingleArray:
+ return GVSpan(GSpan(*m_type, m_data.single_array.start, m_data.single_array.size));
+ case VArraySpanCategory::StartsAndSizes:
+ return GVSpan(GSpan(
+ *m_type, m_data.starts_and_sizes.starts[index], m_data.starts_and_sizes.sizes[index]));
+ }
+ BLI_assert(false);
+ return GVSpan(*m_type);
+ }
+};
+
+} // namespace fn
+} // namespace blender
+
+#endif /* __FN_ARRAY_SPANS_HH__ */
diff --git a/source/blender/functions/FN_attributes_ref.hh b/source/blender/functions/FN_attributes_ref.hh
new file mode 100644
index 00000000000..383b26330bf
--- /dev/null
+++ b/source/blender/functions/FN_attributes_ref.hh
@@ -0,0 +1,248 @@
+/*
+ * 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.
+ */
+
+#ifndef __FN_ATTRIBUTES_REF_HH__
+#define __FN_ATTRIBUTES_REF_HH__
+
+/** \file
+ * \ingroup fn
+ *
+ * An AttributesRef references multiple arrays of equal length. Each array has a corresponding name
+ * and index.
+ */
+
+#include <optional>
+
+#include "FN_spans.hh"
+
+#include "BLI_linear_allocator.hh"
+#include "BLI_map.hh"
+#include "BLI_utility_mixins.hh"
+#include "BLI_vector_set.hh"
+
+namespace blender {
+namespace fn {
+
+class AttributesInfo;
+
+class AttributesInfoBuilder : NonCopyable, NonMovable {
+ private:
+ LinearAllocator<> m_allocator;
+ VectorSet<std::string> m_names;
+ Vector<const CPPType *> m_types;
+ Vector<void *> m_defaults;
+
+ friend AttributesInfo;
+
+ public:
+ AttributesInfoBuilder() = default;
+ ~AttributesInfoBuilder();
+
+ template<typename T> void add(StringRef name, const T &default_value)
+ {
+ this->add(name, CPPType::get<T>(), (const void *)&default_value);
+ }
+
+ void add(StringRef name, const CPPType &type, const void *default_value = nullptr);
+};
+
+/**
+ * Stores which attributes are in an AttributesRef. Every attribute has a unique index, a unique
+ * name, a type and a default value.
+ */
+class AttributesInfo : NonCopyable, NonMovable {
+ private:
+ LinearAllocator<> m_allocator;
+ Map<StringRefNull, uint> m_index_by_name;
+ Vector<StringRefNull> m_name_by_index;
+ Vector<const CPPType *> m_type_by_index;
+ Vector<void *> m_defaults;
+
+ public:
+ AttributesInfo() = default;
+ AttributesInfo(const AttributesInfoBuilder &builder);
+ ~AttributesInfo();
+
+ uint size() const
+ {
+ return m_name_by_index.size();
+ }
+
+ IndexRange index_range() const
+ {
+ return m_name_by_index.index_range();
+ }
+
+ StringRefNull name_of(uint index) const
+ {
+ return m_name_by_index[index];
+ }
+
+ uint index_of(StringRef name) const
+ {
+ return m_index_by_name.lookup_as(name);
+ }
+
+ const void *default_of(uint index) const
+ {
+ return m_defaults[index];
+ }
+
+ const void *default_of(StringRef name) const
+ {
+ return this->default_of(this->index_of(name));
+ }
+
+ template<typename T> const T &default_of(uint index) const
+ {
+ BLI_assert(m_type_by_index[index]->is<T>());
+ return *(T *)m_defaults[index];
+ }
+
+ template<typename T> const T &default_of(StringRef name) const
+ {
+ return this->default_of<T>(this->index_of(name));
+ }
+
+ const CPPType &type_of(uint index) const
+ {
+ return *m_type_by_index[index];
+ }
+
+ const CPPType &type_of(StringRef name) const
+ {
+ return this->type_of(this->index_of(name));
+ }
+
+ bool has_attribute(StringRef name, const CPPType &type) const
+ {
+ return this->try_index_of(name, type) >= 0;
+ }
+
+ int try_index_of(StringRef name) const
+ {
+ return (int)m_index_by_name.lookup_default_as(name, -1);
+ }
+
+ int try_index_of(StringRef name, const CPPType &type) const
+ {
+ int index = this->try_index_of(name);
+ if (index == -1) {
+ return -1;
+ }
+ else if (this->type_of((uint)index) == type) {
+ return index;
+ }
+ else {
+ return -1;
+ }
+ }
+};
+
+/**
+ * References multiple arrays that match the description of an AttributesInfo instance. This class
+ * is supposed to be relatively cheap to copy. It does not own any of the arrays itself.
+ */
+class MutableAttributesRef {
+ private:
+ const AttributesInfo *m_info;
+ Span<void *> m_buffers;
+ IndexRange m_range;
+
+ public:
+ MutableAttributesRef(const AttributesInfo &info, Span<void *> buffers, uint size)
+ : MutableAttributesRef(info, buffers, IndexRange(size))
+ {
+ }
+
+ MutableAttributesRef(const AttributesInfo &info, Span<void *> buffers, IndexRange range)
+ : m_info(&info), m_buffers(buffers), m_range(range)
+ {
+ }
+
+ uint size() const
+ {
+ return m_range.size();
+ }
+
+ const AttributesInfo &info() const
+ {
+ return *m_info;
+ }
+
+ GMutableSpan get(uint index) const
+ {
+ const CPPType &type = m_info->type_of(index);
+ void *ptr = POINTER_OFFSET(m_buffers[index], type.size() * m_range.start());
+ return GMutableSpan(type, ptr, m_range.size());
+ }
+
+ GMutableSpan get(StringRef name) const
+ {
+ return this->get(m_info->index_of(name));
+ }
+
+ template<typename T> MutableSpan<T> get(uint index) const
+ {
+ BLI_assert(m_info->type_of(index).is<T>());
+ return MutableSpan<T>((T *)m_buffers[index] + m_range.start(), m_range.size());
+ }
+
+ template<typename T> MutableSpan<T> get(StringRef name) const
+ {
+ return this->get<T>(m_info->index_of(name));
+ }
+
+ std::optional<GMutableSpan> try_get(StringRef name, const CPPType &type) const
+ {
+ int index = m_info->try_index_of(name, type);
+ if (index == -1) {
+ return {};
+ }
+ else {
+ return this->get((uint)index);
+ }
+ }
+
+ template<typename T> std::optional<MutableSpan<T>> try_get(StringRef name) const
+ {
+ int index = m_info->try_index_of(name);
+ if (index == -1) {
+ return {};
+ }
+ else if (m_info->type_of((uint)index).is<T>()) {
+ return this->get<T>((uint)index);
+ }
+ else {
+ return {};
+ }
+ }
+
+ MutableAttributesRef slice(IndexRange range) const
+ {
+ return this->slice(range.start(), range.size());
+ }
+
+ MutableAttributesRef slice(uint start, uint size) const
+ {
+ return MutableAttributesRef(*m_info, m_buffers, m_range.slice(start, size));
+ }
+};
+
+} // namespace fn
+} // namespace blender
+
+#endif /* __FN_ATTRIBUTES_REF_HH__ */
diff --git a/source/blender/functions/FN_cpp_type.hh b/source/blender/functions/FN_cpp_type.hh
index 1dc72e16e77..706ff85bdf3 100644
--- a/source/blender/functions/FN_cpp_type.hh
+++ b/source/blender/functions/FN_cpp_type.hh
@@ -21,7 +21,7 @@
* \ingroup functions
*
* The CPPType class is the core of the runtime-type-system used by the functions system. An
- * instance of this class can represent any C++ type, that is default-constructable, destructable,
+ * instance of this class can represent any C++ type, that is default-constructible, destructible,
* movable and copyable. Therefore it also works for all C types. This restrictions might need to
* be removed in the future, but for now every required type has these properties.
*
@@ -71,11 +71,7 @@
#include "BLI_string_ref.hh"
namespace blender {
-namespace FN {
-
-using blender::IndexMask;
-using blender::StringRef;
-using blender::StringRefNull;
+namespace fn {
class CPPType {
public:
@@ -241,14 +237,14 @@ class CPPType {
void construct_default_n(void *ptr, uint n) const
{
- BLI_assert(this->pointer_can_point_to_instance(ptr));
+ BLI_assert(this->pointer_has_valid_alignment(ptr));
m_construct_default_n(ptr, n);
}
void construct_default_indices(void *ptr, IndexMask index_mask) const
{
- BLI_assert(this->pointer_can_point_to_instance(ptr));
+ BLI_assert(this->pointer_has_valid_alignment(ptr));
m_construct_default_indices(ptr, index_mask);
}
@@ -270,14 +266,14 @@ class CPPType {
void destruct_n(void *ptr, uint n) const
{
- BLI_assert(this->pointer_can_point_to_instance(ptr));
+ BLI_assert(this->pointer_has_valid_alignment(ptr));
m_destruct_n(ptr, n);
}
void destruct_indices(void *ptr, IndexMask index_mask) const
{
- BLI_assert(this->pointer_can_point_to_instance(ptr));
+ BLI_assert(this->pointer_has_valid_alignment(ptr));
m_destruct_indices(ptr, index_mask);
}
@@ -300,8 +296,8 @@ class CPPType {
void copy_to_initialized_n(const void *src, void *dst, uint n) const
{
BLI_assert(src != dst);
- BLI_assert(this->pointer_can_point_to_instance(src));
- BLI_assert(this->pointer_can_point_to_instance(dst));
+ BLI_assert(this->pointer_has_valid_alignment(src));
+ BLI_assert(this->pointer_has_valid_alignment(dst));
m_copy_to_initialized_n(src, dst, n);
}
@@ -309,8 +305,8 @@ class CPPType {
void copy_to_initialized_indices(const void *src, void *dst, IndexMask index_mask) const
{
BLI_assert(src != dst);
- BLI_assert(this->pointer_can_point_to_instance(src));
- BLI_assert(this->pointer_can_point_to_instance(dst));
+ BLI_assert(this->pointer_has_valid_alignment(src));
+ BLI_assert(this->pointer_has_valid_alignment(dst));
m_copy_to_initialized_indices(src, dst, index_mask);
}
@@ -335,8 +331,8 @@ class CPPType {
void copy_to_uninitialized_n(const void *src, void *dst, uint n) const
{
BLI_assert(src != dst);
- BLI_assert(this->pointer_can_point_to_instance(src));
- BLI_assert(this->pointer_can_point_to_instance(dst));
+ BLI_assert(this->pointer_has_valid_alignment(src));
+ BLI_assert(this->pointer_has_valid_alignment(dst));
m_copy_to_uninitialized_n(src, dst, n);
}
@@ -344,8 +340,8 @@ class CPPType {
void copy_to_uninitialized_indices(const void *src, void *dst, IndexMask index_mask) const
{
BLI_assert(src != dst);
- BLI_assert(this->pointer_can_point_to_instance(src));
- BLI_assert(this->pointer_can_point_to_instance(dst));
+ BLI_assert(this->pointer_has_valid_alignment(src));
+ BLI_assert(this->pointer_has_valid_alignment(dst));
m_copy_to_uninitialized_indices(src, dst, index_mask);
}
@@ -370,8 +366,8 @@ class CPPType {
void relocate_to_initialized_n(void *src, void *dst, uint n) const
{
BLI_assert(src != dst);
- BLI_assert(this->pointer_can_point_to_instance(src));
- BLI_assert(this->pointer_can_point_to_instance(dst));
+ BLI_assert(this->pointer_has_valid_alignment(src));
+ BLI_assert(this->pointer_has_valid_alignment(dst));
m_relocate_to_initialized_n(src, dst, n);
}
@@ -379,8 +375,8 @@ class CPPType {
void relocate_to_initialized_indices(void *src, void *dst, IndexMask index_mask) const
{
BLI_assert(src != dst);
- BLI_assert(this->pointer_can_point_to_instance(src));
- BLI_assert(this->pointer_can_point_to_instance(dst));
+ BLI_assert(this->pointer_has_valid_alignment(src));
+ BLI_assert(this->pointer_has_valid_alignment(dst));
m_relocate_to_initialized_indices(src, dst, index_mask);
}
@@ -405,8 +401,8 @@ class CPPType {
void relocate_to_uninitialized_n(void *src, void *dst, uint n) const
{
BLI_assert(src != dst);
- BLI_assert(this->pointer_can_point_to_instance(src));
- BLI_assert(this->pointer_can_point_to_instance(dst));
+ BLI_assert(this->pointer_has_valid_alignment(src));
+ BLI_assert(this->pointer_has_valid_alignment(dst));
m_relocate_to_uninitialized_n(src, dst, n);
}
@@ -414,8 +410,8 @@ class CPPType {
void relocate_to_uninitialized_indices(void *src, void *dst, IndexMask index_mask) const
{
BLI_assert(src != dst);
- BLI_assert(this->pointer_can_point_to_instance(src));
- BLI_assert(this->pointer_can_point_to_instance(dst));
+ BLI_assert(this->pointer_has_valid_alignment(src));
+ BLI_assert(this->pointer_has_valid_alignment(dst));
m_relocate_to_uninitialized_indices(src, dst, index_mask);
}
@@ -435,8 +431,8 @@ class CPPType {
void fill_initialized_indices(const void *value, void *dst, IndexMask index_mask) const
{
- BLI_assert(this->pointer_can_point_to_instance(value));
- BLI_assert(this->pointer_can_point_to_instance(dst));
+ BLI_assert(this->pointer_has_valid_alignment(value));
+ BLI_assert(this->pointer_has_valid_alignment(dst));
m_fill_initialized_indices(value, dst, index_mask);
}
@@ -456,8 +452,8 @@ class CPPType {
void fill_uninitialized_indices(const void *value, void *dst, IndexMask index_mask) const
{
- BLI_assert(this->pointer_can_point_to_instance(value));
- BLI_assert(this->pointer_can_point_to_instance(dst));
+ BLI_assert(this->pointer_has_valid_alignment(value));
+ BLI_assert(this->pointer_has_valid_alignment(dst));
m_fill_uninitialized_indices(value, dst, index_mask);
}
@@ -487,6 +483,11 @@ class CPPType {
template<typename T> static const CPPType &get();
+ template<typename T> bool is() const
+ {
+ return this == &CPPType::get<T>();
+ }
+
private:
uint m_size;
uint m_alignment;
@@ -719,16 +720,18 @@ static std::unique_ptr<const CPPType> create_cpp_type(StringRef name, const T &d
return std::unique_ptr<const CPPType>(type);
}
-} // namespace FN
+} // namespace fn
} // namespace blender
#define MAKE_CPP_TYPE(IDENTIFIER, TYPE_NAME) \
static TYPE_NAME default_value_##IDENTIFIER; \
- static std::unique_ptr<const blender::FN::CPPType> CPPTYPE_##IDENTIFIER##_owner = \
- blender::FN::create_cpp_type<TYPE_NAME>(STRINGIFY(IDENTIFIER), default_value_##IDENTIFIER); \
- const blender::FN::CPPType &CPPType_##IDENTIFIER = *CPPTYPE_##IDENTIFIER##_owner; \
- template<> const blender::FN::CPPType &blender::FN::CPPType::get<TYPE_NAME>() \
+ static std::unique_ptr<const blender::fn::CPPType> CPPTYPE_##IDENTIFIER##_owner = \
+ blender::fn::create_cpp_type<TYPE_NAME>(STRINGIFY(IDENTIFIER), default_value_##IDENTIFIER); \
+ const blender::fn::CPPType &CPPType_##IDENTIFIER = *CPPTYPE_##IDENTIFIER##_owner; \
+ template<> const blender::fn::CPPType &blender::fn::CPPType::get<TYPE_NAME>() \
{ \
+ /* This can happen when trying to access a CPPType during static storage initialization. */ \
+ BLI_assert(CPPTYPE_##IDENTIFIER##_owner.get() != nullptr); \
return CPPType_##IDENTIFIER; \
}
diff --git a/source/blender/functions/FN_cpp_types.hh b/source/blender/functions/FN_cpp_types.hh
index 6ee8788cd52..c39b284d5c4 100644
--- a/source/blender/functions/FN_cpp_types.hh
+++ b/source/blender/functions/FN_cpp_types.hh
@@ -27,7 +27,7 @@
#include "FN_cpp_type.hh"
namespace blender {
-namespace FN {
+namespace fn {
extern const CPPType &CPPType_bool;
@@ -44,7 +44,7 @@ extern const CPPType &CPPType_Color4b;
extern const CPPType &CPPType_string;
-} // namespace FN
+} // namespace fn
} // namespace blender
#endif /* __FN_CPP_TYPES_HH__ */
diff --git a/source/blender/functions/FN_generic_vector_array.hh b/source/blender/functions/FN_generic_vector_array.hh
new file mode 100644
index 00000000000..6be1b68da4d
--- /dev/null
+++ b/source/blender/functions/FN_generic_vector_array.hh
@@ -0,0 +1,208 @@
+/*
+ * 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.
+ */
+
+#ifndef __FN_GENERIC_VECTOR_ARRAY_HH__
+#define __FN_GENERIC_VECTOR_ARRAY_HH__
+
+/** \file
+ * \ingroup fn
+ *
+ * A `GVectorArray` is a container for a fixed amount of dynamically growing arrays with a generic
+ * type. Its main use case is to store many small vectors with few separate allocations. Using this
+ * structure is generally more efficient than allocating each small vector separately.
+ *
+ * `GVectorArrayRef<T>` is a typed reference to a GVectorArray and makes it easier and safer to
+ * work with the class when the type is known at compile time.
+ */
+
+#include "FN_array_spans.hh"
+#include "FN_cpp_type.hh"
+
+#include "BLI_array.hh"
+#include "BLI_linear_allocator.hh"
+#include "BLI_utility_mixins.hh"
+
+namespace blender {
+namespace fn {
+
+template<typename T> class GVectorArrayRef;
+
+class GVectorArray : NonCopyable, NonMovable {
+ private:
+ const CPPType &m_type;
+ uint m_element_size;
+ Array<void *, 1> m_starts;
+ Array<uint, 1> m_lengths;
+ Array<uint, 1> m_capacities;
+ LinearAllocator<> m_allocator;
+
+ template<typename T> friend class GVectorArrayRef;
+
+ public:
+ GVectorArray() = delete;
+
+ GVectorArray(const CPPType &type, uint array_size)
+ : m_type(type),
+ m_element_size(type.size()),
+ m_starts(array_size),
+ m_lengths(array_size),
+ m_capacities(array_size)
+ {
+ m_starts.fill(nullptr);
+ m_lengths.fill(0);
+ m_capacities.fill(0);
+ }
+
+ ~GVectorArray()
+ {
+ if (m_type.is_trivially_destructible()) {
+ return;
+ }
+
+ for (uint i : m_starts.index_range()) {
+ m_type.destruct_n(m_starts[i], m_lengths[i]);
+ }
+ }
+
+ operator GVArraySpan() const
+ {
+ return GVArraySpan(m_type, m_starts.as_span(), m_lengths);
+ }
+
+ bool is_empty() const
+ {
+ return m_starts.size() == 0;
+ }
+
+ uint size() const
+ {
+ return m_starts.size();
+ }
+
+ const CPPType &type() const
+ {
+ return m_type;
+ }
+
+ Span<const void *> starts() const
+ {
+ return m_starts.as_span();
+ }
+
+ Span<uint> lengths() const
+ {
+ return m_lengths;
+ }
+
+ void append(uint index, const void *src)
+ {
+ uint old_length = m_lengths[index];
+ if (old_length == m_capacities[index]) {
+ this->grow_at_least_one(index);
+ }
+
+ void *dst = POINTER_OFFSET(m_starts[index], m_element_size * old_length);
+ m_type.copy_to_uninitialized(src, dst);
+ m_lengths[index]++;
+ }
+
+ void extend(uint index, GVSpan span)
+ {
+ BLI_assert(m_type == span.type());
+ for (uint i = 0; i < span.size(); i++) {
+ this->append(index, span[i]);
+ }
+ }
+
+ void extend(IndexMask mask, GVArraySpan array_span)
+ {
+ BLI_assert(m_type == array_span.type());
+ BLI_assert(mask.min_array_size() <= array_span.size());
+ for (uint i : mask) {
+ this->extend(i, array_span[i]);
+ }
+ }
+
+ GMutableSpan operator[](uint index)
+ {
+ BLI_assert(index < m_starts.size());
+ return GMutableSpan(m_type, m_starts[index], m_lengths[index]);
+ }
+ template<typename T> GVectorArrayRef<T> typed()
+ {
+ return GVectorArrayRef<T>(*this);
+ }
+
+ private:
+ void grow_at_least_one(uint index)
+ {
+ BLI_assert(m_lengths[index] == m_capacities[index]);
+ uint new_capacity = m_lengths[index] * 2 + 1;
+
+ void *new_buffer = m_allocator.allocate(m_element_size * new_capacity, m_type.alignment());
+ m_type.relocate_to_uninitialized_n(m_starts[index], new_buffer, m_lengths[index]);
+
+ m_starts[index] = new_buffer;
+ m_capacities[index] = new_capacity;
+ }
+};
+
+template<typename T> class GVectorArrayRef {
+ private:
+ GVectorArray *m_vector_array;
+
+ public:
+ GVectorArrayRef(GVectorArray &vector_array) : m_vector_array(&vector_array)
+ {
+ BLI_assert(vector_array.m_type.is<T>());
+ }
+
+ void append(uint index, const T &value)
+ {
+ m_vector_array->append(index, &value);
+ }
+
+ void extend(uint index, Span<T> values)
+ {
+ m_vector_array->extend(index, values);
+ }
+
+ void extend(uint index, VSpan<T> values)
+ {
+ m_vector_array->extend(index, GVSpan(values));
+ }
+
+ MutableSpan<T> operator[](uint index)
+ {
+ BLI_assert(index < m_vector_array->m_starts.size());
+ return MutableSpan<T>((T *)m_vector_array->m_starts[index], m_vector_array->m_lengths[index]);
+ }
+
+ uint size() const
+ {
+ return m_vector_array->size();
+ }
+
+ bool is_empty() const
+ {
+ return m_vector_array->is_empty();
+ }
+};
+
+} // namespace fn
+} // namespace blender
+
+#endif /* __FN_GENERIC_VECTOR_ARRAY_HH__ */
diff --git a/source/blender/functions/FN_multi_function.hh b/source/blender/functions/FN_multi_function.hh
new file mode 100644
index 00000000000..a7e964b6651
--- /dev/null
+++ b/source/blender/functions/FN_multi_function.hh
@@ -0,0 +1,108 @@
+/*
+ * 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.
+ */
+
+#ifndef __FN_MULTI_FUNCTION_HH__
+#define __FN_MULTI_FUNCTION_HH__
+
+/** \file
+ * \ingroup fn
+ *
+ * A `MultiFunction` encapsulates a function that is optimized for throughput (instead of latency).
+ * The throughput is optimized by always processing many elements at once, instead of each element
+ * separately. This is ideal for functions that are evaluated often (e.g. for every particle).
+ *
+ * By processing a lot of data at once, individual functions become easier to optimize for humans
+ * and for the compiler. Furthermore, performance profiles become easier to understand and show
+ * better where bottlenecks are.
+ *
+ * Every multi-function has a name and an ordered list of parameters. Parameters are used for input
+ * and output. In fact, there are three kinds of parameters: inputs, outputs and mutable (which is
+ * combination of input and output).
+ *
+ * To call a multi-function, one has to provide three things:
+ * - `MFParams`: This references the input and output arrays that the function works with. The
+ * arrays are not owned by MFParams.
+ * - `IndexMask`: An array of indices indicating which indices in the provided arrays should be
+ * touched/processed.
+ * - `MFContext`: Further information for the called function.
+ *
+ * A new multi-function is generally implemented as follows:
+ * 1. Create a new subclass of MultiFunction.
+ * 2. Implement a constructor that initialized the signature of the function.
+ * 3. Override the `call` function.
+ */
+
+#include "FN_multi_function_context.hh"
+#include "FN_multi_function_params.hh"
+
+namespace blender {
+namespace fn {
+
+class MultiFunction {
+ private:
+ MFSignature m_signature;
+
+ public:
+ virtual ~MultiFunction()
+ {
+ }
+
+ virtual void call(IndexMask mask, MFParams params, MFContext context) const = 0;
+
+ IndexRange param_indices() const
+ {
+ return m_signature.param_types.index_range();
+ }
+
+ MFParamType param_type(uint param_index) const
+ {
+ return m_signature.param_types[param_index];
+ }
+
+ StringRefNull param_name(uint param_index) const
+ {
+ return m_signature.param_names[param_index];
+ }
+
+ StringRefNull name() const
+ {
+ return m_signature.function_name;
+ }
+
+ const MFSignature &signature() const
+ {
+ return m_signature;
+ }
+
+ protected:
+ MFSignatureBuilder get_builder(std::string function_name)
+ {
+ m_signature.function_name = std::move(function_name);
+ return MFSignatureBuilder(m_signature);
+ }
+};
+
+inline MFParamsBuilder::MFParamsBuilder(const class MultiFunction &fn, uint min_array_size)
+ : MFParamsBuilder(fn.signature(), min_array_size)
+{
+}
+
+extern const MultiFunction &dummy_multi_function;
+
+} // namespace fn
+} // namespace blender
+
+#endif /* __FN_MULTI_FUNCTION_HH__ */
diff --git a/source/blender/functions/FN_multi_function_builder.hh b/source/blender/functions/FN_multi_function_builder.hh
new file mode 100644
index 00000000000..9fcf31443b2
--- /dev/null
+++ b/source/blender/functions/FN_multi_function_builder.hh
@@ -0,0 +1,232 @@
+/*
+ * 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.
+ */
+
+#ifndef __FN_MULTI_FUNCTION_BUILDER_HH__
+#define __FN_MULTI_FUNCTION_BUILDER_HH__
+
+/** \file
+ * \ingroup fn
+ *
+ * This file contains several utilities to create multi-functions with less redundant code.
+ */
+
+#include <functional>
+
+#include "FN_multi_function.hh"
+
+namespace blender {
+namespace fn {
+
+/**
+ * Generates a multi-function with the following parameters:
+ * 1. single input (SI) of type In1
+ * 2. single output (SO) of type Out1
+ *
+ * This example creates a function that adds 10 to the incoming values:
+ * CustomMF_SI_SO<int, int> fn("add 10", [](int value) { return value + 10; });
+ */
+template<typename In1, typename Out1> class CustomMF_SI_SO : public MultiFunction {
+ private:
+ using FunctionT = std::function<void(IndexMask, VSpan<In1>, MutableSpan<Out1>)>;
+ FunctionT m_function;
+
+ public:
+ CustomMF_SI_SO(StringRef name, FunctionT function) : m_function(std::move(function))
+ {
+ MFSignatureBuilder signature = this->get_builder(name);
+ signature.single_input<In1>("In1");
+ signature.single_output<Out1>("Out1");
+ }
+
+ template<typename ElementFuncT>
+ CustomMF_SI_SO(StringRef name, ElementFuncT element_fn)
+ : CustomMF_SI_SO(name, CustomMF_SI_SO::create_function(element_fn))
+ {
+ }
+
+ template<typename ElementFuncT> static FunctionT create_function(ElementFuncT element_fn)
+ {
+ return [=](IndexMask mask, VSpan<In1> in1, MutableSpan<Out1> out1) {
+ mask.foreach_index([&](uint i) { new ((void *)&out1[i]) Out1(element_fn(in1[i])); });
+ };
+ }
+
+ void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
+ {
+ VSpan<In1> in1 = params.readonly_single_input<In1>(0);
+ MutableSpan<Out1> out1 = params.uninitialized_single_output<Out1>(1);
+ m_function(mask, in1, out1);
+ }
+};
+
+/**
+ * Generates a multi-function with the following parameters:
+ * 1. single input (SI) of type In1
+ * 2. single input (SI) of type In2
+ * 3. single output (SO) of type Out1
+ */
+template<typename In1, typename In2, typename Out1>
+class CustomMF_SI_SI_SO : public MultiFunction {
+ private:
+ using FunctionT = std::function<void(IndexMask, VSpan<In1>, VSpan<In2>, MutableSpan<Out1>)>;
+ FunctionT m_function;
+
+ public:
+ CustomMF_SI_SI_SO(StringRef name, FunctionT function) : m_function(std::move(function))
+ {
+ MFSignatureBuilder signature = this->get_builder(name);
+ signature.single_input<In1>("In1");
+ signature.single_input<In2>("In2");
+ signature.single_output<Out1>("Out1");
+ }
+
+ template<typename ElementFuncT>
+ CustomMF_SI_SI_SO(StringRef name, ElementFuncT element_fn)
+ : CustomMF_SI_SI_SO(name, CustomMF_SI_SI_SO::create_function(element_fn))
+ {
+ }
+
+ template<typename ElementFuncT> static FunctionT create_function(ElementFuncT element_fn)
+ {
+ return [=](IndexMask mask, VSpan<In1> in1, VSpan<In2> in2, MutableSpan<Out1> out1) {
+ mask.foreach_index([&](uint i) { new ((void *)&out1[i]) Out1(element_fn(in1[i], in2[i])); });
+ };
+ }
+
+ void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
+ {
+ VSpan<In1> in1 = params.readonly_single_input<In1>(0);
+ VSpan<In2> in2 = params.readonly_single_input<In2>(1);
+ MutableSpan<Out1> out1 = params.uninitialized_single_output<Out1>(2);
+ m_function(mask, in1, in2, out1);
+ }
+};
+
+/**
+ * Generates a multi-function with the following parameters:
+ * 1. single input (SI) of type In1
+ * 2. single input (SI) of type In2
+ * 3. single input (SI) of type In3
+ * 4. single output (SO) of type Out1
+ */
+template<typename In1, typename In2, typename In3, typename Out1>
+class CustomMF_SI_SI_SI_SO : public MultiFunction {
+ private:
+ using FunctionT =
+ std::function<void(IndexMask, VSpan<In1>, VSpan<In2>, VSpan<In3>, MutableSpan<Out1>)>;
+ FunctionT m_function;
+
+ public:
+ CustomMF_SI_SI_SI_SO(StringRef name, FunctionT function) : m_function(std::move(function))
+ {
+ MFSignatureBuilder signature = this->get_builder(name);
+ signature.single_input<In1>("In1");
+ signature.single_input<In2>("In2");
+ signature.single_input<In3>("In3");
+ signature.single_output<Out1>("Out1");
+ }
+
+ template<typename ElementFuncT>
+ CustomMF_SI_SI_SI_SO(StringRef name, ElementFuncT element_fn)
+ : CustomMF_SI_SI_SI_SO(name, CustomMF_SI_SI_SI_SO::create_function(element_fn))
+ {
+ }
+
+ template<typename ElementFuncT> static FunctionT create_function(ElementFuncT element_fn)
+ {
+ return [=](IndexMask mask,
+ VSpan<In1> in1,
+ VSpan<In2> in2,
+ VSpan<In3> in3,
+ MutableSpan<Out1> out1) {
+ mask.foreach_index(
+ [&](uint i) { new ((void *)&out1[i]) Out1(element_fn(in1[i], in2[i], in3[i])); });
+ };
+ }
+
+ void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
+ {
+ VSpan<In1> in1 = params.readonly_single_input<In1>(0);
+ VSpan<In2> in2 = params.readonly_single_input<In2>(1);
+ VSpan<In3> in3 = params.readonly_single_input<In3>(2);
+ MutableSpan<Out1> out1 = params.uninitialized_single_output<Out1>(3);
+ m_function(mask, in1, in2, in3, out1);
+ }
+};
+
+/**
+ * Generates a multi-function with the following parameters:
+ * 1. single mutable (SM) of type Mut1
+ */
+template<typename Mut1> class CustomMF_SM : public MultiFunction {
+ private:
+ using FunctionT = std::function<void(IndexMask, MutableSpan<Mut1>)>;
+ FunctionT m_function;
+
+ public:
+ CustomMF_SM(StringRef name, FunctionT function) : m_function(std::move(function))
+ {
+ MFSignatureBuilder signature = this->get_builder(name);
+ signature.single_mutable<Mut1>("Mut1");
+ }
+
+ template<typename ElementFuncT>
+ CustomMF_SM(StringRef name, ElementFuncT element_fn)
+ : CustomMF_SM(name, CustomMF_SM::create_function(element_fn))
+ {
+ }
+
+ template<typename ElementFuncT> static FunctionT create_function(ElementFuncT element_fn)
+ {
+ return [=](IndexMask mask, MutableSpan<Mut1> mut1) {
+ mask.foreach_index([&](uint i) { element_fn(mut1[i]); });
+ };
+ }
+
+ void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
+ {
+ MutableSpan<Mut1> mut1 = params.single_mutable<Mut1>(0);
+ m_function(mask, mut1);
+ }
+};
+
+/**
+ * Generates a multi-function that outputs a constant value.
+ */
+template<typename T> class CustomMF_Constant : public MultiFunction {
+ private:
+ T m_value;
+
+ public:
+ template<typename U> CustomMF_Constant(U &&value) : m_value(std::forward<U>(value))
+ {
+ MFSignatureBuilder signature = this->get_builder("Constant");
+ std::stringstream ss;
+ ss << m_value;
+ signature.single_output<T>(ss.str());
+ }
+
+ void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
+ {
+ MutableSpan<T> output = params.uninitialized_single_output<T>(0);
+ mask.foreach_index([&](uint i) { new (&output[i]) T(m_value); });
+ }
+};
+
+} // namespace fn
+} // namespace blender
+
+#endif /* __FN_MULTI_FUNCTION_BUILDER_HH__ */
diff --git a/source/blender/functions/FN_multi_function_context.hh b/source/blender/functions/FN_multi_function_context.hh
new file mode 100644
index 00000000000..2fb1cc94812
--- /dev/null
+++ b/source/blender/functions/FN_multi_function_context.hh
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+#ifndef __FN_MULTI_FUNCTION_CONTEXT_HH__
+#define __FN_MULTI_FUNCTION_CONTEXT_HH__
+
+/** \file
+ * \ingroup fn
+ *
+ * An #MFContext is passed along with every call to a multi-function. Right now it does nothing,
+ * but it can be used for the following purposes:
+ * - Pass debug information up and down the function call stack.
+ * - Pass reusable memory buffers to sub-functions to increase performance.
+ * - Pass cached data to called functions.
+ */
+
+#include "BLI_utildefines.h"
+
+namespace blender {
+namespace fn {
+
+class MFContextBuilder {
+};
+
+class MFContext {
+ public:
+ MFContext(MFContextBuilder &UNUSED(builder))
+ {
+ }
+};
+
+} // namespace fn
+} // namespace blender
+
+#endif /* __FN_MULTI_FUNCTION_CONTEXT_HH__ */
diff --git a/source/blender/functions/FN_multi_function_data_type.hh b/source/blender/functions/FN_multi_function_data_type.hh
new file mode 100644
index 00000000000..1a7b179c6ae
--- /dev/null
+++ b/source/blender/functions/FN_multi_function_data_type.hh
@@ -0,0 +1,127 @@
+/*
+ * 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.
+ */
+
+#ifndef __FN_MULTI_FUNCTION_DATA_TYPE_HH__
+#define __FN_MULTI_FUNCTION_DATA_TYPE_HH__
+
+/** \file
+ * \ingroup fn
+ *
+ * A MFDataType describes what type of data a multi-function gets as input, outputs or mutates.
+ * Currently, only individual elements or vectors of elements are supported. Adding more data types
+ * is possible when necessary.
+ */
+
+#include "FN_cpp_type.hh"
+
+namespace blender {
+namespace fn {
+
+class MFDataType {
+ public:
+ enum Category {
+ Single,
+ Vector,
+ };
+
+ private:
+ Category m_category;
+ const CPPType *m_type;
+
+ MFDataType(Category category, const CPPType &type) : m_category(category), m_type(&type)
+ {
+ }
+
+ public:
+ MFDataType() = default;
+
+ static MFDataType ForSingle(const CPPType &type)
+ {
+ return MFDataType(Single, type);
+ }
+
+ static MFDataType ForVector(const CPPType &type)
+ {
+ return MFDataType(Vector, type);
+ }
+
+ template<typename T> static MFDataType ForSingle()
+ {
+ return MFDataType::ForSingle(CPPType::get<T>());
+ }
+
+ template<typename T> static MFDataType ForVector()
+ {
+ return MFDataType::ForVector(CPPType::get<T>());
+ }
+
+ bool is_single() const
+ {
+ return m_category == Single;
+ }
+
+ bool is_vector() const
+ {
+ return m_category == Vector;
+ }
+
+ Category category() const
+ {
+ return m_category;
+ }
+
+ const CPPType &single_type() const
+ {
+ BLI_assert(this->is_single());
+ return *m_type;
+ }
+
+ const CPPType &vector_base_type() const
+ {
+ BLI_assert(this->is_vector());
+ return *m_type;
+ }
+
+ friend bool operator==(const MFDataType &a, const MFDataType &b);
+ friend bool operator!=(const MFDataType &a, const MFDataType &b);
+
+ std::string to_string() const
+ {
+ switch (m_category) {
+ case Single:
+ return m_type->name();
+ case Vector:
+ return m_type->name() + " Vector";
+ }
+ BLI_assert(false);
+ return "";
+ }
+};
+
+inline bool operator==(const MFDataType &a, const MFDataType &b)
+{
+ return a.m_category == b.m_category && a.m_type == b.m_type;
+}
+
+inline bool operator!=(const MFDataType &a, const MFDataType &b)
+{
+ return !(a == b);
+}
+
+} // namespace fn
+} // namespace blender
+
+#endif /* __FN_MULTI_FUNCTION_DATA_TYPE_HH__ */
diff --git a/source/blender/functions/FN_multi_function_network.hh b/source/blender/functions/FN_multi_function_network.hh
new file mode 100644
index 00000000000..bb0c870746b
--- /dev/null
+++ b/source/blender/functions/FN_multi_function_network.hh
@@ -0,0 +1,495 @@
+/*
+ * 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.
+ */
+
+#ifndef __FN_MULTI_FUNCTION_NETWORK_HH__
+#define __FN_MULTI_FUNCTION_NETWORK_HH__
+
+/** \file
+ * \ingroup fn
+ *
+ * A multi-function network (`MFNetwork`) allows you to connect multiple multi-functions. The
+ * `MFNetworkEvaluator` is a multi-function that wraps an entire network into a new multi-function
+ * (which can be used in another network and so on).
+ *
+ * A MFNetwork is a graph data structure with two kinds of nodes:
+ * - MFFunctionNode: Represents a multi-function. Its input and output sockets correspond to
+ * parameters of the referenced multi-function.
+ * - MFDummyNode: Does not reference a multi-function. Instead it just has sockets that can be
+ * used to represent node group inputs and outputs.
+ *
+ * Links represent data flow. Unlinked input sockets have no value. In order to execute a function
+ * node, all its inputs have to be connected to something.
+ *
+ * Links are only allowed between sockets with the exact same MFDataType. There are no implicit
+ * conversions.
+ *
+ * Every input and output parameter of a multi-function corresponds to exactly one input or output
+ * socket respectively. A multiple parameter belongs to exactly one input AND one output socket.
+ *
+ * There is an .to_dot() method that generates a graph in dot format for debugging purposes.
+ */
+
+#include "FN_multi_function.hh"
+
+#include "BLI_vector_set.hh"
+
+namespace blender {
+namespace fn {
+
+class MFNode;
+class MFFunctionNode;
+class MFDummyNode;
+class MFSocket;
+class MFInputSocket;
+class MFOutputSocket;
+class MFNetwork;
+
+class MFNode : NonCopyable, NonMovable {
+ protected:
+ MFNetwork *m_network;
+ Span<MFInputSocket *> m_inputs;
+ Span<MFOutputSocket *> m_outputs;
+ bool m_is_dummy;
+ uint m_id;
+
+ friend MFNetwork;
+
+ public:
+ StringRefNull name() const;
+
+ uint id() const;
+
+ MFNetwork &network();
+ const MFNetwork &network() const;
+
+ bool is_dummy() const;
+ bool is_function() const;
+
+ MFDummyNode &as_dummy();
+ const MFDummyNode &as_dummy() const;
+
+ MFFunctionNode &as_function();
+ const MFFunctionNode &as_function() const;
+
+ MFInputSocket &input(uint index);
+ const MFInputSocket &input(uint index) const;
+
+ MFOutputSocket &output(uint index);
+ const MFOutputSocket &output(uint index) const;
+
+ Span<MFInputSocket *> inputs();
+ Span<const MFInputSocket *> inputs() const;
+
+ Span<MFOutputSocket *> outputs();
+ Span<const MFOutputSocket *> outputs() const;
+
+ template<typename FuncT> void foreach_origin_socket(const FuncT &func) const;
+
+ bool all_inputs_have_origin() const;
+
+ private:
+ void destruct_sockets();
+};
+
+class MFFunctionNode : public MFNode {
+ private:
+ const MultiFunction *m_function;
+ Span<uint> m_input_param_indices;
+ Span<uint> m_output_param_indices;
+
+ friend MFNetwork;
+
+ public:
+ StringRefNull name() const;
+
+ const MultiFunction &function() const;
+
+ const MFInputSocket &input_for_param(uint param_index) const;
+ const MFOutputSocket &output_for_param(uint param_index) const;
+};
+
+class MFDummyNode : public MFNode {
+ private:
+ StringRefNull m_name;
+ MutableSpan<StringRefNull> m_input_names;
+ MutableSpan<StringRefNull> m_output_names;
+
+ friend MFNetwork;
+
+ public:
+ StringRefNull name() const;
+
+ Span<StringRefNull> input_names() const;
+ Span<StringRefNull> output_names() const;
+};
+
+class MFSocket : NonCopyable, NonMovable {
+ protected:
+ MFNode *m_node;
+ bool m_is_output;
+ uint m_index;
+ MFDataType m_data_type;
+ uint m_id;
+ StringRefNull m_name;
+
+ friend MFNetwork;
+
+ public:
+ StringRefNull name() const;
+
+ uint id() const;
+
+ const MFDataType &data_type() const;
+
+ MFNode &node();
+ const MFNode &node() const;
+
+ bool is_input() const;
+ bool is_output() const;
+
+ MFInputSocket &as_input();
+ const MFInputSocket &as_input() const;
+
+ MFOutputSocket &as_output();
+ const MFOutputSocket &as_output() const;
+};
+
+class MFInputSocket : public MFSocket {
+ private:
+ MFOutputSocket *m_origin;
+
+ friend MFNetwork;
+
+ public:
+ MFOutputSocket *origin();
+ const MFOutputSocket *origin() const;
+};
+
+class MFOutputSocket : public MFSocket {
+ private:
+ Vector<MFInputSocket *, 1> m_targets;
+
+ friend MFNetwork;
+
+ public:
+ Span<MFInputSocket *> targets();
+ Span<const MFInputSocket *> targets() const;
+};
+
+class MFNetwork : NonCopyable, NonMovable {
+ private:
+ LinearAllocator<> m_allocator;
+
+ VectorSet<MFFunctionNode *> m_function_nodes;
+ VectorSet<MFDummyNode *> m_dummy_nodes;
+
+ Vector<MFNode *> m_node_or_null_by_id;
+ Vector<MFSocket *> m_socket_or_null_by_id;
+
+ public:
+ MFNetwork() = default;
+ ~MFNetwork();
+
+ MFFunctionNode &add_function(const MultiFunction &function);
+ MFDummyNode &add_dummy(StringRef name,
+ Span<MFDataType> input_types,
+ Span<MFDataType> output_types,
+ Span<StringRef> input_names,
+ Span<StringRef> output_names);
+ void add_link(MFOutputSocket &from, MFInputSocket &to);
+
+ MFOutputSocket &add_input(StringRef name, MFDataType data_type);
+ MFInputSocket &add_output(StringRef name, MFDataType data_type);
+
+ void relink(MFOutputSocket &old_output, MFOutputSocket &new_output);
+
+ void remove(MFNode &node);
+
+ uint max_socket_id() const;
+
+ std::string to_dot() const;
+};
+
+/* --------------------------------------------------------------------
+ * MFNode inline methods.
+ */
+
+inline StringRefNull MFNode::name() const
+{
+ if (m_is_dummy) {
+ return this->as_dummy().name();
+ }
+ else {
+ return this->as_function().name();
+ }
+}
+
+inline uint MFNode::id() const
+{
+ return m_id;
+}
+
+inline MFNetwork &MFNode::network()
+{
+ return *m_network;
+}
+
+inline const MFNetwork &MFNode::network() const
+{
+ return *m_network;
+}
+
+inline bool MFNode::is_dummy() const
+{
+ return m_is_dummy;
+}
+
+inline bool MFNode::is_function() const
+{
+ return !m_is_dummy;
+}
+
+inline MFDummyNode &MFNode::as_dummy()
+{
+ BLI_assert(m_is_dummy);
+ return *(MFDummyNode *)this;
+}
+
+inline const MFDummyNode &MFNode::as_dummy() const
+{
+ BLI_assert(m_is_dummy);
+ return *(const MFDummyNode *)this;
+}
+
+inline MFFunctionNode &MFNode::as_function()
+{
+ BLI_assert(!m_is_dummy);
+ return *(MFFunctionNode *)this;
+}
+
+inline const MFFunctionNode &MFNode::as_function() const
+{
+ BLI_assert(!m_is_dummy);
+ return *(const MFFunctionNode *)this;
+}
+
+inline MFInputSocket &MFNode::input(uint index)
+{
+ return *m_inputs[index];
+}
+
+inline const MFInputSocket &MFNode::input(uint index) const
+{
+ return *m_inputs[index];
+}
+
+inline MFOutputSocket &MFNode::output(uint index)
+{
+ return *m_outputs[index];
+}
+
+inline const MFOutputSocket &MFNode::output(uint index) const
+{
+ return *m_outputs[index];
+}
+
+inline Span<MFInputSocket *> MFNode::inputs()
+{
+ return m_inputs;
+}
+
+inline Span<const MFInputSocket *> MFNode::inputs() const
+{
+ return m_inputs;
+}
+
+inline Span<MFOutputSocket *> MFNode::outputs()
+{
+ return m_outputs;
+}
+
+inline Span<const MFOutputSocket *> MFNode::outputs() const
+{
+ return m_outputs;
+}
+
+template<typename FuncT> void MFNode::foreach_origin_socket(const FuncT &func) const
+{
+ for (const MFInputSocket *socket : m_inputs) {
+ const MFOutputSocket *origin = socket->origin();
+ if (origin != nullptr) {
+ func(*origin);
+ }
+ }
+}
+
+inline bool MFNode::all_inputs_have_origin() const
+{
+ for (const MFInputSocket *socket : m_inputs) {
+ if (socket->origin() == nullptr) {
+ return false;
+ }
+ }
+ return true;
+}
+
+/* --------------------------------------------------------------------
+ * MFFunctionNode inline methods.
+ */
+
+inline StringRefNull MFFunctionNode::name() const
+{
+ return m_function->name();
+}
+
+inline const MultiFunction &MFFunctionNode::function() const
+{
+ return *m_function;
+}
+
+inline const MFInputSocket &MFFunctionNode::input_for_param(uint param_index) const
+{
+ return this->input(m_input_param_indices.first_index(param_index));
+}
+
+inline const MFOutputSocket &MFFunctionNode::output_for_param(uint param_index) const
+{
+ return this->output(m_output_param_indices.first_index(param_index));
+}
+
+/* --------------------------------------------------------------------
+ * MFDummyNode inline methods.
+ */
+
+inline StringRefNull MFDummyNode::name() const
+{
+ return m_name;
+}
+
+inline Span<StringRefNull> MFDummyNode::input_names() const
+{
+ return m_input_names;
+}
+
+inline Span<StringRefNull> MFDummyNode::output_names() const
+{
+ return m_output_names;
+}
+
+/* --------------------------------------------------------------------
+ * MFSocket inline methods.
+ */
+
+inline StringRefNull MFSocket::name() const
+{
+ return m_name;
+}
+
+inline uint MFSocket::id() const
+{
+ return m_id;
+}
+
+inline const MFDataType &MFSocket::data_type() const
+{
+ return m_data_type;
+}
+
+inline MFNode &MFSocket::node()
+{
+ return *m_node;
+}
+
+inline const MFNode &MFSocket::node() const
+{
+ return *m_node;
+}
+
+inline bool MFSocket::is_input() const
+{
+ return !m_is_output;
+}
+
+inline bool MFSocket::is_output() const
+{
+ return m_is_output;
+}
+
+inline MFInputSocket &MFSocket::as_input()
+{
+ BLI_assert(this->is_input());
+ return *(MFInputSocket *)this;
+}
+
+inline const MFInputSocket &MFSocket::as_input() const
+{
+ BLI_assert(this->is_input());
+ return *(const MFInputSocket *)this;
+}
+
+inline MFOutputSocket &MFSocket::as_output()
+{
+ BLI_assert(this->is_output());
+ return *(MFOutputSocket *)this;
+}
+
+inline const MFOutputSocket &MFSocket::as_output() const
+{
+ BLI_assert(this->is_output());
+ return *(const MFOutputSocket *)this;
+}
+
+/* --------------------------------------------------------------------
+ * MFInputSocket inline methods.
+ */
+
+inline MFOutputSocket *MFInputSocket::origin()
+{
+ return m_origin;
+}
+
+inline const MFOutputSocket *MFInputSocket::origin() const
+{
+ return m_origin;
+}
+
+/* --------------------------------------------------------------------
+ * MFOutputSocket inline methods.
+ */
+
+inline Span<MFInputSocket *> MFOutputSocket::targets()
+{
+ return m_targets;
+}
+
+inline Span<const MFInputSocket *> MFOutputSocket::targets() const
+{
+ return m_targets.as_span();
+}
+
+/* --------------------------------------------------------------------
+ * MFNetwork inline methods.
+ */
+
+inline uint MFNetwork::max_socket_id() const
+{
+ return m_socket_or_null_by_id.size() - 1;
+}
+
+} // namespace fn
+} // namespace blender
+
+#endif /* __FN_MULTI_FUNCTION_NETWORK_HH__ */
diff --git a/source/blender/functions/FN_multi_function_network_evaluation.hh b/source/blender/functions/FN_multi_function_network_evaluation.hh
new file mode 100644
index 00000000000..85ccd1361ef
--- /dev/null
+++ b/source/blender/functions/FN_multi_function_network_evaluation.hh
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#ifndef __FN_MULTI_FUNCTION_NETWORK_EVALUATION_HH__
+#define __FN_MULTI_FUNCTION_NETWORK_EVALUATION_HH__
+
+/** \file
+ * \ingroup fn
+ */
+
+#include "FN_multi_function_network.hh"
+
+namespace blender {
+namespace fn {
+
+class MFNetworkEvaluationStorage;
+
+class MFNetworkEvaluator : public MultiFunction {
+ private:
+ Vector<const MFOutputSocket *> m_inputs;
+ Vector<const MFInputSocket *> m_outputs;
+
+ public:
+ MFNetworkEvaluator(Vector<const MFOutputSocket *> inputs, Vector<const MFInputSocket *> outputs);
+
+ void call(IndexMask mask, MFParams params, MFContext context) const override;
+
+ private:
+ using Storage = MFNetworkEvaluationStorage;
+
+ void copy_inputs_to_storage(MFParams params, Storage &storage) const;
+ void copy_outputs_to_storage(
+ MFParams params,
+ Storage &storage,
+ Vector<const MFInputSocket *> &outputs_to_initialize_in_the_end) const;
+
+ void evaluate_network_to_compute_outputs(MFContext &global_context, Storage &storage) const;
+
+ void evaluate_function(MFContext &global_context,
+ const MFFunctionNode &function_node,
+ Storage &storage) const;
+
+ bool can_do_single_value_evaluation(const MFFunctionNode &function_node, Storage &storage) const;
+
+ void initialize_remaining_outputs(MFParams params,
+ Storage &storage,
+ Span<const MFInputSocket *> remaining_outputs) const;
+};
+
+} // namespace fn
+} // namespace blender
+
+#endif /* __FN_MULTI_FUNCTION_NETWORK_EVALUATION_HH__ */
diff --git a/source/blender/functions/FN_multi_function_param_type.hh b/source/blender/functions/FN_multi_function_param_type.hh
new file mode 100644
index 00000000000..d89c13505f9
--- /dev/null
+++ b/source/blender/functions/FN_multi_function_param_type.hh
@@ -0,0 +1,165 @@
+/*
+ * 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.
+ */
+
+#ifndef __FN_MULTI_FUNCTION_PARAM_TYPE_HH__
+#define __FN_MULTI_FUNCTION_PARAM_TYPE_HH__
+
+/** \file
+ * \ingroup fn
+ *
+ * A multi-function has an arbitrary amount of parameters. Every parameter belongs to one of three
+ * interface types:
+ * - Input: An input parameter is readonly inside the function. The values have to be provided by
+ * the caller.
+ * - Output: An output parameter has to be initialized by the function. However, the caller
+ * provides the memory where the data has to be constructed.
+ * - Mutable: A mutable parameter can be considered to be an input and output. The caller has to
+ * initialize the data, but the function is allowed to modify it.
+ *
+ * Furthermore, every parameter has a MFDataType that describes what kind of data is being passed
+ * around.
+ */
+
+#include "FN_multi_function_data_type.hh"
+
+namespace blender {
+namespace fn {
+
+class MFParamType {
+ public:
+ enum InterfaceType {
+ Input,
+ Output,
+ Mutable,
+ };
+
+ enum Category {
+ SingleInput,
+ VectorInput,
+ SingleOutput,
+ VectorOutput,
+ SingleMutable,
+ VectorMutable,
+ };
+
+ private:
+ InterfaceType m_interface_type;
+ MFDataType m_data_type;
+
+ public:
+ MFParamType(InterfaceType interface_type, MFDataType data_type)
+ : m_interface_type(interface_type), m_data_type(data_type)
+ {
+ }
+
+ static MFParamType ForSingleInput(const CPPType &type)
+ {
+ return MFParamType(InterfaceType::Input, MFDataType::ForSingle(type));
+ }
+
+ static MFParamType ForVectorInput(const CPPType &base_type)
+ {
+ return MFParamType(InterfaceType::Input, MFDataType::ForVector(base_type));
+ }
+
+ static MFParamType ForSingleOutput(const CPPType &type)
+ {
+ return MFParamType(InterfaceType::Output, MFDataType::ForSingle(type));
+ }
+
+ static MFParamType ForVectorOutput(const CPPType &base_type)
+ {
+ return MFParamType(InterfaceType::Output, MFDataType::ForVector(base_type));
+ }
+
+ static MFParamType ForMutableSingle(const CPPType &type)
+ {
+ return MFParamType(InterfaceType::Mutable, MFDataType::ForSingle(type));
+ }
+
+ static MFParamType ForMutableVector(const CPPType &base_type)
+ {
+ return MFParamType(InterfaceType::Mutable, MFDataType::ForVector(base_type));
+ }
+
+ MFDataType data_type() const
+ {
+ return m_data_type;
+ }
+
+ InterfaceType interface_type() const
+ {
+ return m_interface_type;
+ }
+
+ Category category() const
+ {
+ switch (m_data_type.category()) {
+ case MFDataType::Single: {
+ switch (m_interface_type) {
+ case Input:
+ return SingleInput;
+ case Output:
+ return SingleOutput;
+ case Mutable:
+ return SingleMutable;
+ }
+ break;
+ }
+ case MFDataType::Vector: {
+ switch (m_interface_type) {
+ case Input:
+ return VectorInput;
+ case Output:
+ return VectorOutput;
+ case Mutable:
+ return VectorMutable;
+ }
+ break;
+ }
+ }
+ BLI_assert(false);
+ return SingleInput;
+ }
+
+ bool is_input_or_mutable() const
+ {
+ return ELEM(m_interface_type, Input, Mutable);
+ }
+
+ bool is_output_or_mutable() const
+ {
+ return ELEM(m_interface_type, Output, Mutable);
+ }
+
+ friend bool operator==(const MFParamType &a, const MFParamType &b);
+ friend bool operator!=(const MFParamType &a, const MFParamType &b);
+};
+
+inline bool operator==(const MFParamType &a, const MFParamType &b)
+{
+ return a.m_interface_type == b.m_interface_type && a.m_data_type == b.m_data_type;
+}
+
+inline bool operator!=(const MFParamType &a, const MFParamType &b)
+{
+ return !(a == b);
+}
+
+} // namespace fn
+} // namespace blender
+
+#endif /* __FN_MULTI_FUNCTION_PARAM_TYPE_HH__ */
diff --git a/source/blender/functions/FN_multi_function_params.hh b/source/blender/functions/FN_multi_function_params.hh
new file mode 100644
index 00000000000..6a0eb698250
--- /dev/null
+++ b/source/blender/functions/FN_multi_function_params.hh
@@ -0,0 +1,240 @@
+/*
+ * 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.
+ */
+
+#ifndef __FN_MULTI_FUNCTION_PARAMS_HH__
+#define __FN_MULTI_FUNCTION_PARAMS_HH__
+
+/** \file
+ * \ingroup fn
+ *
+ * This file provides an MFParams and MFParamsBuilder structure.
+ *
+ * `MFParamsBuilder` is used by a function caller to be prepare all parameters that are passed into
+ * the function. `MFParams` is then used inside the called function to access the parameters.
+ */
+
+#include "FN_generic_vector_array.hh"
+#include "FN_multi_function_signature.hh"
+
+namespace blender {
+namespace fn {
+
+class MFParamsBuilder {
+ private:
+ const MFSignature *m_signature;
+ uint m_min_array_size;
+ Vector<GVSpan> m_virtual_spans;
+ Vector<GMutableSpan> m_mutable_spans;
+ Vector<GVArraySpan> m_virtual_array_spans;
+ Vector<GVectorArray *> m_vector_arrays;
+
+ friend class MFParams;
+
+ public:
+ MFParamsBuilder(const MFSignature &signature, uint min_array_size)
+ : m_signature(&signature), m_min_array_size(min_array_size)
+ {
+ }
+
+ MFParamsBuilder(const class MultiFunction &fn, uint min_array_size);
+
+ template<typename T> void add_readonly_single_input(const T *value)
+ {
+ this->add_readonly_single_input(
+ GVSpan::FromSingle(CPPType::get<T>(), value, m_min_array_size));
+ }
+ void add_readonly_single_input(GVSpan ref)
+ {
+ this->assert_current_param_type(MFParamType::ForSingleInput(ref.type()));
+ BLI_assert(ref.size() >= m_min_array_size);
+ m_virtual_spans.append(ref);
+ }
+
+ void add_readonly_vector_input(GVArraySpan ref)
+ {
+ this->assert_current_param_type(MFParamType::ForVectorInput(ref.type()));
+ BLI_assert(ref.size() >= m_min_array_size);
+ m_virtual_array_spans.append(ref);
+ }
+
+ void add_uninitialized_single_output(GMutableSpan ref)
+ {
+ this->assert_current_param_type(MFParamType::ForSingleOutput(ref.type()));
+ BLI_assert(ref.size() >= m_min_array_size);
+ m_mutable_spans.append(ref);
+ }
+
+ void add_vector_output(GVectorArray &vector_array)
+ {
+ this->assert_current_param_type(MFParamType::ForVectorOutput(vector_array.type()));
+ BLI_assert(vector_array.size() >= m_min_array_size);
+ m_vector_arrays.append(&vector_array);
+ }
+
+ void add_single_mutable(GMutableSpan ref)
+ {
+ this->assert_current_param_type(MFParamType::ForMutableSingle(ref.type()));
+ BLI_assert(ref.size() >= m_min_array_size);
+ m_mutable_spans.append(ref);
+ }
+
+ void add_vector_mutable(GVectorArray &vector_array)
+ {
+ this->assert_current_param_type(MFParamType::ForMutableVector(vector_array.type()));
+ BLI_assert(vector_array.size() >= m_min_array_size);
+ m_vector_arrays.append(&vector_array);
+ }
+
+ GMutableSpan computed_array(uint param_index)
+ {
+ BLI_assert(ELEM(m_signature->param_types[param_index].category(),
+ MFParamType::SingleOutput,
+ MFParamType::SingleMutable));
+ uint data_index = m_signature->data_index(param_index);
+ return m_mutable_spans[data_index];
+ }
+
+ GVectorArray &computed_vector_array(uint param_index)
+ {
+ BLI_assert(ELEM(m_signature->param_types[param_index].category(),
+ MFParamType::VectorOutput,
+ MFParamType::VectorMutable));
+ uint data_index = m_signature->data_index(param_index);
+ return *m_vector_arrays[data_index];
+ }
+
+ private:
+ void assert_current_param_type(MFParamType param_type)
+ {
+ UNUSED_VARS_NDEBUG(param_type);
+#ifdef DEBUG
+ uint param_index = this->current_param_index();
+ MFParamType expected_type = m_signature->param_types[param_index];
+ BLI_assert(expected_type == param_type);
+#endif
+ }
+
+ uint current_param_index() const
+ {
+ return m_virtual_spans.size() + m_mutable_spans.size() + m_virtual_array_spans.size() +
+ m_vector_arrays.size();
+ }
+};
+
+class MFParams {
+ private:
+ MFParamsBuilder *m_builder;
+
+ public:
+ MFParams(MFParamsBuilder &builder) : m_builder(&builder)
+ {
+ }
+
+ template<typename T> VSpan<T> readonly_single_input(uint param_index, StringRef name = "")
+ {
+ return this->readonly_single_input(param_index, name).typed<T>();
+ }
+ GVSpan readonly_single_input(uint param_index, StringRef name = "")
+ {
+ this->assert_correct_param(param_index, name, MFParamType::SingleInput);
+ uint data_index = m_builder->m_signature->data_index(param_index);
+ return m_builder->m_virtual_spans[data_index];
+ }
+
+ template<typename T>
+ MutableSpan<T> uninitialized_single_output(uint param_index, StringRef name = "")
+ {
+ return this->uninitialized_single_output(param_index, name).typed<T>();
+ }
+ GMutableSpan uninitialized_single_output(uint param_index, StringRef name = "")
+ {
+ this->assert_correct_param(param_index, name, MFParamType::SingleOutput);
+ uint data_index = m_builder->m_signature->data_index(param_index);
+ return m_builder->m_mutable_spans[data_index];
+ }
+
+ template<typename T> VArraySpan<T> readonly_vector_input(uint param_index, StringRef name = "")
+ {
+ return this->readonly_vector_input(param_index, name).typed<T>();
+ }
+ GVArraySpan readonly_vector_input(uint param_index, StringRef name = "")
+ {
+ this->assert_correct_param(param_index, name, MFParamType::VectorInput);
+ uint data_index = m_builder->m_signature->data_index(param_index);
+ return m_builder->m_virtual_array_spans[data_index];
+ }
+
+ template<typename T> GVectorArrayRef<T> vector_output(uint param_index, StringRef name = "")
+ {
+ return this->vector_output(param_index, name).typed<T>();
+ }
+ GVectorArray &vector_output(uint param_index, StringRef name = "")
+ {
+ this->assert_correct_param(param_index, name, MFParamType::VectorOutput);
+ uint data_index = m_builder->m_signature->data_index(param_index);
+ return *m_builder->m_vector_arrays[data_index];
+ }
+
+ template<typename T> MutableSpan<T> single_mutable(uint param_index, StringRef name = "")
+ {
+ return this->single_mutable(param_index, name).typed<T>();
+ }
+ GMutableSpan single_mutable(uint param_index, StringRef name = "")
+ {
+ this->assert_correct_param(param_index, name, MFParamType::SingleMutable);
+ uint data_index = m_builder->m_signature->data_index(param_index);
+ return m_builder->m_mutable_spans[data_index];
+ }
+
+ template<typename T> GVectorArrayRef<T> vector_mutable(uint param_index, StringRef name = "")
+ {
+ return this->vector_mutable(param_index, name).typed<T>();
+ }
+ GVectorArray &vector_mutable(uint param_index, StringRef name = "")
+ {
+ this->assert_correct_param(param_index, name, MFParamType::VectorMutable);
+ uint data_index = m_builder->m_signature->data_index(param_index);
+ return *m_builder->m_vector_arrays[data_index];
+ }
+
+ private:
+ void assert_correct_param(uint param_index, StringRef name, MFParamType param_type)
+ {
+ UNUSED_VARS_NDEBUG(param_index, name, param_type);
+#ifdef DEBUG
+ BLI_assert(m_builder->m_signature->param_types[param_index] == param_type);
+ if (name.size() > 0) {
+ BLI_assert(m_builder->m_signature->param_names[param_index] == name);
+ }
+#endif
+ }
+
+ void assert_correct_param(uint param_index, StringRef name, MFParamType::Category category)
+ {
+ UNUSED_VARS_NDEBUG(param_index, name, category);
+#ifdef DEBUG
+ BLI_assert(m_builder->m_signature->param_types[param_index].category() == category);
+ if (name.size() > 0) {
+ BLI_assert(m_builder->m_signature->param_names[param_index] == name);
+ }
+#endif
+ }
+};
+
+} // namespace fn
+} // namespace blender
+
+#endif /* __FN_MULTI_FUNCTION_PARAMS_HH__ */
diff --git a/source/blender/functions/FN_multi_function_signature.hh b/source/blender/functions/FN_multi_function_signature.hh
new file mode 100644
index 00000000000..77a8ce14c03
--- /dev/null
+++ b/source/blender/functions/FN_multi_function_signature.hh
@@ -0,0 +1,166 @@
+/*
+ * 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.
+ */
+
+#ifndef __FN_MULTI_FUNCTION_SIGNATURE_HH__
+#define __FN_MULTI_FUNCTION_SIGNATURE_HH__
+
+/** \file
+ * \ingroup fn
+ *
+ * The signature of a multi-function contains the functions name and expected parameters. New
+ * signatures should be build using the MFSignatureBuilder class.
+ */
+
+#include "FN_multi_function_param_type.hh"
+
+#include "BLI_vector.hh"
+
+namespace blender {
+namespace fn {
+
+struct MFSignature {
+ std::string function_name;
+ /* Use RawAllocator so that a MultiFunction can have static storage duration. */
+ Vector<std::string, 4, RawAllocator> param_names;
+ Vector<MFParamType, 4, RawAllocator> param_types;
+ Vector<uint, 4, RawAllocator> param_data_indices;
+
+ uint data_index(uint param_index) const
+ {
+ return param_data_indices[param_index];
+ }
+};
+
+class MFSignatureBuilder {
+ private:
+ MFSignature &m_data;
+ uint m_span_count = 0;
+ uint m_virtual_span_count = 0;
+ uint m_virtual_array_span_count = 0;
+ uint m_vector_array_count = 0;
+
+ public:
+ MFSignatureBuilder(MFSignature &data) : m_data(data)
+ {
+ BLI_assert(data.param_names.is_empty());
+ BLI_assert(data.param_types.is_empty());
+ BLI_assert(data.param_data_indices.is_empty());
+ }
+
+ /* Input Param Types */
+
+ template<typename T> void single_input(StringRef name)
+ {
+ this->single_input(name, CPPType::get<T>());
+ }
+ void single_input(StringRef name, const CPPType &type)
+ {
+ this->input(name, MFDataType::ForSingle(type));
+ }
+ template<typename T> void vector_input(StringRef name)
+ {
+ this->vector_input(name, CPPType::get<T>());
+ }
+ void vector_input(StringRef name, const CPPType &base_type)
+ {
+ this->input(name, MFDataType::ForVector(base_type));
+ }
+ void input(StringRef name, MFDataType data_type)
+ {
+ m_data.param_names.append(name);
+ m_data.param_types.append(MFParamType(MFParamType::Input, data_type));
+
+ switch (data_type.category()) {
+ case MFDataType::Single:
+ m_data.param_data_indices.append(m_virtual_span_count++);
+ break;
+ case MFDataType::Vector:
+ m_data.param_data_indices.append(m_virtual_array_span_count++);
+ break;
+ }
+ }
+
+ /* Output Param Types */
+
+ template<typename T> void single_output(StringRef name)
+ {
+ this->single_output(name, CPPType::get<T>());
+ }
+ void single_output(StringRef name, const CPPType &type)
+ {
+ this->output(name, MFDataType::ForSingle(type));
+ }
+ template<typename T> void vector_output(StringRef name)
+ {
+ this->vector_output(name, CPPType::get<T>());
+ }
+ void vector_output(StringRef name, const CPPType &base_type)
+ {
+ this->output(name, MFDataType::ForVector(base_type));
+ }
+ void output(StringRef name, MFDataType data_type)
+ {
+ m_data.param_names.append(name);
+ m_data.param_types.append(MFParamType(MFParamType::Output, data_type));
+
+ switch (data_type.category()) {
+ case MFDataType::Single:
+ m_data.param_data_indices.append(m_span_count++);
+ break;
+ case MFDataType::Vector:
+ m_data.param_data_indices.append(m_vector_array_count++);
+ break;
+ }
+ }
+
+ /* Mutable Param Types */
+
+ template<typename T> void single_mutable(StringRef name)
+ {
+ this->single_mutable(name, CPPType::get<T>());
+ }
+ void single_mutable(StringRef name, const CPPType &type)
+ {
+ this->mutable_(name, MFDataType::ForSingle(type));
+ }
+ template<typename T> void vector_mutable(StringRef name)
+ {
+ this->vector_mutable(name, CPPType::get<T>());
+ }
+ void vector_mutable(StringRef name, const CPPType &base_type)
+ {
+ this->mutable_(name, MFDataType::ForVector(base_type));
+ }
+ void mutable_(StringRef name, MFDataType data_type)
+ {
+ m_data.param_names.append(name);
+ m_data.param_types.append(MFParamType(MFParamType::Mutable, data_type));
+
+ switch (data_type.category()) {
+ case MFDataType::Single:
+ m_data.param_data_indices.append(m_span_count++);
+ break;
+ case MFDataType::Vector:
+ m_data.param_data_indices.append(m_vector_array_count++);
+ break;
+ }
+ }
+};
+
+} // namespace fn
+} // namespace blender
+
+#endif /* __FN_MULTI_FUNCTION_SIGNATURE_HH__ */
diff --git a/source/blender/functions/FN_spans.hh b/source/blender/functions/FN_spans.hh
new file mode 100644
index 00000000000..b4607527fb5
--- /dev/null
+++ b/source/blender/functions/FN_spans.hh
@@ -0,0 +1,404 @@
+/*
+ * 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.
+ */
+
+#ifndef __FN_SPANS_HH__
+#define __FN_SPANS_HH__
+
+/** \file
+ * \ingroup fn
+ *
+ * This file implements multiple variants of a span for different use cases. There are two
+ * requirements of the function system that require span implementations other from
+ * blender::Span<T>.
+ * 1. The function system works with a run-time type system (see `CPPType`). Therefore, it has to
+ * deal with types in a generic way. The type of a Span<T> has to be known at compile time.
+ * 2. Span<T> expects an underlying memory buffer that is as large as the span. However, sometimes
+ * we can save some memory and processing when we know that all elements are the same.
+ *
+ * The first requirement is solved with generic spans, which use the "G" prefix. Those
+ * store a CPPType instance to keep track of the type that is currently stored.
+ *
+ * The second requirement is solved with virtual spans. A virtual span behaves like a normal span,
+ * but it might not be backed up by an actual array. Elements in a virtual span are always
+ * immutable.
+ *
+ * Different use cases require different combinations of these properties and therefore use
+ * different data structures.
+ */
+
+#include "BLI_span.hh"
+
+#include "FN_cpp_type.hh"
+
+namespace blender {
+namespace fn {
+
+/**
+ * A generic span. It behaves just like a blender::Span<T>, but the type is only known at run-time.
+ */
+class GSpan {
+ private:
+ const CPPType *m_type;
+ const void *m_buffer;
+ uint m_size;
+
+ public:
+ GSpan(const CPPType &type, const void *buffer, uint size)
+ : m_type(&type), m_buffer(buffer), m_size(size)
+ {
+ BLI_assert(buffer != nullptr || size == 0);
+ BLI_assert(type.pointer_has_valid_alignment(buffer));
+ }
+
+ GSpan(const CPPType &type) : GSpan(type, nullptr, 0)
+ {
+ }
+
+ template<typename T>
+ GSpan(Span<T> array) : GSpan(CPPType::get<T>(), (const void *)array.data(), array.size())
+ {
+ }
+
+ const CPPType &type() const
+ {
+ return *m_type;
+ }
+
+ bool is_empty() const
+ {
+ return m_size == 0;
+ }
+
+ uint size() const
+ {
+ return m_size;
+ }
+
+ const void *buffer() const
+ {
+ return m_buffer;
+ }
+
+ const void *operator[](uint index) const
+ {
+ BLI_assert(index < m_size);
+ return POINTER_OFFSET(m_buffer, m_type->size() * index);
+ }
+
+ template<typename T> Span<T> typed() const
+ {
+ BLI_assert(m_type->is<T>());
+ return Span<T>((const T *)m_buffer, m_size);
+ }
+};
+
+/**
+ * A generic mutable span. It behaves just like a blender::MutableSpan<T>, but the type is only
+ * known at run-time.
+ */
+class GMutableSpan {
+ private:
+ const CPPType *m_type;
+ void *m_buffer;
+ uint m_size;
+
+ public:
+ GMutableSpan(const CPPType &type, void *buffer, uint size)
+ : m_type(&type), m_buffer(buffer), m_size(size)
+ {
+ BLI_assert(buffer != nullptr || size == 0);
+ BLI_assert(type.pointer_has_valid_alignment(buffer));
+ }
+
+ GMutableSpan(const CPPType &type) : GMutableSpan(type, nullptr, 0)
+ {
+ }
+
+ template<typename T>
+ GMutableSpan(MutableSpan<T> array)
+ : GMutableSpan(CPPType::get<T>(), (void *)array.begin(), array.size())
+ {
+ }
+
+ operator GSpan() const
+ {
+ return GSpan(*m_type, m_buffer, m_size);
+ }
+
+ const CPPType &type() const
+ {
+ return *m_type;
+ }
+
+ bool is_empty() const
+ {
+ return m_size == 0;
+ }
+
+ uint size() const
+ {
+ return m_size;
+ }
+
+ void *buffer()
+ {
+ return m_buffer;
+ }
+
+ void *operator[](uint index)
+ {
+ BLI_assert(index < m_size);
+ return POINTER_OFFSET(m_buffer, m_type->size() * index);
+ }
+
+ template<typename T> MutableSpan<T> typed()
+ {
+ BLI_assert(m_type->is<T>());
+ return MutableSpan<T>((T *)m_buffer, m_size);
+ }
+};
+
+enum class VSpanCategory {
+ Single,
+ FullArray,
+ FullPointerArray,
+};
+
+template<typename T> struct VSpanBase {
+ protected:
+ uint m_virtual_size;
+ VSpanCategory m_category;
+ union {
+ struct {
+ const T *data;
+ } single;
+ struct {
+ const T *data;
+ } full_array;
+ struct {
+ const T *const *data;
+ } full_pointer_array;
+ } m_data;
+
+ public:
+ bool is_single_element() const
+ {
+ switch (m_category) {
+ case VSpanCategory::Single:
+ return true;
+ case VSpanCategory::FullArray:
+ return m_virtual_size == 1;
+ case VSpanCategory::FullPointerArray:
+ return m_virtual_size == 1;
+ }
+ BLI_assert(false);
+ return false;
+ }
+
+ bool is_empty() const
+ {
+ return this->m_virtual_size == 0;
+ }
+
+ uint size() const
+ {
+ return this->m_virtual_size;
+ }
+};
+
+BLI_STATIC_ASSERT((sizeof(VSpanBase<void>) == sizeof(VSpanBase<AlignedBuffer<64, 64>>)),
+ "should not depend on the size of the type");
+
+/**
+ * A virtual span. It behaves like a blender::Span<T>, but might not be backed up by an actual
+ * array.
+ */
+template<typename T> class VSpan : public VSpanBase<T> {
+ friend class GVSpan;
+
+ VSpan(const VSpanBase<void> &values)
+ {
+ memcpy(this, &values, sizeof(VSpanBase<void>));
+ }
+
+ public:
+ VSpan()
+ {
+ this->m_virtual_size = 0;
+ this->m_category = VSpanCategory::FullArray;
+ this->m_data.full_array.data = nullptr;
+ }
+
+ VSpan(Span<T> values)
+ {
+ this->m_virtual_size = values.size();
+ this->m_category = VSpanCategory::FullArray;
+ this->m_data.full_array.data = values.begin();
+ }
+
+ VSpan(MutableSpan<T> values) : VSpan(Span<T>(values))
+ {
+ }
+
+ VSpan(Span<const T *> values)
+ {
+ this->m_virtual_size = values.size();
+ this->m_category = VSpanCategory::FullPointerArray;
+ this->m_data.full_pointer_array.data = values.begin();
+ }
+
+ static VSpan FromSingle(const T *value, uint virtual_size)
+ {
+ VSpan ref;
+ ref.m_virtual_size = virtual_size;
+ ref.m_category = VSpanCategory::Single;
+ ref.m_data.single.data = value;
+ return ref;
+ }
+
+ const T &operator[](uint index) const
+ {
+ BLI_assert(index < this->m_virtual_size);
+ switch (this->m_category) {
+ case VSpanCategory::Single:
+ return *this->m_data.single.data;
+ case VSpanCategory::FullArray:
+ return this->m_data.full_array.data[index];
+ case VSpanCategory::FullPointerArray:
+ return *this->m_data.full_pointer_array.data[index];
+ }
+ BLI_assert(false);
+ return *this->m_data.single.data;
+ }
+};
+
+/**
+ * A generic virtual span. It behaves like a blender::Span<T>, but the type is only known at
+ * run-time and it might not be backed up by an actual array.
+ */
+class GVSpan : public VSpanBase<void> {
+ private:
+ const CPPType *m_type;
+
+ GVSpan() = default;
+
+ public:
+ GVSpan(const CPPType &type)
+ {
+ this->m_type = &type;
+ this->m_virtual_size = 0;
+ this->m_category = VSpanCategory::FullArray;
+ this->m_data.full_array.data = nullptr;
+ }
+
+ GVSpan(GSpan values)
+ {
+ this->m_type = &values.type();
+ this->m_virtual_size = values.size();
+ this->m_category = VSpanCategory::FullArray;
+ this->m_data.full_array.data = values.buffer();
+ }
+
+ GVSpan(GMutableSpan values) : GVSpan(GSpan(values))
+ {
+ }
+
+ template<typename T> GVSpan(const VSpanBase<T> &values)
+ {
+ this->m_type = &CPPType::get<T>();
+ memcpy(this, &values, sizeof(VSpanBase<void>));
+ }
+
+ template<typename T> GVSpan(Span<T> values) : GVSpan(GSpan(values))
+ {
+ }
+
+ template<typename T> GVSpan(MutableSpan<T> values) : GVSpan(GSpan(values))
+ {
+ }
+
+ static GVSpan FromSingle(const CPPType &type, const void *value, uint virtual_size)
+ {
+ GVSpan ref;
+ ref.m_type = &type;
+ ref.m_virtual_size = virtual_size;
+ ref.m_category = VSpanCategory::Single;
+ ref.m_data.single.data = value;
+ return ref;
+ }
+
+ static GVSpan FromFullPointerArray(const CPPType &type, const void *const *values, uint size)
+ {
+ GVSpan ref;
+ ref.m_type = &type;
+ ref.m_virtual_size = size;
+ ref.m_category = VSpanCategory::FullPointerArray;
+ ref.m_data.full_pointer_array.data = values;
+ return ref;
+ }
+
+ const CPPType &type() const
+ {
+ return *this->m_type;
+ }
+
+ const void *operator[](uint index) const
+ {
+ BLI_assert(index < this->m_virtual_size);
+ switch (this->m_category) {
+ case VSpanCategory::Single:
+ return this->m_data.single.data;
+ case VSpanCategory::FullArray:
+ return POINTER_OFFSET(this->m_data.full_array.data, index * m_type->size());
+ case VSpanCategory::FullPointerArray:
+ return this->m_data.full_pointer_array.data[index];
+ }
+ BLI_assert(false);
+ return this->m_data.single.data;
+ }
+
+ template<typename T> VSpan<T> typed() const
+ {
+ BLI_assert(m_type->is<T>());
+ return VSpan<T>(*this);
+ }
+
+ const void *as_single_element() const
+ {
+ BLI_assert(this->is_single_element());
+ return (*this)[0];
+ }
+
+ void materialize_to_uninitialized(void *dst) const
+ {
+ this->materialize_to_uninitialized(IndexRange(m_virtual_size), dst);
+ }
+
+ void materialize_to_uninitialized(IndexMask mask, void *dst) const
+ {
+ BLI_assert(this->size() >= mask.min_array_size());
+
+ uint element_size = m_type->size();
+ for (uint i : mask) {
+ m_type->copy_to_uninitialized((*this)[i], POINTER_OFFSET(dst, element_size * i));
+ }
+ }
+};
+
+} // namespace fn
+} // namespace blender
+
+#endif /* __FN_SPANS_HH__ */
diff --git a/source/blender/functions/intern/attributes_ref.cc b/source/blender/functions/intern/attributes_ref.cc
new file mode 100644
index 00000000000..dc64f571596
--- /dev/null
+++ b/source/blender/functions/intern/attributes_ref.cc
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+#include "FN_attributes_ref.hh"
+
+namespace blender {
+namespace fn {
+
+AttributesInfoBuilder::~AttributesInfoBuilder()
+{
+ for (uint i : m_defaults.index_range()) {
+ m_types[i]->destruct(m_defaults[i]);
+ }
+}
+
+void AttributesInfoBuilder::add(StringRef name, const CPPType &type, const void *default_value)
+{
+ if (m_names.add_as(name)) {
+ m_types.append(&type);
+
+ if (default_value == nullptr) {
+ default_value = type.default_value();
+ }
+ void *dst = m_allocator.allocate(type.size(), type.alignment());
+ type.copy_to_uninitialized(default_value, dst);
+ m_defaults.append(dst);
+ }
+ else {
+ /* The same name can be added more than once as long as the type is always the same. */
+ BLI_assert(m_types[m_names.index_of_as(name)] == &type);
+ }
+}
+
+AttributesInfo::AttributesInfo(const AttributesInfoBuilder &builder)
+{
+ for (uint i : builder.m_types.index_range()) {
+ StringRefNull name = m_allocator.copy_string(builder.m_names[i]);
+ const CPPType &type = *builder.m_types[i];
+ const void *default_value = builder.m_defaults[i];
+
+ m_index_by_name.add_new(name, i);
+ m_name_by_index.append(name);
+ m_type_by_index.append(&type);
+
+ void *dst = m_allocator.allocate(type.size(), type.alignment());
+ type.copy_to_uninitialized(default_value, dst);
+ m_defaults.append(dst);
+ }
+}
+
+AttributesInfo::~AttributesInfo()
+{
+ for (uint i : m_defaults.index_range()) {
+ m_type_by_index[i]->destruct(m_defaults[i]);
+ }
+}
+
+} // namespace fn
+} // namespace blender
diff --git a/source/blender/functions/intern/cpp_types.cc b/source/blender/functions/intern/cpp_types.cc
index 6339250caa5..cb4b065e5bc 100644
--- a/source/blender/functions/intern/cpp_types.cc
+++ b/source/blender/functions/intern/cpp_types.cc
@@ -22,7 +22,7 @@
#include "BLI_float4x4.hh"
namespace blender {
-namespace FN {
+namespace fn {
MAKE_CPP_TYPE(bool, bool)
@@ -39,5 +39,5 @@ MAKE_CPP_TYPE(Color4b, blender::Color4b)
MAKE_CPP_TYPE(string, std::string)
-} // namespace FN
+} // namespace fn
} // namespace blender
diff --git a/source/blender/io/alembic/intern/abc_writer_nurbs.h b/source/blender/functions/intern/multi_function.cc
index c6a3c399b66..8eb5355511d 100644
--- a/source/blender/io/alembic/intern/abc_writer_nurbs.h
+++ b/source/blender/functions/intern/multi_function.cc
@@ -14,29 +14,27 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-/** \file
- * \ingroup balembic
- */
-
-#ifndef __ABC_WRITER_NURBS_H__
-#define __ABC_WRITER_NURBS_H__
-
-#include "abc_writer_object.h"
+#include "FN_multi_function.hh"
-class AbcNurbsWriter : public AbcObjectWriter {
- std::vector<Alembic::AbcGeom::ONuPatchSchema> m_nurbs_schema;
- bool m_is_animated;
+namespace blender {
+namespace fn {
+class DummyMultiFunction : public MultiFunction {
public:
- AbcNurbsWriter(Object *ob,
- AbcTransformWriter *parent,
- uint32_t time_sampling,
- ExportSettings &settings);
-
- private:
- virtual void do_write();
-
- bool isAnimated() const;
+ DummyMultiFunction()
+ {
+ this->get_builder("Dummy");
+ }
+
+ void call(IndexMask UNUSED(mask),
+ MFParams UNUSED(params),
+ MFContext UNUSED(context)) const override
+ {
+ }
};
-#endif /* __ABC_WRITER_NURBS_H__ */
+static DummyMultiFunction dummy_multi_function_;
+const MultiFunction &dummy_multi_function = dummy_multi_function_;
+
+} // namespace fn
+} // namespace blender
diff --git a/source/blender/functions/intern/multi_function_network.cc b/source/blender/functions/intern/multi_function_network.cc
new file mode 100644
index 00000000000..93d062f3e5c
--- /dev/null
+++ b/source/blender/functions/intern/multi_function_network.cc
@@ -0,0 +1,278 @@
+/*
+ * 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.
+ */
+
+#include "BLI_dot_export.hh"
+#include "FN_multi_function_network.hh"
+
+namespace blender {
+namespace fn {
+
+MFNetwork::~MFNetwork()
+{
+ for (MFFunctionNode *node : m_function_nodes) {
+ node->destruct_sockets();
+ node->~MFFunctionNode();
+ }
+ for (MFDummyNode *node : m_dummy_nodes) {
+ node->destruct_sockets();
+ node->~MFDummyNode();
+ }
+}
+
+void MFNode::destruct_sockets()
+{
+ for (MFInputSocket *socket : m_inputs) {
+ socket->~MFInputSocket();
+ }
+ for (MFOutputSocket *socket : m_outputs) {
+ socket->~MFOutputSocket();
+ }
+}
+
+/**
+ * Add a new function node to the network. The caller keeps the ownership of the function. The
+ * function should not be freed before the network. A reference to the new node is returned. The
+ * node is owned by the network.
+ */
+MFFunctionNode &MFNetwork::add_function(const MultiFunction &function)
+{
+ Vector<uint, 16> input_param_indices, output_param_indices;
+
+ for (uint param_index : function.param_indices()) {
+ switch (function.param_type(param_index).interface_type()) {
+ case MFParamType::Input: {
+ input_param_indices.append(param_index);
+ break;
+ }
+ case MFParamType::Output: {
+ output_param_indices.append(param_index);
+ break;
+ }
+ case MFParamType::Mutable: {
+ input_param_indices.append(param_index);
+ output_param_indices.append(param_index);
+ break;
+ }
+ }
+ }
+
+ MFFunctionNode &node = *m_allocator.construct<MFFunctionNode>();
+ m_function_nodes.add_new(&node);
+
+ node.m_network = this;
+ node.m_is_dummy = false;
+ node.m_id = m_node_or_null_by_id.append_and_get_index(&node);
+ node.m_function = &function;
+ node.m_input_param_indices = m_allocator.construct_array_copy<uint>(input_param_indices);
+ node.m_output_param_indices = m_allocator.construct_array_copy<uint>(output_param_indices);
+
+ node.m_inputs = m_allocator.construct_elements_and_pointer_array<MFInputSocket>(
+ input_param_indices.size());
+ node.m_outputs = m_allocator.construct_elements_and_pointer_array<MFOutputSocket>(
+ output_param_indices.size());
+
+ for (uint i : input_param_indices.index_range()) {
+ uint param_index = input_param_indices[i];
+ MFParamType param = function.param_type(param_index);
+ BLI_assert(param.is_input_or_mutable());
+
+ MFInputSocket &socket = *node.m_inputs[i];
+ socket.m_data_type = param.data_type();
+ socket.m_node = &node;
+ socket.m_index = i;
+ socket.m_is_output = false;
+ socket.m_name = function.param_name(param_index);
+ socket.m_origin = nullptr;
+ socket.m_id = m_socket_or_null_by_id.append_and_get_index(&socket);
+ }
+
+ for (uint i : output_param_indices.index_range()) {
+ uint param_index = output_param_indices[i];
+ MFParamType param = function.param_type(param_index);
+ BLI_assert(param.is_output_or_mutable());
+
+ MFOutputSocket &socket = *node.m_outputs[i];
+ socket.m_data_type = param.data_type();
+ socket.m_node = &node;
+ socket.m_index = i;
+ socket.m_is_output = true;
+ socket.m_name = function.param_name(param_index);
+ socket.m_id = m_socket_or_null_by_id.append_and_get_index(&socket);
+ }
+
+ return node;
+}
+
+/**
+ * Add a dummy node with the given input and output sockets.
+ */
+MFDummyNode &MFNetwork::add_dummy(StringRef name,
+ Span<MFDataType> input_types,
+ Span<MFDataType> output_types,
+ Span<StringRef> input_names,
+ Span<StringRef> output_names)
+{
+ assert_same_size(input_types, input_names);
+ assert_same_size(output_types, output_names);
+
+ MFDummyNode &node = *m_allocator.construct<MFDummyNode>();
+ m_dummy_nodes.add_new(&node);
+
+ node.m_network = this;
+ node.m_is_dummy = true;
+ node.m_name = m_allocator.copy_string(name);
+ node.m_id = m_node_or_null_by_id.append_and_get_index(&node);
+
+ node.m_inputs = m_allocator.construct_elements_and_pointer_array<MFInputSocket>(
+ input_types.size());
+ node.m_outputs = m_allocator.construct_elements_and_pointer_array<MFOutputSocket>(
+ output_types.size());
+
+ node.m_input_names = m_allocator.allocate_array<StringRefNull>(input_types.size());
+ node.m_output_names = m_allocator.allocate_array<StringRefNull>(output_types.size());
+
+ for (uint i : input_types.index_range()) {
+ MFInputSocket &socket = *node.m_inputs[i];
+ socket.m_data_type = input_types[i];
+ socket.m_node = &node;
+ socket.m_index = i;
+ socket.m_is_output = false;
+ socket.m_name = m_allocator.copy_string(input_names[i]);
+ socket.m_id = m_socket_or_null_by_id.append_and_get_index(&socket);
+ node.m_input_names[i] = socket.m_name;
+ }
+
+ for (uint i : output_types.index_range()) {
+ MFOutputSocket &socket = *node.m_outputs[i];
+ socket.m_data_type = output_types[i];
+ socket.m_node = &node;
+ socket.m_index = i;
+ socket.m_is_output = true;
+ socket.m_name = m_allocator.copy_string(output_names[i]);
+ socket.m_id = m_socket_or_null_by_id.append_and_get_index(&socket);
+ node.m_output_names[i] = socket.m_name;
+ }
+
+ return node;
+}
+
+/**
+ * Connect two sockets. This invokes undefined behavior if the sockets belong to different
+ * networks, the sockets have a different data type, or the `to` socket is connected to something
+ * else already.
+ */
+void MFNetwork::add_link(MFOutputSocket &from, MFInputSocket &to)
+{
+ BLI_assert(to.m_origin == nullptr);
+ BLI_assert(from.m_node->m_network == to.m_node->m_network);
+ BLI_assert(from.m_data_type == to.m_data_type);
+ from.m_targets.append(&to);
+ to.m_origin = &from;
+}
+
+MFOutputSocket &MFNetwork::add_input(StringRef name, MFDataType data_type)
+{
+ return this->add_dummy(name, {}, {data_type}, {}, {name}).output(0);
+}
+
+MFInputSocket &MFNetwork::add_output(StringRef name, MFDataType data_type)
+{
+ return this->add_dummy(name, {data_type}, {}, {name}, {}).input(0);
+}
+
+void MFNetwork::relink(MFOutputSocket &old_output, MFOutputSocket &new_output)
+{
+ BLI_assert(&old_output != &new_output);
+ for (MFInputSocket *input : old_output.targets()) {
+ input->m_origin = &new_output;
+ }
+ new_output.m_targets.extend(old_output.m_targets);
+ old_output.m_targets.clear();
+}
+
+void MFNetwork::remove(MFNode &node)
+{
+ for (MFInputSocket *socket : node.m_inputs) {
+ if (socket->m_origin != nullptr) {
+ socket->m_origin->m_targets.remove_first_occurrence_and_reorder(socket);
+ }
+ m_socket_or_null_by_id[socket->m_id] = nullptr;
+ }
+ for (MFOutputSocket *socket : node.m_outputs) {
+ for (MFInputSocket *other : socket->m_targets) {
+ other->m_origin = nullptr;
+ }
+ m_socket_or_null_by_id[socket->m_id] = nullptr;
+ }
+ node.destruct_sockets();
+ if (node.is_dummy()) {
+ MFDummyNode &dummy_node = node.as_dummy();
+ dummy_node.~MFDummyNode();
+ m_dummy_nodes.remove_contained(&dummy_node);
+ }
+ else {
+ MFFunctionNode &function_node = node.as_function();
+ function_node.~MFFunctionNode();
+ m_function_nodes.remove_contained(&function_node);
+ }
+ m_node_or_null_by_id[node.m_id] = nullptr;
+}
+
+std::string MFNetwork::to_dot() const
+{
+ dot::DirectedGraph digraph;
+ digraph.set_rankdir(dot::Attr_rankdir::LeftToRight);
+
+ Map<const MFNode *, dot::NodeWithSocketsRef> dot_nodes;
+
+ Vector<const MFNode *> all_nodes;
+ all_nodes.extend(m_function_nodes.as_span());
+ all_nodes.extend(m_dummy_nodes.as_span());
+
+ for (const MFNode *node : all_nodes) {
+ dot::Node &dot_node = digraph.new_node("");
+
+ Vector<std::string> input_names, output_names;
+ for (const MFInputSocket *socket : node->m_inputs) {
+ input_names.append(socket->name() + "(" + socket->data_type().to_string() + ")");
+ }
+ for (const MFOutputSocket *socket : node->m_outputs) {
+ output_names.append(socket->name() + " (" + socket->data_type().to_string() + ")");
+ }
+
+ dot::NodeWithSocketsRef dot_node_ref{dot_node, node->name(), input_names, output_names};
+ dot_nodes.add_new(node, dot_node_ref);
+ }
+
+ for (const MFNode *to_node : all_nodes) {
+ dot::NodeWithSocketsRef to_dot_node = dot_nodes.lookup(to_node);
+
+ for (const MFInputSocket *to_socket : to_node->m_inputs) {
+ const MFOutputSocket *from_socket = to_socket->m_origin;
+ if (from_socket != nullptr) {
+ const MFNode *from_node = from_socket->m_node;
+ dot::NodeWithSocketsRef from_dot_node = dot_nodes.lookup(from_node);
+ digraph.new_edge(from_dot_node.output(from_socket->m_index),
+ to_dot_node.input(to_socket->m_index));
+ }
+ }
+ }
+
+ return digraph.to_dot_string();
+}
+
+} // namespace fn
+} // namespace blender
diff --git a/source/blender/functions/intern/multi_function_network_evaluation.cc b/source/blender/functions/intern/multi_function_network_evaluation.cc
new file mode 100644
index 00000000000..327a3a66561
--- /dev/null
+++ b/source/blender/functions/intern/multi_function_network_evaluation.cc
@@ -0,0 +1,1063 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup fn
+ *
+ * The `MFNetworkEvaluator` class is a multi-function that consists of potentially many smaller
+ * multi-functions. When called, it traverses the underlying MFNetwork and executes the required
+ * function nodes.
+ *
+ * There are many possible approaches to evaluate a function network. The approach implemented
+ * below has the following features:
+ * - It does not use recursion. Those could become problematic with long node chains.
+ * - It can handle all existing parameter types (including mutable parameters).
+ * - Avoids data copies in many cases.
+ * - Every node is executed at most once.
+ * - Can compute sub-functions on a single element, when the result is the same for all elements.
+ *
+ * Possible improvements:
+ * - Cache and reuse buffers.
+ * - Use "deepest depth first" heuristic to decide which order the inputs of a node should be
+ * computed. This reduces the number of required temporary buffers when they are reused.
+ */
+
+#include "FN_multi_function_network_evaluation.hh"
+
+#include "BLI_stack.hh"
+
+namespace blender {
+namespace fn {
+
+struct Value;
+
+/**
+ * This keeps track of all the values that flow through the multi-function network. Therefore it
+ * maintains a mapping between output sockets and their corresponding values. Every `value`
+ * references some memory, that is owned either by the caller or this storage.
+ *
+ * A value can be owned by different sockets over time to avoid unnecessary copies.
+ */
+class MFNetworkEvaluationStorage {
+ private:
+ LinearAllocator<> m_allocator;
+ IndexMask m_mask;
+ Array<Value *> m_value_per_output_id;
+ uint m_min_array_size;
+
+ public:
+ MFNetworkEvaluationStorage(IndexMask mask, uint max_socket_id);
+ ~MFNetworkEvaluationStorage();
+
+ /* Add the values that have been provided by the caller of the multi-function network. */
+ void add_single_input_from_caller(const MFOutputSocket &socket, GVSpan virtual_span);
+ void add_vector_input_from_caller(const MFOutputSocket &socket, GVArraySpan virtual_array_span);
+ void add_single_output_from_caller(const MFOutputSocket &socket, GMutableSpan span);
+ void add_vector_output_from_caller(const MFOutputSocket &socket, GVectorArray &vector_array);
+
+ /* Get input buffers for function node evaluations. */
+ GVSpan get_single_input__full(const MFInputSocket &socket);
+ GVSpan get_single_input__single(const MFInputSocket &socket);
+ GVArraySpan get_vector_input__full(const MFInputSocket &socket);
+ GVArraySpan get_vector_input__single(const MFInputSocket &socket);
+
+ /* Get output buffers for function node evaluations. */
+ GMutableSpan get_single_output__full(const MFOutputSocket &socket);
+ GMutableSpan get_single_output__single(const MFOutputSocket &socket);
+ GVectorArray &get_vector_output__full(const MFOutputSocket &socket);
+ GVectorArray &get_vector_output__single(const MFOutputSocket &socket);
+
+ /* Get mutable buffers for function node evaluations. */
+ GMutableSpan get_mutable_single__full(const MFInputSocket &input, const MFOutputSocket &output);
+ GMutableSpan get_mutable_single__single(const MFInputSocket &input,
+ const MFOutputSocket &output);
+ GVectorArray &get_mutable_vector__full(const MFInputSocket &input, const MFOutputSocket &output);
+ GVectorArray &get_mutable_vector__single(const MFInputSocket &input,
+ const MFOutputSocket &output);
+
+ /* Mark a node as being done with evaluation. This might free temporary buffers that are no
+ * longer needed. */
+ void finish_node(const MFFunctionNode &node);
+ void finish_output_socket(const MFOutputSocket &socket);
+ void finish_input_socket(const MFInputSocket &socket);
+
+ IndexMask mask() const;
+ bool socket_is_computed(const MFOutputSocket &socket);
+ bool is_same_value_for_every_index(const MFOutputSocket &socket);
+ bool socket_has_buffer_for_output(const MFOutputSocket &socket);
+};
+
+MFNetworkEvaluator::MFNetworkEvaluator(Vector<const MFOutputSocket *> inputs,
+ Vector<const MFInputSocket *> outputs)
+ : m_inputs(std::move(inputs)), m_outputs(std::move(outputs))
+{
+ BLI_assert(m_outputs.size() > 0);
+ MFSignatureBuilder signature = this->get_builder("Function Tree");
+
+ for (auto socket : m_inputs) {
+ BLI_assert(socket->node().is_dummy());
+
+ MFDataType type = socket->data_type();
+ switch (type.category()) {
+ case MFDataType::Single:
+ signature.single_input("Input", type.single_type());
+ break;
+ case MFDataType::Vector:
+ signature.vector_input("Input", type.vector_base_type());
+ break;
+ }
+ }
+
+ for (auto socket : m_outputs) {
+ BLI_assert(socket->node().is_dummy());
+
+ MFDataType type = socket->data_type();
+ switch (type.category()) {
+ case MFDataType::Single:
+ signature.single_output("Output", type.single_type());
+ break;
+ case MFDataType::Vector:
+ signature.vector_output("Output", type.vector_base_type());
+ break;
+ }
+ }
+}
+
+void MFNetworkEvaluator::call(IndexMask mask, MFParams params, MFContext context) const
+{
+ if (mask.size() == 0) {
+ return;
+ }
+
+ const MFNetwork &network = m_outputs[0]->node().network();
+ Storage storage(mask, network.max_socket_id());
+
+ Vector<const MFInputSocket *> outputs_to_initialize_in_the_end;
+
+ this->copy_inputs_to_storage(params, storage);
+ this->copy_outputs_to_storage(params, storage, outputs_to_initialize_in_the_end);
+ this->evaluate_network_to_compute_outputs(context, storage);
+ this->initialize_remaining_outputs(params, storage, outputs_to_initialize_in_the_end);
+}
+
+BLI_NOINLINE void MFNetworkEvaluator::copy_inputs_to_storage(MFParams params,
+ Storage &storage) const
+{
+ for (uint input_index : m_inputs.index_range()) {
+ uint param_index = input_index + 0;
+ const MFOutputSocket &socket = *m_inputs[input_index];
+ switch (socket.data_type().category()) {
+ case MFDataType::Single: {
+ GVSpan input_list = params.readonly_single_input(param_index);
+ storage.add_single_input_from_caller(socket, input_list);
+ break;
+ }
+ case MFDataType::Vector: {
+ GVArraySpan input_list_list = params.readonly_vector_input(param_index);
+ storage.add_vector_input_from_caller(socket, input_list_list);
+ break;
+ }
+ }
+ }
+}
+
+BLI_NOINLINE void MFNetworkEvaluator::copy_outputs_to_storage(
+ MFParams params,
+ Storage &storage,
+ Vector<const MFInputSocket *> &outputs_to_initialize_in_the_end) const
+{
+ for (uint output_index : m_outputs.index_range()) {
+ uint param_index = output_index + m_inputs.size();
+ const MFInputSocket &socket = *m_outputs[output_index];
+ const MFOutputSocket &origin = *socket.origin();
+
+ if (origin.node().is_dummy()) {
+ BLI_assert(m_inputs.contains(&origin));
+ /* Don't overwrite input buffers. */
+ outputs_to_initialize_in_the_end.append(&socket);
+ continue;
+ }
+
+ if (storage.socket_has_buffer_for_output(origin)) {
+ /* When two outputs will be initialized to the same values. */
+ outputs_to_initialize_in_the_end.append(&socket);
+ continue;
+ }
+
+ switch (socket.data_type().category()) {
+ case MFDataType::Single: {
+ GMutableSpan span = params.uninitialized_single_output(param_index);
+ storage.add_single_output_from_caller(origin, span);
+ break;
+ }
+ case MFDataType::Vector: {
+ GVectorArray &vector_array = params.vector_output(param_index);
+ storage.add_vector_output_from_caller(origin, vector_array);
+ break;
+ }
+ }
+ }
+}
+
+BLI_NOINLINE void MFNetworkEvaluator::evaluate_network_to_compute_outputs(
+ MFContext &global_context, Storage &storage) const
+{
+ Stack<const MFOutputSocket *, 32> sockets_to_compute;
+ for (const MFInputSocket *socket : m_outputs) {
+ sockets_to_compute.push(socket->origin());
+ }
+
+ Vector<const MFOutputSocket *, 32> missing_sockets;
+
+ /* This is the main loop that traverses the MFNetwork. */
+ while (!sockets_to_compute.is_empty()) {
+ const MFOutputSocket &socket = *sockets_to_compute.peek();
+ const MFNode &node = socket.node();
+
+ if (storage.socket_is_computed(socket)) {
+ sockets_to_compute.pop();
+ continue;
+ }
+
+ BLI_assert(node.is_function());
+ BLI_assert(node.all_inputs_have_origin());
+ const MFFunctionNode &function_node = node.as_function();
+
+ missing_sockets.clear();
+ function_node.foreach_origin_socket([&](const MFOutputSocket &origin) {
+ if (!storage.socket_is_computed(origin)) {
+ missing_sockets.append(&origin);
+ }
+ });
+
+ sockets_to_compute.push_multiple(missing_sockets);
+
+ bool all_inputs_are_computed = missing_sockets.size() == 0;
+ if (all_inputs_are_computed) {
+ this->evaluate_function(global_context, function_node, storage);
+ sockets_to_compute.pop();
+ }
+ }
+}
+
+BLI_NOINLINE void MFNetworkEvaluator::evaluate_function(MFContext &global_context,
+ const MFFunctionNode &function_node,
+ Storage &storage) const
+{
+ const MultiFunction &function = function_node.function();
+ // std::cout << "Function: " << function.name() << "\n";
+
+ if (this->can_do_single_value_evaluation(function_node, storage)) {
+ /* The function output would be the same for all elements. Therefore, it is enough to call the
+ * function only on a single element. This can avoid many duplicate computations. */
+ MFParamsBuilder params{function, 1};
+
+ for (uint param_index : function.param_indices()) {
+ MFParamType param_type = function.param_type(param_index);
+ switch (param_type.category()) {
+ case MFParamType::SingleInput: {
+ const MFInputSocket &socket = function_node.input_for_param(param_index);
+ GVSpan values = storage.get_single_input__single(socket);
+ params.add_readonly_single_input(values);
+ break;
+ }
+ case MFParamType::VectorInput: {
+ const MFInputSocket &socket = function_node.input_for_param(param_index);
+ GVArraySpan values = storage.get_vector_input__single(socket);
+ params.add_readonly_vector_input(values);
+ break;
+ }
+ case MFParamType::SingleOutput: {
+ const MFOutputSocket &socket = function_node.output_for_param(param_index);
+ GMutableSpan values = storage.get_single_output__single(socket);
+ params.add_uninitialized_single_output(values);
+ break;
+ }
+ case MFParamType::VectorOutput: {
+ const MFOutputSocket &socket = function_node.output_for_param(param_index);
+ GVectorArray &values = storage.get_vector_output__single(socket);
+ params.add_vector_output(values);
+ break;
+ }
+ case MFParamType::SingleMutable: {
+ const MFInputSocket &input = function_node.input_for_param(param_index);
+ const MFOutputSocket &output = function_node.output_for_param(param_index);
+ GMutableSpan values = storage.get_mutable_single__single(input, output);
+ params.add_single_mutable(values);
+ break;
+ }
+ case MFParamType::VectorMutable: {
+ const MFInputSocket &input = function_node.input_for_param(param_index);
+ const MFOutputSocket &output = function_node.output_for_param(param_index);
+ GVectorArray &values = storage.get_mutable_vector__single(input, output);
+ params.add_vector_mutable(values);
+ break;
+ }
+ }
+ }
+
+ function.call(IndexRange(1), params, global_context);
+ }
+ else {
+ MFParamsBuilder params{function, storage.mask().min_array_size()};
+
+ for (uint param_index : function.param_indices()) {
+ MFParamType param_type = function.param_type(param_index);
+ switch (param_type.category()) {
+ case MFParamType::SingleInput: {
+ const MFInputSocket &socket = function_node.input_for_param(param_index);
+ GVSpan values = storage.get_single_input__full(socket);
+ params.add_readonly_single_input(values);
+ break;
+ }
+ case MFParamType::VectorInput: {
+ const MFInputSocket &socket = function_node.input_for_param(param_index);
+ GVArraySpan values = storage.get_vector_input__full(socket);
+ params.add_readonly_vector_input(values);
+ break;
+ }
+ case MFParamType::SingleOutput: {
+ const MFOutputSocket &socket = function_node.output_for_param(param_index);
+ GMutableSpan values = storage.get_single_output__full(socket);
+ params.add_uninitialized_single_output(values);
+ break;
+ }
+ case MFParamType::VectorOutput: {
+ const MFOutputSocket &socket = function_node.output_for_param(param_index);
+ GVectorArray &values = storage.get_vector_output__full(socket);
+ params.add_vector_output(values);
+ break;
+ }
+ case MFParamType::SingleMutable: {
+ const MFInputSocket &input = function_node.input_for_param(param_index);
+ const MFOutputSocket &output = function_node.output_for_param(param_index);
+ GMutableSpan values = storage.get_mutable_single__full(input, output);
+ params.add_single_mutable(values);
+ break;
+ }
+ case MFParamType::VectorMutable: {
+ const MFInputSocket &input = function_node.input_for_param(param_index);
+ const MFOutputSocket &output = function_node.output_for_param(param_index);
+ GVectorArray &values = storage.get_mutable_vector__full(input, output);
+ params.add_vector_mutable(values);
+ break;
+ }
+ }
+ }
+
+ function.call(storage.mask(), params, global_context);
+ }
+
+ storage.finish_node(function_node);
+}
+
+bool MFNetworkEvaluator::can_do_single_value_evaluation(const MFFunctionNode &function_node,
+ Storage &storage) const
+{
+ for (const MFInputSocket *socket : function_node.inputs()) {
+ if (!storage.is_same_value_for_every_index(*socket->origin())) {
+ return false;
+ }
+ }
+ if (storage.mask().min_array_size() >= 1) {
+ for (const MFOutputSocket *socket : function_node.outputs()) {
+ if (storage.socket_has_buffer_for_output(*socket)) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+BLI_NOINLINE void MFNetworkEvaluator::initialize_remaining_outputs(
+ MFParams params, Storage &storage, Span<const MFInputSocket *> remaining_outputs) const
+{
+ for (const MFInputSocket *socket : remaining_outputs) {
+ uint param_index = m_inputs.size() + m_outputs.first_index_of(socket);
+
+ switch (socket->data_type().category()) {
+ case MFDataType::Single: {
+ GVSpan values = storage.get_single_input__full(*socket);
+ GMutableSpan output_values = params.uninitialized_single_output(param_index);
+ values.materialize_to_uninitialized(storage.mask(), output_values.buffer());
+ break;
+ }
+ case MFDataType::Vector: {
+ GVArraySpan values = storage.get_vector_input__full(*socket);
+ GVectorArray &output_values = params.vector_output(param_index);
+ output_values.extend(storage.mask(), values);
+ break;
+ }
+ }
+ }
+}
+
+/* -------------------------------------------------------------------- */
+/** \name Value Types
+ * \{ */
+
+enum class ValueType {
+ InputSingle,
+ InputVector,
+ OutputSingle,
+ OutputVector,
+ OwnSingle,
+ OwnVector,
+};
+
+struct Value {
+ ValueType type;
+
+ Value(ValueType type) : type(type)
+ {
+ }
+};
+
+struct InputSingleValue : public Value {
+ /** This span has been provided by the code that called the multi-function network. */
+ GVSpan virtual_span;
+
+ InputSingleValue(GVSpan virtual_span) : Value(ValueType::InputSingle), virtual_span(virtual_span)
+ {
+ }
+};
+
+struct InputVectorValue : public Value {
+ /** This span has been provided by the code that called the multi-function network. */
+ GVArraySpan virtual_array_span;
+
+ InputVectorValue(GVArraySpan virtual_array_span)
+ : Value(ValueType::InputVector), virtual_array_span(virtual_array_span)
+ {
+ }
+};
+
+struct OutputValue : public Value {
+ bool is_computed = false;
+
+ OutputValue(ValueType type) : Value(type)
+ {
+ }
+};
+
+struct OutputSingleValue : public OutputValue {
+ /** This span has been provided by the code that called the multi-function network. */
+ GMutableSpan span;
+
+ OutputSingleValue(GMutableSpan span) : OutputValue(ValueType::OutputSingle), span(span)
+ {
+ }
+};
+
+struct OutputVectorValue : public OutputValue {
+ /** This vector array has been provided by the code that called the multi-function network. */
+ GVectorArray *vector_array;
+
+ OutputVectorValue(GVectorArray &vector_array)
+ : OutputValue(ValueType::OutputVector), vector_array(&vector_array)
+ {
+ }
+};
+
+struct OwnSingleValue : public Value {
+ /** This span has been allocated during the evaluation of the multi-function network and contains
+ * intermediate data. It has to be freed once the network evaluation is finished. */
+ GMutableSpan span;
+ int max_remaining_users;
+ bool is_single_allocated;
+
+ OwnSingleValue(GMutableSpan span, int max_remaining_users, bool is_single_allocated)
+ : Value(ValueType::OwnSingle),
+ span(span),
+ max_remaining_users(max_remaining_users),
+ is_single_allocated(is_single_allocated)
+ {
+ }
+};
+
+struct OwnVectorValue : public Value {
+ /** This vector array has been allocated during the evaluation of the multi-function network and
+ * contains intermediate data. It has to be freed once the network evaluation is finished. */
+ GVectorArray *vector_array;
+ int max_remaining_users;
+
+ OwnVectorValue(GVectorArray &vector_array, int max_remaining_users)
+ : Value(ValueType::OwnVector),
+ vector_array(&vector_array),
+ max_remaining_users(max_remaining_users)
+ {
+ }
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Storage methods
+ * \{ */
+
+MFNetworkEvaluationStorage::MFNetworkEvaluationStorage(IndexMask mask, uint max_socket_id)
+ : m_mask(mask),
+ m_value_per_output_id(max_socket_id + 1, nullptr),
+ m_min_array_size(mask.min_array_size())
+{
+}
+
+MFNetworkEvaluationStorage::~MFNetworkEvaluationStorage()
+{
+ for (Value *any_value : m_value_per_output_id) {
+ if (any_value == nullptr) {
+ continue;
+ }
+ else if (any_value->type == ValueType::OwnSingle) {
+ OwnSingleValue *value = (OwnSingleValue *)any_value;
+ GMutableSpan span = value->span;
+ const CPPType &type = span.type();
+ if (value->is_single_allocated) {
+ type.destruct(span.buffer());
+ }
+ else {
+ type.destruct_indices(span.buffer(), m_mask);
+ MEM_freeN(span.buffer());
+ }
+ }
+ else if (any_value->type == ValueType::OwnVector) {
+ OwnVectorValue *value = (OwnVectorValue *)any_value;
+ delete value->vector_array;
+ }
+ }
+}
+
+IndexMask MFNetworkEvaluationStorage::mask() const
+{
+ return m_mask;
+}
+
+bool MFNetworkEvaluationStorage::socket_is_computed(const MFOutputSocket &socket)
+{
+ Value *any_value = m_value_per_output_id[socket.id()];
+ if (any_value == nullptr) {
+ return false;
+ }
+ if (ELEM(any_value->type, ValueType::OutputSingle, ValueType::OutputVector)) {
+ return ((OutputValue *)any_value)->is_computed;
+ }
+ return true;
+}
+
+bool MFNetworkEvaluationStorage::is_same_value_for_every_index(const MFOutputSocket &socket)
+{
+ Value *any_value = m_value_per_output_id[socket.id()];
+ switch (any_value->type) {
+ case ValueType::OwnSingle:
+ return ((OwnSingleValue *)any_value)->span.size() == 1;
+ case ValueType::OwnVector:
+ return ((OwnVectorValue *)any_value)->vector_array->size() == 1;
+ case ValueType::InputSingle:
+ return ((InputSingleValue *)any_value)->virtual_span.is_single_element();
+ case ValueType::InputVector:
+ return ((InputVectorValue *)any_value)->virtual_array_span.is_single_array();
+ case ValueType::OutputSingle:
+ return ((OutputSingleValue *)any_value)->span.size() == 1;
+ case ValueType::OutputVector:
+ return ((OutputVectorValue *)any_value)->vector_array->size() == 1;
+ }
+ BLI_assert(false);
+ return false;
+}
+
+bool MFNetworkEvaluationStorage::socket_has_buffer_for_output(const MFOutputSocket &socket)
+{
+ Value *any_value = m_value_per_output_id[socket.id()];
+ if (any_value == nullptr) {
+ return false;
+ }
+
+ BLI_assert(ELEM(any_value->type, ValueType::OutputSingle, ValueType::OutputVector));
+ return true;
+}
+
+void MFNetworkEvaluationStorage::finish_node(const MFFunctionNode &node)
+{
+ for (const MFInputSocket *socket : node.inputs()) {
+ this->finish_input_socket(*socket);
+ }
+ for (const MFOutputSocket *socket : node.outputs()) {
+ this->finish_output_socket(*socket);
+ }
+}
+
+void MFNetworkEvaluationStorage::finish_output_socket(const MFOutputSocket &socket)
+{
+ Value *any_value = m_value_per_output_id[socket.id()];
+ if (any_value == nullptr) {
+ return;
+ }
+
+ if (ELEM(any_value->type, ValueType::OutputSingle, ValueType::OutputVector)) {
+ ((OutputValue *)any_value)->is_computed = true;
+ }
+}
+
+void MFNetworkEvaluationStorage::finish_input_socket(const MFInputSocket &socket)
+{
+ const MFOutputSocket &origin = *socket.origin();
+
+ Value *any_value = m_value_per_output_id[origin.id()];
+ if (any_value == nullptr) {
+ /* Can happen when a value has been forward to the next node. */
+ return;
+ }
+
+ switch (any_value->type) {
+ case ValueType::InputSingle:
+ case ValueType::OutputSingle:
+ case ValueType::InputVector:
+ case ValueType::OutputVector: {
+ break;
+ }
+ case ValueType::OwnSingle: {
+ OwnSingleValue *value = (OwnSingleValue *)any_value;
+ BLI_assert(value->max_remaining_users >= 1);
+ value->max_remaining_users--;
+ if (value->max_remaining_users == 0) {
+ GMutableSpan span = value->span;
+ const CPPType &type = span.type();
+ if (value->is_single_allocated) {
+ type.destruct(span.buffer());
+ }
+ else {
+ type.destruct_indices(span.buffer(), m_mask);
+ MEM_freeN(span.buffer());
+ }
+ m_value_per_output_id[origin.id()] = nullptr;
+ }
+ break;
+ }
+ case ValueType::OwnVector: {
+ OwnVectorValue *value = (OwnVectorValue *)any_value;
+ BLI_assert(value->max_remaining_users >= 1);
+ value->max_remaining_users--;
+ if (value->max_remaining_users == 0) {
+ delete value->vector_array;
+ m_value_per_output_id[origin.id()] = nullptr;
+ }
+ break;
+ }
+ }
+}
+
+void MFNetworkEvaluationStorage::add_single_input_from_caller(const MFOutputSocket &socket,
+ GVSpan virtual_span)
+{
+ BLI_assert(m_value_per_output_id[socket.id()] == nullptr);
+ BLI_assert(virtual_span.size() >= m_min_array_size);
+
+ auto *value = m_allocator.construct<InputSingleValue>(virtual_span);
+ m_value_per_output_id[socket.id()] = value;
+}
+
+void MFNetworkEvaluationStorage::add_vector_input_from_caller(const MFOutputSocket &socket,
+ GVArraySpan virtual_array_span)
+{
+ BLI_assert(m_value_per_output_id[socket.id()] == nullptr);
+ BLI_assert(virtual_array_span.size() >= m_min_array_size);
+
+ auto *value = m_allocator.construct<InputVectorValue>(virtual_array_span);
+ m_value_per_output_id[socket.id()] = value;
+}
+
+void MFNetworkEvaluationStorage::add_single_output_from_caller(const MFOutputSocket &socket,
+ GMutableSpan span)
+{
+ BLI_assert(m_value_per_output_id[socket.id()] == nullptr);
+ BLI_assert(span.size() >= m_min_array_size);
+
+ auto *value = m_allocator.construct<OutputSingleValue>(span);
+ m_value_per_output_id[socket.id()] = value;
+}
+
+void MFNetworkEvaluationStorage::add_vector_output_from_caller(const MFOutputSocket &socket,
+ GVectorArray &vector_array)
+{
+ BLI_assert(m_value_per_output_id[socket.id()] == nullptr);
+ BLI_assert(vector_array.size() >= m_min_array_size);
+
+ auto *value = m_allocator.construct<OutputVectorValue>(vector_array);
+ m_value_per_output_id[socket.id()] = value;
+}
+
+GMutableSpan MFNetworkEvaluationStorage::get_single_output__full(const MFOutputSocket &socket)
+{
+ Value *any_value = m_value_per_output_id[socket.id()];
+ if (any_value == nullptr) {
+ const CPPType &type = socket.data_type().single_type();
+ void *buffer = MEM_mallocN_aligned(m_min_array_size * type.size(), type.alignment(), AT);
+ GMutableSpan span(type, buffer, m_min_array_size);
+
+ auto *value = m_allocator.construct<OwnSingleValue>(span, socket.targets().size(), false);
+ m_value_per_output_id[socket.id()] = value;
+
+ return span;
+ }
+ else {
+ BLI_assert(any_value->type == ValueType::OutputSingle);
+ return ((OutputSingleValue *)any_value)->span;
+ }
+}
+
+GMutableSpan MFNetworkEvaluationStorage::get_single_output__single(const MFOutputSocket &socket)
+{
+ Value *any_value = m_value_per_output_id[socket.id()];
+ if (any_value == nullptr) {
+ const CPPType &type = socket.data_type().single_type();
+ void *buffer = m_allocator.allocate(type.size(), type.alignment());
+ GMutableSpan span(type, buffer, 1);
+
+ auto *value = m_allocator.construct<OwnSingleValue>(span, socket.targets().size(), true);
+ m_value_per_output_id[socket.id()] = value;
+
+ return value->span;
+ }
+ else {
+ BLI_assert(any_value->type == ValueType::OutputSingle);
+ GMutableSpan span = ((OutputSingleValue *)any_value)->span;
+ BLI_assert(span.size() == 1);
+ return span;
+ }
+}
+
+GVectorArray &MFNetworkEvaluationStorage::get_vector_output__full(const MFOutputSocket &socket)
+{
+ Value *any_value = m_value_per_output_id[socket.id()];
+ if (any_value == nullptr) {
+ const CPPType &type = socket.data_type().vector_base_type();
+ GVectorArray *vector_array = new GVectorArray(type, m_min_array_size);
+
+ auto *value = m_allocator.construct<OwnVectorValue>(*vector_array, socket.targets().size());
+ m_value_per_output_id[socket.id()] = value;
+
+ return *value->vector_array;
+ }
+ else {
+ BLI_assert(any_value->type == ValueType::OutputVector);
+ return *((OutputVectorValue *)any_value)->vector_array;
+ }
+}
+
+GVectorArray &MFNetworkEvaluationStorage::get_vector_output__single(const MFOutputSocket &socket)
+{
+ Value *any_value = m_value_per_output_id[socket.id()];
+ if (any_value == nullptr) {
+ const CPPType &type = socket.data_type().vector_base_type();
+ GVectorArray *vector_array = new GVectorArray(type, 1);
+
+ auto *value = m_allocator.construct<OwnVectorValue>(*vector_array, socket.targets().size());
+ m_value_per_output_id[socket.id()] = value;
+
+ return *value->vector_array;
+ }
+ else {
+ BLI_assert(any_value->type == ValueType::OutputVector);
+ GVectorArray &vector_array = *((OutputVectorValue *)any_value)->vector_array;
+ BLI_assert(vector_array.size() == 1);
+ return vector_array;
+ }
+}
+
+GMutableSpan MFNetworkEvaluationStorage::get_mutable_single__full(const MFInputSocket &input,
+ const MFOutputSocket &output)
+{
+ const MFOutputSocket &from = *input.origin();
+ const MFOutputSocket &to = output;
+ const CPPType &type = from.data_type().single_type();
+
+ Value *from_any_value = m_value_per_output_id[from.id()];
+ Value *to_any_value = m_value_per_output_id[to.id()];
+ BLI_assert(from_any_value != nullptr);
+ BLI_assert(type == to.data_type().single_type());
+
+ if (to_any_value != nullptr) {
+ BLI_assert(to_any_value->type == ValueType::OutputSingle);
+ GMutableSpan span = ((OutputSingleValue *)to_any_value)->span;
+ GVSpan virtual_span = this->get_single_input__full(input);
+ virtual_span.materialize_to_uninitialized(m_mask, span.buffer());
+ return span;
+ }
+
+ if (from_any_value->type == ValueType::OwnSingle) {
+ OwnSingleValue *value = (OwnSingleValue *)from_any_value;
+ if (value->max_remaining_users == 1 && !value->is_single_allocated) {
+ m_value_per_output_id[to.id()] = value;
+ m_value_per_output_id[from.id()] = nullptr;
+ value->max_remaining_users = to.targets().size();
+ return value->span;
+ }
+ }
+
+ GVSpan virtual_span = this->get_single_input__full(input);
+ void *new_buffer = MEM_mallocN_aligned(m_min_array_size * type.size(), type.alignment(), AT);
+ GMutableSpan new_array_ref(type, new_buffer, m_min_array_size);
+ virtual_span.materialize_to_uninitialized(m_mask, new_array_ref.buffer());
+
+ OwnSingleValue *new_value = m_allocator.construct<OwnSingleValue>(
+ new_array_ref, to.targets().size(), false);
+ m_value_per_output_id[to.id()] = new_value;
+ return new_array_ref;
+}
+
+GMutableSpan MFNetworkEvaluationStorage::get_mutable_single__single(const MFInputSocket &input,
+ const MFOutputSocket &output)
+{
+ const MFOutputSocket &from = *input.origin();
+ const MFOutputSocket &to = output;
+ const CPPType &type = from.data_type().single_type();
+
+ Value *from_any_value = m_value_per_output_id[from.id()];
+ Value *to_any_value = m_value_per_output_id[to.id()];
+ BLI_assert(from_any_value != nullptr);
+ BLI_assert(type == to.data_type().single_type());
+
+ if (to_any_value != nullptr) {
+ BLI_assert(to_any_value->type == ValueType::OutputSingle);
+ GMutableSpan span = ((OutputSingleValue *)to_any_value)->span;
+ BLI_assert(span.size() == 1);
+ GVSpan virtual_span = this->get_single_input__single(input);
+ type.copy_to_uninitialized(virtual_span.as_single_element(), span[0]);
+ return span;
+ }
+
+ if (from_any_value->type == ValueType::OwnSingle) {
+ OwnSingleValue *value = (OwnSingleValue *)from_any_value;
+ if (value->max_remaining_users == 1) {
+ m_value_per_output_id[to.id()] = value;
+ m_value_per_output_id[from.id()] = nullptr;
+ value->max_remaining_users = to.targets().size();
+ BLI_assert(value->span.size() == 1);
+ return value->span;
+ }
+ }
+
+ GVSpan virtual_span = this->get_single_input__single(input);
+
+ void *new_buffer = m_allocator.allocate(type.size(), type.alignment());
+ type.copy_to_uninitialized(virtual_span.as_single_element(), new_buffer);
+ GMutableSpan new_array_ref(type, new_buffer, 1);
+
+ OwnSingleValue *new_value = m_allocator.construct<OwnSingleValue>(
+ new_array_ref, to.targets().size(), true);
+ m_value_per_output_id[to.id()] = new_value;
+ return new_array_ref;
+}
+
+GVectorArray &MFNetworkEvaluationStorage::get_mutable_vector__full(const MFInputSocket &input,
+ const MFOutputSocket &output)
+{
+ const MFOutputSocket &from = *input.origin();
+ const MFOutputSocket &to = output;
+ const CPPType &base_type = from.data_type().vector_base_type();
+
+ Value *from_any_value = m_value_per_output_id[from.id()];
+ Value *to_any_value = m_value_per_output_id[to.id()];
+ BLI_assert(from_any_value != nullptr);
+ BLI_assert(base_type == to.data_type().vector_base_type());
+
+ if (to_any_value != nullptr) {
+ BLI_assert(to_any_value->type == ValueType::OutputVector);
+ GVectorArray &vector_array = *((OutputVectorValue *)to_any_value)->vector_array;
+ GVArraySpan virtual_array_span = this->get_vector_input__full(input);
+ vector_array.extend(m_mask, virtual_array_span);
+ return vector_array;
+ }
+
+ if (from_any_value->type == ValueType::OwnVector) {
+ OwnVectorValue *value = (OwnVectorValue *)from_any_value;
+ if (value->max_remaining_users == 1) {
+ m_value_per_output_id[to.id()] = value;
+ m_value_per_output_id[from.id()] = nullptr;
+ value->max_remaining_users = to.targets().size();
+ return *value->vector_array;
+ }
+ }
+
+ GVArraySpan virtual_array_span = this->get_vector_input__full(input);
+
+ GVectorArray *new_vector_array = new GVectorArray(base_type, m_min_array_size);
+ new_vector_array->extend(m_mask, virtual_array_span);
+
+ OwnVectorValue *new_value = m_allocator.construct<OwnVectorValue>(*new_vector_array,
+ to.targets().size());
+ m_value_per_output_id[to.id()] = new_value;
+
+ return *new_vector_array;
+}
+
+GVectorArray &MFNetworkEvaluationStorage::get_mutable_vector__single(const MFInputSocket &input,
+ const MFOutputSocket &output)
+{
+ const MFOutputSocket &from = *input.origin();
+ const MFOutputSocket &to = output;
+ const CPPType &base_type = from.data_type().vector_base_type();
+
+ Value *from_any_value = m_value_per_output_id[from.id()];
+ Value *to_any_value = m_value_per_output_id[to.id()];
+ BLI_assert(from_any_value != nullptr);
+ BLI_assert(base_type == to.data_type().vector_base_type());
+
+ if (to_any_value != nullptr) {
+ BLI_assert(to_any_value->type == ValueType::OutputVector);
+ GVectorArray &vector_array = *((OutputVectorValue *)to_any_value)->vector_array;
+ BLI_assert(vector_array.size() == 1);
+ GVArraySpan virtual_array_span = this->get_vector_input__single(input);
+ vector_array.extend(0, virtual_array_span[0]);
+ return vector_array;
+ }
+
+ if (from_any_value->type == ValueType::OwnVector) {
+ OwnVectorValue *value = (OwnVectorValue *)from_any_value;
+ if (value->max_remaining_users == 1) {
+ m_value_per_output_id[to.id()] = value;
+ m_value_per_output_id[from.id()] = nullptr;
+ value->max_remaining_users = to.targets().size();
+ return *value->vector_array;
+ }
+ }
+
+ GVArraySpan virtual_array_span = this->get_vector_input__single(input);
+
+ GVectorArray *new_vector_array = new GVectorArray(base_type, 1);
+ new_vector_array->extend(0, virtual_array_span[0]);
+
+ OwnVectorValue *new_value = m_allocator.construct<OwnVectorValue>(*new_vector_array,
+ to.targets().size());
+ m_value_per_output_id[to.id()] = new_value;
+ return *new_vector_array;
+}
+
+GVSpan MFNetworkEvaluationStorage::get_single_input__full(const MFInputSocket &socket)
+{
+ const MFOutputSocket &origin = *socket.origin();
+ Value *any_value = m_value_per_output_id[origin.id()];
+ BLI_assert(any_value != nullptr);
+
+ if (any_value->type == ValueType::OwnSingle) {
+ OwnSingleValue *value = (OwnSingleValue *)any_value;
+ if (value->is_single_allocated) {
+ return GVSpan::FromSingle(value->span.type(), value->span.buffer(), m_min_array_size);
+ }
+ else {
+ return value->span;
+ }
+ }
+ else if (any_value->type == ValueType::InputSingle) {
+ InputSingleValue *value = (InputSingleValue *)any_value;
+ return value->virtual_span;
+ }
+ else if (any_value->type == ValueType::OutputSingle) {
+ OutputSingleValue *value = (OutputSingleValue *)any_value;
+ BLI_assert(value->is_computed);
+ return value->span;
+ }
+
+ BLI_assert(false);
+ return GVSpan(CPPType::get<float>());
+}
+
+GVSpan MFNetworkEvaluationStorage::get_single_input__single(const MFInputSocket &socket)
+{
+ const MFOutputSocket &origin = *socket.origin();
+ Value *any_value = m_value_per_output_id[origin.id()];
+ BLI_assert(any_value != nullptr);
+
+ if (any_value->type == ValueType::OwnSingle) {
+ OwnSingleValue *value = (OwnSingleValue *)any_value;
+ BLI_assert(value->span.size() == 1);
+ return value->span;
+ }
+ else if (any_value->type == ValueType::InputSingle) {
+ InputSingleValue *value = (InputSingleValue *)any_value;
+ BLI_assert(value->virtual_span.is_single_element());
+ return value->virtual_span;
+ }
+ else if (any_value->type == ValueType::OutputSingle) {
+ OutputSingleValue *value = (OutputSingleValue *)any_value;
+ BLI_assert(value->is_computed);
+ BLI_assert(value->span.size() == 1);
+ return value->span;
+ }
+
+ BLI_assert(false);
+ return GVSpan(CPPType::get<float>());
+}
+
+GVArraySpan MFNetworkEvaluationStorage::get_vector_input__full(const MFInputSocket &socket)
+{
+ const MFOutputSocket &origin = *socket.origin();
+ Value *any_value = m_value_per_output_id[origin.id()];
+ BLI_assert(any_value != nullptr);
+
+ if (any_value->type == ValueType::OwnVector) {
+ OwnVectorValue *value = (OwnVectorValue *)any_value;
+ if (value->vector_array->size() == 1) {
+ GSpan span = (*value->vector_array)[0];
+ return GVArraySpan(span, m_min_array_size);
+ }
+ else {
+ return *value->vector_array;
+ }
+ }
+ else if (any_value->type == ValueType::InputVector) {
+ InputVectorValue *value = (InputVectorValue *)any_value;
+ return value->virtual_array_span;
+ }
+ else if (any_value->type == ValueType::OutputVector) {
+ OutputVectorValue *value = (OutputVectorValue *)any_value;
+ return *value->vector_array;
+ }
+
+ BLI_assert(false);
+ return GVArraySpan(CPPType::get<float>());
+}
+
+GVArraySpan MFNetworkEvaluationStorage::get_vector_input__single(const MFInputSocket &socket)
+{
+ const MFOutputSocket &origin = *socket.origin();
+ Value *any_value = m_value_per_output_id[origin.id()];
+ BLI_assert(any_value != nullptr);
+
+ if (any_value->type == ValueType::OwnVector) {
+ OwnVectorValue *value = (OwnVectorValue *)any_value;
+ BLI_assert(value->vector_array->size() == 1);
+ return *value->vector_array;
+ }
+ else if (any_value->type == ValueType::InputVector) {
+ InputVectorValue *value = (InputVectorValue *)any_value;
+ BLI_assert(value->virtual_array_span.is_single_array());
+ return value->virtual_array_span;
+ }
+ else if (any_value->type == ValueType::OutputVector) {
+ OutputVectorValue *value = (OutputVectorValue *)any_value;
+ BLI_assert(value->vector_array->size() == 1);
+ return *value->vector_array;
+ }
+
+ BLI_assert(false);
+ return GVArraySpan(CPPType::get<float>());
+}
+
+/** \} */
+
+} // namespace fn
+} // namespace blender
diff --git a/source/blender/gpencil_modifiers/CMakeLists.txt b/source/blender/gpencil_modifiers/CMakeLists.txt
index d3c85b891c6..92aacc74190 100644
--- a/source/blender/gpencil_modifiers/CMakeLists.txt
+++ b/source/blender/gpencil_modifiers/CMakeLists.txt
@@ -25,11 +25,14 @@ set(INC
../blenfont
../blenkernel
../blenlib
+ ../blentranslation
../bmesh
../depsgraph
+ ../editors/include
../makesdna
../makesrna
../render/extern/include
+ ../windowmanager
../../../intern/eigen
../../../intern/guardedalloc
)
@@ -40,6 +43,7 @@ set(INC_SYS
set(SRC
intern/MOD_gpencil_util.h
+ intern/MOD_gpencil_ui_common.c
intern/MOD_gpencil_util.c
intern/MOD_gpencilarmature.c
@@ -61,6 +65,7 @@ set(SRC
intern/MOD_gpenciltime.c
intern/MOD_gpenciltint.c
+ intern/MOD_gpencil_ui_common.h
MOD_gpencil_modifiertypes.h
)
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c b/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c
new file mode 100644
index 00000000000..c15bef1f748
--- /dev/null
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c
@@ -0,0 +1,458 @@
+/* This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup modifiers
+ */
+
+#include <string.h>
+
+#include "BLI_listbase.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BKE_context.h"
+#include "BKE_gpencil_modifier.h"
+#include "BKE_material.h"
+#include "BKE_object.h"
+#include "BKE_screen.h"
+
+#include "DNA_object_force_types.h"
+#include "DNA_object_types.h"
+#include "DNA_particle_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_space_types.h"
+
+#include "ED_object.h"
+
+#include "BLT_translation.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "RNA_access.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "MOD_gpencil_ui_common.h" /* Self include */
+
+static Object *get_gpencilmodifier_object(const bContext *C)
+{
+ SpaceProperties *sbuts = CTX_wm_space_properties(C);
+ if (sbuts != NULL && (sbuts->pinid != NULL) && GS(sbuts->pinid->name) == ID_OB) {
+ return (Object *)sbuts->pinid;
+ }
+ else {
+ return CTX_data_active_object(C);
+ }
+}
+
+/**
+ * Poll function so these modifier panels only show for grease pencil objects.
+ */
+static bool gpencil_modifier_ui_poll(const bContext *C, PanelType *UNUSED(pt))
+{
+ Object *ob = get_gpencilmodifier_object(C);
+
+ return (ob != NULL) && (ob->type == OB_GPENCIL);
+}
+
+/* -------------------------------------------------------------------- */
+/** \name Panel Drag and Drop, Expansion Saving
+ * \{ */
+
+/**
+ * Move a modifier to the index it's moved to after a drag and drop.
+ */
+static void gpencil_modifier_reorder(bContext *C, Panel *panel, int new_index)
+{
+ Object *ob = get_gpencilmodifier_object(C);
+
+ GpencilModifierData *md = BLI_findlink(&ob->greasepencil_modifiers, panel->runtime.list_index);
+ PointerRNA props_ptr;
+ wmOperatorType *ot = WM_operatortype_find("OBJECT_OT_gpencil_modifier_move_to_index", false);
+ WM_operator_properties_create_ptr(&props_ptr, ot);
+ RNA_string_set(&props_ptr, "modifier", md->name);
+ RNA_int_set(&props_ptr, "index", new_index);
+ WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr);
+ WM_operator_properties_free(&props_ptr);
+}
+
+static short get_gpencil_modifier_expand_flag(const bContext *C, Panel *panel)
+{
+ Object *ob = get_gpencilmodifier_object(C);
+ GpencilModifierData *md = BLI_findlink(&ob->greasepencil_modifiers, panel->runtime.list_index);
+ return md->ui_expand_flag;
+ return 0;
+}
+
+static void set_gpencil_modifier_expand_flag(const bContext *C, Panel *panel, short expand_flag)
+{
+ Object *ob = get_gpencilmodifier_object(C);
+ GpencilModifierData *md = BLI_findlink(&ob->greasepencil_modifiers, panel->runtime.list_index);
+ md->ui_expand_flag = expand_flag;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Modifier Panel Layouts
+ * \{ */
+
+void gpencil_modifier_masking_panel_draw(const bContext *C,
+ Panel *panel,
+ bool use_material,
+ bool use_vertex)
+{
+ uiLayout *row, *col, *sub;
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ PointerRNA ob_ptr;
+ gpencil_modifier_panel_get_property_pointers(C, panel, &ob_ptr, &ptr);
+
+ PointerRNA obj_data_ptr = RNA_pointer_get(&ob_ptr, "data");
+ bool has_layer = RNA_string_length(&ptr, "layer") != 0;
+
+ uiLayoutSetPropSep(layout, true);
+
+ col = uiLayoutColumn(layout, true);
+ row = uiLayoutRow(col, true);
+ uiItemPointerR(row, &ptr, "layer", &obj_data_ptr, "layers", NULL, ICON_GREASEPENCIL);
+ sub = uiLayoutRow(row, true);
+ uiLayoutSetActive(sub, has_layer);
+ uiLayoutSetPropDecorate(sub, false);
+ uiItemR(sub, &ptr, "invert_layers", 0, "", ICON_ARROW_LEFTRIGHT);
+
+ row = uiLayoutRow(col, true);
+ uiItemR(row, &ptr, "layer_pass", 0, NULL, ICON_NONE);
+ sub = uiLayoutRow(row, true);
+ uiLayoutSetActive(sub, RNA_int_get(&ptr, "layer_pass") != 0);
+ uiLayoutSetPropDecorate(sub, false);
+ uiItemR(sub, &ptr, "invert_layer_pass", 0, "", ICON_ARROW_LEFTRIGHT);
+
+ if (use_material) {
+ PointerRNA material_ptr = RNA_pointer_get(&ptr, "material");
+ bool has_material = !RNA_pointer_is_null(&material_ptr);
+
+ /* Because the Gpencil modifier material property used to be a string in an earlier version of
+ * Blender, we need to check if the material is valid and display it differently if so. */
+ bool valid = false;
+ {
+ if (!has_material) {
+ valid = true;
+ }
+ else {
+ Material *current_material = material_ptr.data;
+ Object *ob = ob_ptr.data;
+ for (int i = 0; i <= ob->totcol; i++) {
+ Material *mat = BKE_object_material_get(ob, i);
+ if (mat == current_material) {
+ valid = true;
+ break;
+ }
+ }
+ }
+ }
+
+ col = uiLayoutColumn(layout, true);
+ row = uiLayoutRow(col, true);
+ uiLayoutSetRedAlert(row, !valid);
+ uiItemPointerR(row,
+ &ptr,
+ "material",
+ &obj_data_ptr,
+ "materials",
+ NULL,
+ valid ? ICON_SHADING_TEXTURE : ICON_ERROR);
+ sub = uiLayoutRow(row, true);
+ uiLayoutSetActive(sub, has_material);
+ uiLayoutSetPropDecorate(sub, false);
+ uiItemR(sub, &ptr, "invert_materials", 0, "", ICON_ARROW_LEFTRIGHT);
+
+ row = uiLayoutRow(col, true);
+ uiItemR(row, &ptr, "pass_index", 0, NULL, ICON_NONE);
+ sub = uiLayoutRow(row, true);
+ uiLayoutSetActive(sub, RNA_int_get(&ptr, "pass_index") != 0);
+ uiLayoutSetPropDecorate(sub, false);
+ uiItemR(sub, &ptr, "invert_material_pass", 0, "", ICON_ARROW_LEFTRIGHT);
+ }
+
+ if (use_vertex) {
+ bool has_vertex_group = RNA_string_length(&ptr, "vertex_group") != 0;
+
+ row = uiLayoutRow(layout, true);
+ uiItemPointerR(row, &ptr, "vertex_group", &ob_ptr, "vertex_groups", NULL, ICON_NONE);
+ sub = uiLayoutRow(row, true);
+ uiLayoutSetActive(sub, has_vertex_group);
+ uiLayoutSetPropDecorate(sub, false);
+ uiItemR(sub, &ptr, "invert_vertex", 0, "", ICON_ARROW_LEFTRIGHT);
+ }
+}
+
+void gpencil_modifier_curve_header_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ gpencil_modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ uiItemR(layout, &ptr, "use_custom_curve", 0, NULL, ICON_NONE);
+}
+
+void gpencil_modifier_curve_panel_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ gpencil_modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ uiTemplateCurveMapping(layout, &ptr, "curve", 0, false, false, false, false);
+}
+
+/**
+ * Draw modifier error message.
+ */
+void gpencil_modifier_panel_end(uiLayout *layout, PointerRNA *ptr)
+{
+ GpencilModifierData *md = ptr->data;
+ if (md->error) {
+ uiLayout *row = uiLayoutRow(layout, false);
+ uiItemL(row, IFACE_(md->error), ICON_ERROR);
+ }
+}
+
+/**
+ * Gets RNA pointers for the active object and the panel's modifier data.
+ */
+#define ERROR_LIBDATA_MESSAGE TIP_("External library data")
+void gpencil_modifier_panel_get_property_pointers(const bContext *C,
+ Panel *panel,
+ PointerRNA *r_ob_ptr,
+ PointerRNA *r_md_ptr)
+{
+ Object *ob = get_gpencilmodifier_object(C);
+ GpencilModifierData *md = BLI_findlink(&ob->greasepencil_modifiers, panel->runtime.list_index);
+
+ RNA_pointer_create(&ob->id, &RNA_GpencilModifier, md, r_md_ptr);
+
+ if (r_ob_ptr != NULL) {
+ RNA_pointer_create(&ob->id, &RNA_Object, ob, r_ob_ptr);
+ }
+
+ uiBlock *block = uiLayoutGetBlock(panel->layout);
+ UI_block_lock_clear(block);
+ UI_block_lock_set(block, ob && ID_IS_LINKED(ob), ERROR_LIBDATA_MESSAGE);
+
+ uiLayoutSetContextPointer(panel->layout, "modifier", r_md_ptr);
+}
+
+static void gpencil_modifier_ops_extra_draw(bContext *C, uiLayout *layout, void *md_v)
+{
+ PointerRNA op_ptr;
+ uiLayout *row;
+ GpencilModifierData *md = (GpencilModifierData *)md_v;
+ const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(md->type);
+
+ PointerRNA ptr;
+ Object *ob = get_gpencilmodifier_object(C);
+ RNA_pointer_create(&ob->id, &RNA_GpencilModifier, md, &ptr);
+ uiLayoutSetContextPointer(layout, "modifier", &ptr);
+ uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT);
+
+ uiLayoutSetUnitsX(layout, 4.0f);
+
+ /* Apply. */
+ if (!(mti->flags & eGpencilModifierTypeFlag_NoApply)) {
+ uiItemO(layout,
+ CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Apply"),
+ ICON_CHECKMARK,
+ "OBJECT_OT_gpencil_modifier_apply");
+ }
+
+ /* Duplicate. */
+ uiItemO(layout,
+ CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Duplicate"),
+ ICON_DUPLICATE,
+ "OBJECT_OT_gpencil_modifier_copy");
+
+ uiItemS(layout);
+
+ /* Move to first. */
+ row = uiLayoutColumn(layout, false);
+ uiItemFullO(row,
+ "OBJECT_OT_gpencil_modifier_move_to_index",
+ IFACE_("Move to First"),
+ ICON_TRIA_UP,
+ NULL,
+ WM_OP_INVOKE_DEFAULT,
+ 0,
+ &op_ptr);
+ RNA_int_set(&op_ptr, "index", 0);
+ if (!md->prev) {
+ uiLayoutSetEnabled(row, false);
+ }
+
+ /* Move to last. */
+ row = uiLayoutColumn(layout, false);
+ uiItemFullO(row,
+ "OBJECT_OT_gpencil_modifier_move_to_index",
+ IFACE_("Move to Last"),
+ ICON_TRIA_DOWN,
+ NULL,
+ WM_OP_INVOKE_DEFAULT,
+ 0,
+ &op_ptr);
+ RNA_int_set(&op_ptr, "index", BLI_listbase_count(&ob->greasepencil_modifiers) - 1);
+ if (!md->next) {
+ uiLayoutSetEnabled(row, false);
+ }
+}
+
+static void gpencil_modifier_panel_header(const bContext *C, Panel *panel)
+{
+ uiLayout *row, *sub;
+ uiLayout *layout = panel->layout;
+
+ Object *ob = get_gpencilmodifier_object(C);
+ GpencilModifierData *md = BLI_findlink(&ob->greasepencil_modifiers, panel->runtime.list_index);
+ PointerRNA ptr;
+ RNA_pointer_create(&ob->id, &RNA_GpencilModifier, md, &ptr);
+ uiLayoutSetContextPointer(panel->layout, "modifier", &ptr);
+
+ const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(md->type);
+ bool narrow_panel = (panel->sizex < UI_UNIT_X * 9 && panel->sizex != 0);
+
+ /* Modifier Icon. */
+ row = uiLayoutRow(layout, false);
+ if (mti->isDisabled && mti->isDisabled(md, 0)) {
+ uiLayoutSetRedAlert(row, true);
+ }
+ uiItemL(row, "", RNA_struct_ui_icon(ptr.type));
+
+ /* Modifier name. */
+ row = uiLayoutRow(layout, true);
+ if (!narrow_panel) {
+ uiItemR(row, &ptr, "name", 0, "", ICON_NONE);
+ }
+ else {
+ uiLayoutSetAlignment(row, UI_LAYOUT_ALIGN_RIGHT);
+ }
+
+ /* Display mode buttons. */
+ if (mti->flags & eGpencilModifierTypeFlag_SupportsEditmode) {
+ sub = uiLayoutRow(row, true);
+ uiItemR(sub, &ptr, "show_in_editmode", 0, "", ICON_NONE);
+ }
+ uiItemR(row, &ptr, "show_viewport", 0, "", ICON_NONE);
+ uiItemR(row, &ptr, "show_render", 0, "", ICON_NONE);
+
+ /* Extra operators. */
+ // row = uiLayoutRow(layout, true);
+ uiItemMenuF(row, "", ICON_DOWNARROW_HLT, gpencil_modifier_ops_extra_draw, md);
+
+ /* Remove button. */
+ sub = uiLayoutRow(row, true);
+ uiLayoutSetEmboss(sub, UI_EMBOSS_NONE);
+ uiItemO(sub, "", ICON_X, "OBJECT_OT_gpencil_modifier_remove");
+
+ /* Extra padding. */
+ uiItemS(layout);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Modifier Registration Helpers
+ * \{ */
+
+/**
+ * Create a panel in the context's region
+ */
+PanelType *gpencil_modifier_panel_register(ARegionType *region_type,
+ GpencilModifierType type,
+ PanelDrawFn draw)
+{
+
+ /* Get the name for the modifier's panel. */
+ char panel_idname[BKE_ST_MAXNAME];
+ BKE_gpencil_modifierType_panel_id(type, panel_idname);
+
+ PanelType *panel_type = MEM_callocN(sizeof(PanelType), panel_idname);
+
+ strcpy(panel_type->idname, panel_idname);
+ strcpy(panel_type->label, "");
+ strcpy(panel_type->context, "modifier");
+ strcpy(panel_type->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA);
+
+ panel_type->draw_header = gpencil_modifier_panel_header;
+ panel_type->draw = draw;
+ panel_type->poll = gpencil_modifier_ui_poll;
+
+ /* Give the panel the special flag that says it was built here and corresponds to a
+ * modifier rather than a #PanelType. */
+ panel_type->flag = PNL_LAYOUT_HEADER_EXPAND | PNL_DRAW_BOX | PNL_INSTANCED;
+ panel_type->reorder = gpencil_modifier_reorder;
+ panel_type->get_list_data_expand_flag = get_gpencil_modifier_expand_flag;
+ panel_type->set_list_data_expand_flag = set_gpencil_modifier_expand_flag;
+
+ BLI_addtail(&region_type->paneltypes, panel_type);
+
+ return panel_type;
+}
+
+/**
+ * Add a child panel to the parent.
+ *
+ * \note To create the panel type's idname, it appends the \a name argument to the \a parent's
+ * idname.
+ */
+PanelType *gpencil_modifier_subpanel_register(ARegionType *region_type,
+ const char *name,
+ const char *label,
+ PanelDrawFn draw_header,
+ PanelDrawFn draw,
+ PanelType *parent)
+{
+ /* Create the subpanel's ID name. */
+ char panel_idname[BKE_ST_MAXNAME];
+ strcpy(panel_idname, parent->idname);
+ strcat(panel_idname, "_");
+ strcat(panel_idname, name);
+
+ PanelType *panel_type = MEM_callocN(sizeof(PanelType), panel_idname);
+
+ strcpy(panel_type->idname, panel_idname);
+ strcpy(panel_type->label, label);
+ strcpy(panel_type->context, "modifier");
+ strcpy(panel_type->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA);
+
+ panel_type->draw_header = draw_header;
+ panel_type->draw = draw;
+ panel_type->poll = gpencil_modifier_ui_poll;
+ panel_type->flag = (PNL_DEFAULT_CLOSED | PNL_DRAW_BOX);
+
+ BLI_assert(parent != NULL);
+ strcpy(panel_type->parent_id, parent->idname);
+ panel_type->parent = parent;
+ BLI_addtail(&parent->children, BLI_genericNodeN(panel_type));
+ BLI_addtail(&region_type->paneltypes, panel_type);
+
+ return panel_type;
+}
+
+/** \} */
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.h b/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.h
new file mode 100644
index 00000000000..9c6edb51d63
--- /dev/null
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.h
@@ -0,0 +1,66 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup modifiers
+ */
+
+#ifndef __MOD_UI_COMMON__GPENCIL_H__
+#define __MOD_UI_COMMON__GPENCIL_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "MOD_gpencil_modifiertypes.h"
+
+struct ARegionType;
+struct bContext;
+struct PanelType;
+struct uiLayout;
+typedef void (*PanelDrawFn)(const bContext *, Panel *);
+
+void gpencil_modifier_masking_panel_draw(const bContext *C,
+ Panel *panel,
+ bool use_material,
+ bool use_vertex);
+
+void gpencil_modifier_curve_header_draw(const bContext *C, Panel *panel);
+void gpencil_modifier_curve_panel_draw(const bContext *C, Panel *panel);
+
+void gpencil_modifier_panel_end(struct uiLayout *layout, PointerRNA *ptr);
+
+void gpencil_modifier_panel_get_property_pointers(const bContext *C,
+ struct Panel *panel,
+ struct PointerRNA *r_ob_ptr,
+ struct PointerRNA *r_ptr);
+
+PanelType *gpencil_modifier_panel_register(struct ARegionType *region_type,
+ GpencilModifierType type,
+ PanelDrawFn draw);
+
+struct PanelType *gpencil_modifier_subpanel_register(struct ARegionType *region_type,
+ const char *name,
+ const char *label,
+ PanelDrawFn draw_header,
+ PanelDrawFn draw,
+ struct PanelType *parent);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __MOD_UI_COMMON__GPENCIL_H__ */
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilarmature.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilarmature.c
index 776e5521179..60c3877b89a 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilarmature.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilarmature.c
@@ -28,6 +28,8 @@
#include "BLI_math.h"
+#include "BLT_translation.h"
+
#include "DNA_armature_types.h"
#include "DNA_gpencil_modifier_types.h"
#include "DNA_gpencil_types.h"
@@ -35,8 +37,10 @@
#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
#include "BKE_armature.h"
+#include "BKE_context.h"
#include "BKE_gpencil.h"
#include "BKE_gpencil_geom.h"
#include "BKE_gpencil_modifier.h"
@@ -44,10 +48,17 @@
#include "BKE_main.h"
#include "BKE_modifier.h"
#include "BKE_scene.h"
+#include "BKE_screen.h"
#include "MEM_guardedalloc.h"
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "RNA_access.h"
+
#include "MOD_gpencil_modifiertypes.h"
+#include "MOD_gpencil_ui_common.h"
#include "MOD_gpencil_util.h"
#include "DEG_depsgraph.h"
@@ -181,6 +192,39 @@ static void foreachObjectLink(GpencilModifierData *md,
walk(userData, ob, &mmd->object, IDWALK_CB_NOP);
}
+static void panel_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *sub, *row, *col;
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ PointerRNA ob_ptr;
+ gpencil_modifier_panel_get_property_pointers(C, panel, &ob_ptr, &ptr);
+
+ bool has_vertex_group = RNA_string_length(&ptr, "vertex_group") != 0;
+
+ uiLayoutSetPropSep(layout, true);
+
+ uiItemR(layout, &ptr, "object", 0, NULL, ICON_NONE);
+ row = uiLayoutRow(layout, true);
+ uiItemPointerR(row, &ptr, "vertex_group", &ob_ptr, "vertex_groups", NULL, ICON_NONE);
+ sub = uiLayoutRow(row, true);
+ uiLayoutSetActive(sub, has_vertex_group);
+ uiLayoutSetPropDecorate(sub, false);
+ uiItemR(sub, &ptr, "invert_vertex_group", 0, "", ICON_ARROW_LEFTRIGHT);
+
+ col = uiLayoutColumnWithHeading(layout, true, IFACE_("Bind to"));
+ uiItemR(col, &ptr, "use_vertex_groups", 0, IFACE_("Vertex Groups"), ICON_NONE);
+ uiItemR(col, &ptr, "use_bone_envelopes", 0, IFACE_("Bone Envelopes"), ICON_NONE);
+
+ gpencil_modifier_panel_end(layout, &ptr);
+}
+
+static void panelRegister(ARegionType *region_type)
+{
+ gpencil_modifier_panel_register(region_type, eGpencilModifierType_Armature, panel_draw);
+}
+
GpencilModifierTypeInfo modifierType_Gpencil_Armature = {
/* name */ "Armature",
/* structName */ "ArmatureGpencilModifierData",
@@ -202,4 +246,5 @@ GpencilModifierTypeInfo modifierType_Gpencil_Armature = {
/* foreachObjectLink */ foreachObjectLink,
/* foreachIDLink */ NULL,
/* foreachTexLink */ NULL,
+ /* panelRegister */ panelRegister,
};
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c
index da4c1f71f44..d92721f887f 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c
@@ -35,12 +35,16 @@
#include "BLI_math.h"
#include "BLI_rand.h"
+#include "BLT_translation.h"
+
#include "DNA_gpencil_modifier_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
#include "BKE_collection.h"
+#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_gpencil.h"
#include "BKE_gpencil_geom.h"
@@ -51,12 +55,19 @@
#include "BKE_modifier.h"
#include "BKE_object.h"
#include "BKE_scene.h"
+#include "BKE_screen.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "RNA_access.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_build.h"
#include "DEG_depsgraph_query.h"
#include "MOD_gpencil_modifiertypes.h"
+#include "MOD_gpencil_ui_common.h"
#include "MOD_gpencil_util.h"
typedef struct tmpStrokes {
@@ -78,6 +89,9 @@ static void initData(GpencilModifierData *md)
gpmd->flag |= GP_ARRAY_USE_RELATIVE;
gpmd->seed = 1;
gpmd->material = NULL;
+
+ /* Open the first subpanel too, because it's activated by default. */
+ md->ui_expand_flag = (1 << 0) | (1 << 1);
}
static void copyData(const GpencilModifierData *md, GpencilModifierData *target)
@@ -330,6 +344,143 @@ static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk,
foreachObjectLink(md, ob, (ObjectWalkFunc)walk, userData);
}
+static void panel_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ gpencil_modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ uiLayoutSetPropSep(layout, true);
+
+ uiItemR(layout, &ptr, "count", 0, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "replace_material", 0, IFACE_("Material Override"), ICON_NONE);
+
+ gpencil_modifier_panel_end(layout, &ptr);
+}
+
+static void relative_offset_header_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ gpencil_modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ uiItemR(layout, &ptr, "use_relative_offset", 0, IFACE_("Relative Offset"), ICON_NONE);
+}
+
+static void relative_offset_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ gpencil_modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ uiLayoutSetPropSep(layout, true);
+
+ uiLayout *col = uiLayoutColumn(layout, false);
+
+ uiLayoutSetActive(col, RNA_boolean_get(&ptr, "use_relative_offset"));
+ uiItemR(col, &ptr, "relative_offset", 0, IFACE_("Factor"), ICON_NONE);
+}
+
+static void constant_offset_header_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ gpencil_modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ uiItemR(layout, &ptr, "use_constant_offset", 0, IFACE_("Constant Offset"), ICON_NONE);
+}
+
+static void constant_offset_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ gpencil_modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ uiLayoutSetPropSep(layout, true);
+
+ uiLayout *col = uiLayoutColumn(layout, false);
+
+ uiLayoutSetActive(col, RNA_boolean_get(&ptr, "use_constant_offset"));
+ uiItemR(col, &ptr, "constant_offset", 0, IFACE_("Distance"), ICON_NONE);
+}
+
+/**
+ * Object offset in a subpanel for consistency with the other offset types.
+ */
+static void object_offset_header_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ gpencil_modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ uiItemR(layout, &ptr, "use_object_offset", 0, NULL, ICON_NONE);
+}
+
+static void object_offset_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ gpencil_modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ uiLayoutSetPropSep(layout, true);
+
+ uiLayout *col = uiLayoutColumn(layout, false);
+
+ uiLayoutSetActive(col, RNA_boolean_get(&ptr, "use_object_offset"));
+ uiItemR(col, &ptr, "offset_object", 0, NULL, ICON_NONE);
+}
+
+static void random_panel_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ gpencil_modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ uiLayoutSetPropSep(layout, true);
+
+ uiItemR(layout, &ptr, "random_offset", 0, IFACE_("Offset"), ICON_NONE);
+ uiItemR(layout, &ptr, "random_rotation", 0, IFACE_("Rotation"), ICON_NONE);
+ uiItemR(layout, &ptr, "random_scale", 0, IFACE_("Scale"), ICON_NONE);
+ uiItemR(layout, &ptr, "seed", 0, NULL, ICON_NONE);
+}
+
+static void mask_panel_draw(const bContext *C, Panel *panel)
+{
+ gpencil_modifier_masking_panel_draw(C, panel, true, false);
+}
+
+static void panelRegister(ARegionType *region_type)
+{
+ PanelType *panel_type = gpencil_modifier_panel_register(
+ region_type, eGpencilModifierType_Array, panel_draw);
+ gpencil_modifier_subpanel_register(region_type,
+ "relative_offset",
+ "",
+ relative_offset_header_draw,
+ relative_offset_draw,
+ panel_type);
+ gpencil_modifier_subpanel_register(region_type,
+ "constant_offset",
+ "",
+ constant_offset_header_draw,
+ constant_offset_draw,
+ panel_type);
+ gpencil_modifier_subpanel_register(
+ region_type, "object_offset", "", object_offset_header_draw, object_offset_draw, panel_type);
+ gpencil_modifier_subpanel_register(
+ region_type, "randomize", "Randomize", NULL, random_panel_draw, panel_type);
+ gpencil_modifier_subpanel_register(
+ region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type);
+}
+
GpencilModifierTypeInfo modifierType_Gpencil_Array = {
/* name */ "Array",
/* structName */ "ArrayGpencilModifierData",
@@ -352,4 +503,5 @@ GpencilModifierTypeInfo modifierType_Gpencil_Array = {
/* foreachObjectLink */ foreachObjectLink,
/* foreachIDLink */ foreachIDLink,
/* foreachTexLink */ NULL,
+ /* panelRegister */ panelRegister,
};
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c
index 71a051629d8..54ed2ffafe1 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c
@@ -30,20 +30,31 @@
#include "BLI_blenlib.h"
#include "BLI_math.h"
+#include "BLT_translation.h"
+
#include "DNA_gpencil_modifier_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
+#include "BKE_context.h"
#include "BKE_gpencil.h"
#include "BKE_gpencil_geom.h"
#include "BKE_gpencil_modifier.h"
+#include "BKE_screen.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "RNA_access.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
#include "MOD_gpencil_modifiertypes.h"
+#include "MOD_gpencil_ui_common.h"
#include "MOD_gpencil_util.h"
static void initData(GpencilModifierData *md)
@@ -537,6 +548,88 @@ static void generateStrokes(GpencilModifierData *md, Depsgraph *depsgraph, Objec
}
}
+static void panel_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *row, *sub;
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ PointerRNA ob_ptr;
+ gpencil_modifier_panel_get_property_pointers(C, panel, &ob_ptr, &ptr);
+
+ int mode = RNA_enum_get(&ptr, "mode");
+
+ uiLayoutSetPropSep(layout, true);
+
+ uiItemR(layout, &ptr, "mode", 0, NULL, ICON_NONE);
+ if (mode == GP_BUILD_MODE_CONCURRENT) {
+ uiItemR(layout, &ptr, "concurrent_time_alignment", 0, NULL, ICON_NONE);
+ }
+
+ uiItemS(layout);
+
+ uiItemR(layout, &ptr, "transition", 0, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "start_delay", 0, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "length", 0, IFACE_("Frames"), ICON_NONE);
+
+ uiItemS(layout);
+
+ row = uiLayoutRowWithHeading(layout, true, IFACE_("Use Factor"));
+ uiItemR(row, &ptr, "use_percentage", 0, "", ICON_NONE);
+ sub = uiLayoutRow(row, true);
+ uiLayoutSetActive(sub, RNA_boolean_get(&ptr, "use_percentage"));
+ uiItemR(sub, &ptr, "percentage_factor", 0, "", ICON_NONE);
+
+ /* Check for incompatible time modifier. */
+ Object *ob = ob_ptr.data;
+ GpencilModifierData *md = ptr.data;
+ if (BKE_gpencil_modifiers_findby_type(ob, eGpencilModifierType_Time) != NULL) {
+ BKE_gpencil_modifier_set_error(md, "Build and Time Offset modifiers are incompatible");
+ }
+
+ gpencil_modifier_panel_end(layout, &ptr);
+}
+
+static void frame_range_header_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ gpencil_modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ uiItemR(layout, &ptr, "use_restrict_frame_range", 0, IFACE_("Custom Range"), ICON_NONE);
+}
+
+static void frame_range_panel_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *col;
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ gpencil_modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ uiLayoutSetPropSep(layout, true);
+
+ col = uiLayoutColumn(layout, false);
+ uiItemR(col, &ptr, "frame_start", 0, IFACE_("Start"), ICON_NONE);
+ uiItemR(col, &ptr, "frame_end", 0, IFACE_("End"), ICON_NONE);
+}
+
+static void mask_panel_draw(const bContext *C, Panel *panel)
+{
+ gpencil_modifier_masking_panel_draw(C, panel, false, false);
+}
+
+static void panelRegister(ARegionType *region_type)
+{
+ PanelType *panel_type = gpencil_modifier_panel_register(
+ region_type, eGpencilModifierType_Build, panel_draw);
+ gpencil_modifier_subpanel_register(
+ region_type, "frame_range", "", frame_range_header_draw, frame_range_panel_draw, panel_type);
+ gpencil_modifier_subpanel_register(
+ region_type, "_mask", "Influence", NULL, mask_panel_draw, panel_type);
+}
+
/* ******************************************** */
GpencilModifierTypeInfo modifierType_Gpencil_Build = {
@@ -561,4 +654,5 @@ GpencilModifierTypeInfo modifierType_Gpencil_Build = {
/* foreachObjectLink */ NULL,
/* foreachIDLink */ NULL,
/* foreachTexLink */ NULL,
+ /* panelRegister */ panelRegister,
};
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c
index 14125d5c8d4..03137a5cf23 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c
@@ -29,22 +29,33 @@
#include "BLI_math_color.h"
#include "BLI_math_vector.h"
+#include "BLT_translation.h"
+
#include "DNA_gpencil_modifier_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
#include "BKE_colortools.h"
+#include "BKE_context.h"
#include "BKE_gpencil.h"
#include "BKE_gpencil_modifier.h"
#include "BKE_lib_query.h"
#include "BKE_main.h"
#include "BKE_material.h"
+#include "BKE_screen.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
#include "BKE_modifier.h"
+#include "RNA_access.h"
#include "DEG_depsgraph.h"
#include "MOD_gpencil_modifiertypes.h"
+#include "MOD_gpencil_ui_common.h"
#include "MOD_gpencil_util.h"
static void initData(GpencilModifierData *md)
@@ -186,6 +197,42 @@ static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk,
walk(userData, ob, (ID **)&mmd->material, IDWALK_CB_USER);
}
+static void panel_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ gpencil_modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ uiLayoutSetPropSep(layout, true);
+
+ uiItemR(layout, &ptr, "modify_color", 0, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "hue", UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "saturation", UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "value", UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+
+ gpencil_modifier_panel_end(layout, &ptr);
+}
+
+static void mask_panel_draw(const bContext *C, Panel *panel)
+{
+ gpencil_modifier_masking_panel_draw(C, panel, true, false);
+}
+
+static void panelRegister(ARegionType *region_type)
+{
+ PanelType *panel_type = gpencil_modifier_panel_register(
+ region_type, eGpencilModifierType_Color, panel_draw);
+ PanelType *mask_panel_type = gpencil_modifier_subpanel_register(
+ region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type);
+ gpencil_modifier_subpanel_register(region_type,
+ "curve",
+ "",
+ gpencil_modifier_curve_header_draw,
+ gpencil_modifier_curve_panel_draw,
+ mask_panel_type);
+}
+
GpencilModifierTypeInfo modifierType_Gpencil_Color = {
/* name */ "Hue/Saturation",
/* structName */ "ColorGpencilModifierData",
@@ -208,4 +255,5 @@ GpencilModifierTypeInfo modifierType_Gpencil_Color = {
/* foreachObjectLink */ NULL,
/* foreachIDLink */ foreachIDLink,
/* foreachTexLink */ NULL,
+ /* panelRegister */ panelRegister,
};
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c
index d39c94e06d5..a0987aafcd2 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c
@@ -28,15 +28,19 @@
#include "BLI_math.h"
+#include "BLT_translation.h"
+
#include "DNA_gpencil_modifier_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
#include "BKE_action.h"
#include "BKE_colortools.h"
+#include "BKE_context.h"
#include "BKE_deform.h"
#include "BKE_gpencil_geom.h"
#include "BKE_gpencil_modifier.h"
@@ -45,10 +49,17 @@
#include "BKE_main.h"
#include "BKE_modifier.h"
#include "BKE_scene.h"
+#include "BKE_screen.h"
#include "MEM_guardedalloc.h"
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "RNA_access.h"
+
#include "MOD_gpencil_modifiertypes.h"
+#include "MOD_gpencil_ui_common.h"
#include "MOD_gpencil_util.h"
#include "DEG_depsgraph.h"
@@ -345,6 +356,81 @@ static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk,
foreachObjectLink(md, ob, (ObjectWalkFunc)walk, userData);
}
+static void panel_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *sub, *row, *col;
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ PointerRNA ob_ptr;
+ gpencil_modifier_panel_get_property_pointers(C, panel, &ob_ptr, &ptr);
+
+ PointerRNA hook_object_ptr = RNA_pointer_get(&ptr, "object");
+ bool has_vertex_group = RNA_string_length(&ptr, "vertex_group") != 0;
+
+ uiLayoutSetPropSep(layout, true);
+
+ col = uiLayoutColumn(layout, false);
+ uiItemR(col, &ptr, "object", 0, NULL, ICON_NONE);
+ if (!RNA_pointer_is_null(&hook_object_ptr) &&
+ RNA_enum_get(&hook_object_ptr, "type") == OB_ARMATURE) {
+ PointerRNA hook_object_data_ptr = RNA_pointer_get(&hook_object_ptr, "data");
+ uiItemPointerR(
+ col, &ptr, "subtarget", &hook_object_data_ptr, "bones", IFACE_("Bone"), ICON_NONE);
+ }
+
+ row = uiLayoutRow(layout, true);
+ uiItemPointerR(row, &ptr, "vertex_group", &ob_ptr, "vertex_groups", NULL, ICON_NONE);
+ sub = uiLayoutRow(row, true);
+ uiLayoutSetActive(sub, has_vertex_group);
+ uiLayoutSetPropSep(sub, false);
+ uiItemR(sub, &ptr, "invert_vertex", 0, "", ICON_ARROW_LEFTRIGHT);
+
+ uiItemR(layout, &ptr, "strength", UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+
+ gpencil_modifier_panel_end(layout, &ptr);
+}
+
+static void falloff_panel_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *row;
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ gpencil_modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ bool use_falloff = RNA_enum_get(&ptr, "falloff_type") != eWarp_Falloff_None;
+
+ uiLayoutSetPropSep(layout, true);
+
+ uiItemR(layout, &ptr, "falloff_type", 0, IFACE_("Type"), ICON_NONE);
+
+ row = uiLayoutRow(layout, false);
+ uiLayoutSetActive(row, use_falloff);
+ uiItemR(row, &ptr, "falloff_radius", 0, NULL, ICON_NONE);
+
+ uiItemR(layout, &ptr, "use_falloff_uniform", 0, NULL, ICON_NONE);
+
+ if (RNA_enum_get(&ptr, "falloff_type") == eWarp_Falloff_Curve) {
+ uiTemplateCurveMapping(layout, &ptr, "falloff_curve", 0, false, false, false, false);
+ }
+}
+
+static void mask_panel_draw(const bContext *C, Panel *panel)
+{
+ gpencil_modifier_masking_panel_draw(C, panel, true, false);
+}
+
+static void panelRegister(ARegionType *region_type)
+{
+ PanelType *panel_type = gpencil_modifier_panel_register(
+ region_type, eGpencilModifierType_Hook, panel_draw);
+ gpencil_modifier_subpanel_register(
+ region_type, "falloff", "Falloff", NULL, falloff_panel_draw, panel_type);
+ gpencil_modifier_subpanel_register(
+ region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type);
+}
+
GpencilModifierTypeInfo modifierType_Gpencil_Hook = {
/* name */ "Hook",
/* structName */ "HookGpencilModifierData",
@@ -367,4 +453,5 @@ GpencilModifierTypeInfo modifierType_Gpencil_Hook = {
/* foreachObjectLink */ foreachObjectLink,
/* foreachIDLink */ foreachIDLink,
/* foreachTexLink */ NULL,
+ /* panelRegister */ panelRegister,
};
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c
index 92b35f390f8..0f5fc4d5cf6 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c
@@ -26,12 +26,16 @@
#include "BLI_listbase.h"
#include "BLI_utildefines.h"
+#include "BLT_translation.h"
+
#include "DNA_gpencil_modifier_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
+#include "BKE_context.h"
#include "BKE_deform.h"
#include "BKE_gpencil_geom.h"
#include "BKE_gpencil_modifier.h"
@@ -41,10 +45,17 @@
#include "BKE_main.h"
#include "BKE_modifier.h"
#include "BKE_scene.h"
+#include "BKE_screen.h"
#include "MEM_guardedalloc.h"
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "RNA_access.h"
+
#include "MOD_gpencil_modifiertypes.h"
+#include "MOD_gpencil_ui_common.h"
#include "MOD_gpencil_util.h"
#include "DEG_depsgraph.h"
@@ -208,6 +219,54 @@ static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk,
foreachObjectLink(md, ob, (ObjectWalkFunc)walk, userData);
}
+static void panel_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *sub, *row, *col;
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ PointerRNA ob_ptr;
+ gpencil_modifier_panel_get_property_pointers(C, panel, &ob_ptr, &ptr);
+
+ PointerRNA hook_object_ptr = RNA_pointer_get(&ptr, "object");
+ bool has_vertex_group = RNA_string_length(&ptr, "vertex_group") != 0;
+
+ uiLayoutSetPropSep(layout, true);
+
+ col = uiLayoutColumn(layout, false);
+ uiItemR(col, &ptr, "object", 0, NULL, ICON_NONE);
+ if (!RNA_pointer_is_null(&hook_object_ptr) &&
+ RNA_enum_get(&hook_object_ptr, "type") == OB_ARMATURE) {
+ PointerRNA hook_object_data_ptr = RNA_pointer_get(&hook_object_ptr, "data");
+ uiItemPointerR(
+ col, &ptr, "subtarget", &hook_object_data_ptr, "bones", IFACE_("Bone"), ICON_NONE);
+ }
+
+ row = uiLayoutRow(layout, true);
+ uiItemPointerR(row, &ptr, "vertex_group", &ob_ptr, "vertex_groups", NULL, ICON_NONE);
+ sub = uiLayoutRow(row, true);
+ uiLayoutSetActive(sub, has_vertex_group);
+ uiLayoutSetPropSep(sub, false);
+ uiItemR(sub, &ptr, "invert_vertex", 0, "", ICON_ARROW_LEFTRIGHT);
+
+ uiItemR(layout, &ptr, "strength", UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+
+ gpencil_modifier_panel_end(layout, &ptr);
+}
+
+static void mask_panel_draw(const bContext *C, Panel *panel)
+{
+ gpencil_modifier_masking_panel_draw(C, panel, true, false);
+}
+
+static void panelRegister(ARegionType *region_type)
+{
+ PanelType *panel_type = gpencil_modifier_panel_register(
+ region_type, eGpencilModifierType_Lattice, panel_draw);
+ gpencil_modifier_subpanel_register(
+ region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type);
+}
+
GpencilModifierTypeInfo modifierType_Gpencil_Lattice = {
/* name */ "Lattice",
/* structName */ "LatticeGpencilModifierData",
@@ -230,4 +289,5 @@ GpencilModifierTypeInfo modifierType_Gpencil_Lattice = {
/* foreachObjectLink */ foreachObjectLink,
/* foreachIDLink */ foreachIDLink,
/* foreachTexLink */ NULL,
+ /* panelRegister */ panelRegister,
};
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c
index 10f0dd763b1..581eaa886d8 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c
@@ -28,12 +28,16 @@
#include "BLI_listbase.h"
#include "BLI_math.h"
+#include "BLT_translation.h"
+
#include "DNA_gpencil_modifier_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
+#include "BKE_context.h"
#include "BKE_deform.h"
#include "BKE_gpencil.h"
#include "BKE_gpencil_modifier.h"
@@ -43,10 +47,17 @@
#include "BKE_modifier.h"
#include "BKE_object.h"
#include "BKE_scene.h"
+#include "BKE_screen.h"
#include "MEM_guardedalloc.h"
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "RNA_access.h"
+
#include "MOD_gpencil_modifiertypes.h"
+#include "MOD_gpencil_ui_common.h"
#include "MOD_gpencil_util.h"
#include "DEG_depsgraph.h"
@@ -254,6 +265,40 @@ static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk,
foreachObjectLink(md, ob, (ObjectWalkFunc)walk, userData);
}
+static void panel_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *row;
+ uiLayout *layout = panel->layout;
+ int toggles_flag = UI_ITEM_R_TOGGLE | UI_ITEM_R_FORCE_BLANK_DECORATE;
+
+ PointerRNA ptr;
+ gpencil_modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ uiLayoutSetPropSep(layout, true);
+
+ row = uiLayoutRowWithHeading(layout, true, IFACE_("Axis"));
+ uiItemR(row, &ptr, "x_axis", toggles_flag, NULL, ICON_NONE);
+ uiItemR(row, &ptr, "y_axis", toggles_flag, NULL, ICON_NONE);
+ uiItemR(row, &ptr, "z_axis", toggles_flag, NULL, ICON_NONE);
+
+ uiItemR(layout, &ptr, "object", 0, NULL, ICON_NONE);
+
+ gpencil_modifier_panel_end(layout, &ptr);
+}
+
+static void mask_panel_draw(const bContext *C, Panel *panel)
+{
+ gpencil_modifier_masking_panel_draw(C, panel, true, false);
+}
+
+static void panelRegister(ARegionType *region_type)
+{
+ PanelType *panel_type = gpencil_modifier_panel_register(
+ region_type, eGpencilModifierType_Mirror, panel_draw);
+ gpencil_modifier_subpanel_register(
+ region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type);
+}
+
GpencilModifierTypeInfo modifierType_Gpencil_Mirror = {
/* name */ "Mirror",
/* structName */ "MirrorGpencilModifierData",
@@ -276,4 +321,5 @@ GpencilModifierTypeInfo modifierType_Gpencil_Mirror = {
/* foreachObjectLink */ foreachObjectLink,
/* foreachIDLink */ foreachIDLink,
/* foreachTexLink */ NULL,
+ /* panelRegister */ panelRegister,
};
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilmultiply.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilmultiply.c
index 22e46626a13..619c37015e4 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilmultiply.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilmultiply.c
@@ -29,6 +29,7 @@
#include "DNA_gpencil_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
#include "BLI_alloca.h"
#include "BLI_blenlib.h"
@@ -37,6 +38,8 @@
#include "BLI_rand.h"
#include "BLI_utildefines.h"
+#include "BLT_translation.h"
+
#include "BKE_collection.h"
#include "BKE_context.h"
#include "BKE_global.h"
@@ -51,6 +54,7 @@
#include "BKE_modifier.h"
#include "BKE_object.h"
#include "BKE_scene.h"
+#include "BKE_screen.h"
#include "bmesh.h"
#include "bmesh_tools.h"
@@ -59,7 +63,13 @@
#include "DEG_depsgraph_build.h"
#include "DEG_depsgraph_query.h"
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "RNA_access.h"
+
#include "MOD_gpencil_modifiertypes.h"
+#include "MOD_gpencil_ui_common.h"
#include "MOD_gpencil_util.h"
static void initData(GpencilModifierData *md)
@@ -305,8 +315,71 @@ static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk,
walk(userData, ob, (ID **)&mmd->material, IDWALK_CB_USER);
}
+static void panel_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *col;
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ gpencil_modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ uiLayoutSetPropSep(layout, true);
+
+ uiItemR(layout, &ptr, "duplicates", 0, NULL, ICON_NONE);
+
+ col = uiLayoutColumn(layout, false);
+ uiLayoutSetActive(layout, RNA_int_get(&ptr, "duplicates") > 0);
+ uiItemR(col, &ptr, "distance", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "offset", UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+
+ gpencil_modifier_panel_end(layout, &ptr);
+}
+
+static void fade_header_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ gpencil_modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ uiItemR(layout, &ptr, "use_fade", 0, NULL, ICON_NONE);
+}
+
+static void fade_panel_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *col;
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ gpencil_modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ uiLayoutSetPropSep(layout, true);
+
+ uiLayoutSetActive(layout, RNA_boolean_get(&ptr, "use_fade"));
+
+ col = uiLayoutColumn(layout, false);
+ uiItemR(col, &ptr, "fading_center", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "fading_thickness", UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "fading_opacity", UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+}
+
+static void mask_panel_draw(const bContext *C, Panel *panel)
+{
+ gpencil_modifier_masking_panel_draw(C, panel, true, false);
+}
+
+static void panelRegister(ARegionType *region_type)
+{
+ PanelType *panel_type = gpencil_modifier_panel_register(
+ region_type, eGpencilModifierType_Multiply, panel_draw);
+ gpencil_modifier_subpanel_register(
+ region_type, "fade", "", fade_header_draw, fade_panel_draw, panel_type);
+ gpencil_modifier_subpanel_register(
+ region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type);
+}
+
GpencilModifierTypeInfo modifierType_Gpencil_Multiply = {
- /* name */ "Multiple Strokes",
+ /* name */ "MultipleStrokes",
/* structName */ "MultiplyGpencilModifierData",
/* structSize */ sizeof(MultiplyGpencilModifierData),
/* type */ eGpencilModifierTypeType_Gpencil,
@@ -327,4 +400,5 @@ GpencilModifierTypeInfo modifierType_Gpencil_Multiply = {
/* foreachObjectLink */ NULL,
/* foreachIDLink */ foreachIDLink,
/* foreachTexLink */ NULL,
+ /* panelRegister */ panelRegister,
};
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c
index 73328d7dd31..812bb5628e1 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c
@@ -31,6 +31,8 @@
#include "BLI_math_vector.h"
#include "BLI_rand.h"
+#include "BLT_translation.h"
+
#include "MEM_guardedalloc.h"
#include "DNA_gpencil_modifier_types.h"
@@ -38,8 +40,10 @@
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
#include "BKE_colortools.h"
+#include "BKE_context.h"
#include "BKE_deform.h"
#include "BKE_gpencil.h"
#include "BKE_gpencil_geom.h"
@@ -47,11 +51,18 @@
#include "BKE_lib_query.h"
#include "BKE_modifier.h"
#include "BKE_object.h"
+#include "BKE_screen.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "RNA_access.h"
+
#include "MOD_gpencil_modifiertypes.h"
+#include "MOD_gpencil_ui_common.h"
#include "MOD_gpencil_util.h"
static void initData(GpencilModifierData *md)
@@ -269,6 +280,72 @@ static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk,
walk(userData, ob, (ID **)&mmd->material, IDWALK_CB_USER);
}
+static void panel_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *col;
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ gpencil_modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ uiLayoutSetPropSep(layout, true);
+
+ col = uiLayoutColumn(layout, false);
+ uiItemR(col, &ptr, "factor", 0, IFACE_("Position"), ICON_NONE);
+ uiItemR(col, &ptr, "factor_strength", 0, IFACE_("Strength"), ICON_NONE);
+ uiItemR(col, &ptr, "factor_thickness", 0, IFACE_("Thickness"), ICON_NONE);
+ uiItemR(col, &ptr, "factor_uvs", 0, IFACE_("UV"), ICON_NONE);
+ uiItemR(col, &ptr, "noise_scale", 0, NULL, ICON_NONE);
+
+ gpencil_modifier_panel_end(layout, &ptr);
+}
+
+static void random_header_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ gpencil_modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ uiItemR(layout, &ptr, "random", 0, IFACE_("Randomize"), ICON_NONE);
+}
+
+static void random_panel_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ gpencil_modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ uiLayoutSetPropSep(layout, true);
+
+ uiLayoutSetActive(layout, RNA_boolean_get(&ptr, "random"));
+
+ uiItemR(layout, &ptr, "step", 0, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "seed", 0, NULL, ICON_NONE);
+}
+
+static void mask_panel_draw(const bContext *C, Panel *panel)
+{
+ gpencil_modifier_masking_panel_draw(C, panel, true, false);
+}
+
+static void panelRegister(ARegionType *region_type)
+{
+ PanelType *panel_type = gpencil_modifier_panel_register(
+ region_type, eGpencilModifierType_Noise, panel_draw);
+ gpencil_modifier_subpanel_register(
+ region_type, "randomize", "", random_header_draw, random_panel_draw, panel_type);
+ PanelType *mask_panel_type = gpencil_modifier_subpanel_register(
+ region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type);
+ gpencil_modifier_subpanel_register(region_type,
+ "curve",
+ "",
+ gpencil_modifier_curve_header_draw,
+ gpencil_modifier_curve_panel_draw,
+ mask_panel_type);
+}
+
GpencilModifierTypeInfo modifierType_Gpencil_Noise = {
/* name */ "Noise",
/* structName */ "NoiseGpencilModifierData",
@@ -291,4 +368,5 @@ GpencilModifierTypeInfo modifierType_Gpencil_Noise = {
/* foreachObjectLink */ NULL,
/* foreachIDLink */ foreachIDLink,
/* foreachTexLink */ NULL,
+ /* panelRegister */ panelRegister,
};
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c b/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c
index 686f589ffe9..9cc3712e8f4 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c
@@ -28,22 +28,33 @@
#include "BLI_math.h"
+#include "BLT_translation.h"
+
#include "DNA_gpencil_modifier_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
#include "BKE_colortools.h"
+#include "BKE_context.h"
#include "BKE_deform.h"
#include "BKE_gpencil_geom.h"
#include "BKE_gpencil_modifier.h"
#include "BKE_lib_query.h"
#include "BKE_modifier.h"
+#include "BKE_screen.h"
#include "DEG_depsgraph.h"
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "RNA_access.h"
+
#include "MOD_gpencil_modifiertypes.h"
+#include "MOD_gpencil_ui_common.h"
#include "MOD_gpencil_util.h"
static void initData(GpencilModifierData *md)
@@ -136,6 +147,35 @@ static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk,
walk(userData, ob, (ID **)&mmd->material, IDWALK_CB_USER);
}
+static void panel_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ gpencil_modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ uiLayoutSetPropSep(layout, true);
+
+ uiItemR(layout, &ptr, "location", 0, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "rotation", 0, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "scale", 0, NULL, ICON_NONE);
+
+ gpencil_modifier_panel_end(layout, &ptr);
+}
+
+static void mask_panel_draw(const bContext *C, Panel *panel)
+{
+ gpencil_modifier_masking_panel_draw(C, panel, true, true);
+}
+
+static void panelRegister(ARegionType *region_type)
+{
+ PanelType *panel_type = gpencil_modifier_panel_register(
+ region_type, eGpencilModifierType_Offset, panel_draw);
+ gpencil_modifier_subpanel_register(
+ region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type);
+}
+
GpencilModifierTypeInfo modifierType_Gpencil_Offset = {
/* name */ "Offset",
/* structName */ "OffsetGpencilModifierData",
@@ -158,4 +198,5 @@ GpencilModifierTypeInfo modifierType_Gpencil_Offset = {
/* foreachObjectLink */ NULL,
/* foreachIDLink */ foreachIDLink,
/* foreachTexLink */ NULL,
+ /* panelRegister */ panelRegister,
};
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c
index 92b2621d211..34142709c18 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c
@@ -28,13 +28,17 @@
#include "BLI_blenlib.h"
#include "BLI_math_vector.h"
+#include "BLT_translation.h"
+
#include "DNA_gpencil_modifier_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
#include "BKE_colortools.h"
+#include "BKE_context.h"
#include "BKE_deform.h"
#include "BKE_gpencil.h"
#include "BKE_gpencil_modifier.h"
@@ -42,10 +46,17 @@
#include "BKE_main.h"
#include "BKE_material.h"
#include "BKE_modifier.h"
+#include "BKE_screen.h"
#include "DEG_depsgraph.h"
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "RNA_access.h"
+
#include "MOD_gpencil_modifiertypes.h"
+#include "MOD_gpencil_ui_common.h"
#include "MOD_gpencil_util.h"
static void initData(GpencilModifierData *md)
@@ -180,6 +191,7 @@ static void bakeModifier(Main *UNUSED(bmain),
}
}
}
+
static void freeData(GpencilModifierData *md)
{
OpacityGpencilModifierData *gpmd = (OpacityGpencilModifierData *)md;
@@ -196,6 +208,79 @@ static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk,
walk(userData, ob, (ID **)&mmd->material, IDWALK_CB_USER);
}
+static void panel_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ gpencil_modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ uiLayoutSetPropSep(layout, true);
+
+ int modify_color = RNA_enum_get(&ptr, "modify_color");
+
+ uiItemR(layout, &ptr, "modify_color", 0, NULL, ICON_NONE);
+
+ if (modify_color == GP_MODIFY_COLOR_HARDNESS) {
+ uiItemR(layout, &ptr, "hardness", 0, NULL, ICON_NONE);
+ }
+ else {
+ uiItemR(layout, &ptr, "normalize_opacity", 0, NULL, ICON_NONE);
+ const char *text = (RNA_boolean_get(&ptr, "normalize_opacity")) ? IFACE_("Strength") :
+ IFACE_("Opacity Factor");
+ uiItemR(layout, &ptr, "hardness", 0, text, ICON_NONE);
+ }
+
+ gpencil_modifier_panel_end(layout, &ptr);
+}
+
+static void mask_panel_draw(const bContext *C, Panel *panel)
+{
+ PointerRNA ptr;
+ gpencil_modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ int modify_color = RNA_enum_get(&ptr, "modify_color");
+ bool show_vertex = (modify_color != GP_MODIFY_COLOR_HARDNESS);
+
+ gpencil_modifier_masking_panel_draw(C, panel, true, show_vertex);
+}
+
+static void curve_header_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ gpencil_modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ int modify_color = RNA_enum_get(&ptr, "modify_color");
+ uiLayoutSetActive(layout, modify_color != GP_MODIFY_COLOR_HARDNESS);
+
+ gpencil_modifier_curve_header_draw(C, panel);
+}
+
+static void curve_panel_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ gpencil_modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ int modify_color = RNA_enum_get(&ptr, "modify_color");
+ uiLayoutSetActive(layout, modify_color != GP_MODIFY_COLOR_HARDNESS);
+
+ gpencil_modifier_curve_panel_draw(C, panel);
+}
+
+static void panelRegister(ARegionType *region_type)
+{
+ PanelType *panel_type = gpencil_modifier_panel_register(
+ region_type, eGpencilModifierType_Opacity, panel_draw);
+ PanelType *mask_panel_type = gpencil_modifier_subpanel_register(
+ region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type);
+ gpencil_modifier_subpanel_register(
+ region_type, "curve", "", curve_header_draw, curve_panel_draw, mask_panel_type);
+}
+
GpencilModifierTypeInfo modifierType_Gpencil_Opacity = {
/* name */ "Opacity",
/* structName */ "OpacityGpencilModifierData",
@@ -218,4 +303,5 @@ GpencilModifierTypeInfo modifierType_Gpencil_Opacity = {
/* foreachObjectLink */ NULL,
/* foreachIDLink */ foreachIDLink,
/* foreachTexLink */ NULL,
+ /* panelRegister */ panelRegister,
};
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c
index 2cda108682e..8d4556421eb 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c
@@ -26,20 +26,31 @@
#include "BLI_listbase.h"
#include "BLI_utildefines.h"
+#include "BLT_translation.h"
+
#include "DNA_gpencil_modifier_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
#include "DNA_vec_types.h"
+#include "BKE_context.h"
#include "BKE_gpencil_geom.h"
#include "BKE_gpencil_modifier.h"
#include "BKE_lib_query.h"
#include "BKE_modifier.h"
+#include "BKE_screen.h"
#include "DEG_depsgraph.h"
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "RNA_access.h"
+
#include "MOD_gpencil_modifiertypes.h"
+#include "MOD_gpencil_ui_common.h"
#include "MOD_gpencil_util.h"
static void initData(GpencilModifierData *md)
@@ -131,6 +142,48 @@ static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk,
walk(userData, ob, (ID **)&mmd->material, IDWALK_CB_USER);
}
+static void panel_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ gpencil_modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ int mode = RNA_enum_get(&ptr, "mode");
+
+ uiLayoutSetPropSep(layout, true);
+
+ uiItemR(layout, &ptr, "mode", 0, NULL, ICON_NONE);
+
+ if (mode == GP_SIMPLIFY_FIXED) {
+ uiItemR(layout, &ptr, "step", 0, NULL, ICON_NONE);
+ }
+ else if (mode == GP_SIMPLIFY_ADAPTIVE) {
+ uiItemR(layout, &ptr, "factor", 0, NULL, ICON_NONE);
+ }
+ else if (mode == GP_SIMPLIFY_SAMPLE) {
+ uiItemR(layout, &ptr, "length", 0, NULL, ICON_NONE);
+ }
+ else if (mode == GP_SIMPLIFY_MERGE) {
+ uiItemR(layout, &ptr, "distance", 0, NULL, ICON_NONE);
+ }
+
+ gpencil_modifier_panel_end(layout, &ptr);
+}
+
+static void mask_panel_draw(const bContext *C, Panel *panel)
+{
+ gpencil_modifier_masking_panel_draw(C, panel, true, false);
+}
+
+static void panelRegister(ARegionType *region_type)
+{
+ PanelType *panel_type = gpencil_modifier_panel_register(
+ region_type, eGpencilModifierType_Simplify, panel_draw);
+ gpencil_modifier_subpanel_register(
+ region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type);
+}
+
GpencilModifierTypeInfo modifierType_Gpencil_Simplify = {
/* name */ "Simplify",
/* structName */ "SimplifyGpencilModifierData",
@@ -153,4 +206,5 @@ GpencilModifierTypeInfo modifierType_Gpencil_Simplify = {
/* foreachObjectLink */ NULL,
/* foreachIDLink */ foreachIDLink,
/* foreachTexLink */ NULL,
+ /* panelRegister */ panelRegister,
};
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c
index e2e13b736e4..175a6d81b1b 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c
@@ -26,21 +26,32 @@
#include "BLI_listbase.h"
#include "BLI_utildefines.h"
+#include "BLT_translation.h"
+
#include "DNA_gpencil_modifier_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
+#include "DNA_screen_types.h"
#include "BKE_colortools.h"
+#include "BKE_context.h"
#include "BKE_deform.h"
#include "BKE_gpencil_geom.h"
#include "BKE_gpencil_modifier.h"
#include "BKE_lib_query.h"
#include "BKE_modifier.h"
+#include "BKE_screen.h"
#include "DEG_depsgraph.h"
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "RNA_access.h"
+
#include "MOD_gpencil_modifiertypes.h"
+#include "MOD_gpencil_ui_common.h"
#include "MOD_gpencil_util.h"
static void initData(GpencilModifierData *md)
@@ -174,6 +185,47 @@ static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk,
walk(userData, ob, (ID **)&mmd->material, IDWALK_CB_USER);
}
+static void panel_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *row;
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ gpencil_modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ row = uiLayoutRow(layout, true);
+ uiItemR(row, &ptr, "use_edit_position", UI_ITEM_R_TOGGLE, IFACE_("Position"), ICON_NONE);
+ uiItemR(row, &ptr, "use_edit_strength", UI_ITEM_R_TOGGLE, IFACE_("Stength"), ICON_NONE);
+ uiItemR(row, &ptr, "use_edit_thickness", UI_ITEM_R_TOGGLE, IFACE_("Thickness"), ICON_NONE);
+ uiItemR(row, &ptr, "use_edit_uv", UI_ITEM_R_TOGGLE, IFACE_("UV"), ICON_NONE);
+
+ uiLayoutSetPropSep(layout, true);
+
+ uiItemR(layout, &ptr, "factor", 0, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "step", 0, IFACE_("Repeat"), ICON_NONE);
+
+ gpencil_modifier_panel_end(layout, &ptr);
+}
+
+static void mask_panel_draw(const bContext *C, Panel *panel)
+{
+ gpencil_modifier_masking_panel_draw(C, panel, true, true);
+}
+
+static void panelRegister(ARegionType *region_type)
+{
+ PanelType *panel_type = gpencil_modifier_panel_register(
+ region_type, eGpencilModifierType_Smooth, panel_draw);
+ PanelType *mask_panel_type = gpencil_modifier_subpanel_register(
+ region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type);
+ gpencil_modifier_subpanel_register(region_type,
+ "curve",
+ "",
+ gpencil_modifier_curve_header_draw,
+ gpencil_modifier_curve_panel_draw,
+ mask_panel_type);
+}
+
GpencilModifierTypeInfo modifierType_Gpencil_Smooth = {
/* name */ "Smooth",
/* structName */ "SmoothGpencilModifierData",
@@ -196,4 +248,5 @@ GpencilModifierTypeInfo modifierType_Gpencil_Smooth = {
/* foreachObjectLink */ NULL,
/* foreachIDLink */ foreachIDLink,
/* foreachTexLink */ NULL,
+ /* panelRegister */ panelRegister,
};
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c
index 072159136ce..2797235c002 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c
@@ -28,20 +28,31 @@
#include "BLI_listbase.h"
#include "BLI_utildefines.h"
+#include "BLT_translation.h"
+
#include "DNA_gpencil_modifier_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
+#include "BKE_context.h"
#include "BKE_gpencil_geom.h"
#include "BKE_gpencil_modifier.h"
#include "BKE_lib_query.h"
#include "BKE_modifier.h"
+#include "BKE_screen.h"
#include "DEG_depsgraph.h"
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "RNA_access.h"
+
#include "MOD_gpencil_modifiertypes.h"
+#include "MOD_gpencil_ui_common.h"
#include "MOD_gpencil_util.h"
static void initData(GpencilModifierData *md)
@@ -112,6 +123,34 @@ static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk,
walk(userData, ob, (ID **)&mmd->material, IDWALK_CB_USER);
}
+static void panel_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ gpencil_modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ uiLayoutSetPropSep(layout, true);
+
+ uiItemR(layout, &ptr, "subdivision_type", 0, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "level", 0, IFACE_("Subdivisions"), ICON_NONE);
+
+ gpencil_modifier_panel_end(layout, &ptr);
+}
+
+static void mask_panel_draw(const bContext *C, Panel *panel)
+{
+ gpencil_modifier_masking_panel_draw(C, panel, true, false);
+}
+
+static void panelRegister(ARegionType *region_type)
+{
+ PanelType *panel_type = gpencil_modifier_panel_register(
+ region_type, eGpencilModifierType_Subdiv, panel_draw);
+ gpencil_modifier_subpanel_register(
+ region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type);
+}
+
GpencilModifierTypeInfo modifierType_Gpencil_Subdiv = {
/* name */ "Subdivide",
/* structName */ "SubdivGpencilModifierData",
@@ -134,4 +173,5 @@ GpencilModifierTypeInfo modifierType_Gpencil_Subdiv = {
/* foreachObjectLink */ NULL,
/* foreachIDLink */ foreachIDLink,
/* foreachTexLink */ NULL,
+ /* panelRegister */ panelRegister,
};
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpenciltexture.c b/source/blender/gpencil_modifiers/intern/MOD_gpenciltexture.c
index a5adf12b617..2d16b6ead5c 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpenciltexture.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpenciltexture.c
@@ -27,23 +27,34 @@
#include "BLI_math.h"
#include "BLI_utildefines.h"
+#include "BLT_translation.h"
+
#include "DNA_gpencil_modifier_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
#include "BKE_colortools.h"
+#include "BKE_context.h"
#include "BKE_deform.h"
#include "BKE_gpencil.h"
#include "BKE_gpencil_geom.h"
#include "BKE_gpencil_modifier.h"
#include "BKE_lib_query.h"
#include "BKE_modifier.h"
+#include "BKE_screen.h"
#include "DEG_depsgraph.h"
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "RNA_access.h"
+
#include "MOD_gpencil_modifiertypes.h"
+#include "MOD_gpencil_ui_common.h"
#include "MOD_gpencil_util.h"
static void initData(GpencilModifierData *md)
@@ -147,8 +158,56 @@ static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk,
walk(userData, ob, (ID **)&mmd->material, IDWALK_CB_USER);
}
+static void panel_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *col;
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ gpencil_modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ int mode = RNA_enum_get(&ptr, "mode");
+
+ uiLayoutSetPropSep(layout, true);
+
+ uiItemR(layout, &ptr, "mode", 0, NULL, ICON_NONE);
+
+ if (ELEM(mode, STROKE, STROKE_AND_FILL)) {
+ col = uiLayoutColumn(layout, false);
+ uiItemR(col, &ptr, "fit_method", 0, IFACE_("Stroke Fit Method"), ICON_NONE);
+ uiItemR(col, &ptr, "uv_offset", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "uv_scale", 0, IFACE_("Scale"), ICON_NONE);
+ }
+
+ if (mode == STROKE_AND_FILL) {
+ uiItemS(layout);
+ }
+
+ if (ELEM(mode, FILL, STROKE_AND_FILL)) {
+ col = uiLayoutColumn(layout, false);
+ uiItemR(col, &ptr, "fill_rotation", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "fill_offset", 0, IFACE_("Offset"), ICON_NONE);
+ uiItemR(col, &ptr, "fill_scale", 0, IFACE_("Scale"), ICON_NONE);
+ }
+
+ gpencil_modifier_panel_end(layout, &ptr);
+}
+
+static void mask_panel_draw(const bContext *C, Panel *panel)
+{
+ gpencil_modifier_masking_panel_draw(C, panel, true, true);
+}
+
+static void panelRegister(ARegionType *region_type)
+{
+ PanelType *panel_type = gpencil_modifier_panel_register(
+ region_type, eGpencilModifierType_Texture, panel_draw);
+ gpencil_modifier_subpanel_register(
+ region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type);
+}
+
GpencilModifierTypeInfo modifierType_Gpencil_Texture = {
- /* name */ "Texture Mapping",
+ /* name */ "TextureMapping",
/* structName */ "TextureGpencilModifierData",
/* structSize */ sizeof(TextureGpencilModifierData),
/* type */ eGpencilModifierTypeType_Gpencil,
@@ -169,4 +228,5 @@ GpencilModifierTypeInfo modifierType_Gpencil_Texture = {
/* foreachObjectLink */ NULL,
/* foreachIDLink */ foreachIDLink,
/* foreachTexLink */ NULL,
+ /* panelRegister */ panelRegister,
};
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c
index b9fadea7fd0..4fa47a592ba 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c
@@ -27,22 +27,33 @@
#include "BLI_math.h"
#include "BLI_utildefines.h"
+#include "BLT_translation.h"
+
#include "DNA_gpencil_modifier_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
#include "BKE_colortools.h"
+#include "BKE_context.h"
#include "BKE_deform.h"
#include "BKE_gpencil.h"
#include "BKE_gpencil_modifier.h"
#include "BKE_lib_query.h"
#include "BKE_modifier.h"
+#include "BKE_screen.h"
#include "DEG_depsgraph.h"
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "RNA_access.h"
+
#include "MOD_gpencil_modifiertypes.h"
+#include "MOD_gpencil_ui_common.h"
#include "MOD_gpencil_util.h"
static void initData(GpencilModifierData *md)
@@ -166,6 +177,46 @@ static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk,
walk(userData, ob, (ID **)&mmd->material, IDWALK_CB_USER);
}
+static void panel_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ gpencil_modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ uiLayoutSetPropSep(layout, true);
+
+ uiItemR(layout, &ptr, "normalize_thickness", 0, NULL, ICON_NONE);
+
+ if (RNA_boolean_get(&ptr, "normalize_thickness")) {
+ uiItemR(layout, &ptr, "thickness", 0, NULL, ICON_NONE);
+ }
+ else {
+ uiItemR(layout, &ptr, "thickness_factor", 0, NULL, ICON_NONE);
+ }
+
+ gpencil_modifier_panel_end(layout, &ptr);
+}
+
+static void mask_panel_draw(const bContext *C, Panel *panel)
+{
+ gpencil_modifier_masking_panel_draw(C, panel, true, true);
+}
+
+static void panelRegister(ARegionType *region_type)
+{
+ PanelType *panel_type = gpencil_modifier_panel_register(
+ region_type, eGpencilModifierType_Thick, panel_draw);
+ PanelType *mask_panel_type = gpencil_modifier_subpanel_register(
+ region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type);
+ gpencil_modifier_subpanel_register(region_type,
+ "curve",
+ "",
+ gpencil_modifier_curve_header_draw,
+ gpencil_modifier_curve_panel_draw,
+ mask_panel_type);
+}
+
GpencilModifierTypeInfo modifierType_Gpencil_Thick = {
/* name */ "Thickness",
/* structName */ "ThickGpencilModifierData",
@@ -188,4 +239,5 @@ GpencilModifierTypeInfo modifierType_Gpencil_Thick = {
/* foreachObjectLink */ NULL,
/* foreachIDLink */ foreachIDLink,
/* foreachTexLink */ NULL,
+ /* panelRegister */ panelRegister,
};
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpenciltime.c b/source/blender/gpencil_modifiers/intern/MOD_gpenciltime.c
index 85400b56cad..49396f56d26 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpenciltime.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpenciltime.c
@@ -26,20 +26,31 @@
#include "BLI_utildefines.h"
+#include "BLT_translation.h"
+
#include "DNA_gpencil_modifier_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
#include "BKE_colortools.h"
+#include "BKE_context.h"
#include "BKE_deform.h"
#include "BKE_gpencil.h"
#include "BKE_gpencil_modifier.h"
+#include "BKE_screen.h"
#include "DEG_depsgraph.h"
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "RNA_access.h"
+
#include "MOD_gpencil_modifiertypes.h"
+#include "MOD_gpencil_ui_common.h"
#include "MOD_gpencil_util.h"
static void initData(GpencilModifierData *md)
@@ -165,8 +176,91 @@ static int remapTime(struct GpencilModifierData *md,
return cfra + offset;
}
+static void panel_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *row, *col;
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ gpencil_modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ int mode = RNA_enum_get(&ptr, "mode");
+
+ uiLayoutSetPropSep(layout, true);
+
+ uiItemR(layout, &ptr, "mode", 0, NULL, ICON_NONE);
+
+ col = uiLayoutColumn(layout, false);
+
+ const char *text = (mode == GP_TIME_MODE_FIX) ? IFACE_("Frame") : IFACE_("Frame Offset");
+ uiItemR(col, &ptr, "offset", 0, text, ICON_NONE);
+
+ row = uiLayoutRow(col, false);
+ uiLayoutSetActive(row, mode != GP_TIME_MODE_FIX);
+ uiItemR(row, &ptr, "frame_scale", 0, IFACE_("Scale"), ICON_NONE);
+
+ row = uiLayoutRow(layout, false);
+ uiLayoutSetActive(row, mode != GP_TIME_MODE_FIX);
+ uiItemR(row, &ptr, "use_keep_loop", 0, NULL, ICON_NONE);
+
+ gpencil_modifier_panel_end(layout, &ptr);
+}
+
+static void custom_range_header_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ gpencil_modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ int mode = RNA_enum_get(&ptr, "mode");
+
+ uiLayoutSetActive(layout, mode != GP_TIME_MODE_FIX);
+
+ uiItemR(layout, &ptr, "use_custom_frame_range", 0, NULL, ICON_NONE);
+}
+
+static void custom_range_panel_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *col;
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ gpencil_modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ int mode = RNA_enum_get(&ptr, "mode");
+
+ uiLayoutSetPropSep(layout, true);
+
+ uiLayoutSetActive(
+ layout, (mode != GP_TIME_MODE_FIX) && (RNA_boolean_get(&ptr, "use_custom_frame_range")));
+
+ col = uiLayoutColumn(layout, false);
+ uiItemR(col, &ptr, "frame_start", 0, IFACE_("Frame Start"), ICON_NONE);
+ uiItemR(col, &ptr, "frame_end", 0, IFACE_("End"), ICON_NONE);
+}
+
+static void mask_panel_draw(const bContext *C, Panel *panel)
+{
+ gpencil_modifier_masking_panel_draw(C, panel, false, false);
+}
+
+static void panelRegister(ARegionType *region_type)
+{
+ PanelType *panel_type = gpencil_modifier_panel_register(
+ region_type, eGpencilModifierType_Time, panel_draw);
+ gpencil_modifier_subpanel_register(region_type,
+ "custom_range",
+ "",
+ custom_range_header_draw,
+ custom_range_panel_draw,
+ panel_type);
+ gpencil_modifier_subpanel_register(
+ region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type);
+}
+
GpencilModifierTypeInfo modifierType_Gpencil_Time = {
- /* name */ "Time Offset",
+ /* name */ "TimeOffset",
/* structName */ "TimeGpencilModifierData",
/* structSize */ sizeof(TimeGpencilModifierData),
/* type */ eGpencilModifierTypeType_Gpencil,
@@ -187,4 +281,5 @@ GpencilModifierTypeInfo modifierType_Gpencil_Time = {
/* foreachObjectLink */ NULL,
/* foreachIDLink */ NULL,
/* foreachTexLink */ NULL,
+ /* panelRegister */ panelRegister,
};
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c b/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c
index c35728bc8b3..da7d33839f1 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c
@@ -28,16 +28,20 @@
#include "BLI_listbase.h"
#include "BLI_math.h"
+#include "BLT_translation.h"
+
#include "DNA_gpencil_modifier_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
#include "BKE_action.h"
#include "BKE_colorband.h"
#include "BKE_colortools.h"
+#include "BKE_context.h"
#include "BKE_deform.h"
#include "BKE_gpencil.h"
#include "BKE_gpencil_modifier.h"
@@ -47,10 +51,17 @@
#include "BKE_material.h"
#include "BKE_modifier.h"
#include "BKE_scene.h"
+#include "BKE_screen.h"
#include "MEM_guardedalloc.h"
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "RNA_access.h"
+
#include "MOD_gpencil_modifiertypes.h"
+#include "MOD_gpencil_ui_common.h"
#include "MOD_gpencil_util.h"
#include "DEG_depsgraph.h"
@@ -330,6 +341,56 @@ static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk,
foreachObjectLink(md, ob, (ObjectWalkFunc)walk, userData);
}
+static void panel_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *col;
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ gpencil_modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ int tint_type = RNA_enum_get(&ptr, "tint_type");
+
+ uiLayoutSetPropSep(layout, true);
+
+ uiItemR(layout, &ptr, "vertex_mode", 0, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "factor", 0, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "tint_type", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+
+ if (tint_type == GP_TINT_UNIFORM) {
+ uiItemR(layout, &ptr, "color", 0, NULL, ICON_NONE);
+ }
+ else {
+ col = uiLayoutColumn(layout, false);
+ uiLayoutSetPropSep(col, false);
+ uiTemplateColorRamp(col, &ptr, "colors", true);
+ uiItemS(layout);
+ uiItemR(layout, &ptr, "object", 0, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "radius", 0, NULL, ICON_NONE);
+ }
+
+ gpencil_modifier_panel_end(layout, &ptr);
+}
+
+static void mask_panel_draw(const bContext *C, Panel *panel)
+{
+ gpencil_modifier_masking_panel_draw(C, panel, true, true);
+}
+
+static void panelRegister(ARegionType *region_type)
+{
+ PanelType *panel_type = gpencil_modifier_panel_register(
+ region_type, eGpencilModifierType_Tint, panel_draw);
+ PanelType *mask_panel_type = gpencil_modifier_subpanel_register(
+ region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type);
+ gpencil_modifier_subpanel_register(region_type,
+ "curve",
+ "",
+ gpencil_modifier_curve_header_draw,
+ gpencil_modifier_curve_panel_draw,
+ mask_panel_type);
+}
+
GpencilModifierTypeInfo modifierType_Gpencil_Tint = {
/* name */ "Tint",
/* structName */ "TintGpencilModifierData",
@@ -352,4 +413,5 @@ GpencilModifierTypeInfo modifierType_Gpencil_Tint = {
/* foreachObjectLink */ foreachObjectLink,
/* foreachIDLink */ foreachIDLink,
/* foreachTexLink */ NULL,
+ /* panelRegister */ panelRegister,
};
diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt
index 1d6a5031d7e..0f7e804de9e 100644
--- a/source/blender/gpu/CMakeLists.txt
+++ b/source/blender/gpu/CMakeLists.txt
@@ -210,6 +210,7 @@ data_to_c_simple(shaders/gpu_shader_2D_point_uniform_size_varying_color_outline_
data_to_c_simple(shaders/gpu_shader_2D_edituvs_points_vert.glsl SRC)
data_to_c_simple(shaders/gpu_shader_2D_edituvs_facedots_vert.glsl SRC)
data_to_c_simple(shaders/gpu_shader_2D_edituvs_edges_vert.glsl SRC)
+data_to_c_simple(shaders/gpu_shader_2D_edituvs_edges_frag.glsl SRC)
data_to_c_simple(shaders/gpu_shader_2D_edituvs_faces_vert.glsl SRC)
data_to_c_simple(shaders/gpu_shader_2D_edituvs_stretch_vert.glsl SRC)
diff --git a/source/blender/gpu/GPU_buffers.h b/source/blender/gpu/GPU_buffers.h
index ab16bfc43c4..41a29a4d45d 100644
--- a/source/blender/gpu/GPU_buffers.h
+++ b/source/blender/gpu/GPU_buffers.h
@@ -37,6 +37,7 @@ struct DMFlagMat;
struct GSet;
struct MLoop;
struct MLoopCol;
+struct MPropCol;
struct MLoopTri;
struct MPoly;
struct MVert;
@@ -82,6 +83,7 @@ void GPU_pbvh_mesh_buffers_update(GPU_PBVH_Buffers *buffers,
const int *sculpt_face_sets,
const int face_sets_color_seed,
const int face_sets_color_default,
+ const struct MPropCol *vtcol,
const int update_flags);
void GPU_pbvh_bmesh_buffers_update(GPU_PBVH_Buffers *buffers,
diff --git a/source/blender/gpu/GPU_draw.h b/source/blender/gpu/GPU_draw.h
index ab507d852e8..1cd5c4d519e 100644
--- a/source/blender/gpu/GPU_draw.h
+++ b/source/blender/gpu/GPU_draw.h
@@ -86,7 +86,7 @@ void GPU_create_smoke_coba_field(struct FluidModifierData *mmd);
void GPU_create_smoke_velocity(struct FluidModifierData *mmd);
/* Delayed free of OpenGL buffers by main thread */
-void GPU_free_unused_buffers(struct Main *bmain);
+void GPU_free_unused_buffers(void);
#ifdef __cplusplus
}
diff --git a/source/blender/gpu/GPU_vertex_buffer.h b/source/blender/gpu/GPU_vertex_buffer.h
index fe45ec7b78b..f9bdf726930 100644
--- a/source/blender/gpu/GPU_vertex_buffer.h
+++ b/source/blender/gpu/GPU_vertex_buffer.h
@@ -80,6 +80,8 @@ void GPU_vertbuf_init_with_format_ex(GPUVertBuf *, const GPUVertFormat *, GPUUsa
#define GPU_vertbuf_init_with_format(verts, format) \
GPU_vertbuf_init_with_format_ex(verts, format, GPU_USAGE_STATIC)
+GPUVertBuf *GPU_vertbuf_duplicate(GPUVertBuf *verts);
+
uint GPU_vertbuf_size_get(const GPUVertBuf *);
void GPU_vertbuf_data_alloc(GPUVertBuf *, uint v_len);
void GPU_vertbuf_data_resize(GPUVertBuf *, uint v_len);
diff --git a/source/blender/gpu/GPU_vertex_format.h b/source/blender/gpu/GPU_vertex_format.h
index 61b14a4c5c0..34bfbb27823 100644
--- a/source/blender/gpu/GPU_vertex_format.h
+++ b/source/blender/gpu/GPU_vertex_format.h
@@ -124,6 +124,10 @@ BLI_INLINE const char *GPU_vertformat_attr_name_get(const GPUVertFormat *format,
return format->names + attr->names[n_idx];
}
+/* WARNING: Can only rename using a string with same character count.
+ * WARNING: This removes all other aliases of this attrib */
+void GPU_vertformat_attr_rename(GPUVertFormat *format, int attr, const char *new_name);
+
void GPU_vertformat_safe_attr_name(const char *attr_name, char *r_safe_name, uint max_len);
/* format conversion */
diff --git a/source/blender/gpu/intern/gpu_buffers.c b/source/blender/gpu/intern/gpu_buffers.c
index cef90d57ef5..9c21f9040da 100644
--- a/source/blender/gpu/intern/gpu_buffers.c
+++ b/source/blender/gpu/intern/gpu_buffers.c
@@ -34,6 +34,7 @@
#include "BLI_hash.h"
#include "BLI_math.h"
#include "BLI_math_color.h"
+#include "BLI_math_color_blend.h"
#include "BLI_utildefines.h"
#include "DNA_meshdata_types.h"
@@ -227,12 +228,13 @@ void GPU_pbvh_mesh_buffers_update(GPU_PBVH_Buffers *buffers,
const int *sculpt_face_sets,
const int face_sets_color_seed,
const int face_sets_color_default,
+ const MPropCol *vtcol,
const int update_flags)
{
const bool show_mask = vmask && (update_flags & GPU_PBVH_BUFFERS_SHOW_MASK) != 0;
- const bool show_vcol = vcol && (update_flags & GPU_PBVH_BUFFERS_SHOW_VCOL) != 0;
const bool show_face_sets = sculpt_face_sets &&
(update_flags & GPU_PBVH_BUFFERS_SHOW_SCULPT_FACE_SETS) != 0;
+ const bool show_vcol = (vcol || vtcol) && (update_flags & GPU_PBVH_BUFFERS_SHOW_VCOL) != 0;
bool empty_mask = true;
bool default_face_set = true;
@@ -244,8 +246,8 @@ void GPU_pbvh_mesh_buffers_update(GPU_PBVH_Buffers *buffers,
GPUVertBufRaw pos_step = {0};
GPUVertBufRaw nor_step = {0};
GPUVertBufRaw msk_step = {0};
- GPUVertBufRaw col_step = {0};
GPUVertBufRaw fset_step = {0};
+ GPUVertBufRaw col_step = {0};
GPU_vertbuf_attr_get_raw_data(buffers->vert_buf, g_vbo_id.pos, &pos_step);
GPU_vertbuf_attr_get_raw_data(buffers->vert_buf, g_vbo_id.nor, &nor_step);
@@ -312,25 +314,33 @@ void GPU_pbvh_mesh_buffers_update(GPU_PBVH_Buffers *buffers,
*(uchar *)GPU_vertbuf_raw_step(&msk_step) = cmask;
empty_mask = empty_mask && (cmask == 0);
-
+ /* Vertex Colors. */
if (show_vcol) {
- const uint loop_index = lt->tri[j];
- const MLoopCol *mcol = &vcol[loop_index];
- ushort scol[4];
- scol[0] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->r]);
- scol[1] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->g]);
- scol[2] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->b]);
- scol[3] = unit_float_to_ushort_clamp(mcol->a * (1.0f / 255.0f));
- memcpy(GPU_vertbuf_raw_step(&col_step), scol, sizeof(scol));
+ ushort scol[4] = {USHRT_MAX, USHRT_MAX, USHRT_MAX, USHRT_MAX};
+ if (vtcol) {
+ scol[0] = unit_float_to_ushort_clamp(vtcol[vtri[j]].color[0]);
+ scol[1] = unit_float_to_ushort_clamp(vtcol[vtri[j]].color[1]);
+ scol[2] = unit_float_to_ushort_clamp(vtcol[vtri[j]].color[2]);
+ scol[3] = unit_float_to_ushort_clamp(vtcol[vtri[j]].color[3]);
+ memcpy(GPU_vertbuf_raw_step(&col_step), scol, sizeof(scol));
+ }
+ else {
+ const uint loop_index = lt->tri[j];
+ const MLoopCol *mcol = &vcol[loop_index];
+ scol[0] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->r]);
+ scol[1] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->g]);
+ scol[2] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->b]);
+ scol[3] = unit_float_to_ushort_clamp(mcol->a * (1.0f / 255.0f));
+ memcpy(GPU_vertbuf_raw_step(&col_step), scol, sizeof(scol));
+ }
}
-
/* Face Sets. */
memcpy(GPU_vertbuf_raw_step(&fset_step), face_set_color, sizeof(uchar) * 3);
}
}
-
- gpu_pbvh_batch_init(buffers, GPU_PRIM_TRIS);
}
+
+ gpu_pbvh_batch_init(buffers, GPU_PRIM_TRIS);
}
/* Get material index from the first face of this buffer. */
diff --git a/source/blender/gpu/intern/gpu_draw.c b/source/blender/gpu/intern/gpu_draw.c
index b03b0fc0b1e..f07e3ed70d7 100644
--- a/source/blender/gpu/intern/gpu_draw.c
+++ b/source/blender/gpu/intern/gpu_draw.c
@@ -60,7 +60,8 @@
#include "PIL_time.h"
-static void gpu_free_image_immediate(Image *ima);
+static void gpu_free_image(Image *ima, const bool immediate);
+static void gpu_free_unused_buffers(void);
//* Checking powers of two for images since OpenGL ES requires it */
#ifdef WITH_DDS
@@ -859,9 +860,13 @@ GPUTexture *GPU_texture_from_blender(Image *ima, ImageUser *iuser, ImBuf *ibuf,
return NULL;
}
+ /* Free any unused GPU textures, since we know we are in a thread with OpenGL
+ * context and might as well ensure we have as much space free as possible. */
+ gpu_free_unused_buffers();
+
/* currently, gpu refresh tagging is used by ima sequences */
if (ima->gpuflag & IMA_GPU_REFRESH) {
- gpu_free_image_immediate(ima);
+ gpu_free_image(ima, true);
ima->gpuflag &= ~IMA_GPU_REFRESH;
}
@@ -1338,63 +1343,65 @@ void GPU_paint_update_image(Image *ima, ImageUser *iuser, int x, int y, int w, i
#endif
}
-static LinkNode *image_free_queue = NULL;
-static ThreadMutex img_queue_mutex = BLI_MUTEX_INITIALIZER;
+/* Delayed GPU texture free. Image datablocks can be deleted by any thread,
+ * but there may not be any active OpenGL context. In that case we push them
+ * into a queue and free the buffers later. */
+static LinkNode *gpu_texture_free_queue = NULL;
+static ThreadMutex gpu_texture_queue_mutex = BLI_MUTEX_INITIALIZER;
-static void gpu_queue_image_for_free(Image *ima)
+static void gpu_free_unused_buffers()
{
- BLI_mutex_lock(&img_queue_mutex);
- BLI_linklist_prepend(&image_free_queue, ima);
- BLI_mutex_unlock(&img_queue_mutex);
-}
-
-void GPU_free_unused_buffers(Main *bmain)
-{
- if (!BLI_thread_is_main()) {
+ if (gpu_texture_free_queue == NULL) {
return;
}
- BLI_mutex_lock(&img_queue_mutex);
-
- /* images */
- for (LinkNode *node = image_free_queue; node; node = node->next) {
- Image *ima = node->link;
+ BLI_mutex_lock(&gpu_texture_queue_mutex);
- /* check in case it was freed in the meantime */
- if (bmain && BLI_findindex(&bmain->images, ima) != -1) {
- GPU_free_image(ima);
+ if (gpu_texture_free_queue != NULL) {
+ for (LinkNode *node = gpu_texture_free_queue; node; node = node->next) {
+ GPUTexture *tex = node->link;
+ GPU_texture_free(tex);
}
- }
- BLI_linklist_free(image_free_queue, NULL);
- image_free_queue = NULL;
+ BLI_linklist_free(gpu_texture_free_queue, NULL);
+ gpu_texture_free_queue = NULL;
+ }
- BLI_mutex_unlock(&img_queue_mutex);
+ BLI_mutex_unlock(&gpu_texture_queue_mutex);
}
-static void gpu_free_image_immediate(Image *ima)
+static void gpu_free_image(Image *ima, const bool immediate)
{
for (int eye = 0; eye < 2; eye++) {
for (int i = 0; i < TEXTARGET_COUNT; i++) {
- /* free glsl image binding */
if (ima->gputexture[i][eye] != NULL) {
- GPU_texture_free(ima->gputexture[i][eye]);
+ if (immediate) {
+ GPU_texture_free(ima->gputexture[i][eye]);
+ }
+ else {
+ BLI_mutex_lock(&gpu_texture_queue_mutex);
+ BLI_linklist_prepend(&gpu_texture_free_queue, ima->gputexture[i][eye]);
+ BLI_mutex_unlock(&gpu_texture_queue_mutex);
+ }
+
ima->gputexture[i][eye] = NULL;
}
}
}
- ima->gpuflag &= ~(IMA_GPU_MIPMAP_COMPLETE);
+ ima->gpuflag &= ~IMA_GPU_MIPMAP_COMPLETE;
}
-void GPU_free_image(Image *ima)
+void GPU_free_unused_buffers()
{
- if (!BLI_thread_is_main()) {
- gpu_queue_image_for_free(ima);
- return;
+ if (BLI_thread_is_main()) {
+ gpu_free_unused_buffers();
}
+}
- gpu_free_image_immediate(ima);
+void GPU_free_image(Image *ima)
+{
+ gpu_free_image(ima, BLI_thread_is_main());
}
void GPU_free_images(Main *bmain)
diff --git a/source/blender/gpu/intern/gpu_draw_smoke.c b/source/blender/gpu/intern/gpu_draw_smoke.c
index 80c59ed47c9..67947df0ff7 100644
--- a/source/blender/gpu/intern/gpu_draw_smoke.c
+++ b/source/blender/gpu/intern/gpu_draw_smoke.c
@@ -99,6 +99,7 @@ static void create_color_ramp(const struct ColorBand *coba, float *data)
{
for (int i = 0; i < TFUNC_WIDTH; i++) {
BKE_colorband_evaluate(coba, (float)i / TFUNC_WIDTH, &data[i * 4]);
+ straight_to_premul_v4(&data[i * 4]);
}
}
@@ -115,7 +116,7 @@ static GPUTexture *create_transfer_function(int type, const struct ColorBand *co
break;
}
- GPUTexture *tex = GPU_texture_create_1d(TFUNC_WIDTH, GPU_RGBA8, data, NULL);
+ GPUTexture *tex = GPU_texture_create_1d(TFUNC_WIDTH, GPU_SRGB8_A8, data, NULL);
MEM_freeN(data);
diff --git a/source/blender/gpu/intern/gpu_extensions.c b/source/blender/gpu/intern/gpu_extensions.c
index ab55fcfb1e0..fbeb2edc266 100644
--- a/source/blender/gpu/intern/gpu_extensions.c
+++ b/source/blender/gpu/intern/gpu_extensions.c
@@ -71,7 +71,7 @@ static struct GPUGlobal {
int samples_color_texture_max;
float line_width_range[2];
/* workaround for different calculation of dfdy factors on GPUs. Some GPUs/drivers
- * calculate dfdy in shader differently when drawing to an offscreen buffer. First
+ * calculate dfdy in shader differently when drawing to an off-screen buffer. First
* number is factor on screen and second is off-screen */
float dfdyfactors[2];
float max_anisotropy;
@@ -84,9 +84,9 @@ static struct GPUGlobal {
* GL_TEXTURE_MAX_LEVEL is higher than the target mip.
* We need a workaround in this cases. */
bool mip_render_workaround;
- /* There is an issue with the glBlitFramebuffer on MacOS with radeon pro graphics.
- * Blitting depth with GL_DEPTH24_STENCIL8 is buggy so the workaround is to use
- * GPU_DEPTH32F_STENCIL8. Then Blitting depth will work but blitting stencil will
+ /* There is an issue with the #glBlitFramebuffer on MacOS with radeon pro graphics.
+ * Blitting depth with#GL_DEPTH24_STENCIL8 is buggy so the workaround is to use
+ * #GPU_DEPTH32F_STENCIL8. Then Blitting depth will work but blitting stencil will
* still be broken. */
bool depth_blitting_workaround;
/* Crappy driver don't know how to map framebuffer slot to output vars...
@@ -96,7 +96,7 @@ static struct GPUGlobal {
/* Some crappy Intel drivers don't work well with shaders created in different
* rendering contexts. */
bool context_local_shaders_workaround;
- /* Intel drivers exhibit artifacts when using glCopyImageSubData & workbench antialiasing.
+ /* Intel drivers exhibit artifacts when using #glCopyImageSubData & workbench anti-aliasing.
* (see T76273) */
bool texture_copy_workaround;
} GG = {1, 0};
@@ -288,6 +288,19 @@ void gpu_extensions_init(void)
}
}
+ if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_UNIX, GPU_DRIVER_OPENSOURCE) &&
+ strstr(renderer, "AMD VERDE")) {
+ /* We have issues with this specific renderer. (see T74024) */
+ GG.unused_fb_slot_workaround = true;
+ GG.broken_amd_driver = true;
+ }
+
+ if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_UNIX, GPU_DRIVER_OPENSOURCE) &&
+ strstr(version, "Mesa 19.3.4")) {
+ /* Fix slowdown on this particular driver. (see T77641) */
+ GG.broken_amd_driver = true;
+ }
+
if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_MAC, GPU_DRIVER_OFFICIAL)) {
if (strstr(renderer, "AMD Radeon Pro") || strstr(renderer, "AMD Radeon R9") ||
strstr(renderer, "AMD Radeon RX")) {
diff --git a/source/blender/gpu/intern/gpu_platform.c b/source/blender/gpu/intern/gpu_platform.c
index a758787466f..5cabde61bc3 100644
--- a/source/blender/gpu/intern/gpu_platform.c
+++ b/source/blender/gpu/intern/gpu_platform.c
@@ -44,6 +44,8 @@ static struct GPUPlatformGlobal {
char *gpu_name;
} GPG = {false};
+/* Remove this? */
+#if 0
typedef struct GPUPlatformSupportTest {
eGPUSupportLevel support_level;
eGPUDeviceType device;
@@ -53,6 +55,7 @@ typedef struct GPUPlatformSupportTest {
const char *renderer;
const char *version;
} GPUPlatformSupportTest;
+#endif
eGPUSupportLevel GPU_platform_support_level(void)
{
diff --git a/source/blender/gpu/intern/gpu_shader.c b/source/blender/gpu/intern/gpu_shader.c
index 8c03567b95f..711147a61e9 100644
--- a/source/blender/gpu/intern/gpu_shader.c
+++ b/source/blender/gpu/intern/gpu_shader.c
@@ -117,6 +117,7 @@ extern char datatoc_gpu_shader_2D_point_uniform_size_varying_color_outline_aa_ve
extern char datatoc_gpu_shader_2D_edituvs_points_vert_glsl[];
extern char datatoc_gpu_shader_2D_edituvs_facedots_vert_glsl[];
extern char datatoc_gpu_shader_2D_edituvs_edges_vert_glsl[];
+extern char datatoc_gpu_shader_2D_edituvs_edges_frag_glsl[];
extern char datatoc_gpu_shader_2D_edituvs_faces_vert_glsl[];
extern char datatoc_gpu_shader_2D_edituvs_stretch_vert_glsl[];
@@ -1171,12 +1172,12 @@ static const GPUShaderStages builtin_shader_stages[GPU_SHADER_BUILTIN_LEN] = {
[GPU_SHADER_2D_UV_EDGES] =
{
.vert = datatoc_gpu_shader_2D_edituvs_edges_vert_glsl,
- .frag = datatoc_gpu_shader_flat_color_frag_glsl,
+ .frag = datatoc_gpu_shader_2D_edituvs_edges_frag_glsl,
},
[GPU_SHADER_2D_UV_EDGES_SMOOTH] =
{
.vert = datatoc_gpu_shader_2D_edituvs_edges_vert_glsl,
- .frag = datatoc_gpu_shader_2D_smooth_color_frag_glsl,
+ .frag = datatoc_gpu_shader_2D_edituvs_edges_frag_glsl,
.defs = "#define SMOOTH_COLOR\n",
},
[GPU_SHADER_2D_UV_FACES] =
diff --git a/source/blender/gpu/intern/gpu_state.c b/source/blender/gpu/intern/gpu_state.c
index 908f5fa5771..30b258a73d1 100644
--- a/source/blender/gpu/intern/gpu_state.c
+++ b/source/blender/gpu/intern/gpu_state.c
@@ -208,7 +208,7 @@ typedef struct {
uint is_cull_face : 1;
uint is_depth_test : 1;
uint is_dither : 1;
- uint is_lighting : 1;
+ /* uint is_lighting : 1; */ /* UNUSED */
uint is_line_smooth : 1;
uint is_color_logic_op : 1;
uint is_multisample : 1;
diff --git a/source/blender/gpu/intern/gpu_texture.c b/source/blender/gpu/intern/gpu_texture.c
index a985d45162c..56e82b94fd2 100644
--- a/source/blender/gpu/intern/gpu_texture.c
+++ b/source/blender/gpu/intern/gpu_texture.c
@@ -677,12 +677,14 @@ static bool gpu_texture_check_capacity(
GPUTexture *tex, GLenum proxy, GLenum internalformat, GLenum data_format, GLenum data_type)
{
if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_WIN, GPU_DRIVER_ANY) ||
+ GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_MAC, GPU_DRIVER_OFFICIAL) ||
GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_UNIX, GPU_DRIVER_OFFICIAL)) {
/* Some AMD drivers have a faulty `GL_PROXY_TEXTURE_..` check.
* (see T55888, T56185, T59351).
* Checking with `GL_PROXY_TEXTURE_..` doesn't prevent `Out Of Memory` issue,
* it just states that the OGL implementation can support the texture.
- * So manually check the maximum size and maximum number of layers. */
+ * So manually check the maximum size and maximum number of layers.
+ * Same thing happens on Nvidia/macOS 10.15 (T78175). */
switch (proxy) {
case GL_PROXY_TEXTURE_2D_ARRAY:
if ((tex->d < 0) || (tex->d > GPU_max_texture_layers())) {
@@ -1772,8 +1774,13 @@ void GPU_texture_unbind(GPUTexture *tex)
void GPU_texture_unbind_all(void)
{
if (GLEW_ARB_multi_bind) {
- glBindTextures(0, GPU_max_textures(), NULL);
- glBindSamplers(0, GPU_max_textures(), NULL);
+ /* Some drivers crash because of the NULL array even if that's explicitly
+ * allowed by the spec... *sigh* (see T77549). */
+ GLuint texs[32] = {0};
+ int count = min_ii(32, GPU_max_textures());
+
+ glBindTextures(0, count, texs);
+ glBindSamplers(0, count, texs);
return;
}
diff --git a/source/blender/gpu/intern/gpu_vertex_buffer.c b/source/blender/gpu/intern/gpu_vertex_buffer.c
index 25daabe601d..3b4d469542c 100644
--- a/source/blender/gpu/intern/gpu_vertex_buffer.c
+++ b/source/blender/gpu/intern/gpu_vertex_buffer.c
@@ -85,6 +85,35 @@ void GPU_vertbuf_init_with_format_ex(GPUVertBuf *verts,
}
}
+GPUVertBuf *GPU_vertbuf_duplicate(GPUVertBuf *verts)
+{
+ GPUVertBuf *verts_dst = GPU_vertbuf_create(GPU_USAGE_STATIC);
+ /* Full copy. */
+ *verts_dst = *verts;
+ GPU_vertformat_copy(&verts_dst->format, &verts->format);
+
+ if (verts->vbo_id) {
+ uint buffer_sz = GPU_vertbuf_size_get(verts);
+
+ verts_dst->vbo_id = GPU_buf_alloc();
+
+ glBindBuffer(GL_COPY_READ_BUFFER, verts->vbo_id);
+ glBindBuffer(GL_COPY_WRITE_BUFFER, verts_dst->vbo_id);
+
+ glBufferData(GL_COPY_WRITE_BUFFER, buffer_sz, NULL, convert_usage_type_to_gl(verts->usage));
+
+ glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, buffer_sz);
+#if VRAM_USAGE
+ vbo_memory_usage += GPU_vertbuf_size_get(verts);
+#endif
+ }
+
+ if (verts->data) {
+ verts_dst->data = MEM_dupallocN(verts->data);
+ }
+ return verts_dst;
+}
+
/** Same as discard but does not free. */
void GPU_vertbuf_clear(GPUVertBuf *verts)
{
diff --git a/source/blender/gpu/intern/gpu_vertex_format.c b/source/blender/gpu/intern/gpu_vertex_format.c
index b84a7e0f554..585a22277b2 100644
--- a/source/blender/gpu/intern/gpu_vertex_format.c
+++ b/source/blender/gpu/intern/gpu_vertex_format.c
@@ -262,6 +262,20 @@ int GPU_vertformat_attr_id_get(const GPUVertFormat *format, const char *name)
return -1;
}
+void GPU_vertformat_attr_rename(GPUVertFormat *format, int attr_id, const char *new_name)
+{
+ BLI_assert(attr_id > -1 && attr_id < format->attr_len);
+ GPUVertAttr *attr = &format->attrs[attr_id];
+ char *attr_name = (char *)GPU_vertformat_attr_name_get(format, attr, 0);
+ BLI_assert(strlen(attr_name) == strlen(new_name));
+ int i = 0;
+ while (attr_name[i] != '\0') {
+ attr_name[i] = new_name[i];
+ i++;
+ }
+ attr->name_len = 1;
+}
+
/* Encode 8 original bytes into 11 safe bytes. */
static void safe_bytes(char out[11], const char data[8])
{
diff --git a/source/blender/gpu/shaders/gpu_shader_2D_edituvs_edges_frag.glsl b/source/blender/gpu/shaders/gpu_shader_2D_edituvs_edges_frag.glsl
new file mode 100644
index 00000000000..108fc85c4a5
--- /dev/null
+++ b/source/blender/gpu/shaders/gpu_shader_2D_edituvs_edges_frag.glsl
@@ -0,0 +1,30 @@
+
+
+uniform float dashWidth;
+
+#ifdef SMOOTH_COLOR
+noperspective in vec4 finalColor;
+#else
+flat in vec4 finalColor;
+#endif
+
+noperspective in vec2 stipple_pos;
+flat in vec2 stipple_start;
+
+out vec4 fragColor;
+
+void main()
+{
+ fragColor = finalColor;
+
+ /* Avoid passing viewport size */
+ vec2 dd = fwidth(stipple_pos);
+
+ float dist = distance(stipple_start, stipple_pos) / max(dd.x, dd.y);
+
+ if (fract(dist / dashWidth) > 0.5) {
+ fragColor.rgb = vec3(0.0);
+ }
+
+ fragColor = blender_srgb_to_framebuffer_space(fragColor);
+}
diff --git a/source/blender/gpu/shaders/gpu_shader_2D_edituvs_edges_vert.glsl b/source/blender/gpu/shaders/gpu_shader_2D_edituvs_edges_vert.glsl
index 02bbe545436..69fe5c93a61 100644
--- a/source/blender/gpu/shaders/gpu_shader_2D_edituvs_edges_vert.glsl
+++ b/source/blender/gpu/shaders/gpu_shader_2D_edituvs_edges_vert.glsl
@@ -12,6 +12,9 @@ noperspective out vec4 finalColor;
flat out vec4 finalColor;
#endif
+noperspective out vec2 stipple_pos;
+flat out vec2 stipple_start;
+
/* TODO: Port drawing to draw manager and
* remove constants duplications. */
#define VERT_UV_SELECT (1 << 3)
@@ -28,5 +31,8 @@ void main()
gl_Position = ModelViewProjectionMatrix * vec4(pos, 0.0, 1.0);
gl_Position.z = float(!is_select);
+ /* Avoid precision loss. */
+ stipple_start = stipple_pos = 500.0 + 500.0 * (gl_Position.xy / gl_Position.w);
+
finalColor = (is_select) ? selectColor : edgeColor;
}
diff --git a/source/blender/gpu/shaders/gpu_shader_2D_widget_base_frag.glsl b/source/blender/gpu/shaders/gpu_shader_2D_widget_base_frag.glsl
index bdc87baf924..21c7f79a57c 100644
--- a/source/blender/gpu/shaders/gpu_shader_2D_widget_base_frag.glsl
+++ b/source/blender/gpu/shaders/gpu_shader_2D_widget_base_frag.glsl
@@ -1,11 +1,67 @@
uniform vec3 checkerColorAndSize;
-noperspective in vec4 finalColor;
+noperspective in vec2 uvInterp;
noperspective in float butCo;
flat in float discardFac;
+flat in float shadeTri;
+flat in vec2 outRectSize;
+flat in vec4 outRoundCorners;
+noperspective in vec4 innerColor;
+flat in vec4 borderColor;
+flat in vec4 embossColor;
+flat in float lineWidth;
out vec4 fragColor;
+vec3 compute_masks(vec2 uv)
+{
+ bool upper_half = uv.y > outRectSize.y * 0.5;
+ bool right_half = uv.x > outRectSize.x * 0.5;
+ float corner_rad;
+
+ /* Correct aspect ratio for 2D views not using uniform scalling.
+ * uv is already in pixel space so a uniform scale should give us a ratio of 1. */
+ float ratio = (butCo != -2.0) ? (dFdy(uv.y) / dFdx(uv.x)) : 1.0;
+ vec2 uv_sdf = uv;
+ uv_sdf.x *= ratio;
+
+ if (right_half) {
+ uv_sdf.x = outRectSize.x * ratio - uv_sdf.x;
+ }
+ if (upper_half) {
+ uv_sdf.y = outRectSize.y - uv_sdf.y;
+ corner_rad = right_half ? outRoundCorners.z : outRoundCorners.w;
+ }
+ else {
+ corner_rad = right_half ? outRoundCorners.y : outRoundCorners.x;
+ }
+
+ /* Signed distance field from the corner (in pixel).
+ * inner_sdf is sharp and outer_sdf is rounded. */
+ uv_sdf -= corner_rad;
+ float inner_sdf = max(0.0, min(uv_sdf.x, uv_sdf.y));
+ float outer_sdf = -length(min(uv_sdf, 0.0));
+ float sdf = inner_sdf + outer_sdf + corner_rad;
+
+ /* Fade emboss at the border. */
+ float emboss_size = clamp((upper_half) ? 0.0 : (uv.x / corner_rad), 0.0, 1.0);
+
+ /* Clamp line width to be at least 1px wide. This can happen if the projection matrix
+ * has been scaled (i.e: Node editor)... */
+ float line_width = (lineWidth > 0.0) ? max(fwidth(uv.y), lineWidth) : 0.0;
+
+ const float aa_radius = 0.5;
+ vec3 masks;
+ masks.x = smoothstep(-aa_radius, aa_radius, sdf);
+ masks.y = smoothstep(-aa_radius, aa_radius, sdf - line_width);
+ masks.z = smoothstep(-aa_radius, aa_radius, sdf + line_width * emboss_size);
+
+ /* Compose masks together to avoid having too much alpha. */
+ masks.zx = max(vec2(0.0), masks.zx - masks.xy);
+
+ return masks;
+}
+
vec4 do_checkerboard()
{
float size = checkerColorAndSize.z;
@@ -25,16 +81,32 @@ void main()
discard;
}
- fragColor = finalColor;
-
- if (butCo > 0.5) {
- vec4 checker = do_checkerboard();
- fragColor = mix(checker, fragColor, fragColor.a);
- }
+ vec3 masks = compute_masks(uvInterp);
if (butCo > 0.0) {
+ /* Alpha checker widget. */
+ if (butCo > 0.5) {
+ vec4 checker = do_checkerboard();
+ fragColor = mix(checker, innerColor, innerColor.a);
+ }
+ else {
+ /* Set alpha to 1.0. */
+ fragColor = innerColor;
+ }
fragColor.a = 1.0;
}
+ else {
+ /* Premultiply here. */
+ fragColor = innerColor * vec4(innerColor.aaa, 1.0);
+ }
+ fragColor *= masks.y;
+ fragColor += masks.x * borderColor;
+ fragColor += masks.z * embossColor;
+
+ /* Un-premult because the blend equation is already doing the mult. */
+ if (fragColor.a > 0.0) {
+ fragColor.rgb /= fragColor.a;
+ }
fragColor = blender_srgb_to_framebuffer_space(fragColor);
}
diff --git a/source/blender/gpu/shaders/gpu_shader_2D_widget_base_vert.glsl b/source/blender/gpu/shaders/gpu_shader_2D_widget_base_vert.glsl
index d7cc851556b..2fd5effccce 100644
--- a/source/blender/gpu/shaders/gpu_shader_2D_widget_base_vert.glsl
+++ b/source/blender/gpu/shaders/gpu_shader_2D_widget_base_vert.glsl
@@ -1,127 +1,7 @@
-#define BIT_RANGE(x) uint((1 << x) - 1)
-
-/* 2 bits for corner */
-/* Attention! Not the same order as in UI_interface.h!
- * Ordered by drawing order. */
-#define BOTTOM_LEFT 0u
-#define BOTTOM_RIGHT 1u
-#define TOP_RIGHT 2u
-#define TOP_LEFT 3u
-#define CNR_FLAG_RANGE BIT_RANGE(2)
-
-/* 4bits for corner id */
-#define CORNER_VEC_OFS 2u
-#define CORNER_VEC_RANGE BIT_RANGE(4)
-const vec2 cornervec[9] = vec2[9](vec2(0.0, 1.0),
- vec2(0.02, 0.805),
- vec2(0.067, 0.617),
- vec2(0.169, 0.45),
- vec2(0.293, 0.293),
- vec2(0.45, 0.169),
- vec2(0.617, 0.076),
- vec2(0.805, 0.02),
- vec2(1.0, 0.0));
-
-/* 4bits for jitter id */
-#define JIT_OFS 6u
-#define JIT_RANGE BIT_RANGE(4)
-const vec2 jit[9] = vec2[9](vec2(0.468813, -0.481430),
- vec2(-0.155755, -0.352820),
- vec2(0.219306, -0.238501),
- vec2(-0.393286, -0.110949),
- vec2(-0.024699, 0.013908),
- vec2(0.343805, 0.147431),
- vec2(-0.272855, 0.269918),
- vec2(0.095909, 0.388710),
- vec2(0.0, 0.0));
-
-/* 2bits for other flags */
-#define INNER_FLAG uint(1 << 10) /* is inner vert */
-#define EMBOSS_FLAG uint(1 << 11) /* is emboss vert */
-
-/* 2bits for color */
-#define COLOR_OFS 12u
-#define COLOR_RANGE BIT_RANGE(2)
-#define COLOR_INNER 0u
-#define COLOR_EDGE 1u
-#define COLOR_EMBOSS 2u
-
-/* 2bits for trias type */
-#define TRIA_FLAG uint(1 << 14) /* is tria vert */
-#define TRIA_FIRST INNER_FLAG /* is first tria (reuse INNER_FLAG) */
-
-/* We can reuse the CORNER_* bits for tria */
-#define TRIA_VEC_RANGE BIT_RANGE(6)
-
-/* Some GPUs have performanse issues with this array being const (Doesn't fit in the registers?).
- * To resolve this issue, store the array as a uniform buffer.
- * (The array is still stored in the registry, but indexing is done in the uniform buffer.) */
-uniform vec2 triavec[43] = vec2[43](
-
- /* ROUNDBOX_TRIA_ARROWS */
- vec2(-0.170000, 0.400000),
- vec2(-0.050000, 0.520000),
- vec2(0.250000, 0.000000),
- vec2(0.470000, -0.000000),
- vec2(-0.170000, -0.400000),
- vec2(-0.050000, -0.520000),
- vec2(0.170000, 0.400000),
- vec2(0.050000, 0.520000),
- vec2(-0.250000, 0.000000),
- vec2(-0.470000, -0.000000),
- vec2(0.170000, -0.400000),
- vec2(0.050000, -0.520000),
-
- /* ROUNDBOX_TRIA_SCROLL - circle tria (triangle strip) */
- vec2(0.000000, 1.000000),
- vec2(0.382684, 0.923879),
- vec2(-0.382683, 0.923880),
- vec2(0.707107, 0.707107),
- vec2(-0.707107, 0.707107),
- vec2(0.923879, 0.382684),
- vec2(-0.923879, 0.382684),
- vec2(1.000000, 0.000000),
- vec2(-1.000000, 0.000000),
- vec2(0.923879, -0.382684),
- vec2(-0.923879, -0.382684),
- vec2(0.707107, -0.707107),
- vec2(-0.707107, -0.707107),
- vec2(0.382684, -0.923879),
- vec2(-0.382683, -0.923880),
- vec2(0.000000, -1.000000),
-
- /* ROUNDBOX_TRIA_MENU - menu arrows */
- vec2(-0.51, 0.07),
- vec2(-0.4, 0.18),
- vec2(-0.05, -0.39),
- vec2(-0.05, -0.17),
- vec2(0.41, 0.07),
- vec2(0.3, 0.18),
-
- /* ROUNDBOX_TRIA_CHECK - check mark */
- vec2(-0.67000, 0.020000),
- vec2(-0.500000, 0.190000),
- vec2(-0.130000, -0.520000),
- vec2(-0.130000, -0.170000),
- vec2(0.720000, 0.430000),
- vec2(0.530000, 0.590000),
-
-/* ROUNDBOX_TRIA_HOLD_ACTION_ARROW - hold action arrows */
-#define OX (-0.32)
-#define OY (0.1)
-#define SC (0.35 * 2)
- // vec2(-0.5 + SC, 1.0 + OY), vec2( 0.5, 1.0 + OY), vec2( 0.5, 0.0 + OY + SC),
- vec2((0.5 - SC) + OX, 1.0 + OY),
- vec2(-0.5 + OX, 1.0 + OY),
- vec2(-0.5 + OX, SC + OY)
-#undef OX
-#undef OY
-#undef SC
-);
uniform mat4 ModelViewProjectionMatrix;
-#define MAX_PARAM 11
+#define MAX_PARAM 12
#ifdef USE_INSTANCE
# define MAX_INSTANCE 6
uniform vec4 parameters[MAX_PARAM * MAX_INSTANCE];
@@ -147,105 +27,152 @@ uniform vec4 parameters[MAX_PARAM];
#define tria2Size parameters[gl_InstanceID * MAX_PARAM + 10].y
#define shadeDir parameters[gl_InstanceID * MAX_PARAM + 10].z
#define alphaDiscard parameters[gl_InstanceID * MAX_PARAM + 10].w
+#define triaType parameters[gl_InstanceID * MAX_PARAM + 11].x
/* We encode alpha check and discard factor together. */
#define doAlphaCheck (alphaDiscard < 0.0)
#define discardFactor abs(alphaDiscard)
-in uint vflag;
-
-noperspective out vec4 finalColor;
+noperspective out vec2 uvInterp;
+flat out vec2 outRectSize;
+flat out vec4 outRoundCorners;
+noperspective out vec4 innerColor;
+flat out vec4 borderColor;
+flat out vec4 embossColor;
+flat out float lineWidth;
noperspective out float butCo;
flat out float discardFac;
vec2 do_widget(void)
{
- uint cflag = vflag & CNR_FLAG_RANGE;
- uint vofs = (vflag >> CORNER_VEC_OFS) & CORNER_VEC_RANGE;
- bool is_inner = (vflag & INNER_FLAG) != 0u;
-
- vec2 v = cornervec[vofs];
- /* Scale by corner radius */
- v *= roundCorners[cflag] * ((is_inner) ? radsi : rads);
- /* Flip in the right direction and osition to corner */
- vec4 rct = (is_inner) ? recti : rect;
- if (cflag == BOTTOM_LEFT) {
- v += rct.xz;
- }
- else if (cflag == BOTTOM_RIGHT) {
- v = vec2(-v.y, v.x);
- v += rct.yz;
+ lineWidth = abs(rect.x - recti.x);
+ vec2 emboss_ofs = vec2(0.0, -lineWidth);
+ vec2 v_pos[4] = vec2[4](rect.xz + emboss_ofs, rect.xw, rect.yz + emboss_ofs, rect.yw);
+ vec2 pos = v_pos[gl_VertexID];
+
+ uvInterp = pos - rect.xz;
+ outRectSize = rect.yw - rect.xz;
+ outRoundCorners = rads * roundCorners;
+
+ vec2 uv = uvInterp / outRectSize;
+ float fac = clamp((shadeDir > 0.0) ? uv.y : uv.x, 0.0, 1.0);
+ /* Note innerColor is premultiplied inside the fragment shader. */
+ if (doAlphaCheck) {
+ innerColor = colorInner1;
+ butCo = uv.x;
}
- else if (cflag == TOP_RIGHT) {
- v = -v;
- v += rct.yw;
- }
- else /* (cflag == TOP_LEFT) */ {
- v = vec2(v.y, -v.x);
- v += rct.xw;
- }
-
- vec2 uv = faci * (v - recti.xz);
-
- /* compute uv and color gradient */
- uint color_id = (vflag >> COLOR_OFS) & COLOR_RANGE;
- if (color_id == COLOR_INNER) {
- float fac = clamp((shadeDir > 0.0) ? uv.y : uv.x, 0.0, 1.0);
-
- if (doAlphaCheck) {
- finalColor = colorInner1;
- butCo = uv.x;
- }
- else {
- finalColor = mix(colorInner2, colorInner1, fac);
- butCo = -abs(uv.x);
- }
- }
- else if (color_id == COLOR_EDGE) {
- finalColor = colorEdge;
- butCo = -abs(uv.x);
- }
- else /* (color_id == COLOR_EMBOSS) */ {
- finalColor = colorEmboss;
+ else {
+ innerColor = mix(colorInner2, colorInner1, fac);
butCo = -abs(uv.x);
}
- bool is_emboss = (vflag & EMBOSS_FLAG) != 0u;
- v.y -= (is_emboss) ? (recti.z - rect.z) : 0.0;
+ /* We need premultiplied color for transparency. */
+ borderColor = colorEdge * vec4(colorEdge.aaa, 1.0);
+ embossColor = colorEmboss * vec4(colorEmboss.aaa, 1.0);
- return v;
+ return pos;
}
vec2 do_tria()
{
- uint vofs = vflag & TRIA_VEC_RANGE;
+ int vidx = gl_VertexID % 4;
+ bool tria2 = gl_VertexID > 7;
+
+ vec2 pos;
+ float size = (tria2) ? -tria2Size : tria1Size;
+ vec2 center = (tria2) ? tria2Center : tria1Center;
+
+ vec2 arrow_pos[4] = vec2[4](vec2(0.0, 0.6), vec2(0.6, 0.0), vec2(-0.6, 0.0), vec2(0.0, -0.6));
+ /* Rotated uv space by 45deg and mirrored. */
+ vec2 arrow_uvs[4] = vec2[4](vec2(0.0, 0.85), vec2(0.85, 0.85), vec2(0.0, 0.0), vec2(0.0, 0.85));
+
+ vec2 point_pos[4] = vec2[4](vec2(-1.0, -1.0), vec2(-1.0, 1.0), vec2(1.0, -1.0), vec2(1.0, 1.0));
+ vec2 point_uvs[4] = vec2[4](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 0.0), vec2(1.0, 1.0));
+
+ /* We reuse the SDF roundbox rendering of widget to render the tria shapes.
+ * This means we do clever tricks to position the rectangle the way we want using
+ * the 2 triangles uvs. */
+ if (triaType == 0.0) {
+ /* ROUNDBOX_TRIA_NONE */
+ outRectSize = uvInterp = pos = vec2(0);
+ outRoundCorners = vec4(0.01);
+ }
+ else if (triaType == 1.0) {
+ /* ROUNDBOX_TRIA_ARROWS */
+ pos = arrow_pos[vidx];
+ uvInterp = arrow_uvs[vidx];
+ uvInterp -= vec2(0.05, 0.63); /* Translate */
+ outRectSize = vec2(0.74, 0.17);
+ outRoundCorners = vec4(0.08);
+ }
+ else if (triaType == 2.0) {
+ /* ROUNDBOX_TRIA_SCROLL */
+ pos = point_pos[vidx];
+ uvInterp = point_uvs[vidx];
+ outRectSize = vec2(1.0);
+ outRoundCorners = vec4(0.5);
+ }
+ else if (triaType == 3.0) {
+ /* ROUNDBOX_TRIA_MENU */
+ pos = tria2 ? vec2(0.0) : arrow_pos[vidx]; /* Solo tria */
+ pos = vec2(pos.y, -pos.x); /* Rotate */
+ pos += vec2(-0.05, 0.0); /* Translate */
+ size *= 0.8; /* Scale */
+ uvInterp = arrow_uvs[vidx];
+ uvInterp -= vec2(0.05, 0.63); /* Translate */
+ outRectSize = vec2(0.74, 0.17);
+ outRoundCorners = vec4(0.01);
+ }
+ else if (triaType == 4.0) {
+ /* ROUNDBOX_TRIA_CHECK */
+ /* A bit more hacky: We use the two trias joined together to render
+ * both sides of the checkmark with different length. */
+ pos = arrow_pos[min(vidx, 2)]; /* Only keep 1 triangle. */
+ pos.y = tria2 ? -pos.y : pos.y; /* Mirror along X */
+ pos = pos.x * vec2(0.0872, -0.996) + pos.y * vec2(0.996, 0.0872); /* Rotate (85deg) */
+ pos += vec2(-0.1, 0.2); /* Translate */
+ center = tria1Center;
+ size = tria1Size * 1.7; /* Scale */
+ uvInterp = arrow_uvs[vidx];
+ uvInterp -= tria2 ? vec2(0.4, 0.65) : vec2(0.08, 0.65); /* Translate */
+ outRectSize = vec2(0.74, 0.14);
+ outRoundCorners = vec4(0.01);
+ }
+ else {
+ /* ROUNDBOX_TRIA_HOLD_ACTION_ARROW */
+ /* We use a single triangle to cut the round rect in half. The edge will not be Antialiased. */
+ pos = tria2 ? vec2(0.0) : arrow_pos[min(vidx, 2)]; /* Only keep 1 triangle. */
+ pos = pos.x * vec2(0.707, 0.707) + pos.y * vec2(-0.707, 0.707); /* Rotate (45deg) */
+ pos += vec2(-1.7, 2.4); /* Translate (hardcoded, might want to remove) */
+ size *= 0.4; /* Scale */
+ uvInterp = arrow_uvs[vidx];
+ uvInterp -= vec2(0.05, 0.05); /* Translate */
+ outRectSize = vec2(0.75);
+ outRoundCorners = vec4(0.01);
+ }
- vec2 v = triavec[vofs];
+ uvInterp *= abs(size);
+ outRectSize *= abs(size);
+ outRoundCorners *= abs(size);
- finalColor = colorTria;
- butCo = -1.0;
+ pos = pos * size + center;
- bool is_tria_first = (vflag & TRIA_FIRST) != 0u;
+ innerColor = colorTria * vec4(colorTria.aaa, 1.0);
- if (is_tria_first) {
- v = v * tria1Size + tria1Center;
- }
- else {
- v = v * tria2Size + tria2Center;
- }
+ lineWidth = 0.0;
+ borderColor = vec4(0.0);
+ embossColor = vec4(0.0);
- return v;
+ butCo = -2.0;
+
+ return pos;
}
void main()
{
discardFac = discardFactor;
- bool is_tria = (vflag & TRIA_FLAG) != 0u;
-
- vec2 v = (is_tria) ? do_tria() : do_widget();
-
- /* Antialiasing offset */
- v += jit[(vflag >> JIT_OFS) & JIT_RANGE];
+ bool is_tria = (gl_VertexID > 3);
+ vec2 pos = (is_tria) ? do_tria() : do_widget();
- gl_Position = ModelViewProjectionMatrix * vec4(v, 0.0, 1.0);
+ gl_Position = ModelViewProjectionMatrix * vec4(pos, 0.0, 1.0);
}
diff --git a/source/blender/gpu/shaders/gpu_shader_point_varying_color_varying_outline_aa_frag.glsl b/source/blender/gpu/shaders/gpu_shader_point_varying_color_varying_outline_aa_frag.glsl
index f30e292154e..6d997ec14cc 100644
--- a/source/blender/gpu/shaders/gpu_shader_point_varying_color_varying_outline_aa_frag.glsl
+++ b/source/blender/gpu/shaders/gpu_shader_point_varying_color_varying_outline_aa_frag.glsl
@@ -29,4 +29,6 @@ void main()
else {
fragColor = mix(fillColor, outlineColor, smoothstep(radii[3], radii[2], dist));
}
+
+ fragColor = blender_srgb_to_framebuffer_space(fragColor);
}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_ambient_occlusion.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_ambient_occlusion.glsl
index 8f8ebebb5f1..d6d6fbab971 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_ambient_occlusion.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_ambient_occlusion.glsl
@@ -9,5 +9,5 @@ void node_ambient_occlusion(
}
#else
/* Stub ambient occlusion because it is not compatible with volumetrics. */
-# define node_ambient_occlusion
+# define node_ambient_occlusion(a, b, c, d, e) (e = CLOSURE_DEFAULT)
#endif
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_anisotropic.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_anisotropic.glsl
index a8a900b40c6..e1137d9d0e7 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_anisotropic.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_anisotropic.glsl
@@ -11,5 +11,5 @@ void node_bsdf_anisotropic(vec4 color,
}
#else
/* Stub anisotropic because it is not compatible with volumetrics. */
-# define node_bsdf_anisotropic
+# define node_bsdf_anisotropic(a, b, c, d, e, f, g) (g = CLOSURE_DEFAULT)
#endif
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_diffuse.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_diffuse.glsl
index 714792489f6..d7b6143d2a1 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_diffuse.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_diffuse.glsl
@@ -9,5 +9,5 @@ void node_bsdf_diffuse(vec4 color, float roughness, vec3 N, out Closure result)
}
#else
/* Stub diffuse because it is not compatible with volumetrics. */
-# define node_bsdf_diffuse
+# define node_bsdf_diffuse(a, b, c, d) (d = CLOSURE_DEFAULT)
#endif
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_eevee_specular.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_eevee_specular.glsl
index 747395857ee..443bab7529b 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_eevee_specular.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_eevee_specular.glsl
@@ -40,4 +40,7 @@ void node_eevee_specular(vec4 diffuse,
closure_load_ssr_data(ssr_spec * alpha, roughness, normal, viewCameraVec, int(ssr_id), result);
}
+#else
+/* Stub specular because it is not compatible with volumetrics. */
+# define node_eevee_specular(a, b, c, d, e, f, g, h, i, j, k, result) (result = CLOSURE_DEFAULT)
#endif
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_glass.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_glass.glsl
index ece770f0e73..6d782ff18de 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_glass.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_glass.glsl
@@ -28,5 +28,5 @@ void node_bsdf_glass(
}
#else
/* Stub glass because it is not compatible with volumetrics. */
-# define node_bsdf_glass
+# define node_bsdf_glass(a, b, c, d, e, f) (f = CLOSURE_DEFAULT)
#endif
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_glossy.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_glossy.glsl
index 7513c3a4edb..5ea22f3e0b4 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_glossy.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_glossy.glsl
@@ -12,5 +12,5 @@ void node_bsdf_glossy(vec4 color, float roughness, vec3 N, float ssr_id, out Clo
}
#else
/* Stub glossy because it is not compatible with volumetrics. */
-# define node_bsdf_glossy
+# define node_bsdf_glossy(a, b, c, d, e) (e = CLOSURE_DEFAULT)
#endif
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl
index 3c85dc6456c..80ed4e1ef69 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl
@@ -442,11 +442,13 @@ void node_bsdf_principled_glass(vec4 base_color,
result.transmittance = vec3(1.0 - alpha);
}
#else
+/* clang-format off */
/* Stub principled because it is not compatible with volumetrics. */
-# define node_bsdf_principled
-# define node_bsdf_principled_dielectric
-# define node_bsdf_principled_metallic
-# define node_bsdf_principled_clearcoat
-# define node_bsdf_principled_subsurface
-# define node_bsdf_principled_glass
+# define node_bsdf_principled(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, result) (result = CLOSURE_DEFAULT)
+# define node_bsdf_principled_dielectric(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, result) (result = CLOSURE_DEFAULT)
+# define node_bsdf_principled_metallic(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, result) (result = CLOSURE_DEFAULT)
+# define node_bsdf_principled_clearcoat(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, result) (result = CLOSURE_DEFAULT)
+# define node_bsdf_principled_subsurface(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, result) (result = CLOSURE_DEFAULT)
+# define node_bsdf_principled_glass(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, result) (result = CLOSURE_DEFAULT)
+/* clang-format on */
#endif
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_refraction.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_refraction.glsl
index 4088d6db06a..cd043020a7f 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_refraction.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_refraction.glsl
@@ -12,5 +12,5 @@ void node_bsdf_refraction(vec4 color, float roughness, float ior, vec3 N, out Cl
}
#else
/* Stub refraction because it is not compatible with volumetrics. */
-# define node_bsdf_refraction
+# define node_bsdf_refraction(a, b, c, d, e) (e = CLOSURE_DEFAULT)
#endif
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl
index 9bbbe71b206..c13b55513da 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl
@@ -24,5 +24,5 @@ void node_subsurface_scattering(vec4 color,
}
#else
/* Stub subsurface scattering because it is not compatible with volumetrics. */
-# define node_subsurface_scattering
+# define node_subsurface_scattering(a, b, c, d, e, f, g, h) (h = CLOSURE_DEFAULT)
#endif
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_toon.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_toon.glsl
index 02d288d42bf..bbfc99ccc73 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_toon.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_toon.glsl
@@ -5,5 +5,5 @@ void node_bsdf_toon(vec4 color, float size, float tsmooth, vec3 N, out Closure r
}
#else
/* Stub toon because it is not compatible with volumetrics. */
-# define node_bsdf_toon
+# define node_bsdf_toon(a, b, c, d, e) (e = CLOSURE_DEFAULT)
#endif
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_translucent.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_translucent.glsl
index 5c3ed81410a..79bfd9b24bb 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_translucent.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_translucent.glsl
@@ -9,5 +9,5 @@ void node_bsdf_translucent(vec4 color, vec3 N, out Closure result)
}
#else
/* Stub translucent because it is not compatible with volumetrics. */
-# define node_bsdf_translucent
+# define node_bsdf_translucent(a, b, c) (c = CLOSURE_DEFAULT)
#endif
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_transparent.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_transparent.glsl
index 800d0f81d4a..9040f62bd3f 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_transparent.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_transparent.glsl
@@ -7,5 +7,5 @@ void node_bsdf_transparent(vec4 color, out Closure result)
}
#else
/* Stub transparent because it is not compatible with volumetrics. */
-# define node_bsdf_transparent
+# define node_bsdf_transparent(a, b) (b = CLOSURE_DEFAULT)
#endif
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_velvet.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_velvet.glsl
index 9646ffff8ca..989f18b881a 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_velvet.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_velvet.glsl
@@ -5,5 +5,5 @@ void node_bsdf_velvet(vec4 color, float sigma, vec3 N, out Closure result)
}
#else
/* Stub velvet because it is not compatible with volumetrics. */
-# define node_bsdf_velvet
+# define node_bsdf_velvet(a, b, c, d) (d = CLOSURE_DEFAULT)
#endif
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_wireframe.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_wireframe.glsl
index 2fcf1b8d914..e2789e046e1 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_wireframe.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_wireframe.glsl
@@ -26,6 +26,6 @@ void node_wireframe_screenspace(float size, vec2 barycentric, out float fac)
}
#else
/* Stub wireframe because it is not compatible with volumetrics. */
-# define node_wireframe
-# define node_wireframe_screenspace
+# define node_wireframe(a, b, c, d) (d = 0.0)
+# define node_wireframe_screenspace(a, b, c) (c = 0.0)
#endif
diff --git a/source/blender/io/CMakeLists.txt b/source/blender/io/CMakeLists.txt
index bc2f8d628e2..360cacc4360 100644
--- a/source/blender/io/CMakeLists.txt
+++ b/source/blender/io/CMakeLists.txt
@@ -18,6 +18,8 @@
# All rights reserved.
# ***** END GPL LICENSE BLOCK *****
+add_subdirectory(common)
+
if(WITH_ALEMBIC)
add_subdirectory(alembic)
endif()
diff --git a/source/blender/io/alembic/ABC_alembic.h b/source/blender/io/alembic/ABC_alembic.h
index ba430752b29..ddf75aa3258 100644
--- a/source/blender/io/alembic/ABC_alembic.h
+++ b/source/blender/io/alembic/ABC_alembic.h
@@ -13,14 +13,12 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#pragma once
/** \file
* \ingroup balembic
*/
-#ifndef __ABC_ALEMBIC_H__
-#define __ABC_ALEMBIC_H__
-
#ifdef __cplusplus
extern "C" {
#endif
@@ -133,5 +131,3 @@ struct CacheReader *CacheReader_open_alembic_object(struct AbcArchiveHandle *han
#ifdef __cplusplus
}
#endif
-
-#endif /* __ABC_ALEMBIC_H__ */
diff --git a/source/blender/io/alembic/CMakeLists.txt b/source/blender/io/alembic/CMakeLists.txt
index 16f2d944876..da36272b850 100644
--- a/source/blender/io/alembic/CMakeLists.txt
+++ b/source/blender/io/alembic/CMakeLists.txt
@@ -20,6 +20,7 @@
set(INC
.
+ ../common
../../blenkernel
../../blenlib
../../blenloader
@@ -29,6 +30,7 @@ set(INC
../../makesdna
../../makesrna
../../windowmanager
+ ../../../../intern/clog
../../../../intern/guardedalloc
../../../../intern/utfconv
)
@@ -42,7 +44,6 @@ set(INC_SYS
set(SRC
intern/abc_axis_conversion.cc
intern/abc_customdata.cc
- intern/abc_exporter.cc
intern/abc_reader_archive.cc
intern/abc_reader_camera.cc
intern/abc_reader_curves.cc
@@ -52,22 +53,25 @@ set(SRC
intern/abc_reader_points.cc
intern/abc_reader_transform.cc
intern/abc_util.cc
- intern/abc_writer_archive.cc
- intern/abc_writer_camera.cc
- intern/abc_writer_curves.cc
- intern/abc_writer_hair.cc
- intern/abc_writer_mball.cc
- intern/abc_writer_mesh.cc
- intern/abc_writer_nurbs.cc
- intern/abc_writer_object.cc
- intern/abc_writer_points.cc
- intern/abc_writer_transform.cc
intern/alembic_capi.cc
+ exporter/abc_archive.cc
+ exporter/abc_export_capi.cc
+ exporter/abc_hierarchy_iterator.cc
+ exporter/abc_subdiv_disabler.cc
+ exporter/abc_writer_abstract.cc
+ exporter/abc_writer_camera.cc
+ exporter/abc_writer_curves.cc
+ exporter/abc_writer_hair.cc
+ exporter/abc_writer_mesh.cc
+ exporter/abc_writer_mball.cc
+ exporter/abc_writer_nurbs.cc
+ exporter/abc_writer_points.cc
+ exporter/abc_writer_transform.cc
+
ABC_alembic.h
intern/abc_axis_conversion.h
intern/abc_customdata.h
- intern/abc_exporter.h
intern/abc_reader_archive.h
intern/abc_reader_camera.h
intern/abc_reader_curves.h
@@ -77,21 +81,25 @@ set(SRC
intern/abc_reader_points.h
intern/abc_reader_transform.h
intern/abc_util.h
- intern/abc_writer_archive.h
- intern/abc_writer_camera.h
- intern/abc_writer_curves.h
- intern/abc_writer_hair.h
- intern/abc_writer_mball.h
- intern/abc_writer_mesh.h
- intern/abc_writer_nurbs.h
- intern/abc_writer_object.h
- intern/abc_writer_points.h
- intern/abc_writer_transform.h
+
+ exporter/abc_archive.h
+ exporter/abc_hierarchy_iterator.h
+ exporter/abc_subdiv_disabler.h
+ exporter/abc_writer_abstract.h
+ exporter/abc_writer_camera.h
+ exporter/abc_writer_curves.h
+ exporter/abc_writer_hair.h
+ exporter/abc_writer_mesh.h
+ exporter/abc_writer_mball.h
+ exporter/abc_writer_nurbs.h
+ exporter/abc_writer_points.h
+ exporter/abc_writer_transform.h
)
set(LIB
bf_blenkernel
bf_blenlib
+ bf_io_common
${ALEMBIC_LIBRARIES}
${OPENEXR_LIBRARIES}
diff --git a/source/blender/io/alembic/exporter/abc_archive.cc b/source/blender/io/alembic/exporter/abc_archive.cc
new file mode 100644
index 00000000000..5fbf74f0705
--- /dev/null
+++ b/source/blender/io/alembic/exporter/abc_archive.cc
@@ -0,0 +1,265 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+
+#include "abc_archive.h"
+
+#include "BKE_blender_version.h"
+#include "BKE_main.h"
+#include "BKE_scene.h"
+
+#include "DEG_depsgraph_query.h"
+
+#include "DNA_scene_types.h"
+
+#include <Alembic/AbcCoreOgawa/All.h>
+#include <Alembic/AbcGeom/All.h>
+
+#ifdef WIN32
+# include "BLI_path_util.h"
+# include "BLI_string.h"
+
+# include "utfconv.h"
+#endif
+
+namespace blender {
+namespace io {
+namespace alembic {
+
+using Alembic::Abc::ErrorHandler;
+using Alembic::Abc::kWrapExisting;
+using Alembic::Abc::MetaData;
+using Alembic::Abc::OArchive;
+using Alembic::Abc::TimeSampling;
+using Alembic::Abc::TimeSamplingPtr;
+using Alembic::Abc::TimeSamplingType;
+
+static MetaData create_abc_metadata(const Main *bmain, double scene_fps)
+{
+ MetaData abc_metadata;
+
+ std::string abc_user_description(bmain->name);
+ if (abc_user_description.empty()) {
+ abc_user_description = "unknown";
+ }
+
+ abc_metadata.set(Alembic::Abc::kApplicationNameKey, "Blender");
+ abc_metadata.set(Alembic::Abc::kUserDescriptionKey, abc_user_description);
+ abc_metadata.set("blender_version", std::string("v") + BKE_blender_version_string());
+ abc_metadata.set("FramesPerTimeUnit", std::to_string(scene_fps));
+
+ time_t raw_time;
+ time(&raw_time);
+ char buffer[128];
+
+#if defined _WIN32 || defined _WIN64
+ ctime_s(buffer, 128, &raw_time);
+#else
+ ctime_r(&raw_time, buffer);
+#endif
+
+ const std::size_t buffer_len = strlen(buffer);
+ if (buffer_len > 0 && buffer[buffer_len - 1] == '\n') {
+ buffer[buffer_len - 1] = '\0';
+ }
+
+ abc_metadata.set(Alembic::Abc::kDateWrittenKey, buffer);
+ return abc_metadata;
+}
+
+static OArchive *create_archive(std::ofstream *abc_ostream,
+ const std::string &filename,
+ MetaData &abc_metadata)
+{
+ /* Use stream to support unicode character paths on Windows. */
+#ifdef WIN32
+ char filename_cstr[FILE_MAX];
+ BLI_strncpy(filename_cstr, filename.c_str(), FILE_MAX);
+
+ UTF16_ENCODE(filename_cstr);
+ std::wstring wstr(filename_cstr_16);
+ abc_ostream->open(wstr.c_str(), std::ios::out | std::ios::binary);
+ UTF16_UN_ENCODE(filename_cstr);
+#else
+ abc_ostream->open(filename, std::ios::out | std::ios::binary);
+#endif
+
+ ErrorHandler::Policy policy = ErrorHandler::kThrowPolicy;
+
+ Alembic::AbcCoreOgawa::WriteArchive archive_writer;
+ return new OArchive(archive_writer(abc_ostream, abc_metadata), kWrapExisting, policy);
+}
+
+/* Construct list of shutter samples.
+ *
+ * These are taken from the interval [shutter open, shutter close),
+ * uniformly sampled with 'nr_of_samples' samples.
+ *
+ * TODO(Sybren): test that the above interval is indeed half-open.
+ *
+ * If 'time_relative' is true, samples are returned as time (in seconds) from params.frame_start.
+ * If 'time_relative' is false, samples are returned as fractional frames from 0.
+ * */
+static void get_shutter_samples(double scene_fps,
+ const AlembicExportParams &params,
+ int nr_of_samples,
+ bool time_relative,
+ std::vector<double> &r_samples)
+{
+ int frame_offset = time_relative ? params.frame_start : 0;
+ double time_factor = time_relative ? scene_fps : 1.0;
+ double shutter_open = params.shutter_open;
+ double shutter_close = params.shutter_close;
+ double time_inc = (shutter_close - shutter_open) / nr_of_samples;
+
+ /* sample between shutter open & close */
+ for (int sample = 0; sample < nr_of_samples; sample++) {
+ double sample_time = shutter_open + time_inc * sample;
+ double time = (frame_offset + sample_time) / time_factor;
+
+ r_samples.push_back(time);
+ }
+}
+
+static TimeSamplingPtr create_time_sampling(double scene_fps,
+ const AlembicExportParams &params,
+ int nr_of_samples)
+{
+ std::vector<double> samples;
+
+ if (params.frame_start == params.frame_end) {
+ return TimeSamplingPtr(new TimeSampling());
+ }
+
+ get_shutter_samples(scene_fps, params, nr_of_samples, true, samples);
+
+ TimeSamplingType ts(static_cast<uint32_t>(samples.size()), 1.0 / scene_fps);
+ return TimeSamplingPtr(new TimeSampling(ts, samples));
+}
+
+static void get_frames(double scene_fps,
+ const AlembicExportParams &params,
+ unsigned int nr_of_samples,
+ std::set<double> &r_frames)
+{
+ /* Get one set of shutter samples, then add those around each frame to export. */
+ std::vector<double> shutter_samples;
+ get_shutter_samples(scene_fps, params, nr_of_samples, false, shutter_samples);
+
+ for (double frame = params.frame_start; frame <= params.frame_end; frame += 1.0) {
+ for (size_t j = 0; j < nr_of_samples; j++) {
+ r_frames.insert(frame + shutter_samples[j]);
+ }
+ }
+}
+
+/* ****************************************************************** */
+
+ABCArchive::ABCArchive(const Main *bmain,
+ const Scene *scene,
+ AlembicExportParams params,
+ std::string filename)
+ : archive(nullptr)
+{
+ double scene_fps = FPS;
+ MetaData abc_metadata = create_abc_metadata(bmain, scene_fps);
+
+ // Create the Archive.
+ archive = create_archive(&abc_ostream_, filename, abc_metadata);
+
+ // Create time samples for transforms and shapes.
+ TimeSamplingPtr ts_xform;
+ TimeSamplingPtr ts_shapes;
+
+ ts_xform = create_time_sampling(scene_fps, params, params.frame_samples_xform);
+ time_sampling_index_transforms_ = archive->addTimeSampling(*ts_xform);
+
+ const bool export_animation = params.frame_start != params.frame_end;
+ if (!export_animation || params.frame_samples_shape == params.frame_samples_xform) {
+ ts_shapes = ts_xform;
+ time_sampling_index_shapes_ = time_sampling_index_transforms_;
+ }
+ else {
+ ts_shapes = create_time_sampling(scene_fps, params, params.frame_samples_shape);
+ time_sampling_index_shapes_ = archive->addTimeSampling(*ts_shapes);
+ }
+
+ // Construct the frames to export.
+ get_frames(scene_fps, params, params.frame_samples_xform, xform_frames_);
+ get_frames(scene_fps, params, params.frame_samples_shape, shape_frames_);
+
+ // Merge all frames to get the final set of frames to export.
+ export_frames_.insert(xform_frames_.begin(), xform_frames_.end());
+ export_frames_.insert(shape_frames_.begin(), shape_frames_.end());
+
+ abc_archive_bbox_ = Alembic::AbcGeom::CreateOArchiveBounds(*archive,
+ time_sampling_index_transforms_);
+}
+
+ABCArchive::~ABCArchive()
+{
+ delete archive;
+}
+
+uint32_t ABCArchive::time_sampling_index_transforms() const
+{
+ return time_sampling_index_transforms_;
+}
+
+uint32_t ABCArchive::time_sampling_index_shapes() const
+{
+ return time_sampling_index_shapes_;
+}
+
+ABCArchive::Frames::const_iterator ABCArchive::frames_begin() const
+{
+ return export_frames_.begin();
+}
+ABCArchive::Frames::const_iterator ABCArchive::frames_end() const
+{
+ return export_frames_.end();
+}
+size_t ABCArchive::total_frame_count() const
+{
+ return export_frames_.size();
+}
+
+bool ABCArchive::is_xform_frame(double frame) const
+{
+ return xform_frames_.find(frame) != xform_frames_.end();
+}
+bool ABCArchive::is_shape_frame(double frame) const
+{
+ return shape_frames_.find(frame) != shape_frames_.end();
+}
+ExportSubset ABCArchive::export_subset_for_frame(double frame) const
+{
+ ExportSubset subset;
+ subset.transforms = is_xform_frame(frame);
+ subset.shapes = is_shape_frame(frame);
+ return subset;
+}
+
+void ABCArchive::update_bounding_box(const Imath::Box3d &bounds)
+{
+ abc_archive_bbox_.set(bounds);
+}
+
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/exporter/abc_archive.h b/source/blender/io/alembic/exporter/abc_archive.h
new file mode 100644
index 00000000000..43d0acf2520
--- /dev/null
+++ b/source/blender/io/alembic/exporter/abc_archive.h
@@ -0,0 +1,87 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup Alembic
+ */
+
+#pragma once
+
+#include "ABC_alembic.h"
+#include "IO_abstract_hierarchy_iterator.h"
+
+#include <Alembic/Abc/OArchive.h>
+#include <Alembic/Abc/OTypedScalarProperty.h>
+
+#include <fstream>
+#include <set>
+#include <string>
+
+struct Main;
+struct Scene;
+
+namespace blender {
+namespace io {
+namespace alembic {
+
+/* Container for an Alembic archive and time sampling info.
+ *
+ * Constructor arguments are used to create the correct output stream and to set the archive's
+ * metadata. */
+class ABCArchive {
+ public:
+ typedef std::set<double> Frames;
+
+ Alembic::Abc::OArchive *archive;
+
+ ABCArchive(const Main *bmain,
+ const Scene *scene,
+ AlembicExportParams params,
+ std::string filename);
+ ~ABCArchive();
+
+ uint32_t time_sampling_index_transforms() const;
+ uint32_t time_sampling_index_shapes() const;
+
+ Frames::const_iterator frames_begin() const;
+ Frames::const_iterator frames_end() const;
+ size_t total_frame_count() const;
+
+ bool is_xform_frame(double frame) const;
+ bool is_shape_frame(double frame) const;
+
+ ExportSubset export_subset_for_frame(double frame) const;
+
+ void update_bounding_box(const Imath::Box3d &bounds);
+
+ private:
+ std::ofstream abc_ostream_;
+ uint32_t time_sampling_index_transforms_;
+ uint32_t time_sampling_index_shapes_;
+
+ Frames xform_frames_;
+ Frames shape_frames_;
+ Frames export_frames_;
+
+ Alembic::Abc::OBox3dProperty abc_archive_bbox_;
+};
+
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/exporter/abc_export_capi.cc b/source/blender/io/alembic/exporter/abc_export_capi.cc
new file mode 100644
index 00000000000..fbc5b2d5c02
--- /dev/null
+++ b/source/blender/io/alembic/exporter/abc_export_capi.cc
@@ -0,0 +1,220 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+
+#include "ABC_alembic.h"
+#include "abc_archive.h"
+#include "abc_hierarchy_iterator.h"
+#include "abc_subdiv_disabler.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_build.h"
+#include "DEG_depsgraph_query.h"
+
+#include "DNA_modifier_types.h"
+#include "DNA_scene_types.h"
+
+#include "BKE_blender_version.h"
+#include "BKE_context.h"
+#include "BKE_global.h"
+#include "BKE_main.h"
+#include "BKE_scene.h"
+
+#include "BLI_fileops.h"
+#include "BLI_path_util.h"
+#include "BLI_string.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "CLG_log.h"
+static CLG_LogRef LOG = {"io.alembic"};
+
+#include <algorithm>
+
+struct ExportJobData {
+ Main *bmain;
+ Depsgraph *depsgraph;
+ wmWindowManager *wm;
+
+ char filename[FILE_MAX];
+ AlembicExportParams params;
+
+ bool was_canceled;
+ bool export_ok;
+};
+
+namespace blender {
+namespace io {
+namespace alembic {
+
+// Construct the depsgraph for exporting.
+static void build_depsgraph(Depsgraph *depsgraph, Main *bmain)
+{
+ Scene *scene = DEG_get_input_scene(depsgraph);
+ ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph);
+ DEG_graph_build_from_view_layer(depsgraph, bmain, scene, view_layer);
+}
+
+static void export_startjob(void *customdata, short *stop, short *do_update, float *progress)
+{
+ ExportJobData *data = static_cast<ExportJobData *>(customdata);
+ data->was_canceled = false;
+
+ G.is_rendering = true;
+ WM_set_locked_interface(data->wm, true);
+ G.is_break = false;
+
+ *progress = 0.0f;
+ *do_update = true;
+
+ build_depsgraph(data->depsgraph, data->bmain);
+ SubdivModifierDisabler subdiv_disabler(data->depsgraph);
+ if (!data->params.apply_subdiv) {
+ subdiv_disabler.disable_modifiers();
+ }
+ BKE_scene_graph_update_tagged(data->depsgraph, data->bmain);
+
+ // For restoring the current frame after exporting animation is done.
+ Scene *scene = DEG_get_input_scene(data->depsgraph);
+ const int orig_frame = CFRA;
+ const bool export_animation = (data->params.frame_start != data->params.frame_end);
+
+ // Create the Alembic archive.
+ ABCArchive abc_archive(data->bmain, scene, data->params, std::string(data->filename));
+
+ ABCHierarchyIterator iter(data->depsgraph, &abc_archive, data->params);
+
+ if (export_animation) {
+ CLOG_INFO(&LOG, 2, "Exporting animation");
+
+ // Writing the animated frames is not 100% of the work, but it's our best guess.
+ const float progress_per_frame = 1.0f / std::max(size_t(1), abc_archive.total_frame_count());
+ ABCArchive::Frames::const_iterator frame_it = abc_archive.frames_begin();
+ const ABCArchive::Frames::const_iterator frames_end = abc_archive.frames_end();
+
+ for (; frame_it != frames_end; frame_it++) {
+ double frame = *frame_it;
+
+ if (G.is_break || (stop != nullptr && *stop)) {
+ break;
+ }
+
+ // Update the scene for the next frame to render.
+ scene->r.cfra = static_cast<int>(frame);
+ scene->r.subframe = frame - scene->r.cfra;
+ BKE_scene_graph_update_for_newframe(data->depsgraph, data->bmain);
+
+ CLOG_INFO(&LOG, 2, "Exporting frame %.2f", frame);
+ ExportSubset export_subset = abc_archive.export_subset_for_frame(frame);
+ iter.set_export_subset(export_subset);
+ iter.iterate_and_write();
+
+ *progress += progress_per_frame;
+ *do_update = true;
+ }
+ }
+ else {
+ // If we're not animating, a single iteration over all objects is enough.
+ iter.iterate_and_write();
+ }
+
+ iter.release_writers();
+
+ // Finish up by going back to the keyframe that was current before we started.
+ if (CFRA != orig_frame) {
+ CFRA = orig_frame;
+ BKE_scene_graph_update_for_newframe(data->depsgraph, data->bmain);
+ }
+
+ data->export_ok = !data->was_canceled;
+
+ *progress = 1.0f;
+ *do_update = true;
+}
+
+static void export_endjob(void *customdata)
+{
+ ExportJobData *data = static_cast<ExportJobData *>(customdata);
+
+ DEG_graph_free(data->depsgraph);
+
+ if (data->was_canceled && BLI_exists(data->filename)) {
+ BLI_delete(data->filename, false, false);
+ }
+
+ G.is_rendering = false;
+ WM_set_locked_interface(data->wm, false);
+}
+
+} // namespace alembic
+} // namespace io
+} // namespace blender
+
+bool ABC_export(Scene *scene,
+ bContext *C,
+ const char *filepath,
+ const AlembicExportParams *params,
+ bool as_background_job)
+{
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+
+ ExportJobData *job = static_cast<ExportJobData *>(
+ MEM_mallocN(sizeof(ExportJobData), "ExportJobData"));
+
+ job->bmain = CTX_data_main(C);
+ job->wm = CTX_wm_manager(C);
+ job->export_ok = false;
+ BLI_strncpy(job->filename, filepath, sizeof(job->filename));
+
+ job->depsgraph = DEG_graph_new(
+ job->bmain, scene, view_layer, DAG_EVAL_RENDER /* TODO(Sybren): params->evaluation_mode */);
+ job->params = *params;
+
+ bool export_ok = false;
+ if (as_background_job) {
+ wmJob *wm_job = WM_jobs_get(
+ job->wm, CTX_wm_window(C), scene, "Alembic Export", WM_JOB_PROGRESS, WM_JOB_TYPE_ALEMBIC);
+
+ /* setup job */
+ WM_jobs_customdata_set(wm_job, job, MEM_freeN);
+ WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_FRAME, NC_SCENE | ND_FRAME);
+ WM_jobs_callbacks(wm_job,
+ blender::io::alembic::export_startjob,
+ NULL,
+ NULL,
+ blender::io::alembic::export_endjob);
+
+ WM_jobs_start(CTX_wm_manager(C), wm_job);
+ }
+ else {
+ /* Fake a job context, so that we don't need NULL pointer checks while exporting. */
+ short stop = 0, do_update = 0;
+ float progress = 0.f;
+
+ blender::io::alembic::export_startjob(job, &stop, &do_update, &progress);
+ blender::io::alembic::export_endjob(job);
+ export_ok = job->export_ok;
+
+ MEM_freeN(job);
+ }
+
+ return export_ok;
+}
diff --git a/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc
new file mode 100644
index 00000000000..90004c0e85b
--- /dev/null
+++ b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc
@@ -0,0 +1,261 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+
+#include "abc_hierarchy_iterator.h"
+#include "abc_writer_abstract.h"
+#include "abc_writer_camera.h"
+#include "abc_writer_curves.h"
+#include "abc_writer_hair.h"
+#include "abc_writer_mball.h"
+#include "abc_writer_mesh.h"
+#include "abc_writer_nurbs.h"
+#include "abc_writer_points.h"
+#include "abc_writer_transform.h"
+
+#include <memory>
+#include <string>
+
+#include "BLI_assert.h"
+
+#include "DEG_depsgraph_query.h"
+
+#include "DNA_ID.h"
+#include "DNA_layer_types.h"
+#include "DNA_object_types.h"
+
+namespace blender {
+namespace io {
+namespace alembic {
+
+ABCHierarchyIterator::ABCHierarchyIterator(Depsgraph *depsgraph,
+ ABCArchive *abc_archive,
+ const AlembicExportParams &params)
+ : AbstractHierarchyIterator(depsgraph), abc_archive_(abc_archive), params_(params)
+{
+}
+
+void ABCHierarchyIterator::iterate_and_write()
+{
+ AbstractHierarchyIterator::iterate_and_write();
+ update_archive_bounding_box();
+}
+
+void ABCHierarchyIterator::update_archive_bounding_box()
+{
+ Imath::Box3d bounds;
+ update_bounding_box_recursive(bounds, HierarchyContext::root());
+ abc_archive_->update_bounding_box(bounds);
+}
+
+void ABCHierarchyIterator::update_bounding_box_recursive(Imath::Box3d &bounds,
+ const HierarchyContext *context)
+{
+ if (context != nullptr) {
+ AbstractHierarchyWriter *abstract_writer = writers_[context->export_path];
+ ABCAbstractWriter *abc_writer = static_cast<ABCAbstractWriter *>(abstract_writer);
+
+ if (abc_writer != nullptr) {
+ bounds.extendBy(abc_writer->bounding_box());
+ }
+ }
+
+ for (HierarchyContext *child_context : graph_children(context)) {
+ update_bounding_box_recursive(bounds, child_context);
+ }
+}
+
+bool ABCHierarchyIterator::mark_as_weak_export(const Object *object) const
+{
+ if (params_.selected_only && (object->base_flag & BASE_SELECTED) == 0) {
+ return true;
+ }
+ /* TODO(Sybren): handle other flags too? */
+ return false;
+}
+
+void ABCHierarchyIterator::delete_object_writer(AbstractHierarchyWriter *writer)
+{
+ delete writer;
+}
+
+std::string ABCHierarchyIterator::make_valid_name(const std::string &name) const
+{
+ std::string abc_name(name);
+ std::replace(abc_name.begin(), abc_name.end(), ' ', '_');
+ std::replace(abc_name.begin(), abc_name.end(), '.', '_');
+ std::replace(abc_name.begin(), abc_name.end(), ':', '_');
+ return abc_name;
+}
+
+AbstractHierarchyIterator::ExportGraph::key_type ABCHierarchyIterator::
+ determine_graph_index_object(const HierarchyContext *context)
+{
+ if (params_.flatten_hierarchy) {
+ return std::make_pair(nullptr, nullptr);
+ }
+
+ return AbstractHierarchyIterator::determine_graph_index_object(context);
+}
+
+AbstractHierarchyIterator::ExportGraph::key_type ABCHierarchyIterator::determine_graph_index_dupli(
+ const HierarchyContext *context, const std::set<Object *> &dupli_set)
+{
+ if (params_.flatten_hierarchy) {
+ return std::make_pair(nullptr, nullptr);
+ }
+
+ return AbstractHierarchyIterator::determine_graph_index_dupli(context, dupli_set);
+}
+
+Alembic::Abc::OObject ABCHierarchyIterator::get_alembic_parent(
+ const HierarchyContext *context) const
+{
+ Alembic::Abc::OObject parent;
+
+ if (!context->higher_up_export_path.empty()) {
+ AbstractHierarchyWriter *writer = get_writer(context->higher_up_export_path);
+ ABCAbstractWriter *abc_writer = static_cast<ABCAbstractWriter *>(writer);
+ parent = abc_writer->get_alembic_object();
+ }
+
+ if (!parent.valid()) {
+ /* An invalid parent object means "no parent", which should be translated to Alembic's top
+ * archive object. */
+ return abc_archive_->archive->getTop();
+ }
+
+ return parent;
+}
+
+ABCWriterConstructorArgs ABCHierarchyIterator::writer_constructor_args(
+ const HierarchyContext *context) const
+{
+ ABCWriterConstructorArgs constructor_args;
+ constructor_args.depsgraph = depsgraph_;
+ constructor_args.abc_archive = abc_archive_;
+ constructor_args.abc_parent = get_alembic_parent(context);
+ constructor_args.abc_name = context->export_name;
+ constructor_args.abc_path = context->export_path;
+ constructor_args.hierarchy_iterator = this;
+ constructor_args.export_params = &params_;
+ return constructor_args;
+}
+
+AbstractHierarchyWriter *ABCHierarchyIterator::create_transform_writer(
+ const HierarchyContext *context)
+{
+ ABCAbstractWriter *transform_writer = new ABCTransformWriter(writer_constructor_args(context));
+ transform_writer->create_alembic_objects(context);
+ return transform_writer;
+}
+
+AbstractHierarchyWriter *ABCHierarchyIterator::create_data_writer(const HierarchyContext *context)
+{
+ const ABCWriterConstructorArgs writer_args = writer_constructor_args(context);
+ ABCAbstractWriter *data_writer = nullptr;
+
+ switch (context->object->type) {
+ case OB_MESH:
+ data_writer = new ABCMeshWriter(writer_args);
+ break;
+ case OB_CAMERA:
+ data_writer = new ABCCameraWriter(writer_args);
+ break;
+ case OB_CURVE:
+ if (params_.curves_as_mesh) {
+ data_writer = new ABCCurveMeshWriter(writer_args);
+ }
+ else {
+ data_writer = new ABCCurveWriter(writer_args);
+ }
+ break;
+ case OB_SURF:
+ if (params_.curves_as_mesh) {
+ data_writer = new ABCCurveMeshWriter(writer_args);
+ }
+ else {
+ data_writer = new ABCNurbsWriter(writer_args);
+ }
+ break;
+ case OB_MBALL:
+ data_writer = new ABCMetaballWriter(writer_args);
+ break;
+
+ case OB_EMPTY:
+ case OB_LAMP:
+ case OB_FONT:
+ case OB_SPEAKER:
+ case OB_LIGHTPROBE:
+ case OB_LATTICE:
+ case OB_ARMATURE:
+ case OB_GPENCIL:
+ return nullptr;
+ case OB_TYPE_MAX:
+ BLI_assert(!"OB_TYPE_MAX should not be used");
+ return nullptr;
+ }
+
+ if (!data_writer->is_supported(context)) {
+ delete data_writer;
+ return nullptr;
+ }
+
+ data_writer->create_alembic_objects(context);
+ return data_writer;
+}
+
+AbstractHierarchyWriter *ABCHierarchyIterator::create_hair_writer(const HierarchyContext *context)
+{
+ if (!params_.export_hair) {
+ return nullptr;
+ }
+
+ const ABCWriterConstructorArgs writer_args = writer_constructor_args(context);
+ ABCAbstractWriter *hair_writer = new ABCHairWriter(writer_args);
+
+ if (!hair_writer->is_supported(context)) {
+ delete hair_writer;
+ return nullptr;
+ }
+
+ hair_writer->create_alembic_objects(context);
+ return hair_writer;
+}
+
+AbstractHierarchyWriter *ABCHierarchyIterator::create_particle_writer(
+ const HierarchyContext *context)
+{
+ if (!params_.export_particles) {
+ return nullptr;
+ }
+
+ const ABCWriterConstructorArgs writer_args = writer_constructor_args(context);
+ std::unique_ptr<ABCPointsWriter> particle_writer(std::make_unique<ABCPointsWriter>(writer_args));
+
+ if (!particle_writer->is_supported(context)) {
+ return nullptr;
+ }
+
+ particle_writer->create_alembic_objects(context);
+ return particle_writer.release();
+}
+
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/exporter/abc_hierarchy_iterator.h b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.h
new file mode 100644
index 00000000000..edcb31806ba
--- /dev/null
+++ b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.h
@@ -0,0 +1,90 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+#pragma once
+
+#include "ABC_alembic.h"
+#include "abc_archive.h"
+
+#include "IO_abstract_hierarchy_iterator.h"
+
+#include <string>
+
+#include <Alembic/Abc/OArchive.h>
+#include <Alembic/Abc/OObject.h>
+
+struct Depsgraph;
+struct ID;
+struct Object;
+
+namespace blender {
+namespace io {
+namespace alembic {
+
+class ABCHierarchyIterator;
+
+struct ABCWriterConstructorArgs {
+ Depsgraph *depsgraph;
+ ABCArchive *abc_archive;
+ Alembic::Abc::OObject abc_parent;
+ std::string abc_name;
+ std::string abc_path;
+ const ABCHierarchyIterator *hierarchy_iterator;
+ const AlembicExportParams *export_params;
+};
+
+class ABCHierarchyIterator : public AbstractHierarchyIterator {
+ private:
+ ABCArchive *abc_archive_;
+ const AlembicExportParams &params_;
+
+ public:
+ ABCHierarchyIterator(Depsgraph *depsgraph,
+ ABCArchive *abc_archive_,
+ const AlembicExportParams &params);
+
+ virtual void iterate_and_write() override;
+ virtual std::string make_valid_name(const std::string &name) const override;
+
+ protected:
+ virtual bool mark_as_weak_export(const Object *object) const override;
+
+ virtual ExportGraph::key_type determine_graph_index_object(
+ const HierarchyContext *context) override;
+ virtual AbstractHierarchyIterator::ExportGraph::key_type determine_graph_index_dupli(
+ const HierarchyContext *context, const std::set<Object *> &dupli_set) override;
+
+ virtual AbstractHierarchyWriter *create_transform_writer(
+ const HierarchyContext *context) override;
+ virtual AbstractHierarchyWriter *create_data_writer(const HierarchyContext *context) override;
+ virtual AbstractHierarchyWriter *create_hair_writer(const HierarchyContext *context) override;
+ virtual AbstractHierarchyWriter *create_particle_writer(
+ const HierarchyContext *context) override;
+
+ virtual void delete_object_writer(AbstractHierarchyWriter *writer) override;
+
+ private:
+ Alembic::Abc::OObject get_alembic_parent(const HierarchyContext *context) const;
+ ABCWriterConstructorArgs writer_constructor_args(const HierarchyContext *context) const;
+ void update_archive_bounding_box();
+ void update_bounding_box_recursive(Imath::Box3d &bounds, const HierarchyContext *context);
+};
+
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/exporter/abc_subdiv_disabler.cc b/source/blender/io/alembic/exporter/abc_subdiv_disabler.cc
new file mode 100644
index 00000000000..7c147076975
--- /dev/null
+++ b/source/blender/io/alembic/exporter/abc_subdiv_disabler.cc
@@ -0,0 +1,107 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+#include "abc_subdiv_disabler.h"
+
+#include <stdio.h>
+
+#include "BLI_listbase.h"
+
+#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_query.h"
+
+#include "DNA_layer_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_object_types.h"
+
+#include "BKE_modifier.h"
+
+namespace blender {
+namespace io {
+namespace alembic {
+
+SubdivModifierDisabler::SubdivModifierDisabler(Depsgraph *depsgraph) : depsgraph_(depsgraph)
+{
+}
+
+SubdivModifierDisabler::~SubdivModifierDisabler()
+{
+ for (ModifierData *modifier : disabled_modifiers_) {
+ modifier->mode &= ~eModifierMode_DisableTemporary;
+ }
+}
+
+void SubdivModifierDisabler::disable_modifiers()
+{
+ Scene *scene = DEG_get_input_scene(depsgraph_);
+ ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph_);
+
+ LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ Object *object = base->object;
+
+ if (object->type != OB_MESH) {
+ continue;
+ }
+
+ ModifierData *subdiv = get_subdiv_modifier(scene, object);
+ if (subdiv == nullptr) {
+ continue;
+ }
+
+ /* This disables more modifiers than necessary, as it doesn't take restrictions like
+ * "export selected objects only" into account. However, with the subsurfs disabled,
+ * moving to a different frame is also going to be faster, so in the end this is probably
+ * a good thing to do. */
+ subdiv->mode |= eModifierMode_DisableTemporary;
+ disabled_modifiers_.insert(subdiv);
+ DEG_id_tag_update(&object->id, ID_RECALC_GEOMETRY);
+ }
+}
+
+/* Check if the mesh is a subsurf, ignoring disabled modifiers and
+ * displace if it's after subsurf. */
+ModifierData *SubdivModifierDisabler::get_subdiv_modifier(Scene *scene, Object *ob)
+{
+ ModifierData *md = static_cast<ModifierData *>(ob->modifiers.last);
+
+ for (; md; md = md->prev) {
+ if (!BKE_modifier_is_enabled(scene, md, eModifierMode_Render)) {
+ continue;
+ }
+
+ if (md->type == eModifierType_Subsurf) {
+ SubsurfModifierData *smd = reinterpret_cast<SubsurfModifierData *>(md);
+
+ if (smd->subdivType == ME_CC_SUBSURF) {
+ return md;
+ }
+ }
+
+ /* mesh is not a subsurf. break */
+ if ((md->type != eModifierType_Displace) && (md->type != eModifierType_ParticleSystem)) {
+ return nullptr;
+ }
+ }
+
+ return nullptr;
+}
+
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/exporter/abc_subdiv_disabler.h b/source/blender/io/alembic/exporter/abc_subdiv_disabler.h
new file mode 100644
index 00000000000..677847f3f63
--- /dev/null
+++ b/source/blender/io/alembic/exporter/abc_subdiv_disabler.h
@@ -0,0 +1,55 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+#pragma once
+
+#include <set>
+
+struct Depsgraph;
+struct ModifierData;
+struct Object;
+struct Scene;
+
+namespace blender {
+namespace io {
+namespace alembic {
+
+/**
+ * Temporarily all subdivision modifiers on mesh objects.
+ * The destructor restores all disabled modifiers.
+ *
+ * This is used to export unsubdivided meshes to Alembic. It is done in a separate step before the
+ * exporter starts iterating over all the frames, so that it only has to happen once per export.
+ */
+class SubdivModifierDisabler final {
+ private:
+ Depsgraph *depsgraph_;
+ std::set<ModifierData *> disabled_modifiers_;
+
+ public:
+ explicit SubdivModifierDisabler(Depsgraph *depsgraph);
+ ~SubdivModifierDisabler();
+
+ void disable_modifiers();
+
+ static ModifierData *get_subdiv_modifier(Scene *scene, Object *ob);
+};
+
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/exporter/abc_writer_abstract.cc b/source/blender/io/alembic/exporter/abc_writer_abstract.cc
new file mode 100644
index 00000000000..e43b394e27f
--- /dev/null
+++ b/source/blender/io/alembic/exporter/abc_writer_abstract.cc
@@ -0,0 +1,101 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+#include "abc_writer_abstract.h"
+#include "abc_hierarchy_iterator.h"
+
+#include "BKE_animsys.h"
+#include "BKE_key.h"
+#include "BKE_object.h"
+
+#include "DNA_modifier_types.h"
+
+#include "CLG_log.h"
+static CLG_LogRef LOG = {"io.alembic"};
+
+namespace blender {
+namespace io {
+namespace alembic {
+
+using Alembic::Abc::OObject;
+using Alembic::Abc::TimeSamplingPtr;
+
+ABCAbstractWriter::ABCAbstractWriter(const ABCWriterConstructorArgs &args)
+ : args_(args),
+ frame_has_been_written_(false),
+ is_animated_(false),
+ timesample_index_(args_.abc_archive->time_sampling_index_shapes())
+{
+}
+
+ABCAbstractWriter::~ABCAbstractWriter()
+{
+}
+
+bool ABCAbstractWriter::is_supported(const HierarchyContext * /*context*/) const
+{
+ return true;
+}
+
+void ABCAbstractWriter::write(HierarchyContext &context)
+{
+ if (!frame_has_been_written_) {
+ is_animated_ = (args_.export_params->frame_start != args_.export_params->frame_end) &&
+ check_is_animated(context);
+ }
+ else if (!is_animated_) {
+ /* A frame has already been written, and without animation one frame is enough. */
+ return;
+ }
+
+ do_write(context);
+
+ frame_has_been_written_ = true;
+}
+
+const Imath::Box3d &ABCAbstractWriter::bounding_box() const
+{
+ return bounding_box_;
+}
+
+void ABCAbstractWriter::update_bounding_box(Object *object)
+{
+ BoundBox *bb = BKE_object_boundbox_get(object);
+
+ if (!bb) {
+ if (object->type != OB_CAMERA) {
+ CLOG_WARN(&LOG, "Bounding box is null!\n");
+ }
+ bounding_box_.min.x = bounding_box_.min.y = bounding_box_.min.z = 0;
+ bounding_box_.max.x = bounding_box_.max.y = bounding_box_.max.z = 0;
+ return;
+ }
+
+ /* Convert Z-up to Y-up. This also changes which vector goes into which min/max property. */
+ bounding_box_.min.x = bb->vec[0][0];
+ bounding_box_.min.y = bb->vec[0][2];
+ bounding_box_.min.z = -bb->vec[6][1];
+
+ bounding_box_.max.x = bb->vec[6][0];
+ bounding_box_.max.y = bb->vec[6][2];
+ bounding_box_.max.z = -bb->vec[0][1];
+}
+
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/exporter/abc_writer_abstract.h b/source/blender/io/alembic/exporter/abc_writer_abstract.h
new file mode 100644
index 00000000000..a83373a567a
--- /dev/null
+++ b/source/blender/io/alembic/exporter/abc_writer_abstract.h
@@ -0,0 +1,77 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+#pragma once
+
+#include "IO_abstract_hierarchy_iterator.h"
+#include "abc_hierarchy_iterator.h"
+
+#include <Alembic/Abc/OObject.h>
+#include <vector>
+
+#include "DEG_depsgraph_query.h"
+#include "DNA_material_types.h"
+
+struct Material;
+struct Object;
+
+namespace blender {
+namespace io {
+namespace alembic {
+
+class ABCAbstractWriter : public AbstractHierarchyWriter {
+ protected:
+ const ABCWriterConstructorArgs args_;
+
+ bool frame_has_been_written_;
+ bool is_animated_;
+ uint32_t timesample_index_;
+ Imath::Box3d bounding_box_;
+
+ public:
+ explicit ABCAbstractWriter(const ABCWriterConstructorArgs &args);
+ virtual ~ABCAbstractWriter();
+
+ virtual void write(HierarchyContext &context) override;
+
+ /* Returns true if the data to be written is actually supported. This would, for example, allow a
+ * hypothetical camera writer accept a perspective camera but reject an orthogonal one.
+ *
+ * Returning false from a transform writer will prevent the object and all its descendants from
+ * being exported. Returning false from a data writer (object data, hair, or particles) will
+ * only prevent that data from being written (and thus cause the object to be exported as an
+ * Empty). */
+ virtual bool is_supported(const HierarchyContext *context) const;
+
+ const Imath::Box3d &bounding_box() const;
+
+ /* Called by AlembicHierarchyCreator after checking that the data is supported via
+ * is_supported(). */
+ virtual void create_alembic_objects(const HierarchyContext *context) = 0;
+
+ virtual const Alembic::Abc::OObject get_alembic_object() const = 0;
+
+ protected:
+ virtual void do_write(HierarchyContext &context) = 0;
+
+ virtual void update_bounding_box(Object *object);
+};
+
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/exporter/abc_writer_camera.cc b/source/blender/io/alembic/exporter/abc_writer_camera.cc
new file mode 100644
index 00000000000..7e7277cb4ea
--- /dev/null
+++ b/source/blender/io/alembic/exporter/abc_writer_camera.cc
@@ -0,0 +1,110 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup balembic
+ */
+
+#include "abc_writer_camera.h"
+#include "abc_hierarchy_iterator.h"
+
+#include "BKE_camera.h"
+
+#include "BLI_assert.h"
+
+#include "DNA_camera_types.h"
+#include "DNA_scene_types.h"
+
+#include "CLG_log.h"
+static CLG_LogRef LOG = {"io.alembic"};
+
+namespace blender {
+namespace io {
+namespace alembic {
+
+using Alembic::AbcGeom::CameraSample;
+using Alembic::AbcGeom::OCamera;
+using Alembic::AbcGeom::OFloatProperty;
+
+ABCCameraWriter::ABCCameraWriter(const ABCWriterConstructorArgs &args) : ABCAbstractWriter(args)
+{
+}
+
+bool ABCCameraWriter::is_supported(const HierarchyContext *context) const
+{
+ Camera *camera = static_cast<Camera *>(context->object->data);
+ return camera->type == CAM_PERSP;
+}
+
+void ABCCameraWriter::create_alembic_objects(const HierarchyContext * /*context*/)
+{
+ CLOG_INFO(&LOG, 2, "exporting %s", args_.abc_path.c_str());
+ abc_camera_ = OCamera(args_.abc_parent, args_.abc_name, timesample_index_);
+ abc_camera_schema_ = abc_camera_.getSchema();
+
+ abc_custom_data_container_ = abc_camera_schema_.getUserProperties();
+ abc_stereo_distance_ = OFloatProperty(
+ abc_custom_data_container_, "stereoDistance", timesample_index_);
+ abc_eye_separation_ = OFloatProperty(
+ abc_custom_data_container_, "eyeSeparation", timesample_index_);
+}
+
+const Alembic::Abc::OObject ABCCameraWriter::get_alembic_object() const
+{
+ return abc_camera_;
+}
+
+void ABCCameraWriter::do_write(HierarchyContext &context)
+{
+ Camera *cam = static_cast<Camera *>(context.object->data);
+
+ abc_stereo_distance_.set(cam->stereo.convergence_distance);
+ abc_eye_separation_.set(cam->stereo.interocular_distance);
+
+ const double apperture_x = cam->sensor_x / 10.0;
+ const double apperture_y = cam->sensor_y / 10.0;
+ const double film_aspect = apperture_x / apperture_y;
+
+ CameraSample camera_sample;
+ camera_sample.setFocalLength(cam->lens);
+ camera_sample.setHorizontalAperture(apperture_x);
+ camera_sample.setVerticalAperture(apperture_y);
+ camera_sample.setHorizontalFilmOffset(apperture_x * cam->shiftx);
+ camera_sample.setVerticalFilmOffset(apperture_y * cam->shifty * film_aspect);
+ camera_sample.setNearClippingPlane(cam->clip_start);
+ camera_sample.setFarClippingPlane(cam->clip_end);
+
+ if (cam->dof.focus_object) {
+ Imath::V3f v(context.object->loc[0] - cam->dof.focus_object->loc[0],
+ context.object->loc[1] - cam->dof.focus_object->loc[1],
+ context.object->loc[2] - cam->dof.focus_object->loc[2]);
+ camera_sample.setFocusDistance(v.length());
+ }
+ else {
+ camera_sample.setFocusDistance(cam->dof.focus_distance);
+ }
+
+ /* Blender camera does not have an fstop param, so try to find a custom prop
+ * instead. */
+ camera_sample.setFStop(cam->dof.aperture_fstop);
+
+ camera_sample.setLensSqueezeRatio(1.0);
+ abc_camera_schema_.set(camera_sample);
+}
+
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/exporter/abc_writer_camera.h b/source/blender/io/alembic/exporter/abc_writer_camera.h
new file mode 100644
index 00000000000..a72cfa2f357
--- /dev/null
+++ b/source/blender/io/alembic/exporter/abc_writer_camera.h
@@ -0,0 +1,52 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+#pragma once
+
+/** \file
+ * \ingroup balembic
+ */
+
+#include "abc_writer_abstract.h"
+
+#include <Alembic/AbcGeom/OCamera.h>
+
+namespace blender {
+namespace io {
+namespace alembic {
+
+class ABCCameraWriter : public ABCAbstractWriter {
+ private:
+ Alembic::AbcGeom::OCamera abc_camera_;
+ Alembic::AbcGeom::OCameraSchema abc_camera_schema_;
+
+ Alembic::AbcGeom::OCompoundProperty abc_custom_data_container_;
+ Alembic::AbcGeom::OFloatProperty abc_stereo_distance_;
+ Alembic::AbcGeom::OFloatProperty abc_eye_separation_;
+
+ public:
+ explicit ABCCameraWriter(const ABCWriterConstructorArgs &args);
+
+ virtual void create_alembic_objects(const HierarchyContext *context) override;
+ virtual const Alembic::Abc::OObject get_alembic_object() const override;
+
+ protected:
+ virtual bool is_supported(const HierarchyContext *context) const override;
+ virtual void do_write(HierarchyContext &context) override;
+};
+
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/intern/abc_writer_curves.cc b/source/blender/io/alembic/exporter/abc_writer_curves.cc
index db93ac1920e..f2a46c5e4fe 100644
--- a/source/blender/io/alembic/intern/abc_writer_curves.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_curves.cc
@@ -22,9 +22,7 @@
*/
#include "abc_writer_curves.h"
-#include "abc_axis_conversion.h"
-#include "abc_reader_curves.h"
-#include "abc_writer_transform.h"
+#include "intern/abc_axis_conversion.h"
#include "DNA_curve_types.h"
#include "DNA_object_types.h"
@@ -33,6 +31,9 @@
#include "BKE_mesh.h"
#include "BKE_object.h"
+#include "CLG_log.h"
+static CLG_LogRef LOG = {"io.alembic"};
+
using Alembic::AbcGeom::OCompoundProperty;
using Alembic::AbcGeom::OCurves;
using Alembic::AbcGeom::OCurvesSchema;
@@ -40,24 +41,36 @@ using Alembic::AbcGeom::OInt16Property;
using Alembic::AbcGeom::ON3fGeomParam;
using Alembic::AbcGeom::OV2fGeomParam;
-AbcCurveWriter::AbcCurveWriter(Object *ob,
- AbcTransformWriter *parent,
- uint32_t time_sampling,
- ExportSettings &settings)
- : AbcObjectWriter(ob, time_sampling, settings, parent)
+namespace blender {
+namespace io {
+namespace alembic {
+
+const std::string ABC_CURVE_RESOLUTION_U_PROPNAME("blender:resolution");
+
+ABCCurveWriter::ABCCurveWriter(const ABCWriterConstructorArgs &args) : ABCAbstractWriter(args)
+{
+}
+
+void ABCCurveWriter::create_alembic_objects(const HierarchyContext *context)
{
- OCurves curves(parent->alembicXform(), m_name, m_time_sampling);
- m_schema = curves.getSchema();
+ CLOG_INFO(&LOG, 2, "exporting %s", args_.abc_path.c_str());
+ abc_curve_ = OCurves(args_.abc_parent, args_.abc_name, timesample_index_);
+ abc_curve_schema_ = abc_curve_.getSchema();
- Curve *cu = static_cast<Curve *>(m_object->data);
- OCompoundProperty user_props = m_schema.getUserProperties();
+ Curve *cu = static_cast<Curve *>(context->object->data);
+ OCompoundProperty user_props = abc_curve_schema_.getUserProperties();
OInt16Property user_prop_resolu(user_props, ABC_CURVE_RESOLUTION_U_PROPNAME);
user_prop_resolu.set(cu->resolu);
}
-void AbcCurveWriter::do_write()
+const Alembic::Abc::OObject ABCCurveWriter::get_alembic_object() const
{
- Curve *curve = static_cast<Curve *>(m_object->data);
+ return abc_curve_;
+}
+
+void ABCCurveWriter::do_write(HierarchyContext &context)
+{
+ Curve *curve = static_cast<Curve *>(context.object->data);
std::vector<Imath::V3f> verts;
std::vector<int32_t> vert_counts;
@@ -148,35 +161,31 @@ void AbcCurveWriter::do_write()
Alembic::AbcGeom::OFloatGeomParam::Sample width_sample;
width_sample.setVals(widths);
- m_sample = OCurvesSchema::Sample(verts,
- vert_counts,
- curve_type,
- periodicity,
- width_sample,
- OV2fGeomParam::Sample(), /* UVs */
- ON3fGeomParam::Sample(), /* normals */
- curve_basis,
- weights,
- orders,
- knots);
-
- m_sample.setSelfBounds(bounds());
- m_schema.set(m_sample);
+ OCurvesSchema::Sample sample(verts,
+ vert_counts,
+ curve_type,
+ periodicity,
+ width_sample,
+ OV2fGeomParam::Sample(), /* UVs */
+ ON3fGeomParam::Sample(), /* normals */
+ curve_basis,
+ weights,
+ orders,
+ knots);
+
+ update_bounding_box(context.object);
+ sample.setSelfBounds(bounding_box_);
+ abc_curve_schema_.set(sample);
}
-AbcCurveMeshWriter::AbcCurveMeshWriter(Object *ob,
- AbcTransformWriter *parent,
- uint32_t time_sampling,
- ExportSettings &settings)
- : AbcGenericMeshWriter(ob, parent, time_sampling, settings)
+ABCCurveMeshWriter::ABCCurveMeshWriter(const ABCWriterConstructorArgs &args)
+ : ABCGenericMeshWriter(args)
{
}
-Mesh *AbcCurveMeshWriter::getEvaluatedMesh(Scene * /*scene_eval*/,
- Object *ob_eval,
- bool &r_needsfree)
+Mesh *ABCCurveMeshWriter::get_export_mesh(Object *object_eval, bool &r_needsfree)
{
- Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob_eval);
+ Mesh *mesh_eval = BKE_object_get_evaluated_mesh(object_eval);
if (mesh_eval != NULL) {
/* Mesh_eval only exists when generative modifiers are in use. */
r_needsfree = false;
@@ -184,5 +193,9 @@ Mesh *AbcCurveMeshWriter::getEvaluatedMesh(Scene * /*scene_eval*/,
}
r_needsfree = true;
- return BKE_mesh_new_nomain_from_curve(ob_eval);
+ return BKE_mesh_new_nomain_from_curve(object_eval);
}
+
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/intern/abc_writer_curves.h b/source/blender/io/alembic/exporter/abc_writer_curves.h
index 83f0289dd2d..12a909761f5 100644
--- a/source/blender/io/alembic/intern/abc_writer_curves.h
+++ b/source/blender/io/alembic/exporter/abc_writer_curves.h
@@ -16,40 +16,46 @@
* The Original Code is Copyright (C) 2016 Kévin Dietrich.
* All rights reserved.
*/
+#pragma once
/** \file
* \ingroup balembic
*/
-#ifndef __ABC_WRITER_CURVES_H__
-#define __ABC_WRITER_CURVES_H__
-
+#include "abc_writer_abstract.h"
#include "abc_writer_mesh.h"
-#include "abc_writer_object.h"
-class AbcCurveWriter : public AbcObjectWriter {
- Alembic::AbcGeom::OCurvesSchema m_schema;
- Alembic::AbcGeom::OCurvesSchema::Sample m_sample;
+#include <Alembic/AbcGeom/OCurves.h>
+
+namespace blender {
+namespace io {
+namespace alembic {
+
+extern const std::string ABC_CURVE_RESOLUTION_U_PROPNAME;
+
+class ABCCurveWriter : public ABCAbstractWriter {
+ private:
+ Alembic::AbcGeom::OCurves abc_curve_;
+ Alembic::AbcGeom::OCurvesSchema abc_curve_schema_;
public:
- AbcCurveWriter(Object *ob,
- AbcTransformWriter *parent,
- uint32_t time_sampling,
- ExportSettings &settings);
+ explicit ABCCurveWriter(const ABCWriterConstructorArgs &args);
+
+ virtual void create_alembic_objects(const HierarchyContext *context) override;
+ virtual const Alembic::Abc::OObject get_alembic_object() const override;
protected:
- void do_write();
+ virtual void do_write(HierarchyContext &context) override;
};
-class AbcCurveMeshWriter : public AbcGenericMeshWriter {
+class ABCCurveMeshWriter : public ABCGenericMeshWriter {
public:
- AbcCurveMeshWriter(Object *ob,
- AbcTransformWriter *parent,
- uint32_t time_sampling,
- ExportSettings &settings);
+ ABCCurveMeshWriter(const ABCWriterConstructorArgs &args);
protected:
- Mesh *getEvaluatedMesh(Scene *scene_eval, Object *ob_eval, bool &r_needsfree);
+ virtual Mesh *get_export_mesh(Object *object_eval, bool &r_needsfree) override;
};
-#endif /* __ABC_WRITER_CURVES_H__ */
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/intern/abc_writer_hair.cc b/source/blender/io/alembic/exporter/abc_writer_hair.cc
index ed62889b03d..ac4deddd9b4 100644
--- a/source/blender/io/alembic/intern/abc_writer_hair.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_hair.cc
@@ -19,8 +19,7 @@
*/
#include "abc_writer_hair.h"
-#include "abc_axis_conversion.h"
-#include "abc_writer_transform.h"
+#include "intern/abc_axis_conversion.h"
#include <cstdio>
@@ -35,35 +34,46 @@
#include "BKE_mesh_runtime.h"
#include "BKE_particle.h"
-using Alembic::Abc::P3fArraySamplePtr;
+#include "CLG_log.h"
+static CLG_LogRef LOG = {"io.alembic"};
+using Alembic::Abc::P3fArraySamplePtr;
using Alembic::AbcGeom::OCurves;
using Alembic::AbcGeom::OCurvesSchema;
using Alembic::AbcGeom::ON3fGeomParam;
using Alembic::AbcGeom::OV2fGeomParam;
-/* ************************************************************************** */
+namespace blender {
+namespace io {
+namespace alembic {
-AbcHairWriter::AbcHairWriter(Object *ob,
- AbcTransformWriter *parent,
- uint32_t time_sampling,
- ExportSettings &settings,
- ParticleSystem *psys)
- : AbcObjectWriter(ob, time_sampling, settings, parent), m_uv_warning_shown(false)
+ABCHairWriter::ABCHairWriter(const ABCWriterConstructorArgs &args)
+ : ABCAbstractWriter(args), uv_warning_shown_(false)
{
- m_psys = psys;
+}
- OCurves curves(parent->alembicXform(), psys->name, m_time_sampling);
- m_schema = curves.getSchema();
+void ABCHairWriter::create_alembic_objects(const HierarchyContext * /*context*/)
+{
+ CLOG_INFO(&LOG, 2, "exporting %s", args_.abc_path.c_str());
+ abc_curves_ = OCurves(args_.abc_parent, args_.abc_name, timesample_index_);
+ abc_curves_schema_ = abc_curves_.getSchema();
}
-void AbcHairWriter::do_write()
+const Alembic::Abc::OObject ABCHairWriter::get_alembic_object() const
{
- if (!m_psys) {
- return;
- }
- Mesh *mesh = mesh_get_eval_final(
- m_settings.depsgraph, m_settings.scene, m_object, &CD_MASK_MESH);
+ return abc_curves_;
+}
+
+bool ABCHairWriter::check_is_animated(const HierarchyContext & /*context*/) const
+{
+ /* We assume that hair particles are always animated. */
+ return true;
+}
+
+void ABCHairWriter::do_write(HierarchyContext &context)
+{
+ Scene *scene_eval = DEG_get_evaluated_scene(args_.depsgraph);
+ Mesh *mesh = mesh_get_eval_final(args_.depsgraph, scene_eval, context.object, &CD_MASK_MESH);
BKE_mesh_tessface_ensure(mesh);
std::vector<Imath::V3f> verts;
@@ -71,44 +81,45 @@ void AbcHairWriter::do_write()
std::vector<Imath::V2f> uv_values;
std::vector<Imath::V3f> norm_values;
- if (m_psys->pathcache) {
- ParticleSettings *part = m_psys->part;
- bool export_children = m_settings.export_child_hairs && m_psys->childcache &&
- part->childtype != 0;
+ ParticleSystem *psys = context.particle_system;
+ if (psys->pathcache) {
+ ParticleSettings *part = psys->part;
+ bool export_children = psys->childcache && part->childtype != 0;
if (!export_children || part->draw & PART_DRAW_PARENT) {
- write_hair_sample(mesh, part, verts, norm_values, uv_values, hvertices);
+ write_hair_sample(context, mesh, verts, norm_values, uv_values, hvertices);
}
if (export_children) {
- write_hair_child_sample(mesh, part, verts, norm_values, uv_values, hvertices);
+ write_hair_child_sample(context, mesh, verts, norm_values, uv_values, hvertices);
}
}
Alembic::Abc::P3fArraySample iPos(verts);
- m_sample = OCurvesSchema::Sample(iPos, hvertices);
- m_sample.setBasis(Alembic::AbcGeom::kNoBasis);
- m_sample.setType(Alembic::AbcGeom::kLinear);
- m_sample.setWrap(Alembic::AbcGeom::kNonPeriodic);
+ OCurvesSchema::Sample sample(iPos, hvertices);
+ sample.setBasis(Alembic::AbcGeom::kNoBasis);
+ sample.setType(Alembic::AbcGeom::kLinear);
+ sample.setWrap(Alembic::AbcGeom::kNonPeriodic);
if (!uv_values.empty()) {
OV2fGeomParam::Sample uv_smp;
uv_smp.setVals(uv_values);
- m_sample.setUVs(uv_smp);
+ sample.setUVs(uv_smp);
}
if (!norm_values.empty()) {
ON3fGeomParam::Sample norm_smp;
norm_smp.setVals(norm_values);
- m_sample.setNormals(norm_smp);
+ sample.setNormals(norm_smp);
}
- m_sample.setSelfBounds(bounds());
- m_schema.set(m_sample);
+ update_bounding_box(context.object);
+ sample.setSelfBounds(bounding_box_);
+ abc_curves_schema_.set(sample);
}
-void AbcHairWriter::write_hair_sample(Mesh *mesh,
- ParticleSettings *part,
+void ABCHairWriter::write_hair_sample(const HierarchyContext &context,
+ Mesh *mesh,
std::vector<Imath::V3f> &verts,
std::vector<Imath::V3f> &norm_values,
std::vector<Imath::V2f> &uv_values,
@@ -116,28 +127,30 @@ void AbcHairWriter::write_hair_sample(Mesh *mesh,
{
/* Get untransformed vertices, there's a xform under the hair. */
float inv_mat[4][4];
- invert_m4_m4_safe(inv_mat, m_object->obmat);
+ invert_m4_m4_safe(inv_mat, context.object->obmat);
MTFace *mtface = mesh->mtface;
MFace *mface = mesh->mface;
MVert *mverts = mesh->mvert;
- if ((!mtface || !mface) && !m_uv_warning_shown) {
+ if ((!mtface || !mface) && !uv_warning_shown_) {
std::fprintf(stderr,
"Warning, no UV set found for underlying geometry of %s.\n",
- m_object->id.name + 2);
- m_uv_warning_shown = true;
+ context.object->id.name + 2);
+ uv_warning_shown_ = true;
}
- ParticleData *pa = m_psys->particles;
+ ParticleSystem *psys = context.particle_system;
+ ParticleSettings *part = psys->part;
+ ParticleData *pa = psys->particles;
int k;
- ParticleCacheKey **cache = m_psys->pathcache;
+ ParticleCacheKey **cache = psys->pathcache;
ParticleCacheKey *path;
float normal[3];
Imath::V3f tmp_nor;
- for (int p = 0; p < m_psys->totpart; p++, pa++) {
+ for (int p = 0; p < psys->totpart; p++, pa++) {
/* underlying info for faces-only emission */
path = cache[p];
@@ -219,8 +232,8 @@ void AbcHairWriter::write_hair_sample(Mesh *mesh,
}
}
-void AbcHairWriter::write_hair_child_sample(Mesh *mesh,
- ParticleSettings *part,
+void ABCHairWriter::write_hair_child_sample(const HierarchyContext &context,
+ Mesh *mesh,
std::vector<Imath::V3f> &verts,
std::vector<Imath::V3f> &norm_values,
std::vector<Imath::V2f> &uv_values,
@@ -228,26 +241,30 @@ void AbcHairWriter::write_hair_child_sample(Mesh *mesh,
{
/* Get untransformed vertices, there's a xform under the hair. */
float inv_mat[4][4];
- invert_m4_m4_safe(inv_mat, m_object->obmat);
+ invert_m4_m4_safe(inv_mat, context.object->obmat);
MTFace *mtface = mesh->mtface;
MVert *mverts = mesh->mvert;
- ParticleCacheKey **cache = m_psys->childcache;
+ ParticleSystem *psys = context.particle_system;
+ ParticleSettings *part = psys->part;
+ ParticleCacheKey **cache = psys->childcache;
ParticleCacheKey *path;
- ChildParticle *pc = m_psys->child;
+ ChildParticle *pc = psys->child;
- for (int p = 0; p < m_psys->totchild; p++, pc++) {
+ for (int p = 0; p < psys->totchild; p++, pc++) {
path = cache[p];
if (part->from == PART_FROM_FACE && part->childtype != PART_CHILD_PARTICLES && mtface) {
const int num = pc->num;
if (num < 0) {
- ABC_LOG(m_settings.logger)
- << "Warning, child particle of hair system " << m_psys->name
- << " has unknown face index of geometry of " << (m_object->id.name + 2)
- << ", skipping child hair." << std::endl;
+ CLOG_WARN(
+ &LOG,
+ "Child particle of hair system %s has unknown face index of geometry of %s, skipping "
+ "child hair.",
+ psys->name,
+ context.object->id.name + 2);
continue;
}
@@ -288,3 +305,7 @@ void AbcHairWriter::write_hair_child_sample(Mesh *mesh,
}
}
}
+
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/intern/abc_writer_hair.h b/source/blender/io/alembic/exporter/abc_writer_hair.h
index 67d1b7b3d23..af1372a08f3 100644
--- a/source/blender/io/alembic/intern/abc_writer_hair.h
+++ b/source/blender/io/alembic/exporter/abc_writer_hair.h
@@ -13,50 +13,56 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#pragma once
/** \file
* \ingroup balembic
*/
-#ifndef __ABC_WRITER_HAIR_H__
-#define __ABC_WRITER_HAIR_H__
-
-#include "abc_writer_object.h"
+#include "abc_writer_abstract.h"
+#include <Alembic/AbcGeom/OCurves.h>
+#include <vector>
struct ParticleSettings;
struct ParticleSystem;
-class AbcHairWriter : public AbcObjectWriter {
- ParticleSystem *m_psys;
+namespace blender {
+namespace io {
+namespace alembic {
- Alembic::AbcGeom::OCurvesSchema m_schema;
- Alembic::AbcGeom::OCurvesSchema::Sample m_sample;
+class ABCHairWriter : public ABCAbstractWriter {
+ private:
+ Alembic::AbcGeom::OCurves abc_curves_;
+ Alembic::AbcGeom::OCurvesSchema abc_curves_schema_;
- bool m_uv_warning_shown;
+ bool uv_warning_shown_;
public:
- AbcHairWriter(Object *ob,
- AbcTransformWriter *parent,
- uint32_t time_sampling,
- ExportSettings &settings,
- ParticleSystem *psys);
+ explicit ABCHairWriter(const ABCWriterConstructorArgs &args);
- private:
- virtual void do_write();
+ virtual void create_alembic_objects(const HierarchyContext *context) override;
+ virtual const Alembic::Abc::OObject get_alembic_object() const override;
- void write_hair_sample(struct Mesh *mesh,
- ParticleSettings *part,
+ protected:
+ virtual void do_write(HierarchyContext &context) override;
+ virtual bool check_is_animated(const HierarchyContext &context) const override;
+
+ private:
+ void write_hair_sample(const HierarchyContext &context,
+ struct Mesh *mesh,
std::vector<Imath::V3f> &verts,
std::vector<Imath::V3f> &norm_values,
std::vector<Imath::V2f> &uv_values,
std::vector<int32_t> &hvertices);
- void write_hair_child_sample(struct Mesh *mesh,
- ParticleSettings *part,
+ void write_hair_child_sample(const HierarchyContext &context,
+ struct Mesh *mesh,
std::vector<Imath::V3f> &verts,
std::vector<Imath::V3f> &norm_values,
std::vector<Imath::V2f> &uv_values,
std::vector<int32_t> &hvertices);
};
-#endif /* __ABC_WRITER_HAIR_H__ */
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/exporter/abc_writer_mball.cc b/source/blender/io/alembic/exporter/abc_writer_mball.cc
new file mode 100644
index 00000000000..167e392eb96
--- /dev/null
+++ b/source/blender/io/alembic/exporter/abc_writer_mball.cc
@@ -0,0 +1,90 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup balembic
+ */
+
+#include "abc_writer_mball.h"
+#include "abc_hierarchy_iterator.h"
+
+#include "BLI_assert.h"
+
+#include "BKE_displist.h"
+#include "BKE_lib_id.h"
+#include "BKE_mball.h"
+#include "BKE_mesh.h"
+#include "BKE_object.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meta_types.h"
+
+namespace blender {
+namespace io {
+namespace alembic {
+
+ABCMetaballWriter::ABCMetaballWriter(const ABCWriterConstructorArgs &args)
+ : ABCGenericMeshWriter(args)
+{
+}
+
+bool ABCMetaballWriter::is_supported(const HierarchyContext *context) const
+{
+ Scene *scene = DEG_get_input_scene(args_.depsgraph);
+ bool supported = is_basis_ball(scene, context->object) &&
+ ABCGenericMeshWriter::is_supported(context);
+ return supported;
+}
+
+bool ABCMetaballWriter::check_is_animated(const HierarchyContext & /*context*/) const
+{
+ /* We assume that metaballs are always animated, as the current object may
+ * not be animated but another ball in the same group may be. */
+ return true;
+}
+
+bool ABCMetaballWriter::export_as_subdivision_surface(Object * /*ob_eval*/) const
+{
+ /* Metaballs should be exported to subdivision surfaces, if the export options allow. */
+ return true;
+}
+
+Mesh *ABCMetaballWriter::get_export_mesh(Object *object_eval, bool &r_needsfree)
+{
+ Mesh *mesh_eval = BKE_object_get_evaluated_mesh(object_eval);
+ if (mesh_eval != nullptr) {
+ /* Mesh_eval only exists when generative modifiers are in use. */
+ r_needsfree = false;
+ return mesh_eval;
+ }
+ r_needsfree = true;
+ return BKE_mesh_new_from_object(args_.depsgraph, object_eval, false);
+}
+
+void ABCMetaballWriter::free_export_mesh(Mesh *mesh)
+{
+ BKE_id_free(nullptr, mesh);
+}
+
+bool ABCMetaballWriter::is_basis_ball(Scene *scene, Object *ob) const
+{
+ Object *basis_ob = BKE_mball_basis_find(scene, ob);
+ return ob == basis_ob;
+}
+
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/intern/abc_writer_camera.h b/source/blender/io/alembic/exporter/abc_writer_mball.h
index 3b515911a48..90d8c4d4b15 100644
--- a/source/blender/io/alembic/intern/abc_writer_camera.h
+++ b/source/blender/io/alembic/exporter/abc_writer_mball.h
@@ -13,33 +13,33 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#pragma once
/** \file
* \ingroup balembic
*/
-#ifndef __ABC_WRITER_CAMERA_H__
-#define __ABC_WRITER_CAMERA_H__
+#include "abc_writer_mesh.h"
-#include "abc_writer_object.h"
-
-/* ************************************************************************** */
-
-class AbcCameraWriter : public AbcObjectWriter {
- Alembic::AbcGeom::OCameraSchema m_camera_schema;
- Alembic::AbcGeom::CameraSample m_camera_sample;
- Alembic::AbcGeom::OCompoundProperty m_custom_data_container;
- Alembic::AbcGeom::OFloatProperty m_stereo_distance;
- Alembic::AbcGeom::OFloatProperty m_eye_separation;
+namespace blender {
+namespace io {
+namespace alembic {
+class ABCMetaballWriter : public ABCGenericMeshWriter {
public:
- AbcCameraWriter(Object *ob,
- AbcTransformWriter *parent,
- uint32_t time_sampling,
- ExportSettings &settings);
+ explicit ABCMetaballWriter(const ABCWriterConstructorArgs &args);
+
+ protected:
+ virtual Mesh *get_export_mesh(Object *object_eval, bool &r_needsfree) override;
+ virtual void free_export_mesh(Mesh *mesh) override;
+ virtual bool is_supported(const HierarchyContext *context) const override;
+ virtual bool check_is_animated(const HierarchyContext &context) const override;
+ virtual bool export_as_subdivision_surface(Object *ob_eval) const override;
private:
- virtual void do_write();
+ bool is_basis_ball(Scene *scene, Object *ob) const;
};
-#endif /* __ABC_WRITER_CAMERA_H__ */
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/intern/abc_writer_mesh.cc b/source/blender/io/alembic/exporter/abc_writer_mesh.cc
index df1734c9de1..07196f2b81f 100644
--- a/source/blender/io/alembic/intern/abc_writer_mesh.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_mesh.cc
@@ -19,29 +19,37 @@
*/
#include "abc_writer_mesh.h"
-#include "abc_axis_conversion.h"
-#include "abc_writer_transform.h"
+#include "abc_hierarchy_iterator.h"
+#include "intern/abc_axis_conversion.h"
-#include "DNA_material_types.h"
-#include "DNA_mesh_types.h"
-#include "DNA_meshdata_types.h"
-#include "DNA_object_fluidsim_types.h"
+#include "BLI_assert.h"
+#include "BLI_math_vector.h"
-#include "BKE_anim_data.h"
-#include "BKE_key.h"
+#include "BKE_customdata.h"
#include "BKE_lib_id.h"
#include "BKE_material.h"
#include "BKE_mesh.h"
-#include "BKE_mesh_runtime.h"
#include "BKE_modifier.h"
+#include "BKE_object.h"
#include "bmesh.h"
#include "bmesh_tools.h"
-#include "DEG_depsgraph_query.h"
+#include "DEG_depsgraph.h"
+
+#include "DNA_layer_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_object_fluidsim_types.h"
+#include "DNA_particle_types.h"
+
+#include "CLG_log.h"
+static CLG_LogRef LOG = {"io.alembic"};
using Alembic::Abc::FloatArraySample;
using Alembic::Abc::Int32ArraySample;
+using Alembic::Abc::OObject;
using Alembic::Abc::V2fArraySample;
using Alembic::Abc::V3fArraySample;
@@ -58,138 +66,83 @@ using Alembic::AbcGeom::OSubDSchema;
using Alembic::AbcGeom::OV2fGeomParam;
using Alembic::AbcGeom::UInt32ArraySample;
-/* NOTE: Alembic's polygon winding order is clockwise, to match with Renderman. */
-
-static void get_vertices(struct Mesh *mesh, std::vector<Imath::V3f> &points)
-{
- points.clear();
- points.resize(mesh->totvert);
+namespace blender {
+namespace io {
+namespace alembic {
- MVert *verts = mesh->mvert;
-
- for (int i = 0, e = mesh->totvert; i < e; i++) {
- copy_yup_from_zup(points[i].getValue(), verts[i].co);
- }
-}
+/* NOTE: Alembic's polygon winding order is clockwise, to match with Renderman. */
+static void get_vertices(struct Mesh *mesh, std::vector<Imath::V3f> &points);
static void get_topology(struct Mesh *mesh,
std::vector<int32_t> &poly_verts,
std::vector<int32_t> &loop_counts,
- bool &r_has_flat_shaded_poly)
-{
- const int num_poly = mesh->totpoly;
- const int num_loops = mesh->totloop;
- MLoop *mloop = mesh->mloop;
- MPoly *mpoly = mesh->mpoly;
- r_has_flat_shaded_poly = false;
-
- poly_verts.clear();
- loop_counts.clear();
- poly_verts.reserve(num_loops);
- loop_counts.reserve(num_poly);
-
- /* NOTE: data needs to be written in the reverse order. */
- for (int i = 0; i < num_poly; i++) {
- MPoly &poly = mpoly[i];
- loop_counts.push_back(poly.totloop);
-
- r_has_flat_shaded_poly |= (poly.flag & ME_SMOOTH) == 0;
-
- MLoop *loop = mloop + poly.loopstart + (poly.totloop - 1);
-
- for (int j = 0; j < poly.totloop; j++, loop--) {
- poly_verts.push_back(loop->v);
- }
- }
-}
-
+ bool &r_has_flat_shaded_poly);
static void get_creases(struct Mesh *mesh,
std::vector<int32_t> &indices,
std::vector<int32_t> &lengths,
- std::vector<float> &sharpnesses)
-{
- const float factor = 1.0f / 255.0f;
+ std::vector<float> &sharpnesses);
+static void get_loop_normals(struct Mesh *mesh,
+ std::vector<Imath::V3f> &normals,
+ bool has_flat_shaded_poly);
- indices.clear();
- lengths.clear();
- sharpnesses.clear();
+ABCGenericMeshWriter::ABCGenericMeshWriter(const ABCWriterConstructorArgs &args)
+ : ABCAbstractWriter(args), is_subd_(false)
+{
+}
- MEdge *edge = mesh->medge;
+void ABCGenericMeshWriter::create_alembic_objects(const HierarchyContext *context)
+{
+ if (!args_.export_params->apply_subdiv && export_as_subdivision_surface(context->object)) {
+ is_subd_ = args_.export_params->use_subdiv_schema;
+ }
- for (int i = 0, e = mesh->totedge; i < e; i++) {
- const float sharpness = static_cast<float>(edge[i].crease) * factor;
+ if (is_subd_) {
+ CLOG_INFO(&LOG, 2, "exporting OSubD %s", args_.abc_path.c_str());
+ abc_subdiv_ = OSubD(args_.abc_parent, args_.abc_name, timesample_index_);
+ abc_subdiv_schema_ = abc_subdiv_.getSchema();
+ }
+ else {
+ CLOG_INFO(&LOG, 2, "exporting OPolyMesh %s", args_.abc_path.c_str());
+ abc_poly_mesh_ = OPolyMesh(args_.abc_parent, args_.abc_name, timesample_index_);
+ abc_poly_mesh_schema_ = abc_poly_mesh_.getSchema();
- if (sharpness != 0.0f) {
- indices.push_back(edge[i].v1);
- indices.push_back(edge[i].v2);
- sharpnesses.push_back(sharpness);
- }
+ OCompoundProperty typeContainer = abc_poly_mesh_.getSchema().getUserProperties();
+ OBoolProperty type(typeContainer, "meshtype");
+ type.set(subsurf_modifier_ == nullptr);
}
- lengths.resize(sharpnesses.size(), 2);
+ Scene *scene_eval = DEG_get_evaluated_scene(args_.depsgraph);
+ liquid_sim_modifier_ = get_liquid_sim_modifier(scene_eval, context->object);
}
-static void get_loop_normals(struct Mesh *mesh,
- std::vector<Imath::V3f> &normals,
- bool has_flat_shaded_poly)
+ABCGenericMeshWriter::~ABCGenericMeshWriter()
{
- normals.clear();
-
- /* If all polygons are smooth shaded, and there are no custom normals, we don't need to export
- * normals at all. This is also done by other software, see T71246. */
- if (!has_flat_shaded_poly && !CustomData_has_layer(&mesh->ldata, CD_CUSTOMLOOPNORMAL) &&
- (mesh->flag & ME_AUTOSMOOTH) == 0) {
- return;
- }
-
- BKE_mesh_calc_normals_split(mesh);
- const float(*lnors)[3] = static_cast<float(*)[3]>(CustomData_get_layer(&mesh->ldata, CD_NORMAL));
- BLI_assert(lnors != NULL || !"BKE_mesh_calc_normals_split() should have computed CD_NORMAL");
-
- normals.resize(mesh->totloop);
+}
- /* NOTE: data needs to be written in the reverse order. */
- int abc_index = 0;
- MPoly *mp = mesh->mpoly;
- for (int i = 0, e = mesh->totpoly; i < e; i++, mp++) {
- for (int j = mp->totloop - 1; j >= 0; j--, abc_index++) {
- int blender_index = mp->loopstart + j;
- copy_yup_from_zup(normals[abc_index].getValue(), lnors[blender_index]);
- }
+const Alembic::Abc::OObject ABCGenericMeshWriter::get_alembic_object() const
+{
+ if (is_subd_) {
+ return abc_subdiv_;
}
+ return abc_poly_mesh_;
}
-/* *************** Modifiers *************** */
-
-/* check if the mesh is a subsurf, ignoring disabled modifiers and
- * displace if it's after subsurf. */
-static ModifierData *get_subsurf_modifier(Scene *scene, Object *ob)
+bool ABCGenericMeshWriter::export_as_subdivision_surface(Object *ob_eval) const
{
- ModifierData *md = static_cast<ModifierData *>(ob->modifiers.last);
+ ModifierData *md = static_cast<ModifierData *>(ob_eval->modifiers.last);
for (; md; md = md->prev) {
- if (!BKE_modifier_is_enabled(scene, md, eModifierMode_Render)) {
- continue;
- }
-
- if (md->type == eModifierType_Subsurf) {
- SubsurfModifierData *smd = reinterpret_cast<SubsurfModifierData *>(md);
-
- if (smd->subdivType == ME_CC_SUBSURF) {
- return md;
- }
- }
-
- /* mesh is not a subsurf. break */
- if ((md->type != eModifierType_Displace) && (md->type != eModifierType_ParticleSystem)) {
- return NULL;
+ /* This modifier has been temporarily disabled by SubdivModifierDisabler,
+ * so this indicates this is to be exported as subdivision surface. */
+ if (md->type == eModifierType_Subsurf && (md->mode & eModifierMode_DisableTemporary)) {
+ return true;
}
}
- return NULL;
+ return false;
}
-static ModifierData *get_liquid_sim_modifier(Scene *scene, Object *ob)
+ModifierData *ABCGenericMeshWriter::get_liquid_sim_modifier(Scene *scene, Object *ob)
{
ModifierData *md = BKE_modifiers_findby_type(ob, eModifierType_Fluidsim);
@@ -201,122 +154,99 @@ static ModifierData *get_liquid_sim_modifier(Scene *scene, Object *ob)
}
}
- return NULL;
+ return nullptr;
}
-/* ************************************************************************** */
-
-AbcGenericMeshWriter::AbcGenericMeshWriter(Object *ob,
- AbcTransformWriter *parent,
- uint32_t time_sampling,
- ExportSettings &settings)
- : AbcObjectWriter(ob, time_sampling, settings, parent)
+bool ABCGenericMeshWriter::is_supported(const HierarchyContext *context) const
{
- m_is_animated = isAnimated();
- m_subsurf_mod = NULL;
- m_is_subd = false;
-
- /* If the object is static, use the default static time sampling. */
- if (!m_is_animated) {
- time_sampling = 0;
- }
+ Object *object = context->object;
+ bool is_dupli = context->duplicator != nullptr;
+ int base_flag;
- if (!m_settings.apply_subdiv) {
- m_subsurf_mod = get_subsurf_modifier(m_settings.scene, m_object);
- m_is_subd = (m_subsurf_mod != NULL);
+ if (is_dupli) {
+ /* Construct the object's base flags from its dupli-parent, just like is done in
+ * deg_objects_dupli_iterator_next(). Without this, the visibility check below will fail. Doing
+ * this here, instead of a more suitable location in AbstractHierarchyIterator, prevents
+ * copying the Object for every dupli. */
+ base_flag = object->base_flag;
+ object->base_flag = context->duplicator->base_flag | BASE_FROM_DUPLI;
}
- m_is_liquid = (get_liquid_sim_modifier(m_settings.scene, m_object) != NULL);
+ int visibility = BKE_object_visibility(
+ object, DAG_EVAL_RENDER /* TODO(Sybren): add evaluation mode to export options? */);
- while (parent->alembicXform().getChildHeader(m_name)) {
- m_name.append("_");
+ if (is_dupli) {
+ object->base_flag = base_flag;
}
- if (m_settings.use_subdiv_schema && m_is_subd) {
- OSubD subd(parent->alembicXform(), m_name, m_time_sampling);
- m_subdiv_schema = subd.getSchema();
- }
- else {
- OPolyMesh mesh(parent->alembicXform(), m_name, m_time_sampling);
- m_mesh_schema = mesh.getSchema();
-
- OCompoundProperty typeContainer = m_mesh_schema.getUserProperties();
- OBoolProperty type(typeContainer, "meshtype");
- type.set(m_is_subd);
- }
+ return (visibility & OB_VISIBLE_SELF) != 0;
}
-AbcGenericMeshWriter::~AbcGenericMeshWriter()
+void ABCGenericMeshWriter::do_write(HierarchyContext &context)
{
- if (m_subsurf_mod) {
- m_subsurf_mod->mode &= ~eModifierMode_DisableTemporary;
- }
-}
+ Object *object = context.object;
+ bool needsfree = false;
-bool AbcGenericMeshWriter::isAnimated() const
-{
- if (BKE_animdata_id_is_animated(static_cast<ID *>(m_object->data))) {
- return true;
- }
- if (BKE_key_from_object(m_object) != NULL) {
- return true;
- }
+ Mesh *mesh = get_export_mesh(object, needsfree);
- /* Test modifiers. */
- ModifierData *md = static_cast<ModifierData *>(m_object->modifiers.first);
- while (md) {
+ if (mesh == nullptr) {
+ return;
+ }
- if (md->type != eModifierType_Subsurf) {
- return true;
- }
+ if (args_.export_params->triangulate) {
+ const bool tag_only = false;
+ const int quad_method = args_.export_params->quad_method;
+ const int ngon_method = args_.export_params->ngon_method;
- md = md->next;
- }
+ struct BMeshCreateParams bmcp = {false};
+ struct BMeshFromMeshParams bmfmp = {true, false, false, 0};
+ BMesh *bm = BKE_mesh_to_bmesh_ex(mesh, &bmcp, &bmfmp);
- return false;
-}
+ BM_mesh_triangulate(bm, quad_method, ngon_method, 4, tag_only, nullptr, nullptr, nullptr);
-void AbcGenericMeshWriter::setIsAnimated(bool is_animated)
-{
- m_is_animated = is_animated;
-}
+ Mesh *triangulated_mesh = BKE_mesh_from_bmesh_for_eval_nomain(bm, nullptr, mesh);
+ BM_mesh_free(bm);
-void AbcGenericMeshWriter::do_write()
-{
- /* We have already stored a sample for this object. */
- if (!m_first_frame && !m_is_animated) {
- return;
+ if (needsfree) {
+ free_export_mesh(mesh);
+ }
+ mesh = triangulated_mesh;
+ needsfree = true;
}
- bool needsfree;
- struct Mesh *mesh = getFinalMesh(needsfree);
+ m_custom_data_config.pack_uvs = args_.export_params->packuv;
+ m_custom_data_config.mpoly = mesh->mpoly;
+ m_custom_data_config.mloop = mesh->mloop;
+ m_custom_data_config.totpoly = mesh->totpoly;
+ m_custom_data_config.totloop = mesh->totloop;
+ m_custom_data_config.totvert = mesh->totvert;
try {
- if (m_settings.use_subdiv_schema && m_subdiv_schema.valid()) {
- writeSubD(mesh);
+ if (is_subd_) {
+ write_subd(context, mesh);
}
else {
- writeMesh(mesh);
+ write_mesh(context, mesh);
}
if (needsfree) {
- freeEvaluatedMesh(mesh);
+ free_export_mesh(mesh);
}
}
catch (...) {
if (needsfree) {
- freeEvaluatedMesh(mesh);
+ free_export_mesh(mesh);
}
throw;
}
}
-void AbcGenericMeshWriter::freeEvaluatedMesh(struct Mesh *mesh)
+void ABCGenericMeshWriter::free_export_mesh(Mesh *mesh)
{
- BKE_id_free(NULL, mesh);
+ BKE_id_free(nullptr, mesh);
}
-void AbcGenericMeshWriter::writeMesh(struct Mesh *mesh)
+void ABCGenericMeshWriter::write_mesh(HierarchyContext &context, Mesh *mesh)
{
std::vector<Imath::V3f> points, normals;
std::vector<int32_t> poly_verts, loop_counts;
@@ -326,32 +256,33 @@ void AbcGenericMeshWriter::writeMesh(struct Mesh *mesh)
get_vertices(mesh, points);
get_topology(mesh, poly_verts, loop_counts, has_flat_shaded_poly);
- if (m_first_frame && m_settings.export_face_sets) {
- writeFaceSets(mesh, m_mesh_schema);
+ if (!frame_has_been_written_ && args_.export_params->face_sets) {
+ write_face_sets(context.object, mesh, abc_poly_mesh_schema_);
}
- m_mesh_sample = OPolyMeshSchema::Sample(
+ OPolyMeshSchema::Sample mesh_sample = OPolyMeshSchema::Sample(
V3fArraySample(points), Int32ArraySample(poly_verts), Int32ArraySample(loop_counts));
- UVSample sample;
- if (m_settings.export_uvs) {
- const char *name = get_uv_sample(sample, m_custom_data_config, &mesh->ldata);
+ UVSample uvs_and_indices;
- if (!sample.indices.empty() && !sample.uvs.empty()) {
+ if (!frame_has_been_written_ && args_.export_params->uvs) {
+ const char *name = get_uv_sample(uvs_and_indices, m_custom_data_config, &mesh->ldata);
+
+ if (!uvs_and_indices.indices.empty() && !uvs_and_indices.uvs.empty()) {
OV2fGeomParam::Sample uv_sample;
- uv_sample.setVals(V2fArraySample(sample.uvs));
- uv_sample.setIndices(UInt32ArraySample(sample.indices));
+ uv_sample.setVals(V2fArraySample(uvs_and_indices.uvs));
+ uv_sample.setIndices(UInt32ArraySample(uvs_and_indices.indices));
uv_sample.setScope(kFacevaryingScope);
- m_mesh_schema.setUVSourceName(name);
- m_mesh_sample.setUVs(uv_sample);
+ abc_poly_mesh_schema_.setUVSourceName(name);
+ mesh_sample.setUVs(uv_sample);
}
write_custom_data(
- m_mesh_schema.getArbGeomParams(), m_custom_data_config, &mesh->ldata, CD_MLOOPUV);
+ abc_poly_mesh_schema_.getArbGeomParams(), m_custom_data_config, &mesh->ldata, CD_MLOOPUV);
}
- if (m_settings.export_normals) {
+ if (args_.export_params->normals) {
get_loop_normals(mesh, normals, has_flat_shaded_poly);
ON3fGeomParam::Sample normals_sample;
@@ -360,22 +291,23 @@ void AbcGenericMeshWriter::writeMesh(struct Mesh *mesh)
normals_sample.setVals(V3fArraySample(normals));
}
- m_mesh_sample.setNormals(normals_sample);
+ mesh_sample.setNormals(normals_sample);
}
- if (m_is_liquid) {
- getVelocities(mesh, velocities);
- m_mesh_sample.setVelocities(V3fArraySample(velocities));
+ if (liquid_sim_modifier_ != nullptr) {
+ get_velocities(mesh, velocities);
+ mesh_sample.setVelocities(V3fArraySample(velocities));
}
- m_mesh_sample.setSelfBounds(bounds());
+ update_bounding_box(context.object);
+ mesh_sample.setSelfBounds(bounding_box_);
- m_mesh_schema.set(m_mesh_sample);
+ abc_poly_mesh_schema_.set(mesh_sample);
- writeArbGeoParams(mesh);
+ write_arb_geo_params(mesh);
}
-void AbcGenericMeshWriter::writeSubD(struct Mesh *mesh)
+void ABCGenericMeshWriter::write_subd(HierarchyContext &context, struct Mesh *mesh)
{
std::vector<float> crease_sharpness;
std::vector<Imath::V3f> points;
@@ -387,15 +319,15 @@ void AbcGenericMeshWriter::writeSubD(struct Mesh *mesh)
get_topology(mesh, poly_verts, loop_counts, has_flat_poly);
get_creases(mesh, crease_indices, crease_lengths, crease_sharpness);
- if (m_first_frame && m_settings.export_face_sets) {
- writeFaceSets(mesh, m_subdiv_schema);
+ if (!frame_has_been_written_ && args_.export_params->face_sets) {
+ write_face_sets(context.object, mesh, abc_subdiv_schema_);
}
- m_subdiv_sample = OSubDSchema::Sample(
+ OSubDSchema::Sample subdiv_sample = OSubDSchema::Sample(
V3fArraySample(points), Int32ArraySample(poly_verts), Int32ArraySample(loop_counts));
UVSample sample;
- if (m_first_frame && m_settings.export_uvs) {
+ if (!frame_has_been_written_ && args_.export_params->uvs) {
const char *name = get_uv_sample(sample, m_custom_data_config, &mesh->ldata);
if (!sample.indices.empty() && !sample.uvs.empty()) {
@@ -404,30 +336,32 @@ void AbcGenericMeshWriter::writeSubD(struct Mesh *mesh)
uv_sample.setIndices(UInt32ArraySample(sample.indices));
uv_sample.setScope(kFacevaryingScope);
- m_subdiv_schema.setUVSourceName(name);
- m_subdiv_sample.setUVs(uv_sample);
+ abc_subdiv_schema_.setUVSourceName(name);
+ subdiv_sample.setUVs(uv_sample);
}
write_custom_data(
- m_subdiv_schema.getArbGeomParams(), m_custom_data_config, &mesh->ldata, CD_MLOOPUV);
+ abc_subdiv_schema_.getArbGeomParams(), m_custom_data_config, &mesh->ldata, CD_MLOOPUV);
}
if (!crease_indices.empty()) {
- m_subdiv_sample.setCreaseIndices(Int32ArraySample(crease_indices));
- m_subdiv_sample.setCreaseLengths(Int32ArraySample(crease_lengths));
- m_subdiv_sample.setCreaseSharpnesses(FloatArraySample(crease_sharpness));
+ subdiv_sample.setCreaseIndices(Int32ArraySample(crease_indices));
+ subdiv_sample.setCreaseLengths(Int32ArraySample(crease_lengths));
+ subdiv_sample.setCreaseSharpnesses(FloatArraySample(crease_sharpness));
}
- m_subdiv_sample.setSelfBounds(bounds());
- m_subdiv_schema.set(m_subdiv_sample);
+ update_bounding_box(context.object);
+ subdiv_sample.setSelfBounds(bounding_box_);
+ abc_subdiv_schema_.set(subdiv_sample);
- writeArbGeoParams(mesh);
+ write_arb_geo_params(mesh);
}
-template<typename Schema> void AbcGenericMeshWriter::writeFaceSets(struct Mesh *me, Schema &schema)
+template<typename Schema>
+void ABCGenericMeshWriter::write_face_sets(Object *object, struct Mesh *mesh, Schema &schema)
{
std::map<std::string, std::vector<int32_t>> geo_groups;
- getGeoGroups(me, geo_groups);
+ get_geo_groups(object, mesh, geo_groups);
std::map<std::string, std::vector<int32_t>>::iterator it;
for (it = geo_groups.begin(); it != geo_groups.end(); ++it) {
@@ -438,83 +372,35 @@ template<typename Schema> void AbcGenericMeshWriter::writeFaceSets(struct Mesh *
}
}
-Mesh *AbcGenericMeshWriter::getFinalMesh(bool &r_needsfree)
+void ABCGenericMeshWriter::write_arb_geo_params(struct Mesh *me)
{
- /* We don't want subdivided mesh data */
- if (m_subsurf_mod) {
- m_subsurf_mod->mode |= eModifierMode_DisableTemporary;
- }
-
- r_needsfree = false;
-
- Scene *scene = DEG_get_evaluated_scene(m_settings.depsgraph);
- Object *ob_eval = DEG_get_evaluated_object(m_settings.depsgraph, m_object);
- struct Mesh *mesh = getEvaluatedMesh(scene, ob_eval, r_needsfree);
-
- if (m_subsurf_mod) {
- m_subsurf_mod->mode &= ~eModifierMode_DisableTemporary;
- }
-
- if (m_settings.triangulate) {
- const bool tag_only = false;
- const int quad_method = m_settings.quad_method;
- const int ngon_method = m_settings.ngon_method;
-
- struct BMeshCreateParams bmcp = {false};
- struct BMeshFromMeshParams bmfmp = {true, false, false, 0};
- BMesh *bm = BKE_mesh_to_bmesh_ex(mesh, &bmcp, &bmfmp);
-
- BM_mesh_triangulate(bm, quad_method, ngon_method, 4, tag_only, NULL, NULL, NULL);
-
- Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh);
- BM_mesh_free(bm);
-
- if (r_needsfree) {
- BKE_id_free(NULL, mesh);
- }
-
- mesh = result;
- r_needsfree = true;
+ if (liquid_sim_modifier_ != nullptr) {
+ /* We don't need anything more for liquid meshes. */
+ return;
}
- m_custom_data_config.pack_uvs = m_settings.pack_uv;
- m_custom_data_config.mpoly = mesh->mpoly;
- m_custom_data_config.mloop = mesh->mloop;
- m_custom_data_config.totpoly = mesh->totpoly;
- m_custom_data_config.totloop = mesh->totloop;
- m_custom_data_config.totvert = mesh->totvert;
-
- return mesh;
-}
-
-void AbcGenericMeshWriter::writeArbGeoParams(struct Mesh *me)
-{
- if (m_is_liquid) {
- /* We don't need anything more for liquid meshes. */
+ if (frame_has_been_written_ || !args_.export_params->vcolors) {
return;
}
- if (m_first_frame && m_settings.export_vcols) {
- if (m_subdiv_schema.valid()) {
- write_custom_data(
- m_subdiv_schema.getArbGeomParams(), m_custom_data_config, &me->ldata, CD_MLOOPCOL);
- }
- else {
- write_custom_data(
- m_mesh_schema.getArbGeomParams(), m_custom_data_config, &me->ldata, CD_MLOOPCOL);
- }
+ OCompoundProperty arb_geom_params;
+ if (is_subd_) {
+ arb_geom_params = abc_subdiv_.getSchema().getArbGeomParams();
}
+ else {
+ arb_geom_params = abc_poly_mesh_.getSchema().getArbGeomParams();
+ }
+ write_custom_data(arb_geom_params, m_custom_data_config, &me->ldata, CD_MLOOPCOL);
}
-void AbcGenericMeshWriter::getVelocities(struct Mesh *mesh, std::vector<Imath::V3f> &vels)
+void ABCGenericMeshWriter::get_velocities(struct Mesh *mesh, std::vector<Imath::V3f> &vels)
{
const int totverts = mesh->totvert;
vels.clear();
vels.resize(totverts);
- ModifierData *md = get_liquid_sim_modifier(m_settings.scene, m_object);
- FluidsimModifierData *fmd = reinterpret_cast<FluidsimModifierData *>(md);
+ FluidsimModifierData *fmd = reinterpret_cast<FluidsimModifierData *>(liquid_sim_modifier_);
FluidsimSettings *fss = fmd->fss;
if (fss->meshVelocities) {
@@ -530,8 +416,9 @@ void AbcGenericMeshWriter::getVelocities(struct Mesh *mesh, std::vector<Imath::V
}
}
-void AbcGenericMeshWriter::getGeoGroups(struct Mesh *mesh,
- std::map<std::string, std::vector<int32_t>> &geo_groups)
+void ABCGenericMeshWriter::get_geo_groups(Object *object,
+ struct Mesh *mesh,
+ std::map<std::string, std::vector<int32_t>> &geo_groups)
{
const int num_poly = mesh->totpoly;
MPoly *polygons = mesh->mpoly;
@@ -540,13 +427,13 @@ void AbcGenericMeshWriter::getGeoGroups(struct Mesh *mesh,
MPoly &current_poly = polygons[i];
short mnr = current_poly.mat_nr;
- Material *mat = BKE_object_material_get(m_object, mnr + 1);
+ Material *mat = BKE_object_material_get(object, mnr + 1);
if (!mat) {
continue;
}
- std::string name = get_id_name(&mat->id);
+ std::string name = args_.hierarchy_iterator->get_id_name(&mat->id);
if (geo_groups.find(name) == geo_groups.end()) {
std::vector<int32_t> faceArray;
@@ -557,9 +444,9 @@ void AbcGenericMeshWriter::getGeoGroups(struct Mesh *mesh,
}
if (geo_groups.size() == 0) {
- Material *mat = BKE_object_material_get(m_object, 1);
+ Material *mat = BKE_object_material_get(object, 1);
- std::string name = (mat) ? get_id_name(&mat->id) : "default";
+ std::string name = (mat) ? args_.hierarchy_iterator->get_id_name(&mat->id) : "default";
std::vector<int32_t> faceArray;
@@ -571,21 +458,116 @@ void AbcGenericMeshWriter::getGeoGroups(struct Mesh *mesh,
}
}
-AbcMeshWriter::AbcMeshWriter(Object *ob,
- AbcTransformWriter *parent,
- uint32_t time_sampling,
- ExportSettings &settings)
- : AbcGenericMeshWriter(ob, parent, time_sampling, settings)
+/* NOTE: Alembic's polygon winding order is clockwise, to match with Renderman. */
+
+static void get_vertices(struct Mesh *mesh, std::vector<Imath::V3f> &points)
{
+ points.clear();
+ points.resize(mesh->totvert);
+
+ MVert *verts = mesh->mvert;
+
+ for (int i = 0, e = mesh->totvert; i < e; i++) {
+ copy_yup_from_zup(points[i].getValue(), verts[i].co);
+ }
}
-AbcMeshWriter::~AbcMeshWriter()
+static void get_topology(struct Mesh *mesh,
+ std::vector<int32_t> &poly_verts,
+ std::vector<int32_t> &loop_counts,
+ bool &r_has_flat_shaded_poly)
{
+ const int num_poly = mesh->totpoly;
+ const int num_loops = mesh->totloop;
+ MLoop *mloop = mesh->mloop;
+ MPoly *mpoly = mesh->mpoly;
+ r_has_flat_shaded_poly = false;
+
+ poly_verts.clear();
+ loop_counts.clear();
+ poly_verts.reserve(num_loops);
+ loop_counts.reserve(num_poly);
+
+ /* NOTE: data needs to be written in the reverse order. */
+ for (int i = 0; i < num_poly; i++) {
+ MPoly &poly = mpoly[i];
+ loop_counts.push_back(poly.totloop);
+
+ r_has_flat_shaded_poly |= (poly.flag & ME_SMOOTH) == 0;
+
+ MLoop *loop = mloop + poly.loopstart + (poly.totloop - 1);
+
+ for (int j = 0; j < poly.totloop; j++, loop--) {
+ poly_verts.push_back(loop->v);
+ }
+ }
+}
+
+static void get_creases(struct Mesh *mesh,
+ std::vector<int32_t> &indices,
+ std::vector<int32_t> &lengths,
+ std::vector<float> &sharpnesses)
+{
+ const float factor = 1.0f / 255.0f;
+
+ indices.clear();
+ lengths.clear();
+ sharpnesses.clear();
+
+ MEdge *edge = mesh->medge;
+
+ for (int i = 0, e = mesh->totedge; i < e; i++) {
+ const float sharpness = static_cast<float>(edge[i].crease) * factor;
+
+ if (sharpness != 0.0f) {
+ indices.push_back(edge[i].v1);
+ indices.push_back(edge[i].v2);
+ sharpnesses.push_back(sharpness);
+ }
+ }
+
+ lengths.resize(sharpnesses.size(), 2);
}
-Mesh *AbcMeshWriter::getEvaluatedMesh(Scene *scene_eval,
- Object *ob_eval,
- bool &UNUSED(r_needsfree))
+static void get_loop_normals(struct Mesh *mesh,
+ std::vector<Imath::V3f> &normals,
+ bool has_flat_shaded_poly)
+{
+ normals.clear();
+
+ /* If all polygons are smooth shaded, and there are no custom normals, we don't need to export
+ * normals at all. This is also done by other software, see T71246. */
+ if (!has_flat_shaded_poly && !CustomData_has_layer(&mesh->ldata, CD_CUSTOMLOOPNORMAL) &&
+ (mesh->flag & ME_AUTOSMOOTH) == 0) {
+ return;
+ }
+
+ BKE_mesh_calc_normals_split(mesh);
+ const float(*lnors)[3] = static_cast<float(*)[3]>(CustomData_get_layer(&mesh->ldata, CD_NORMAL));
+ BLI_assert(lnors != nullptr || !"BKE_mesh_calc_normals_split() should have computed CD_NORMAL");
+
+ normals.resize(mesh->totloop);
+
+ /* NOTE: data needs to be written in the reverse order. */
+ int abc_index = 0;
+ MPoly *mp = mesh->mpoly;
+ for (int i = 0, e = mesh->totpoly; i < e; i++, mp++) {
+ for (int j = mp->totloop - 1; j >= 0; j--, abc_index++) {
+ int blender_index = mp->loopstart + j;
+ copy_yup_from_zup(normals[abc_index].getValue(), lnors[blender_index]);
+ }
+ }
+}
+
+ABCMeshWriter::ABCMeshWriter(const ABCWriterConstructorArgs &args) : ABCGenericMeshWriter(args)
{
- return mesh_get_eval_final(m_settings.depsgraph, scene_eval, ob_eval, &CD_MASK_MESH);
}
+
+Mesh *ABCMeshWriter::get_export_mesh(Object *object_eval, bool & /*r_needsfree*/)
+{
+ return BKE_object_get_evaluated_mesh(object_eval);
+}
+
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/exporter/abc_writer_mesh.h b/source/blender/io/alembic/exporter/abc_writer_mesh.h
new file mode 100644
index 00000000000..bf4d4e9b9d8
--- /dev/null
+++ b/source/blender/io/alembic/exporter/abc_writer_mesh.h
@@ -0,0 +1,95 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+#pragma once
+
+/** \file
+ * \ingroup balembic
+ */
+
+#include "abc_writer_abstract.h"
+#include "intern/abc_customdata.h"
+
+#include <Alembic/AbcGeom/OPolyMesh.h>
+#include <Alembic/AbcGeom/OSubD.h>
+
+struct ModifierData;
+
+namespace blender {
+namespace io {
+namespace alembic {
+
+/* Writer for Alembic geometry. Does not assume the object is a mesh object. */
+class ABCGenericMeshWriter : public ABCAbstractWriter {
+ private:
+ /* Either polymesh or subd is used, depending on is_subd_.
+ * References to the schema must be kept, or Alembic will not properly write. */
+ Alembic::AbcGeom::OPolyMesh abc_poly_mesh_;
+ Alembic::AbcGeom::OPolyMeshSchema abc_poly_mesh_schema_;
+
+ Alembic::AbcGeom::OSubD abc_subdiv_;
+ Alembic::AbcGeom::OSubDSchema abc_subdiv_schema_;
+
+ /* Determines whether a poly mesh or a subdivision surface is exported.
+ * The value is set by an export option but only true if there is a subdivision modifier on the
+ * exported object. */
+ bool is_subd_;
+ ModifierData *subsurf_modifier_;
+ ModifierData *liquid_sim_modifier_;
+
+ CDStreamConfig m_custom_data_config;
+
+ public:
+ explicit ABCGenericMeshWriter(const ABCWriterConstructorArgs &args);
+ virtual ~ABCGenericMeshWriter();
+
+ virtual void create_alembic_objects(const HierarchyContext *context) override;
+ virtual const Alembic::Abc::OObject get_alembic_object() const;
+
+ protected:
+ virtual bool is_supported(const HierarchyContext *context) const override;
+ virtual void do_write(HierarchyContext &context) override;
+
+ virtual Mesh *get_export_mesh(Object *object_eval, bool &r_needsfree) = 0;
+ virtual void free_export_mesh(Mesh *mesh);
+
+ virtual bool export_as_subdivision_surface(Object *ob_eval) const;
+
+ private:
+ void write_mesh(HierarchyContext &context, Mesh *mesh);
+ void write_subd(HierarchyContext &context, Mesh *mesh);
+ template<typename Schema> void write_face_sets(Object *object, Mesh *mesh, Schema &schema);
+
+ ModifierData *get_liquid_sim_modifier(Scene *scene_eval, Object *ob_eval);
+
+ void write_arb_geo_params(Mesh *me);
+ void get_velocities(Mesh *mesh, std::vector<Imath::V3f> &vels);
+ void get_geo_groups(Object *object,
+ Mesh *mesh,
+ std::map<std::string, std::vector<int32_t>> &geo_groups);
+};
+
+/* Writer for Alembic geometry of Blender Mesh objects. */
+class ABCMeshWriter : public ABCGenericMeshWriter {
+ public:
+ ABCMeshWriter(const ABCWriterConstructorArgs &args);
+
+ protected:
+ virtual Mesh *get_export_mesh(Object *object_eval, bool &r_needsfree) override;
+};
+
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/intern/abc_writer_nurbs.cc b/source/blender/io/alembic/exporter/abc_writer_nurbs.cc
index 8b4a1050d33..1fd382214a6 100644
--- a/source/blender/io/alembic/intern/abc_writer_nurbs.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_nurbs.cc
@@ -19,8 +19,7 @@
*/
#include "abc_writer_nurbs.h"
-#include "abc_axis_conversion.h"
-#include "abc_writer_transform.h"
+#include "intern/abc_axis_conversion.h"
#include "DNA_curve_types.h"
#include "DNA_object_types.h"
@@ -29,48 +28,70 @@
#include "BKE_curve.h"
+#include "CLG_log.h"
+static CLG_LogRef LOG = {"io.alembic"};
+
+namespace blender {
+namespace io {
+namespace alembic {
+
+using Alembic::Abc::OObject;
using Alembic::AbcGeom::FloatArraySample;
using Alembic::AbcGeom::OBoolProperty;
using Alembic::AbcGeom::OCompoundProperty;
using Alembic::AbcGeom::ONuPatch;
using Alembic::AbcGeom::ONuPatchSchema;
-AbcNurbsWriter::AbcNurbsWriter(Object *ob,
- AbcTransformWriter *parent,
- uint32_t time_sampling,
- ExportSettings &settings)
- : AbcObjectWriter(ob, time_sampling, settings, parent)
+ABCNurbsWriter::ABCNurbsWriter(const ABCWriterConstructorArgs &args) : ABCAbstractWriter(args)
{
- m_is_animated = isAnimated();
-
- /* if the object is static, use the default static time sampling */
- if (!m_is_animated) {
- m_time_sampling = 0;
- }
+}
- Curve *curve = static_cast<Curve *>(m_object->data);
- size_t numNurbs = BLI_listbase_count(&curve->nurb);
+void ABCNurbsWriter::create_alembic_objects(const HierarchyContext *context)
+{
+ Curve *curve = static_cast<Curve *>(context->object->data);
+ size_t num_nurbs = BLI_listbase_count(&curve->nurb);
+ OObject abc_parent = args_.abc_parent;
+ const char *abc_parent_path = abc_parent.getFullName().c_str();
- for (size_t i = 0; i < numNurbs; i++) {
- std::stringstream str;
- str << m_name << '_' << i;
+ for (size_t i = 0; i < num_nurbs; i++) {
+ std::stringstream patch_name_stream;
+ patch_name_stream << args_.abc_name << '_' << i;
- while (parent->alembicXform().getChildHeader(str.str())) {
- str << "_";
+ while (abc_parent.getChildHeader(patch_name_stream.str())) {
+ patch_name_stream << "_";
}
- ONuPatch nurbs(parent->alembicXform(), str.str().c_str(), m_time_sampling);
- m_nurbs_schema.push_back(nurbs.getSchema());
+ std::string patch_name = patch_name_stream.str();
+ CLOG_INFO(&LOG, 2, "exporting %s/%s", abc_parent_path, patch_name.c_str());
+
+ ONuPatch nurbs(abc_parent, patch_name.c_str(), timesample_index_);
+ abc_nurbs_.push_back(nurbs);
+ abc_nurbs_schemas_.push_back(nurbs.getSchema());
+ }
+}
+
+const OObject ABCNurbsWriter::get_alembic_object() const
+{
+ if (abc_nurbs_.empty()) {
+ return OObject();
}
+ /* For parenting purposes within the Alembic file, all NURBS patches are equal, so just use the
+ * first one. */
+ return abc_nurbs_[0];
}
-bool AbcNurbsWriter::isAnimated() const
+bool ABCNurbsWriter::check_is_animated(const HierarchyContext &context) const
{
- /* check if object has shape keys */
- Curve *cu = static_cast<Curve *>(m_object->data);
+ /* Check if object has shape keys. */
+ Curve *cu = static_cast<Curve *>(context.object->data);
return (cu->key != NULL);
}
+bool ABCNurbsWriter::is_supported(const HierarchyContext *context) const
+{
+ return ELEM(context->object->type, OB_SURF, OB_CURVE);
+}
+
static void get_knots(std::vector<float> &knots, const int num_knots, float *nu_knots)
{
if (num_knots <= 1) {
@@ -91,22 +112,13 @@ static void get_knots(std::vector<float> &knots, const int num_knots, float *nu_
knots.push_back(2.0f * knots[num_knots] - knots[num_knots - 1]);
}
-void AbcNurbsWriter::do_write()
+void ABCNurbsWriter::do_write(HierarchyContext &context)
{
- /* we have already stored a sample for this object. */
- if (!m_first_frame && !m_is_animated) {
- return;
- }
-
- if (!ELEM(m_object->type, OB_SURF, OB_CURVE)) {
- return;
- }
-
- Curve *curve = static_cast<Curve *>(m_object->data);
+ Curve *curve = static_cast<Curve *>(context.object->data);
ListBase *nulb;
- if (m_object->runtime.curve_cache->deformed_nurbs.first != NULL) {
- nulb = &m_object->runtime.curve_cache->deformed_nurbs;
+ if (context.object->runtime.curve_cache->deformed_nurbs.first != NULL) {
+ nulb = &context.object->runtime.curve_cache->deformed_nurbs;
}
else {
nulb = BKE_curve_nurbs_get(curve);
@@ -143,7 +155,7 @@ void AbcNurbsWriter::do_write()
/* TODO(kevin): to accommodate other software we should duplicate control
* points to indicate that a NURBS is cyclic. */
- OCompoundProperty user_props = m_nurbs_schema[count].getUserProperties();
+ OCompoundProperty user_props = abc_nurbs_schemas_[count].getUserProperties();
if ((nu->flagu & CU_NURB_ENDPOINT) != 0) {
OBoolProperty prop(user_props, "endpoint_u");
@@ -165,6 +177,10 @@ void AbcNurbsWriter::do_write()
prop.set(true);
}
- m_nurbs_schema[count].set(sample);
+ abc_nurbs_schemas_[count].set(sample);
}
}
+
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/exporter/abc_writer_nurbs.h b/source/blender/io/alembic/exporter/abc_writer_nurbs.h
new file mode 100644
index 00000000000..23af4c40556
--- /dev/null
+++ b/source/blender/io/alembic/exporter/abc_writer_nurbs.h
@@ -0,0 +1,57 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+#pragma once
+
+/** \file
+ * \ingroup balembic
+ */
+
+#include "abc_writer_abstract.h"
+#include "abc_writer_mesh.h"
+#include <vector>
+
+namespace blender {
+namespace io {
+namespace alembic {
+
+class ABCNurbsWriter : public ABCAbstractWriter {
+ private:
+ std::vector<Alembic::AbcGeom::ONuPatch> abc_nurbs_;
+ std::vector<Alembic::AbcGeom::ONuPatchSchema> abc_nurbs_schemas_;
+
+ public:
+ explicit ABCNurbsWriter(const ABCWriterConstructorArgs &args);
+
+ virtual void create_alembic_objects(const HierarchyContext *context) override;
+ virtual const Alembic::Abc::OObject get_alembic_object() const override;
+
+ protected:
+ virtual bool is_supported(const HierarchyContext *context) const override;
+ virtual void do_write(HierarchyContext &context) override;
+ virtual bool check_is_animated(const HierarchyContext &context) const override;
+};
+
+class ABCNurbsMeshWriter : public ABCGenericMeshWriter {
+ public:
+ explicit ABCNurbsMeshWriter(const ABCWriterConstructorArgs &args);
+
+ protected:
+ virtual Mesh *get_export_mesh(Object *object_eval, bool &r_needsfree) override;
+};
+
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/exporter/abc_writer_points.cc b/source/blender/io/alembic/exporter/abc_writer_points.cc
new file mode 100644
index 00000000000..19870e39a90
--- /dev/null
+++ b/source/blender/io/alembic/exporter/abc_writer_points.cc
@@ -0,0 +1,148 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2016 Kévin Dietrich.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup balembic
+ */
+
+#include "abc_writer_points.h"
+
+#include "DNA_object_types.h"
+#include "DNA_particle_types.h"
+
+#include "BKE_lattice.h"
+#include "BKE_particle.h"
+
+#include "BLI_math.h"
+
+#include "DEG_depsgraph_query.h"
+
+#include "CLG_log.h"
+static CLG_LogRef LOG = {"io.alembic"};
+
+namespace blender {
+namespace io {
+namespace alembic {
+
+using Alembic::AbcGeom::kVertexScope;
+using Alembic::AbcGeom::OPoints;
+using Alembic::AbcGeom::OPointsSchema;
+
+ABCPointsWriter::ABCPointsWriter(const ABCWriterConstructorArgs &args) : ABCAbstractWriter(args)
+{
+}
+
+void ABCPointsWriter::create_alembic_objects(const HierarchyContext * /*context*/)
+{
+ CLOG_INFO(&LOG, 2, "exporting OPoints %s", args_.abc_path.c_str());
+ abc_points_ = OPoints(args_.abc_parent, args_.abc_name, timesample_index_);
+ abc_points_schema_ = abc_points_.getSchema();
+}
+
+const Alembic::Abc::OObject ABCPointsWriter::get_alembic_object() const
+{
+ return abc_points_;
+}
+
+bool ABCPointsWriter::is_supported(const HierarchyContext *context) const
+{
+ return ELEM(context->particle_system->part->type,
+ PART_EMITTER,
+ PART_FLUID_FLIP,
+ PART_FLUID_SPRAY,
+ PART_FLUID_BUBBLE,
+ PART_FLUID_FOAM,
+ PART_FLUID_TRACER,
+ PART_FLUID_SPRAYFOAM,
+ PART_FLUID_SPRAYBUBBLE,
+ PART_FLUID_FOAMBUBBLE,
+ PART_FLUID_SPRAYFOAMBUBBLE);
+}
+
+bool ABCPointsWriter::check_is_animated(const HierarchyContext & /*context*/) const
+{
+ /* We assume that particles are always animated. */
+ return true;
+}
+
+void ABCPointsWriter::do_write(HierarchyContext &context)
+{
+ BLI_assert(context.particle_system != nullptr);
+
+ std::vector<Imath::V3f> points;
+ std::vector<Imath::V3f> velocities;
+ std::vector<float> widths;
+ std::vector<uint64_t> ids;
+
+ ParticleSystem *psys = context.particle_system;
+ ParticleKey state;
+ ParticleSimulationData sim;
+ sim.depsgraph = args_.depsgraph;
+ sim.scene = DEG_get_evaluated_scene(args_.depsgraph);
+ sim.ob = context.object;
+ sim.psys = psys;
+
+ psys->lattice_deform_data = psys_create_lattice_deform_data(&sim);
+
+ uint64_t index = 0;
+ for (int p = 0; p < psys->totpart; p++) {
+ float pos[3], vel[3];
+
+ if (psys->particles[p].flag & (PARS_NO_DISP | PARS_UNEXIST)) {
+ continue;
+ }
+
+ state.time = DEG_get_ctime(args_.depsgraph);
+ if (psys_get_particle_state(&sim, p, &state, 0) == 0) {
+ continue;
+ }
+
+ /* location */
+ mul_v3_m4v3(pos, context.object->imat, state.co);
+
+ /* velocity */
+ sub_v3_v3v3(vel, state.co, psys->particles[p].prev_state.co);
+
+ /* Convert Z-up to Y-up. */
+ points.push_back(Imath::V3f(pos[0], pos[2], -pos[1]));
+ velocities.push_back(Imath::V3f(vel[0], vel[2], -vel[1]));
+ widths.push_back(psys->particles[p].size);
+ ids.push_back(index++);
+ }
+
+ if (psys->lattice_deform_data) {
+ BKE_lattice_deform_data_destroy(psys->lattice_deform_data);
+ psys->lattice_deform_data = NULL;
+ }
+
+ Alembic::Abc::P3fArraySample psample(points);
+ Alembic::Abc::UInt64ArraySample idsample(ids);
+ Alembic::Abc::V3fArraySample vsample(velocities);
+ Alembic::Abc::FloatArraySample wsample_array(widths);
+ Alembic::AbcGeom::OFloatGeomParam::Sample wsample(wsample_array, kVertexScope);
+
+ OPointsSchema::Sample sample(psample, idsample, vsample, wsample);
+ update_bounding_box(context.object);
+ sample.setSelfBounds(bounding_box_);
+ abc_points_schema_.set(sample);
+}
+
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/intern/abc_writer_archive.h b/source/blender/io/alembic/exporter/abc_writer_points.h
index 737717c1710..03800b80acf 100644
--- a/source/blender/io/alembic/intern/abc_writer_archive.h
+++ b/source/blender/io/alembic/exporter/abc_writer_points.h
@@ -16,35 +16,37 @@
* The Original Code is Copyright (C) 2016 Kévin Dietrich.
* All rights reserved.
*/
+#pragma once
/** \file
* \ingroup balembic
*/
-#ifndef __ABC_WRITER_ARCHIVE_H__
-#define __ABC_WRITER_ARCHIVE_H__
+#include "abc_writer_abstract.h"
-#include <Alembic/Abc/All.h>
-#include <Alembic/AbcCoreOgawa/All.h>
+#include <Alembic/AbcGeom/OPoints.h>
-#include <fstream>
+namespace blender {
+namespace io {
+namespace alembic {
-struct Main;
-struct Scene;
+class ABCPointsWriter : public ABCAbstractWriter {
+ Alembic::AbcGeom::OPoints abc_points_;
+ Alembic::AbcGeom::OPointsSchema abc_points_schema_;
-/* Wrappers around input and output archives. The goal is to be able to use
- * streams so that unicode paths work on Windows (T49112), and to make sure that
- * the stream objects remain valid as long as the archives are open.
- */
+ public:
+ explicit ABCPointsWriter(const ABCWriterConstructorArgs &args);
-class ArchiveWriter {
- std::ofstream m_outfile;
- Alembic::Abc::OArchive m_archive;
+ virtual void create_alembic_objects(const HierarchyContext *context) override;
+ virtual const Alembic::Abc::OObject get_alembic_object() const override;
- public:
- ArchiveWriter(const char *filename, const std::string &abc_scene_name, const Scene *scene);
+ virtual bool is_supported(const HierarchyContext *context) const override;
- Alembic::Abc::OArchive &archive();
+ protected:
+ virtual bool check_is_animated(const HierarchyContext &context) const override;
+ virtual void do_write(HierarchyContext &context) override;
};
-#endif /* __ABC_WRITER_ARCHIVE_H__ */
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/exporter/abc_writer_transform.cc b/source/blender/io/alembic/exporter/abc_writer_transform.cc
new file mode 100644
index 00000000000..65d6b7c5b41
--- /dev/null
+++ b/source/blender/io/alembic/exporter/abc_writer_transform.cc
@@ -0,0 +1,115 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup balembic
+ */
+
+#include "abc_writer_transform.h"
+#include "abc_hierarchy_iterator.h"
+#include "intern/abc_axis_conversion.h"
+#include "intern/abc_util.h"
+
+#include "BKE_object.h"
+
+#include "BLI_math_matrix.h"
+#include "BLI_math_rotation.h"
+
+#include "DNA_layer_types.h"
+
+#include "CLG_log.h"
+static CLG_LogRef LOG = {"io.alembic"};
+
+namespace blender {
+namespace io {
+namespace alembic {
+
+using Alembic::Abc::OObject;
+using Alembic::AbcGeom::OXform;
+using Alembic::AbcGeom::OXformSchema;
+using Alembic::AbcGeom::XformSample;
+
+ABCTransformWriter::ABCTransformWriter(const ABCWriterConstructorArgs &args)
+ : ABCAbstractWriter(args)
+{
+ timesample_index_ = args_.abc_archive->time_sampling_index_transforms();
+}
+
+void ABCTransformWriter::create_alembic_objects(const HierarchyContext * /*context*/)
+{
+ CLOG_INFO(&LOG, 2, "exporting %s", args_.abc_path.c_str());
+ abc_xform_ = OXform(args_.abc_parent, args_.abc_name, timesample_index_);
+ abc_xform_schema_ = abc_xform_.getSchema();
+}
+
+void ABCTransformWriter::do_write(HierarchyContext &context)
+{
+ float parent_relative_matrix[4][4]; // The object matrix relative to the parent.
+ mul_m4_m4m4(parent_relative_matrix, context.parent_matrix_inv_world, context.matrix_world);
+
+ // After this, parent_relative_matrix uses Y=up.
+ copy_m44_axis_swap(parent_relative_matrix, parent_relative_matrix, ABC_YUP_FROM_ZUP);
+
+ /* If the parent is a camera, undo its to-Maya rotation (see below). */
+ bool is_root_object = context.export_parent == nullptr;
+ if (!is_root_object && context.export_parent->type == OB_CAMERA) {
+ float rot_mat[4][4];
+ axis_angle_to_mat4_single(rot_mat, 'X', M_PI_2);
+ mul_m4_m4m4(parent_relative_matrix, rot_mat, parent_relative_matrix);
+ }
+
+ /* If the object is a camera, apply an extra rotation to Maya camera orientation. */
+ if (context.object->type == OB_CAMERA) {
+ float rot_mat[4][4];
+ axis_angle_to_mat4_single(rot_mat, 'X', -M_PI_2);
+ mul_m4_m4m4(parent_relative_matrix, parent_relative_matrix, rot_mat);
+ }
+
+ if (is_root_object) {
+ /* Only apply scaling to root objects, parenting will propagate it. */
+ float scale_mat[4][4];
+ scale_m4_fl(scale_mat, args_.export_params->global_scale);
+ scale_mat[3][3] = args_.export_params->global_scale; /* also scale translation */
+ mul_m4_m4m4(parent_relative_matrix, parent_relative_matrix, scale_mat);
+ parent_relative_matrix[3][3] /=
+ args_.export_params->global_scale; /* normalise the homogeneous component */
+ }
+
+ XformSample xform_sample;
+ xform_sample.setMatrix(convert_matrix_datatype(parent_relative_matrix));
+ xform_sample.setInheritsXforms(true);
+ abc_xform_schema_.set(xform_sample);
+}
+
+const OObject ABCTransformWriter::get_alembic_object() const
+{
+ return abc_xform_;
+}
+
+bool ABCTransformWriter::check_is_animated(const HierarchyContext &context) const
+{
+ if (context.duplicator != NULL) {
+ /* This object is being duplicated, so could be emitted by a particle system and thus
+ * influenced by forces. TODO(Sybren): Make this more strict. Probably better to get from the
+ * depsgraph whether this object instance has a time source. */
+ return true;
+ }
+ return BKE_object_moves_in_time(context.object, context.animation_check_include_parent);
+}
+
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/intern/abc_writer_points.h b/source/blender/io/alembic/exporter/abc_writer_transform.h
index 184a363ae6b..950bff39c29 100644
--- a/source/blender/io/alembic/intern/abc_writer_points.h
+++ b/source/blender/io/alembic/exporter/abc_writer_transform.h
@@ -12,38 +12,36 @@
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * The Original Code is Copyright (C) 2016 Kévin Dietrich.
- * All rights reserved.
*/
+#pragma once
/** \file
* \ingroup balembic
*/
-#ifndef __ABC_WRITER_POINTS_H__
-#define __ABC_WRITER_POINTS_H__
-
-#include "abc_customdata.h"
-#include "abc_writer_object.h"
+#include "abc_writer_abstract.h"
-struct ParticleSystem;
+#include <Alembic/AbcGeom/OXform.h>
-/* ************************************************************************** */
+namespace blender {
+namespace io {
+namespace alembic {
-class AbcPointsWriter : public AbcObjectWriter {
- Alembic::AbcGeom::OPointsSchema m_schema;
- Alembic::AbcGeom::OPointsSchema::Sample m_sample;
- ParticleSystem *m_psys;
+class ABCTransformWriter : public ABCAbstractWriter {
+ private:
+ Alembic::AbcGeom::OXform abc_xform_;
+ Alembic::AbcGeom::OXformSchema abc_xform_schema_;
public:
- AbcPointsWriter(Object *ob,
- AbcTransformWriter *parent,
- uint32_t time_sampling,
- ExportSettings &settings,
- ParticleSystem *psys);
+ explicit ABCTransformWriter(const ABCWriterConstructorArgs &args);
+ virtual void create_alembic_objects(const HierarchyContext *context) override;
- void do_write();
+ protected:
+ virtual void do_write(HierarchyContext &context) override;
+ virtual bool check_is_animated(const HierarchyContext &context) const override;
+ virtual const Alembic::Abc::OObject get_alembic_object() const override;
};
-#endif /* __ABC_WRITER_POINTS_H__ */
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/intern/abc_axis_conversion.cc b/source/blender/io/alembic/intern/abc_axis_conversion.cc
index 17db5e9c99f..cebab1f2e41 100644
--- a/source/blender/io/alembic/intern/abc_axis_conversion.cc
+++ b/source/blender/io/alembic/intern/abc_axis_conversion.cc
@@ -20,12 +20,14 @@
#include "abc_axis_conversion.h"
-extern "C" {
#include "BLI_assert.h"
+#include "BLI_math_geom.h"
+
#include "DNA_object_types.h"
-#include "BLI_math_geom.h"
-}
+namespace blender {
+namespace io {
+namespace alembic {
void create_swapped_rotation_matrix(float rot_x_mat[3][3],
float rot_y_mat[3][3],
@@ -72,7 +74,8 @@ void create_swapped_rotation_matrix(float rot_x_mat[3][3],
rot_z_mat[1][0] = -sin(rz);
rot_z_mat[0][1] = sin(rz);
rot_z_mat[1][1] = cos(rz);
-}
+} // namespace
+ // alembicvoidcreate_swapped_rotation_matrix(floatrot_x_mat[3][3],floatrot_y_mat[3][3],floatrot_z_mat[3][3],constfloateuler[3],AbcAxisSwapModemode)
/* Convert matrix from Z=up to Y=up or vice versa.
* Use yup_mat = zup_mat for in-place conversion. */
@@ -164,3 +167,7 @@ void create_transform_matrix(Object *obj,
copy_m44_axis_swap(r_yup_mat, zup_mat, ABC_YUP_FROM_ZUP);
}
+
+} // namespace alembic
+} // namespace io
+} // namespace blender \ No newline at end of file
diff --git a/source/blender/io/alembic/intern/abc_axis_conversion.h b/source/blender/io/alembic/intern/abc_axis_conversion.h
index 7fde0e92ea4..9a19e9116be 100644
--- a/source/blender/io/alembic/intern/abc_axis_conversion.h
+++ b/source/blender/io/alembic/intern/abc_axis_conversion.h
@@ -22,13 +22,13 @@
* \ingroup Alembic
*/
+#include "BLI_compiler_compat.h"
+
struct Object;
-#ifdef _MSC_VER
-# define ABC_INLINE static __forceinline
-#else
-# define ABC_INLINE static inline
-#endif
+namespace blender {
+namespace io {
+namespace alembic {
/* TODO(kevin): for now keeping these transformations hardcoded to make sure
* everything works properly, and also because Alembic is almost exclusively
@@ -37,7 +37,7 @@ struct Object;
/* Copy from Y-up to Z-up. */
-ABC_INLINE void copy_zup_from_yup(float zup[3], const float yup[3])
+BLI_INLINE void copy_zup_from_yup(float zup[3], const float yup[3])
{
const float old_yup1 = yup[1]; /* in case zup == yup */
zup[0] = yup[0];
@@ -45,7 +45,7 @@ ABC_INLINE void copy_zup_from_yup(float zup[3], const float yup[3])
zup[2] = old_yup1;
}
-ABC_INLINE void copy_zup_from_yup(short zup[3], const short yup[3])
+BLI_INLINE void copy_zup_from_yup(short zup[3], const short yup[3])
{
const short old_yup1 = yup[1]; /* in case zup == yup */
zup[0] = yup[0];
@@ -55,7 +55,7 @@ ABC_INLINE void copy_zup_from_yup(short zup[3], const short yup[3])
/* Copy from Z-up to Y-up. */
-ABC_INLINE void copy_yup_from_zup(float yup[3], const float zup[3])
+BLI_INLINE void copy_yup_from_zup(float yup[3], const float zup[3])
{
const float old_zup1 = zup[1]; /* in case yup == zup */
yup[0] = zup[0];
@@ -63,7 +63,7 @@ ABC_INLINE void copy_yup_from_zup(float yup[3], const float zup[3])
yup[2] = -old_zup1;
}
-ABC_INLINE void copy_yup_from_zup(short yup[3], const short zup[3])
+BLI_INLINE void copy_yup_from_zup(short yup[3], const short zup[3])
{
const short old_zup1 = zup[1]; /* in case yup == zup */
yup[0] = zup[0];
@@ -97,3 +97,7 @@ void create_transform_matrix(Object *obj,
float r_transform_mat[4][4],
AbcMatrixMode mode,
Object *proxy_from);
+
+} // namespace alembic
+} // namespace io
+} // namespace blender \ No newline at end of file
diff --git a/source/blender/io/alembic/intern/abc_customdata.cc b/source/blender/io/alembic/intern/abc_customdata.cc
index 62f6a52f7cf..f3e2342e844 100644
--- a/source/blender/io/alembic/intern/abc_customdata.cc
+++ b/source/blender/io/alembic/intern/abc_customdata.cc
@@ -50,6 +50,9 @@ using Alembic::Abc::V2fArraySample;
using Alembic::AbcGeom::OC4fGeomParam;
using Alembic::AbcGeom::OV2fGeomParam;
+namespace blender {
+namespace io {
+namespace alembic {
static void get_uvs(const CDStreamConfig &config,
std::vector<Imath::V2f> &uvs,
@@ -485,3 +488,7 @@ void read_custom_data(const std::string &iobject_full_name,
}
}
}
+
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/intern/abc_customdata.h b/source/blender/io/alembic/intern/abc_customdata.h
index 96b57b08681..8f4accb70dc 100644
--- a/source/blender/io/alembic/intern/abc_customdata.h
+++ b/source/blender/io/alembic/intern/abc_customdata.h
@@ -16,14 +16,12 @@
* The Original Code is Copyright (C) 2016 Kévin Dietrich.
* All rights reserved.
*/
+#pragma once
/** \file
* \ingroup balembic
*/
-#ifndef __ABC_CUSTOMDATA_H__
-#define __ABC_CUSTOMDATA_H__
-
#include <Alembic/Abc/All.h>
#include <Alembic/AbcGeom/All.h>
@@ -38,6 +36,9 @@ struct Mesh;
using Alembic::Abc::ICompoundProperty;
using Alembic::Abc::OCompoundProperty;
+namespace blender {
+namespace io {
+namespace alembic {
struct UVSample {
std::vector<Imath::V2f> uvs;
@@ -112,4 +113,6 @@ void read_custom_data(const std::string &iobject_full_name,
const CDStreamConfig &config,
const Alembic::Abc::ISampleSelector &iss);
-#endif /* __ABC_CUSTOMDATA_H__ */
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/intern/abc_exporter.cc b/source/blender/io/alembic/intern/abc_exporter.cc
deleted file mode 100644
index 8dad8dff199..00000000000
--- a/source/blender/io/alembic/intern/abc_exporter.cc
+++ /dev/null
@@ -1,673 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-/** \file
- * \ingroup balembic
- */
-
-#include "abc_exporter.h"
-
-#include <cmath>
-
-#include "abc_util.h"
-#include "abc_writer_archive.h"
-#include "abc_writer_camera.h"
-#include "abc_writer_curves.h"
-#include "abc_writer_hair.h"
-#include "abc_writer_mball.h"
-#include "abc_writer_mesh.h"
-#include "abc_writer_nurbs.h"
-#include "abc_writer_points.h"
-#include "abc_writer_transform.h"
-
-#include "DNA_camera_types.h"
-#include "DNA_curve_types.h"
-#include "DNA_fluid_types.h"
-#include "DNA_mesh_types.h"
-#include "DNA_meta_types.h"
-#include "DNA_modifier_types.h"
-#include "DNA_object_types.h"
-#include "DNA_scene_types.h"
-#include "DNA_space_types.h" /* for FILE_MAX */
-
-#include "BLI_string.h"
-
-#ifdef WIN32
-/* needed for MSCV because of snprintf from BLI_string */
-# include "BLI_winstuff.h"
-#endif
-
-#include "BKE_duplilist.h"
-#include "BKE_global.h"
-#include "BKE_idprop.h"
-#include "BKE_layer.h"
-#include "BKE_main.h"
-#include "BKE_mball.h"
-#include "BKE_modifier.h"
-#include "BKE_particle.h"
-#include "BKE_scene.h"
-
-#include "DEG_depsgraph_query.h"
-
-using Alembic::Abc::OBox3dProperty;
-using Alembic::Abc::TimeSamplingPtr;
-
-/* ************************************************************************** */
-
-ExportSettings::ExportSettings()
- : scene(NULL),
- view_layer(NULL),
- depsgraph(NULL),
- logger(),
- selected_only(false),
- visible_objects_only(false),
- renderable_only(false),
- frame_start(1),
- frame_end(1),
- frame_samples_xform(1),
- frame_samples_shape(1),
- shutter_open(0.0),
- shutter_close(1.0),
- global_scale(1.0f),
- flatten_hierarchy(false),
- export_normals(false),
- export_uvs(false),
- export_vcols(false),
- export_face_sets(false),
- export_vweigths(false),
- export_hair(true),
- export_particles(true),
- apply_subdiv(false),
- use_subdiv_schema(false),
- export_child_hairs(true),
- pack_uv(false),
- triangulate(false),
- quad_method(0),
- ngon_method(0)
-{
-}
-
-static bool object_is_smoke_sim(Object *ob)
-{
- ModifierData *md = BKE_modifiers_findby_type(ob, eModifierType_Fluid);
-
- if (md) {
- FluidModifierData *smd = reinterpret_cast<FluidModifierData *>(md);
- return (smd->type == MOD_FLUID_TYPE_DOMAIN && smd->domain &&
- smd->domain->type == FLUID_DOMAIN_TYPE_GAS);
- }
-
- return false;
-}
-
-static bool object_type_is_exportable(Scene *scene, Object *ob)
-{
- switch (ob->type) {
- case OB_MESH:
- if (object_is_smoke_sim(ob)) {
- return false;
- }
-
- return true;
- case OB_EMPTY:
- case OB_CURVE:
- case OB_SURF:
- case OB_CAMERA:
- return true;
- case OB_MBALL:
- return AbcMBallWriter::isBasisBall(scene, ob);
- default:
- return false;
- }
-}
-
-/**
- * Returns whether this object should be exported into the Alembic file.
- *
- * \param settings: export settings, used for options like 'selected only'.
- * \param ob: the object's base in question.
- * \param is_duplicated: Normally false; true when the object is instanced
- * into the scene by a dupli-object (e.g. part of a dupligroup).
- * This ignores selection and layer visibility,
- * and assumes that the dupli-object itself (e.g. the group-instantiating empty) is exported.
- */
-static bool export_object(const ExportSettings *const settings,
- const Base *const base,
- bool is_duplicated)
-{
- if (!is_duplicated) {
- View3D *v3d = NULL;
-
- /* These two tests only make sense when the object isn't being instanced
- * into the scene. When it is, its exportability is determined by
- * its dupli-object and the DupliObject::no_draw property. */
- if (settings->selected_only && !BASE_SELECTED(v3d, base)) {
- return false;
- }
- // FIXME Sybren: handle these cleanly (maybe just remove code),
- // now using active scene layer instead.
- if (settings->visible_objects_only && !BASE_VISIBLE(v3d, base)) {
- return false;
- }
- }
-
- Object *ob_eval = DEG_get_evaluated_object(settings->depsgraph, base->object);
- if ((ob_eval->id.tag & LIB_TAG_COPIED_ON_WRITE) == 0) {
- /* XXX fix after 2.80: the object was not part of the depsgraph, and thus we cannot get the
- * evaluated copy to export. This will be handled more elegantly in the new
- * AbstractHierarchyIterator that Sybren is working on. This condition is temporary, and avoids
- * a BLI_assert() failure getting the evaluated mesh of this object. */
- return false;
- }
-
- // if (settings->renderable_only && (ob->restrictflag & OB_RESTRICT_RENDER)) {
- // return false;
- // }
-
- return true;
-}
-
-/* ************************************************************************** */
-
-AbcExporter::AbcExporter(Main *bmain, const char *filename, ExportSettings &settings)
- : m_bmain(bmain),
- m_settings(settings),
- m_filename(filename),
- m_trans_sampling_index(0),
- m_shape_sampling_index(0),
- m_writer(NULL)
-{
-}
-
-AbcExporter::~AbcExporter()
-{
- /* Free xforms map */
- m_xforms_type::iterator it_x, e_x;
- for (it_x = m_xforms.begin(), e_x = m_xforms.end(); it_x != e_x; ++it_x) {
- delete it_x->second;
- }
-
- /* Free shapes vector */
- for (int i = 0, e = m_shapes.size(); i != e; i++) {
- delete m_shapes[i];
- }
-
- delete m_writer;
-}
-
-void AbcExporter::getShutterSamples(unsigned int nr_of_samples,
- bool time_relative,
- std::vector<double> &samples)
-{
- Scene *scene = m_settings.scene; /* for use in the FPS macro */
- samples.clear();
-
- unsigned int frame_offset = time_relative ? m_settings.frame_start : 0;
- double time_factor = time_relative ? FPS : 1.0;
- double shutter_open = m_settings.shutter_open;
- double shutter_close = m_settings.shutter_close;
- double time_inc = (shutter_close - shutter_open) / nr_of_samples;
-
- /* sample between shutter open & close */
- for (int sample = 0; sample < nr_of_samples; sample++) {
- double sample_time = shutter_open + time_inc * sample;
- double time = (frame_offset + sample_time) / time_factor;
-
- samples.push_back(time);
- }
-}
-
-Alembic::Abc::TimeSamplingPtr AbcExporter::createTimeSampling(double step)
-{
- std::vector<double> samples;
-
- if (m_settings.frame_start == m_settings.frame_end) {
- return TimeSamplingPtr(new Alembic::Abc::TimeSampling());
- }
-
- getShutterSamples(step, true, samples);
-
- /* TODO(Sybren): shouldn't we use the FPS macro here? */
- Alembic::Abc::TimeSamplingType ts(static_cast<uint32_t>(samples.size()),
- 1.0 / m_settings.scene->r.frs_sec);
-
- return TimeSamplingPtr(new Alembic::Abc::TimeSampling(ts, samples));
-}
-
-void AbcExporter::getFrameSet(unsigned int nr_of_samples, std::set<double> &frames)
-{
- frames.clear();
-
- std::vector<double> shutter_samples;
-
- getShutterSamples(nr_of_samples, false, shutter_samples);
-
- for (double frame = m_settings.frame_start; frame <= m_settings.frame_end; frame += 1.0) {
- for (size_t j = 0; j < nr_of_samples; j++) {
- frames.insert(frame + shutter_samples[j]);
- }
- }
-}
-
-void AbcExporter::operator()(short *do_update, float *progress, bool *was_canceled)
-{
- std::string abc_scene_name;
-
- if (m_bmain->name[0] != '\0') {
- char scene_file_name[FILE_MAX];
- BLI_strncpy(scene_file_name, m_bmain->name, FILE_MAX);
- abc_scene_name = scene_file_name;
- }
- else {
- abc_scene_name = "untitled";
- }
-
- m_writer = new ArchiveWriter(m_filename, abc_scene_name, m_settings.scene);
-
- /* Create time samplings for transforms and shapes. */
-
- TimeSamplingPtr trans_time = createTimeSampling(m_settings.frame_samples_xform);
-
- m_trans_sampling_index = m_writer->archive().addTimeSampling(*trans_time);
-
- TimeSamplingPtr shape_time;
-
- if ((m_settings.frame_samples_shape == m_settings.frame_samples_xform) ||
- (m_settings.frame_start == m_settings.frame_end)) {
- shape_time = trans_time;
- m_shape_sampling_index = m_trans_sampling_index;
- }
- else {
- shape_time = createTimeSampling(m_settings.frame_samples_shape);
- m_shape_sampling_index = m_writer->archive().addTimeSampling(*shape_time);
- }
-
- OBox3dProperty archive_bounds_prop = Alembic::AbcGeom::CreateOArchiveBounds(
- m_writer->archive(), m_trans_sampling_index);
-
- createTransformWritersHierarchy();
- createShapeWriters();
-
- /* Make a list of frames to export. */
-
- std::set<double> xform_frames;
- getFrameSet(m_settings.frame_samples_xform, xform_frames);
-
- std::set<double> shape_frames;
- getFrameSet(m_settings.frame_samples_shape, shape_frames);
-
- /* Merge all frames needed. */
- std::set<double> frames(xform_frames);
- frames.insert(shape_frames.begin(), shape_frames.end());
-
- /* Export all frames. */
-
- std::set<double>::const_iterator begin = frames.begin();
- std::set<double>::const_iterator end = frames.end();
-
- const float size = static_cast<float>(frames.size());
- size_t i = 0;
-
- for (; begin != end; ++begin) {
- *progress = (++i / size);
- *do_update = 1;
-
- if (G.is_break) {
- *was_canceled = true;
- break;
- }
-
- const double frame = *begin;
-
- /* 'frame' is offset by start frame, so need to cancel the offset. */
- setCurrentFrame(m_bmain, frame);
-
- if (shape_frames.count(frame) != 0) {
- for (int i = 0, e = m_shapes.size(); i != e; i++) {
- m_shapes[i]->write();
- }
- }
-
- if (xform_frames.count(frame) == 0) {
- continue;
- }
-
- m_xforms_type::iterator xit, xe;
- for (xit = m_xforms.begin(), xe = m_xforms.end(); xit != xe; ++xit) {
- xit->second->write();
- }
-
- /* Save the archive 's bounding box. */
- Imath::Box3d bounds;
-
- for (xit = m_xforms.begin(), xe = m_xforms.end(); xit != xe; ++xit) {
- Imath::Box3d box = xit->second->bounds();
- bounds.extendBy(box);
- }
-
- archive_bounds_prop.set(bounds);
- }
-}
-
-void AbcExporter::createTransformWritersHierarchy()
-{
- for (Base *base = static_cast<Base *>(m_settings.view_layer->object_bases.first); base;
- base = base->next) {
- Object *ob = base->object;
-
- if (export_object(&m_settings, base, false)) {
- switch (ob->type) {
- case OB_LAMP:
- case OB_LATTICE:
- case OB_SPEAKER:
- /* We do not export transforms for objects of these classes. */
- break;
- default:
- exploreTransform(base, ob, ob->parent, NULL);
- }
- }
- }
-}
-
-void AbcExporter::exploreTransform(Base *base,
- Object *object,
- Object *parent,
- Object *dupliObParent)
-{
- /* If an object isn't exported itself, its duplilist shouldn't be
- * exported either. */
- if (!export_object(&m_settings, base, dupliObParent != NULL)) {
- return;
- }
-
- Object *ob = DEG_get_evaluated_object(m_settings.depsgraph, object);
- if (object_type_is_exportable(m_settings.scene, ob)) {
- createTransformWriter(ob, parent, dupliObParent);
- }
-
- ListBase *lb = object_duplilist(m_settings.depsgraph, m_settings.scene, ob);
-
- if (lb) {
- DupliObject *link = static_cast<DupliObject *>(lb->first);
- Object *dupli_ob = NULL;
- Object *dupli_parent = NULL;
-
- for (; link; link = link->next) {
- /* This skips things like custom bone shapes. */
- if (m_settings.renderable_only && link->no_draw) {
- continue;
- }
-
- if (link->type == OB_DUPLICOLLECTION) {
- dupli_ob = link->ob;
- dupli_parent = (dupli_ob->parent) ? dupli_ob->parent : ob;
-
- exploreTransform(base, dupli_ob, dupli_parent, ob);
- }
- }
-
- free_object_duplilist(lb);
- }
-}
-
-AbcTransformWriter *AbcExporter::createTransformWriter(Object *ob,
- Object *parent,
- Object *dupliObParent)
-{
- /* An object should not be its own parent, or we'll get infinite loops. */
- BLI_assert(ob != parent);
- BLI_assert(ob != dupliObParent);
-
- std::string name;
- if (m_settings.flatten_hierarchy) {
- name = get_id_name(ob);
- }
- else {
- name = get_object_dag_path_name(ob, dupliObParent);
- }
-
- /* check if we have already created a transform writer for this object */
- AbcTransformWriter *my_writer = getXForm(name);
- if (my_writer != NULL) {
- return my_writer;
- }
-
- AbcTransformWriter *parent_writer = NULL;
- Alembic::Abc::OObject alembic_parent;
-
- if (m_settings.flatten_hierarchy || parent == NULL) {
- /* Parentless objects still have the "top object" as parent
- * in Alembic. */
- alembic_parent = m_writer->archive().getTop();
- }
- else {
- /* Since there are so many different ways to find parents (as evident
- * in the number of conditions below), we can't really look up the
- * parent by name. We'll just call createTransformWriter(), which will
- * return the parent's AbcTransformWriter pointer. */
- if (parent->parent) {
- if (parent == dupliObParent) {
- parent_writer = createTransformWriter(parent, parent->parent, NULL);
- }
- else {
- parent_writer = createTransformWriter(parent, parent->parent, dupliObParent);
- }
- }
- else if (parent == dupliObParent) {
- if (dupliObParent->parent == NULL) {
- parent_writer = createTransformWriter(parent, NULL, NULL);
- }
- else {
- parent_writer = createTransformWriter(
- parent, dupliObParent->parent, dupliObParent->parent);
- }
- }
- else {
- parent_writer = createTransformWriter(parent, dupliObParent, dupliObParent);
- }
-
- BLI_assert(parent_writer);
- alembic_parent = parent_writer->alembicXform();
- }
-
- my_writer = new AbcTransformWriter(
- ob, alembic_parent, parent_writer, m_trans_sampling_index, m_settings);
-
- /* When flattening, the matrix of the dupliobject has to be added. */
- if (m_settings.flatten_hierarchy && dupliObParent) {
- my_writer->m_proxy_from = dupliObParent;
- }
-
- m_xforms[name] = my_writer;
- return my_writer;
-}
-
-void AbcExporter::createShapeWriters()
-{
- for (Base *base = static_cast<Base *>(m_settings.view_layer->object_bases.first); base;
- base = base->next) {
- exploreObject(base, base->object, NULL);
- }
-}
-
-void AbcExporter::exploreObject(Base *base, Object *object, Object *dupliObParent)
-{
- /* If an object isn't exported itself, its duplilist shouldn't be
- * exported either. */
- if (!export_object(&m_settings, base, dupliObParent != NULL)) {
- return;
- }
-
- Object *ob = DEG_get_evaluated_object(m_settings.depsgraph, object);
- createShapeWriter(ob, dupliObParent);
-
- ListBase *lb = object_duplilist(m_settings.depsgraph, m_settings.scene, ob);
-
- if (lb) {
- DupliObject *link = static_cast<DupliObject *>(lb->first);
-
- for (; link; link = link->next) {
- /* This skips things like custom bone shapes. */
- if (m_settings.renderable_only && link->no_draw) {
- continue;
- }
- if (link->type == OB_DUPLICOLLECTION) {
- exploreObject(base, link->ob, ob);
- }
- }
-
- free_object_duplilist(lb);
- }
-}
-
-void AbcExporter::createParticleSystemsWriters(Object *ob, AbcTransformWriter *xform)
-{
- if (!m_settings.export_hair && !m_settings.export_particles) {
- return;
- }
-
- ParticleSystem *psys = static_cast<ParticleSystem *>(ob->particlesystem.first);
-
- for (; psys; psys = psys->next) {
- if (!psys_check_enabled(ob, psys, G.is_rendering) || !psys->part) {
- continue;
- }
-
- if (m_settings.export_hair && psys->part->type == PART_HAIR) {
- m_settings.export_child_hairs = true;
- m_shapes.push_back(new AbcHairWriter(ob, xform, m_shape_sampling_index, m_settings, psys));
- }
- else if (m_settings.export_particles &&
- (psys->part->type == PART_EMITTER || psys->part->type == PART_FLUID_FLIP ||
- psys->part->type == PART_FLUID_SPRAY || psys->part->type == PART_FLUID_BUBBLE ||
- psys->part->type == PART_FLUID_FOAM || psys->part->type == PART_FLUID_TRACER ||
- psys->part->type == PART_FLUID_SPRAYFOAM ||
- psys->part->type == PART_FLUID_SPRAYBUBBLE ||
- psys->part->type == PART_FLUID_FOAMBUBBLE ||
- psys->part->type == PART_FLUID_SPRAYFOAMBUBBLE)) {
- m_shapes.push_back(new AbcPointsWriter(ob, xform, m_shape_sampling_index, m_settings, psys));
- }
- }
-}
-
-void AbcExporter::createShapeWriter(Object *ob, Object *dupliObParent)
-{
- if (!object_type_is_exportable(m_settings.scene, ob)) {
- return;
- }
-
- std::string name;
-
- if (m_settings.flatten_hierarchy) {
- name = get_id_name(ob);
- }
- else {
- name = get_object_dag_path_name(ob, dupliObParent);
- }
-
- AbcTransformWriter *xform = getXForm(name);
-
- if (!xform) {
- ABC_LOG(m_settings.logger) << __func__ << ": xform " << name << " is NULL\n";
- return;
- }
-
- createParticleSystemsWriters(ob, xform);
-
- switch (ob->type) {
- case OB_MESH: {
- Mesh *me = static_cast<Mesh *>(ob->data);
-
- if (!me) {
- return;
- }
-
- m_shapes.push_back(new AbcMeshWriter(ob, xform, m_shape_sampling_index, m_settings));
- break;
- }
- case OB_SURF: {
- Curve *cu = static_cast<Curve *>(ob->data);
-
- if (!cu) {
- return;
- }
-
- AbcObjectWriter *writer;
- if (m_settings.curves_as_mesh) {
- writer = new AbcCurveMeshWriter(ob, xform, m_shape_sampling_index, m_settings);
- }
- else {
- writer = new AbcNurbsWriter(ob, xform, m_shape_sampling_index, m_settings);
- }
- m_shapes.push_back(writer);
- break;
- }
- case OB_CURVE: {
- Curve *cu = static_cast<Curve *>(ob->data);
-
- if (!cu) {
- return;
- }
-
- AbcObjectWriter *writer;
- if (m_settings.curves_as_mesh) {
- writer = new AbcCurveMeshWriter(ob, xform, m_shape_sampling_index, m_settings);
- }
- else {
- writer = new AbcCurveWriter(ob, xform, m_shape_sampling_index, m_settings);
- }
- m_shapes.push_back(writer);
- break;
- }
- case OB_CAMERA: {
- Camera *cam = static_cast<Camera *>(ob->data);
-
- if (cam->type == CAM_PERSP) {
- m_shapes.push_back(new AbcCameraWriter(ob, xform, m_shape_sampling_index, m_settings));
- }
-
- break;
- }
- case OB_MBALL: {
- MetaBall *mball = static_cast<MetaBall *>(ob->data);
- if (!mball) {
- return;
- }
-
- m_shapes.push_back(
- new AbcMBallWriter(m_bmain, ob, xform, m_shape_sampling_index, m_settings));
- break;
- }
- }
-}
-
-AbcTransformWriter *AbcExporter::getXForm(const std::string &name)
-{
- std::map<std::string, AbcTransformWriter *>::iterator it = m_xforms.find(name);
-
- if (it == m_xforms.end()) {
- return NULL;
- }
-
- return it->second;
-}
-
-void AbcExporter::setCurrentFrame(Main *bmain, double t)
-{
- m_settings.scene->r.cfra = static_cast<int>(t);
- m_settings.scene->r.subframe = static_cast<float>(t) - m_settings.scene->r.cfra;
- BKE_scene_graph_update_for_newframe(m_settings.depsgraph, bmain);
-}
diff --git a/source/blender/io/alembic/intern/abc_exporter.h b/source/blender/io/alembic/intern/abc_exporter.h
deleted file mode 100644
index 049ccb291bd..00000000000
--- a/source/blender/io/alembic/intern/abc_exporter.h
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-/** \file
- * \ingroup balembic
- */
-
-#ifndef __ABC_EXPORTER_H__
-#define __ABC_EXPORTER_H__
-
-#include <Alembic/Abc/All.h>
-#include <map>
-#include <set>
-#include <vector>
-
-#include "abc_util.h"
-
-class AbcObjectWriter;
-class AbcTransformWriter;
-class ArchiveWriter;
-
-struct Base;
-struct Depsgraph;
-struct Main;
-struct Object;
-struct Scene;
-struct ViewLayer;
-
-struct ExportSettings {
- ExportSettings();
-
- Scene *scene;
- /** Scene layer to export; all its objects will be exported, unless selected_only=true. */
- ViewLayer *view_layer;
- Depsgraph *depsgraph;
- SimpleLogger logger;
-
- bool selected_only;
- bool visible_objects_only;
- bool renderable_only;
-
- double frame_start, frame_end;
- double frame_samples_xform;
- double frame_samples_shape;
- double shutter_open;
- double shutter_close;
- float global_scale;
-
- bool flatten_hierarchy;
-
- bool export_normals;
- bool export_uvs;
- bool export_vcols;
- bool export_face_sets;
- bool export_vweigths;
- bool export_hair;
- bool export_particles;
-
- bool apply_subdiv;
- bool curves_as_mesh;
- bool use_subdiv_schema;
- bool export_child_hairs;
- bool pack_uv;
- bool triangulate;
-
- int quad_method;
- int ngon_method;
-};
-
-class AbcExporter {
- Main *m_bmain;
- ExportSettings &m_settings;
-
- const char *m_filename;
-
- unsigned int m_trans_sampling_index, m_shape_sampling_index;
-
- ArchiveWriter *m_writer;
-
- /* mapping from name to transform writer */
- typedef std::map<std::string, AbcTransformWriter *> m_xforms_type;
- m_xforms_type m_xforms;
-
- std::vector<AbcObjectWriter *> m_shapes;
-
- public:
- AbcExporter(Main *bmain, const char *filename, ExportSettings &settings);
- ~AbcExporter();
-
- void operator()(short *do_update, float *progress, bool *was_canceled);
-
- protected:
- void getShutterSamples(unsigned int nr_of_samples,
- bool time_relative,
- std::vector<double> &samples);
- void getFrameSet(unsigned int nr_of_samples, std::set<double> &frames);
-
- private:
- Alembic::Abc::TimeSamplingPtr createTimeSampling(double step);
-
- void createTransformWritersHierarchy();
- AbcTransformWriter *createTransformWriter(Object *ob, Object *parent, Object *dupliObParent);
- void exploreTransform(Base *base, Object *object, Object *parent, Object *dupliObParent);
- void exploreObject(Base *base, Object *object, Object *dupliObParent);
- void createShapeWriters();
- void createShapeWriter(Object *ob, Object *dupliObParent);
- void createParticleSystemsWriters(Object *ob, AbcTransformWriter *xform);
-
- AbcTransformWriter *getXForm(const std::string &name);
-
- void setCurrentFrame(Main *bmain, double t);
-};
-
-#endif /* __ABC_EXPORTER_H__ */
diff --git a/source/blender/io/alembic/intern/abc_reader_archive.cc b/source/blender/io/alembic/intern/abc_reader_archive.cc
index d55736f732a..d7f1095f0fd 100644
--- a/source/blender/io/alembic/intern/abc_reader_archive.cc
+++ b/source/blender/io/alembic/intern/abc_reader_archive.cc
@@ -39,6 +39,10 @@ using Alembic::Abc::Exception;
using Alembic::Abc::IArchive;
using Alembic::Abc::kWrapExisting;
+namespace blender {
+namespace io {
+namespace alembic {
+
static IArchive open_archive(const std::string &filename,
const std::vector<std::istream *> &input_streams)
{
@@ -103,3 +107,7 @@ Alembic::Abc::IObject ArchiveReader::getTop()
{
return m_archive.getTop();
}
+
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/intern/abc_reader_archive.h b/source/blender/io/alembic/intern/abc_reader_archive.h
index 304c876adce..aea62b46cce 100644
--- a/source/blender/io/alembic/intern/abc_reader_archive.h
+++ b/source/blender/io/alembic/intern/abc_reader_archive.h
@@ -16,14 +16,12 @@
* The Original Code is Copyright (C) 2016 Kévin Dietrich.
* All rights reserved.
*/
+#pragma once
/** \file
* \ingroup balembic
*/
-#ifndef __ABC_READER_ARCHIVE_H__
-#define __ABC_READER_ARCHIVE_H__
-
#include <Alembic/Abc/All.h>
#include <Alembic/AbcCoreOgawa/All.h>
@@ -32,6 +30,10 @@
struct Main;
struct Scene;
+namespace blender {
+namespace io {
+namespace alembic {
+
/* Wrappers around input and output archives. The goal is to be able to use
* streams so that unicode paths work on Windows (T49112), and to make sure that
* the stream objects remain valid as long as the archives are open.
@@ -50,4 +52,6 @@ class ArchiveReader {
Alembic::Abc::IObject getTop();
};
-#endif /* __ABC_READER_ARCHIVE_H__ */
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/intern/abc_reader_camera.cc b/source/blender/io/alembic/intern/abc_reader_camera.cc
index 0752534f8c2..3affb35908d 100644
--- a/source/blender/io/alembic/intern/abc_reader_camera.cc
+++ b/source/blender/io/alembic/intern/abc_reader_camera.cc
@@ -37,6 +37,10 @@ using Alembic::AbcGeom::IFloatProperty;
using Alembic::AbcGeom::ISampleSelector;
using Alembic::AbcGeom::kWrapExisting;
+namespace blender {
+namespace io {
+namespace alembic {
+
AbcCameraReader::AbcCameraReader(const Alembic::Abc::IObject &object, ImportSettings &settings)
: AbcObjectReader(object, settings)
{
@@ -109,3 +113,7 @@ void AbcCameraReader::readObjectData(Main *bmain, const ISampleSelector &sample_
m_object = BKE_object_add_only_object(bmain, OB_CAMERA, m_object_name.c_str());
m_object->data = bcam;
}
+
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/intern/abc_reader_camera.h b/source/blender/io/alembic/intern/abc_reader_camera.h
index 1d9763b0454..b733269407b 100644
--- a/source/blender/io/alembic/intern/abc_reader_camera.h
+++ b/source/blender/io/alembic/intern/abc_reader_camera.h
@@ -13,16 +13,18 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#pragma once
/** \file
* \ingroup balembic
*/
-#ifndef __ABC_READER_CAMERA_H__
-#define __ABC_READER_CAMERA_H__
-
#include "abc_reader_object.h"
+namespace blender {
+namespace io {
+namespace alembic {
+
class AbcCameraReader : public AbcObjectReader {
Alembic::AbcGeom::ICameraSchema m_schema;
@@ -37,4 +39,6 @@ class AbcCameraReader : public AbcObjectReader {
void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel);
};
-#endif /* __ABC_READER_CAMERA_H__ */
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/intern/abc_reader_curves.cc b/source/blender/io/alembic/intern/abc_reader_curves.cc
index d5e0b694294..a505dfd654b 100644
--- a/source/blender/io/alembic/intern/abc_reader_curves.cc
+++ b/source/blender/io/alembic/intern/abc_reader_curves.cc
@@ -54,6 +54,10 @@ using Alembic::AbcGeom::IInt16Property;
using Alembic::AbcGeom::ISampleSelector;
using Alembic::AbcGeom::kWrapExisting;
+namespace blender {
+namespace io {
+namespace alembic {
+
AbcCurveReader::AbcCurveReader(const Alembic::Abc::IObject &object, ImportSettings &settings)
: AbcObjectReader(object, settings)
{
@@ -351,3 +355,7 @@ Mesh *AbcCurveReader::read_mesh(Mesh *existing_mesh,
return BKE_mesh_new_nomain_from_curve(m_object);
}
+
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/intern/abc_reader_curves.h b/source/blender/io/alembic/intern/abc_reader_curves.h
index eb0538308f8..7488adb9b24 100644
--- a/source/blender/io/alembic/intern/abc_reader_curves.h
+++ b/source/blender/io/alembic/intern/abc_reader_curves.h
@@ -16,14 +16,12 @@
* The Original Code is Copyright (C) 2016 Kévin Dietrich.
* All rights reserved.
*/
+#pragma once
/** \file
* \ingroup balembic
*/
-#ifndef __ABC_READER_CURVES_H__
-#define __ABC_READER_CURVES_H__
-
#include "abc_reader_mesh.h"
#include "abc_reader_object.h"
@@ -31,6 +29,10 @@ struct Curve;
#define ABC_CURVE_RESOLUTION_U_PROPNAME "blender:resolution"
+namespace blender {
+namespace io {
+namespace alembic {
+
class AbcCurveReader : public AbcObjectReader {
Alembic::AbcGeom::ICurvesSchema m_curves_schema;
@@ -53,4 +55,6 @@ class AbcCurveReader : public AbcObjectReader {
const Alembic::Abc::ISampleSelector &sample_selector);
};
-#endif /* __ABC_READER_CURVES_H__ */
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.cc b/source/blender/io/alembic/intern/abc_reader_mesh.cc
index 8b79a3a0aa0..756dde3783c 100644
--- a/source/blender/io/alembic/intern/abc_reader_mesh.cc
+++ b/source/blender/io/alembic/intern/abc_reader_mesh.cc
@@ -32,6 +32,7 @@
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
+#include "BLI_compiler_compat.h"
#include "BLI_math_geom.h"
#include "BKE_main.h"
@@ -59,6 +60,10 @@ using Alembic::AbcGeom::N3fArraySamplePtr;
using Alembic::AbcGeom::UInt32ArraySamplePtr;
using Alembic::AbcGeom::V2fArraySamplePtr;
+namespace blender {
+namespace io {
+namespace alembic {
+
/* NOTE: Alembic's polygon winding order is clockwise, to match with Renderman. */
/* Some helpers for mesh generation */
@@ -339,7 +344,7 @@ static void process_normals(CDStreamConfig &config,
}
}
-ABC_INLINE void read_uvs_params(CDStreamConfig &config,
+BLI_INLINE void read_uvs_params(CDStreamConfig &config,
AbcMeshData &abc_data,
const IV2fGeomParam &uv,
const ISampleSelector &selector)
@@ -714,7 +719,7 @@ void AbcMeshReader::readFaceSetsSample(Main *bmain, Mesh *mesh, const ISampleSel
/* ************************************************************************** */
-ABC_INLINE MEdge *find_edge(MEdge *edges, int totedge, int v1, int v2)
+BLI_INLINE MEdge *find_edge(MEdge *edges, int totedge, int v1, int v2)
{
for (int i = 0, e = totedge; i < e; i++) {
MEdge &edge = edges[i];
@@ -930,3 +935,7 @@ Mesh *AbcSubDReader::read_mesh(Mesh *existing_mesh,
return config.mesh;
}
+
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.h b/source/blender/io/alembic/intern/abc_reader_mesh.h
index bc95c7ec134..363a74b8b5f 100644
--- a/source/blender/io/alembic/intern/abc_reader_mesh.h
+++ b/source/blender/io/alembic/intern/abc_reader_mesh.h
@@ -13,19 +13,21 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#pragma once
/** \file
* \ingroup balembic
*/
-#ifndef __ABC_READER_MESH_H__
-#define __ABC_READER_MESH_H__
-
#include "abc_customdata.h"
#include "abc_reader_object.h"
struct Mesh;
+namespace blender {
+namespace io {
+namespace alembic {
+
class AbcMeshReader : public AbcObjectReader {
Alembic::AbcGeom::IPolyMeshSchema m_schema;
@@ -83,4 +85,6 @@ void read_mverts(MVert *mverts,
CDStreamConfig get_config(struct Mesh *mesh);
-#endif /* __ABC_READER_MESH_H__ */
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/intern/abc_reader_nurbs.cc b/source/blender/io/alembic/intern/abc_reader_nurbs.cc
index 5b9954b3ff6..3ca3f6229ab 100644
--- a/source/blender/io/alembic/intern/abc_reader_nurbs.cc
+++ b/source/blender/io/alembic/intern/abc_reader_nurbs.cc
@@ -44,6 +44,10 @@ using Alembic::AbcGeom::INuPatch;
using Alembic::AbcGeom::INuPatchSchema;
using Alembic::AbcGeom::IObject;
+namespace blender {
+namespace io {
+namespace alembic {
+
AbcNurbsReader::AbcNurbsReader(const IObject &object, ImportSettings &settings)
: AbcObjectReader(object, settings)
{
@@ -222,3 +226,7 @@ void AbcNurbsReader::getNurbsPatches(const IObject &obj)
getNurbsPatches(child);
}
}
+
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/intern/abc_reader_nurbs.h b/source/blender/io/alembic/intern/abc_reader_nurbs.h
index f4284c136fb..738da82885d 100644
--- a/source/blender/io/alembic/intern/abc_reader_nurbs.h
+++ b/source/blender/io/alembic/intern/abc_reader_nurbs.h
@@ -13,16 +13,18 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#pragma once
/** \file
* \ingroup balembic
*/
-#ifndef __ABC_READER_NURBS_H__
-#define __ABC_READER_NURBS_H__
-
#include "abc_reader_object.h"
+namespace blender {
+namespace io {
+namespace alembic {
+
class AbcNurbsReader : public AbcObjectReader {
std::vector<std::pair<Alembic::AbcGeom::INuPatchSchema, Alembic::Abc::IObject>> m_schemas;
@@ -37,4 +39,6 @@ class AbcNurbsReader : public AbcObjectReader {
void getNurbsPatches(const Alembic::Abc::IObject &obj);
};
-#endif /* __ABC_READER_NURBS_H__ */
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/intern/abc_reader_object.cc b/source/blender/io/alembic/intern/abc_reader_object.cc
index e5bd0771a42..39b9cd4c161 100644
--- a/source/blender/io/alembic/intern/abc_reader_object.cc
+++ b/source/blender/io/alembic/intern/abc_reader_object.cc
@@ -41,6 +41,10 @@ using Alembic::AbcGeom::IObject;
using Alembic::AbcGeom::IXform;
using Alembic::AbcGeom::IXformSchema;
+namespace blender {
+namespace io {
+namespace alembic {
+
AbcObjectReader::AbcObjectReader(const IObject &object, ImportSettings &settings)
: m_name(""),
m_object_name(""),
@@ -330,3 +334,7 @@ void AbcObjectReader::decref()
m_refcount--;
BLI_assert(m_refcount >= 0);
}
+
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/intern/abc_reader_object.h b/source/blender/io/alembic/intern/abc_reader_object.h
index dcc2697e0b5..0bde60b06b5 100644
--- a/source/blender/io/alembic/intern/abc_reader_object.h
+++ b/source/blender/io/alembic/intern/abc_reader_object.h
@@ -13,14 +13,12 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#pragma once
/** \file
* \ingroup balembic
*/
-#ifndef __ABC_READER_OBJECT_H__
-#define __ABC_READER_OBJECT_H__
-
#include <Alembic/Abc/All.h>
#include <Alembic/AbcGeom/All.h>
@@ -33,6 +31,10 @@ struct Object;
using Alembic::AbcCoreAbstract::chrono_t;
+namespace blender {
+namespace io {
+namespace alembic {
+
struct ImportSettings {
bool do_convert_mat;
float conversion_mat[4][4];
@@ -166,4 +168,6 @@ class AbcObjectReader {
Imath::M44d get_matrix(const Alembic::AbcGeom::IXformSchema &schema, const float time);
-#endif /* __ABC_READER_OBJECT_H__ */
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/intern/abc_reader_points.cc b/source/blender/io/alembic/intern/abc_reader_points.cc
index c5d08693176..b805da4daa3 100644
--- a/source/blender/io/alembic/intern/abc_reader_points.cc
+++ b/source/blender/io/alembic/intern/abc_reader_points.cc
@@ -43,6 +43,10 @@ using Alembic::AbcGeom::IPoints;
using Alembic::AbcGeom::IPointsSchema;
using Alembic::AbcGeom::ISampleSelector;
+namespace blender {
+namespace io {
+namespace alembic {
+
AbcPointsReader::AbcPointsReader(const Alembic::Abc::IObject &object, ImportSettings &settings)
: AbcObjectReader(object, settings)
{
@@ -153,3 +157,7 @@ struct Mesh *AbcPointsReader::read_mesh(struct Mesh *existing_mesh,
return new_mesh ? new_mesh : existing_mesh;
}
+
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/intern/abc_reader_points.h b/source/blender/io/alembic/intern/abc_reader_points.h
index 99881e091f9..8a970ac35b3 100644
--- a/source/blender/io/alembic/intern/abc_reader_points.h
+++ b/source/blender/io/alembic/intern/abc_reader_points.h
@@ -16,17 +16,19 @@
* The Original Code is Copyright (C) 2016 Kévin Dietrich.
* All rights reserved.
*/
+#pragma once
/** \file
* \ingroup balembic
*/
-#ifndef __ABC_READER_POINTS_H__
-#define __ABC_READER_POINTS_H__
-
#include "abc_customdata.h"
#include "abc_reader_object.h"
+namespace blender {
+namespace io {
+namespace alembic {
+
class AbcPointsReader : public AbcObjectReader {
Alembic::AbcGeom::IPointsSchema m_schema;
Alembic::AbcGeom::IPointsSchema::Sample m_sample;
@@ -51,4 +53,6 @@ void read_points_sample(const Alembic::AbcGeom::IPointsSchema &schema,
const Alembic::AbcGeom::ISampleSelector &selector,
CDStreamConfig &config);
-#endif /* __ABC_READER_POINTS_H__ */
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/intern/abc_reader_transform.cc b/source/blender/io/alembic/intern/abc_reader_transform.cc
index 3df391f8432..456d1da2c68 100644
--- a/source/blender/io/alembic/intern/abc_reader_transform.cc
+++ b/source/blender/io/alembic/intern/abc_reader_transform.cc
@@ -29,6 +29,10 @@
using Alembic::Abc::ISampleSelector;
+namespace blender {
+namespace io {
+namespace alembic {
+
AbcEmptyReader::AbcEmptyReader(const Alembic::Abc::IObject &object, ImportSettings &settings)
: AbcObjectReader(object, settings)
{
@@ -72,3 +76,7 @@ void AbcEmptyReader::readObjectData(Main *bmain, const ISampleSelector &UNUSED(s
m_object = BKE_object_add_only_object(bmain, OB_EMPTY, m_object_name.c_str());
m_object->data = NULL;
}
+
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/intern/abc_reader_transform.h b/source/blender/io/alembic/intern/abc_reader_transform.h
index 6b4d23c1884..812d3bdfc92 100644
--- a/source/blender/io/alembic/intern/abc_reader_transform.h
+++ b/source/blender/io/alembic/intern/abc_reader_transform.h
@@ -13,18 +13,20 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#pragma once
/** \file
* \ingroup balembic
*/
-#ifndef __ABC_READER_TRANSFORM_H__
-#define __ABC_READER_TRANSFORM_H__
-
#include "abc_reader_object.h"
#include <Alembic/AbcGeom/All.h>
+namespace blender {
+namespace io {
+namespace alembic {
+
class AbcEmptyReader : public AbcObjectReader {
Alembic::AbcGeom::IXformSchema m_schema;
@@ -39,4 +41,6 @@ class AbcEmptyReader : public AbcObjectReader {
void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel);
};
-#endif /* __ABC_READER_TRANSFORM_H__ */
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/intern/abc_util.cc b/source/blender/io/alembic/intern/abc_util.cc
index 1f3bd2a1aaa..04febd7bfcb 100644
--- a/source/blender/io/alembic/intern/abc_util.cc
+++ b/source/blender/io/alembic/intern/abc_util.cc
@@ -38,6 +38,10 @@
#include "PIL_time.h"
+namespace blender {
+namespace io {
+namespace alembic {
+
std::string get_id_name(const Object *const ob)
{
if (!ob) {
@@ -49,12 +53,16 @@ std::string get_id_name(const Object *const ob)
std::string get_id_name(const ID *const id)
{
- std::string name(id->name + 2);
- std::replace(name.begin(), name.end(), ' ', '_');
- std::replace(name.begin(), name.end(), '.', '_');
- std::replace(name.begin(), name.end(), ':', '_');
+ return get_valid_abc_name(id->name + 2);
+}
- return name;
+std::string get_valid_abc_name(const char *name)
+{
+ std::string name_string(name);
+ std::replace(name_string.begin(), name_string.end(), ' ', '_');
+ std::replace(name_string.begin(), name_string.end(), '.', '_');
+ std::replace(name_string.begin(), name_string.end(), ':', '_');
+ return name_string;
}
/**
@@ -252,3 +260,7 @@ std::ostream &operator<<(std::ostream &os, const SimpleLogger &logger)
os << logger.str();
return os;
}
+
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/intern/abc_util.h b/source/blender/io/alembic/intern/abc_util.h
index 57b4d9800a5..4689173ab5f 100644
--- a/source/blender/io/alembic/intern/abc_util.h
+++ b/source/blender/io/alembic/intern/abc_util.h
@@ -13,23 +13,15 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#pragma once
/** \file
* \ingroup balembic
*/
-#ifndef __ABC_UTIL_H__
-#define __ABC_UTIL_H__
-
#include <Alembic/Abc/All.h>
#include <Alembic/AbcGeom/All.h>
-#ifdef _MSC_VER
-# define ABC_INLINE static __forceinline
-#else
-# define ABC_INLINE static inline
-#endif
-
/**
* \brief The CacheReader struct is only used for anonymous pointers,
* to interface between C and C++ code. This library only creates
@@ -41,14 +33,19 @@ struct CacheReader {
using Alembic::Abc::chrono_t;
-class AbcObjectReader;
-struct ImportSettings;
-
struct ID;
struct Object;
+namespace blender {
+namespace io {
+namespace alembic {
+
+class AbcObjectReader;
+struct ImportSettings;
+
std::string get_id_name(const ID *const id);
std::string get_id_name(const Object *const ob);
+std::string get_valid_abc_name(const char *name);
std::string get_object_dag_path_name(const Object *const ob, Object *dupli_parent);
/* Convert from float to Alembic matrix representations. Does NOT convert from Z-up to Y-up. */
@@ -164,4 +161,6 @@ class SimpleLogger {
*/
std::ostream &operator<<(std::ostream &os, const SimpleLogger &logger);
-#endif /* __ABC_UTIL_H__ */
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/intern/abc_writer_archive.cc b/source/blender/io/alembic/intern/abc_writer_archive.cc
deleted file mode 100644
index 40926532f85..00000000000
--- a/source/blender/io/alembic/intern/abc_writer_archive.cc
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * The Original Code is Copyright (C) 2016 Kévin Dietrich.
- * All rights reserved.
- */
-
-/** \file
- * \ingroup balembic
- */
-
-#include "abc_writer_archive.h"
-
-#include "BKE_blender_version.h"
-
-#include "BLI_path_util.h"
-#include "BLI_string.h"
-
-#include "DNA_scene_types.h"
-
-#ifdef WIN32
-# include "utfconv.h"
-#endif
-
-#include <fstream>
-
-using Alembic::Abc::ErrorHandler;
-using Alembic::Abc::kWrapExisting;
-using Alembic::Abc::OArchive;
-
-/* This kinda duplicates CreateArchiveWithInfo, but Alembic does not seem to
- * have a version supporting streams. */
-static OArchive create_archive(std::ostream *ostream,
- const std::string &scene_name,
- double scene_fps)
-{
- Alembic::Abc::MetaData abc_metadata;
-
- abc_metadata.set(Alembic::Abc::kApplicationNameKey, "Blender");
- abc_metadata.set(Alembic::Abc::kUserDescriptionKey, scene_name);
- abc_metadata.set("blender_version", std::string("v") + BKE_blender_version_string());
- abc_metadata.set("FramesPerTimeUnit", std::to_string(scene_fps));
-
- time_t raw_time;
- time(&raw_time);
- char buffer[128];
-
-#if defined _WIN32 || defined _WIN64
- ctime_s(buffer, 128, &raw_time);
-#else
- ctime_r(&raw_time, buffer);
-#endif
-
- const std::size_t buffer_len = strlen(buffer);
- if (buffer_len > 0 && buffer[buffer_len - 1] == '\n') {
- buffer[buffer_len - 1] = '\0';
- }
-
- abc_metadata.set(Alembic::Abc::kDateWrittenKey, buffer);
-
- ErrorHandler::Policy policy = ErrorHandler::kThrowPolicy;
- Alembic::AbcCoreOgawa::WriteArchive archive_writer;
- return OArchive(archive_writer(ostream, abc_metadata), kWrapExisting, policy);
-}
-
-ArchiveWriter::ArchiveWriter(const char *filename,
- const std::string &abc_scene_name,
- const Scene *scene)
-{
- /* Use stream to support unicode character paths on Windows. */
-#ifdef WIN32
- UTF16_ENCODE(filename);
- std::wstring wstr(filename_16);
- m_outfile.open(wstr.c_str(), std::ios::out | std::ios::binary);
- UTF16_UN_ENCODE(filename);
-#else
- m_outfile.open(filename, std::ios::out | std::ios::binary);
-#endif
-
- m_archive = create_archive(&m_outfile, abc_scene_name, FPS);
-}
-
-OArchive &ArchiveWriter::archive()
-{
- return m_archive;
-}
diff --git a/source/blender/io/alembic/intern/abc_writer_camera.cc b/source/blender/io/alembic/intern/abc_writer_camera.cc
deleted file mode 100644
index 07ae81e584f..00000000000
--- a/source/blender/io/alembic/intern/abc_writer_camera.cc
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-/** \file
- * \ingroup balembic
- */
-
-#include "abc_writer_camera.h"
-#include "abc_writer_transform.h"
-
-#include "DNA_camera_types.h"
-#include "DNA_object_types.h"
-
-using Alembic::AbcGeom::OCamera;
-using Alembic::AbcGeom::OFloatProperty;
-
-AbcCameraWriter::AbcCameraWriter(Object *ob,
- AbcTransformWriter *parent,
- uint32_t time_sampling,
- ExportSettings &settings)
- : AbcObjectWriter(ob, time_sampling, settings, parent)
-{
- OCamera camera(parent->alembicXform(), m_name, m_time_sampling);
- m_camera_schema = camera.getSchema();
-
- m_custom_data_container = m_camera_schema.getUserProperties();
- m_stereo_distance = OFloatProperty(m_custom_data_container, "stereoDistance", m_time_sampling);
- m_eye_separation = OFloatProperty(m_custom_data_container, "eyeSeparation", m_time_sampling);
-}
-
-void AbcCameraWriter::do_write()
-{
- Camera *cam = static_cast<Camera *>(m_object->data);
-
- m_stereo_distance.set(cam->stereo.convergence_distance);
- m_eye_separation.set(cam->stereo.interocular_distance);
-
- const double apperture_x = cam->sensor_x / 10.0;
- const double apperture_y = cam->sensor_y / 10.0;
- const double film_aspect = apperture_x / apperture_y;
-
- m_camera_sample.setFocalLength(cam->lens);
- m_camera_sample.setHorizontalAperture(apperture_x);
- m_camera_sample.setVerticalAperture(apperture_y);
- m_camera_sample.setHorizontalFilmOffset(apperture_x * cam->shiftx);
- m_camera_sample.setVerticalFilmOffset(apperture_y * cam->shifty * film_aspect);
- m_camera_sample.setNearClippingPlane(cam->clip_start);
- m_camera_sample.setFarClippingPlane(cam->clip_end);
-
- if (cam->dof.focus_object) {
- Imath::V3f v(m_object->loc[0] - cam->dof.focus_object->loc[0],
- m_object->loc[1] - cam->dof.focus_object->loc[1],
- m_object->loc[2] - cam->dof.focus_object->loc[2]);
- m_camera_sample.setFocusDistance(v.length());
- }
- else {
- m_camera_sample.setFocusDistance(cam->dof.focus_distance);
- }
-
- /* Blender camera does not have an fstop param, so try to find a custom prop
- * instead. */
- m_camera_sample.setFStop(cam->dof.aperture_fstop);
-
- m_camera_sample.setLensSqueezeRatio(1.0);
- m_camera_schema.set(m_camera_sample);
-}
diff --git a/source/blender/io/alembic/intern/abc_writer_mball.cc b/source/blender/io/alembic/intern/abc_writer_mball.cc
deleted file mode 100644
index 3593acf18b0..00000000000
--- a/source/blender/io/alembic/intern/abc_writer_mball.cc
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-/** \file
- * \ingroup balembic
- */
-
-#include "abc_writer_mball.h"
-#include "abc_writer_mesh.h"
-
-#include "DNA_mesh_types.h"
-#include "DNA_meta_types.h"
-#include "DNA_object_types.h"
-
-#include "BKE_displist.h"
-#include "BKE_lib_id.h"
-#include "BKE_mball.h"
-#include "BKE_mesh.h"
-#include "BKE_object.h"
-
-#include "BLI_utildefines.h"
-
-AbcMBallWriter::AbcMBallWriter(Main *bmain,
- Object *ob,
- AbcTransformWriter *parent,
- uint32_t time_sampling,
- ExportSettings &settings)
- : AbcGenericMeshWriter(ob, parent, time_sampling, settings), m_bmain(bmain)
-{
- m_is_animated = isAnimated();
-}
-
-AbcMBallWriter::~AbcMBallWriter()
-{
-}
-
-bool AbcMBallWriter::isAnimated() const
-{
- return true;
-}
-
-Mesh *AbcMBallWriter::getEvaluatedMesh(Scene * /*scene_eval*/, Object *ob_eval, bool &r_needsfree)
-{
- Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob_eval);
- if (mesh_eval != NULL) {
- /* Mesh_eval only exists when generative modifiers are in use. */
- r_needsfree = false;
- return mesh_eval;
- }
- r_needsfree = true;
-
- /* The approach below is copied from BKE_mesh_new_from_object() */
- Mesh *tmpmesh = BKE_mesh_add(m_bmain, ((ID *)m_object->data)->name + 2);
- BLI_assert(tmpmesh != NULL);
-
- /* BKE_mesh_add gives us a user count we don't need */
- id_us_min(&tmpmesh->id);
-
- ListBase disp = {NULL, NULL};
- /* TODO(sergey): This is gonna to work for until Depsgraph
- * only contains for_render flag. As soon as CoW is
- * implemented, this is to be rethought.
- */
- BKE_displist_make_mball_forRender(m_settings.depsgraph, m_settings.scene, m_object, &disp);
- BKE_mesh_from_metaball(&disp, tmpmesh);
- BKE_displist_free(&disp);
-
- BKE_mesh_texspace_copy_from_object(tmpmesh, m_object);
-
- return tmpmesh;
-}
-
-void AbcMBallWriter::freeEvaluatedMesh(struct Mesh *mesh)
-{
- BKE_id_free(m_bmain, mesh);
-}
-
-bool AbcMBallWriter::isBasisBall(Scene *scene, Object *ob)
-{
- Object *basis_ob = BKE_mball_basis_find(scene, ob);
- return ob == basis_ob;
-}
diff --git a/source/blender/io/alembic/intern/abc_writer_mball.h b/source/blender/io/alembic/intern/abc_writer_mball.h
deleted file mode 100644
index e3ac1e69cae..00000000000
--- a/source/blender/io/alembic/intern/abc_writer_mball.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-/** \file
- * \ingroup balembic
- */
-
-#ifndef __ABC_WRITER_MBALL_H__
-#define __ABC_WRITER_MBALL_H__
-
-#include "abc_writer_mesh.h"
-#include "abc_writer_object.h"
-
-struct Main;
-struct Object;
-
-/* AbcMBallWriter converts the metaballs to meshes at every frame,
- * and defers to AbcGenericMeshWriter to perform the writing
- * to the Alembic file. Only the basis balls are exported, as this
- * results in the entire shape as one mesh. */
-class AbcMBallWriter : public AbcGenericMeshWriter {
- Main *m_bmain;
-
- public:
- explicit AbcMBallWriter(Main *bmain,
- Object *ob,
- AbcTransformWriter *parent,
- uint32_t time_sampling,
- ExportSettings &settings);
-
- ~AbcMBallWriter();
-
- static bool isBasisBall(Scene *scene, Object *ob);
-
- protected:
- Mesh *getEvaluatedMesh(Scene *scene_eval, Object *ob_eval, bool &r_needsfree) override;
- void freeEvaluatedMesh(struct Mesh *mesh) override;
-
- private:
- bool isAnimated() const override;
-};
-
-#endif /* __ABC_WRITER_MBALL_H__ */
diff --git a/source/blender/io/alembic/intern/abc_writer_mesh.h b/source/blender/io/alembic/intern/abc_writer_mesh.h
deleted file mode 100644
index 9152a370e4f..00000000000
--- a/source/blender/io/alembic/intern/abc_writer_mesh.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-/** \file
- * \ingroup balembic
- */
-
-#ifndef __ABC_WRITER_MESH_H__
-#define __ABC_WRITER_MESH_H__
-
-#include "abc_customdata.h"
-#include "abc_writer_object.h"
-
-struct Mesh;
-struct ModifierData;
-
-/* Writer for Alembic meshes. Does not assume the object is a mesh object. */
-class AbcGenericMeshWriter : public AbcObjectWriter {
- protected:
- Alembic::AbcGeom::OPolyMeshSchema m_mesh_schema;
- Alembic::AbcGeom::OPolyMeshSchema::Sample m_mesh_sample;
-
- Alembic::AbcGeom::OSubDSchema m_subdiv_schema;
- Alembic::AbcGeom::OSubDSchema::Sample m_subdiv_sample;
-
- Alembic::Abc::OArrayProperty m_mat_indices;
-
- bool m_is_animated;
- ModifierData *m_subsurf_mod;
-
- CDStreamConfig m_custom_data_config;
-
- bool m_is_liquid;
- bool m_is_subd;
-
- public:
- AbcGenericMeshWriter(Object *ob,
- AbcTransformWriter *parent,
- uint32_t time_sampling,
- ExportSettings &settings);
-
- ~AbcGenericMeshWriter();
- void setIsAnimated(bool is_animated);
-
- protected:
- virtual void do_write();
- virtual bool isAnimated() const;
- virtual Mesh *getEvaluatedMesh(Scene *scene_eval, Object *ob_eval, bool &r_needsfree) = 0;
- virtual void freeEvaluatedMesh(struct Mesh *mesh);
-
- Mesh *getFinalMesh(bool &r_needsfree);
-
- void writeMesh(struct Mesh *mesh);
- void writeSubD(struct Mesh *mesh);
-
- void writeArbGeoParams(struct Mesh *mesh);
- void getGeoGroups(struct Mesh *mesh, std::map<std::string, std::vector<int32_t>> &geoGroups);
-
- /* fluid surfaces support */
- void getVelocities(struct Mesh *mesh, std::vector<Imath::V3f> &vels);
-
- template<typename Schema> void writeFaceSets(struct Mesh *mesh, Schema &schema);
-};
-
-class AbcMeshWriter : public AbcGenericMeshWriter {
- public:
- AbcMeshWriter(Object *ob,
- AbcTransformWriter *parent,
- uint32_t time_sampling,
- ExportSettings &settings);
-
- ~AbcMeshWriter();
-
- protected:
- virtual Mesh *getEvaluatedMesh(Scene *scene_eval, Object *ob_eval, bool &r_needsfree) override;
-};
-
-#endif /* __ABC_WRITER_MESH_H__ */
diff --git a/source/blender/io/alembic/intern/abc_writer_object.cc b/source/blender/io/alembic/intern/abc_writer_object.cc
deleted file mode 100644
index f4a3587f54d..00000000000
--- a/source/blender/io/alembic/intern/abc_writer_object.cc
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-/** \file
- * \ingroup balembic
- */
-
-#include "abc_writer_object.h"
-
-#include "DNA_object_types.h"
-
-#include "BKE_object.h"
-
-AbcObjectWriter::AbcObjectWriter(Object *ob,
- uint32_t time_sampling,
- ExportSettings &settings,
- AbcObjectWriter *parent)
- : m_object(ob), m_settings(settings), m_time_sampling(time_sampling), m_first_frame(true)
-{
- m_name = get_id_name(m_object) + "Shape";
-
- if (parent) {
- parent->addChild(this);
- }
-}
-
-AbcObjectWriter::~AbcObjectWriter()
-{
-}
-
-void AbcObjectWriter::addChild(AbcObjectWriter *child)
-{
- m_children.push_back(child);
-}
-
-Imath::Box3d AbcObjectWriter::bounds()
-{
- BoundBox *bb = BKE_object_boundbox_get(this->m_object);
-
- if (!bb) {
- if (this->m_object->type != OB_CAMERA) {
- ABC_LOG(m_settings.logger) << "Bounding box is null!\n";
- }
-
- return Imath::Box3d();
- }
-
- /* Convert Z-up to Y-up. This also changes which vector goes into which min/max property. */
- this->m_bounds.min.x = bb->vec[0][0];
- this->m_bounds.min.y = bb->vec[0][2];
- this->m_bounds.min.z = -bb->vec[6][1];
-
- this->m_bounds.max.x = bb->vec[6][0];
- this->m_bounds.max.y = bb->vec[6][2];
- this->m_bounds.max.z = -bb->vec[0][1];
-
- return this->m_bounds;
-}
-
-void AbcObjectWriter::write()
-{
- do_write();
- m_first_frame = false;
-}
diff --git a/source/blender/io/alembic/intern/abc_writer_object.h b/source/blender/io/alembic/intern/abc_writer_object.h
deleted file mode 100644
index 830c4aee903..00000000000
--- a/source/blender/io/alembic/intern/abc_writer_object.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-/** \file
- * \ingroup balembic
- */
-
-#ifndef __ABC_WRITER_OBJECT_H__
-#define __ABC_WRITER_OBJECT_H__
-
-#include <Alembic/Abc/All.h>
-#include <Alembic/AbcGeom/All.h>
-
-#include "abc_exporter.h"
-
-#include "DNA_ID.h"
-
-class AbcTransformWriter;
-
-struct Main;
-struct Object;
-
-class AbcObjectWriter {
- protected:
- Object *m_object;
- ExportSettings &m_settings;
-
- uint32_t m_time_sampling;
-
- Imath::Box3d m_bounds;
- std::vector<AbcObjectWriter *> m_children;
-
- std::vector<std::pair<std::string, IDProperty *>> m_props;
-
- bool m_first_frame;
- std::string m_name;
-
- public:
- AbcObjectWriter(Object *ob,
- uint32_t time_sampling,
- ExportSettings &settings,
- AbcObjectWriter *parent = NULL);
-
- virtual ~AbcObjectWriter();
-
- void addChild(AbcObjectWriter *child);
-
- virtual Imath::Box3d bounds();
-
- void write();
-
- private:
- virtual void do_write() = 0;
-};
-
-#endif /* __ABC_WRITER_OBJECT_H__ */
diff --git a/source/blender/io/alembic/intern/abc_writer_points.cc b/source/blender/io/alembic/intern/abc_writer_points.cc
deleted file mode 100644
index ac663b62693..00000000000
--- a/source/blender/io/alembic/intern/abc_writer_points.cc
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * The Original Code is Copyright (C) 2016 Kévin Dietrich.
- * All rights reserved.
- */
-
-/** \file
- * \ingroup balembic
- */
-
-#include "abc_writer_points.h"
-#include "abc_util.h"
-#include "abc_writer_mesh.h"
-#include "abc_writer_transform.h"
-
-#include "DNA_object_types.h"
-#include "DNA_particle_types.h"
-
-#include "BKE_lattice.h"
-#include "BKE_particle.h"
-
-#include "BLI_math.h"
-
-#include "DEG_depsgraph_query.h"
-
-using Alembic::AbcGeom::kVertexScope;
-using Alembic::AbcGeom::OPoints;
-using Alembic::AbcGeom::OPointsSchema;
-
-/* ************************************************************************** */
-
-AbcPointsWriter::AbcPointsWriter(Object *ob,
- AbcTransformWriter *parent,
- uint32_t time_sampling,
- ExportSettings &settings,
- ParticleSystem *psys)
- : AbcObjectWriter(ob, time_sampling, settings, parent)
-{
- m_psys = psys;
-
- OPoints points(parent->alembicXform(), psys->name, m_time_sampling);
- m_schema = points.getSchema();
-}
-
-void AbcPointsWriter::do_write()
-{
- if (!m_psys) {
- return;
- }
-
- std::vector<Imath::V3f> points;
- std::vector<Imath::V3f> velocities;
- std::vector<float> widths;
- std::vector<uint64_t> ids;
-
- ParticleKey state;
-
- ParticleSimulationData sim;
- sim.depsgraph = m_settings.depsgraph;
- sim.scene = m_settings.scene;
- sim.ob = m_object;
- sim.psys = m_psys;
-
- m_psys->lattice_deform_data = psys_create_lattice_deform_data(&sim);
-
- uint64_t index = 0;
- for (int p = 0; p < m_psys->totpart; p++) {
- float pos[3], vel[3];
-
- if (m_psys->particles[p].flag & (PARS_NO_DISP | PARS_UNEXIST)) {
- continue;
- }
-
- state.time = DEG_get_ctime(m_settings.depsgraph);
-
- if (psys_get_particle_state(&sim, p, &state, 0) == 0) {
- continue;
- }
-
- /* location */
- mul_v3_m4v3(pos, m_object->imat, state.co);
-
- /* velocity */
- sub_v3_v3v3(vel, state.co, m_psys->particles[p].prev_state.co);
-
- /* Convert Z-up to Y-up. */
- points.push_back(Imath::V3f(pos[0], pos[2], -pos[1]));
- velocities.push_back(Imath::V3f(vel[0], vel[2], -vel[1]));
- widths.push_back(m_psys->particles[p].size);
- ids.push_back(index++);
- }
-
- if (m_psys->lattice_deform_data) {
- BKE_lattice_deform_data_destroy(m_psys->lattice_deform_data);
- m_psys->lattice_deform_data = NULL;
- }
-
- Alembic::Abc::P3fArraySample psample(points);
- Alembic::Abc::UInt64ArraySample idsample(ids);
- Alembic::Abc::V3fArraySample vsample(velocities);
- Alembic::Abc::FloatArraySample wsample_array(widths);
- Alembic::AbcGeom::OFloatGeomParam::Sample wsample(wsample_array, kVertexScope);
-
- m_sample = OPointsSchema::Sample(psample, idsample, vsample, wsample);
- m_sample.setSelfBounds(bounds());
-
- m_schema.set(m_sample);
-}
diff --git a/source/blender/io/alembic/intern/abc_writer_transform.cc b/source/blender/io/alembic/intern/abc_writer_transform.cc
deleted file mode 100644
index 1ec7db0a1c6..00000000000
--- a/source/blender/io/alembic/intern/abc_writer_transform.cc
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-/** \file
- * \ingroup balembic
- */
-
-#include "abc_writer_transform.h"
-#include "abc_axis_conversion.h"
-
-#include <OpenEXR/ImathBoxAlgo.h>
-
-#include "DNA_object_types.h"
-
-#include "BLI_math.h"
-
-#include "DEG_depsgraph_query.h"
-
-using Alembic::AbcGeom::OObject;
-using Alembic::AbcGeom::OXform;
-
-AbcTransformWriter::AbcTransformWriter(Object *ob,
- const OObject &abc_parent,
- AbcTransformWriter *parent,
- unsigned int time_sampling,
- ExportSettings &settings)
- : AbcObjectWriter(ob, time_sampling, settings, parent), m_proxy_from(NULL)
-{
- m_is_animated = hasAnimation(m_object);
-
- if (!m_is_animated) {
- time_sampling = 0;
- }
-
- m_xform = OXform(abc_parent, get_id_name(m_object), time_sampling);
- m_schema = m_xform.getSchema();
-
- /* Blender objects can't have a parent without inheriting the transform. */
- m_inherits_xform = parent != NULL;
-}
-
-void AbcTransformWriter::do_write()
-{
- Object *ob_eval = DEG_get_evaluated_object(m_settings.depsgraph, m_object);
-
- if (m_first_frame) {
- m_visibility = Alembic::AbcGeom::CreateVisibilityProperty(
- m_xform, m_xform.getSchema().getTimeSampling());
- }
-
- m_visibility.set(!(ob_eval->restrictflag & OB_RESTRICT_VIEWPORT));
-
- if (!m_first_frame && !m_is_animated) {
- return;
- }
-
- float yup_mat[4][4];
- create_transform_matrix(
- ob_eval, yup_mat, m_inherits_xform ? ABC_MATRIX_LOCAL : ABC_MATRIX_WORLD, m_proxy_from);
-
- /* If the parent is a camera, undo its to-Maya rotation (see below). */
- bool is_root_object = !m_inherits_xform || ob_eval->parent == nullptr;
- if (!is_root_object && ob_eval->parent->type == OB_CAMERA) {
- float rot_mat[4][4];
- axis_angle_to_mat4_single(rot_mat, 'X', M_PI_2);
- mul_m4_m4m4(yup_mat, rot_mat, yup_mat);
- }
-
- /* If the object is a camera, apply an extra rotation to Maya camera orientation. */
- if (ob_eval->type == OB_CAMERA) {
- float rot_mat[4][4];
- axis_angle_to_mat4_single(rot_mat, 'X', -M_PI_2);
- mul_m4_m4m4(yup_mat, yup_mat, rot_mat);
- }
-
- if (is_root_object) {
- /* Only apply scaling to root objects, parenting will propagate it. */
- float scale_mat[4][4];
- scale_m4_fl(scale_mat, m_settings.global_scale);
- scale_mat[3][3] = m_settings.global_scale; /* also scale translation */
- mul_m4_m4m4(yup_mat, yup_mat, scale_mat);
- yup_mat[3][3] /= m_settings.global_scale; /* normalise the homogeneous component */
- }
-
- m_matrix = convert_matrix_datatype(yup_mat);
- m_sample.setMatrix(m_matrix);
- m_sample.setInheritsXforms(m_inherits_xform);
- m_schema.set(m_sample);
-}
-
-Imath::Box3d AbcTransformWriter::bounds()
-{
- Imath::Box3d bounds;
-
- for (int i = 0; i < m_children.size(); i++) {
- Imath::Box3d box(m_children[i]->bounds());
- bounds.extendBy(box);
- }
-
- return Imath::transform(bounds, m_matrix);
-}
-
-bool AbcTransformWriter::hasAnimation(Object * /*ob*/) const
-{
- return true;
-}
diff --git a/source/blender/io/alembic/intern/abc_writer_transform.h b/source/blender/io/alembic/intern/abc_writer_transform.h
deleted file mode 100644
index 4397b220761..00000000000
--- a/source/blender/io/alembic/intern/abc_writer_transform.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-/** \file
- * \ingroup balembic
- */
-
-#ifndef __ABC_WRITER_TRANSFORM_H__
-#define __ABC_WRITER_TRANSFORM_H__
-
-#include "abc_writer_object.h"
-
-#include <Alembic/AbcGeom/All.h>
-
-class AbcTransformWriter : public AbcObjectWriter {
- Alembic::AbcGeom::OXform m_xform;
- Alembic::AbcGeom::OXformSchema m_schema;
- Alembic::AbcGeom::XformSample m_sample;
- Alembic::AbcGeom::OVisibilityProperty m_visibility;
- Alembic::Abc::M44d m_matrix;
-
- bool m_is_animated;
- bool m_inherits_xform;
-
- public:
- Object *m_proxy_from;
-
- public:
- AbcTransformWriter(Object *ob,
- const Alembic::AbcGeom::OObject &abc_parent,
- AbcTransformWriter *parent,
- unsigned int time_sampling,
- ExportSettings &settings);
-
- Alembic::AbcGeom::OXform &alembicXform()
- {
- return m_xform;
- }
- virtual Imath::Box3d bounds();
-
- private:
- virtual void do_write();
-
- bool hasAnimation(Object *ob) const;
-};
-
-#endif /* __ABC_WRITER_TRANSFORM_H__ */
diff --git a/source/blender/io/alembic/intern/alembic_capi.cc b/source/blender/io/alembic/intern/alembic_capi.cc
index a30b15fee5f..5932791ecf4 100644
--- a/source/blender/io/alembic/intern/alembic_capi.cc
+++ b/source/blender/io/alembic/intern/alembic_capi.cc
@@ -30,13 +30,6 @@
#include "abc_reader_points.h"
#include "abc_reader_transform.h"
#include "abc_util.h"
-#include "abc_writer_camera.h"
-#include "abc_writer_curves.h"
-#include "abc_writer_hair.h"
-#include "abc_writer_mesh.h"
-#include "abc_writer_nurbs.h"
-#include "abc_writer_points.h"
-#include "abc_writer_transform.h"
#include "MEM_guardedalloc.h"
@@ -58,12 +51,15 @@
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_build.h"
+#include "ED_undo.h"
+
/* SpaceType struct has a member called 'new' which obviously conflicts with C++
* so temporarily redefining the new keyword to make it compile. */
#define new extern_new
#include "BKE_screen.h"
#undef new
+#include "BLI_compiler_compat.h"
#include "BLI_fileops.h"
#include "BLI_ghash.h"
#include "BLI_listbase.h"
@@ -74,48 +70,33 @@
#include "WM_api.h"
#include "WM_types.h"
-using Alembic::Abc::Int32ArraySamplePtr;
using Alembic::Abc::ObjectHeader;
-
-using Alembic::AbcGeom::kWrapExisting;
-using Alembic::AbcGeom::MetaData;
-using Alembic::AbcGeom::P3fArraySamplePtr;
-
using Alembic::AbcGeom::ICamera;
-using Alembic::AbcGeom::ICompoundProperty;
using Alembic::AbcGeom::ICurves;
-using Alembic::AbcGeom::ICurvesSchema;
using Alembic::AbcGeom::IFaceSet;
using Alembic::AbcGeom::ILight;
-using Alembic::AbcGeom::IN3fArrayProperty;
-using Alembic::AbcGeom::IN3fGeomParam;
using Alembic::AbcGeom::INuPatch;
using Alembic::AbcGeom::IObject;
using Alembic::AbcGeom::IPoints;
-using Alembic::AbcGeom::IPointsSchema;
using Alembic::AbcGeom::IPolyMesh;
-using Alembic::AbcGeom::IPolyMeshSchema;
using Alembic::AbcGeom::ISampleSelector;
using Alembic::AbcGeom::ISubD;
-using Alembic::AbcGeom::IV2fGeomParam;
using Alembic::AbcGeom::IXform;
-using Alembic::AbcGeom::IXformSchema;
-using Alembic::AbcGeom::N3fArraySamplePtr;
-using Alembic::AbcGeom::V3fArraySamplePtr;
-using Alembic::AbcGeom::XformSample;
-
+using Alembic::AbcGeom::MetaData;
using Alembic::AbcMaterial::IMaterial;
+using namespace blender::io::alembic;
+
struct AbcArchiveHandle {
int unused;
};
-ABC_INLINE ArchiveReader *archive_from_handle(AbcArchiveHandle *handle)
+BLI_INLINE ArchiveReader *archive_from_handle(AbcArchiveHandle *handle)
{
return reinterpret_cast<ArchiveReader *>(handle);
}
-ABC_INLINE AbcArchiveHandle *handle_from_archive(ArchiveReader *archive)
+BLI_INLINE AbcArchiveHandle *handle_from_archive(ArchiveReader *archive)
{
return reinterpret_cast<AbcArchiveHandle *>(archive);
}
@@ -225,195 +206,6 @@ static void find_iobject(const IObject &object, IObject &ret, const std::string
ret = tmp;
}
-struct ExportJobData {
- ViewLayer *view_layer;
- Main *bmain;
- wmWindowManager *wm;
-
- char filename[1024];
- ExportSettings settings;
-
- short *stop;
- short *do_update;
- float *progress;
-
- bool was_canceled;
- bool export_ok;
-};
-
-static void export_startjob(void *customdata, short *stop, short *do_update, float *progress)
-{
- ExportJobData *data = static_cast<ExportJobData *>(customdata);
-
- data->stop = stop;
- data->do_update = do_update;
- data->progress = progress;
-
- /* XXX annoying hack: needed to prevent data corruption when changing
- * scene frame in separate threads
- */
- G.is_rendering = true;
- WM_set_locked_interface(data->wm, true);
- G.is_break = false;
-
- DEG_graph_build_from_view_layer(
- data->settings.depsgraph, data->bmain, data->settings.scene, data->view_layer);
- BKE_scene_graph_update_tagged(data->settings.depsgraph, data->bmain);
-
- try {
- AbcExporter exporter(data->bmain, data->filename, data->settings);
-
- Scene *scene = data->settings.scene; /* for the CFRA macro */
- const int orig_frame = CFRA;
-
- data->was_canceled = false;
- exporter(do_update, progress, &data->was_canceled);
-
- if (CFRA != orig_frame) {
- CFRA = orig_frame;
-
- BKE_scene_graph_update_for_newframe(data->settings.depsgraph, data->bmain);
- }
-
- data->export_ok = !data->was_canceled;
- }
- catch (const std::exception &e) {
- ABC_LOG(data->settings.logger) << "Abc Export error: " << e.what() << '\n';
- }
- catch (...) {
- ABC_LOG(data->settings.logger) << "Abc Export: unknown error...\n";
- }
-}
-
-static void export_endjob(void *customdata)
-{
- ExportJobData *data = static_cast<ExportJobData *>(customdata);
-
- DEG_graph_free(data->settings.depsgraph);
-
- if (data->was_canceled && BLI_exists(data->filename)) {
- BLI_delete(data->filename, false, false);
- }
-
- std::string log = data->settings.logger.str();
- if (!log.empty()) {
- std::cerr << log;
- WM_report(RPT_ERROR, "Errors occurred during the export, look in the console to know more...");
- }
-
- G.is_rendering = false;
- WM_set_locked_interface(data->wm, false);
-}
-
-bool ABC_export(Scene *scene,
- bContext *C,
- const char *filepath,
- const struct AlembicExportParams *params,
- bool as_background_job)
-{
- ExportJobData *job = static_cast<ExportJobData *>(
- MEM_mallocN(sizeof(ExportJobData), "ExportJobData"));
-
- job->view_layer = CTX_data_view_layer(C);
- job->bmain = CTX_data_main(C);
- job->wm = CTX_wm_manager(C);
- job->export_ok = false;
- BLI_strncpy(job->filename, filepath, 1024);
-
- /* Alright, alright, alright....
- *
- * ExportJobData contains an ExportSettings containing a SimpleLogger.
- *
- * Since ExportJobData is a C-style struct dynamically allocated with
- * MEM_mallocN (see above), its constructor is never called, therefore the
- * ExportSettings constructor is not called which implies that the
- * SimpleLogger one is not called either. SimpleLogger in turn does not call
- * the constructor of its data members which ultimately means that its
- * std::ostringstream member has a NULL pointer. To be able to properly use
- * the stream's operator<<, the pointer needs to be set, therefore we have
- * to properly construct everything. And this is done using the placement
- * new operator as here below. It seems hackish, but I'm too lazy to
- * do bigger refactor and maybe there is a better way which does not involve
- * hardcore refactoring. */
- new (&job->settings) ExportSettings();
- job->settings.scene = scene;
- job->settings.depsgraph = DEG_graph_new(job->bmain, scene, job->view_layer, DAG_EVAL_RENDER);
-
- /* TODO(Sybren): for now we only export the active scene layer.
- * Later in the 2.8 development process this may be replaced by using
- * a specific collection for Alembic I/O, which can then be toggled
- * between "real" objects and cached Alembic files. */
- job->settings.view_layer = job->view_layer;
-
- job->settings.frame_start = params->frame_start;
- job->settings.frame_end = params->frame_end;
- job->settings.frame_samples_xform = params->frame_samples_xform;
- job->settings.frame_samples_shape = params->frame_samples_shape;
- job->settings.shutter_open = params->shutter_open;
- job->settings.shutter_close = params->shutter_close;
-
- /* TODO(Sybren): For now this is ignored, until we can get selection
- * detection working through Base pointers (instead of ob->flags). */
- job->settings.selected_only = params->selected_only;
-
- job->settings.export_face_sets = params->face_sets;
- job->settings.export_normals = params->normals;
- job->settings.export_uvs = params->uvs;
- job->settings.export_vcols = params->vcolors;
- job->settings.export_hair = params->export_hair;
- job->settings.export_particles = params->export_particles;
- job->settings.apply_subdiv = params->apply_subdiv;
- job->settings.curves_as_mesh = params->curves_as_mesh;
- job->settings.flatten_hierarchy = params->flatten_hierarchy;
-
- /* TODO(Sybren): visible_layer & renderable only is ignored for now,
- * to be replaced with collections later in the 2.8 dev process
- * (also see note above). */
- job->settings.visible_objects_only = params->visible_objects_only;
- job->settings.renderable_only = params->renderable_only;
-
- job->settings.use_subdiv_schema = params->use_subdiv_schema;
- job->settings.pack_uv = params->packuv;
- job->settings.global_scale = params->global_scale;
- job->settings.triangulate = params->triangulate;
- job->settings.quad_method = params->quad_method;
- job->settings.ngon_method = params->ngon_method;
-
- if (job->settings.frame_start > job->settings.frame_end) {
- std::swap(job->settings.frame_start, job->settings.frame_end);
- }
-
- bool export_ok = false;
- if (as_background_job) {
- wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C),
- CTX_wm_window(C),
- job->settings.scene,
- "Alembic Export",
- WM_JOB_PROGRESS,
- WM_JOB_TYPE_ALEMBIC);
-
- /* setup job */
- WM_jobs_customdata_set(wm_job, job, MEM_freeN);
- WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_FRAME, NC_SCENE | ND_FRAME);
- WM_jobs_callbacks(wm_job, export_startjob, NULL, NULL, export_endjob);
-
- WM_jobs_start(CTX_wm_manager(C), wm_job);
- }
- else {
- /* Fake a job context, so that we don't need NULL pointer checks while exporting. */
- short stop = 0, do_update = 0;
- float progress = 0.f;
-
- export_startjob(job, &stop, &do_update, &progress);
- export_endjob(job);
- export_ok = job->export_ok;
-
- MEM_freeN(job);
- }
-
- return export_ok;
-}
-
/* ********************** Import file ********************** */
/**
@@ -622,6 +414,7 @@ enum {
};
struct ImportJobData {
+ bContext *C;
Main *bmain;
Scene *scene;
ViewLayer *view_layer;
@@ -640,6 +433,7 @@ struct ImportJobData {
char error_code;
bool was_cancelled;
bool import_ok;
+ bool is_background_job;
};
static void import_startjob(void *user_data, short *stop, short *do_update, float *progress)
@@ -823,6 +617,12 @@ static void import_endjob(void *user_data)
DEG_id_tag_update(&data->scene->id, ID_RECALC_BASE_FLAGS);
DEG_relations_tag_update(data->bmain);
+
+ if (data->is_background_job) {
+ /* Blender already returned from the import operator, so we need to store our own extra undo
+ * step. */
+ ED_undo_push(data->C, "Alembic Import Finished");
+ }
}
for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) {
@@ -868,6 +668,7 @@ bool ABC_import(bContext *C,
{
/* Using new here since MEM_* functions do not call constructor to properly initialize data. */
ImportJobData *job = new ImportJobData();
+ job->C = C;
job->bmain = CTX_data_main(C);
job->scene = CTX_data_scene(C);
job->view_layer = CTX_data_view_layer(C);
@@ -884,6 +685,7 @@ bool ABC_import(bContext *C,
job->error_code = ABC_NO_ERROR;
job->was_cancelled = false;
job->archive = NULL;
+ job->is_background_job = as_background_job;
G.is_break = false;
diff --git a/source/blender/io/collada/AnimationImporter.cpp b/source/blender/io/collada/AnimationImporter.cpp
index edac84e2aaa..1de86425521 100644
--- a/source/blender/io/collada/AnimationImporter.cpp
+++ b/source/blender/io/collada/AnimationImporter.cpp
@@ -344,9 +344,11 @@ bool AnimationImporter::write_animation_list(const COLLADAFW::AnimationList *ani
return true;
}
-/* \todo refactor read_node_transform to not automatically apply anything,
+/**
+ * \todo refactor read_node_transform to not automatically apply anything,
* but rather return the transform matrix, so caller can do with it what is
- * necessary. Same for \ref get_node_mat */
+ * necessary. Same for \ref get_node_mat
+ */
void AnimationImporter::read_node_transform(COLLADAFW::Node *node, Object *ob)
{
float mat[4][4];
diff --git a/source/blender/io/collada/BCMath.cpp b/source/blender/io/collada/BCMath.cpp
index ec9977c1469..e8765fa2bcd 100644
--- a/source/blender/io/collada/BCMath.cpp
+++ b/source/blender/io/collada/BCMath.cpp
@@ -17,6 +17,8 @@
* All rights reserved.
*/
+#include "BLI_utildefines.h"
+
#include "BCMath.h"
#include "BlenderContext.h"
diff --git a/source/blender/io/collada/ImageExporter.cpp b/source/blender/io/collada/ImageExporter.cpp
index 1c897e37a4a..51d753db03f 100644
--- a/source/blender/io/collada/ImageExporter.cpp
+++ b/source/blender/io/collada/ImageExporter.cpp
@@ -55,7 +55,7 @@ void ImagesExporter::export_UV_Image(Image *image, bool use_copies)
ImBuf *imbuf = BKE_image_acquire_ibuf(image, NULL, NULL);
if (!imbuf) {
- fprintf(stderr, "Collada export: image does not exist:\n%s\n", image->name);
+ fprintf(stderr, "Collada export: image does not exist:\n%s\n", image->filepath);
return;
}
@@ -104,7 +104,7 @@ void ImagesExporter::export_UV_Image(Image *image, bool use_copies)
else {
/* make absolute source path */
- BLI_strncpy(source_path, image->name, sizeof(source_path));
+ BLI_strncpy(source_path, image->filepath, sizeof(source_path));
BLI_path_abs(source_path, ID_BLEND_PATH_FROM_GLOBAL(&image->id));
BLI_path_normalize(NULL, source_path);
diff --git a/build_files/build_environment/cmake/hidapi.cmake b/source/blender/io/common/CMakeLists.txt
index 9a3d2ebf8df..4ed6f12762e 100644
--- a/build_files/build_environment/cmake/hidapi.cmake
+++ b/source/blender/io/common/CMakeLists.txt
@@ -14,16 +14,32 @@
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
+# The Original Code is Copyright (C) 2020, Blender Foundation
+# All rights reserved.
# ***** END GPL LICENSE BLOCK *****
-set(HIDAPI_EXTRA_ARGS)
+set(INC
+ .
+ ../../blenkernel
+ ../../blenlib
+ ../../depsgraph
+ ../../makesdna
+)
+
+set(INC_SYS
+)
+
+set(SRC
+ intern/abstract_hierarchy_iterator.cc
-ExternalProject_Add(external_hidapi
- URL ${HIDAPI_URI}
- DOWNLOAD_DIR ${DOWNLOAD_DIR}
- URL_HASH MD5=${HIDAPI_HASH}
- PREFIX ${BUILD_DIR}/hidapi
- PATCH_COMMAND COMMAND ${CMAKE_COMMAND} -E copy ${PATCH_DIR}/cmakelists_hidapi.txt ${BUILD_DIR}/hidapi/src/external_hidapi/cmakelists.txt && ${PATCH_CMD} -p 0 -d ${BUILD_DIR}/hidapi/src/external_hidapi < ${PATCH_DIR}/hidapi.diff
- CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${LIBDIR}/hidapi -Wno-dev ${DEFAULT_CMAKE_FLAGS} ${HIDAPI_EXTRA_ARGS}
- INSTALL_DIR ${LIBDIR}/hidapi
+ IO_abstract_hierarchy_iterator.h
)
+
+set(LIB
+ bf_blenkernel
+ bf_blenlib
+)
+
+blender_add_lib(bf_io_common "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
+
+target_link_libraries(bf_io_common INTERFACE)
diff --git a/source/blender/io/usd/intern/abstract_hierarchy_iterator.h b/source/blender/io/common/IO_abstract_hierarchy_iterator.h
index e31d5c91252..5f84fd48b71 100644
--- a/source/blender/io/usd/intern/abstract_hierarchy_iterator.h
+++ b/source/blender/io/common/IO_abstract_hierarchy_iterator.h
@@ -48,7 +48,8 @@ struct Object;
struct ParticleSystem;
struct ViewLayer;
-namespace USD {
+namespace blender {
+namespace io {
class AbstractHierarchyWriter;
@@ -90,6 +91,14 @@ struct HierarchyContext {
* exported objects, in which case this string is empty even though 'duplicator' is set. */
std::string original_export_path;
+ /* Export path of the higher-up exported data. For transforms, this is the export path of the
+ * parent object. For object data, this is the export path of that object's transform.
+ *
+ * From the exported file's point of view, this is the path to the parent in that file. The term
+ * "parent" is not used here to avoid confusion with Blender's meaning of the word (which always
+ * refers to a different object). */
+ std::string higher_up_export_path;
+
bool operator<(const HierarchyContext &other) const;
/* Return a HierarchyContext representing the root of the export hierarchy. */
@@ -119,6 +128,39 @@ class AbstractHierarchyWriter {
virtual bool check_is_animated(const HierarchyContext &context) const;
};
+/* Determines which subset of the writers actually gets to write. */
+struct ExportSubset {
+ bool transforms : 1;
+ bool shapes : 1;
+};
+
+/* EnsuredWriter represents an AbstractHierarchyWriter* combined with information whether it was
+ * newly created or not. It's returned by AbstractHierarchyIterator::ensure_writer(). */
+class EnsuredWriter {
+ private:
+ AbstractHierarchyWriter *writer_;
+
+ /* Is set to truth when ensure_writer() did not find existing writer and created a new one.
+ * Is set to false when writer has been re-used or when allocation of the new one has failed
+ * (`writer` will be `nullptr` in that case and bool(ensured_writer) will be false). */
+ bool newly_created_;
+
+ EnsuredWriter(AbstractHierarchyWriter *writer, bool newly_created);
+
+ public:
+ EnsuredWriter();
+
+ static EnsuredWriter empty();
+ static EnsuredWriter existing(AbstractHierarchyWriter *writer);
+ static EnsuredWriter newly_created(AbstractHierarchyWriter *writer);
+
+ bool is_newly_created() const;
+
+ /* These operators make an EnsuredWriter* act as an AbstractHierarchyWriter* */
+ operator bool() const;
+ AbstractHierarchyWriter *operator->();
+};
+
/* AbstractHierarchyIterator iterates over objects in a dependency graph, and constructs export
* writers. These writers are then called to perform the actual writing to a USD or Alembic file.
*
@@ -147,6 +189,7 @@ class AbstractHierarchyIterator {
ExportPathMap duplisource_export_path_;
Depsgraph *depsgraph_;
WriterMap writers_;
+ ExportSubset export_subset_;
public:
explicit AbstractHierarchyIterator(Depsgraph *depsgraph);
@@ -154,12 +197,21 @@ class AbstractHierarchyIterator {
/* Iterate over the depsgraph, create writers, and tell the writers to write.
* Main entry point for the AbstractHierarchyIterator, must be called for every to-be-exported
- * frame. */
- void iterate_and_write();
+ * (sub)frame. */
+ virtual void iterate_and_write();
/* Release all writers. Call after all frames have been exported. */
void release_writers();
+ /* Determine which subset of writers is used for exporting.
+ * Set this before calling iterate_and_write().
+ *
+ * Note that writers are created for each iterated object, regardless of this option. When a
+ * writer is created it will also write the current iteration, to ensure the hierarchy is
+ * complete. The `export_subset` option is only in effect when the writer already existed from a
+ * previous iteration. */
+ void set_export_subset(ExportSubset export_subset_);
+
/* Convert the given name to something that is valid for the exported file format.
* This base implementation is a no-op; override in a concrete subclass. */
virtual std::string make_valid_name(const std::string &name) const;
@@ -187,7 +239,6 @@ class AbstractHierarchyIterator {
Object *duplicator,
const std::set<Object *> &dupli_set);
- ExportChildren &graph_children(const HierarchyContext *parent_context);
void context_update_for_graph_index(HierarchyContext *context,
const ExportGraph::key_type &graph_index) const;
@@ -200,18 +251,21 @@ class AbstractHierarchyIterator {
void make_writer_object_data(const HierarchyContext *context);
void make_writers_particle_systems(const HierarchyContext *context);
+ /* Return the appropriate HierarchyContext for the data of the object represented by
+ * object_context. */
+ HierarchyContext context_for_object_data(const HierarchyContext *object_context) const;
+
/* Convenience wrappers around get_id_name(). */
std::string get_object_name(const Object *object) const;
std::string get_object_data_name(const Object *object) const;
- AbstractHierarchyWriter *get_writer(const std::string &export_path) const;
-
typedef AbstractHierarchyWriter *(AbstractHierarchyIterator::*create_writer_func)(
const HierarchyContext *);
- /* Ensure that a writer exists; if it doesn't, call create_func(context). The create_func
- * function should be one of the create_XXXX_writer(context) functions declared below. */
- AbstractHierarchyWriter *ensure_writer(HierarchyContext *context,
- create_writer_func create_func);
+ /* Ensure that a writer exists; if it doesn't, call create_func(context).
+ *
+ * The create_func function should be one of the create_XXXX_writer(context) functions declared
+ * below. */
+ EnsuredWriter ensure_writer(HierarchyContext *context, create_writer_func create_func);
protected:
/* Construct a valid path for the export file format. This class concatenates by using '/' as a
@@ -256,8 +310,12 @@ class AbstractHierarchyIterator {
/* Called by release_writers() to free what the create_XXX_writer() functions allocated. */
virtual void delete_object_writer(AbstractHierarchyWriter *writer) = 0;
+
+ AbstractHierarchyWriter *get_writer(const std::string &export_path) const;
+ ExportChildren &graph_children(const HierarchyContext *parent_context);
};
-} // namespace USD
+} // namespace io
+} // namespace blender
#endif /* __ABSTRACT_HIERARCHY_ITERATOR_H__ */
diff --git a/source/blender/io/usd/intern/abstract_hierarchy_iterator.cc b/source/blender/io/common/intern/abstract_hierarchy_iterator.cc
index ab83ea2c3c4..9a456bfaf69 100644
--- a/source/blender/io/usd/intern/abstract_hierarchy_iterator.cc
+++ b/source/blender/io/common/intern/abstract_hierarchy_iterator.cc
@@ -16,7 +16,7 @@
* The Original Code is Copyright (C) 2019 Blender Foundation.
* All rights reserved.
*/
-#include "abstract_hierarchy_iterator.h"
+#include "IO_abstract_hierarchy_iterator.h"
#include <iostream>
#include <limits.h>
@@ -41,7 +41,8 @@
#include "DEG_depsgraph_query.h"
-namespace USD {
+namespace blender {
+namespace io {
const HierarchyContext *HierarchyContext::root()
{
@@ -74,6 +75,43 @@ void HierarchyContext::mark_as_not_instanced()
original_export_path.clear();
}
+EnsuredWriter::EnsuredWriter() : writer_(nullptr), newly_created_(false)
+{
+}
+
+EnsuredWriter::EnsuredWriter(AbstractHierarchyWriter *writer, bool newly_created)
+ : writer_(writer), newly_created_(newly_created)
+{
+}
+
+EnsuredWriter EnsuredWriter::empty()
+{
+ return EnsuredWriter(nullptr, false);
+}
+EnsuredWriter EnsuredWriter::existing(AbstractHierarchyWriter *writer)
+{
+ return EnsuredWriter(writer, false);
+}
+EnsuredWriter EnsuredWriter::newly_created(AbstractHierarchyWriter *writer)
+{
+ return EnsuredWriter(writer, true);
+}
+
+bool EnsuredWriter::is_newly_created() const
+{
+ return newly_created_;
+}
+
+EnsuredWriter::operator bool() const
+{
+ return writer_ != nullptr;
+}
+
+AbstractHierarchyWriter *EnsuredWriter::operator->()
+{
+ return writer_;
+}
+
AbstractHierarchyWriter::~AbstractHierarchyWriter()
{
}
@@ -104,7 +142,7 @@ bool AbstractHierarchyWriter::check_is_animated(const HierarchyContext &context)
}
AbstractHierarchyIterator::AbstractHierarchyIterator(Depsgraph *depsgraph)
- : depsgraph_(depsgraph), writers_()
+ : depsgraph_(depsgraph), writers_(), export_subset_({true, true})
{
}
@@ -131,6 +169,11 @@ void AbstractHierarchyIterator::release_writers()
writers_.clear();
}
+void AbstractHierarchyIterator::set_export_subset(ExportSubset export_subset)
+{
+ export_subset_ = export_subset;
+}
+
std::string AbstractHierarchyIterator::make_valid_name(const std::string &name) const
{
return name;
@@ -341,6 +384,8 @@ void AbstractHierarchyIterator::visit_object(Object *object,
context->animation_check_include_parent = false;
context->export_path = "";
context->original_export_path = "";
+ context->higher_up_export_path = "";
+
copy_m4_m4(context->matrix_world, object->obmat);
ExportGraph::key_type graph_index = determine_graph_index_object(context);
@@ -494,7 +539,6 @@ void AbstractHierarchyIterator::determine_duplication_references(
void AbstractHierarchyIterator::make_writers(const HierarchyContext *parent_context)
{
- AbstractHierarchyWriter *transform_writer = nullptr;
float parent_matrix_inv_world[4][4];
if (parent_context) {
@@ -507,20 +551,27 @@ void AbstractHierarchyIterator::make_writers(const HierarchyContext *parent_cont
for (HierarchyContext *context : graph_children(parent_context)) {
// Update the context so that it is correct for this parent-child relation.
copy_m4_m4(context->parent_matrix_inv_world, parent_matrix_inv_world);
+ if (parent_context != nullptr) {
+ context->higher_up_export_path = parent_context->export_path;
+ }
// Get or create the transform writer.
- transform_writer = ensure_writer(context, &AbstractHierarchyIterator::create_transform_writer);
- if (transform_writer == nullptr) {
+ EnsuredWriter transform_writer = ensure_writer(
+ context, &AbstractHierarchyIterator::create_transform_writer);
+
+ if (!transform_writer) {
// Unable to export, so there is nothing to attach any children to; just abort this entire
// branch of the export hierarchy.
return;
}
BLI_assert(DEG_is_evaluated_object(context->object));
- /* XXX This can lead to too many XForms being written. For example, a camera writer can refuse
- * to write an orthographic camera. By the time that this is known, the XForm has already been
- * written. */
- transform_writer->write(*context);
+ if (transform_writer.is_newly_created() || export_subset_.transforms) {
+ /* XXX This can lead to too many XForms being written. For example, a camera writer can
+ * refuse to write an orthographic camera. By the time that this is known, the XForm has
+ * already been written. */
+ transform_writer->write(*context);
+ }
if (!context->weak_export) {
make_writers_particle_systems(context);
@@ -534,17 +585,24 @@ void AbstractHierarchyIterator::make_writers(const HierarchyContext *parent_cont
// TODO(Sybren): iterate over all unused writers and call unused_during_iteration() or something.
}
+HierarchyContext AbstractHierarchyIterator::context_for_object_data(
+ const HierarchyContext *object_context) const
+{
+ HierarchyContext data_context = *object_context;
+ data_context.higher_up_export_path = object_context->export_path;
+ data_context.export_name = get_object_data_name(data_context.object);
+ data_context.export_path = path_concatenate(data_context.higher_up_export_path,
+ data_context.export_name);
+ return data_context;
+}
+
void AbstractHierarchyIterator::make_writer_object_data(const HierarchyContext *context)
{
if (context->object->data == nullptr) {
return;
}
- HierarchyContext data_context = *context;
- data_context.export_path = get_object_data_path(context);
-
- /* data_context.original_export_path is just a copy from the context. It points to the object,
- * but needs to point to the object data. */
+ HierarchyContext data_context = context_for_object_data(context);
if (data_context.is_instance()) {
ID *object_data = static_cast<ID *>(context->object->data);
data_context.original_export_path = duplisource_export_path_[object_data];
@@ -553,13 +611,16 @@ void AbstractHierarchyIterator::make_writer_object_data(const HierarchyContext *
BLI_assert(data_context.is_instance());
}
- AbstractHierarchyWriter *data_writer;
- data_writer = ensure_writer(&data_context, &AbstractHierarchyIterator::create_data_writer);
- if (data_writer == nullptr) {
+ /* Always write upon creation, otherwise depend on which subset is active. */
+ EnsuredWriter data_writer = ensure_writer(&data_context,
+ &AbstractHierarchyIterator::create_data_writer);
+ if (!data_writer) {
return;
}
- data_writer->write(data_context);
+ if (data_writer.is_newly_created() || export_subset_.shapes) {
+ data_writer->write(data_context);
+ }
}
void AbstractHierarchyIterator::make_writers_particle_systems(
@@ -573,11 +634,13 @@ void AbstractHierarchyIterator::make_writers_particle_systems(
}
HierarchyContext hair_context = *transform_context;
+ hair_context.export_name = make_valid_name(psys->name);
hair_context.export_path = path_concatenate(transform_context->export_path,
- make_valid_name(psys->name));
+ hair_context.export_name);
+ hair_context.higher_up_export_path = transform_context->export_path;
hair_context.particle_system = psys;
- AbstractHierarchyWriter *writer = nullptr;
+ EnsuredWriter writer;
switch (psys->part->type) {
case PART_HAIR:
writer = ensure_writer(&hair_context, &AbstractHierarchyIterator::create_hair_writer);
@@ -586,8 +649,12 @@ void AbstractHierarchyIterator::make_writers_particle_systems(
writer = ensure_writer(&hair_context, &AbstractHierarchyIterator::create_particle_writer);
break;
}
+ if (!writer) {
+ continue;
+ }
- if (writer != nullptr) {
+ /* Always write upon creation, otherwise depend on which subset is active. */
+ if (writer.is_newly_created() || export_subset_.shapes) {
writer->write(hair_context);
}
}
@@ -615,22 +682,21 @@ AbstractHierarchyWriter *AbstractHierarchyIterator::get_writer(
return it->second;
}
-AbstractHierarchyWriter *AbstractHierarchyIterator::ensure_writer(
+EnsuredWriter AbstractHierarchyIterator::ensure_writer(
HierarchyContext *context, AbstractHierarchyIterator::create_writer_func create_func)
{
AbstractHierarchyWriter *writer = get_writer(context->export_path);
if (writer != nullptr) {
- return writer;
+ return EnsuredWriter::existing(writer);
}
writer = (this->*create_func)(context);
if (writer == nullptr) {
- return nullptr;
+ return EnsuredWriter::empty();
}
writers_[context->export_path] = writer;
-
- return writer;
+ return EnsuredWriter::newly_created(writer);
}
std::string AbstractHierarchyIterator::path_concatenate(const std::string &parent_path,
@@ -649,4 +715,5 @@ bool AbstractHierarchyIterator::should_visit_dupli_object(const DupliObject *dup
return !dupli_object->no_draw;
}
-} // namespace USD
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/usd/CMakeLists.txt b/source/blender/io/usd/CMakeLists.txt
index 732a638a255..19e16a5b328 100644
--- a/source/blender/io/usd/CMakeLists.txt
+++ b/source/blender/io/usd/CMakeLists.txt
@@ -32,6 +32,7 @@ add_definitions(-DPXR_STATIC)
set(INC
.
+ ../common
../../blenkernel
../../blenlib
../../blenloader
@@ -52,7 +53,6 @@ set(INC_SYS
)
set(SRC
- intern/abstract_hierarchy_iterator.cc
intern/usd_capi.cc
intern/usd_hierarchy_iterator.cc
intern/usd_writer_abstract.cc
@@ -64,7 +64,6 @@ set(SRC
intern/usd_writer_transform.cc
usd.h
- intern/abstract_hierarchy_iterator.h
intern/usd_exporter_context.h
intern/usd_hierarchy_iterator.h
intern/usd_writer_abstract.h
@@ -79,6 +78,7 @@ set(SRC
set(LIB
bf_blenkernel
bf_blenlib
+ bf_io_common
)
list(APPEND LIB
@@ -100,10 +100,10 @@ endif()
# Source: https://github.com/PixarAnimationStudios/USD/blob/master/BUILDING.md#linking-whole-archives
if(WIN32)
target_link_libraries(bf_usd INTERFACE ${USD_LIBRARIES})
-elseif(CMAKE_COMPILER_IS_GNUCXX)
- target_link_libraries(bf_usd INTERFACE "-Wl,--whole-archive ${USD_LIBRARIES} -Wl,--no-whole-archive ${TBB_LIBRARIES}")
-elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
+elseif(APPLE)
target_link_libraries(bf_usd INTERFACE -Wl,-force_load ${USD_LIBRARIES})
+elseif(UNIX)
+ target_link_libraries(bf_usd INTERFACE "-Wl,--whole-archive ${USD_LIBRARIES} -Wl,--no-whole-archive ${TBB_LIBRARIES}")
else()
message(FATAL_ERROR "Unknown how to link USD with your compiler ${CMAKE_CXX_COMPILER_ID}")
endif()
diff --git a/source/blender/io/usd/intern/usd_capi.cc b/source/blender/io/usd/intern/usd_capi.cc
index cf962446d04..2d3972410a4 100644
--- a/source/blender/io/usd/intern/usd_capi.cc
+++ b/source/blender/io/usd/intern/usd_capi.cc
@@ -44,7 +44,9 @@
#include "WM_api.h"
#include "WM_types.h"
-namespace USD {
+namespace blender {
+namespace io {
+namespace usd {
struct ExportJobData {
Main *bmain;
@@ -157,7 +159,9 @@ static void export_endjob(void *customdata)
WM_set_locked_interface(data->wm, false);
}
-} // namespace USD
+} // namespace usd
+} // namespace io
+} // namespace blender
bool USD_export(bContext *C,
const char *filepath,
@@ -167,8 +171,8 @@ bool USD_export(bContext *C,
ViewLayer *view_layer = CTX_data_view_layer(C);
Scene *scene = CTX_data_scene(C);
- USD::ExportJobData *job = static_cast<USD::ExportJobData *>(
- MEM_mallocN(sizeof(USD::ExportJobData), "ExportJobData"));
+ blender::io::usd::ExportJobData *job = static_cast<blender::io::usd::ExportJobData *>(
+ MEM_mallocN(sizeof(blender::io::usd::ExportJobData), "ExportJobData"));
job->bmain = CTX_data_main(C);
job->wm = CTX_wm_manager(C);
@@ -186,7 +190,11 @@ bool USD_export(bContext *C,
/* setup job */
WM_jobs_customdata_set(wm_job, job, MEM_freeN);
WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_FRAME, NC_SCENE | ND_FRAME);
- WM_jobs_callbacks(wm_job, USD::export_startjob, nullptr, nullptr, USD::export_endjob);
+ WM_jobs_callbacks(wm_job,
+ blender::io::usd::export_startjob,
+ nullptr,
+ nullptr,
+ blender::io::usd::export_endjob);
WM_jobs_start(CTX_wm_manager(C), wm_job);
}
@@ -195,8 +203,8 @@ bool USD_export(bContext *C,
short stop = 0, do_update = 0;
float progress = 0.f;
- USD::export_startjob(job, &stop, &do_update, &progress);
- USD::export_endjob(job);
+ blender::io::usd::export_startjob(job, &stop, &do_update, &progress);
+ blender::io::usd::export_endjob(job);
export_ok = job->export_ok;
MEM_freeN(job);
diff --git a/source/blender/io/usd/intern/usd_exporter_context.h b/source/blender/io/usd/intern/usd_exporter_context.h
index 4ae415b3d34..07a9d0fc0c5 100644
--- a/source/blender/io/usd/intern/usd_exporter_context.h
+++ b/source/blender/io/usd/intern/usd_exporter_context.h
@@ -27,7 +27,9 @@
struct Depsgraph;
struct Object;
-namespace USD {
+namespace blender {
+namespace io {
+namespace usd {
class USDHierarchyIterator;
@@ -39,6 +41,8 @@ struct USDExporterContext {
const USDExportParams &export_params;
};
-} // namespace USD
+} // namespace usd
+} // namespace io
+} // namespace blender
#endif /* __USD_EXPORTER_CONTEXT_H__ */
diff --git a/source/blender/io/usd/intern/usd_hierarchy_iterator.cc b/source/blender/io/usd/intern/usd_hierarchy_iterator.cc
index 56e367dd877..388b588b331 100644
--- a/source/blender/io/usd/intern/usd_hierarchy_iterator.cc
+++ b/source/blender/io/usd/intern/usd_hierarchy_iterator.cc
@@ -41,7 +41,9 @@
#include "DNA_layer_types.h"
#include "DNA_object_types.h"
-namespace USD {
+namespace blender {
+namespace io {
+namespace usd {
USDHierarchyIterator::USDHierarchyIterator(Depsgraph *depsgraph,
pxr::UsdStageRefPtr stage,
@@ -145,4 +147,6 @@ AbstractHierarchyWriter *USDHierarchyIterator::create_particle_writer(const Hier
return nullptr;
}
-} // namespace USD
+} // namespace usd
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/usd/intern/usd_hierarchy_iterator.h b/source/blender/io/usd/intern/usd_hierarchy_iterator.h
index a608012a390..7d750bff0cb 100644
--- a/source/blender/io/usd/intern/usd_hierarchy_iterator.h
+++ b/source/blender/io/usd/intern/usd_hierarchy_iterator.h
@@ -19,7 +19,7 @@
#ifndef __USD_HIERARCHY_ITERATOR_H__
#define __USD_HIERARCHY_ITERATOR_H__
-#include "abstract_hierarchy_iterator.h"
+#include "IO_abstract_hierarchy_iterator.h"
#include "usd.h"
#include "usd_exporter_context.h"
@@ -32,7 +32,13 @@ struct Depsgraph;
struct ID;
struct Object;
-namespace USD {
+namespace blender {
+namespace io {
+namespace usd {
+
+using blender::io::AbstractHierarchyIterator;
+using blender::io::AbstractHierarchyWriter;
+using blender::io::HierarchyContext;
class USDHierarchyIterator : public AbstractHierarchyIterator {
private:
@@ -66,6 +72,8 @@ class USDHierarchyIterator : public AbstractHierarchyIterator {
USDExporterContext create_usd_export_context(const HierarchyContext *context);
};
-} // namespace USD
+} // namespace usd
+} // namespace io
+} // namespace blender
#endif /* __USD_HIERARCHY_ITERATOR_H__ */
diff --git a/source/blender/io/usd/intern/usd_writer_abstract.cc b/source/blender/io/usd/intern/usd_writer_abstract.cc
index 76a2436ee92..a416941fb4d 100644
--- a/source/blender/io/usd/intern/usd_writer_abstract.cc
+++ b/source/blender/io/usd/intern/usd_writer_abstract.cc
@@ -32,7 +32,9 @@ static const pxr::TfToken roughness("roughness", pxr::TfToken::Immortal);
static const pxr::TfToken surface("surface", pxr::TfToken::Immortal);
} // namespace usdtokens
-namespace USD {
+namespace blender {
+namespace io {
+namespace usd {
USDAbstractWriter::USDAbstractWriter(const USDExporterContext &usd_export_context)
: usd_export_context_(usd_export_context),
@@ -112,4 +114,6 @@ pxr::UsdShadeMaterial USDAbstractWriter::ensure_usd_material(Material *material)
return usd_material;
}
-} // namespace USD
+} // namespace usd
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/usd/intern/usd_writer_abstract.h b/source/blender/io/usd/intern/usd_writer_abstract.h
index 01b53f4c916..f81cf5197af 100644
--- a/source/blender/io/usd/intern/usd_writer_abstract.h
+++ b/source/blender/io/usd/intern/usd_writer_abstract.h
@@ -19,7 +19,7 @@
#ifndef __USD_WRITER_ABSTRACT_H__
#define __USD_WRITER_ABSTRACT_H__
-#include "abstract_hierarchy_iterator.h"
+#include "IO_abstract_hierarchy_iterator.h"
#include "usd_exporter_context.h"
#include <pxr/usd/sdf/path.h>
@@ -36,7 +36,12 @@
struct Material;
struct Object;
-namespace USD {
+namespace blender {
+namespace io {
+namespace usd {
+
+using blender::io::AbstractHierarchyWriter;
+using blender::io::HierarchyContext;
class USDAbstractWriter : public AbstractHierarchyWriter {
protected:
@@ -70,6 +75,8 @@ class USDAbstractWriter : public AbstractHierarchyWriter {
pxr::UsdShadeMaterial ensure_usd_material(Material *material);
};
-} // namespace USD
+} // namespace usd
+} // namespace io
+} // namespace blender
#endif /* __USD_WRITER_ABSTRACT_H__ */
diff --git a/source/blender/io/usd/intern/usd_writer_camera.cc b/source/blender/io/usd/intern/usd_writer_camera.cc
index ea551a43c9f..d51eb32d3fd 100644
--- a/source/blender/io/usd/intern/usd_writer_camera.cc
+++ b/source/blender/io/usd/intern/usd_writer_camera.cc
@@ -28,7 +28,9 @@
#include "DNA_camera_types.h"
#include "DNA_scene_types.h"
-namespace USD {
+namespace blender {
+namespace io {
+namespace usd {
USDCameraWriter::USDCameraWriter(const USDExporterContext &ctx) : USDAbstractWriter(ctx)
{
@@ -106,4 +108,6 @@ void USDCameraWriter::do_write(HierarchyContext &context)
}
}
-} // namespace USD
+} // namespace usd
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/usd/intern/usd_writer_camera.h b/source/blender/io/usd/intern/usd_writer_camera.h
index 971264ef11e..8b5795d7d9f 100644
--- a/source/blender/io/usd/intern/usd_writer_camera.h
+++ b/source/blender/io/usd/intern/usd_writer_camera.h
@@ -21,7 +21,9 @@
#include "usd_writer_abstract.h"
-namespace USD {
+namespace blender {
+namespace io {
+namespace usd {
/* Writer for writing camera data to UsdGeomCamera. */
class USDCameraWriter : public USDAbstractWriter {
@@ -33,6 +35,8 @@ class USDCameraWriter : public USDAbstractWriter {
virtual void do_write(HierarchyContext &context) override;
};
-} // namespace USD
+} // namespace usd
+} // namespace io
+} // namespace blender
#endif /* __USD_WRITER_CAMERA_H__ */
diff --git a/source/blender/io/usd/intern/usd_writer_hair.cc b/source/blender/io/usd/intern/usd_writer_hair.cc
index d38c1032969..0e0256bdb69 100644
--- a/source/blender/io/usd/intern/usd_writer_hair.cc
+++ b/source/blender/io/usd/intern/usd_writer_hair.cc
@@ -26,7 +26,9 @@
#include "DNA_particle_types.h"
-namespace USD {
+namespace blender {
+namespace io {
+namespace usd {
USDHairWriter::USDHairWriter(const USDExporterContext &ctx) : USDAbstractWriter(ctx)
{
@@ -85,4 +87,6 @@ bool USDHairWriter::check_is_animated(const HierarchyContext &) const
return true;
}
-} // namespace USD
+} // namespace usd
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/usd/intern/usd_writer_hair.h b/source/blender/io/usd/intern/usd_writer_hair.h
index 1e882fa1654..cecacd0a355 100644
--- a/source/blender/io/usd/intern/usd_writer_hair.h
+++ b/source/blender/io/usd/intern/usd_writer_hair.h
@@ -21,7 +21,9 @@
#include "usd_writer_abstract.h"
-namespace USD {
+namespace blender {
+namespace io {
+namespace usd {
/* Writer for writing hair particle data as USD curves. */
class USDHairWriter : public USDAbstractWriter {
@@ -33,6 +35,8 @@ class USDHairWriter : public USDAbstractWriter {
virtual bool check_is_animated(const HierarchyContext &context) const override;
};
-} // namespace USD
+} // namespace usd
+} // namespace io
+} // namespace blender
#endif /* __USD_WRITER_HAIR_H__ */
diff --git a/source/blender/io/usd/intern/usd_writer_light.cc b/source/blender/io/usd/intern/usd_writer_light.cc
index 0ce3ee5f8ce..19115dd1a4e 100644
--- a/source/blender/io/usd/intern/usd_writer_light.cc
+++ b/source/blender/io/usd/intern/usd_writer_light.cc
@@ -30,7 +30,9 @@
#include "DNA_light_types.h"
#include "DNA_object_types.h"
-namespace USD {
+namespace blender {
+namespace io {
+namespace usd {
USDLightWriter::USDLightWriter(const USDExporterContext &ctx) : USDAbstractWriter(ctx)
{
@@ -107,4 +109,6 @@ void USDLightWriter::do_write(HierarchyContext &context)
usd_light.CreateSpecularAttr().Set(light->spec_fac, timecode);
}
-} // namespace USD
+} // namespace usd
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/usd/intern/usd_writer_light.h b/source/blender/io/usd/intern/usd_writer_light.h
index 349c034b6bc..73666622af1 100644
--- a/source/blender/io/usd/intern/usd_writer_light.h
+++ b/source/blender/io/usd/intern/usd_writer_light.h
@@ -21,7 +21,9 @@
#include "usd_writer_abstract.h"
-namespace USD {
+namespace blender {
+namespace io {
+namespace usd {
class USDLightWriter : public USDAbstractWriter {
public:
@@ -32,6 +34,8 @@ class USDLightWriter : public USDAbstractWriter {
virtual void do_write(HierarchyContext &context) override;
};
-} // namespace USD
+} // namespace usd
+} // namespace io
+} // namespace blender
#endif /* __USD_WRITER_LIGHT_H__ */
diff --git a/source/blender/io/usd/intern/usd_writer_mesh.cc b/source/blender/io/usd/intern/usd_writer_mesh.cc
index 61337beff20..29a9734f876 100644
--- a/source/blender/io/usd/intern/usd_writer_mesh.cc
+++ b/source/blender/io/usd/intern/usd_writer_mesh.cc
@@ -42,7 +42,9 @@
#include "DNA_object_fluidsim_types.h"
#include "DNA_particle_types.h"
-namespace USD {
+namespace blender {
+namespace io {
+namespace usd {
USDGenericMeshWriter::USDGenericMeshWriter(const USDExporterContext &ctx) : USDAbstractWriter(ctx)
{
@@ -484,4 +486,6 @@ Mesh *USDMeshWriter::get_export_mesh(Object *object_eval, bool & /*r_needsfree*/
return BKE_object_get_evaluated_mesh(object_eval);
}
-} // namespace USD
+} // namespace usd
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/usd/intern/usd_writer_mesh.h b/source/blender/io/usd/intern/usd_writer_mesh.h
index 4175e2b7e27..a14ceecfa53 100644
--- a/source/blender/io/usd/intern/usd_writer_mesh.h
+++ b/source/blender/io/usd/intern/usd_writer_mesh.h
@@ -23,7 +23,9 @@
#include <pxr/usd/usdGeom/mesh.h>
-namespace USD {
+namespace blender {
+namespace io {
+namespace usd {
struct USDMeshData;
@@ -61,6 +63,8 @@ class USDMeshWriter : public USDGenericMeshWriter {
virtual Mesh *get_export_mesh(Object *object_eval, bool &r_needsfree) override;
};
-} // namespace USD
+} // namespace usd
+} // namespace io
+} // namespace blender
#endif /* __USD_WRITER_MESH_H__ */
diff --git a/source/blender/io/usd/intern/usd_writer_metaball.cc b/source/blender/io/usd/intern/usd_writer_metaball.cc
index 96bf854d327..f003fba18a4 100644
--- a/source/blender/io/usd/intern/usd_writer_metaball.cc
+++ b/source/blender/io/usd/intern/usd_writer_metaball.cc
@@ -34,7 +34,9 @@
#include "DNA_mesh_types.h"
#include "DNA_meta_types.h"
-namespace USD {
+namespace blender {
+namespace io {
+namespace usd {
USDMetaballWriter::USDMetaballWriter(const USDExporterContext &ctx) : USDGenericMeshWriter(ctx)
{
@@ -76,4 +78,6 @@ bool USDMetaballWriter::is_basis_ball(Scene *scene, Object *ob) const
return ob == basis_ob;
}
-} // namespace USD
+} // namespace usd
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/usd/intern/usd_writer_metaball.h b/source/blender/io/usd/intern/usd_writer_metaball.h
index 1a86daae2ae..9f51a3314a5 100644
--- a/source/blender/io/usd/intern/usd_writer_metaball.h
+++ b/source/blender/io/usd/intern/usd_writer_metaball.h
@@ -21,7 +21,9 @@
#include "usd_writer_mesh.h"
-namespace USD {
+namespace blender {
+namespace io {
+namespace usd {
class USDMetaballWriter : public USDGenericMeshWriter {
public:
@@ -37,6 +39,8 @@ class USDMetaballWriter : public USDGenericMeshWriter {
bool is_basis_ball(Scene *scene, Object *ob) const;
};
-} // namespace USD
+} // namespace usd
+} // namespace io
+} // namespace blender
#endif /* __USD_WRITER_METABALL_H__ */
diff --git a/source/blender/io/usd/intern/usd_writer_transform.cc b/source/blender/io/usd/intern/usd_writer_transform.cc
index 0694d873002..643f1a8f4b1 100644
--- a/source/blender/io/usd/intern/usd_writer_transform.cc
+++ b/source/blender/io/usd/intern/usd_writer_transform.cc
@@ -28,7 +28,9 @@
#include "DNA_layer_types.h"
-namespace USD {
+namespace blender {
+namespace io {
+namespace usd {
USDTransformWriter::USDTransformWriter(const USDExporterContext &ctx) : USDAbstractWriter(ctx)
{
@@ -59,4 +61,6 @@ bool USDTransformWriter::check_is_animated(const HierarchyContext &context) cons
return BKE_object_moves_in_time(context.object, context.animation_check_include_parent);
}
-} // namespace USD
+} // namespace usd
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/usd/intern/usd_writer_transform.h b/source/blender/io/usd/intern/usd_writer_transform.h
index 52c4a657f33..8b4741f1177 100644
--- a/source/blender/io/usd/intern/usd_writer_transform.h
+++ b/source/blender/io/usd/intern/usd_writer_transform.h
@@ -23,7 +23,9 @@
#include <pxr/usd/usdGeom/xform.h>
-namespace USD {
+namespace blender {
+namespace io {
+namespace usd {
class USDTransformWriter : public USDAbstractWriter {
private:
@@ -37,6 +39,8 @@ class USDTransformWriter : public USDAbstractWriter {
bool check_is_animated(const HierarchyContext &context) const override;
};
-} // namespace USD
+} // namespace usd
+} // namespace io
+} // namespace blender
#endif /* __USD_WRITER_TRANSFORM_H__ */
diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h
index 2c9888ec06e..76af761ff27 100644
--- a/source/blender/makesdna/DNA_ID.h
+++ b/source/blender/makesdna/DNA_ID.h
@@ -400,16 +400,17 @@ typedef struct Library {
ID id;
struct FileData *filedata;
/** Path name used for reading, can be relative and edited in the outliner. */
- char name[1024];
+ char filepath[1024];
/**
- * Absolute filepath, this is only for convenience,
- * 'name' is the real path used on file read but in
- * some cases its useful to access the absolute one.
- * This is set on file read.
- * Use BKE_library_filepath_set() rather than setting 'name'
- * directly and it will be kept in sync - campbell */
- char filepath[1024];
+ * Run-time only, absolute file-path (set on read).
+ * This is only for convenience, `filepath` is the real path
+ * used on file read but in some cases its useful to access the absolute one.
+ *
+ * Use #BKE_library_filepath_set() rather than setting `filepath`
+ * directly and it will be kept in sync - campbell
+ */
+ char filepath_abs[1024];
/** Set for indirectly linked libs, used in the outliner and while reading. */
struct Library *parent;
@@ -557,21 +558,21 @@ typedef enum ID_Type {
/* fluidsim Ipo */
#define ID_FLUIDSIM MAKE_ID2('F', 'S')
-#define ID_FAKE_USERS(id) ((((ID *)id)->flag & LIB_FAKEUSER) ? 1 : 0)
-#define ID_REAL_USERS(id) (((ID *)id)->us - ID_FAKE_USERS(id))
-#define ID_EXTRA_USERS(id) (((ID *)id)->tag & LIB_TAG_EXTRAUSER ? 1 : 0)
+#define ID_FAKE_USERS(id) ((((const ID *)id)->flag & LIB_FAKEUSER) ? 1 : 0)
+#define ID_REAL_USERS(id) (((const ID *)id)->us - ID_FAKE_USERS(id))
+#define ID_EXTRA_USERS(id) (((const ID *)id)->tag & LIB_TAG_EXTRAUSER ? 1 : 0)
#define ID_CHECK_UNDO(id) \
((GS((id)->name) != ID_SCR) && (GS((id)->name) != ID_WM) && (GS((id)->name) != ID_WS))
#define ID_BLEND_PATH(_bmain, _id) \
- ((_id)->lib ? (_id)->lib->filepath : BKE_main_blendfile_path((_bmain)))
+ ((_id)->lib ? (_id)->lib->filepath_abs : BKE_main_blendfile_path((_bmain)))
#define ID_BLEND_PATH_FROM_GLOBAL(_id) \
- ((_id)->lib ? (_id)->lib->filepath : BKE_main_blendfile_path_from_global())
+ ((_id)->lib ? (_id)->lib->filepath_abs : BKE_main_blendfile_path_from_global())
-#define ID_MISSING(_id) ((((ID *)(_id))->tag & LIB_TAG_MISSING) != 0)
+#define ID_MISSING(_id) ((((const ID *)(_id))->tag & LIB_TAG_MISSING) != 0)
-#define ID_IS_LINKED(_id) (((ID *)(_id))->lib != NULL)
+#define ID_IS_LINKED(_id) (((const ID *)(_id))->lib != NULL)
#define LIB_IS_VIRTUAL(_lib) (((_lib)->flag & LIBRARY_FLAG_VIRTUAL) != 0)
#define ID_IS_LINKED_DATABLOCK(_id) (ID_IS_LINKED(_id) && !LIB_IS_VIRTUAL(((ID *)(_id))->lib))
#define ID_IS_LINKED_DATAPATH(_id) (ID_IS_LINKED(_id) && LIB_IS_VIRTUAL(((ID *)(_id))->lib))
@@ -579,10 +580,17 @@ typedef enum ID_Type {
/* Note that this is a fairly high-level check, should be used at user interaction level, not in
* BKE_library_override typically (especially due to the check on LIB_TAG_EXTERN). */
#define ID_IS_OVERRIDABLE_LIBRARY(_id) \
- (ID_IS_LINKED(_id) && !ID_MISSING(_id) && (((ID *)(_id))->tag & LIB_TAG_EXTERN) != 0)
+ (ID_IS_LINKED(_id) && !ID_MISSING(_id) && (((const ID *)(_id))->tag & LIB_TAG_EXTERN) != 0)
+
+#define ID_IS_OVERRIDE_LIBRARY_REAL(_id) \
+ (((const ID *)(_id))->override_library != NULL && \
+ ((const ID *)(_id))->override_library->reference != NULL)
+
+#define ID_IS_OVERRIDE_LIBRARY_VIRTUAL(_id) \
+ ((((const ID *)(_id))->flag & LIB_EMBEDDED_DATA_LIB_OVERRIDE) != 0)
#define ID_IS_OVERRIDE_LIBRARY(_id) \
- (((ID *)(_id))->override_library != NULL && ((ID *)(_id))->override_library->reference != NULL)
+ (ID_IS_OVERRIDE_LIBRARY_REAL(_id) || ID_IS_OVERRIDE_LIBRARY_VIRTUAL(_id))
#define ID_IS_OVERRIDE_LIBRARY_TEMPLATE(_id) \
(((ID *)(_id))->override_library != NULL && ((ID *)(_id))->override_library->reference == NULL)
@@ -624,6 +632,11 @@ enum {
* we want to restore if possible, and silently drop if it's missing.
*/
LIB_INDIRECT_WEAK_LINK = 1 << 11,
+ /**
+ * The data-block is a sub-data of another one, which is an override.
+ * Note that this also applies to shapekeys, even though they are not 100% embedded data...
+ */
+ LIB_EMBEDDED_DATA_LIB_OVERRIDE = 1 << 12,
};
/**
diff --git a/source/blender/makesdna/DNA_action_types.h b/source/blender/makesdna/DNA_action_types.h
index c95a701a78a..98e858dbf41 100644
--- a/source/blender/makesdna/DNA_action_types.h
+++ b/source/blender/makesdna/DNA_action_types.h
@@ -703,8 +703,9 @@ typedef struct bDopeSheet {
/** String to search for in displayed names of F-Curves, or NlaTracks/GP Layers/etc. */
char searchstr[64];
- /** Flags to use for filtering data. */
+ /** Flags to use for filtering data #eAnimFilter_Flags. */
int filterflag;
+ /** #eDopeSheet_FilterFlag2 */
int filterflag2;
/** Standard flags. */
int flag;
diff --git a/source/blender/makesdna/DNA_brush_defaults.h b/source/blender/makesdna/DNA_brush_defaults.h
index f315cc4b8a0..2ec4f4ee991 100644
--- a/source/blender/makesdna/DNA_brush_defaults.h
+++ b/source/blender/makesdna/DNA_brush_defaults.h
@@ -47,6 +47,7 @@
.crease_pinch_factor = 0.5f, \
.normal_radius_factor = 0.5f, \
.area_radius_factor = 0.5f, \
+ .disconnected_distance_max = 0.1f, \
.sculpt_plane = SCULPT_DISP_DIR_AREA, \
.cloth_damping = 0.01, \
.cloth_mass = 1, \
diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h
index 7490dbe5cdc..4056faf359f 100644
--- a/source/blender/makesdna/DNA_brush_types.h
+++ b/source/blender/makesdna/DNA_brush_types.h
@@ -439,6 +439,21 @@ typedef struct Brush {
float rgb[3];
/** Opacity. */
float alpha;
+ /** Hardness */
+ float hardness;
+ /** Flow */
+ float flow;
+ /** Wet Mix */
+ float wet_mix;
+ float wet_persistence;
+ /** Density */
+ float density;
+
+ /** Tip Shape */
+ /* Factor that controls the shape of the brush tip by rounding the corners of a square. */
+ /* 0.0 value produces a square, 1.0 produces a circle. */
+ float tip_roundness;
+ float tip_scale_x;
/** Background color. */
float secondary_rgb[3];
@@ -503,16 +518,14 @@ typedef struct Brush {
float texture_sample_bias;
int curve_preset;
- float hardness;
+
+ /* Maximun distance to search fake neighbors from a vertex. */
+ float disconnected_distance_max;
/* automasking */
int automasking_flags;
int automasking_boundary_edges_propagation_steps;
- /* Factor that controls the shape of the brush tip by rounding the corners of a square. */
- /* 0.0 value produces a square, 1.0 produces a circle. */
- float tip_roundness;
-
int elastic_deform_type;
float elastic_deform_volume_preservation;
@@ -670,6 +683,7 @@ typedef enum eBrushFlags2 {
BRUSH_MULTIPLANE_SCRAPE_DYNAMIC = (1 << 0),
BRUSH_MULTIPLANE_SCRAPE_PLANES_PREVIEW = (1 << 1),
BRUSH_POSE_IK_ANCHORED = (1 << 2),
+ BRUSH_USE_CONNECTED_ONLY = (1 << 3),
} eBrushFlags2;
typedef enum {
@@ -720,6 +734,8 @@ typedef enum eBrushSculptTool {
SCULPT_TOOL_CLAY_THUMB = 25,
SCULPT_TOOL_CLOTH = 26,
SCULPT_TOOL_DRAW_FACE_SETS = 27,
+ SCULPT_TOOL_PAINT = 28,
+ SCULPT_TOOL_SMEAR = 29,
} eBrushSculptTool;
/* Brush.uv_sculpt_tool */
@@ -762,6 +778,8 @@ typedef enum eBrushUVSculptTool {
SCULPT_TOOL_ELASTIC_DEFORM, \
SCULPT_TOOL_POSE, \
SCULPT_TOOL_DRAW_FACE_SETS, \
+ SCULPT_TOOL_PAINT, \
+ SCULPT_TOOL_SMEAR, \
\
/* These brushes could handle dynamic topology, \ \
* but user feedback indicates it's better not to */ \
diff --git a/source/blender/makesdna/DNA_constraint_types.h b/source/blender/makesdna/DNA_constraint_types.h
index 65087a6d459..85d9a04a902 100644
--- a/source/blender/makesdna/DNA_constraint_types.h
+++ b/source/blender/makesdna/DNA_constraint_types.h
@@ -61,7 +61,8 @@ typedef struct bConstraint {
/** Constraint name, MAX_NAME. */
char name[64];
- char _pad[2];
+ /* Flag for panel and subpanel closed / open state in the UI. */
+ short ui_expand_flag;
/** Amount of influence exherted by constraint (0.0-1.0). */
float enforce;
@@ -689,8 +690,8 @@ typedef enum eBConstraint_Types {
/* flag 0x20 (1 << 5) was used to indicate that a constraint was evaluated
* using a 'local' hack for posebones only. */
typedef enum eBConstraint_Flags {
- /* expand for UI */
- CONSTRAINT_EXPAND = (1 << 0),
+ /* Expansion for old box constraint layouts. Just for versioning. */
+ CONSTRAINT_EXPAND_DEPRECATED = (1 << 0),
/* pre-check for illegal object name or bone name */
CONSTRAINT_DISABLE = (1 << 2),
/* to indicate which Ipo should be shown, maybe for 3d access later too */
diff --git a/source/blender/makesdna/DNA_curveprofile_types.h b/source/blender/makesdna/DNA_curveprofile_types.h
index 63e1049b636..ca00f783905 100644
--- a/source/blender/makesdna/DNA_curveprofile_types.h
+++ b/source/blender/makesdna/DNA_curveprofile_types.h
@@ -43,13 +43,22 @@ typedef struct CurveProfilePoint {
float x, y;
/** Flag selection state and others. */
short flag;
- /** Flags for both handle's type (eBezTriple_Handle). */
+ /** Flags for both handle's type (eBezTriple_Handle auto, vect, free, and aligned supported). */
char h1, h2;
+ /** Handle locations, keep together.
+ * \note For now the two handle types are set to the same type in RNA. */
+ float h1_loc[2];
+ float h2_loc[2];
+ char _pad[4];
+ /** Runtime pointer to the point's profile for updating the curve with no direct reference. */
+ struct CurveProfile *profile;
} CurveProfilePoint;
/** #CurveProfilePoint.flag */
enum {
PROF_SELECT = (1 << 0),
+ PROF_H1_SELECT = (1 << 1),
+ PROF_H2_SELECT = (1 << 2),
};
/** Defines a profile. */
@@ -76,10 +85,11 @@ typedef struct CurveProfile {
/** #CurveProfile.flag */
enum {
- PROF_USE_CLIP = (1 << 0), /* Keep control points inside bounding rectangle. */
- /* PROF_SYMMETRY_MODE = (1 << 1), */ /* Unused for now. */
- PROF_SAMPLE_STRAIGHT_EDGES = (1 << 2), /* Sample extra points on straight edges. */
- PROF_SAMPLE_EVEN_LENGTHS = (1 << 3), /* Put segments evenly spaced along the path. */
+ PROF_USE_CLIP = (1 << 0), /* Keep control points inside bounding rectangle. */
+ /* PROF_SYMMETRY_MODE = (1 << 1), Unused for now. */
+ PROF_SAMPLE_STRAIGHT_EDGES = (1 << 2), /* Sample extra points on straight edges. */
+ PROF_SAMPLE_EVEN_LENGTHS = (1 << 3), /* Put segments evenly spaced along the path. */
+ PROF_DIRTY_PRESET = (1 << 4), /* Marks when the dynamic preset has been changed. */
};
typedef enum eCurveProfilePresets {
diff --git a/source/blender/makesdna/DNA_customdata_types.h b/source/blender/makesdna/DNA_customdata_types.h
index e4999fd4464..c24bbccae1e 100644
--- a/source/blender/makesdna/DNA_customdata_types.h
+++ b/source/blender/makesdna/DNA_customdata_types.h
@@ -148,10 +148,9 @@ typedef enum CustomDataType {
CD_CUSTOMLOOPNORMAL = 41,
CD_SCULPT_FACE_SETS = 42,
- /* Hair and PointCloud */
CD_LOCATION = 43,
- CD_RADIUS = 44,
CD_HAIRCURVE = 45,
+ CD_RADIUS = 44,
CD_HAIRMAPPING = 46,
CD_PROP_COLOR = 47,
@@ -205,7 +204,7 @@ typedef enum CustomDataType {
#define CD_MASK_TESSLOOPNORMAL (1LL << CD_TESSLOOPNORMAL)
#define CD_MASK_CUSTOMLOOPNORMAL (1LL << CD_CUSTOMLOOPNORMAL)
#define CD_MASK_SCULPT_FACE_SETS (1LL << CD_SCULPT_FACE_SETS)
-#define CD_MASK_PROP_COLOR (1LL << CD_PROP_COLOR)
+#define CD_MASK_PROP_COLOR (1ULL << CD_PROP_COLOR)
/** Data types that may be defined for all mesh elements types. */
#define CD_MASK_GENERIC_DATA (CD_MASK_PROP_FLOAT | CD_MASK_PROP_INT32 | CD_MASK_PROP_STRING)
diff --git a/source/blender/makesdna/DNA_fluid_types.h b/source/blender/makesdna/DNA_fluid_types.h
index e8a22d8c821..e3f4865e894 100644
--- a/source/blender/makesdna/DNA_fluid_types.h
+++ b/source/blender/makesdna/DNA_fluid_types.h
@@ -45,6 +45,7 @@ enum {
FLUID_DOMAIN_USE_FRACTIONS = (1 << 13), /* Use second order obstacles. */
FLUID_DOMAIN_DELETE_IN_OBSTACLE = (1 << 14), /* Delete fluid inside obstacles. */
FLUID_DOMAIN_USE_DIFFUSION = (1 << 15), /* Use diffusion (e.g. viscosity, surface tension). */
+ FLUID_DOMAIN_USE_RESUMABLE_CACHE = (1 << 16), /* Determine if cache should be resumable. */
};
/* Border collisions. */
@@ -214,6 +215,7 @@ enum {
#define FLUID_DOMAIN_DIR_SCRIPT "script"
#define FLUID_DOMAIN_SMOKE_SCRIPT "smoke_script.py"
#define FLUID_DOMAIN_LIQUID_SCRIPT "liquid_script.py"
+#define FLUID_CACHE_VERSION "C01"
/* Cache file names. */
#define FLUID_NAME_CONFIG "config"
@@ -224,24 +226,24 @@ enum {
#define FLUID_NAME_GUIDING "fluid_guiding"
/* Fluid object names.*/
-#define FLUID_NAME_FLAGS "flags"
-#define FLUID_NAME_VELOCITY "vel"
-#define FLUID_NAME_VELOCITYTMP "velocityTmp"
+#define FLUID_NAME_FLAGS "flags" /* == OpenVDB grid attribute name. */
+#define FLUID_NAME_VELOCITY "velocity" /* == OpenVDB grid attribute name. */
+#define FLUID_NAME_VELOCITYTMP "velocity_previous" /* == OpenVDB grid attribute name. */
#define FLUID_NAME_VELOCITYX "x_vel"
#define FLUID_NAME_VELOCITYY "y_vel"
#define FLUID_NAME_VELOCITYZ "z_vel"
#define FLUID_NAME_PRESSURE "pressure"
-#define FLUID_NAME_PHIOBS "phiObs"
+#define FLUID_NAME_PHIOBS "phi_obstacle" /* == OpenVDB grid attribute name. */
#define FLUID_NAME_PHISIN "phiSIn"
-#define FLUID_NAME_PHIIN "phiIn"
-#define FLUID_NAME_PHIOUT "phiOut"
+#define FLUID_NAME_PHIIN "phi_inflow" /* == OpenVDB grid attribute name. */
+#define FLUID_NAME_PHIOUT "phi_out" /* == OpenVDB grid attribute name. */
#define FLUID_NAME_FORCES "forces"
#define FLUID_NAME_FORCE_X "x_force"
#define FLUID_NAME_FORCE_Y "y_force"
#define FLUID_NAME_FORCE_Z "z_force"
#define FLUID_NAME_NUMOBS "numObs"
#define FLUID_NAME_PHIOBSSIN "phiObsSIn"
-#define FLUID_NAME_PHIOBSIN "phiObsIn"
+#define FLUID_NAME_PHIOBSIN "phi_obstacle_inflow"
#define FLUID_NAME_OBVEL "obvel"
#define FLUID_NAME_OBVELC "obvelC"
#define FLUID_NAME_OBVEL_X "x_obvel"
@@ -254,44 +256,48 @@ enum {
#define FLUID_NAME_INVEL_Y "y_invel"
#define FLUID_NAME_INVEL_Z "z_invel"
#define FLUID_NAME_PHIOUTSIN "phiOutSIn"
-#define FLUID_NAME_PHIOUTIN "phiOutIn"
+#define FLUID_NAME_PHIOUTIN "phi_out_inflow"
/* Smoke object names. */
-#define FLUID_NAME_SHADOW "shadow"
-#define FLUID_NAME_EMISSION "emission"
+#define FLUID_NAME_SHADOW "shadow" /* == OpenVDB grid attribute name. */
+#define FLUID_NAME_EMISSION "emission" /* == OpenVDB grid attribute name. */
#define FLUID_NAME_EMISSIONIN "emissionIn"
-#define FLUID_NAME_DENSITY "density"
-#define FLUID_NAME_DENSITYIN "densityIn"
+#define FLUID_NAME_DENSITY "density" /* == OpenVDB grid attribute name. */
+#define FLUID_NAME_DENSITYIN "density_inflow" /* == OpenVDB grid attribute name. */
#define FLUID_NAME_HEAT "heat"
#define FLUID_NAME_HEATIN "heatIn"
-#define FLUID_NAME_COLORR "color_r"
-#define FLUID_NAME_COLORG "color_g"
-#define FLUID_NAME_COLORB "color_b"
-#define FLUID_NAME_COLORRIN "color_r_in"
-#define FLUID_NAME_COLORGIN "color_g_in"
-#define FLUID_NAME_COLORBIN "color_b_in"
-#define FLUID_NAME_FLAME "flame"
-#define FLUID_NAME_FUEL "fuel"
-#define FLUID_NAME_REACT "react"
-#define FLUID_NAME_FUELIN "fuelIn"
-#define FLUID_NAME_REACTIN "reactIn"
+#define FLUID_NAME_TEMPERATURE "temperature" /* == OpenVDB grid attribute name. */
+#define FLUID_NAME_TEMPERATUREIN "temperature_inflow" /* == OpenVDB grid attribute name. */
+#define FLUID_NAME_COLORR "color_r" /* == OpenVDB grid attribute name. */
+#define FLUID_NAME_COLORG "color_g" /* == OpenVDB grid attribute name. */
+#define FLUID_NAME_COLORB "color_b" /* == OpenVDB grid attribute name. */
+#define FLUID_NAME_COLORRIN "color_r_inflow" /* == OpenVDB grid attribute name. */
+#define FLUID_NAME_COLORGIN "color_g_inflow" /* == OpenVDB grid attribute name. */
+#define FLUID_NAME_COLORBIN "color_b_inflow" /* == OpenVDB grid attribute name. */
+#define FLUID_NAME_FLAME "flame" /* == OpenVDB grid attribute name. */
+#define FLUID_NAME_FUEL "fuel" /* == OpenVDB grid attribute name. */
+#define FLUID_NAME_REACT "react" /* == OpenVDB grid attribute name. */
+#define FLUID_NAME_FUELIN "fuel_inflow" /* == OpenVDB grid attribute name. */
+#define FLUID_NAME_REACTIN "react_inflow" /* == OpenVDB grid attribute name. */
/* Liquid object names. */
-#define FLUID_NAME_PHIPARTS "phiParts"
-#define FLUID_NAME_PHI "phi"
-#define FLUID_NAME_PHITMP "phiTmp"
+#define FLUID_NAME_PHIPARTS "phi_particles" /* == OpenVDB grid attribute name. */
+#define FLUID_NAME_PHI "phi" /* == OpenVDB grid attribute name. */
+#define FLUID_NAME_PHITMP "phi_previous" /* == OpenVDB grid attribute name. */
#define FLUID_NAME_VELOCITYOLD "velOld"
#define FLUID_NAME_VELOCITYPARTS "velParts"
#define FLUID_NAME_MAPWEIGHTS "mapWeights"
#define FLUID_NAME_PP "pp"
#define FLUID_NAME_PVEL "pVel"
+#define FLUID_NAME_PARTS "particles" /* == OpenVDB grid attribute name. */
+#define FLUID_NAME_PARTSVELOCITY "particles_velocity" /* == OpenVDB grid attribute name. */
#define FLUID_NAME_PINDEX "pindex"
#define FLUID_NAME_GPI "gpi"
#define FLUID_NAME_CURVATURE "gpi"
/* Noise object names. */
#define FLUID_NAME_VELOCITY_NOISE "velocity_noise"
-#define FLUID_NAME_DENSITY_NOISE "density_noise"
+#define FLUID_NAME_DENSITY_NOISE "density_noise" /* == OpenVDB grid attribute name. */
#define FLUID_NAME_PHIIN_NOISE "phiIn_noise"
#define FLUID_NAME_PHIOUT_NOISE "phiOut_noise"
#define FLUID_NAME_PHIOBS_NOISE "phiObs_noise"
@@ -306,11 +312,11 @@ enum {
#define FLUID_NAME_TEXTURE_U2 "textureU2"
#define FLUID_NAME_TEXTURE_V2 "textureV2"
#define FLUID_NAME_TEXTURE_W2 "textureW2"
-#define FLUID_NAME_UV0 "uvGrid0"
-#define FLUID_NAME_UV1 "uvGrid1"
-#define FLUID_NAME_COLORR_NOISE "color_r_noise"
-#define FLUID_NAME_COLORG_NOISE "color_g_noise"
-#define FLUID_NAME_COLORB_NOISE "color_b_noise"
+#define FLUID_NAME_UV0 "uv_grid_0" /* == OpenVDB grid attribute name. */
+#define FLUID_NAME_UV1 "uv_grid_1" /* == OpenVDB grid attribute name. */
+#define FLUID_NAME_COLORR_NOISE "color_r_noise" /* == OpenVDB grid attribute name. */
+#define FLUID_NAME_COLORG_NOISE "color_g_noise" /* == OpenVDB grid attribute name. */
+#define FLUID_NAME_COLORB_NOISE "color_b_noise" /* == OpenVDB grid attribute name. */
#define FLUID_NAME_FLAME_NOISE "flame_noise"
#define FLUID_NAME_FUEL_NOISE "fuel_noise"
#define FLUID_NAME_REACT_NOISE "react_noise"
@@ -321,7 +327,8 @@ enum {
#define FLUID_NAME_PP_MESH "pp_mesh"
#define FLUID_NAME_FLAGS_MESH "flags_mesh"
#define FLUID_NAME_LMESH "lMesh"
-#define FLUID_NAME_VELOCITYVEC_MESH "lVelMesh"
+/* == OpenVDB grid attribute name. */
+#define FLUID_NAME_VELOCITYVEC_MESH "vertex_velocities_mesh"
#define FLUID_NAME_VELOCITY_MESH "velocity_mesh"
#define FLUID_NAME_PINDEX_MESH "pindex_mesh"
#define FLUID_NAME_GPI_MESH "gpi_mesh"
@@ -329,18 +336,28 @@ enum {
/* Particles object names. */
#define FLUID_NAME_PP_PARTICLES "ppSnd"
#define FLUID_NAME_PVEL_PARTICLES "pVelSnd"
-#define FLUID_NAME_PFORCE_PARTICLES "pForceSnd"
#define FLUID_NAME_PLIFE_PARTICLES "pLifeSnd"
-#define FLUID_NAME_VELOCITY_PARTICLES "velocity_particles"
-#define FLUID_NAME_FLAGS_PARTICLES "flags_particles"
-#define FLUID_NAME_PHI_PARTICLES "phi_particles"
-#define FLUID_NAME_PHIOBS_PARTICLES "phiObs_particles"
-#define FLUID_NAME_PHIOUT_PARTICLES "phiOut_particles"
-#define FLUID_NAME_NORMAL_PARTICLES "normal_particles"
-#define FLUID_NAME_NEIGHBORRATIO_PARTICLES "neighborRatio_particles"
-#define FLUID_NAME_TRAPPEDAIR_PARTICLES "trappedAir_particles"
-#define FLUID_NAME_WAVECREST_PARTICLES "waveCrest_particles"
-#define FLUID_NAME_KINETICENERGY_PARTICLES "kineticEnergy_particles"
+#define FLUID_NAME_PFORCE_PARTICLES "pForceSnd"
+/* == OpenVDB grid attribute name. */
+#define FLUID_NAME_PARTS_PARTICLES "particles_secondary"
+/* == OpenVDB grid attribute name. */
+#define FLUID_NAME_PARTSVEL_PARTICLES "particles_velocity_secondary"
+/* == OpenVDB grid attribute name. */
+#define FLUID_NAME_PARTSLIFE_PARTICLES "particles_life_secondary"
+#define FLUID_NAME_PARTSFORCE_PARTICLES "particles_force_secondary"
+#define FLUID_NAME_VELOCITY_PARTICLES "velocity_secondary"
+#define FLUID_NAME_FLAGS_PARTICLES "flags_secondary"
+#define FLUID_NAME_PHI_PARTICLES "phi_secondary"
+#define FLUID_NAME_PHIOBS_PARTICLES "phiObs_secondary"
+#define FLUID_NAME_PHIOUT_PARTICLES "phiOut_secondary"
+#define FLUID_NAME_NORMAL_PARTICLES "normal_secondary"
+#define FLUID_NAME_NEIGHBORRATIO_PARTICLES "neighbor_ratio_secondary"
+/* == OpenVDB grid attribute name. */
+#define FLUID_NAME_TRAPPEDAIR_PARTICLES "trapped_air_secondary"
+/* == OpenVDB grid attribute name. */
+#define FLUID_NAME_WAVECREST_PARTICLES "wave_crest_secondary"
+/* == OpenVDB grid attribute name. */
+#define FLUID_NAME_KINETICENERGY_PARTICLES "kinetic_energy_secondary"
/* Guiding object names. */
#define FLUID_NAME_VELT "velT"
@@ -349,9 +366,9 @@ enum {
#define FLUID_NAME_PHIGUIDEIN "phiGuideIn"
#define FLUID_NAME_GUIDEVELC "guidevelC"
#define FLUID_NAME_GUIDEVEL_X "x_guidevel"
-#define FLUID_NAME_GUIDEVEL_Y "Y_guidevel"
+#define FLUID_NAME_GUIDEVEL_Y "y_guidevel"
#define FLUID_NAME_GUIDEVEL_Z "z_guidevel"
-#define FLUID_NAME_GUIDEVEL "guidevel"
+#define FLUID_NAME_GUIDEVEL "velocity_guide"
/* Cache file extensions. */
#define FLUID_DOMAIN_EXTENSION_UNI ".uni"
@@ -374,7 +391,18 @@ enum {
enum {
FLUID_DOMAIN_CACHE_REPLAY = 0,
FLUID_DOMAIN_CACHE_MODULAR = 1,
- FLUID_DOMAIN_CACHE_FINAL = 2,
+ FLUID_DOMAIN_CACHE_ALL = 2,
+};
+
+enum {
+ VDB_COMPRESSION_BLOSC = 0,
+ VDB_COMPRESSION_ZIP = 1,
+ VDB_COMPRESSION_NONE = 2,
+};
+
+enum {
+ VDB_PRECISION_HALF_FLOAT = 0,
+ VDB_PRECISION_FULL_FLOAT = 1,
};
/* Deprecated values (i.e. all defines and enums below this line up until typedefs). */
@@ -391,12 +419,6 @@ enum {
SM_HRES_FULLSAMPLE = 2,
};
-enum {
- VDB_COMPRESSION_BLOSC = 0,
- VDB_COMPRESSION_ZIP = 1,
- VDB_COMPRESSION_NONE = 2,
-};
-
typedef struct FluidDomainVertexVelocity {
float vel[3];
} FluidDomainVertexVelocity;
@@ -566,7 +588,8 @@ typedef struct FluidDomainSettings {
char cache_directory[1024];
char error[64]; /* Bake error description. */
short cache_type;
- char _pad8[2]; /* Unused. */
+ char cache_id[4]; /* Run-time only */
+ char _pad8[6];
/* Time options. */
float dt;
@@ -591,17 +614,17 @@ typedef struct FluidDomainSettings {
char coba_field; /* Simulation field used for the color mapping. */
char interp_method;
+ /* OpenVDB cache options. */
+ int openvdb_compression;
+ float clipping;
+ char openvdb_data_depth;
+ char _pad9[7]; /* Unused. */
+
/* -- Deprecated / unsed options (below). -- */
/* View options. */
int viewsettings;
- char _pad9[4]; /* Unused. */
-
- /* OpenVDB cache options. */
- int openvdb_comp;
- float clipping;
- char data_depth;
- char _pad10[7]; /* Unused. */
+ char _pad10[4]; /* Unused. */
/* Pointcache options. */
/* Smoke uses only one cache from now on (index [0]), but keeping the array for now for reading
diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h
index 674999ab465..5d35db1a960 100644
--- a/source/blender/makesdna/DNA_gpencil_modifier_types.h
+++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h
@@ -58,7 +58,7 @@ typedef enum GpencilModifierMode {
eGpencilModifierMode_Realtime = (1 << 0),
eGpencilModifierMode_Render = (1 << 1),
eGpencilModifierMode_Editmode = (1 << 2),
- eGpencilModifierMode_Expanded = (1 << 3),
+ eGpencilModifierMode_Expanded_DEPRECATED = (1 << 3),
} GpencilModifierMode;
typedef enum {
@@ -72,7 +72,7 @@ typedef struct GpencilModifierData {
int type, mode;
int stackindex;
short flag;
- short _pad;
+ short ui_expand_flag;
/** MAX_NAME. */
char name[64];
diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h
index c3180ae79db..b6e2910a1b0 100644
--- a/source/blender/makesdna/DNA_gpencil_types.h
+++ b/source/blender/makesdna/DNA_gpencil_types.h
@@ -735,10 +735,10 @@ typedef enum eGP_DrawMode {
#define GPENCIL_ANY_EDIT_MODE(gpd) \
((gpd) && ((gpd)->flag & \
(GP_DATA_STROKE_EDITMODE | GP_DATA_STROKE_SCULPTMODE | GP_DATA_STROKE_WEIGHTMODE)))
-#define GPENCIL_PAINT_MODE(gpd) ((gpd) && (gpd->flag & (GP_DATA_STROKE_PAINTMODE)))
+#define GPENCIL_PAINT_MODE(gpd) ((gpd) && (gpd->flag & GP_DATA_STROKE_PAINTMODE))
#define GPENCIL_SCULPT_MODE(gpd) ((gpd) && (gpd->flag & GP_DATA_STROKE_SCULPTMODE))
#define GPENCIL_WEIGHT_MODE(gpd) ((gpd) && (gpd->flag & GP_DATA_STROKE_WEIGHTMODE))
-#define GPENCIL_VERTEX_MODE(gpd) ((gpd) && (gpd->flag & (GP_DATA_STROKE_VERTEXMODE)))
+#define GPENCIL_VERTEX_MODE(gpd) ((gpd) && (gpd->flag & GP_DATA_STROKE_VERTEXMODE))
#define GPENCIL_SCULPT_OR_WEIGHT_MODE(gpd) \
((gpd) && ((gpd)->flag & (GP_DATA_STROKE_SCULPTMODE | GP_DATA_STROKE_WEIGHTMODE)))
#define GPENCIL_NONE_EDIT_MODE(gpd) \
diff --git a/source/blender/makesdna/DNA_image_types.h b/source/blender/makesdna/DNA_image_types.h
index 11a6a937e92..0ffb6c8a76a 100644
--- a/source/blender/makesdna/DNA_image_types.h
+++ b/source/blender/makesdna/DNA_image_types.h
@@ -129,7 +129,7 @@ typedef struct Image {
ID id;
/** File path, 1024 = FILE_MAX. */
- char name[1024];
+ char filepath[1024];
/** Not written in file. */
struct MovieCache *cache;
diff --git a/source/blender/makesdna/DNA_key_types.h b/source/blender/makesdna/DNA_key_types.h
index 3685290e571..a1cc6f89314 100644
--- a/source/blender/makesdna/DNA_key_types.h
+++ b/source/blender/makesdna/DNA_key_types.h
@@ -113,7 +113,7 @@ typedef struct Key {
/**
* Can never be 0, this is used for detecting old data.
- * current free uid for keyblocks
+ * current free UID for key-blocks.
*/
int uidgen;
} Key;
diff --git a/source/blender/makesdna/DNA_material_types.h b/source/blender/makesdna/DNA_material_types.h
index 357c3260121..6a4ec65318b 100644
--- a/source/blender/makesdna/DNA_material_types.h
+++ b/source/blender/makesdna/DNA_material_types.h
@@ -112,7 +112,7 @@ typedef enum eMaterialGPencilStyle_Flag {
/* protected from further editing */
GP_MATERIAL_LOCKED = (1 << 2),
/* do onion skinning */
- GP_MATERIAL_ONIONSKIN = (1 << 3),
+ GP_MATERIAL_HIDE_ONIONSKIN = (1 << 3),
/* clamp texture */
GP_MATERIAL_TEX_CLAMP = (1 << 4),
/* mix fill texture */
diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h
index acc020ec710..9435cb3bd78 100644
--- a/source/blender/makesdna/DNA_mesh_types.h
+++ b/source/blender/makesdna/DNA_mesh_types.h
@@ -46,6 +46,7 @@ struct MLoopTri;
struct MLoopUV;
struct MPoly;
struct MVert;
+struct MPropCol;
struct Material;
struct Mesh;
struct Multires;
@@ -244,9 +245,9 @@ typedef struct TFace {
/** #Mesh_Runtime.wrapper_type */
typedef enum eMeshWrapperType {
- /** Use mesh data (#Mesh.mvert,#Mesh.medge, #Mesh.mloop, #Mesh.mpoly). */
+ /** Use mesh data (#Mesh.mvert, #Mesh.medge, #Mesh.mloop, #Mesh.mpoly). */
ME_WRAPPER_TYPE_MDATA = 0,
- /** Use edit-mesh data (#Mesh.#edit_mesh, #Mesh_Runtime.edit_data). */
+ /** Use edit-mesh data (#Mesh.edit_mesh, #Mesh_Runtime.edit_data). */
ME_WRAPPER_TYPE_BMESH = 1,
/* ME_WRAPPER_TYPE_SUBD = 2, */ /* TODO */
} eMeshWrapperType;
@@ -285,7 +286,7 @@ enum {
ME_AUTOSMOOTH = 1 << 5,
ME_FLAG_UNUSED_6 = 1 << 6, /* cleared */
ME_FLAG_UNUSED_7 = 1 << 7, /* cleared */
- ME_FLAG_UNUSED_8 = 1 << 8, /* cleared */
+ ME_REMESH_REPROJECT_VERTEX_COLORS = 1 << 8,
ME_DS_EXPAND = 1 << 9,
ME_SCULPT_DYNAMIC_TOPOLOGY = 1 << 10,
ME_REMESH_SMOOTH_NORMALS = 1 << 11,
diff --git a/source/blender/makesdna/DNA_meshdata_types.h b/source/blender/makesdna/DNA_meshdata_types.h
index 04deecde43e..cc2ba3fb999 100644
--- a/source/blender/makesdna/DNA_meshdata_types.h
+++ b/source/blender/makesdna/DNA_meshdata_types.h
@@ -345,7 +345,7 @@ typedef struct MLoopCol {
} MLoopCol;
typedef struct MPropCol {
- float col[4];
+ float color[4];
} MPropCol;
/** Multi-Resolution loop data. */
diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h
index 1413db4baea..05daf6caa47 100644
--- a/source/blender/makesdna/DNA_modifier_types.h
+++ b/source/blender/makesdna/DNA_modifier_types.h
@@ -394,6 +394,8 @@ typedef struct BevelModifierData {
short flags;
/** Used to interpret the bevel value. */
short val_flags;
+ /** For the type and how we build the bevel's profile. */
+ short profile_type;
/** Flags to tell the tool how to limit the bevel. */
short lim_flags;
/** Flags to direct how edge weights are applied to verts. */
@@ -407,6 +409,7 @@ typedef struct BevelModifierData {
short miter_outer;
/** The method to use for creating >2-way intersections */
short vmesh_method;
+ char _pad0[2];
/** Controls profile shape (0->1, .5 is round). */
float profile;
/** if the MOD_BEVEL_ANGLE is set,
@@ -417,6 +420,7 @@ typedef struct BevelModifierData {
* this will be the name of the vert group, MAX_VGROUP_NAME */
char defgrp_name[64];
+ char _pad1[4];
/** Curve info for the custom profile */
struct CurveProfile *custom_profile;
@@ -429,12 +433,15 @@ enum {
MOD_BEVEL_ANGLE = (1 << 3),
MOD_BEVEL_WEIGHT = (1 << 4),
MOD_BEVEL_VGROUP = (1 << 5),
- MOD_BEVEL_CUSTOM_PROFILE = (1 << 7),
- /* MOD_BEVEL_SAMPLE_STRAIGHT = (1 << 8), */ /* UNUSED */
- /* unused = (1 << 9), */
- /* unused = (1 << 10), */
- /* unused = (1 << 11), */
- /* unused = (1 << 12), */
+/* unused = (1 << 6), */
+#ifdef DNA_DEPRECATED_ALLOW
+ MOD_BEVEL_CUSTOM_PROFILE_DEPRECATED = (1 << 7),
+#endif
+ /* unused = (1 << 8), */
+ /* unused = (1 << 9), */
+ /* unused = (1 << 10), */
+ /* unused = (1 << 11), */
+ /* unused = (1 << 12), */
MOD_BEVEL_OVERLAP_OK = (1 << 13),
MOD_BEVEL_EVEN_WIDTHS = (1 << 14),
MOD_BEVEL_HARDEN_NORMALS = (1 << 15),
@@ -446,6 +453,13 @@ enum {
MOD_BEVEL_AMT_WIDTH = 1,
MOD_BEVEL_AMT_DEPTH = 2,
MOD_BEVEL_AMT_PERCENT = 3,
+ MOD_BEVEL_AMT_ABSOLUTE = 4,
+};
+
+/* BevelModifierData->profile_type */
+enum {
+ MOD_BEVEL_PROFILE_SUPERELLIPSE = 0,
+ MOD_BEVEL_PROFILE_CUSTOM = 1,
};
/* BevelModifierData->edge_flags */
diff --git a/source/blender/makesdna/DNA_movieclip_types.h b/source/blender/makesdna/DNA_movieclip_types.h
index 94d11095108..d750a7f3148 100644
--- a/source/blender/makesdna/DNA_movieclip_types.h
+++ b/source/blender/makesdna/DNA_movieclip_types.h
@@ -78,7 +78,7 @@ typedef struct MovieClip {
struct AnimData *adt;
/** File path, 1024 = FILE_MAX. */
- char name[1024];
+ char filepath[1024];
/** Sequence or movie. */
int source;
diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h
index 4ff0e531168..993aad92564 100644
--- a/source/blender/makesdna/DNA_node_types.h
+++ b/source/blender/makesdna/DNA_node_types.h
@@ -844,6 +844,15 @@ typedef struct NodeTexSky {
float sun_direction[3];
float turbidity;
float ground_albedo;
+ float sun_size;
+ float sun_elevation;
+ float sun_rotation;
+ int altitude;
+ float air_density;
+ float dust_density;
+ float ozone_density;
+ char sun_disc;
+ char _pad[3];
} NodeTexSky;
typedef struct NodeTexImage {
@@ -1171,8 +1180,9 @@ enum {
};
/* sky texture */
-#define SHD_SKY_OLD 0
-#define SHD_SKY_NEW 1
+#define SHD_SKY_PREETHAM 0
+#define SHD_SKY_HOSEK 1
+#define SHD_SKY_NISHITA 2
/* environment texture */
#define SHD_PROJ_EQUIRECTANGULAR 0
diff --git a/source/blender/makesdna/DNA_object_force_types.h b/source/blender/makesdna/DNA_object_force_types.h
index 7f022f104e6..78f645deaa2 100644
--- a/source/blender/makesdna/DNA_object_force_types.h
+++ b/source/blender/makesdna/DNA_object_force_types.h
@@ -92,6 +92,10 @@ typedef struct PartDeflect {
* How much force is converted into "air flow", i.e.
* force used as the velocity of surrounding medium. */
float f_flow;
+ /** How much force is reduced when acting parallel to a surface, e.g. cloth. */
+ float f_wind_factor;
+
+ char _pad0[4];
/** Noise size for noise effector, restlength for harmonic effector. */
float f_size;
diff --git a/source/blender/makesdna/DNA_outliner_types.h b/source/blender/makesdna/DNA_outliner_types.h
index 9776063f220..d8a7599e4cb 100644
--- a/source/blender/makesdna/DNA_outliner_types.h
+++ b/source/blender/makesdna/DNA_outliner_types.h
@@ -62,8 +62,7 @@ enum {
TSE_DRAG_AFTER = (1 << 8),
/* Needed because outliner-only elements can be active */
TSE_ACTIVE = (1 << 9),
- /* Needed because walk selection should not activate */
- TSE_ACTIVE_WALK = (1 << 10),
+ /* TSE_ACTIVE_WALK = (1 << 10), */ /* Unused */
TSE_DRAG_ANY = (TSE_DRAG_INTO | TSE_DRAG_BEFORE | TSE_DRAG_AFTER),
};
diff --git a/source/blender/makesdna/DNA_scene_defaults.h b/source/blender/makesdna/DNA_scene_defaults.h
index 4b6f079aa28..7f01e58f2af 100644
--- a/source/blender/makesdna/DNA_scene_defaults.h
+++ b/source/blender/makesdna/DNA_scene_defaults.h
@@ -218,8 +218,10 @@
.bloom_radius = 6.5f, \
.bloom_clamp = 0.0f, \
\
- .motion_blur_samples = 8, \
.motion_blur_shutter = 0.5f, \
+ .motion_blur_depth_scale = 100.0f, \
+ .motion_blur_max = 32, \
+ .motion_blur_steps = 1, \
\
.shadow_cube_size = 512, \
.shadow_cascade_size = 1024, \
@@ -325,7 +327,7 @@
.doublimit = 0.001, \
.vgroup_weight = 1.0f, \
.uvcalc_margin = 0.001f, \
- .uvcalc_flag = UVCALC_TRANSFORM_CORRECT, \
+ .uvcalc_flag = UVCALC_TRANSFORM_CORRECT_SLIDE, \
.unwrapper = 1, \
.select_thresh = 0.01f, \
\
diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h
index c50e48982b3..dd62ddb640f 100644
--- a/source/blender/makesdna/DNA_scene_types.h
+++ b/source/blender/makesdna/DNA_scene_types.h
@@ -1590,7 +1590,6 @@ typedef struct SceneEEVEE {
float gi_irradiance_smoothing;
float gi_glossy_clamp;
float gi_filter_quality;
- char _pad[4];
float gi_cubemap_draw_size;
float gi_irradiance_draw_size;
@@ -1628,8 +1627,11 @@ typedef struct SceneEEVEE {
float bloom_radius;
float bloom_clamp;
- int motion_blur_samples;
+ int motion_blur_samples DNA_DEPRECATED;
+ int motion_blur_max;
+ int motion_blur_steps;
float motion_blur_shutter;
+ float motion_blur_depth_scale;
int shadow_method DNA_DEPRECATED;
int shadow_cube_size;
@@ -2172,7 +2174,7 @@ typedef enum eSculptFlags {
SCULPT_FLAG_UNUSED_6 = (1 << 6), /* cleared */
- SCULPT_USE_OPENMP = (1 << 7),
+ SCULPT_FLAG_UNUSED_7 = (1 << 7), /* cleared */
SCULPT_ONLY_DEFORM = (1 << 8),
// SCULPT_SHOW_DIFFUSE = (1 << 9), // deprecated
@@ -2233,10 +2235,12 @@ enum {
#define UVCALC_FILLHOLES (1 << 0)
/** would call this UVCALC_ASPECT_CORRECT, except it should be default with old file */
#define UVCALC_NO_ASPECT_CORRECT (1 << 1)
-/** adjust UV's while transforming to avoid distortion */
-#define UVCALC_TRANSFORM_CORRECT (1 << 2)
+/** Adjust UV's while transforming with Vert or Edge Slide. */
+#define UVCALC_TRANSFORM_CORRECT_SLIDE (1 << 2)
/** Use mesh data after subsurf to compute UVs*/
#define UVCALC_USESUBSURF (1 << 3)
+/** adjust UV's while transforming to avoid distortion */
+#define UVCALC_TRANSFORM_CORRECT (1 << 4)
/* ToolSettings.uv_flag */
#define UV_SYNC_SELECTION 1
diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h
index 07cba2bad1c..f9fcbdab55d 100644
--- a/source/blender/makesdna/DNA_screen_types.h
+++ b/source/blender/makesdna/DNA_screen_types.h
@@ -41,6 +41,7 @@ struct uiLayout;
struct wmDrawBuffer;
struct wmTimer;
struct wmTooltipState;
+struct PointerRNA;
/* TODO Doing this is quite ugly :)
* Once the top-bar is merged bScreen should be refactored to use ScrAreaMap. */
@@ -135,6 +136,15 @@ typedef struct Panel_Runtime {
/* For instanced panels: Index of the list item the panel corresponds to. */
int list_index;
+
+ /**
+ * Pointer for storing which data the panel corresponds to.
+ * Useful when there can be multiple instances of the same panel type.
+ *
+ * \note A panel and its sub-panels share the same custom data pointer.
+ * This avoids freeing the same pointer twice when panels are removed.
+ */
+ struct PointerRNA *custom_data_ptr;
} Panel_Runtime;
/** The part from uiBlock that needs saved in file. */
@@ -427,7 +437,7 @@ typedef struct ARegion {
/** Private, cached notifier events. */
short do_draw;
/** Private, cached notifier events. */
- short do_draw_overlay;
+ short do_draw_paintcursor;
/** Private, set for indicate drawing overlapped. */
short overlap;
/** Temporary copy of flag settings for clean fullscreen. */
@@ -609,7 +619,7 @@ enum {
/* regiontype, first two are the default set */
/* Do NOT change order, append on end. Types are hardcoded needed */
-enum {
+typedef enum eRegionType {
RGN_TYPE_WINDOW = 0,
RGN_TYPE_HEADER = 1,
RGN_TYPE_CHANNELS = 2,
@@ -625,7 +635,7 @@ enum {
RGN_TYPE_EXECUTE = 10,
RGN_TYPE_FOOTER = 11,
RGN_TYPE_TOOL_HEADER = 12,
-};
+} eRegionType;
/* use for function args */
#define RGN_TYPE_ANY -1
diff --git a/source/blender/makesdna/DNA_shader_fx_types.h b/source/blender/makesdna/DNA_shader_fx_types.h
index 18a4e8655a3..f19181bf07d 100644
--- a/source/blender/makesdna/DNA_shader_fx_types.h
+++ b/source/blender/makesdna/DNA_shader_fx_types.h
@@ -50,7 +50,7 @@ typedef enum ShaderFxMode {
eShaderFxMode_Realtime = (1 << 0),
eShaderFxMode_Render = (1 << 1),
eShaderFxMode_Editmode = (1 << 2),
- eShaderFxMode_Expanded = (1 << 3),
+ eShaderFxMode_Expanded_DEPRECATED = (1 << 3),
} ShaderFxMode;
typedef enum {
@@ -64,7 +64,8 @@ typedef struct ShaderFxData {
int type, mode;
int stackindex;
short flag;
- char _pad[2];
+ /* Expansion for shader effect panels and sub-panels. */
+ short ui_expand_flag;
/** MAX_NAME. */
char name[64];
@@ -171,6 +172,10 @@ typedef struct PixelShaderFxData {
ShaderFxData_Runtime runtime;
} PixelShaderFxData;
+typedef enum ePixelShaderFx_Flag {
+ FX_PIXEL_FILTER_NEAREST = (1 << 0),
+} ePixelShaderFx_Flag;
+
typedef struct RimShaderFxData {
ShaderFxData shaderfx;
int offset[2];
diff --git a/source/blender/makesdna/DNA_sound_types.h b/source/blender/makesdna/DNA_sound_types.h
index 04ac9d4e5be..35ff3a658ba 100644
--- a/source/blender/makesdna/DNA_sound_types.h
+++ b/source/blender/makesdna/DNA_sound_types.h
@@ -36,7 +36,7 @@ typedef struct bSound {
* The path to the sound file.
*/
/** 1024 = FILE_MAX. */
- char name[1024];
+ char filepath[1024];
/**
* The packed file.
diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h
index 4c329117a13..812ac4b4ccd 100644
--- a/source/blender/makesdna/DNA_space_types.h
+++ b/source/blender/makesdna/DNA_space_types.h
@@ -1205,7 +1205,11 @@ typedef struct SpaceText {
struct Text *text;
- int top, left;
+ /** Determines at what line the top of the text is displayed. */
+ int top;
+
+ /** Determines the horizontal scroll (in columns). */
+ int left;
char _pad1[4];
short flags;
diff --git a/source/blender/makesdna/DNA_text_types.h b/source/blender/makesdna/DNA_text_types.h
index dbd5def74d4..27663ffcbdd 100644
--- a/source/blender/makesdna/DNA_text_types.h
+++ b/source/blender/makesdna/DNA_text_types.h
@@ -43,10 +43,21 @@ typedef struct TextLine {
typedef struct Text {
ID id;
- char *name;
+ /**
+ * Optional file path, when NULL text is considered internal.
+ * Otherwise this path will be used when saving/reloading.
+ *
+ * When set this is where the file will or has been saved.
+ */
+ char *filepath;
+
+ /**
+ * Python code object for this text (cached result of #Py_CompileStringObject).
+ */
void *compiled;
- int flags, nlines;
+ int flags;
+ char _pad0[4];
ListBase lines;
TextLine *curl, *sell;
@@ -57,12 +68,15 @@ typedef struct Text {
#define TXT_TABSIZE 4
-/* text flags */
+/** #Text.flags */
enum {
+ /** Set if the file in run-time differs from the file on disk, or if there is no file on disk. */
TXT_ISDIRTY = 1 << 0,
+ /** When the text hasn't been written to a file. #Text.filepath may be NULL or invalid. */
TXT_ISMEM = 1 << 2,
+ /** Should always be set if the Text is not to be written into the `.blend`. */
TXT_ISEXT = 1 << 3,
- /** Used by space handler scriptlinks. */
+ /** Load the script as a Python module when loading the `.blend` file. */
TXT_ISSCRIPT = 1 << 4,
TXT_FLAG_UNUSED_8 = 1 << 8, /* cleared */
diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h
index 63e7a90547e..d751ad9ac47 100644
--- a/source/blender/makesdna/DNA_userdef_types.h
+++ b/source/blender/makesdna/DNA_userdef_types.h
@@ -619,8 +619,11 @@ typedef struct UserDef_FileSpaceData {
typedef struct UserDef_Experimental {
char use_undo_legacy;
+ char use_new_particle_system;
+ char use_new_hair_type;
+ char use_cycles_debug;
/** `makesdna` does not allow empty structs. */
- char _pad0[7];
+ char _pad0[4];
} UserDef_Experimental;
#define USER_EXPERIMENTAL_TEST(userdef, member) \
@@ -633,12 +636,12 @@ typedef struct UserDef {
/** #eUserPref_Flag. */
int flag;
/** #eDupli_ID_Flags. */
- short dupflag;
+ unsigned int dupflag;
/** #eUserPref_PrefFlag preferences for the preferences. */
char pref_flag;
char savetime;
char mouse_emulate_3_button_modifier;
- char _pad4[3];
+ char _pad4[1];
/** FILE_MAXDIR length. */
char tempdir[768];
char fontdir[768];
@@ -1150,6 +1153,13 @@ typedef enum eDupli_ID_Flags {
USER_DUP_HAIR = (1 << 14),
USER_DUP_POINTCLOUD = (1 << 15),
USER_DUP_VOLUME = (1 << 16),
+
+ USER_DUP_OBDATA = (~0) & ((1 << 24) - 1),
+
+ /* Those are not exposed as user preferences, only used internaly. */
+ USER_DUP_OBJECT = (1 << 24),
+ /* USER_DUP_COLLECTION = (1 << 25), */ /* UNUSED, keep because we may implement. */
+
} eDupli_ID_Flags;
/**
diff --git a/source/blender/makesdna/DNA_vfont_types.h b/source/blender/makesdna/DNA_vfont_types.h
index 295552635fe..086fb2bc905 100644
--- a/source/blender/makesdna/DNA_vfont_types.h
+++ b/source/blender/makesdna/DNA_vfont_types.h
@@ -36,7 +36,7 @@ typedef struct VFont {
ID id;
/** 1024 = FILE_MAX. */
- char name[1024];
+ char filepath[1024];
struct VFontData *data;
struct PackedFile *packedfile;
diff --git a/source/blender/makesdna/intern/CMakeLists.txt b/source/blender/makesdna/intern/CMakeLists.txt
index 01e3971a216..0f2761e311e 100644
--- a/source/blender/makesdna/intern/CMakeLists.txt
+++ b/source/blender/makesdna/intern/CMakeLists.txt
@@ -96,6 +96,7 @@ set(SRC
${CMAKE_CURRENT_BINARY_DIR}/dna_verify.c
${SRC_DNA_INC}
+ ${CMAKE_CURRENT_BINARY_DIR}/dna_type_offsets.h
dna_rename_defs.h
dna_utils.h
)
diff --git a/source/blender/makesdna/intern/dna_genfile.c b/source/blender/makesdna/intern/dna_genfile.c
index cf7c201795e..3d95eba4aed 100644
--- a/source/blender/makesdna/intern/dna_genfile.c
+++ b/source/blender/makesdna/intern/dna_genfile.c
@@ -963,11 +963,10 @@ static int elem_strcmp(const char *name, const char *oname)
* Returns whether the specified field exists according to the struct format
* pointed to by old.
*
- * \param sdna: Old SDNA
- * \param type: Current field type name
- * \param name: Current field name
- * \param old: Pointer to struct information in sdna
- * \return true when existing, false otherwise.
+ * \param type: Current field type name.
+ * \param name: Current field name.
+ * \param old: Pointer to struct information in sdna.
+ * \return true when existing, false otherwise..
*/
static bool elem_exists_impl(
/* Expand SDNA. */
@@ -995,6 +994,9 @@ static bool elem_exists_impl(
return false;
}
+/**
+ * \param sdna: Old SDNA.
+ */
static bool elem_exists(const SDNA *sdna, const char *type, const char *name, const short *old)
{
return elem_exists_impl(
@@ -1079,7 +1081,7 @@ static const char *find_elem(const SDNA *sdna,
/**
* Converts the contents of a single field of a struct, of a non-struct type,
- * from oldsdna to newsdna format.
+ * from \a oldsdna to \a newsdna format.
*
* \param newsdna: SDNA of current Blender
* \param oldsdna: SDNA of Blender that saved file
diff --git a/source/blender/makesdna/intern/dna_rename_defs.h b/source/blender/makesdna/intern/dna_rename_defs.h
index aabc16c4111..f2cf72843bd 100644
--- a/source/blender/makesdna/intern/dna_rename_defs.h
+++ b/source/blender/makesdna/intern/dna_rename_defs.h
@@ -77,6 +77,9 @@ DNA_STRUCT_RENAME_ELEM(FluidDomainSettings, guiding_parent, guide_parent)
DNA_STRUCT_RENAME_ELEM(FluidDomainSettings, guiding_source, guide_source)
DNA_STRUCT_RENAME_ELEM(FluidDomainSettings, guiding_vel_factor, guide_vel_factor)
DNA_STRUCT_RENAME_ELEM(FluidEffectorSettings, guiding_mode, guide_mode)
+DNA_STRUCT_RENAME_ELEM(Image, name, filepath)
+DNA_STRUCT_RENAME_ELEM(Library, name, filepath)
+DNA_STRUCT_RENAME_ELEM(MovieClip, name, filepath)
DNA_STRUCT_RENAME_ELEM(Object, col, color)
DNA_STRUCT_RENAME_ELEM(Object, dup_group, instance_collection)
DNA_STRUCT_RENAME_ELEM(Object, dupfacesca, instance_faces_scale)
@@ -84,8 +87,10 @@ DNA_STRUCT_RENAME_ELEM(Object, size, scale)
DNA_STRUCT_RENAME_ELEM(ParticleSettings, dup_group, instance_collection)
DNA_STRUCT_RENAME_ELEM(ParticleSettings, dup_ob, instance_object)
DNA_STRUCT_RENAME_ELEM(ParticleSettings, dupliweights, instance_weights)
+DNA_STRUCT_RENAME_ELEM(Text, name, filepath)
DNA_STRUCT_RENAME_ELEM(ThemeSpace, scrubbing_background, time_scrub_background)
DNA_STRUCT_RENAME_ELEM(ThemeSpace, show_back_grad, background_type)
+DNA_STRUCT_RENAME_ELEM(VFont, name, filepath)
DNA_STRUCT_RENAME_ELEM(View3D, far, clip_end)
DNA_STRUCT_RENAME_ELEM(View3D, near, clip_start)
DNA_STRUCT_RENAME_ELEM(View3D, ob_centre, ob_center)
@@ -100,6 +105,7 @@ DNA_STRUCT_RENAME_ELEM(bPoseChannel, curveOutY, curve_out_y)
DNA_STRUCT_RENAME_ELEM(bPoseChannel, scaleIn, scale_in_x)
DNA_STRUCT_RENAME_ELEM(bPoseChannel, scaleOut, scale_out_x)
DNA_STRUCT_RENAME_ELEM(bSameVolumeConstraint, flag, free_axis)
+DNA_STRUCT_RENAME_ELEM(bSound, name, filepath)
DNA_STRUCT_RENAME_ELEM(bTheme, tact, space_action)
DNA_STRUCT_RENAME_ELEM(bTheme, tbuts, space_properties)
DNA_STRUCT_RENAME_ELEM(bTheme, tclip, space_clip)
diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c
index 083806350e7..6d220454364 100644
--- a/source/blender/makesdna/intern/makesdna.c
+++ b/source/blender/makesdna/intern/makesdna.c
@@ -217,7 +217,7 @@ void BLI_system_backtrace(FILE *fp)
/**
* Ensure type \c str to is in the #types array.
* \param str: Struct name without any qualifiers.
- * \param len: The struct size in bytes.
+ * \param size: The struct size in bytes.
* \return Index in the #types array.
*/
static int add_type(const char *str, int size);
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index 97313225628..c97fcd16941 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -393,6 +393,8 @@ extern StructRNA RNA_MeshIntProperty;
extern StructRNA RNA_MeshLoop;
extern StructRNA RNA_MeshLoopColor;
extern StructRNA RNA_MeshLoopColorLayer;
+extern StructRNA RNA_MeshVertColor;
+extern StructRNA RNA_MeshVertColorLayer;
extern StructRNA RNA_MeshLoopTriangle;
extern StructRNA RNA_MeshPolygon;
extern StructRNA RNA_MeshSequenceCacheModifier;
@@ -825,12 +827,12 @@ int RNA_property_array_item_index(PropertyRNA *prop, char name);
int RNA_property_string_maxlength(PropertyRNA *prop);
-const char *RNA_property_ui_name(PropertyRNA *prop);
-const char *RNA_property_ui_name_raw(PropertyRNA *prop);
-const char *RNA_property_ui_description(PropertyRNA *prop);
-const char *RNA_property_ui_description_raw(PropertyRNA *prop);
-const char *RNA_property_translation_context(PropertyRNA *prop);
-int RNA_property_ui_icon(PropertyRNA *prop);
+const char *RNA_property_ui_name(const PropertyRNA *prop);
+const char *RNA_property_ui_name_raw(const PropertyRNA *prop);
+const char *RNA_property_ui_description(const PropertyRNA *prop);
+const char *RNA_property_ui_description_raw(const PropertyRNA *prop);
+const char *RNA_property_translation_context(const PropertyRNA *prop);
+int RNA_property_ui_icon(const PropertyRNA *prop);
/* Dynamic Property Information */
@@ -1471,6 +1473,7 @@ bool RNA_struct_override_matches(struct Main *bmain,
struct PointerRNA *ptr_local,
struct PointerRNA *ptr_reference,
const char *root_path,
+ const size_t root_path_len,
struct IDOverrideLibrary *override,
const eRNAOverrideMatch flags,
eRNAOverrideMatchResult *r_report_flags);
@@ -1488,7 +1491,8 @@ void RNA_struct_override_apply(struct Main *bmain,
struct IDOverrideLibrary *override);
struct IDOverrideLibraryProperty *RNA_property_override_property_find(PointerRNA *ptr,
- PropertyRNA *prop);
+ PropertyRNA *prop,
+ struct ID **r_owner_id);
struct IDOverrideLibraryProperty *RNA_property_override_property_get(PointerRNA *ptr,
PropertyRNA *prop,
bool *r_created);
diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt
index 64a8e5cbdfb..eaea895746e 100644
--- a/source/blender/makesrna/intern/CMakeLists.txt
+++ b/source/blender/makesrna/intern/CMakeLists.txt
@@ -48,6 +48,7 @@ set(DEFSRC
rna_fluid.c
rna_gpencil.c
rna_gpencil_modifier.c
+ rna_hair.c
rna_image.c
rna_key.c
rna_lattice.c
@@ -78,9 +79,11 @@ set(DEFSRC
rna_sculpt_paint.c
rna_sequencer.c
rna_shader_fx.c
+ rna_simulation.c
rna_sound.c
rna_space.c
rna_speaker.c
+ rna_pointcloud.c
rna_test.c
rna_text.c
rna_texture.c
@@ -97,20 +100,6 @@ set(DEFSRC
rna_xr.c
)
-if(WITH_NEW_OBJECT_TYPES)
- list(APPEND DEFSRC
- rna_hair.c
- rna_pointcloud.c
- )
-endif()
-
-if (WITH_NEW_SIMULATION_TYPE)
- list(APPEND DEFSRC
- rna_simulation.c
- )
-endif()
-
-
set(APISRC
rna_action_api.c
rna_animation_api.c
@@ -346,15 +335,6 @@ if(WITH_XR_OPENXR)
add_definitions(-DWITH_XR_OPENXR)
endif()
-if(WITH_NEW_OBJECT_TYPES)
- add_definitions(-DWITH_NEW_OBJECT_TYPES)
-endif()
-
-if (WITH_NEW_SIMULATION_TYPE)
- add_definitions(-DWITH_NEW_SIMULATION_TYPE)
-endif()
-
-
# Build makesrna executable
blender_include_dirs(
.
@@ -444,3 +424,6 @@ set(LIB
add_definitions(${GL_DEFINITIONS})
blender_add_lib(bf_rna "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
+
+# Needed so we can use dna_type_offsets.h for defaults initialization.
+add_dependencies(bf_blenkernel bf_dna)
diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c
index ff435710561..8d61b21759f 100644
--- a/source/blender/makesrna/intern/makesrna.c
+++ b/source/blender/makesrna/intern/makesrna.c
@@ -3770,7 +3770,8 @@ static void rna_generate_property(FILE *f, StructRNA *srna, const char *nest, Pr
/* XXX This systematically enforces that flag on ID pointers...
* we'll probably have to revisit. :/ */
StructRNA *type = rna_find_struct((const char *)pprop->type);
- if (type && (type->flag & STRUCT_ID)) {
+ if (type && (type->flag & STRUCT_ID) &&
+ !(prop->flag_internal & PROP_INTERN_PTR_OWNERSHIP_FORCED)) {
prop->flag |= PROP_PTR_NO_OWNERSHIP;
}
break;
@@ -3781,7 +3782,8 @@ static void rna_generate_property(FILE *f, StructRNA *srna, const char *nest, Pr
/* XXX This systematically enforces that flag on ID pointers...
* we'll probably have to revisit. :/ */
StructRNA *type = rna_find_struct((const char *)cprop->item_type);
- if (type && (type->flag & STRUCT_ID)) {
+ if (type && (type->flag & STRUCT_ID) &&
+ !(prop->flag_internal & PROP_INTERN_PTR_OWNERSHIP_FORCED)) {
prop->flag |= PROP_PTR_NO_OWNERSHIP;
}
break;
@@ -4270,9 +4272,7 @@ static RNAProcessItem PROCESS_ITEMS[] = {
{"rna_dynamicpaint.c", NULL, RNA_def_dynamic_paint},
{"rna_fcurve.c", "rna_fcurve_api.c", RNA_def_fcurve},
{"rna_gpencil.c", NULL, RNA_def_gpencil},
-#ifdef WITH_NEW_OBJECT_TYPES
{"rna_hair.c", NULL, RNA_def_hair},
-#endif
{"rna_image.c", "rna_image_api.c", RNA_def_image},
{"rna_key.c", NULL, RNA_def_key},
{"rna_light.c", NULL, RNA_def_light},
@@ -4295,9 +4295,7 @@ static RNAProcessItem PROCESS_ITEMS[] = {
{"rna_packedfile.c", NULL, RNA_def_packedfile},
{"rna_palette.c", NULL, RNA_def_palette},
{"rna_particle.c", NULL, RNA_def_particle},
-#ifdef WITH_NEW_OBJECT_TYPES
{"rna_pointcloud.c", NULL, RNA_def_pointcloud},
-#endif
{"rna_pose.c", "rna_pose_api.c", RNA_def_pose},
{"rna_curveprofile.c", NULL, RNA_def_profile},
{"rna_lightprobe.c", NULL, RNA_def_lightprobe},
@@ -4307,9 +4305,7 @@ static RNAProcessItem PROCESS_ITEMS[] = {
{"rna_screen.c", NULL, RNA_def_screen},
{"rna_sculpt_paint.c", NULL, RNA_def_sculpt_paint},
{"rna_sequencer.c", "rna_sequencer_api.c", RNA_def_sequencer},
-#ifdef WITH_NEW_SIMULATION_TYPE
{"rna_simulation.c", NULL, RNA_def_simulation},
-#endif
{"rna_space.c", "rna_space_api.c", RNA_def_space},
{"rna_speaker.c", NULL, RNA_def_speaker},
{"rna_test.c", NULL, RNA_def_test},
diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c
index 7c84ceb4f7f..b8b60121054 100644
--- a/source/blender/makesrna/intern/rna_ID.c
+++ b/source/blender/makesrna/intern/rna_ID.c
@@ -71,17 +71,13 @@ const EnumPropertyItem rna_enum_id_type_items[] = {
{ID_PA, "PARTICLE", ICON_PARTICLE_DATA, "Particle", ""},
{ID_LP, "LIGHT_PROBE", ICON_LIGHTPROBE_CUBEMAP, "Light Probe", ""},
{ID_SCE, "SCENE", ICON_SCENE_DATA, "Scene", ""},
-#ifdef WITH_NEW_SIMULATION_TYPE
{ID_SIM, "SIMULATION", ICON_PHYSICS, "Simulation", ""}, /* TODO: Use correct icon. */
-#endif
{ID_SO, "SOUND", ICON_SOUND, "Sound", ""},
{ID_SPK, "SPEAKER", ICON_SPEAKER, "Speaker", ""},
{ID_TXT, "TEXT", ICON_TEXT, "Text", ""},
{ID_TE, "TEXTURE", ICON_TEXTURE_DATA, "Texture", ""},
-#ifdef WITH_NEW_OBJECT_TYPES
{ID_HA, "HAIR", ICON_HAIR_DATA, "Hair", ""},
{ID_PT, "POINTCLOUD", ICON_POINTCLOUD_DATA, "PointCloud", ""},
-#endif
{ID_VO, "VOLUME", ICON_VOLUME_DATA, "Volume", ""},
{ID_WM, "WINDOWMANAGER", ICON_WINDOW, "Window Manager", ""},
{ID_WO, "WORLD", ICON_WORLD_DATA, "World", ""},
@@ -256,11 +252,9 @@ short RNA_type_to_ID_code(const StructRNA *type)
if (base_type == &RNA_FreestyleLineStyle) {
return ID_LS;
}
-# ifdef WITH_NEW_OBJECT_TYPES
if (base_type == &RNA_Hair) {
return ID_HA;
}
-# endif
if (base_type == &RNA_Lattice) {
return ID_LT;
}
@@ -294,11 +288,9 @@ short RNA_type_to_ID_code(const StructRNA *type)
if (base_type == &RNA_PaintCurve) {
return ID_PC;
}
-# ifdef WITH_NEW_OBJECT_TYPES
if (base_type == &RNA_PointCloud) {
return ID_PT;
}
-# endif
if (base_type == &RNA_LightProbe) {
return ID_LP;
}
@@ -308,11 +300,9 @@ short RNA_type_to_ID_code(const StructRNA *type)
if (base_type == &RNA_Screen) {
return ID_SCR;
}
-# ifdef WITH_NEW_SIMULATION_TYPE
if (base_type == &RNA_Simulation) {
return ID_SIM;
}
-# endif
if (base_type == &RNA_Sound) {
return ID_SO;
}
@@ -366,11 +356,7 @@ StructRNA *ID_code_to_RNA_type(short idcode)
case ID_GR:
return &RNA_Collection;
case ID_HA:
-# ifdef WITH_NEW_OBJECT_TYPES
return &RNA_Hair;
-# else
- return &RNA_ID;
-# endif
case ID_IM:
return &RNA_Image;
case ID_KE:
@@ -404,11 +390,7 @@ StructRNA *ID_code_to_RNA_type(short idcode)
case ID_PC:
return &RNA_PaintCurve;
case ID_PT:
-# ifdef WITH_NEW_OBJECT_TYPES
return &RNA_PointCloud;
-# else
- return &RNA_ID;
-# endif
case ID_LP:
return &RNA_LightProbe;
case ID_SCE:
@@ -416,11 +398,7 @@ StructRNA *ID_code_to_RNA_type(short idcode)
case ID_SCR:
return &RNA_Screen;
case ID_SIM:
-# ifdef WITH_NEW_SIMULATION_TYPE
return &RNA_Simulation;
-# else
- return &RNA_ID;
-# endif
case ID_SO:
return &RNA_Sound;
case ID_SPK:
@@ -1588,6 +1566,7 @@ static void rna_def_ID(BlenderRNA *brna)
"Preview",
"Preview image and icon of this data-block (None if not supported for this type of data)");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON);
RNA_def_property_pointer_funcs(prop, "rna_IDPreview_get", NULL, NULL, NULL);
prop = RNA_def_pointer(
@@ -1730,7 +1709,7 @@ static void rna_def_library(BlenderRNA *brna)
RNA_def_struct_ui_icon(srna, ICON_LIBRARY_DATA_DIRECT);
prop = RNA_def_property(srna, "filepath", PROP_STRING, PROP_FILEPATH);
- RNA_def_property_string_sdna(prop, NULL, "name");
+ RNA_def_property_string_sdna(prop, NULL, "filepath");
RNA_def_property_ui_text(prop, "File Path", "Path to the library .blend file");
RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Library_filepath_set");
diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c
index 2197764794b..79cf993e0cc 100644
--- a/source/blender/makesrna/intern/rna_access.c
+++ b/source/blender/makesrna/intern/rna_access.c
@@ -273,9 +273,9 @@ static IDProperty *rna_idproperty_ui_container(PropertyRNA *prop)
}
/* return a UI local ID prop definition for this prop */
-static IDProperty *rna_idproperty_ui(PropertyRNA *prop)
+static const IDProperty *rna_idproperty_ui(const PropertyRNA *prop)
{
- IDProperty *idprop = rna_idproperty_ui_container(prop);
+ IDProperty *idprop = rna_idproperty_ui_container((PropertyRNA *)prop);
if (idprop) {
return IDP_GetPropertyTypeFromGroup(idprop, ((IDProperty *)prop)->name, IDP_GROUP);
@@ -645,7 +645,7 @@ static const char *rna_ensure_property_identifier(const PropertyRNA *prop)
}
}
-static const char *rna_ensure_property_description(PropertyRNA *prop)
+static const char *rna_ensure_property_description(const PropertyRNA *prop)
{
const char *description = NULL;
@@ -654,7 +654,7 @@ static const char *rna_ensure_property_description(PropertyRNA *prop)
}
else {
/* attempt to get the local ID values */
- IDProperty *idp_ui = rna_idproperty_ui(prop);
+ const IDProperty *idp_ui = rna_idproperty_ui(prop);
if (idp_ui) {
IDProperty *item = IDP_GetPropertyTypeFromGroup(idp_ui, "description", IDP_STRING);
@@ -1140,7 +1140,7 @@ PropertySubType RNA_property_subtype(PropertyRNA *prop)
/* Restrict to arrays only for now for performance reasons. */
if (idprop->type == IDP_ARRAY && ELEM(idprop->subtype, IDP_INT, IDP_FLOAT, IDP_DOUBLE)) {
- IDProperty *idp_ui = rna_idproperty_ui(prop);
+ const IDProperty *idp_ui = rna_idproperty_ui(prop);
if (idp_ui) {
IDProperty *item = IDP_GetPropertyTypeFromGroup(idp_ui, "subtype", IDP_STRING);
@@ -1312,7 +1312,7 @@ void RNA_property_int_range(PointerRNA *ptr, PropertyRNA *prop, int *hardmin, in
if (prop->magic != RNA_MAGIC) {
/* attempt to get the local ID values */
- IDProperty *idp_ui = rna_idproperty_ui(prop);
+ const IDProperty *idp_ui = rna_idproperty_ui(prop);
if (idp_ui) {
IDProperty *item;
@@ -1353,7 +1353,7 @@ void RNA_property_int_ui_range(
if (prop->magic != RNA_MAGIC) {
/* attempt to get the local ID values */
- IDProperty *idp_ui = rna_idproperty_ui(prop);
+ const IDProperty *idp_ui = rna_idproperty_ui(prop);
if (idp_ui) {
IDProperty *item;
@@ -1403,7 +1403,7 @@ void RNA_property_float_range(PointerRNA *ptr, PropertyRNA *prop, float *hardmin
if (prop->magic != RNA_MAGIC) {
/* attempt to get the local ID values */
- IDProperty *idp_ui = rna_idproperty_ui(prop);
+ const IDProperty *idp_ui = rna_idproperty_ui(prop);
if (idp_ui) {
IDProperty *item;
@@ -1448,7 +1448,7 @@ void RNA_property_float_ui_range(PointerRNA *ptr,
if (prop->magic != RNA_MAGIC) {
/* attempt to get the local ID values */
- IDProperty *idp_ui = rna_idproperty_ui(prop);
+ const IDProperty *idp_ui = rna_idproperty_ui(prop);
if (idp_ui) {
IDProperty *item;
@@ -2025,35 +2025,34 @@ int RNA_property_enum_bitflag_identifiers(
return 0;
}
-const char *RNA_property_ui_name(PropertyRNA *prop)
+const char *RNA_property_ui_name(const PropertyRNA *prop)
{
return CTX_IFACE_(prop->translation_context, rna_ensure_property_name(prop));
}
-const char *RNA_property_ui_name_raw(PropertyRNA *prop)
+const char *RNA_property_ui_name_raw(const PropertyRNA *prop)
{
return rna_ensure_property_name(prop);
}
-const char *RNA_property_ui_description(PropertyRNA *prop)
+const char *RNA_property_ui_description(const PropertyRNA *prop)
{
return TIP_(rna_ensure_property_description(prop));
}
-const char *RNA_property_ui_description_raw(PropertyRNA *prop)
+const char *RNA_property_ui_description_raw(const PropertyRNA *prop)
{
return rna_ensure_property_description(prop);
}
-const char *RNA_property_translation_context(PropertyRNA *_prop)
+const char *RNA_property_translation_context(const PropertyRNA *prop)
{
- PropertyRNA *prop = rna_ensure_property(_prop);
- return prop->translation_context;
+ return rna_ensure_property((PropertyRNA *)prop)->translation_context;
}
-int RNA_property_ui_icon(PropertyRNA *prop)
+int RNA_property_ui_icon(const PropertyRNA *prop)
{
- return rna_ensure_property(prop)->icon;
+ return rna_ensure_property((PropertyRNA *)prop)->icon;
}
bool RNA_property_editable(PointerRNA *ptr, PropertyRNA *prop_orig)
@@ -2065,9 +2064,10 @@ bool RNA_property_editable(PointerRNA *ptr, PropertyRNA *prop_orig)
PropertyRNA *prop = rna_ensure_property(prop_orig);
flag = prop->editable ? prop->editable(ptr, &dummy_info) : prop->flag;
- return ((flag & PROP_EDITABLE) && (flag & PROP_REGISTER) == 0 &&
- (!id || ((!ID_IS_LINKED(id) || (prop->flag & PROP_LIB_EXCEPTION)) &&
- (!id->override_library || RNA_property_overridable_get(ptr, prop_orig)))));
+ return (
+ (flag & PROP_EDITABLE) && (flag & PROP_REGISTER) == 0 &&
+ (!id || ((!ID_IS_LINKED(id) || (prop->flag & PROP_LIB_EXCEPTION)) &&
+ (!ID_IS_OVERRIDE_LIBRARY(id) || RNA_property_overridable_get(ptr, prop_orig)))));
}
/**
@@ -2101,7 +2101,7 @@ bool RNA_property_editable_info(PointerRNA *ptr, PropertyRNA *prop, const char *
}
return false;
}
- if (id->override_library != NULL) {
+ if (ID_IS_OVERRIDE_LIBRARY(id)) {
/* We need the real data property in case of IDProperty here... */
PropertyRNA *real_prop = rna_ensure_property_realdata(&prop, ptr);
if (real_prop == NULL || !RNA_property_overridable_get(ptr, real_prop)) {
@@ -2847,7 +2847,7 @@ int RNA_property_int_get_default(PointerRNA *UNUSED(ptr), PropertyRNA *prop)
if (prop->magic != RNA_MAGIC) {
/* attempt to get the local ID values */
- IDProperty *idp_ui = rna_idproperty_ui(prop);
+ const IDProperty *idp_ui = rna_idproperty_ui(prop);
if (idp_ui) {
IDProperty *item;
@@ -2883,7 +2883,7 @@ void RNA_property_int_get_default_array(PointerRNA *ptr, PropertyRNA *prop, int
if (prop->magic != RNA_MAGIC) {
int length = rna_ensure_property_array_length(ptr, prop);
- IDProperty *idp_ui = rna_idproperty_ui(prop);
+ const IDProperty *idp_ui = rna_idproperty_ui(prop);
IDProperty *item = idp_ui ? IDP_GetPropertyFromGroup(idp_ui, "default") : NULL;
int defval = (item && item->type == IDP_INT) ? IDP_Int(item) : iprop->defaultvalue;
@@ -3220,7 +3220,7 @@ float RNA_property_float_get_default(PointerRNA *UNUSED(ptr), PropertyRNA *prop)
if (prop->magic != RNA_MAGIC) {
/* attempt to get the local ID values */
- IDProperty *idp_ui = rna_idproperty_ui(prop);
+ const IDProperty *idp_ui = rna_idproperty_ui(prop);
if (idp_ui) {
IDProperty *item;
@@ -3256,7 +3256,7 @@ void RNA_property_float_get_default_array(PointerRNA *ptr, PropertyRNA *prop, fl
if (prop->magic != RNA_MAGIC) {
int length = rna_ensure_property_array_length(ptr, prop);
- IDProperty *idp_ui = rna_idproperty_ui(prop);
+ const IDProperty *idp_ui = rna_idproperty_ui(prop);
IDProperty *item = idp_ui ? IDP_GetPropertyFromGroup(idp_ui, "default") : NULL;
float defval = (item && item->type == IDP_DOUBLE) ? IDP_Double(item) : fprop->defaultvalue;
@@ -3473,6 +3473,24 @@ void RNA_property_string_get_default(PointerRNA *UNUSED(ptr), PropertyRNA *prop,
{
StringPropertyRNA *sprop = (StringPropertyRNA *)rna_ensure_property(prop);
+ if (prop->magic != RNA_MAGIC) {
+ /* attempt to get the local ID values */
+ const IDProperty *idp_ui = rna_idproperty_ui(prop);
+
+ if (idp_ui) {
+ IDProperty *item;
+
+ item = IDP_GetPropertyTypeFromGroup(idp_ui, "default", IDP_STRING);
+ if (item) {
+ strcpy(value, IDP_String(item));
+ return;
+ }
+ }
+
+ strcpy(value, "");
+ return;
+ }
+
BLI_assert(RNA_property_type(prop) == PROP_STRING);
strcpy(value, sprop->defaultvalue);
@@ -3507,6 +3525,22 @@ int RNA_property_string_default_length(PointerRNA *UNUSED(ptr), PropertyRNA *pro
{
StringPropertyRNA *sprop = (StringPropertyRNA *)rna_ensure_property(prop);
+ if (prop->magic != RNA_MAGIC) {
+ /* attempt to get the local ID values */
+ const IDProperty *idp_ui = rna_idproperty_ui(prop);
+
+ if (idp_ui) {
+ IDProperty *item;
+
+ item = IDP_GetPropertyTypeFromGroup(idp_ui, "default", IDP_STRING);
+ if (item) {
+ return strlen(IDP_String(item));
+ }
+ }
+
+ return 0;
+ }
+
BLI_assert(RNA_property_type(prop) == PROP_STRING);
return strlen(sprop->defaultvalue);
@@ -5380,7 +5414,7 @@ bool RNA_path_resolve_property_full(
* This is a convenience method to avoid logic errors and ugly syntax,
* it combines both \a RNA_path_resolve and #RNA_path_resolve_property in a single call.
* \note Assumes all pointers provided are valid.
- * \param r_item_pointer: The final Pointer or Collection item value.
+ * \param r_item_ptr: The final Pointer or Collection item value.
* You must check for its validity before use!
* \return True only if both a valid pointer and property are found after resolving the path
*/
@@ -5406,7 +5440,7 @@ bool RNA_path_resolve_property_and_item_pointer(PointerRNA *ptr,
* it combines both \a RNA_path_resolve_full and
* \a RNA_path_resolve_property_full in a single call.
* \note Assumes all pointers provided are valid.
- * \param r_item_pointer: The final Pointer or Collection item value.
+ * \param r_item_ptr: The final Pointer or Collection item value.
* You must check for its validity before use!
* \return True only if both a valid pointer and property are found after resolving the path
*/
diff --git a/source/blender/makesrna/intern/rna_access_compare_override.c b/source/blender/makesrna/intern/rna_access_compare_override.c
index 265e83ddcba..8cd8f80b7c8 100644
--- a/source/blender/makesrna/intern/rna_access_compare_override.c
+++ b/source/blender/makesrna/intern/rna_access_compare_override.c
@@ -18,17 +18,20 @@
* \ingroup RNA
*/
+#include <string.h>
+
#include "MEM_guardedalloc.h"
#include "DNA_ID.h"
#include "DNA_constraint_types.h"
+#include "DNA_key_types.h"
#include "DNA_modifier_types.h"
#include "BLI_listbase.h"
#include "BLI_string.h"
#include "BLI_utildefines.h"
-// #define DEBUG_OVERRIDE_TIMEIT
+//#define DEBUG_OVERRIDE_TIMEIT
#ifdef DEBUG_OVERRIDE_TIMEIT
# include "PIL_time_utildefines.h"
@@ -106,7 +109,7 @@ bool RNA_property_overridden(PointerRNA *ptr, PropertyRNA *prop)
char *rna_path = RNA_path_from_ID_to_property(ptr, prop);
ID *id = ptr->owner_id;
- if (rna_path == NULL || id == NULL || id->override_library == NULL) {
+ if (rna_path == NULL || id == NULL || !ID_IS_OVERRIDE_LIBRARY(id)) {
return false;
}
@@ -180,6 +183,7 @@ static int rna_property_override_diff(Main *bmain,
PropertyRNA *prop_a,
PropertyRNA *prop_b,
const char *rna_path,
+ const size_t rna_path_len,
eRNACompareMode mode,
IDOverrideLibrary *override,
const int flags,
@@ -191,7 +195,7 @@ bool RNA_property_equals(
BLI_assert(ELEM(mode, RNA_EQ_STRICT, RNA_EQ_UNSET_MATCH_ANY, RNA_EQ_UNSET_MATCH_NONE));
return (rna_property_override_diff(
- bmain, ptr_a, ptr_b, prop, NULL, NULL, NULL, mode, NULL, 0, NULL) == 0);
+ bmain, ptr_a, ptr_b, prop, NULL, NULL, NULL, 0, mode, NULL, 0, NULL) == 0);
}
bool RNA_struct_equals(Main *bmain, PointerRNA *ptr_a, PointerRNA *ptr_b, eRNACompareMode mode)
@@ -249,6 +253,7 @@ static int rna_property_override_diff(Main *bmain,
PropertyRNA *prop_a,
PropertyRNA *prop_b,
const char *rna_path,
+ const size_t rna_path_len,
eRNACompareMode mode,
IDOverrideLibrary *override,
const int flags,
@@ -363,6 +368,7 @@ static int rna_property_override_diff(Main *bmain,
mode,
override,
rna_path,
+ rna_path_len,
diff_flags,
&override_changed);
if (override_changed && r_report_flags) {
@@ -568,6 +574,7 @@ bool RNA_struct_override_matches(Main *bmain,
PointerRNA *ptr_local,
PointerRNA *ptr_reference,
const char *root_path,
+ const size_t root_path_len,
IDOverrideLibrary *override,
const eRNAOverrideMatch flags,
eRNAOverrideMatchResult *r_report_flags)
@@ -649,31 +656,51 @@ bool RNA_struct_override_matches(Main *bmain,
#endif
#define RNA_PATH_BUFFSIZE 8192
-#define RNA_PATH_PRINTF(_str, ...) \
- if (BLI_snprintf(rna_path, RNA_PATH_BUFFSIZE, (_str), __VA_ARGS__) >= RNA_PATH_BUFFSIZE) { \
- rna_path = BLI_sprintfN((_str), __VA_ARGS__); \
- } \
- (void)0
-#define RNA_PATH_FREE \
- if (rna_path != rna_path_buffer) \
- MEM_freeN(rna_path)
char rna_path_buffer[RNA_PATH_BUFFSIZE];
char *rna_path = rna_path_buffer;
+ size_t rna_path_len = 0;
/* XXX TODO this will have to be refined to handle collections insertions, and array items */
if (root_path) {
+ BLI_assert(strlen(root_path) == root_path_len);
+
+ const char *prop_name = RNA_property_identifier(prop_local);
+ const size_t prop_name_len = strlen(prop_name);
+
/* Inlined building, much much more efficient. */
if (prop_local->magic == RNA_MAGIC) {
- RNA_PATH_PRINTF("%s.%s", root_path, RNA_property_identifier(prop_local));
+ rna_path_len = root_path_len + 1 + prop_name_len;
+ if (rna_path_len >= RNA_PATH_BUFFSIZE) {
+ rna_path = MEM_mallocN(rna_path_len + 1, __func__);
+ }
+
+ memcpy(rna_path, root_path, root_path_len);
+ rna_path[root_path_len] = '.';
+ memcpy(rna_path + root_path_len + 1, prop_name, prop_name_len);
+ rna_path[rna_path_len] = '\0';
}
else {
- RNA_PATH_PRINTF("%s[\"%s\"]", root_path, RNA_property_identifier(prop_local));
+ rna_path_len = root_path_len + 2 + prop_name_len + 2;
+ if (rna_path_len >= RNA_PATH_BUFFSIZE) {
+ rna_path = MEM_mallocN(rna_path_len + 1, __func__);
+ }
+
+ memcpy(rna_path, root_path, root_path_len);
+ rna_path[root_path_len] = '[';
+ rna_path[root_path_len + 1] = '"';
+ memcpy(rna_path + root_path_len + 2, prop_name, prop_name_len);
+ rna_path[root_path_len + 2 + prop_name_len] = '"';
+ rna_path[root_path_len + 2 + prop_name_len + 1] = ']';
+ rna_path[rna_path_len] = '\0';
}
}
else {
/* This is rather slow, but is not much called, so not really worth optimizing. */
rna_path = RNA_path_from_ID_to_property(ptr_local, prop_local);
+ if (rna_path != NULL) {
+ rna_path_len = strlen(rna_path);
+ }
}
if (rna_path == NULL) {
continue;
@@ -684,7 +711,10 @@ bool RNA_struct_override_matches(Main *bmain,
IDOverrideLibraryProperty *op = BKE_lib_override_library_property_find(override, rna_path);
if (ignore_overridden && op != NULL) {
BKE_lib_override_library_operations_tag(op, IDOVERRIDE_LIBRARY_TAG_UNUSED, false);
- RNA_PATH_FREE;
+
+ if (rna_path != rna_path_buffer) {
+ MEM_freeN(rna_path);
+ }
continue;
}
@@ -702,6 +732,7 @@ bool RNA_struct_override_matches(Main *bmain,
prop_local,
prop_reference,
rna_path,
+ rna_path_len,
RNA_EQ_STRICT,
override,
flags,
@@ -769,17 +800,18 @@ bool RNA_struct_override_matches(Main *bmain,
matching = false;
if (!(do_create || do_restore)) {
/* Since we have no 'changing' action allowed, we can break here. */
- MEM_SAFE_FREE(rna_path);
+ if (rna_path != rna_path_buffer) {
+ MEM_freeN(rna_path);
+ }
break;
}
}
}
- RNA_PATH_FREE;
-
+ if (rna_path != rna_path_buffer) {
+ MEM_freeN(rna_path);
+ }
#undef RNA_PATH_BUFFSIZE
-#undef RNA_PATH_PRINTF
-#undef RNA_PATH_FREE
}
RNA_property_collection_end(&iter);
@@ -1045,18 +1077,70 @@ void RNA_struct_override_apply(Main *bmain,
#endif
}
-IDOverrideLibraryProperty *RNA_property_override_property_find(PointerRNA *ptr, PropertyRNA *prop)
+static char *rna_property_override_property_real_id_owner(PointerRNA *ptr,
+ PropertyRNA *prop,
+ ID **r_id)
{
ID *id = ptr->owner_id;
+ ID *owner_id = id;
+ const char *rna_path_prefix = NULL;
+
+ *r_id = NULL;
+
+ if (id == NULL) {
+ return NULL;
+ }
- if (!id || !id->override_library) {
+ if (id->flag & (LIB_EMBEDDED_DATA | LIB_EMBEDDED_DATA_LIB_OVERRIDE)) {
+ /* XXX this is very bad band-aid code, but for now it will do.
+ * We should at least use a #define for those prop names.
+ * Ideally RNA as a whole should be aware of those PITA of embedded IDs, and have a way to
+ * retrieve their owner IDs and generate paths from those.
+ */
+
+ switch (GS(id->name)) {
+ case ID_KE:
+ owner_id = ((Key *)id)->from;
+ rna_path_prefix = "shape_keys.";
+ break;
+ case ID_GR:
+ /* Master collection, TODO. */
+ break;
+ case ID_NT:
+ /* Root node trees, TODO. */
+ break;
+ default:
+ BLI_assert(0);
+ }
+ }
+
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(owner_id)) {
return NULL;
}
char *rna_path = RNA_path_from_ID_to_property(ptr, prop);
if (rna_path) {
- IDOverrideLibraryProperty *op = BKE_lib_override_library_property_find(id->override_library,
- rna_path);
+ char *rna_path_full = rna_path;
+ if (rna_path_prefix != NULL) {
+ rna_path_full = BLI_sprintfN("%s%s", rna_path_prefix, rna_path);
+ MEM_freeN(rna_path);
+ }
+
+ *r_id = owner_id;
+ return rna_path_full;
+ }
+ return NULL;
+}
+
+IDOverrideLibraryProperty *RNA_property_override_property_find(PointerRNA *ptr,
+ PropertyRNA *prop,
+ ID **r_owner_id)
+{
+ char *rna_path;
+
+ if ((rna_path = rna_property_override_property_real_id_owner(ptr, prop, r_owner_id)) != NULL) {
+ IDOverrideLibraryProperty *op = BKE_lib_override_library_property_find(
+ (*r_owner_id)->override_library, rna_path);
MEM_freeN(rna_path);
return op;
}
@@ -1067,14 +1151,10 @@ IDOverrideLibraryProperty *RNA_property_override_property_get(PointerRNA *ptr,
PropertyRNA *prop,
bool *r_created)
{
- ID *id = ptr->owner_id;
-
- if (!id || !id->override_library) {
- return NULL;
- }
+ ID *id;
+ char *rna_path;
- char *rna_path = RNA_path_from_ID_to_property(ptr, prop);
- if (rna_path) {
+ if ((rna_path = rna_property_override_property_real_id_owner(ptr, prop, &id)) != NULL) {
IDOverrideLibraryProperty *op = BKE_lib_override_library_property_get(
id->override_library, rna_path, r_created);
MEM_freeN(rna_path);
@@ -1086,7 +1166,8 @@ IDOverrideLibraryProperty *RNA_property_override_property_get(PointerRNA *ptr,
IDOverrideLibraryPropertyOperation *RNA_property_override_property_operation_find(
PointerRNA *ptr, PropertyRNA *prop, const int index, const bool strict, bool *r_strict)
{
- IDOverrideLibraryProperty *op = RNA_property_override_property_find(ptr, prop);
+ ID *owner_id;
+ IDOverrideLibraryProperty *op = RNA_property_override_property_find(ptr, prop, &owner_id);
if (!op) {
return NULL;
@@ -1119,13 +1200,13 @@ eRNAOverrideStatus RNA_property_override_library_status(PointerRNA *ptr,
PropertyRNA *prop,
const int index)
{
- int override_status = 0;
+ uint override_status = 0;
if (!BKE_lib_override_library_is_enabled()) {
return override_status;
}
- if (!ptr || !prop || !ptr->owner_id || !(ptr->owner_id)->override_library) {
+ if (!ptr || !prop || !ptr->owner_id || !ID_IS_OVERRIDE_LIBRARY(ptr->owner_id)) {
return override_status;
}
diff --git a/source/blender/makesrna/intern/rna_action.c b/source/blender/makesrna/intern/rna_action.c
index e67366fc7ef..cec32877c0b 100644
--- a/source/blender/makesrna/intern/rna_action.c
+++ b/source/blender/makesrna/intern/rna_action.c
@@ -569,7 +569,6 @@ static void rna_def_dopesheet(BlenderRNA *brna)
RNA_def_property_ui_icon(prop, ICON_FILE, 0);
RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
-# ifdef WITH_NEW_OBJECT_TYPES
prop = RNA_def_property(srna, "show_hairs", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_negative_sdna(prop, NULL, "filterflag2", ADS_FILTER_NOHAIR);
RNA_def_property_ui_text(
@@ -583,7 +582,6 @@ static void rna_def_dopesheet(BlenderRNA *brna)
prop, "Display Point Cloud", "Include visualization of point cloud related animation data");
RNA_def_property_ui_icon(prop, ICON_OUTLINER_OB_POINTCLOUD, 0);
RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
-# endif
prop = RNA_def_property(srna, "show_volumes", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_negative_sdna(prop, NULL, "filterflag2", ADS_FILTER_NOVOLUME);
diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c
index cc5cd97a8a0..b139e4609cd 100644
--- a/source/blender/makesrna/intern/rna_brush.c
+++ b/source/blender/makesrna/intern/rna_brush.c
@@ -99,6 +99,8 @@ const EnumPropertyItem rna_enum_brush_sculpt_tool_items[] = {
{SCULPT_TOOL_CLOTH, "CLOTH", ICON_BRUSH_SCULPT_DRAW, "Cloth", ""},
{SCULPT_TOOL_SIMPLIFY, "SIMPLIFY", ICON_BRUSH_DATA, "Simplify", ""},
{SCULPT_TOOL_MASK, "MASK", ICON_BRUSH_MASK, "Mask", ""},
+ {SCULPT_TOOL_PAINT, "PAINT", ICON_BRUSH_SCULPT_DRAW, "Paint", ""},
+ {SCULPT_TOOL_SMEAR, "SMEAR", ICON_BRUSH_SCULPT_DRAW, "Smear", ""},
{SCULPT_TOOL_DRAW_FACE_SETS, "DRAW_FACE_SETS", ICON_BRUSH_MASK, "Draw Face Sets", ""},
{0, NULL, 0, NULL, NULL},
};
@@ -314,7 +316,8 @@ static bool rna_BrushCapabilitiesSculpt_has_topology_rake_get(PointerRNA *ptr)
static bool rna_BrushCapabilitiesSculpt_has_auto_smooth_get(PointerRNA *ptr)
{
Brush *br = (Brush *)ptr->data;
- return !ELEM(br->sculpt_tool, SCULPT_TOOL_MASK, SCULPT_TOOL_SMOOTH);
+ return !ELEM(
+ br->sculpt_tool, SCULPT_TOOL_MASK, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR);
}
static bool rna_BrushCapabilitiesSculpt_has_height_get(PointerRNA *ptr)
@@ -409,6 +412,12 @@ static bool rna_BrushCapabilitiesSculpt_has_sculpt_plane_get(PointerRNA *ptr)
SCULPT_TOOL_SMOOTH);
}
+static bool rna_BrushCapabilitiesSculpt_has_color_get(PointerRNA *ptr)
+{
+ Brush *br = (Brush *)ptr->data;
+ return ELEM(br->sculpt_tool, SCULPT_TOOL_PAINT);
+}
+
static bool rna_BrushCapabilitiesSculpt_has_secondary_color_get(PointerRNA *ptr)
{
Brush *br = (Brush *)ptr->data;
@@ -1053,6 +1062,7 @@ static void rna_def_sculpt_capabilities(BlenderRNA *brna)
SCULPT_TOOL_CAPABILITY(has_plane_offset, "Has Plane Offset");
SCULPT_TOOL_CAPABILITY(has_random_texture_angle, "Has Random Texture Angle");
SCULPT_TOOL_CAPABILITY(has_sculpt_plane, "Has Sculpt Plane");
+ SCULPT_TOOL_CAPABILITY(has_color, "Has Color");
SCULPT_TOOL_CAPABILITY(has_secondary_color, "Has Secondary Color");
SCULPT_TOOL_CAPABILITY(has_smooth_stroke, "Has Smooth Stroke");
SCULPT_TOOL_CAPABILITY(has_space_attenuation, "Has Space Attenuation");
@@ -2221,6 +2231,46 @@ static void rna_def_brush(BlenderRNA *brna)
prop, "Strength", "How powerful the effect of the brush is when applied");
RNA_def_property_update(prop, 0, "rna_Brush_update");
+ prop = RNA_def_property(srna, "flow", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_float_sdna(prop, NULL, "flow");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.001, 3);
+ RNA_def_property_ui_text(prop, "Flow", "Amount of paint that is applied per stroke sample");
+ RNA_def_property_update(prop, 0, "rna_Brush_update");
+
+ prop = RNA_def_property(srna, "wet_mix", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_float_sdna(prop, NULL, "wet_mix");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.001, 3);
+ RNA_def_property_ui_text(
+ prop, "Wet Mix", "Amount of paint that is picked from the surface into the brush color");
+ RNA_def_property_update(prop, 0, "rna_Brush_update");
+
+ prop = RNA_def_property(srna, "wet_persistence", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_float_sdna(prop, NULL, "wet_persistence");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.001, 3);
+ RNA_def_property_ui_text(
+ prop,
+ "Wet Persistence",
+ "Amount of wet paint that stays in the brush after applyig paint to the surface");
+ RNA_def_property_update(prop, 0, "rna_Brush_update");
+
+ prop = RNA_def_property(srna, "density", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_float_sdna(prop, NULL, "density");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.001, 3);
+ RNA_def_property_ui_text(
+ prop, "Density", "Amount of random elements that are going to be affected by the brush");
+ RNA_def_property_update(prop, 0, "rna_Brush_update");
+
+ prop = RNA_def_property(srna, "tip_scale_x", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_float_sdna(prop, NULL, "tip_scale_x");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.001, 3);
+ RNA_def_property_ui_text(prop, "Tip Scale X", "Scale of the brush tip in the X axis");
+ RNA_def_property_update(prop, 0, "rna_Brush_update");
+
prop = RNA_def_property(srna, "dash_ratio", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "dash_ratio");
RNA_def_property_range(prop, 0.0f, 1.0f);
@@ -2312,6 +2362,14 @@ static void rna_def_brush(BlenderRNA *brna)
prop, "Pose Origin Offset", "Offset of the pose origin in relation to the brush radius");
RNA_def_property_update(prop, 0, "rna_Brush_update");
+ prop = RNA_def_property(srna, "disconnected_distance_max", PROP_FLOAT, PROP_DISTANCE);
+ RNA_def_property_float_sdna(prop, NULL, "disconnected_distance_max");
+ RNA_def_property_range(prop, 0.0f, 10.0f);
+ RNA_def_property_ui_text(prop,
+ "Max Element Distance",
+ "Maximum distance to search for disconnected loose parts in the mesh");
+ RNA_def_property_update(prop, 0, "rna_Brush_update");
+
prop = RNA_def_property(srna, "surface_smooth_shape_preservation", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "surface_smooth_shape_preservation");
RNA_def_property_range(prop, 0.0f, 1.0f);
@@ -2607,6 +2665,11 @@ static void rna_def_brush(BlenderRNA *brna)
prop, "Keep Anchor Point", "Keep the position of the last segment in the IK chain fixed");
RNA_def_property_update(prop, 0, "rna_Brush_update");
+ prop = RNA_def_property(srna, "use_connected_only", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag2", BRUSH_USE_CONNECTED_ONLY);
+ RNA_def_property_ui_text(prop, "Connected Only", "Affect only topologically connected elements");
+ RNA_def_property_update(prop, 0, "rna_Brush_update");
+
prop = RNA_def_property(srna, "invert_to_scrape_fill", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_INVERT_TO_SCRAPE_FILL);
RNA_def_property_ui_text(prop,
diff --git a/source/blender/makesrna/intern/rna_cachefile.c b/source/blender/makesrna/intern/rna_cachefile.c
index 7b4b7c11c17..f9275ef1993 100644
--- a/source/blender/makesrna/intern/rna_cachefile.c
+++ b/source/blender/makesrna/intern/rna_cachefile.c
@@ -69,9 +69,13 @@ static void rna_def_alembic_object_path(BlenderRNA *brna)
RNA_def_struct_ui_text(srna, "Object Path", "Path of an object inside of an Alembic archive");
RNA_def_struct_ui_icon(srna, ICON_NONE);
+ RNA_define_lib_overridable(true);
+
PropertyRNA *prop = RNA_def_property(srna, "path", PROP_STRING, PROP_NONE);
RNA_def_property_ui_text(prop, "Path", "Object path");
RNA_def_struct_name_property(srna, prop);
+
+ RNA_define_lib_overridable(false);
}
/* cachefile.object_paths */
@@ -90,6 +94,8 @@ static void rna_def_cachefile(BlenderRNA *brna)
RNA_def_struct_ui_text(srna, "CacheFile", "");
RNA_def_struct_ui_icon(srna, ICON_FILE);
+ RNA_define_lib_overridable(true);
+
PropertyRNA *prop = RNA_def_property(srna, "filepath", PROP_STRING, PROP_FILEPATH);
RNA_def_property_ui_text(prop, "File Path", "Path to external displacements file");
RNA_def_property_update(prop, 0, "rna_CacheFile_update");
@@ -167,6 +173,9 @@ static void rna_def_cachefile(BlenderRNA *brna)
RNA_def_property_srna(prop, "AlembicObjectPaths");
RNA_def_property_ui_text(
prop, "Object Paths", "Paths of the objects inside the Alembic archive");
+
+ RNA_define_lib_overridable(false);
+
rna_def_cachefile_object_paths(brna, prop);
rna_def_animdata_common(srna);
diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c
index 5405cb4e24a..af300f6e088 100644
--- a/source/blender/makesrna/intern/rna_constraint.c
+++ b/source/blender/makesrna/intern/rna_constraint.c
@@ -2483,7 +2483,7 @@ static void rna_def_constraint_location_limit(BlenderRNA *brna)
prop = RNA_def_property(srna, "use_transform_limit", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag2", LIMIT_TRANSFORM);
RNA_def_property_ui_text(
- prop, "For Transform", "Transforms are affected by this constraint as well");
+ prop, "Affect Transform", "Transforms are affected by this constraint as well");
RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
RNA_define_lib_overridable(false);
@@ -2556,7 +2556,7 @@ static void rna_def_constraint_rotation_limit(BlenderRNA *brna)
prop = RNA_def_property(srna, "use_transform_limit", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag2", LIMIT_TRANSFORM);
RNA_def_property_ui_text(
- prop, "For Transform", "Transforms are affected by this constraint as well");
+ prop, "Affect Transform", "Transforms are affected by this constraint as well");
RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
RNA_define_lib_overridable(false);
@@ -2644,7 +2644,7 @@ static void rna_def_constraint_size_limit(BlenderRNA *brna)
prop = RNA_def_property(srna, "use_transform_limit", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag2", LIMIT_TRANSFORM);
RNA_def_property_ui_text(
- prop, "For Transform", "Transforms are affected by this constraint as well");
+ prop, "Affect Transform", "Transforms are affected by this constraint as well");
RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
RNA_define_lib_overridable(false);
@@ -2684,7 +2684,7 @@ static void rna_def_constraint_distance_limit(BlenderRNA *brna)
prop = RNA_def_property(srna, "use_transform_limit", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", LIMITDIST_TRANSFORM);
RNA_def_property_ui_text(
- prop, "For Transform", "Transforms are affected by this constraint as well");
+ prop, "Affect Transform", "Transforms are affected by this constraint as well");
RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
RNA_define_lib_overridable(false);
@@ -3380,7 +3380,7 @@ void RNA_def_constraint(BlenderRNA *brna)
prop = RNA_def_property(srna, "show_expanded", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE);
- RNA_def_property_boolean_sdna(prop, NULL, "flag", CONSTRAINT_EXPAND);
+ RNA_def_property_boolean_sdna(prop, NULL, "ui_expand_flag", 0);
RNA_def_property_ui_text(prop, "Expanded", "Constraint's panel is expanded in UI");
RNA_def_property_ui_icon(prop, ICON_DISCLOSURE_TRI_RIGHT, 1);
diff --git a/source/blender/makesrna/intern/rna_curve.c b/source/blender/makesrna/intern/rna_curve.c
index 8b2658c7e0c..771235c85aa 100644
--- a/source/blender/makesrna/intern/rna_curve.c
+++ b/source/blender/makesrna/intern/rna_curve.c
@@ -687,7 +687,8 @@ static Nurb *rna_Curve_spline_new(Curve *cu, int type)
nu->pntsv = 1;
nu->orderu = nu->orderv = 4;
- nu->resolu = nu->resolv = 12;
+ nu->resolu = cu->resolu;
+ nu->resolv = cu->resolv;
nu->flag = CU_SMOOTH;
if ((cu->flag & CU_3D) == 0) {
@@ -1533,6 +1534,8 @@ static void rna_def_curve(BlenderRNA *brna)
prop = RNA_def_property(srna, "shape_keys", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "key");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_clear_flag(prop, PROP_PTR_NO_OWNERSHIP);
RNA_def_property_ui_text(prop, "Shape Keys", "");
prop = RNA_def_property(srna, "splines", PROP_COLLECTION, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_curveprofile.c b/source/blender/makesrna/intern/rna_curveprofile.c
index 94a35bdede8..ce91fc79085 100644
--- a/source/blender/makesrna/intern/rna_curveprofile.c
+++ b/source/blender/makesrna/intern/rna_curveprofile.c
@@ -62,6 +62,22 @@
# include "IMB_colormanagement.h"
# include "IMB_imbuf.h"
+/**
+ * Set both handle types for all selected points in the profile-- faster than changing types
+ * for many points individually. Also set both handles for the points.
+ */
+static void rna_CurveProfilePoint_handle_type_set(PointerRNA *ptr, int value)
+{
+ CurveProfilePoint *point = ptr->data;
+ CurveProfile *profile = point->profile;
+
+ if (profile) {
+ BKE_curveprofile_selected_handle_set(profile, value, value);
+ BKE_curveprofile_update(profile, PROF_UPDATE_NONE);
+ WM_main_add_notifier(NC_GEOM | ND_DATA, NULL);
+ }
+}
+
static void rna_CurveProfile_clip_set(PointerRNA *ptr, bool value)
{
CurveProfile *profile = (CurveProfile *)ptr->data;
@@ -73,7 +89,7 @@ static void rna_CurveProfile_clip_set(PointerRNA *ptr, bool value)
profile->flag &= ~PROF_USE_CLIP;
}
- BKE_curveprofile_update(profile, false);
+ BKE_curveprofile_update(profile, PROF_UPDATE_CLIP);
}
static void rna_CurveProfile_sample_straight_set(PointerRNA *ptr, bool value)
@@ -87,7 +103,7 @@ static void rna_CurveProfile_sample_straight_set(PointerRNA *ptr, bool value)
profile->flag &= ~PROF_SAMPLE_STRAIGHT_EDGES;
}
- BKE_curveprofile_update(profile, false);
+ BKE_curveprofile_update(profile, PROF_UPDATE_NONE);
}
static void rna_CurveProfile_sample_even_set(PointerRNA *ptr, bool value)
@@ -101,7 +117,7 @@ static void rna_CurveProfile_sample_even_set(PointerRNA *ptr, bool value)
profile->flag &= ~PROF_SAMPLE_EVEN_LENGTHS;
}
- BKE_curveprofile_update(profile, false);
+ BKE_curveprofile_update(profile, PROF_UPDATE_NONE);
}
static void rna_CurveProfile_remove_point(CurveProfile *profile,
@@ -135,14 +151,16 @@ static void rna_CurveProfile_initialize(struct CurveProfile *profile, int segmen
static void rna_CurveProfile_update(struct CurveProfile *profile)
{
- BKE_curveprofile_update(profile, false);
+ BKE_curveprofile_update(profile, PROF_UPDATE_REMOVE_DOUBLES | PROF_UPDATE_CLIP);
}
#else
static const EnumPropertyItem prop_handle_type_items[] = {
- {HD_AUTO, "AUTO", 0, "Auto Handle", ""},
- {HD_VECT, "VECTOR", 0, "Vector Handle", ""},
+ {HD_AUTO, "AUTO", ICON_HANDLE_AUTO, "Auto Handle", ""},
+ {HD_VECT, "VECTOR", ICON_HANDLE_VECTOR, "Vector Handle", ""},
+ {HD_FREE, "FREE", ICON_HANDLE_FREE, "Free Handle", ""},
+ {HD_ALIGN, "ALIGN", ICON_HANDLE_ALIGNED, "Aligned Free Handles", ""},
{0, NULL, 0, NULL, NULL},
};
@@ -162,14 +180,14 @@ static void rna_def_curveprofilepoint(BlenderRNA *brna)
prop = RNA_def_property(srna, "handle_type_1", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "h1");
RNA_def_property_enum_items(prop, prop_handle_type_items);
- RNA_def_property_ui_text(
- prop, "First Handle Type", "Path interpolation at this point: Bezier or vector");
+ RNA_def_property_enum_funcs(prop, NULL, "rna_CurveProfilePoint_handle_type_set", NULL);
+ RNA_def_property_ui_text(prop, "First Handle Type", "Path interpolation at this point");
prop = RNA_def_property(srna, "handle_type_2", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "h2");
RNA_def_property_enum_items(prop, prop_handle_type_items);
- RNA_def_property_ui_text(
- prop, "Second Handle Type", "Path interpolation at this point: Bezier or vector");
+ RNA_def_property_enum_funcs(prop, NULL, "rna_CurveProfilePoint_handle_type_set", NULL);
+ RNA_def_property_ui_text(prop, "Second Handle Type", "Path interpolation at this point");
prop = RNA_def_property(srna, "select", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", PROF_SELECT);
@@ -260,7 +278,7 @@ static void rna_def_curveprofile(BlenderRNA *brna)
RNA_def_property_boolean_funcs(prop, NULL, "rna_CurveProfile_sample_even_set");
func = RNA_def_function(srna, "update", "rna_CurveProfile_update");
- RNA_def_function_ui_description(func, "Update the profile");
+ RNA_def_function_ui_description(func, "Refresh internal data, remove doubles and clip points");
func = RNA_def_function(srna, "initialize", "rna_CurveProfile_initialize");
parm = RNA_def_int(func,
diff --git a/source/blender/makesrna/intern/rna_define.c b/source/blender/makesrna/intern/rna_define.c
index d6bedc61424..bb791dd6e2d 100644
--- a/source/blender/makesrna/intern/rna_define.c
+++ b/source/blender/makesrna/intern/rna_define.c
@@ -1324,7 +1324,7 @@ PropertyRNA *RNA_def_property(StructOrFunctionRNA *cont_,
switch (type) {
case PROP_BOOLEAN:
if (DefRNA.preprocess) {
- if ((subtype & ~(PROP_LAYER_MEMBER)) != PROP_NONE) {
+ if ((subtype & ~PROP_LAYER_MEMBER) != PROP_NONE) {
CLOG_ERROR(&LOG,
"subtype does not apply to 'PROP_BOOLEAN' \"%s.%s\"",
CONTAINER_RNA_ID(cont),
@@ -1515,6 +1515,9 @@ void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
void RNA_def_property_clear_flag(PropertyRNA *prop, PropertyFlag flag)
{
prop->flag &= ~flag;
+ if (flag & PROP_PTR_NO_OWNERSHIP) {
+ prop->flag_internal |= PROP_INTERN_PTR_OWNERSHIP_FORCED;
+ }
}
void RNA_def_property_override_flag(PropertyRNA *prop, PropertyOverrideFlag flag)
diff --git a/source/blender/makesrna/intern/rna_fcurve.c b/source/blender/makesrna/intern/rna_fcurve.c
index e49186f4cb1..3ae16f8577a 100644
--- a/source/blender/makesrna/intern/rna_fcurve.c
+++ b/source/blender/makesrna/intern/rna_fcurve.c
@@ -687,7 +687,7 @@ static void rna_FModifier_start_frame_set(PointerRNA *ptr, float value)
}
}
-static void rna_FModifer_end_frame_set(PointerRNA *ptr, float value)
+static void rna_FModifier_end_frame_set(PointerRNA *ptr, float value)
{
FModifier *fcm = (FModifier *)ptr->data;
@@ -1665,7 +1665,7 @@ static void rna_def_fmodifier(BlenderRNA *brna)
prop = RNA_def_property(srna, "frame_end", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "efra");
RNA_def_property_float_funcs(
- prop, NULL, "rna_FModifer_end_frame_set", "rna_FModifier_end_frame_range");
+ prop, NULL, "rna_FModifier_end_frame_set", "rna_FModifier_end_frame_range");
RNA_def_property_ui_text(
prop,
"End Frame",
diff --git a/source/blender/makesrna/intern/rna_fluid.c b/source/blender/makesrna/intern/rna_fluid.c
index 94ba09b2bb8..b4fa791362f 100644
--- a/source/blender/makesrna/intern/rna_fluid.c
+++ b/source/blender/makesrna/intern/rna_fluid.c
@@ -557,7 +557,7 @@ static const EnumPropertyItem *rna_Fluid_cachetype_mesh_itemf(bContext *UNUSED(C
}
static const EnumPropertyItem *rna_Fluid_cachetype_volume_itemf(bContext *UNUSED(C),
- PointerRNA *UNUSED(ptr),
+ PointerRNA *ptr,
PropertyRNA *UNUSED(prop),
bool *r_free)
{
@@ -579,11 +579,16 @@ static const EnumPropertyItem *rna_Fluid_cachetype_volume_itemf(bContext *UNUSED
RNA_enum_item_add(&item, &totitem, &tmp);
# endif
- tmp.value = FLUID_DOMAIN_FILE_RAW;
- tmp.identifier = "RAW";
- tmp.name = "Raw Cache";
- tmp.description = "Raw file format (.raw)";
- RNA_enum_item_add(&item, &totitem, &tmp);
+ /* Support for deprecated .raw format. */
+ FluidDomainSettings *mds = (FluidDomainSettings *)ptr->data;
+ if (mds->cache_data_format == FLUID_DOMAIN_FILE_RAW ||
+ mds->cache_noise_format == FLUID_DOMAIN_FILE_RAW) {
+ tmp.value = FLUID_DOMAIN_FILE_RAW;
+ tmp.identifier = "RAW";
+ tmp.name = "Raw Cache";
+ tmp.description = "Raw file format (.raw)";
+ RNA_enum_item_add(&item, &totitem, &tmp);
+ }
RNA_enum_item_end(&item, &totitem);
*r_free = true;
@@ -1058,27 +1063,18 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna)
};
static EnumPropertyItem cache_types[] = {
- {FLUID_DOMAIN_CACHE_REPLAY,
- "REPLAY",
- 0,
- "Replay",
- "Use the timeline to bake the scene. Pausing and resuming possible"},
+ {FLUID_DOMAIN_CACHE_REPLAY, "REPLAY", 0, "Replay", "Use the timeline to bake the scene"},
{FLUID_DOMAIN_CACHE_MODULAR,
"MODULAR",
0,
"Modular",
- "Bake every stage of the simulation separately. Pausing and resuming possible"},
- {FLUID_DOMAIN_CACHE_FINAL,
- "FINAL",
- 0,
- "Final",
- "Bake the entire simulation at once. Only generates the most essential cache files. "
- "Pausing and resuming not possible"},
+ "Bake every stage of the simulation separately"},
+ {FLUID_DOMAIN_CACHE_ALL, "ALL", 0, "All", "Bake all simulation settings at once"},
{0, NULL, 0, NULL, NULL}};
- static const EnumPropertyItem smoke_data_depth_items[] = {
- {16, "16", 0, "Float (Half)", "Half float (16 bit data)"},
- {0, "32", 0, "Float (Full)", "Full float (32 bit data)"}, /* default */
+ static const EnumPropertyItem fluid_data_depth_items[] = {
+ {VDB_PRECISION_HALF_FLOAT, "16", 0, "Half", "Half float (16 bit data)"},
+ {VDB_PRECISION_FULL_FLOAT, "32", 0, "Full", "Full float (32 bit data)"},
{0, NULL, 0, NULL, NULL},
};
@@ -2072,6 +2068,16 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Type", "Change the cache type of the simulation");
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Fluid_domain_reset");
+ prop = RNA_def_property(srna, "cache_resumable", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", FLUID_DOMAIN_USE_RESUMABLE_CACHE);
+ RNA_def_property_ui_text(
+ prop,
+ "Resumable",
+ "Additional data will be saved so that the bake jobs can be resumed after pausing. Because "
+ "more data will be written to disk it is recommended to avoid enabling this option when "
+ "baking at high resolutions");
+ RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_datacache_reset");
+
prop = RNA_def_property(srna, "cache_directory", PROP_STRING, PROP_DIRPATH);
RNA_def_property_string_maxlength(prop, FILE_MAX);
RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Fluid_cache_directory_set");
@@ -2320,13 +2326,13 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna)
/* OpenVDB options */
prop = RNA_def_property(srna, "openvdb_cache_compress_type", PROP_ENUM, PROP_NONE);
- RNA_def_property_enum_sdna(prop, NULL, "openvdb_comp");
+ RNA_def_property_enum_sdna(prop, NULL, "openvdb_compression");
RNA_def_property_enum_items(prop, prop_compression_items);
- RNA_def_property_ui_text(prop, "Compression", "Compression method to be used");
+ RNA_def_property_ui_text(prop, "Compression", "facession method to be used");
- prop = RNA_def_property(srna, "data_depth", PROP_ENUM, PROP_NONE);
- RNA_def_property_enum_bitflag_sdna(prop, NULL, "data_depth");
- RNA_def_property_enum_items(prop, smoke_data_depth_items);
+ prop = RNA_def_property(srna, "openvdb_data_depth", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_bitflag_sdna(prop, NULL, "openvdb_data_depth");
+ RNA_def_property_enum_items(prop, fluid_data_depth_items);
RNA_def_property_ui_text(prop,
"Data Depth",
"Bit depth for writing all scalar (including vector) "
diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c
index c707ad9b84e..c8d16ab65cc 100644
--- a/source/blender/makesrna/intern/rna_gpencil.c
+++ b/source/blender/makesrna/intern/rna_gpencil.c
@@ -1011,6 +1011,12 @@ static void rna_def_gpencil_stroke_point(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "UV Rotation", "Internal UV factor for dot mode");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
+ prop = RNA_def_property(srna, "uv_fill", PROP_FLOAT, PROP_XYZ);
+ RNA_def_property_float_sdna(prop, NULL, "uv_fill");
+ RNA_def_property_array(prop, 2);
+ RNA_def_property_ui_text(prop, "UV Fill", "Internal UV factor for filling");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
+
prop = RNA_def_property(srna, "select", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_SPOINT_SELECT);
RNA_def_property_boolean_funcs(prop, NULL, "rna_GPencil_stroke_point_select_set");
diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c
index 3a8ded1a275..1c39ad3a1a8 100644
--- a/source/blender/makesrna/intern/rna_gpencil_modifier.c
+++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c
@@ -128,12 +128,12 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = {
ICON_MOD_OPACITY,
"Opacity",
"Opacity of the strokes"},
- {eGpencilModifierType_Tint, "GP_TINT", ICON_MOD_TINT, "Tint", "Tint strokes with new color"},
{eGpencilModifierType_Texture,
"GP_TEXTURE",
ICON_TEXTURE,
"Texture Mapping",
"Change stroke uv texture values"},
+ {eGpencilModifierType_Tint, "GP_TINT", ICON_MOD_TINT, "Tint", "Tint strokes with new color"},
{0, NULL, 0, NULL, NULL},
};
@@ -1490,7 +1490,7 @@ static void rna_def_modifier_gpencilarray(BlenderRNA *brna)
RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT);
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
- prop = RNA_def_property(srna, "relative_offset", PROP_FLOAT, PROP_NONE);
+ prop = RNA_def_property(srna, "relative_offset", PROP_FLOAT, PROP_XYZ);
RNA_def_property_float_sdna(prop, NULL, "shift");
RNA_def_property_ui_text(
prop,
@@ -2127,8 +2127,7 @@ static void rna_def_modifier_gpencilmultiply(BlenderRNA *brna)
prop = RNA_def_property(srna, "use_fade", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flags", GP_MULTIPLY_ENABLE_FADING);
- RNA_def_property_ui_text(
- prop, "Enable Fade", "Fade the stroke thickness for each generated stroke");
+ RNA_def_property_ui_text(prop, "Fade", "Fade the stroke thickness for each generated stroke");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "split_angle", PROP_FLOAT, PROP_ANGLE);
@@ -2183,12 +2182,12 @@ static void rna_def_modifier_gpenciltexture(BlenderRNA *brna)
{GP_TEX_CONSTANT_LENGTH,
"CONSTANT_LENGTH",
0,
- "Keep Texture at Constant Length",
+ "Constant Length",
"Keep the texture at a constant length regardless of the length of each stroke"},
{GP_TEX_FIT_STROKE,
"FIT_STROKE",
0,
- "Fit Texture to Stroke Length",
+ "Stroke Length",
"Scale the texture to fit the length of each stroke"},
{0, NULL, 0, NULL, NULL},
};
@@ -2269,7 +2268,7 @@ static void rna_def_modifier_gpenciltexture(BlenderRNA *brna)
RNA_def_property_range(prop, -FLT_MAX, FLT_MAX);
RNA_def_property_ui_range(prop, -100.0, 100.0, 0.1, 3);
RNA_def_property_float_default(prop, 0.0f);
- RNA_def_property_ui_text(prop, "Offset UVs", "Offset value to add to stroke UVs");
+ RNA_def_property_ui_text(prop, "UV Offset", "Offset value to add to stroke UVs");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "uv_scale", PROP_FLOAT, PROP_NONE);
@@ -2360,7 +2359,7 @@ void RNA_def_greasepencil_modifier(BlenderRNA *brna)
prop = RNA_def_property(srna, "show_expanded", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE);
- RNA_def_property_boolean_sdna(prop, NULL, "mode", eGpencilModifierMode_Expanded);
+ RNA_def_property_boolean_sdna(prop, NULL, "ui_expand_flag", 0);
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Expanded", "Set modifier expanded in the user interface");
RNA_def_property_ui_icon(prop, ICON_DISCLOSURE_TRI_RIGHT, 1);
diff --git a/source/blender/makesrna/intern/rna_image.c b/source/blender/makesrna/intern/rna_image.c
index cbc376c863f..887bded8540 100644
--- a/source/blender/makesrna/intern/rna_image.c
+++ b/source/blender/makesrna/intern/rna_image.c
@@ -915,13 +915,13 @@ static void rna_def_image(BlenderRNA *brna)
prop = RNA_def_property(srna, "filepath", PROP_STRING, PROP_FILEPATH);
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
- RNA_def_property_string_sdna(prop, NULL, "name");
+ RNA_def_property_string_sdna(prop, NULL, "filepath");
RNA_def_property_ui_text(prop, "File Name", "Image/Movie file name");
RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Image_reload_update");
/* eek. this is horrible but needed so we can save to a new name without blanking the data :( */
prop = RNA_def_property(srna, "filepath_raw", PROP_STRING, PROP_FILEPATH);
- RNA_def_property_string_sdna(prop, NULL, "name");
+ RNA_def_property_string_sdna(prop, NULL, "filepath");
RNA_def_property_ui_text(prop, "File Name", "Image/Movie file name (without data refreshing)");
prop = RNA_def_property(srna, "file_format", PROP_ENUM, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_image_api.c b/source/blender/makesrna/intern/rna_image_api.c
index fe3ef21d15e..41c0e724234 100644
--- a/source/blender/makesrna/intern/rna_image_api.c
+++ b/source/blender/makesrna/intern/rna_image_api.c
@@ -116,7 +116,7 @@ static void rna_Image_save(Image *image, Main *bmain, bContext *C, ReportList *r
ImBuf *ibuf = BKE_image_acquire_ibuf(image, NULL, &lock);
if (ibuf) {
char filename[FILE_MAX];
- BLI_strncpy(filename, image->name, sizeof(filename));
+ BLI_strncpy(filename, image->filepath, sizeof(filename));
BLI_path_abs(filename, ID_BLEND_PATH(bmain, &image->id));
/* note, we purposefully ignore packed files here,
@@ -138,7 +138,7 @@ static void rna_Image_save(Image *image, Main *bmain, bContext *C, ReportList *r
RPT_ERROR,
"Image '%s' could not be saved to '%s'",
image->id.name + 2,
- image->name);
+ image->filepath);
}
}
else {
diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h
index 381cee64d80..71971cf3cca 100644
--- a/source/blender/makesrna/intern/rna_internal.h
+++ b/source/blender/makesrna/intern/rna_internal.h
@@ -499,6 +499,7 @@ int rna_property_override_diff_default(struct Main *bmain,
const int mode,
struct IDOverrideLibrary *override,
const char *rna_path,
+ const size_t rna_path_len,
const int flags,
bool *r_override_changed);
diff --git a/source/blender/makesrna/intern/rna_internal_types.h b/source/blender/makesrna/intern/rna_internal_types.h
index 0e3fc851a9b..345d84fc5b1 100644
--- a/source/blender/makesrna/intern/rna_internal_types.h
+++ b/source/blender/makesrna/intern/rna_internal_types.h
@@ -176,6 +176,7 @@ typedef int (*RNAPropOverrideDiff)(struct Main *bmain,
const int mode,
struct IDOverrideLibrary *override,
const char *rna_path,
+ const size_t rna_path_len,
const int flags,
bool *r_override_changed);
@@ -328,6 +329,9 @@ typedef enum PropertyFlagIntern {
PROP_INTERN_RAW_ACCESS = (1 << 2),
PROP_INTERN_RAW_ARRAY = (1 << 3),
PROP_INTERN_FREE_POINTERS = (1 << 4),
+ /* Negative mirror of PROP_PTR_NO_OWNERSHIP, used to prevent automatically setting that one in
+ * makesrna when pointer is an ID... */
+ PROP_INTERN_PTR_OWNERSHIP_FORCED = (1 << 5),
} PropertyFlagIntern;
/* Property Types */
diff --git a/source/blender/makesrna/intern/rna_key.c b/source/blender/makesrna/intern/rna_key.c
index 7955109d9bc..f0de7317913 100644
--- a/source/blender/makesrna/intern/rna_key.c
+++ b/source/blender/makesrna/intern/rna_key.c
@@ -918,6 +918,7 @@ static void rna_def_keyblock(BlenderRNA *brna)
* (to test results) */
prop = RNA_def_property(srna, "value", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "curval");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_float_funcs(prop, NULL, "rna_ShapeKey_value_set", "rna_ShapeKey_value_range");
RNA_def_property_ui_range(prop, -10.0f, 10.0f, 10, 3);
RNA_def_property_ui_text(prop, "Value", "Value of shape key at the current frame");
@@ -944,6 +945,7 @@ static void rna_def_keyblock(BlenderRNA *brna)
prop = RNA_def_property(srna, "mute", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", KEYBLOCK_MUTE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Mute", "Toggle this shape key");
RNA_def_property_ui_icon(prop, ICON_CHECKBOX_HLT, -1);
RNA_def_property_update(prop, 0, "rna_Key_update_data");
@@ -966,6 +968,7 @@ static void rna_def_keyblock(BlenderRNA *brna)
prop = RNA_def_property(srna, "data", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "data", "totelem");
RNA_def_property_struct_type(prop, "UnknownType");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE);
RNA_def_property_ui_text(prop, "Data", "");
RNA_def_property_collection_funcs(prop,
"rna_ShapeKey_data_begin",
@@ -1028,6 +1031,7 @@ static void rna_def_key(BlenderRNA *brna)
prop = RNA_def_property(srna, "key_blocks", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "block", NULL);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_struct_type(prop, "ShapeKey");
RNA_def_property_ui_text(prop, "Key Blocks", "Shape keys");
@@ -1051,6 +1055,7 @@ static void rna_def_key(BlenderRNA *brna)
prop = RNA_def_property(srna, "eval_time", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "ctime");
RNA_def_property_range(prop, MINFRAME, MAXFRAME);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Evaluation Time", "Evaluation time for absolute shape keys");
RNA_def_property_update(prop, 0, "rna_Key_update_data");
}
diff --git a/source/blender/makesrna/intern/rna_lattice.c b/source/blender/makesrna/intern/rna_lattice.c
index 176fb838e91..fbbee1f5900 100644
--- a/source/blender/makesrna/intern/rna_lattice.c
+++ b/source/blender/makesrna/intern/rna_lattice.c
@@ -382,6 +382,8 @@ static void rna_def_lattice(BlenderRNA *brna)
prop = RNA_def_property(srna, "shape_keys", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "key");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_clear_flag(prop, PROP_PTR_NO_OWNERSHIP);
RNA_def_property_ui_text(prop, "Shape Keys", "");
prop = RNA_def_property(srna, "points", PROP_COLLECTION, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_light.c b/source/blender/makesrna/intern/rna_light.c
index 2caf315e09e..e43079c967f 100644
--- a/source/blender/makesrna/intern/rna_light.c
+++ b/source/blender/makesrna/intern/rna_light.c
@@ -173,6 +173,7 @@ static void rna_def_light(BlenderRNA *brna)
/* nodes */
prop = RNA_def_property(srna, "node_tree", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "nodetree");
+ RNA_def_property_clear_flag(prop, PROP_PTR_NO_OWNERSHIP);
RNA_def_property_ui_text(prop, "Node Tree", "Node tree for node based lights");
prop = RNA_def_property(srna, "use_nodes", PROP_BOOLEAN, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_linestyle.c b/source/blender/makesrna/intern/rna_linestyle.c
index 788246f5edd..64c50b82d1f 100644
--- a/source/blender/makesrna/intern/rna_linestyle.c
+++ b/source/blender/makesrna/intern/rna_linestyle.c
@@ -2188,6 +2188,7 @@ static void rna_def_linestyle(BlenderRNA *brna)
/* nodes */
prop = RNA_def_property(srna, "node_tree", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "nodetree");
+ RNA_def_property_clear_flag(prop, PROP_PTR_NO_OWNERSHIP);
RNA_def_property_ui_text(prop, "Node Tree", "Node tree for node-based shaders");
prop = RNA_def_property(srna, "use_nodes", PROP_BOOLEAN, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_main.c b/source/blender/makesrna/intern/rna_main.c
index 2f37e4079c7..1670e08325f 100644
--- a/source/blender/makesrna/intern/rna_main.c
+++ b/source/blender/makesrna/intern/rna_main.c
@@ -109,9 +109,7 @@ RNA_MAIN_LISTBASE_FUNCS_DEF(collections)
RNA_MAIN_LISTBASE_FUNCS_DEF(curves)
RNA_MAIN_LISTBASE_FUNCS_DEF(fonts)
RNA_MAIN_LISTBASE_FUNCS_DEF(gpencils)
-# ifdef WITH_NEW_OBJECT_TYPES
RNA_MAIN_LISTBASE_FUNCS_DEF(hairs)
-# endif
RNA_MAIN_LISTBASE_FUNCS_DEF(images)
RNA_MAIN_LISTBASE_FUNCS_DEF(lattices)
RNA_MAIN_LISTBASE_FUNCS_DEF(libraries)
@@ -128,15 +126,11 @@ RNA_MAIN_LISTBASE_FUNCS_DEF(objects)
RNA_MAIN_LISTBASE_FUNCS_DEF(paintcurves)
RNA_MAIN_LISTBASE_FUNCS_DEF(palettes)
RNA_MAIN_LISTBASE_FUNCS_DEF(particles)
-# ifdef WITH_NEW_OBJECT_TYPES
RNA_MAIN_LISTBASE_FUNCS_DEF(pointclouds)
-# endif
RNA_MAIN_LISTBASE_FUNCS_DEF(scenes)
RNA_MAIN_LISTBASE_FUNCS_DEF(screens)
RNA_MAIN_LISTBASE_FUNCS_DEF(shapekeys)
-# ifdef WITH_NEW_SIMULATION_TYPE
RNA_MAIN_LISTBASE_FUNCS_DEF(simulations)
-# endif
RNA_MAIN_LISTBASE_FUNCS_DEF(sounds)
RNA_MAIN_LISTBASE_FUNCS_DEF(speakers)
RNA_MAIN_LISTBASE_FUNCS_DEF(texts)
@@ -390,7 +384,6 @@ void RNA_def_main(BlenderRNA *brna)
"LightProbes",
"LightProbe data-blocks",
RNA_def_main_lightprobes},
-# ifdef WITH_NEW_OBJECT_TYPES
{"hairs", "Hair", "rna_Main_hairs_begin", "Hairs", "Hair data-blocks", RNA_def_main_hairs},
{"pointclouds",
"PointCloud",
@@ -398,21 +391,18 @@ void RNA_def_main(BlenderRNA *brna)
"Point Clouds",
"Point cloud data-blocks",
RNA_def_main_pointclouds},
-# endif
{"volumes",
"Volume",
"rna_Main_volumes_begin",
"Volumes",
"Volume data-blocks",
RNA_def_main_volumes},
-# ifdef WITH_NEW_SIMULATION_TYPE
{"simulations",
"Simulation",
"rna_Main_simulations_begin",
"Simulations",
"Simulation data-blocks",
RNA_def_main_simulations},
-# endif
{NULL, NULL, NULL, NULL, NULL, NULL},
};
@@ -455,7 +445,7 @@ void RNA_def_main(BlenderRNA *brna)
0,
INT_MAX,
"Version",
- "Version of Blender the .blend was saved with",
+ "File format version the .blend file was saved with",
0,
INT_MAX);
RNA_def_property_int_funcs(prop, "rna_Main_version_get", NULL, NULL);
diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c
index 093fd8b3bd5..990a5412093 100644
--- a/source/blender/makesrna/intern/rna_main_api.c
+++ b/source/blender/makesrna/intern/rna_main_api.c
@@ -708,7 +708,6 @@ static bGPdata *rna_Main_gpencils_new(Main *bmain, const char *name)
return gpd;
}
-# ifdef WITH_NEW_OBJECT_TYPES
static Hair *rna_Main_hairs_new(Main *bmain, const char *name)
{
char safe_name[MAX_ID_NAME - 2];
@@ -728,7 +727,6 @@ static PointCloud *rna_Main_pointclouds_new(Main *bmain, const char *name)
id_us_min(&pointcloud->id);
return pointcloud;
}
-# endif
static Volume *rna_Main_volumes_new(Main *bmain, const char *name)
{
@@ -740,7 +738,6 @@ static Volume *rna_Main_volumes_new(Main *bmain, const char *name)
return volume;
}
-# ifdef WITH_NEW_SIMULATION_TYPE
static Simulation *rna_Main_simulations_new(Main *bmain, const char *name)
{
char safe_name[MAX_ID_NAME - 2];
@@ -750,7 +747,6 @@ static Simulation *rna_Main_simulations_new(Main *bmain, const char *name)
id_us_min(&simulation->id);
return simulation;
}
-# endif
/* tag functions, all the same */
# define RNA_MAIN_ID_TAG_FUNCS_DEF(_func_name, _listbase_name, _id_type) \
@@ -794,14 +790,10 @@ RNA_MAIN_ID_TAG_FUNCS_DEF(cachefiles, cachefiles, ID_CF)
RNA_MAIN_ID_TAG_FUNCS_DEF(paintcurves, paintcurves, ID_PC)
RNA_MAIN_ID_TAG_FUNCS_DEF(workspaces, workspaces, ID_WS)
RNA_MAIN_ID_TAG_FUNCS_DEF(lightprobes, lightprobes, ID_LP)
-# ifdef WITH_NEW_OBJECT_TYPES
RNA_MAIN_ID_TAG_FUNCS_DEF(hairs, hairs, ID_HA)
RNA_MAIN_ID_TAG_FUNCS_DEF(pointclouds, pointclouds, ID_PT)
-# endif
RNA_MAIN_ID_TAG_FUNCS_DEF(volumes, volumes, ID_VO)
-# ifdef WITH_NEW_SIMULATION_TYPE
RNA_MAIN_ID_TAG_FUNCS_DEF(simulations, simulations, ID_SIM)
-# endif
# undef RNA_MAIN_ID_TAG_FUNCS_DEF
diff --git a/source/blender/makesrna/intern/rna_material.c b/source/blender/makesrna/intern/rna_material.c
index 5191869be3a..1004fc94d16 100644
--- a/source/blender/makesrna/intern/rna_material.c
+++ b/source/blender/makesrna/intern/rna_material.c
@@ -542,7 +542,7 @@ static void rna_def_material_greasepencil(BlenderRNA *brna)
RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialGpencil_update");
prop = RNA_def_property(srna, "ghost", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_MATERIAL_ONIONSKIN);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_MATERIAL_HIDE_ONIONSKIN);
RNA_def_property_ui_icon(prop, ICON_GHOST_ENABLED, 0);
RNA_def_property_ui_text(
prop, "Show in Ghosts", "Display strokes using this color when showing onion skins");
@@ -784,6 +784,7 @@ void RNA_def_material(BlenderRNA *brna)
/* nodetree */
prop = RNA_def_property(srna, "node_tree", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "nodetree");
+ RNA_def_property_clear_flag(prop, PROP_PTR_NO_OWNERSHIP);
RNA_def_property_ui_text(prop, "Node Tree", "Node tree for node based materials");
prop = RNA_def_property(srna, "use_nodes", PROP_BOOLEAN, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c
index 5f986c28964..506bfdb8c75 100644
--- a/source/blender/makesrna/intern/rna_mesh.c
+++ b/source/blender/makesrna/intern/rna_mesh.c
@@ -721,8 +721,6 @@ static void rna_MeshUVLoopLayer_clone_set(PointerRNA *ptr, bool value)
DEFINE_CUSTOMDATA_LAYER_COLLECTION(vertex_color, ldata, CD_MLOOPCOL)
DEFINE_CUSTOMDATA_LAYER_COLLECTION_ACTIVEITEM(
vertex_color, ldata, CD_MLOOPCOL, active, MeshLoopColorLayer)
-DEFINE_CUSTOMDATA_LAYER_COLLECTION_ACTIVEITEM(
- vertex_color, ldata, CD_MLOOPCOL, render, MeshLoopColorLayer)
static void rna_MeshLoopColorLayer_data_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
{
@@ -758,6 +756,46 @@ static void rna_MeshLoopColorLayer_active_set(PointerRNA *ptr, bool value)
rna_CustomDataLayer_active_set(ptr, rna_mesh_ldata(ptr), value, CD_MLOOPCOL, 0);
}
+/* sculpt_vertex_color_layers */
+
+DEFINE_CUSTOMDATA_LAYER_COLLECTION(sculpt_vertex_color, vdata, CD_PROP_COLOR)
+DEFINE_CUSTOMDATA_LAYER_COLLECTION_ACTIVEITEM(
+ sculpt_vertex_color, vdata, CD_PROP_COLOR, active, MeshVertColorLayer)
+
+static void rna_MeshVertColorLayer_data_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
+{
+ Mesh *me = rna_mesh(ptr);
+ CustomDataLayer *layer = (CustomDataLayer *)ptr->data;
+ rna_iterator_array_begin(
+ iter, layer->data, sizeof(MPropCol), (me->edit_mesh) ? 0 : me->totvert, 0, NULL);
+}
+
+static int rna_MeshVertColorLayer_data_length(PointerRNA *ptr)
+{
+ Mesh *me = rna_mesh(ptr);
+ return (me->edit_mesh) ? 0 : me->totvert;
+}
+
+static bool rna_MeshVertColorLayer_active_render_get(PointerRNA *ptr)
+{
+ return rna_CustomDataLayer_active_get(ptr, rna_mesh_vdata(ptr), CD_PROP_COLOR, 1);
+}
+
+static bool rna_MeshVertColorLayer_active_get(PointerRNA *ptr)
+{
+ return rna_CustomDataLayer_active_get(ptr, rna_mesh_vdata(ptr), CD_PROP_COLOR, 0);
+}
+
+static void rna_MeshVertColorLayer_active_render_set(PointerRNA *ptr, bool value)
+{
+ rna_CustomDataLayer_active_set(ptr, rna_mesh_vdata(ptr), value, CD_PROP_COLOR, 1);
+}
+
+static void rna_MeshVertColorLayer_active_set(PointerRNA *ptr, bool value)
+{
+ rna_CustomDataLayer_active_set(ptr, rna_mesh_vdata(ptr), value, CD_PROP_COLOR, 0);
+}
+
static int rna_float_layer_check(CollectionPropertyIterator *UNUSED(iter), void *data)
{
CustomDataLayer *layer = (CustomDataLayer *)data;
@@ -1218,6 +1256,19 @@ static char *rna_MeshColor_path(PointerRNA *ptr)
return rna_LoopCustomData_data_path(ptr, "vertex_colors", CD_MLOOPCOL);
}
+static char *rna_MeshVertColorLayer_path(PointerRNA *ptr)
+{
+ CustomDataLayer *cdl = ptr->data;
+ char name_esc[sizeof(cdl->name) * 2];
+ BLI_strescape(name_esc, cdl->name, sizeof(name_esc));
+ return BLI_sprintfN("sculpt_vertex_colors[\"%s\"]", name_esc);
+}
+
+static char *rna_MeshVertColor_path(PointerRNA *ptr)
+{
+ return rna_VertCustomData_data_path(ptr, "sculpt_vertex_colors", CD_PROP_COLOR);
+}
+
/**** Float Property Layer API ****/
static char *rna_MeshVertexFloatPropertyLayer_path(PointerRNA *ptr)
{
@@ -1439,6 +1490,33 @@ static void rna_Mesh_vertex_color_remove(struct Mesh *me,
}
}
+static PointerRNA rna_Mesh_sculpt_vertex_color_new(struct Mesh *me,
+ const char *name,
+ const bool do_init)
+{
+ PointerRNA ptr;
+ CustomData *vdata;
+ CustomDataLayer *cdl = NULL;
+ int index = ED_mesh_sculpt_color_add(me, name, false, do_init);
+
+ if (index != -1) {
+ vdata = rna_mesh_vdata_helper(me);
+ cdl = &vdata->layers[CustomData_get_layer_index_n(vdata, CD_PROP_COLOR, index)];
+ }
+
+ RNA_pointer_create(&me->id, &RNA_MeshVertColorLayer, cdl, &ptr);
+ return ptr;
+}
+
+static void rna_Mesh_sculpt_vertex_color_remove(struct Mesh *me,
+ ReportList *reports,
+ CustomDataLayer *layer)
+{
+ if (ED_mesh_sculpt_color_remove_named(me, layer->name) == false) {
+ BKE_reportf(reports, RPT_ERROR, "Sculpt vertex color '%s' not found", layer->name);
+ }
+}
+
# define DEFINE_CUSTOMDATA_PROPERTY_API( \
elemname, datatype, cd_prop_type, cdata, countvar, layertype) \
static PointerRNA rna_Mesh_##elemname##_##datatype##_property_new(struct Mesh *me, \
@@ -1510,10 +1588,6 @@ static void UNUSED_FUNCTION(rna_mesh_unused)(void)
(void)rna_Mesh_uv_layer_render_index_get;
(void)rna_Mesh_uv_layer_render_index_set;
(void)rna_Mesh_uv_layer_render_set;
- (void)rna_Mesh_vertex_color_render_get;
- (void)rna_Mesh_vertex_color_render_index_get;
- (void)rna_Mesh_vertex_color_render_index_set;
- (void)rna_Mesh_vertex_color_render_set;
(void)rna_Mesh_face_map_index_range;
(void)rna_Mesh_face_map_active_index_set;
(void)rna_Mesh_face_map_active_index_get;
@@ -2034,6 +2108,65 @@ static void rna_def_mloopcol(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_Mesh_update_data");
}
+static void rna_def_MPropCol(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "MeshVertColorLayer", NULL);
+ RNA_def_struct_ui_text(srna,
+ "Mesh Sculpt Vertex Color Layer",
+ "Layer of sculpt vertex colors in a Mesh data-block");
+ RNA_def_struct_sdna(srna, "CustomDataLayer");
+ RNA_def_struct_path_func(srna, "rna_MeshVertColorLayer_path");
+ RNA_def_struct_ui_icon(srna, ICON_GROUP_VCOL);
+
+ prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
+ RNA_def_struct_name_property(srna, prop);
+ RNA_def_property_string_funcs(prop, NULL, NULL, "rna_MeshVertexLayer_name_set");
+ RNA_def_property_ui_text(prop, "Name", "Name of Sculpt Vertex color layer");
+ RNA_def_property_update(prop, 0, "rna_Mesh_update_data");
+
+ prop = RNA_def_property(srna, "active", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_funcs(
+ prop, "rna_MeshVertColorLayer_active_get", "rna_MeshVertColorLayer_active_set");
+ RNA_def_property_ui_text(
+ prop, "Active", "Sets the sculpt vertex color layer as active for display and editing");
+ RNA_def_property_update(prop, 0, "rna_Mesh_update_data");
+
+ prop = RNA_def_property(srna, "active_render", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "active_rnd", 0);
+ RNA_def_property_boolean_funcs(prop,
+ "rna_MeshVertColorLayer_active_render_get",
+ "rna_MeshVertColorLayer_active_render_set");
+ RNA_def_property_ui_text(
+ prop, "Active Render", "Sets the sculpt vertex color layer as active for rendering");
+ RNA_def_property_update(prop, 0, "rna_Mesh_update_data");
+
+ prop = RNA_def_property(srna, "data", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_struct_type(prop, "MeshVertColor");
+ RNA_def_property_ui_text(prop, "Data", "");
+ RNA_def_property_collection_funcs(prop,
+ "rna_MeshVertColorLayer_data_begin",
+ "rna_iterator_array_next",
+ "rna_iterator_array_end",
+ "rna_iterator_array_get",
+ "rna_MeshVertColorLayer_data_length",
+ NULL,
+ NULL,
+ NULL);
+
+ srna = RNA_def_struct(brna, "MeshVertColor", NULL);
+ RNA_def_struct_sdna(srna, "MPropCol");
+ RNA_def_struct_ui_text(srna, "Mesh Sculpt Vertex Color", "Vertex colors in a Mesh");
+ RNA_def_struct_path_func(srna, "rna_MeshVertColor_path");
+
+ prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR);
+ RNA_def_property_array(prop, 4);
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Color", "");
+ RNA_def_property_update(prop, 0, "rna_Mesh_update_data");
+}
static void rna_def_mproperties(BlenderRNA *brna)
{
StructRNA *srna;
@@ -2373,6 +2506,60 @@ static void rna_def_loop_colors(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_property_update(prop, 0, "rna_Mesh_update_data_edit_active_color");
}
+static void rna_def_vert_colors(BlenderRNA *brna, PropertyRNA *cprop)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ FunctionRNA *func;
+ PropertyRNA *parm;
+
+ RNA_def_property_srna(cprop, "VertColors");
+ srna = RNA_def_struct(brna, "VertColors", NULL);
+ RNA_def_struct_sdna(srna, "Mesh");
+ RNA_def_struct_ui_text(srna, "Vert Colors", "Collection of sculpt vertex colors");
+
+ func = RNA_def_function(srna, "new", "rna_Mesh_sculpt_vertex_color_new");
+ RNA_def_function_ui_description(func, "Add a sculpt vertex color layer to Mesh");
+ RNA_def_string(func, "name", "Col", 0, "", "Sculpt Vertex color name");
+ RNA_def_boolean(func,
+ "do_init",
+ true,
+ "",
+ "Whether new layer's data should be initialized by copying current active one");
+ parm = RNA_def_pointer(func, "layer", "MeshVertColorLayer", "", "The newly created layer");
+ RNA_def_parameter_flags(parm, 0, PARM_RNAPTR);
+ RNA_def_function_return(func, parm);
+
+ func = RNA_def_function(srna, "remove", "rna_Mesh_sculpt_vertex_color_remove");
+ RNA_def_function_ui_description(func, "Remove a vertex color layer");
+ RNA_def_function_flag(func, FUNC_USE_REPORTS);
+ parm = RNA_def_pointer(func, "layer", "MeshVertColorLayer", "", "The layer to remove");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ RNA_def_property_clear_flag(parm, PROP_THICK_WRAP);
+
+ prop = RNA_def_property(srna, "active", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "MeshVertColorLayer");
+ RNA_def_property_pointer_funcs(prop,
+ "rna_Mesh_sculpt_vertex_color_active_get",
+ "rna_Mesh_sculpt_vertex_color_active_set",
+ NULL,
+ NULL);
+ RNA_def_property_flag(prop, PROP_EDITABLE | PROP_NEVER_UNLINK);
+ RNA_def_property_ui_text(
+ prop, "Active Sculpt Vertex Color Layer", "Active sculpt vertex color layer");
+ RNA_def_property_update(prop, 0, "rna_Mesh_update_data_edit_active_color");
+
+ prop = RNA_def_property(srna, "active_index", PROP_INT, PROP_UNSIGNED);
+ RNA_def_property_int_funcs(prop,
+ "rna_Mesh_sculpt_vertex_color_active_index_get",
+ "rna_Mesh_sculpt_vertex_color_active_index_set",
+ "rna_Mesh_sculpt_vertex_color_index_range");
+ RNA_def_property_ui_text(
+ prop, "Active Sculpt Vertex Color Index", "Active sculpt vertex color index");
+ RNA_def_property_update(prop, 0, "rna_Mesh_update_data_edit_active_color");
+}
+
static void rna_def_uv_layers(BlenderRNA *brna, PropertyRNA *cprop)
{
StructRNA *srna;
@@ -2739,30 +2926,35 @@ static void rna_def_mesh(BlenderRNA *brna)
prop = RNA_def_property(srna, "vertices", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "mvert", "totvert");
RNA_def_property_struct_type(prop, "MeshVertex");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE);
RNA_def_property_ui_text(prop, "Vertices", "Vertices of the mesh");
rna_def_mesh_vertices(brna, prop);
prop = RNA_def_property(srna, "edges", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "medge", "totedge");
RNA_def_property_struct_type(prop, "MeshEdge");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE);
RNA_def_property_ui_text(prop, "Edges", "Edges of the mesh");
rna_def_mesh_edges(brna, prop);
prop = RNA_def_property(srna, "loops", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "mloop", "totloop");
RNA_def_property_struct_type(prop, "MeshLoop");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE);
RNA_def_property_ui_text(prop, "Loops", "Loops of the mesh (polygon corners)");
rna_def_mesh_loops(brna, prop);
prop = RNA_def_property(srna, "polygons", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "mpoly", "totpoly");
RNA_def_property_struct_type(prop, "MeshPolygon");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE);
RNA_def_property_ui_text(prop, "Polygons", "Polygons of the mesh");
rna_def_mesh_polygons(brna, prop);
prop = RNA_def_property(srna, "loop_triangles", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "runtime.looptris.array", "runtime.looptris.len");
RNA_def_property_struct_type(prop, "MeshLoopTriangle");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE);
RNA_def_property_ui_text(prop, "Loop Triangles", "Tessellation of mesh polygons into triangles");
rna_def_mesh_looptris(brna, prop);
@@ -2789,6 +2981,7 @@ static void rna_def_mesh(BlenderRNA *brna)
NULL,
NULL);
RNA_def_property_struct_type(prop, "MeshUVLoopLayer");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE);
RNA_def_property_ui_text(prop, "UV Loop Layers", "All UV loop layers");
rna_def_uv_layers(brna, prop);
@@ -2836,9 +3029,27 @@ static void rna_def_mesh(BlenderRNA *brna)
NULL,
NULL);
RNA_def_property_struct_type(prop, "MeshLoopColorLayer");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE);
RNA_def_property_ui_text(prop, "Vertex Colors", "All vertex colors");
rna_def_loop_colors(brna, prop);
+ /* Sculpt Vertex colors */
+
+ prop = RNA_def_property(srna, "sculpt_vertex_colors", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_sdna(prop, NULL, "vdata.layers", "vdata.totlayer");
+ RNA_def_property_collection_funcs(prop,
+ "rna_Mesh_sculpt_vertex_colors_begin",
+ NULL,
+ NULL,
+ NULL,
+ "rna_Mesh_sculpt_vertex_colors_length",
+ NULL,
+ NULL,
+ NULL);
+ RNA_def_property_struct_type(prop, "MeshVertColorLayer");
+ RNA_def_property_ui_text(prop, "Sculpt Vertex Colors", "All vertex colors");
+ rna_def_vert_colors(brna, prop);
+
/* TODO, edge customdata layers (bmesh py api can access already) */
prop = RNA_def_property(srna, "vertex_layers_float", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "vdata.layers", "vdata.totlayer");
@@ -2852,6 +3063,7 @@ static void rna_def_mesh(BlenderRNA *brna)
NULL,
NULL);
RNA_def_property_struct_type(prop, "MeshVertexFloatPropertyLayer");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE);
RNA_def_property_ui_text(prop, "Float Property Layers", "");
rna_def_vertex_float_layers(brna, prop);
@@ -2867,6 +3079,7 @@ static void rna_def_mesh(BlenderRNA *brna)
NULL,
NULL);
RNA_def_property_struct_type(prop, "MeshVertexIntPropertyLayer");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE);
RNA_def_property_ui_text(prop, "Int Property Layers", "");
rna_def_vertex_int_layers(brna, prop);
@@ -2882,6 +3095,7 @@ static void rna_def_mesh(BlenderRNA *brna)
NULL,
NULL);
RNA_def_property_struct_type(prop, "MeshVertexStringPropertyLayer");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE);
RNA_def_property_ui_text(prop, "String Property Layers", "");
rna_def_vertex_string_layers(brna, prop);
@@ -2897,6 +3111,7 @@ static void rna_def_mesh(BlenderRNA *brna)
NULL,
NULL);
RNA_def_property_struct_type(prop, "MeshPolygonFloatPropertyLayer");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE);
RNA_def_property_ui_text(prop, "Float Property Layers", "");
rna_def_polygon_float_layers(brna, prop);
@@ -2912,6 +3127,7 @@ static void rna_def_mesh(BlenderRNA *brna)
NULL,
NULL);
RNA_def_property_struct_type(prop, "MeshPolygonIntPropertyLayer");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE);
RNA_def_property_ui_text(prop, "Int Property Layers", "");
rna_def_polygon_int_layers(brna, prop);
@@ -2927,6 +3143,7 @@ static void rna_def_mesh(BlenderRNA *brna)
NULL,
NULL);
RNA_def_property_struct_type(prop, "MeshPolygonStringPropertyLayer");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE);
RNA_def_property_ui_text(prop, "String Property Layers", "");
rna_def_polygon_string_layers(brna, prop);
@@ -2943,6 +3160,7 @@ static void rna_def_mesh(BlenderRNA *brna)
NULL,
NULL);
RNA_def_property_struct_type(prop, "MeshFaceMapLayer");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE);
RNA_def_property_ui_text(prop, "FaceMap", "");
rna_def_face_maps(brna, prop);
@@ -2959,6 +3177,7 @@ static void rna_def_mesh(BlenderRNA *brna)
NULL,
NULL);
RNA_def_property_struct_type(prop, "MeshSkinVertexLayer");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE);
RNA_def_property_ui_text(prop, "Skin Vertices", "All skin vertices");
rna_def_skin_vertices(brna, prop);
/* End skin vertices */
@@ -2976,6 +3195,7 @@ static void rna_def_mesh(BlenderRNA *brna)
NULL,
NULL);
RNA_def_property_struct_type(prop, "MeshPaintMaskLayer");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE);
RNA_def_property_ui_text(prop, "Vertex Paint Mask", "Vertex paint mask");
rna_def_paint_mask(brna, prop);
/* End paint mask */
@@ -3033,6 +3253,13 @@ static void rna_def_mesh(BlenderRNA *brna)
prop, "Preserve Face Sets", "Keep the current Face Sets on the new mesh");
RNA_def_property_update(prop, 0, "rna_Mesh_update_draw");
+ prop = RNA_def_property(srna, "use_remesh_preserve_vertex_colors", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_REMESH_REPROJECT_VERTEX_COLORS);
+ RNA_def_property_boolean_default(prop, false);
+ RNA_def_property_ui_text(
+ prop, "Preserve Vertex Colors", "Keep the current vertex colors on the new mesh");
+ RNA_def_property_update(prop, 0, "rna_Mesh_update_draw");
+
prop = RNA_def_property(srna, "remesh_mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "remesh_mode");
RNA_def_property_enum_items(prop, rna_enum_mesh_remesh_mode_items);
@@ -3077,6 +3304,8 @@ static void rna_def_mesh(BlenderRNA *brna)
prop = RNA_def_property(srna, "shape_keys", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "key");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_clear_flag(prop, PROP_PTR_NO_OWNERSHIP);
RNA_def_property_ui_text(prop, "Shape Keys", "");
/* texture space */
@@ -3183,6 +3412,7 @@ void RNA_def_mesh(BlenderRNA *brna)
rna_def_mpolygon(brna);
rna_def_mloopuv(brna);
rna_def_mloopcol(brna);
+ rna_def_MPropCol(brna);
rna_def_mproperties(brna);
rna_def_face_map(brna);
}
diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c
index ffd9bb772cc..57e46314777 100644
--- a/source/blender/makesrna/intern/rna_modifier.c
+++ b/source/blender/makesrna/intern/rna_modifier.c
@@ -290,13 +290,11 @@ const EnumPropertyItem rna_enum_object_modifier_type_items[] = {
"Spawn particles from the shape"},
{eModifierType_Softbody, "SOFT_BODY", ICON_MOD_SOFT, "Soft Body", ""},
{eModifierType_Surface, "SURFACE", ICON_MODIFIER, "Surface", ""},
-#ifdef WITH_NEW_SIMULATION_TYPE
{eModifierType_Simulation,
"SIMULATION",
ICON_PHYSICS,
"Simulation",
""}, /* TODO: Use correct icon. */
-#endif
{0, NULL, 0, NULL, NULL},
};
@@ -1163,7 +1161,7 @@ static PointerRNA rna_CollisionModifier_settings_get(PointerRNA *ptr)
static void rna_BevelModifier_update_segments(Main *bmain, Scene *scene, PointerRNA *ptr)
{
BevelModifierData *bmd = (BevelModifierData *)ptr->data;
- if (RNA_boolean_get(ptr, "use_custom_profile")) {
+ if (RNA_enum_get(ptr, "profile_type") == MOD_BEVEL_PROFILE_CUSTOM) {
short segments = (short)RNA_int_get(ptr, "segments");
BKE_curveprofile_initialize(bmd->custom_profile, segments);
}
@@ -1632,7 +1630,6 @@ static void rna_ParticleInstanceModifier_particle_system_set(PointerRNA *ptr,
CLAMP_MIN(psmd->psys, 1);
}
-# ifdef WITH_NEW_SIMULATION_TYPE
static void rna_SimulationModifier_simulation_update(Main *bmain, Scene *scene, PointerRNA *ptr)
{
SimulationModifierData *smd = ptr->data;
@@ -1641,11 +1638,10 @@ static void rna_SimulationModifier_simulation_update(Main *bmain, Scene *scene,
}
rna_Modifier_dependency_update(bmain, scene, ptr);
}
-# endif
/**
* Special set callback that just changes the first bit of the expansion flag.
- * This way the expansion state of all the subpanels is not changed by RNA.
+ * This way the expansion state of all the sub-panels is not changed by RNA.
*/
static void rna_Modifier_show_expanded_set(PointerRNA *ptr, bool value)
{
@@ -3948,6 +3944,25 @@ static void rna_def_modifier_bevel(BlenderRNA *brna)
0,
"Percent",
"Amount is percent of adjacent edge length"},
+ {MOD_BEVEL_AMT_ABSOLUTE,
+ "ABSOLUTE",
+ 0,
+ "Absolute",
+ "Amount is absolute distance along adjacent edge"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ static const EnumPropertyItem prop_profile_type_items[] = {
+ {MOD_BEVEL_PROFILE_SUPERELLIPSE,
+ "SUPERELLIPSE",
+ 0,
+ "Superellipse",
+ "The profile can be a concave or convex curve"},
+ {MOD_BEVEL_PROFILE_CUSTOM,
+ "CUSTOM",
+ 0,
+ "Custom",
+ "The profile can be any arbitrary path between its endpoints"},
{0, NULL, 0, NULL, NULL},
};
@@ -4061,6 +4076,13 @@ static void rna_def_modifier_bevel(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Width Type", "What distance Width measures");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
+ prop = RNA_def_property(srna, "profile_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "profile_type");
+ RNA_def_property_enum_items(prop, prop_profile_type_items);
+ RNA_def_property_ui_text(
+ prop, "Profile Type", "The type of shape used to rebuild a beveled section");
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
prop = RNA_def_property(srna, "profile", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.05, 2);
@@ -4120,12 +4142,6 @@ static void rna_def_modifier_bevel(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Spread", "Spread distance for inner miter arcs");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
- prop = RNA_def_property(srna, "use_custom_profile", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "flags", MOD_BEVEL_CUSTOM_PROFILE);
- RNA_def_property_ui_text(
- prop, "Custom Profile", "Whether to use a user inputted curve for the bevel's profile");
- RNA_def_property_update(prop, 0, "rna_Modifier_update");
-
prop = RNA_def_property(srna, "custom_profile", PROP_POINTER, PROP_NONE);
RNA_def_property_struct_type(prop, "CurveProfile");
RNA_def_property_pointer_sdna(prop, NULL, "custom_profile");
@@ -5021,7 +5037,7 @@ static void rna_def_modifier_weightvgedit(BlenderRNA *brna)
{MOD_WVG_MAPPING_RANDOM, "RANDOM", ICON_RNDCURVE, "Random", ""},
{MOD_WVG_MAPPING_STEP,
"STEP",
- ICON_NOCURVE /* Would need a better icon... */,
+ ICON_IPO_CONSTANT,
"Median Step",
"Map all values below 0.5 to 0.0, and all others to 1.0"},
{0, NULL, 0, NULL, NULL},
@@ -5272,7 +5288,7 @@ static void rna_def_modifier_weightvgproximity(BlenderRNA *brna)
{MOD_WVG_MAPPING_RANDOM, "RANDOM", ICON_RNDCURVE, "Random", ""},
{MOD_WVG_MAPPING_STEP,
"STEP",
- ICON_NOCURVE /* Would need a better icon... */,
+ ICON_IPO_CONSTANT,
"Median Step",
"Map all values below 0.5 to 0.0, and all others to 1.0"},
{0, NULL, 0, NULL, NULL},
@@ -5438,6 +5454,7 @@ static void rna_def_modifier_remesh(BlenderRNA *brna)
prop = RNA_def_property(srna, "voxel_size", PROP_FLOAT, PROP_DISTANCE);
RNA_def_property_float_sdna(prop, NULL, "voxel_size");
+ RNA_def_property_range(prop, 0.0001f, FLT_MAX);
RNA_def_property_ui_range(prop, 0.0001, 2, 0.1, 3);
RNA_def_property_ui_text(prop,
"Voxel Size",
@@ -6818,12 +6835,10 @@ static void rna_def_modifier_simulation(BlenderRNA *brna)
RNA_define_lib_overridable(true);
-# ifdef WITH_NEW_SIMULATION_TYPE
prop = RNA_def_property(srna, "simulation", PROP_POINTER, PROP_NONE);
RNA_def_property_ui_text(prop, "Simulation", "Simulation to access");
RNA_def_property_flag(prop, PROP_EDITABLE);
RNA_def_property_update(prop, 0, "rna_SimulationModifier_simulation_update");
-# endif
prop = RNA_def_property(srna, "data_path", PROP_STRING, PROP_NONE);
RNA_def_property_ui_text(
diff --git a/source/blender/makesrna/intern/rna_movieclip.c b/source/blender/makesrna/intern/rna_movieclip.c
index f55d44e7bad..f3c73e75434 100644
--- a/source/blender/makesrna/intern/rna_movieclip.c
+++ b/source/blender/makesrna/intern/rna_movieclip.c
@@ -329,7 +329,7 @@ static void rna_def_movieclip(BlenderRNA *brna)
RNA_def_struct_ui_icon(srna, ICON_SEQUENCE);
prop = RNA_def_property(srna, "filepath", PROP_STRING, PROP_FILEPATH);
- RNA_def_property_string_sdna(prop, NULL, "name");
+ RNA_def_property_string_sdna(prop, NULL, "filepath");
RNA_def_property_ui_text(prop, "File Path", "Filename of the movie or sequence file");
RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, "rna_MovieClip_reload_update");
diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c
index 32999c91fad..6312c84cf9f 100644
--- a/source/blender/makesrna/intern/rna_nodetree.c
+++ b/source/blender/makesrna/intern/rna_nodetree.c
@@ -3629,8 +3629,8 @@ static void rna_ShaderNodeTexIES_mode_set(PointerRNA *ptr, int value)
if (node->id) {
Text *text = (Text *)node->id;
- if (value == NODE_IES_EXTERNAL && text->name) {
- BLI_strncpy(nss->filepath, text->name, sizeof(nss->filepath));
+ if (value == NODE_IES_EXTERNAL && text->filepath) {
+ BLI_strncpy(nss->filepath, text->filepath, sizeof(nss->filepath));
BLI_path_rel(nss->filepath, BKE_main_blendfile_path_from_global());
}
@@ -3654,8 +3654,8 @@ static void rna_ShaderNodeScript_mode_set(PointerRNA *ptr, int value)
if (node->id) {
Text *text = (Text *)node->id;
- if (value == NODE_SCRIPT_EXTERNAL && text->name) {
- BLI_strncpy(nss->filepath, text->name, sizeof(nss->filepath));
+ if (value == NODE_SCRIPT_EXTERNAL && text->filepath) {
+ BLI_strncpy(nss->filepath, text->filepath, sizeof(nss->filepath));
BLI_path_rel(nss->filepath, BKE_main_blendfile_path_from_global());
}
@@ -4405,8 +4405,9 @@ static void def_sh_tex(StructRNA *srna)
static void def_sh_tex_sky(StructRNA *srna)
{
static const EnumPropertyItem prop_sky_type[] = {
- {SHD_SKY_OLD, "PREETHAM", 0, "Preetham", ""},
- {SHD_SKY_NEW, "HOSEK_WILKIE", 0, "Hosek / Wilkie", ""},
+ {SHD_SKY_PREETHAM, "PREETHAM", 0, "Preetham", "Preetham 1999"},
+ {SHD_SKY_HOSEK, "HOSEK_WILKIE", 0, "Hosek / Wilkie", "Hosek / Wilkie 2012"},
+ {SHD_SKY_NISHITA, "NISHITA", 0, "Nishita", "Nishita 1993 improved"},
{0, NULL, 0, NULL, NULL},
};
static float default_dir[3] = {0.0f, 0.0f, 1.0f};
@@ -4419,7 +4420,7 @@ static void def_sh_tex_sky(StructRNA *srna)
prop = RNA_def_property(srna, "sky_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "sky_model");
RNA_def_property_enum_items(prop, prop_sky_type);
- RNA_def_property_ui_text(prop, "Sky Type", "");
+ RNA_def_property_ui_text(prop, "Sky Type", "Which sky model should be used");
RNA_def_property_update(prop, 0, "rna_Node_update");
prop = RNA_def_property(srna, "sun_direction", PROP_FLOAT, PROP_DIRECTION);
@@ -4439,6 +4440,54 @@ static void def_sh_tex_sky(StructRNA *srna)
RNA_def_property_ui_text(
prop, "Ground Albedo", "Ground color that is subtly reflected in the sky");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+
+ prop = RNA_def_property(srna, "sun_disc", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Sun Disc", "Include the sun itself in the output");
+ RNA_def_property_boolean_sdna(prop, NULL, "sun_disc", 1);
+ RNA_def_property_boolean_default(prop, true);
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+
+ prop = RNA_def_property(srna, "sun_size", PROP_FLOAT, PROP_ANGLE);
+ RNA_def_property_ui_text(prop, "Sun Size", "Size of sun disc (angular diameter)");
+ RNA_def_property_range(prop, 0.0f, M_PI_2);
+ RNA_def_property_float_default(prop, DEG2RADF(0.545));
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+
+ prop = RNA_def_property(srna, "sun_elevation", PROP_FLOAT, PROP_ANGLE);
+ RNA_def_property_ui_text(prop, "Sun Elevation", "Angle between sun and horizon");
+ RNA_def_property_range(prop, -M_PI_2, M_PI_2);
+ RNA_def_property_float_default(prop, M_PI_2);
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+
+ prop = RNA_def_property(srna, "sun_rotation", PROP_FLOAT, PROP_ANGLE);
+ RNA_def_property_ui_text(prop, "Sun Rotation", "Rotation of sun around zenith");
+ RNA_def_property_range(prop, 0.0f, 2.0f * M_PI);
+ RNA_def_property_float_default(prop, 0.0f);
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+
+ prop = RNA_def_property(srna, "altitude", PROP_INT, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Altitude", "Altitude height from sea level in meters");
+ RNA_def_property_range(prop, 0, 60000);
+ RNA_def_property_int_default(prop, 0);
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+
+ prop = RNA_def_property(srna, "air_density", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_ui_text(prop, "Air", "Density of air molecules (Rayleigh scattering)");
+ RNA_def_property_range(prop, 0.0f, 10.0f);
+ RNA_def_property_float_default(prop, 1.0f);
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+
+ prop = RNA_def_property(srna, "dust_density", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_ui_text(prop, "Dust", "Density of dust and water molecules (Mie scattering)");
+ RNA_def_property_range(prop, 0.0f, 10.0f);
+ RNA_def_property_float_default(prop, 1.0f);
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+
+ prop = RNA_def_property(srna, "ozone_density", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_ui_text(prop, "Ozone", "Density of Ozone layer (Ozone absorption)");
+ RNA_def_property_range(prop, 0.0f, 10.0f);
+ RNA_def_property_float_default(prop, 1.0f);
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
static const EnumPropertyItem sh_tex_prop_interpolation_items[] = {
@@ -8971,7 +9020,7 @@ static void rna_def_node_socket_standard_types(BlenderRNA *brna)
/* XXX These types should eventually be registered at runtime.
* Then use the nodeStaticSocketType and nodeStaticSocketInterfaceType functions
* to get the idname strings from int type and subtype
- * (see node_socket.c, register_standard_node_socket_types).
+ * (see node_socket.cc, register_standard_node_socket_types).
*/
rna_def_node_socket_float(brna, "NodeSocketFloat", "NodeSocketInterfaceFloat", PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c
index 79a9e0be051..089410ffd25 100644
--- a/source/blender/makesrna/intern/rna_object.c
+++ b/source/blender/makesrna/intern/rna_object.c
@@ -234,10 +234,8 @@ const EnumPropertyItem rna_enum_object_type_items[] = {
OBTYPE_CU_SURF,
{OB_MBALL, "META", 0, "Meta", ""},
OBTYPE_CU_FONT,
-#ifdef WITH_NEW_OBJECT_TYPES
{OB_HAIR, "HAIR", 0, "Hair", ""},
{OB_POINTCLOUD, "POINTCLOUD", 0, "PointCloud", ""},
-#endif
{OB_VOLUME, "VOLUME", 0, "Volume", ""},
{0, "", 0, NULL, NULL},
{OB_ARMATURE, "ARMATURE", 0, "Armature", ""},
@@ -565,12 +563,10 @@ static StructRNA *rna_Object_data_typef(PointerRNA *ptr)
return &RNA_LightProbe;
case OB_GPENCIL:
return &RNA_GreasePencil;
-# ifdef WITH_NEW_OBJECT_TYPES
case OB_HAIR:
return &RNA_Hair;
case OB_POINTCLOUD:
return &RNA_PointCloud;
-# endif
case OB_VOLUME:
return &RNA_Volume;
default:
@@ -1501,6 +1497,17 @@ static void rna_Object_constraints_move(
WM_main_add_notifier(NC_OBJECT | ND_CONSTRAINT, object);
}
+static bConstraint *rna_Object_constraints_copy(Object *object, Main *bmain, PointerRNA *con_ptr)
+{
+ bConstraint *con = con_ptr->data;
+ bConstraint *new_con = BKE_constraint_copy_for_object(object, con);
+
+ ED_object_constraint_tag_update(bmain, object, new_con);
+ WM_main_add_notifier(NC_OBJECT | ND_CONSTRAINT | NA_ADDED, object);
+
+ return new_con;
+}
+
bool rna_Object_constraints_override_apply(Main *UNUSED(bmain),
PointerRNA *ptr_dst,
PointerRNA *ptr_src,
@@ -2134,6 +2141,8 @@ static void rna_def_material_slot(BlenderRNA *brna)
RNA_def_struct_ui_text(srna, "Material Slot", "Material slot in an object");
RNA_def_struct_ui_icon(srna, ICON_MATERIAL_DATA);
+ RNA_define_lib_overridable(true);
+
/* WARNING! Order is crucial for override to work properly here... :/
* 'link' must come before material pointer,
* since it defines where (in object or obdata) that one is set! */
@@ -2141,7 +2150,6 @@ static void rna_def_material_slot(BlenderRNA *brna)
RNA_def_property_enum_items(prop, link_items);
RNA_def_property_enum_funcs(
prop, "rna_MaterialSlot_link_get", "rna_MaterialSlot_link_set", NULL);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Link", "Link material to object or the object's data");
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_MaterialSlot_update");
@@ -2149,7 +2157,6 @@ static void rna_def_material_slot(BlenderRNA *brna)
RNA_def_property_struct_type(prop, "Material");
RNA_def_property_flag(prop, PROP_EDITABLE);
RNA_def_property_editable_func(prop, "rna_MaterialSlot_material_editable");
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_pointer_funcs(prop,
"rna_MaterialSlot_material_get",
"rna_MaterialSlot_material_set",
@@ -2163,8 +2170,12 @@ static void rna_def_material_slot(BlenderRNA *brna)
prop, "rna_MaterialSlot_name_get", "rna_MaterialSlot_name_length", NULL);
RNA_def_property_ui_text(prop, "Name", "Material slot name");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE);
+ RNA_def_property_override_clear_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_struct_name_property(srna, prop);
+ RNA_define_lib_overridable(false);
+
RNA_def_struct_path_func(srna, "rna_MaterialSlot_path");
}
@@ -2221,6 +2232,21 @@ static void rna_def_object_constraints(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
parm = RNA_def_int(func, "to_index", -1, INT_MIN, INT_MAX, "To Index", "Target index", 0, 10000);
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+
+ func = RNA_def_function(srna, "copy", "rna_Object_constraints_copy");
+ RNA_def_function_ui_description(func, "Add a new constraint that is a copy of the given one");
+ RNA_def_function_flag(func, FUNC_USE_MAIN);
+ /* constraint to copy */
+ parm = RNA_def_pointer(func,
+ "constraint",
+ "Constraint",
+ "",
+ "Constraint to copy - may belong to a different object");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
+ RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0);
+ /* return type */
+ parm = RNA_def_pointer(func, "new_constraint", "Constraint", "", "New constraint");
+ RNA_def_function_return(func, parm);
}
/* object.modifiers */
@@ -2521,11 +2547,15 @@ static void rna_def_object_display(BlenderRNA *brna)
RNA_def_struct_nested(brna, srna, "Object");
RNA_def_struct_path_func(srna, "rna_ObjectDisplay_path");
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "show_shadows", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_negative_sdna(prop, NULL, "dtx", OB_DRAW_NO_SHADOW_CAST);
RNA_def_property_boolean_default(prop, true);
RNA_def_property_ui_text(prop, "Shadow", "Object cast shadows in the 3d viewport");
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL);
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_object(BlenderRNA *brna)
@@ -2572,12 +2602,13 @@ static void rna_def_object(BlenderRNA *brna)
RNA_def_struct_clear_flag(srna, STRUCT_ID_REFCOUNT);
RNA_def_struct_ui_icon(srna, ICON_OBJECT_DATA);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "data", PROP_POINTER, PROP_NONE);
RNA_def_property_struct_type(prop, "ID");
RNA_def_property_pointer_funcs(
prop, NULL, "rna_Object_data_set", "rna_Object_data_typef", "rna_Object_data_poll");
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_NEVER_UNLINK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Data", "Object data");
RNA_def_property_update(prop, 0, "rna_Object_internal_update_data_dependency");
@@ -2585,6 +2616,7 @@ static void rna_def_object(BlenderRNA *brna)
RNA_def_property_enum_sdna(prop, NULL, "type");
RNA_def_property_enum_items(prop, rna_enum_object_type_items);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_override_clear_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Type", "Type of Object");
RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_ID);
@@ -2598,6 +2630,7 @@ static void rna_def_object(BlenderRNA *brna)
prop = RNA_def_property(srna, "bound_box", PROP_FLOAT, PROP_NONE);
RNA_def_property_multi_array(prop, 2, boundbox_dimsize);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_override_clear_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_float_funcs(prop, "rna_Object_boundbox_get", NULL, NULL);
RNA_def_property_ui_text(
prop,
@@ -2609,7 +2642,6 @@ static void rna_def_object(BlenderRNA *brna)
prop = RNA_def_property(srna, "parent", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_funcs(prop, NULL, "rna_Object_parent_set", NULL, NULL);
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_override_funcs(prop, NULL, NULL, "rna_Object_parent_override_apply");
RNA_def_property_ui_text(prop, "Parent", "Parent Object");
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_dependency_update");
@@ -2662,11 +2694,13 @@ static void rna_def_object(BlenderRNA *brna)
/* proxy */
prop = RNA_def_property(srna, "proxy", PROP_POINTER, PROP_NONE);
RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON);
+ RNA_def_property_override_clear_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Proxy", "Library object this proxy object controls");
prop = RNA_def_property(srna, "proxy_collection", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "proxy_group");
RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON);
+ RNA_def_property_override_clear_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop, "Proxy Collection", "Library collection duplicator object this proxy object controls");
@@ -2674,8 +2708,7 @@ static void rna_def_object(BlenderRNA *brna)
prop = RNA_def_property(srna, "material_slots", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "mat", "totcol");
RNA_def_property_struct_type(prop, "MaterialSlot");
- RNA_def_property_override_flag(prop,
- PROPOVERRIDE_OVERRIDABLE_LIBRARY | PROPOVERRIDE_NO_PROP_NAME);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_PROP_NAME);
/* don't dereference pointer! */
RNA_def_property_collection_funcs(
prop, NULL, NULL, NULL, "rna_iterator_array_get", NULL, NULL, NULL, NULL);
@@ -2689,7 +2722,6 @@ static void rna_def_object(BlenderRNA *brna)
NULL,
"rna_MaterialSlot_material_poll");
RNA_def_property_flag(prop, PROP_EDITABLE);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_editable_func(prop, "rna_Object_active_material_editable");
RNA_def_property_ui_text(prop, "Active Material", "Active material being displayed");
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_MaterialSlot_update");
@@ -2697,7 +2729,6 @@ static void rna_def_object(BlenderRNA *brna)
prop = RNA_def_property(srna, "active_material_index", PROP_INT, PROP_UNSIGNED);
RNA_def_property_int_sdna(prop, NULL, "actcol");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_int_funcs(prop,
"rna_Object_active_material_index_get",
"rna_Object_active_material_index_set",
@@ -2709,7 +2740,6 @@ static void rna_def_object(BlenderRNA *brna)
prop = RNA_def_property(srna, "location", PROP_FLOAT, PROP_TRANSLATION);
RNA_def_property_float_sdna(prop, NULL, "loc");
RNA_def_property_editable_array_func(prop, "rna_Object_location_editable");
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Location", "Location of the object");
RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT);
RNA_def_property_update(prop, NC_OBJECT | ND_TRANSFORM, "rna_Object_internal_update");
@@ -2717,7 +2747,6 @@ static void rna_def_object(BlenderRNA *brna)
prop = RNA_def_property(srna, "rotation_quaternion", PROP_FLOAT, PROP_QUATERNION);
RNA_def_property_float_sdna(prop, NULL, "quat");
RNA_def_property_editable_array_func(prop, "rna_Object_rotation_4d_editable");
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Quaternion Rotation", "Rotation in Quaternions");
RNA_def_property_update(prop, NC_OBJECT | ND_TRANSFORM, "rna_Object_internal_update");
@@ -2730,7 +2759,6 @@ static void rna_def_object(BlenderRNA *brna)
prop, "rna_Object_rotation_axis_angle_get", "rna_Object_rotation_axis_angle_set", NULL);
RNA_def_property_editable_array_func(prop, "rna_Object_rotation_4d_editable");
RNA_def_property_float_array_default(prop, rna_default_axis_angle);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop, "Axis-Angle Rotation", "Angle of Rotation for Axis-Angle rotation representation");
RNA_def_property_update(prop, NC_OBJECT | ND_TRANSFORM, "rna_Object_internal_update");
@@ -2738,7 +2766,6 @@ static void rna_def_object(BlenderRNA *brna)
prop = RNA_def_property(srna, "rotation_euler", PROP_FLOAT, PROP_EULER);
RNA_def_property_float_sdna(prop, NULL, "rot");
RNA_def_property_editable_array_func(prop, "rna_Object_rotation_euler_editable");
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Euler Rotation", "Rotation in Eulers");
RNA_def_property_update(prop, NC_OBJECT | ND_TRANSFORM, "rna_Object_internal_update");
@@ -2751,7 +2778,6 @@ static void rna_def_object(BlenderRNA *brna)
prop = RNA_def_property(srna, "scale", PROP_FLOAT, PROP_XYZ);
RNA_def_property_flag(prop, PROP_PROPORTIONAL);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_editable_array_func(prop, "rna_Object_scale_editable");
RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, 3);
RNA_def_property_ui_text(prop, "Scale", "Scaling of the object");
@@ -2761,6 +2787,8 @@ static void rna_def_object(BlenderRNA *brna)
RNA_def_property_array(prop, 3);
/* Only as convenient helper for py API, and conflicts with animating scale. */
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON);
+ RNA_def_property_override_clear_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_float_funcs(
prop, "rna_Object_dimensions_get", "rna_Object_dimensions_set", NULL);
RNA_def_property_ui_range(prop, 0.0f, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT);
@@ -2859,12 +2887,16 @@ static void rna_def_object(BlenderRNA *brna)
RNA_def_property_float_sdna(prop, NULL, "obmat");
RNA_def_property_multi_array(prop, 2, rna_matrix_dimsize_4x4);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON);
+ RNA_def_property_override_clear_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Matrix World", "Worldspace transformation matrix");
RNA_def_property_update(prop, NC_OBJECT | ND_TRANSFORM, "rna_Object_matrix_world_update");
prop = RNA_def_property(srna, "matrix_local", PROP_FLOAT, PROP_MATRIX);
RNA_def_property_multi_array(prop, 2, rna_matrix_dimsize_4x4);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON);
+ RNA_def_property_override_clear_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop,
"Local Matrix",
@@ -2878,6 +2910,8 @@ static void rna_def_object(BlenderRNA *brna)
prop = RNA_def_property(srna, "matrix_basis", PROP_FLOAT, PROP_MATRIX);
RNA_def_property_multi_array(prop, 2, rna_matrix_dimsize_4x4);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON);
+ RNA_def_property_override_clear_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop,
"Input Matrix",
"Matrix access to location, rotation and scale (including deltas), "
@@ -2890,7 +2924,6 @@ static void rna_def_object(BlenderRNA *brna)
prop = RNA_def_property(srna, "matrix_parent_inverse", PROP_FLOAT, PROP_MATRIX);
RNA_def_property_float_sdna(prop, NULL, "parentinv");
RNA_def_property_multi_array(prop, 2, rna_matrix_dimsize_4x4);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop, "Parent Inverse Matrix", "Inverse of object's parent matrix at time of parenting");
RNA_def_property_update(prop, NC_OBJECT | ND_TRANSFORM, "rna_Object_internal_update");
@@ -2901,8 +2934,7 @@ static void rna_def_object(BlenderRNA *brna)
RNA_def_property_ui_text(
prop, "Modifiers", "Modifiers affecting the geometric data of the object");
RNA_def_property_override_funcs(prop, NULL, NULL, "rna_Object_modifiers_override_apply");
- RNA_def_property_override_flag(
- prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY | PROPOVERRIDE_LIBRARY_INSERTION);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_LIBRARY_INSERTION);
rna_def_object_modifiers(brna, prop);
/* Grease Pencil modifiers. */
@@ -2913,8 +2945,7 @@ static void rna_def_object(BlenderRNA *brna)
prop, "Grease Pencil Modifiers", "Modifiers affecting the data of the grease pencil object");
RNA_def_property_override_funcs(
prop, NULL, NULL, "rna_Object_greasepencil_modifiers_override_apply");
- RNA_def_property_override_flag(
- prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY | PROPOVERRIDE_LIBRARY_INSERTION);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_LIBRARY_INSERTION);
rna_def_object_grease_pencil_modifiers(brna, prop);
/* Shader FX. */
@@ -2922,13 +2953,14 @@ static void rna_def_object(BlenderRNA *brna)
RNA_def_property_collection_sdna(prop, NULL, "shader_fx", NULL);
RNA_def_property_struct_type(prop, "ShaderFx");
RNA_def_property_ui_text(prop, "Shader Effects", "Effects affecting display of object");
+ RNA_define_lib_overridable(false);
rna_def_object_shaderfxs(brna, prop);
+ RNA_define_lib_overridable(true);
/* constraints */
prop = RNA_def_property(srna, "constraints", PROP_COLLECTION, PROP_NONE);
RNA_def_property_struct_type(prop, "Constraint");
- RNA_def_property_override_flag(
- prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY | PROPOVERRIDE_LIBRARY_INSERTION);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_LIBRARY_INSERTION);
RNA_def_property_ui_text(
prop, "Constraints", "Constraints affecting the transformation of the object");
RNA_def_property_override_funcs(prop, NULL, NULL, "rna_Object_constraints_override_apply");
@@ -2942,6 +2974,7 @@ static void rna_def_object(BlenderRNA *brna)
prop = RNA_def_property(srna, "vertex_groups", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "defbase", NULL);
RNA_def_property_struct_type(prop, "VertexGroup");
+ RNA_def_property_override_clear_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Vertex Groups", "Vertex groups of the object");
rna_def_object_vertex_groups(brna, prop);
@@ -2949,6 +2982,7 @@ static void rna_def_object(BlenderRNA *brna)
prop = RNA_def_property(srna, "face_maps", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "fmaps", NULL);
RNA_def_property_struct_type(prop, "FaceMap");
+ RNA_def_property_override_clear_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Face Maps", "Maps of faces of the object");
rna_def_object_face_maps(brna, prop);
@@ -3083,7 +3117,6 @@ static void rna_def_object(BlenderRNA *brna)
/* restrict */
prop = RNA_def_property(srna, "hide_viewport", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "restrictflag", OB_RESTRICT_VIEWPORT);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Disable in Viewports", "Globally disable in viewports");
RNA_def_property_ui_icon(prop, ICON_RESTRICT_VIEW_OFF, -1);
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_hide_update");
@@ -3091,14 +3124,12 @@ static void rna_def_object(BlenderRNA *brna)
prop = RNA_def_property(srna, "hide_select", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "restrictflag", OB_RESTRICT_SELECT);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Disable Selection", "Disable selection in viewport");
RNA_def_property_ui_icon(prop, ICON_RESTRICT_SELECT_OFF, -1);
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_hide_update");
prop = RNA_def_property(srna, "hide_render", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "restrictflag", OB_RESTRICT_RENDER);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Disable in Renders", "Globally disable in renders");
RNA_def_property_ui_icon(prop, ICON_RESTRICT_RENDER_OFF, -1);
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_hide_update");
@@ -3115,12 +3146,6 @@ static void rna_def_object(BlenderRNA *brna)
RNA_def_property_update(
prop, NC_OBJECT | ND_DRAW, "rna_Object_duplicator_visibility_flag_update");
- /* anim */
- rna_def_animdata_common(srna);
-
- rna_def_animviz_common(srna);
- rna_def_motionpath_common(srna);
-
/* instancing */
prop = RNA_def_property(srna, "instance_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_bitflag_sdna(prop, NULL, "transflag");
@@ -3150,7 +3175,6 @@ static void rna_def_object(BlenderRNA *brna)
RNA_def_property_struct_type(prop, "Collection");
RNA_def_property_pointer_sdna(prop, NULL, "instance_collection");
RNA_def_property_flag(prop, PROP_EDITABLE);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_pointer_funcs(prop, NULL, "rna_Object_dup_collection_set", NULL, NULL);
RNA_def_property_ui_text(prop, "Instance Collection", "Instance an existing collection");
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_dependency_update");
@@ -3158,6 +3182,8 @@ static void rna_def_object(BlenderRNA *brna)
prop = RNA_def_property(srna, "is_instancer", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "transflag", OB_DUPLI);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON);
+ RNA_def_property_override_clear_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
/* drawing */
prop = RNA_def_property(srna, "display_type", PROP_ENUM, PROP_NONE);
@@ -3229,7 +3255,6 @@ static void rna_def_object(BlenderRNA *brna)
prop = RNA_def_property(srna, "pose", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "pose");
RNA_def_property_struct_type(prop, "Pose");
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Pose", "Current pose for armatures");
/* shape keys */
@@ -3249,6 +3274,8 @@ static void rna_def_object(BlenderRNA *brna)
prop = RNA_def_property(srna, "active_shape_key", PROP_POINTER, PROP_NONE);
RNA_def_property_struct_type(prop, "ShapeKey");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE | PROPOVERRIDE_NO_COMPARISON);
+ RNA_def_property_override_clear_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_pointer_funcs(prop, "rna_Object_active_shape_key_get", NULL, NULL, NULL);
RNA_def_property_ui_text(prop, "Active Shape Key", "Current shape key");
@@ -3273,11 +3300,15 @@ static void rna_def_object(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "base_flag", BASE_FROM_DUPLI);
RNA_def_property_ui_text(prop, "Base from Instancer", "Object comes from a instancer");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON);
+ RNA_def_property_override_clear_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
prop = RNA_def_property(srna, "is_from_set", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "base_flag", BASE_FROM_SET);
RNA_def_property_ui_text(prop, "Base from Set", "Object comes from a background set");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON);
+ RNA_def_property_override_clear_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
/* Object Display */
prop = RNA_def_property(srna, "display", PROP_POINTER, PROP_NONE);
@@ -3286,6 +3317,14 @@ static void rna_def_object(BlenderRNA *brna)
RNA_def_property_pointer_funcs(prop, "rna_Object_display_get", NULL, NULL, NULL);
RNA_def_property_ui_text(prop, "Object Display", "Object display settings for 3d viewport");
+ RNA_define_lib_overridable(false);
+
+ /* anim */
+ rna_def_animdata_common(srna);
+
+ rna_def_animviz_common(srna);
+ rna_def_motionpath_common(srna);
+
RNA_api_object(srna);
}
diff --git a/source/blender/makesrna/intern/rna_object_force.c b/source/blender/makesrna/intern/rna_object_force.c
index 30e0ba19608..0a4e714c2ef 100644
--- a/source/blender/makesrna/intern/rna_object_force.c
+++ b/source/blender/makesrna/intern/rna_object_force.c
@@ -1517,6 +1517,15 @@ static void rna_def_field(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Flow", "Convert effector force into air flow velocity");
RNA_def_property_update(prop, 0, "rna_FieldSettings_update");
+ prop = RNA_def_property(srna, "wind_factor", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_float_sdna(prop, NULL, "f_wind_factor");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(
+ prop,
+ "Wind Factor",
+ "How much the force is reduced when acting parallel to a surface, e.g. cloth");
+ RNA_def_property_update(prop, 0, "rna_FieldSettings_update");
+
/* different ui range to above */
prop = RNA_def_property(srna, "inflow", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "f_flow");
diff --git a/source/blender/makesrna/intern/rna_pose.c b/source/blender/makesrna/intern/rna_pose.c
index 8f28fc56712..8dd3670087a 100644
--- a/source/blender/makesrna/intern/rna_pose.c
+++ b/source/blender/makesrna/intern/rna_pose.c
@@ -637,6 +637,21 @@ static void rna_PoseChannel_constraints_move(
WM_main_add_notifier(NC_OBJECT | ND_CONSTRAINT, ob);
}
+static bConstraint *rna_PoseChannel_constraints_copy(ID *id,
+ bPoseChannel *pchan,
+ Main *bmain,
+ PointerRNA *con_ptr)
+{
+ Object *ob = (Object *)id;
+ bConstraint *con = con_ptr->data;
+ bConstraint *new_con = BKE_constraint_copy_for_pose(ob, pchan, con);
+
+ ED_object_constraint_dependency_tag_update(bmain, ob, new_con);
+ WM_main_add_notifier(NC_OBJECT | ND_CONSTRAINT | NA_ADDED, id);
+
+ return new_con;
+}
+
bool rna_PoseChannel_constraints_override_apply(Main *UNUSED(bmain),
PointerRNA *ptr_dst,
PointerRNA *ptr_src,
@@ -978,6 +993,21 @@ static void rna_def_pose_channel_constraints(BlenderRNA *brna, PropertyRNA *cpro
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
parm = RNA_def_int(func, "to_index", -1, INT_MIN, INT_MAX, "To Index", "Target index", 0, 10000);
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+
+ func = RNA_def_function(srna, "copy", "rna_PoseChannel_constraints_copy");
+ RNA_def_function_ui_description(func, "Add a new constraint that is a copy of the given one");
+ RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_SELF_ID);
+ /* constraint to copy */
+ parm = RNA_def_pointer(func,
+ "constraint",
+ "Constraint",
+ "",
+ "Constraint to copy - may belong to a different object");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
+ RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0);
+ /* return type */
+ parm = RNA_def_pointer(func, "new_constraint", "Constraint", "", "New constraint");
+ RNA_def_function_return(func, parm);
}
static void rna_def_pose_channel(BlenderRNA *brna)
diff --git a/source/blender/makesrna/intern/rna_render.c b/source/blender/makesrna/intern/rna_render.c
index 6af031eb7b0..a41abb1a1dd 100644
--- a/source/blender/makesrna/intern/rna_render.c
+++ b/source/blender/makesrna/intern/rna_render.c
@@ -299,7 +299,7 @@ static void rna_RenderEngine_unregister(Main *bmain, StructRNA *type)
BLI_freelinkN(&R_engines, et);
/* Stop all renders in case we were using this one. */
- ED_render_engine_changed(bmain);
+ ED_render_engine_changed(bmain, false);
}
static StructRNA *rna_RenderEngine_register(Main *bmain,
diff --git a/source/blender/makesrna/intern/rna_rna.c b/source/blender/makesrna/intern/rna_rna.c
index d7e93975d31..623b5864f5b 100644
--- a/source/blender/makesrna/intern/rna_rna.c
+++ b/source/blender/makesrna/intern/rna_rna.c
@@ -1133,6 +1133,7 @@ static int rna_BlenderRNA_structs_lookup_string(PointerRNA *ptr,
* (if they are IDs, or have different names or RNA type, then this would be meaningless). */
static bool rna_property_override_diff_propptr_validate_diffing(PointerRNA *propptr_a,
PointerRNA *propptr_b,
+ const bool no_ownership,
const bool no_prop_name,
bool *r_is_id,
bool *r_is_null,
@@ -1172,7 +1173,7 @@ static bool rna_property_override_diff_propptr_validate_diffing(PointerRNA *prop
*r_is_id = RNA_struct_is_ID(propptr_a->type);
*r_is_null = (ELEM(NULL, propptr_b, propptr_b->type, propptr_b->data));
*r_is_type_diff = (propptr_b == NULL || propptr_b->type != propptr_a->type);
- is_valid_for_diffing = !(*r_is_id || *r_is_null);
+ is_valid_for_diffing = !((*r_is_id && no_ownership) || *r_is_null);
}
if (propptr_b == NULL || propptr_a->type != propptr_b->type) {
@@ -1248,6 +1249,7 @@ static int rna_property_override_diff_propptr(Main *bmain,
const bool no_prop_name,
IDOverrideLibrary *override,
const char *rna_path,
+ size_t rna_path_len,
const char *rna_itemname_a,
const char *rna_itemname_b,
const int rna_itemindex_a,
@@ -1265,6 +1267,7 @@ static int rna_property_override_diff_propptr(Main *bmain,
* so no point in going inside of it at all! */
bool is_valid_for_diffing = rna_property_override_diff_propptr_validate_diffing(propptr_a,
propptr_b,
+ no_ownership,
no_prop_name,
&is_id,
&is_null,
@@ -1278,7 +1281,7 @@ static int rna_property_override_diff_propptr(Main *bmain,
if (is_id) {
/* For now, once we deal with nodetrees we'll want to get rid of that one. */
- BLI_assert(no_ownership);
+ // BLI_assert(no_ownership);
}
if (override) {
@@ -1324,16 +1327,7 @@ static int rna_property_override_diff_propptr(Main *bmain,
char extended_rna_path_buffer[RNA_PATH_BUFFSIZE];
char *extended_rna_path = extended_rna_path_buffer;
-
-# define RNA_PATH_PRINTF(_str, ...) \
- if (BLI_snprintf(extended_rna_path_buffer, RNA_PATH_BUFFSIZE, (_str), __VA_ARGS__) >= \
- RNA_PATH_BUFFSIZE - 1) { \
- extended_rna_path = BLI_sprintfN((_str), __VA_ARGS__); \
- } \
- (void)0
-# define RNA_PATH_FREE() \
- if (extended_rna_path != extended_rna_path_buffer && extended_rna_path != rna_path) \
- MEM_freeN(extended_rna_path)
+ size_t extended_rna_path_len = 0;
/* There may be a propname defined in some cases, while no actual name set
* (e.g. happens with point cache), in that case too we want to fall back to index.
@@ -1342,31 +1336,82 @@ static int rna_property_override_diff_propptr(Main *bmain,
if ((rna_itemname_a != NULL && rna_itemname_a[0] != '\0') &&
(rna_itemname_b != NULL && rna_itemname_b[0] != '\0')) {
BLI_assert(STREQ(rna_itemname_a, rna_itemname_b));
+
char esc_item_name[RNA_PATH_BUFFSIZE];
- BLI_strescape(esc_item_name, rna_itemname_a, RNA_PATH_BUFFSIZE);
- RNA_PATH_PRINTF("%s[\"%s\"]", rna_path, esc_item_name);
+ const size_t esc_item_name_len = BLI_strescape(
+ esc_item_name, rna_itemname_a, RNA_PATH_BUFFSIZE);
+ extended_rna_path_len = rna_path_len + 2 + esc_item_name_len + 2;
+ if (extended_rna_path_len >= RNA_PATH_BUFFSIZE) {
+ extended_rna_path = MEM_mallocN(extended_rna_path_len + 1, __func__);
+ }
+
+ memcpy(extended_rna_path, rna_path, rna_path_len);
+ extended_rna_path[rna_path_len] = '[';
+ extended_rna_path[rna_path_len + 1] = '"';
+ memcpy(extended_rna_path + rna_path_len + 2, esc_item_name, esc_item_name_len);
+ extended_rna_path[rna_path_len + 2 + esc_item_name_len] = '"';
+ extended_rna_path[rna_path_len + 2 + esc_item_name_len + 1] = ']';
+ extended_rna_path[extended_rna_path_len] = '\0';
}
else if (rna_itemindex_a != -1) { /* Based on index... */
BLI_assert(rna_itemindex_a == rna_itemindex_b);
- RNA_PATH_PRINTF("%s[%d]", rna_path, rna_itemindex_a);
+
+ /* low-level specific highly-efficient conversion of positive integer to string. */
+ char item_index_buff[32];
+ size_t item_index_buff_len = 0;
+ if (rna_itemindex_a == 0) {
+ item_index_buff[0] = '0';
+ item_index_buff_len = 1;
+ }
+ else {
+ uint index;
+ for (index = rna_itemindex_a;
+ index > 0 && item_index_buff_len < sizeof(item_index_buff);
+ index /= 10) {
+ item_index_buff[item_index_buff_len++] = '0' + (char)(index % 10);
+ }
+ BLI_assert(index == 0);
+ }
+
+ extended_rna_path_len = rna_path_len + item_index_buff_len + 2;
+ if (extended_rna_path_len >= RNA_PATH_BUFFSIZE) {
+ extended_rna_path = MEM_mallocN(extended_rna_path_len + 1, __func__);
+ }
+
+ memcpy(extended_rna_path, rna_path, rna_path_len);
+ extended_rna_path[rna_path_len] = '[';
+ for (size_t i = 1; i <= item_index_buff_len; i++) {
+ /* The first loop above generated inverted string representation of our index number.
+ */
+ extended_rna_path[rna_path_len + i] = item_index_buff[item_index_buff_len - i];
+ }
+ extended_rna_path[rna_path_len + 1 + item_index_buff_len] = ']';
+ extended_rna_path[extended_rna_path_len] = '\0';
}
else {
extended_rna_path = (char *)rna_path;
+ extended_rna_path_len = rna_path_len;
}
}
eRNAOverrideMatchResult report_flags = 0;
- const bool match = RNA_struct_override_matches(
- bmain, propptr_a, propptr_b, extended_rna_path, override, flags, &report_flags);
+ const bool match = RNA_struct_override_matches(bmain,
+ propptr_a,
+ propptr_b,
+ extended_rna_path,
+ extended_rna_path_len,
+ override,
+ flags,
+ &report_flags);
if (r_override_changed && (report_flags & RNA_OVERRIDE_MATCH_RESULT_CREATED) != 0) {
*r_override_changed = true;
}
- RNA_PATH_FREE();
+ if (extended_rna_path != extended_rna_path_buffer && extended_rna_path != rna_path) {
+ MEM_freeN(extended_rna_path);
+ }
# undef RNA_PATH_BUFFSIZE
-# undef RNA_PATH_PRINTF
-# undef RNA_PATH_FREE
return !match;
}
@@ -1395,6 +1440,7 @@ int rna_property_override_diff_default(Main *bmain,
const int mode,
IDOverrideLibrary *override,
const char *rna_path,
+ const size_t rna_path_len,
const int flags,
bool *r_override_changed)
{
@@ -1684,6 +1730,7 @@ int rna_property_override_diff_default(Main *bmain,
no_prop_name,
override,
rna_path,
+ rna_path_len,
NULL,
NULL,
-1,
@@ -1700,6 +1747,7 @@ int rna_property_override_diff_default(Main *bmain,
const bool use_insertion = (RNA_property_override_flag(prop_a) &
PROPOVERRIDE_LIBRARY_INSERTION) &&
do_create;
+ const bool no_ownership = (RNA_property_flag(prop_a) & PROP_PTR_NO_OWNERSHIP) != 0;
const bool no_prop_name = (RNA_property_override_flag(prop_a) & PROPOVERRIDE_NO_PROP_NAME) !=
0;
bool equals = true;
@@ -1733,6 +1781,7 @@ int rna_property_override_diff_default(Main *bmain,
is_valid_for_diffing = rna_property_override_diff_propptr_validate_diffing(
&iter_a.ptr,
&iter_b.ptr,
+ no_ownership,
no_prop_name,
&is_id,
&is_null,
@@ -1750,6 +1799,7 @@ int rna_property_override_diff_default(Main *bmain,
/* We still need propname from 'a' item... */
rna_property_override_diff_propptr_validate_diffing(&iter_a.ptr,
NULL,
+ no_ownership,
no_prop_name,
&is_id,
&is_null,
@@ -1835,7 +1885,6 @@ int rna_property_override_diff_default(Main *bmain,
}
else if (is_id || is_valid_for_diffing) {
if (equals || do_create) {
- const bool no_ownership = (RNA_property_flag(prop_a) & PROP_PTR_NO_OWNERSHIP) != 0;
const int eq = rna_property_override_diff_propptr(bmain,
&iter_a.ptr,
&iter_b.ptr,
@@ -1844,6 +1893,7 @@ int rna_property_override_diff_default(Main *bmain,
no_prop_name,
override,
rna_path,
+ rna_path_len,
propname_a,
propname_b,
idx_a,
diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c
index edbf4c90524..d141e6f3b19 100644
--- a/source/blender/makesrna/intern/rna_scene.c
+++ b/source/blender/makesrna/intern/rna_scene.c
@@ -1662,7 +1662,7 @@ static void rna_RenderSettings_engine_update(Main *bmain,
Scene *UNUSED(unused),
PointerRNA *UNUSED(ptr))
{
- ED_render_engine_changed(bmain);
+ ED_render_engine_changed(bmain, true);
}
static bool rna_RenderSettings_multiple_engines_get(PointerRNA *UNUSED(ptr))
@@ -3090,6 +3090,13 @@ static void rna_def_tool_settings(BlenderRNA *brna)
prop, "Transform Parents", "Transform the parents, leaving the children in place");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL);
+ prop = RNA_def_property(srna, "use_transform_correct_face_attributes", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "uvcalc_flag", UVCALC_TRANSFORM_CORRECT);
+ RNA_def_property_ui_text(prop,
+ "Correct Face Attributes",
+ "Correct data such as UV's and vertex colors when transforming");
+ RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
+
prop = RNA_def_property(srna, "use_mesh_automerge", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "automerge", AUTO_MERGE);
RNA_def_property_ui_text(
@@ -7150,12 +7157,6 @@ static void rna_def_scene_eevee(BlenderRNA *brna)
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
- prop = RNA_def_property(srna, "motion_blur_samples", PROP_INT, PROP_UNSIGNED);
- RNA_def_property_ui_text(prop, "Samples", "Number of samples to take with motion blur");
- RNA_def_property_range(prop, 1, 64);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
- RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
-
prop = RNA_def_property(srna, "motion_blur_shutter", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_ui_text(prop, "Shutter", "Time taken in frames between shutter open and close");
RNA_def_property_range(prop, 0.0f, FLT_MAX);
@@ -7163,6 +7164,33 @@ static void rna_def_scene_eevee(BlenderRNA *brna)
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+ prop = RNA_def_property(srna, "motion_blur_depth_scale", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_ui_text(prop,
+ "Background Separation",
+ "Lower values will reduce background"
+ " bleeding onto foreground elements");
+ RNA_def_property_range(prop, 0.0f, FLT_MAX);
+ RNA_def_property_ui_range(prop, 0.01f, 1000.0f, 1, 2);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+
+ prop = RNA_def_property(srna, "motion_blur_max", PROP_INT, PROP_PIXEL);
+ RNA_def_property_ui_text(prop, "Max Blur", "Maximum blur distance a pixel can spread over");
+ RNA_def_property_range(prop, 0, 2048);
+ RNA_def_property_ui_range(prop, 0, 512, 1, -1);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+
+ prop = RNA_def_property(srna, "motion_blur_steps", PROP_INT, PROP_NONE);
+ RNA_def_property_ui_text(prop,
+ "Motion steps",
+ "Controls accuracy of motion blur, "
+ "more steps means longer render time");
+ RNA_def_property_range(prop, 1, INT_MAX);
+ RNA_def_property_ui_range(prop, 1, 64, 1, -1);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+
/* Shadows */
prop = RNA_def_property(srna, "shadow_cube_size", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, eevee_shadow_size_items);
@@ -7469,6 +7497,7 @@ void RNA_def_scene(BlenderRNA *brna)
/* Nodes (Compositing) */
prop = RNA_def_property(srna, "node_tree", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "nodetree");
+ RNA_def_property_clear_flag(prop, PROP_PTR_NO_OWNERSHIP);
RNA_def_property_ui_text(prop, "Node Tree", "Compositing node tree");
prop = RNA_def_property(srna, "use_nodes", PROP_BOOLEAN, PROP_NONE);
@@ -7699,6 +7728,7 @@ void RNA_def_scene(BlenderRNA *brna)
RNA_def_property_flag(prop, PROP_NEVER_NULL);
RNA_def_property_pointer_sdna(prop, NULL, "master_collection");
RNA_def_property_struct_type(prop, "Collection");
+ RNA_def_property_clear_flag(prop, PROP_PTR_NO_OWNERSHIP);
RNA_def_property_ui_text(
prop,
"Collection",
diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c
index 381908f7ada..8532bf9afab 100644
--- a/source/blender/makesrna/intern/rna_sculpt_paint.c
+++ b/source/blender/makesrna/intern/rna_sculpt_paint.c
@@ -763,12 +763,6 @@ static void rna_def_sculpt(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Lock Z", "Disallow changes to the Z axis of vertices");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
- prop = RNA_def_property(srna, "use_threaded", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "flags", SCULPT_USE_OPENMP);
- RNA_def_property_ui_text(
- prop, "Use OpenMP", "Take advantage of multiple CPU cores to improve sculpting performance");
- RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
-
prop = RNA_def_property(srna, "use_deform_only", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flags", SCULPT_ONLY_DEFORM);
RNA_def_property_ui_text(prop,
diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c
index 39ea054456f..7342879e9e6 100644
--- a/source/blender/makesrna/intern/rna_sequencer.c
+++ b/source/blender/makesrna/intern/rna_sequencer.c
@@ -2565,29 +2565,25 @@ static void rna_def_wipe(StructRNA *srna)
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_ui_text(
prop, "Blur Width", "Width of the blur edge, in percentage relative to the image size");
- RNA_def_property_update(
- prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
prop = RNA_def_property(srna, "angle", PROP_FLOAT, PROP_ANGLE);
RNA_def_property_range(prop, DEG2RADF(-90.0f), DEG2RADF(90.0f));
RNA_def_property_ui_text(prop, "Angle", "Edge angle");
- RNA_def_property_update(
- prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
prop = RNA_def_property(srna, "direction", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "forward");
RNA_def_property_enum_items(prop, wipe_direction_items);
RNA_def_property_ui_text(prop, "Direction", "Wipe direction");
- RNA_def_property_update(
- prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
prop = RNA_def_property(srna, "transition_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "wipetype");
RNA_def_property_enum_items(prop, wipe_type_items);
RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_SEQUENCE);
RNA_def_property_ui_text(prop, "Transition Type", "");
- RNA_def_property_update(
- prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
}
static void rna_def_glow(StructRNA *srna)
@@ -2600,42 +2596,36 @@ static void rna_def_glow(StructRNA *srna)
RNA_def_property_float_sdna(prop, NULL, "fMini");
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_ui_text(prop, "Threshold", "Minimum intensity to trigger a glow");
- RNA_def_property_update(
- prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
prop = RNA_def_property(srna, "clamp", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "fClamp");
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_ui_text(prop, "Clamp", "Brightness limit of intensity");
- RNA_def_property_update(
- prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
prop = RNA_def_property(srna, "boost_factor", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "fBoost");
RNA_def_property_range(prop, 0.0f, 10.0f);
RNA_def_property_ui_text(prop, "Boost Factor", "Brightness multiplier");
- RNA_def_property_update(
- prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
prop = RNA_def_property(srna, "blur_radius", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "dDist");
RNA_def_property_range(prop, 0.5f, 20.0f);
RNA_def_property_ui_text(prop, "Blur Distance", "Radius of glow effect");
- RNA_def_property_update(
- prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
prop = RNA_def_property(srna, "quality", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "dQuality");
RNA_def_property_range(prop, 1, 5);
RNA_def_property_ui_text(prop, "Quality", "Accuracy of the blur effect");
- RNA_def_property_update(
- prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
prop = RNA_def_property(srna, "use_only_boost", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "bNoComp", 0);
RNA_def_property_ui_text(prop, "Only Boost", "Show the glow buffer only");
- RNA_def_property_update(
- prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
}
static void rna_def_transform(StructRNA *srna)
@@ -2661,57 +2651,49 @@ static void rna_def_transform(StructRNA *srna)
RNA_def_property_float_sdna(prop, NULL, "ScalexIni");
RNA_def_property_ui_text(prop, "Scale X", "Amount to scale the input in the X axis");
RNA_def_property_ui_range(prop, 0, 10, 3, 6);
- RNA_def_property_update(
- prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
prop = RNA_def_property(srna, "scale_start_y", PROP_FLOAT, PROP_UNSIGNED);
RNA_def_property_float_sdna(prop, NULL, "ScaleyIni");
RNA_def_property_ui_text(prop, "Scale Y", "Amount to scale the input in the Y axis");
RNA_def_property_ui_range(prop, 0, 10, 3, 6);
- RNA_def_property_update(
- prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
prop = RNA_def_property(srna, "use_uniform_scale", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "uniform_scale", 0);
RNA_def_property_ui_text(prop, "Uniform Scale", "Scale uniformly, preserving aspect ratio");
- RNA_def_property_update(
- prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
prop = RNA_def_property(srna, "translate_start_x", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "xIni");
RNA_def_property_ui_text(prop, "Translate X", "Amount to move the input on the X axis");
RNA_def_property_ui_range(prop, -4000.0f, 4000.0f, 3, 6);
- RNA_def_property_update(
- prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
prop = RNA_def_property(srna, "translate_start_y", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "yIni");
RNA_def_property_ui_text(prop, "Translate Y", "Amount to move the input on the Y axis");
RNA_def_property_ui_range(prop, -4000.0f, 4000.0f, 3, 6);
- RNA_def_property_update(
- prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
prop = RNA_def_property(srna, "rotation_start", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "rotIni");
RNA_def_property_ui_text(prop, "Rotation", "Degrees to rotate the input");
- RNA_def_property_update(
- prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
prop = RNA_def_property(srna, "translation_unit", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "percent");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); /* not meant to be animated */
RNA_def_property_enum_items(prop, translation_unit_items);
RNA_def_property_ui_text(prop, "Translation Unit", "Unit of measure to translate the input");
- RNA_def_property_update(
- prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
prop = RNA_def_property(srna, "interpolation", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, interpolation_items);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); /* not meant to be animated */
RNA_def_property_ui_text(
prop, "Interpolation", "Method to determine how missing pixels are created");
- RNA_def_property_update(
- prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
}
static void rna_def_solid_color(StructRNA *srna)
@@ -2738,29 +2720,25 @@ static void rna_def_speed_control(StructRNA *srna)
RNA_def_property_ui_text(
prop, "Multiply Speed", "Multiply the resulting speed after the speed factor");
RNA_def_property_ui_range(prop, 0.0f, 100.0f, 1, -1);
- RNA_def_property_update(
- prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
prop = RNA_def_property(srna, "use_as_speed", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flags", SEQ_SPEED_INTEGRATE);
RNA_def_property_ui_text(
prop, "Use as Speed", "Interpret the value as speed instead of a frame number");
- RNA_def_property_update(
- prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
prop = RNA_def_property(srna, "use_scale_to_length", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flags", SEQ_SPEED_COMPRESS_IPO_Y);
RNA_def_property_ui_text(
prop, "Scale to Length", "Scale values from 0.0 to 1.0 to target sequence length");
- RNA_def_property_update(
- prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
prop = RNA_def_property(srna, "frame_interpolation_mode", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flags", SEQ_SPEED_USE_INTERPOLATION);
RNA_def_property_ui_text(
prop, "Frame interpolation", "Do crossfade blending between current and next frame");
- RNA_def_property_update(
- prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
}
static void rna_def_gaussian_blur(StructRNA *srna)
@@ -2771,14 +2749,12 @@ static void rna_def_gaussian_blur(StructRNA *srna)
prop = RNA_def_property(srna, "size_x", PROP_FLOAT, PROP_UNSIGNED);
RNA_def_property_ui_text(prop, "Size X", "Size of the blur along X axis");
RNA_def_property_ui_range(prop, 0.0f, FLT_MAX, 1, -1);
- RNA_def_property_update(
- prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
prop = RNA_def_property(srna, "size_y", PROP_FLOAT, PROP_UNSIGNED);
RNA_def_property_ui_text(prop, "Size Y", "Size of the blur along Y axis");
RNA_def_property_ui_range(prop, 0.0f, FLT_MAX, 1, -1);
- RNA_def_property_update(
- prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
}
static void rna_def_text(StructRNA *srna)
@@ -2807,71 +2783,61 @@ static void rna_def_text(StructRNA *srna)
RNA_def_property_ui_text(prop, "Font", "Font of the text. Falls back to the UI font by default");
RNA_def_property_flag(prop, PROP_EDITABLE);
RNA_def_property_pointer_funcs(prop, NULL, "rna_Sequence_text_font_set", NULL, NULL);
- RNA_def_property_update(
- prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
prop = RNA_def_property(srna, "font_size", PROP_INT, PROP_UNSIGNED);
RNA_def_property_int_sdna(prop, NULL, "text_size");
RNA_def_property_ui_text(prop, "Size", "Size of the text");
RNA_def_property_range(prop, 0.0, 2000);
RNA_def_property_ui_range(prop, 0.0f, 1000, 1, -1);
- RNA_def_property_update(
- prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR_GAMMA);
RNA_def_property_float_sdna(prop, NULL, "color");
RNA_def_property_ui_text(prop, "Color", "Text color");
- RNA_def_property_update(
- prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
prop = RNA_def_property(srna, "shadow_color", PROP_FLOAT, PROP_COLOR_GAMMA);
RNA_def_property_float_sdna(prop, NULL, "shadow_color");
RNA_def_property_ui_text(prop, "Shadow Color", "");
- RNA_def_property_update(
- prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
prop = RNA_def_property(srna, "location", PROP_FLOAT, PROP_XYZ);
RNA_def_property_float_sdna(prop, NULL, "loc");
RNA_def_property_ui_text(prop, "Location", "Location of the text");
RNA_def_property_range(prop, -FLT_MAX, FLT_MAX);
RNA_def_property_ui_range(prop, 0.0, 1.0, 1, -1);
- RNA_def_property_update(
- prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
prop = RNA_def_property(srna, "wrap_width", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "wrap_width");
RNA_def_property_ui_text(prop, "Wrap Width", "Word wrap width as factor, zero disables");
RNA_def_property_range(prop, 0, FLT_MAX);
RNA_def_property_ui_range(prop, 0.0, 1.0, 1, -1);
- RNA_def_property_update(
- prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
prop = RNA_def_property(srna, "align_x", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "align");
RNA_def_property_enum_items(prop, text_align_x_items);
RNA_def_property_ui_text(
prop, "Align X", "Align the text along the X axis, relative to the text bounds");
- RNA_def_property_update(
- prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
prop = RNA_def_property(srna, "align_y", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "align_y");
RNA_def_property_enum_items(prop, text_align_y_items);
RNA_def_property_ui_text(
prop, "Align Y", "Align the text along the Y axis, relative to the text bounds");
- RNA_def_property_update(
- prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
prop = RNA_def_property(srna, "text", PROP_STRING, PROP_NONE);
RNA_def_property_ui_text(prop, "Text", "Text that will be displayed");
- RNA_def_property_update(
- prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
prop = RNA_def_property(srna, "use_shadow", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_TEXT_SHADOW);
RNA_def_property_ui_text(prop, "Shadow", "Display shadow behind text");
- RNA_def_property_update(
- prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
}
static void rna_def_color_mix(StructRNA *srna)
@@ -2914,15 +2880,13 @@ static void rna_def_color_mix(StructRNA *srna)
RNA_def_property_enum_items(prop, blend_color_items);
RNA_def_property_ui_text(
prop, "Blending Mode", "Method for controlling how the strip combines with other strips");
- RNA_def_property_update(
- prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
prop = RNA_def_property(srna, "factor", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_ui_text(
prop, "Blend Factor", "Percentage of how much the strip's colors affect other strips");
- RNA_def_property_update(
- prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
}
static EffectInfo def_effects[] = {
diff --git a/source/blender/makesrna/intern/rna_sequencer_api.c b/source/blender/makesrna/intern/rna_sequencer_api.c
index 6c8f51f97a1..28603132c77 100644
--- a/source/blender/makesrna/intern/rna_sequencer_api.c
+++ b/source/blender/makesrna/intern/rna_sequencer_api.c
@@ -114,7 +114,7 @@ static Sequence *rna_Sequences_new_clip(ID *id,
Scene *scene = (Scene *)id;
Sequence *seq;
- seq = alloc_generic_sequence(ed, name, frame_start, channel, SEQ_TYPE_MOVIECLIP, clip->name);
+ seq = alloc_generic_sequence(ed, name, frame_start, channel, SEQ_TYPE_MOVIECLIP, clip->filepath);
seq->clip = clip;
seq->len = BKE_movieclip_get_duration(clip);
id_us_plus((ID *)clip);
@@ -265,7 +265,8 @@ static Sequence *rna_Sequences_new_sound(ID *id,
BKE_report(reports, RPT_ERROR, "Sequences.new_sound: unable to open sound file");
return NULL;
}
- seq = alloc_generic_sequence(ed, name, frame_start, channel, SEQ_TYPE_SOUND_RAM, sound->name);
+ seq = alloc_generic_sequence(
+ ed, name, frame_start, channel, SEQ_TYPE_SOUND_RAM, sound->filepath);
seq->sound = sound;
seq->len = ceil((double)info.length * FPS);
@@ -447,6 +448,21 @@ static void rna_SequenceElements_pop(ID *id, Sequence *seq, ReportList *reports,
WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, scene);
}
+static void rna_Sequence_invalidate_cache_rnafunc(ID *id, Sequence *self, int type)
+{
+ switch (type) {
+ case SEQ_CACHE_STORE_RAW:
+ BKE_sequence_invalidate_cache_raw((Scene *)id, self);
+ break;
+ case SEQ_CACHE_STORE_PREPROCESSED:
+ BKE_sequence_invalidate_cache_preprocessed((Scene *)id, self);
+ break;
+ case SEQ_CACHE_STORE_COMPOSITE:
+ BKE_sequence_invalidate_cache_composite((Scene *)id, self);
+ break;
+ }
+}
+
#else
void RNA_api_sequence_strip(StructRNA *srna)
@@ -454,6 +470,13 @@ void RNA_api_sequence_strip(StructRNA *srna)
FunctionRNA *func;
PropertyRNA *parm;
+ static const EnumPropertyItem seq_cahce_type_items[] = {
+ {SEQ_CACHE_STORE_RAW, "RAW", 0, "Raw", ""},
+ {SEQ_CACHE_STORE_PREPROCESSED, "PREPROCESSED", 0, "Preprocessed", ""},
+ {SEQ_CACHE_STORE_COMPOSITE, "COMPOSITE", 0, "Composite", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+
func = RNA_def_function(srna, "update", "rna_Sequence_update_rnafunc");
RNA_def_function_flag(func, FUNC_USE_SELF_ID);
RNA_def_function_ui_description(func, "Update the strip dimensions");
@@ -479,6 +502,13 @@ void RNA_api_sequence_strip(StructRNA *srna)
RNA_def_function_flag(func, FUNC_USE_REPORTS);
parm = RNA_def_pointer(func, "other", "Sequence", "Other", "");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+
+ func = RNA_def_function(srna, "invalidate_cache", "rna_Sequence_invalidate_cache_rnafunc");
+ RNA_def_function_flag(func, FUNC_USE_SELF_ID);
+ RNA_def_function_ui_description(func,
+ "Invalidate Cached images for strip and all dependant strips. ");
+ parm = RNA_def_enum(func, "type", seq_cahce_type_items, 0, "Type", "Cache Type");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
}
void RNA_api_sequence_elements(BlenderRNA *brna, PropertyRNA *cprop)
diff --git a/source/blender/makesrna/intern/rna_shader_fx.c b/source/blender/makesrna/intern/rna_shader_fx.c
index 71f767fa93b..80f3cab147c 100644
--- a/source/blender/makesrna/intern/rna_shader_fx.c
+++ b/source/blender/makesrna/intern/rna_shader_fx.c
@@ -336,11 +336,9 @@ static void rna_def_shader_fx_pixel(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Size", "Pixel size");
RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
- prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR);
- RNA_def_property_range(prop, 0.0, 1.0);
- RNA_def_property_float_sdna(prop, NULL, "rgba");
- RNA_def_property_array(prop, 4);
- RNA_def_property_ui_text(prop, "Color", "Color used for lines");
+ prop = RNA_def_property(srna, "use_antialiasing", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", FX_PIXEL_FILTER_NEAREST);
+ RNA_def_property_ui_text(prop, "Antialiasing", "Antialiase pixels");
RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
}
@@ -424,7 +422,7 @@ static void rna_def_shader_fx_shadow(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Offset", "Offset of the shadow");
RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
- prop = RNA_def_property(srna, "scale", PROP_FLOAT, PROP_NONE);
+ prop = RNA_def_property(srna, "scale", PROP_FLOAT, PROP_XYZ);
RNA_def_property_float_sdna(prop, NULL, "scale");
RNA_def_property_range(prop, -FLT_MAX, FLT_MAX);
RNA_def_property_ui_text(prop, "Scale", "Offset of the shadow");
@@ -681,9 +679,9 @@ void RNA_def_shader_fx(BlenderRNA *brna)
prop = RNA_def_property(srna, "show_expanded", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE);
- RNA_def_property_boolean_sdna(prop, NULL, "mode", eShaderFxMode_Expanded);
+ RNA_def_property_boolean_sdna(prop, NULL, "ui_expand_flag", 0);
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
- RNA_def_property_ui_text(prop, "Expanded", "Set effect expanded in the user interface");
+ RNA_def_property_ui_text(prop, "Expanded", "Set effect expansion in the user interface");
RNA_def_property_ui_icon(prop, ICON_DISCLOSURE_TRI_RIGHT, 1);
/* types */
diff --git a/source/blender/makesrna/intern/rna_simulation.c b/source/blender/makesrna/intern/rna_simulation.c
index 789ea299feb..cc9a4bec2e7 100644
--- a/source/blender/makesrna/intern/rna_simulation.c
+++ b/source/blender/makesrna/intern/rna_simulation.c
@@ -42,6 +42,7 @@ static void rna_def_simulation(BlenderRNA *brna)
prop = RNA_def_property(srna, "node_tree", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "nodetree");
+ RNA_def_property_clear_flag(prop, PROP_PTR_NO_OWNERSHIP);
RNA_def_property_ui_text(prop, "Node Tree", "Node tree defining the simulation");
/* common */
diff --git a/source/blender/makesrna/intern/rna_sound.c b/source/blender/makesrna/intern/rna_sound.c
index 28a1c0f3cca..2fb50c2e89e 100644
--- a/source/blender/makesrna/intern/rna_sound.c
+++ b/source/blender/makesrna/intern/rna_sound.c
@@ -62,7 +62,7 @@ static void rna_def_sound(BlenderRNA *brna)
/*rna_def_ipo_common(srna); */
prop = RNA_def_property(srna, "filepath", PROP_STRING, PROP_FILEPATH);
- RNA_def_property_string_sdna(prop, NULL, "name");
+ RNA_def_property_string_sdna(prop, NULL, "filepath");
RNA_def_property_ui_text(prop, "File Path", "Sound sample file used by this Sound data-block");
RNA_def_property_update(prop, 0, "rna_Sound_update");
diff --git a/source/blender/makesrna/intern/rna_sound_api.c b/source/blender/makesrna/intern/rna_sound_api.c
index 418205426d2..82da0adf0fe 100644
--- a/source/blender/makesrna/intern/rna_sound_api.c
+++ b/source/blender/makesrna/intern/rna_sound_api.c
@@ -34,7 +34,8 @@
static void rna_Sound_pack(bSound *sound, Main *bmain, ReportList *reports)
{
- sound->packedfile = BKE_packedfile_new(reports, sound->name, ID_BLEND_PATH(bmain, &sound->id));
+ sound->packedfile = BKE_packedfile_new(
+ reports, sound->filepath, ID_BLEND_PATH(bmain, &sound->id));
}
static void rna_Sound_unpack(bSound *sound, Main *bmain, ReportList *reports, int method)
diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c
index e83a49ccb4d..bc8794dac2a 100644
--- a/source/blender/makesrna/intern/rna_space.c
+++ b/source/blender/makesrna/intern/rna_space.c
@@ -2184,7 +2184,6 @@ static void rna_SpaceNodeEditor_node_tree_update(const bContext *C, PointerRNA *
ED_node_tree_update(C);
}
-# ifdef WITH_NEW_SIMULATION_TYPE
static PointerRNA rna_SpaceNodeEditor_simulation_get(PointerRNA *ptr)
{
SpaceNode *snode = (SpaceNode *)ptr->data;
@@ -2216,7 +2215,6 @@ static void rna_SpaceNodeEditor_simulation_set(PointerRNA *ptr,
}
snode->id = &sim->id;
}
-# endif
static int rna_SpaceNodeEditor_tree_type_get(PointerRNA *ptr)
{
@@ -4363,12 +4361,10 @@ static void rna_def_space_view3d(BlenderRNA *brna)
{"Surface", (1 << OB_SURF), {"show_object_viewport_surf", "show_object_select_surf"}},
{"Meta", (1 << OB_MBALL), {"show_object_viewport_meta", "show_object_select_meta"}},
{"Font", (1 << OB_FONT), {"show_object_viewport_font", "show_object_select_font"}},
-# ifdef WITH_NEW_OBJECT_TYPES
{"Hair", (1 << OB_HAIR), {"show_object_viewport_hair", "show_object_select_hair"}},
{"Point Cloud",
(1 << OB_POINTCLOUD),
{"show_object_viewport_pointcloud", "show_object_select_pointcloud"}},
-# endif
{"Volume", (1 << OB_VOLUME), {"show_object_viewport_volume", "show_object_select_volume"}},
{"Armature",
(1 << OB_ARMATURE),
@@ -5580,9 +5576,7 @@ static void rna_def_fileselect_idfilter(BlenderRNA *brna)
"Grease Pencil",
"Show Grease pencil data-blocks"},
{FILTER_ID_GR, "filter_group", ICON_GROUP, "Collections", "Show Collection data-blocks"},
-# ifdef WITH_NEW_OBJECT_TYPES
{FILTER_ID_HA, "filter_hair", ICON_HAIR_DATA, "Hairs", "Show/hide Hair data-blocks"},
-# endif
{FILTER_ID_IM, "filter_image", ICON_IMAGE_DATA, "Images", "Show Image data-blocks"},
{FILTER_ID_LA, "filter_light", ICON_LIGHT_DATA, "Lights", "Show Light data-blocks"},
{FILTER_ID_LP,
@@ -5626,21 +5620,17 @@ static void rna_def_fileselect_idfilter(BlenderRNA *brna)
ICON_CURVE_BEZCURVE,
"Paint Curves",
"Show Paint Curve data-blocks"},
-# ifdef WITH_NEW_OBJECT_TYPES
{FILTER_ID_PT,
"filter_pointcloud",
ICON_POINTCLOUD_DATA,
"Point Clouds",
"Show/hide Point Cloud data-blocks"},
-# endif
{FILTER_ID_SCE, "filter_scene", ICON_SCENE_DATA, "Scenes", "Show Scene data-blocks"},
-# ifdef WITH_NEW_SIMULATION_TYPE
{FILTER_ID_SIM,
"filter_simulation",
ICON_PHYSICS,
"Simulations",
"Show Simulation data-blocks"}, /* TODO: Use correct icon. */
-# endif
{FILTER_ID_SPK, "filter_speaker", ICON_SPEAKER, "Speakers", "Show Speaker data-blocks"},
{FILTER_ID_SO, "filter_sound", ICON_SOUND, "Sounds", "Show Sound data-blocks"},
{FILTER_ID_TE, "filter_texture", ICON_TEXTURE_DATA, "Textures", "Show Texture data-blocks"},
@@ -6340,7 +6330,6 @@ static void rna_def_space_node(BlenderRNA *brna)
RNA_def_property_ui_text(
prop, "ID From", "Data-block from which the edited data-block is linked");
-# ifdef WITH_NEW_SIMULATION_TYPE
prop = RNA_def_property(srna, "simulation", PROP_POINTER, PROP_NONE);
RNA_def_property_flag(prop, PROP_EDITABLE);
RNA_def_property_struct_type(prop, "Simulation");
@@ -6351,7 +6340,6 @@ static void rna_def_space_node(BlenderRNA *brna)
NULL,
NULL);
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_NODE, NULL);
-# endif
prop = RNA_def_property(srna, "path", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "treepath", NULL);
diff --git a/source/blender/makesrna/intern/rna_text.c b/source/blender/makesrna/intern/rna_text.c
index c8797a8bc74..1351c027004 100644
--- a/source/blender/makesrna/intern/rna_text.c
+++ b/source/blender/makesrna/intern/rna_text.c
@@ -43,8 +43,8 @@ static void rna_Text_filename_get(PointerRNA *ptr, char *value)
{
Text *text = (Text *)ptr->data;
- if (text->name) {
- strcpy(value, text->name);
+ if (text->filepath) {
+ strcpy(value, text->filepath);
}
else {
value[0] = '\0';
@@ -54,22 +54,22 @@ static void rna_Text_filename_get(PointerRNA *ptr, char *value)
static int rna_Text_filename_length(PointerRNA *ptr)
{
Text *text = (Text *)ptr->data;
- return (text->name) ? strlen(text->name) : 0;
+ return (text->filepath) ? strlen(text->filepath) : 0;
}
static void rna_Text_filename_set(PointerRNA *ptr, const char *value)
{
Text *text = (Text *)ptr->data;
- if (text->name) {
- MEM_freeN(text->name);
+ if (text->filepath) {
+ MEM_freeN(text->filepath);
}
if (value[0]) {
- text->name = BLI_strdup(value);
+ text->filepath = BLI_strdup(value);
}
else {
- text->name = NULL;
+ text->filepath = NULL;
}
}
diff --git a/source/blender/makesrna/intern/rna_texture.c b/source/blender/makesrna/intern/rna_texture.c
index 9184c54ae88..4d5f0f6c2fa 100644
--- a/source/blender/makesrna/intern/rna_texture.c
+++ b/source/blender/makesrna/intern/rna_texture.c
@@ -1634,6 +1634,7 @@ static void rna_def_texture(BlenderRNA *brna)
prop = RNA_def_property(srna, "node_tree", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "nodetree");
+ RNA_def_property_clear_flag(prop, PROP_PTR_NO_OWNERSHIP);
RNA_def_property_ui_text(prop, "Node Tree", "Node tree for node-based textures");
RNA_def_property_update(prop, 0, "rna_Texture_nodes_update");
diff --git a/source/blender/makesrna/intern/rna_ui_api.c b/source/blender/makesrna/intern/rna_ui_api.c
index fd65b713d15..8dfa54b95da 100644
--- a/source/blender/makesrna/intern/rna_ui_api.c
+++ b/source/blender/makesrna/intern/rna_ui_api.c
@@ -530,6 +530,22 @@ static void rna_uiTemplateEventFromKeymapItem(
uiTemplateEventFromKeymapItem(layout, name, kmi, true);
}
+static uiLayout *rna_uiLayoutRowWithHeading(
+ uiLayout *layout, bool align, const char *heading, const char *heading_ctxt, bool translate)
+{
+ /* Get translated heading. */
+ heading = rna_translate_ui_text(heading, heading_ctxt, NULL, NULL, translate);
+ return uiLayoutRowWithHeading(layout, align, heading);
+}
+
+static uiLayout *rna_uiLayoutColumnWithHeading(
+ uiLayout *layout, bool align, const char *heading, const char *heading_ctxt, bool translate)
+{
+ /* Get translated heading. */
+ heading = rna_translate_ui_text(heading, heading_ctxt, NULL, NULL, translate);
+ return uiLayoutColumnWithHeading(layout, align, heading);
+}
+
static int rna_ui_get_rnaptr_icon(bContext *C, PointerRNA *ptr_icon)
{
return UI_rnaptr_icon_get(C, ptr_icon, RNA_struct_ui_icon(ptr_icon->type), false);
@@ -639,6 +655,24 @@ static int rna_ui_get_enum_icon(bContext *C,
#else
+static void api_ui_item_common_heading(FunctionRNA *func)
+{
+ RNA_def_string(func,
+ "heading",
+ NULL,
+ UI_MAX_NAME_STR,
+ "Heading",
+ "Label to insert into the layout for this sub-layout");
+ RNA_def_string(func,
+ "heading_ctxt",
+ NULL,
+ 0,
+ "",
+ "Override automatic translation context of the given heading");
+ RNA_def_boolean(
+ func, "translate", true, "", "Translate the given heading, when UI translation is enabled");
+}
+
static void api_ui_item_common_text(FunctionRNA *func)
{
PropertyRNA *prop;
@@ -708,7 +742,7 @@ void RNA_api_ui_layout(StructRNA *srna)
static float node_socket_color_default[] = {0.0f, 0.0f, 0.0f, 1.0f};
/* simple layout specifiers */
- func = RNA_def_function(srna, "row", "uiLayoutRowWithHeading");
+ func = RNA_def_function(srna, "row", "rna_uiLayoutRowWithHeading");
parm = RNA_def_pointer(func, "layout", "UILayout", "", "Sub-layout to put items in");
RNA_def_function_return(func, parm);
RNA_def_function_ui_description(
@@ -716,14 +750,9 @@ void RNA_api_ui_layout(StructRNA *srna)
"Sub-layout. Items placed in this sublayout are placed next to each other "
"in a row");
RNA_def_boolean(func, "align", false, "", "Align buttons to each other");
- RNA_def_string(func,
- "heading",
- NULL,
- UI_MAX_NAME_STR,
- "Heading",
- "Label to insert into the layout for this row");
+ api_ui_item_common_heading(func);
- func = RNA_def_function(srna, "column", "uiLayoutColumnWithHeading");
+ func = RNA_def_function(srna, "column", "rna_uiLayoutColumnWithHeading");
parm = RNA_def_pointer(func, "layout", "UILayout", "", "Sub-layout to put items in");
RNA_def_function_return(func, parm);
RNA_def_function_ui_description(
@@ -731,12 +760,7 @@ void RNA_api_ui_layout(StructRNA *srna)
"Sub-layout. Items placed in this sublayout are placed under each other "
"in a column");
RNA_def_boolean(func, "align", false, "", "Align buttons to each other");
- RNA_def_string(func,
- "heading",
- NULL,
- UI_MAX_NAME_STR,
- "Heading",
- "Label to insert into the layout for this column");
+ api_ui_item_common_heading(func);
func = RNA_def_function(srna, "column_flow", "uiLayoutColumnFlow");
RNA_def_int(func, "columns", 0, 0, INT_MAX, "", "Number of columns, 0 is automatic", 0, INT_MAX);
@@ -1213,21 +1237,23 @@ void RNA_api_ui_layout(StructRNA *srna)
RNA_def_function_flag(func, FUNC_USE_CONTEXT);
RNA_def_function_ui_description(func, "Generates the UI layout for the modifier stack");
- func = RNA_def_function(srna, "template_greasepencil_modifier", "uiTemplateGpencilModifier");
+ func = RNA_def_function(srna, "template_constraints", "uiTemplateConstraints");
RNA_def_function_flag(func, FUNC_USE_CONTEXT);
- RNA_def_function_ui_description(func, "Generates the UI layout for grease pencil modifiers");
- parm = RNA_def_pointer(func, "data", "GpencilModifier", "", "Modifier data");
- RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
- parm = RNA_def_pointer(func, "layout", "UILayout", "", "Sub-layout to put items in");
- RNA_def_function_return(func, parm);
+ RNA_def_function_ui_description(func, "Generates the panels for the constraint stack");
+ RNA_def_boolean(func,
+ "use_bone_constraints",
+ true,
+ "",
+ "Add panels for bone constraints instead of object constraints");
+
+ func = RNA_def_function(srna, "template_grease_pencil_modifiers", "uiTemplateGpencilModifiers");
+ RNA_def_function_flag(func, FUNC_USE_CONTEXT);
+ RNA_def_function_ui_description(func,
+ "Generates the panels for the grease pencil modifier stack");
func = RNA_def_function(srna, "template_shaderfx", "uiTemplateShaderFx");
RNA_def_function_flag(func, FUNC_USE_CONTEXT);
- RNA_def_function_ui_description(func, "Generates the UI layout for shader effect");
- parm = RNA_def_pointer(func, "data", "ShaderFx", "", "Shader data");
- RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
- parm = RNA_def_pointer(func, "layout", "UILayout", "", "Sub-layout to put items in");
- RNA_def_function_return(func, parm);
+ RNA_def_function_ui_description(func, "Generates the panels for the shader effect stack");
func = RNA_def_function(srna, "template_greasepencil_color", "uiTemplateGpencilColorPreview");
RNA_def_function_flag(func, FUNC_USE_CONTEXT);
@@ -1251,12 +1277,10 @@ void RNA_api_ui_layout(StructRNA *srna)
"",
"Optionally limit the items which can be selected");
- func = RNA_def_function(srna, "template_constraint", "uiTemplateConstraint");
- RNA_def_function_ui_description(func, "Generates the UI layout for constraints");
+ func = RNA_def_function(srna, "template_constraint_header", "uiTemplateConstraintHeader");
+ RNA_def_function_ui_description(func, "Generates the header for constraint panels");
parm = RNA_def_pointer(func, "data", "Constraint", "", "Constraint data");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
- parm = RNA_def_pointer(func, "layout", "UILayout", "", "Sub-layout to put items in");
- RNA_def_function_return(func, parm);
func = RNA_def_function(srna, "template_preview", "uiTemplatePreview");
RNA_def_function_ui_description(
diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c
index 49a0121dadb..c31b313d827 100644
--- a/source/blender/makesrna/intern/rna_userdef.c
+++ b/source/blender/makesrna/intern/rna_userdef.c
@@ -328,6 +328,8 @@ static void rna_userdef_language_update(Main *UNUSED(bmain),
else {
U.transopts |= (USER_TR_IFACE | USER_TR_TOOLTIPS | USER_TR_NEWDATANAME);
}
+
+ USERDEF_TAG_DIRTY;
}
static void rna_userdef_script_autoexec_update(Main *UNUSED(bmain),
@@ -5044,7 +5046,7 @@ static void rna_def_userdef_edit(BlenderRNA *brna)
prop = RNA_def_property(srna, "use_duplicate_action", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "dupflag", USER_DUP_ACT);
RNA_def_property_ui_text(
- prop, "Duplicate Action", "Causes actions to be duplicated with the object");
+ prop, "Duplicate Action", "Causes actions to be duplicated with the data-blocks");
prop = RNA_def_property(srna, "use_duplicate_particle", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "dupflag", USER_DUP_PSYS);
@@ -5061,7 +5063,6 @@ static void rna_def_userdef_edit(BlenderRNA *brna)
RNA_def_property_ui_text(
prop, "Duplicate GPencil", "Causes grease pencil data to be duplicated with the object");
-# ifdef WITH_NEW_OBJECT_TYPES
prop = RNA_def_property(srna, "use_duplicate_hair", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "dupflag", USER_DUP_HAIR);
RNA_def_property_ui_text(
@@ -5071,7 +5072,6 @@ static void rna_def_userdef_edit(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "dupflag", USER_DUP_POINTCLOUD);
RNA_def_property_ui_text(
prop, "Duplicate Point Cloud", "Causes point cloud data to be duplicated with the object");
-# endif
prop = RNA_def_property(srna, "use_duplicate_volume", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "dupflag", USER_DUP_VOLUME);
@@ -5887,9 +5887,9 @@ static void rna_def_userdef_filepaths(BlenderRNA *brna)
static const EnumPropertyItem anim_player_presets[] = {
{0, "INTERNAL", 0, "Internal", "Built-in animation player"},
- {2, "DJV", 0, "Djv", "Open source frame player: http://djv.sourceforge.net"},
+ {2, "DJV", 0, "DJV", "Open source frame player: http://djv.sourceforge.net"},
{3, "FRAMECYCLER", 0, "FrameCycler", "Frame player from IRIDAS"},
- {4, "RV", 0, "rv", "Frame player from Tweak Software"},
+ {4, "RV", 0, "RV", "Frame player from Tweak Software"},
{5, "MPLAYER", 0, "MPlayer", "Media player for video & png/jpeg/sgi image sequences"},
{50, "CUSTOM", 0, "Custom", "Custom animation player executable path"},
{0, NULL, 0, NULL, NULL},
@@ -6070,6 +6070,20 @@ static void rna_def_userdef_experimental(BlenderRNA *brna)
prop,
"Undo Legacy",
"Use legacy undo (slower than the new default one, but may be more stable in some cases)");
+
+ prop = RNA_def_property(srna, "use_new_particle_system", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "use_new_particle_system", 1);
+ RNA_def_property_ui_text(
+ prop, "New Particle System", "Enable the new particle system in the ui");
+
+ prop = RNA_def_property(srna, "use_new_hair_type", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "use_new_hair_type", 1);
+ RNA_def_property_ui_text(prop, "New Hair Type", "Enable the new hair type in the ui");
+
+ 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");
+ RNA_def_property_update(prop, 0, "rna_userdef_update");
}
static void rna_def_userdef_addon_collection(BlenderRNA *brna, PropertyRNA *cprop)
diff --git a/source/blender/makesrna/intern/rna_vfont.c b/source/blender/makesrna/intern/rna_vfont.c
index 3e96b5816e5..a98a52c2252 100644
--- a/source/blender/makesrna/intern/rna_vfont.c
+++ b/source/blender/makesrna/intern/rna_vfont.c
@@ -72,7 +72,7 @@ void RNA_def_vfont(BlenderRNA *brna)
RNA_def_struct_ui_icon(srna, ICON_FILE_FONT);
prop = RNA_def_property(srna, "filepath", PROP_STRING, PROP_FILEPATH);
- RNA_def_property_string_sdna(prop, NULL, "name");
+ RNA_def_property_string_sdna(prop, NULL, "filepath");
RNA_def_property_editable_func(prop, "rna_VectorFont_filepath_editable");
RNA_def_property_ui_text(prop, "File Path", "");
RNA_def_property_update(prop, NC_GEOM | ND_DATA, "rna_VectorFont_reload_update");
diff --git a/source/blender/makesrna/intern/rna_vfont_api.c b/source/blender/makesrna/intern/rna_vfont_api.c
index 1bf61db4871..80e0511caf9 100644
--- a/source/blender/makesrna/intern/rna_vfont_api.c
+++ b/source/blender/makesrna/intern/rna_vfont_api.c
@@ -34,7 +34,8 @@
static void rna_VectorFont_pack(VFont *vfont, Main *bmain, ReportList *reports)
{
- vfont->packedfile = BKE_packedfile_new(reports, vfont->name, ID_BLEND_PATH(bmain, &vfont->id));
+ vfont->packedfile = BKE_packedfile_new(
+ reports, vfont->filepath, ID_BLEND_PATH(bmain, &vfont->id));
}
static void rna_VectorFont_unpack(VFont *vfont, Main *bmain, ReportList *reports, int method)
diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c
index 120730b19a5..b103741a994 100644
--- a/source/blender/makesrna/intern/rna_wm.c
+++ b/source/blender/makesrna/intern/rna_wm.c
@@ -251,7 +251,7 @@ const EnumPropertyItem rna_enum_event_type_items[] = {
{EVT_RETKEY, "RET", 0, "Return", "Enter"},
{EVT_SPACEKEY, "SPACE", 0, "Spacebar", "Space"},
{EVT_LINEFEEDKEY, "LINE_FEED", 0, "Line Feed", ""},
- {EVT_BACKSPACEKEY, "BACK_SPACE", 0, "Back Space", "BkSpace"},
+ {EVT_BACKSPACEKEY, "BACK_SPACE", 0, "Backspace", "BkSpace"},
{EVT_DELKEY, "DEL", 0, "Delete", "Del"},
{EVT_SEMICOLONKEY, "SEMI_COLON", 0, ";", ""},
{EVT_PERIODKEY, "PERIOD", 0, ".", ""},
diff --git a/source/blender/makesrna/intern/rna_world.c b/source/blender/makesrna/intern/rna_world.c
index 07db2755523..1ca0eb74cf5 100644
--- a/source/blender/makesrna/intern/rna_world.c
+++ b/source/blender/makesrna/intern/rna_world.c
@@ -236,6 +236,7 @@ void RNA_def_world(BlenderRNA *brna)
/* nodes */
prop = RNA_def_property(srna, "node_tree", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "nodetree");
+ RNA_def_property_clear_flag(prop, PROP_PTR_NO_OWNERSHIP);
RNA_def_property_ui_text(prop, "Node Tree", "Node tree for node based worlds");
prop = RNA_def_property(srna, "use_nodes", PROP_BOOLEAN, PROP_NONE);
diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt
index 460f697a0a9..80a1ebab64e 100644
--- a/source/blender/modifiers/CMakeLists.txt
+++ b/source/blender/modifiers/CMakeLists.txt
@@ -175,3 +175,7 @@ endif()
add_definitions(${GL_DEFINITIONS})
blender_add_lib(bf_modifiers "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
+
+# Some modifiers include BLO_read_write.h, which includes dna_type_offsets.h
+# which is generated by bf_dna. Need to ensure compilaiton order here.
+add_dependencies(bf_modifiers bf_dna)
diff --git a/source/blender/modifiers/intern/MOD_armature.c b/source/blender/modifiers/intern/MOD_armature.c
index 9cc1f4596a9..b62c03d1b03 100644
--- a/source/blender/modifiers/intern/MOD_armature.c
+++ b/source/blender/modifiers/intern/MOD_armature.c
@@ -49,6 +49,8 @@
#include "RNA_access.h"
+#include "BLO_read_write.h"
+
#include "DEG_depsgraph_query.h"
#include "bmesh.h"
@@ -249,7 +251,7 @@ static void panel_draw(const bContext *C, Panel *panel)
uiItemR(col, &ptr, "use_deform_preserve_volume", 0, NULL, ICON_NONE);
uiItemR(col, &ptr, "use_multi_modifier", 0, NULL, ICON_NONE);
- col = uiLayoutColumnWithHeading(layout, true, "Bind to");
+ col = uiLayoutColumnWithHeading(layout, true, IFACE_("Bind to"));
uiItemR(col, &ptr, "use_vertex_groups", 0, IFACE_("Vertex Groups"), ICON_NONE);
uiItemR(col, &ptr, "use_bone_envelopes", 0, IFACE_("Bone Envelopes"), ICON_NONE);
@@ -261,6 +263,13 @@ static void panelRegister(ARegionType *region_type)
modifier_panel_register(region_type, eModifierType_Armature, panel_draw);
}
+static void blendRead(BlendDataReader *UNUSED(reader), ModifierData *md)
+{
+ ArmatureModifierData *amd = (ArmatureModifierData *)md;
+
+ amd->vert_coords_prev = NULL;
+}
+
ModifierTypeInfo modifierType_Armature = {
/* name */ "Armature",
/* structName */ "ArmatureModifierData",
@@ -293,5 +302,5 @@ ModifierTypeInfo modifierType_Armature = {
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
/* blendWrite */ NULL,
- /* blendRead */ NULL,
+ /* blendRead */ blendRead,
};
diff --git a/source/blender/modifiers/intern/MOD_bevel.c b/source/blender/modifiers/intern/MOD_bevel.c
index 35e3bb97fc3..0a1eb8eba10 100644
--- a/source/blender/modifiers/intern/MOD_bevel.c
+++ b/source/blender/modifiers/intern/MOD_bevel.c
@@ -51,6 +51,8 @@
#include "MOD_ui_common.h"
#include "MOD_util.h"
+#include "BLO_read_write.h"
+
#include "BKE_curveprofile.h"
#include "bmesh.h"
#include "bmesh_tools.h"
@@ -118,6 +120,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
const bool vertex_only = (bmd->flags & MOD_BEVEL_VERT) != 0;
const bool do_clamp = !(bmd->flags & MOD_BEVEL_OVERLAP_OK);
const int offset_type = bmd->val_flags;
+ const int profile_type = bmd->profile_type;
const float value = bmd->value;
const int mat = CLAMPIS(bmd->mat, -1, ctx->object->totcol - 1);
const bool loop_slide = (bmd->flags & MOD_BEVEL_EVEN_WIDTHS) == 0;
@@ -128,7 +131,6 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
const int miter_outer = bmd->miter_outer;
const int miter_inner = bmd->miter_inner;
const float spread = bmd->spread;
- const bool use_custom_profile = (bmd->flags & MOD_BEVEL_CUSTOM_PROFILE);
const int vmesh_method = bmd->vmesh_method;
const bool invert_vgroup = (bmd->flags & MOD_BEVEL_INVERT_VGROUP) != 0;
@@ -225,6 +227,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
BM_mesh_bevel(bm,
value,
offset_type,
+ profile_type,
bmd->res,
bmd->profile,
vertex_only,
@@ -242,7 +245,6 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
miter_inner,
spread,
mesh->smoothresh,
- use_custom_profile,
bmd->custom_profile,
vmesh_method);
@@ -302,6 +304,9 @@ static void panel_draw(const bContext *C, Panel *panel)
case BEVEL_AMT_OFFSET:
offset_name = "Offset";
break;
+ case BEVEL_AMT_ABSOLUTE:
+ offset_name = "Absolute";
+ break;
}
uiItemR(col, &ptr, "width", 0, IFACE_(offset_name), ICON_NONE);
}
@@ -371,53 +376,64 @@ static void shading_panel_draw(const bContext *C, Panel *panel)
static void profile_panel_draw(const bContext *C, Panel *panel)
{
- PointerRNA ptr;
- modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
- uiLayout *layout = panel->layout;
-
- uiLayoutSetPropSep(layout, true);
-
- uiItemR(layout, &ptr, "profile", UI_ITEM_R_SLIDER, IFACE_("Shape"), ICON_NONE);
-}
-
-static void custom_profile_panel_draw_header(const bContext *C, Panel *panel)
-{
uiLayout *layout = panel->layout;
PointerRNA ptr;
modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
- uiItemR(layout, &ptr, "use_custom_profile", 0, NULL, ICON_NONE);
-}
+ int profile_type = RNA_enum_get(&ptr, "profile_type");
-static void custom_profile_panel_draw(const bContext *C, Panel *panel)
-{
- PointerRNA ptr;
- modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
- uiLayout *layout = panel->layout;
+ uiItemR(layout, &ptr, "profile_type", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
- uiLayoutSetActive(layout, RNA_boolean_get(&ptr, "use_custom_profile"));
+ uiLayoutSetPropSep(layout, true);
- uiTemplateCurveProfile(layout, &ptr, "custom_profile");
+ if (ELEM(profile_type, MOD_BEVEL_PROFILE_SUPERELLIPSE, MOD_BEVEL_PROFILE_CUSTOM)) {
+ uiItemR(layout,
+ &ptr,
+ "profile",
+ UI_ITEM_R_SLIDER,
+ (profile_type == MOD_BEVEL_PROFILE_SUPERELLIPSE) ? IFACE_("Shape") :
+ IFACE_("Miter Shape"),
+ ICON_NONE);
+
+ if (profile_type == MOD_BEVEL_PROFILE_CUSTOM) {
+ uiLayout *sub = uiLayoutColumn(layout, false);
+ uiLayoutSetPropDecorate(sub, false);
+ uiTemplateCurveProfile(sub, &ptr, "custom_profile");
+ }
+ }
}
static void panelRegister(ARegionType *region_type)
{
PanelType *panel_type = modifier_panel_register(region_type, eModifierType_Bevel, panel_draw);
- PanelType *bevel_profil_panel = modifier_subpanel_register(
+ modifier_subpanel_register(
region_type, "profile", "Profile", NULL, profile_panel_draw, panel_type);
- modifier_subpanel_register(region_type,
- "custom",
- "",
- custom_profile_panel_draw_header,
- custom_profile_panel_draw,
- bevel_profil_panel);
modifier_subpanel_register(
region_type, "geometry", "Geometry", NULL, geometry_panel_draw, panel_type);
modifier_subpanel_register(
region_type, "shading", "Shading", NULL, shading_panel_draw, panel_type);
}
+static void blendWrite(BlendWriter *writer, const ModifierData *md)
+{
+ const BevelModifierData *bmd = (const BevelModifierData *)md;
+
+ if (bmd->custom_profile) {
+ BKE_curveprofile_blend_write(writer, bmd->custom_profile);
+ }
+}
+
+static void blendRead(BlendDataReader *reader, ModifierData *md)
+{
+ BevelModifierData *bmd = (BevelModifierData *)md;
+
+ BLO_read_data_address(reader, &bmd->custom_profile);
+ if (bmd->custom_profile) {
+ BKE_curveprofile_blend_read(reader, bmd->custom_profile);
+ }
+}
+
ModifierTypeInfo modifierType_Bevel = {
/* name */ "Bevel",
/* structName */ "BevelModifierData",
@@ -446,6 +462,6 @@ ModifierTypeInfo modifierType_Bevel = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* uiPanel */ panelRegister,
- /* blendWrite */ NULL,
- /* blendRead */ NULL,
+ /* blendWrite */ blendWrite,
+ /* blendRead */ blendRead,
};
diff --git a/source/blender/modifiers/intern/MOD_cast.c b/source/blender/modifiers/intern/MOD_cast.c
index 9f588a4d345..cc9def73e12 100644
--- a/source/blender/modifiers/intern/MOD_cast.c
+++ b/source/blender/modifiers/intern/MOD_cast.c
@@ -565,7 +565,7 @@ static void panel_draw(const bContext *C, Panel *panel)
uiItemR(layout, &ptr, "object", 0, NULL, ICON_NONE);
if (!RNA_pointer_is_null(&cast_object_ptr)) {
- uiItemR(layout, &ptr, "use_radius_as_size", 0, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "use_transform", 0, NULL, ICON_NONE);
}
modifier_panel_end(layout, &ptr);
diff --git a/source/blender/modifiers/intern/MOD_collision.c b/source/blender/modifiers/intern/MOD_collision.c
index 7a338b59e98..a355558a3ba 100644
--- a/source/blender/modifiers/intern/MOD_collision.c
+++ b/source/blender/modifiers/intern/MOD_collision.c
@@ -54,6 +54,8 @@
#include "MOD_ui_common.h"
#include "MOD_util.h"
+#include "BLO_read_write.h"
+
#include "DEG_depsgraph_query.h"
static void initData(ModifierData *md)
@@ -269,6 +271,34 @@ static void panelRegister(ARegionType *region_type)
modifier_panel_register(region_type, eModifierType_Collision, panel_draw);
}
+static void blendRead(BlendDataReader *UNUSED(reader), ModifierData *md)
+{
+ CollisionModifierData *collmd = (CollisionModifierData *)md;
+#if 0
+ // TODO: CollisionModifier should use pointcache
+ // + have proper reset events before enabling this
+ collmd->x = newdataadr(fd, collmd->x);
+ collmd->xnew = newdataadr(fd, collmd->xnew);
+ collmd->mfaces = newdataadr(fd, collmd->mfaces);
+
+ collmd->current_x = MEM_calloc_arrayN(collmd->numverts, sizeof(MVert), "current_x");
+ collmd->current_xnew = MEM_calloc_arrayN(collmd->numverts, sizeof(MVert), "current_xnew");
+ collmd->current_v = MEM_calloc_arrayN(collmd->numverts, sizeof(MVert), "current_v");
+#endif
+
+ collmd->x = NULL;
+ collmd->xnew = NULL;
+ collmd->current_x = NULL;
+ collmd->current_xnew = NULL;
+ collmd->current_v = NULL;
+ collmd->time_x = collmd->time_xnew = -1000;
+ collmd->mvert_num = 0;
+ collmd->tri_num = 0;
+ collmd->is_static = false;
+ collmd->bvhtree = NULL;
+ collmd->tri = NULL;
+}
+
ModifierTypeInfo modifierType_Collision = {
/* name */ "Collision",
/* structName */ "CollisionModifierData",
@@ -300,5 +330,5 @@ ModifierTypeInfo modifierType_Collision = {
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
/* blendWrite */ NULL,
- /* blendRead */ NULL,
+ /* blendRead */ blendRead,
};
diff --git a/source/blender/modifiers/intern/MOD_correctivesmooth.c b/source/blender/modifiers/intern/MOD_correctivesmooth.c
index ba343854d41..ac91056ed7d 100644
--- a/source/blender/modifiers/intern/MOD_correctivesmooth.c
+++ b/source/blender/modifiers/intern/MOD_correctivesmooth.c
@@ -54,6 +54,8 @@
#include "MOD_ui_common.h"
#include "MOD_util.h"
+#include "BLO_read_write.h"
+
#include "BLI_strict_flags.h"
#include "DEG_depsgraph_query.h"
@@ -817,6 +819,28 @@ static void panelRegister(ARegionType *region_type)
modifier_panel_register(region_type, eModifierType_CorrectiveSmooth, panel_draw);
}
+static void blendWrite(BlendWriter *writer, const ModifierData *md)
+{
+ const CorrectiveSmoothModifierData *csmd = (const CorrectiveSmoothModifierData *)md;
+
+ if (csmd->bind_coords) {
+ BLO_write_float3_array(writer, (int)csmd->bind_coords_num, (float *)csmd->bind_coords);
+ }
+}
+
+static void blendRead(BlendDataReader *reader, ModifierData *md)
+{
+ CorrectiveSmoothModifierData *csmd = (CorrectiveSmoothModifierData *)md;
+
+ if (csmd->bind_coords) {
+ BLO_read_float3_array(reader, (int)csmd->bind_coords_num, (float **)&csmd->bind_coords);
+ }
+
+ /* runtime only */
+ csmd->delta_cache.deltas = NULL;
+ csmd->delta_cache.totverts = 0;
+}
+
ModifierTypeInfo modifierType_CorrectiveSmooth = {
/* name */ "CorrectiveSmooth",
/* structName */ "CorrectiveSmoothModifierData",
@@ -847,6 +871,6 @@ ModifierTypeInfo modifierType_CorrectiveSmooth = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
- /* blendWrite */ NULL,
- /* blendRead */ NULL,
+ /* blendWrite */ blendWrite,
+ /* blendRead */ blendRead,
};
diff --git a/source/blender/modifiers/intern/MOD_decimate.c b/source/blender/modifiers/intern/MOD_decimate.c
index 4fa7bf4cd63..75fd558ae39 100644
--- a/source/blender/modifiers/intern/MOD_decimate.c
+++ b/source/blender/modifiers/intern/MOD_decimate.c
@@ -239,14 +239,14 @@ static void panel_draw(const bContext *C, Panel *panel)
int decimate_type = RNA_enum_get(&ptr, "decimate_type");
char count_info[32];
- snprintf(count_info, 32, IFACE_("Face Count: %d"), RNA_int_get(&ptr, "face_count"));
+ snprintf(count_info, 32, "%s: %d", IFACE_("Face Count"), RNA_int_get(&ptr, "face_count"));
uiItemR(layout, &ptr, "decimate_type", 0, NULL, ICON_NONE);
if (decimate_type == MOD_DECIM_MODE_COLLAPSE) {
uiItemR(layout, &ptr, "ratio", UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- row = uiLayoutRowWithHeading(layout, true, "Symmetry");
+ row = uiLayoutRowWithHeading(layout, true, IFACE_("Symmetry"));
uiLayoutSetPropDecorate(row, false);
sub = uiLayoutRow(row, true);
uiItemR(sub, &ptr, "use_symmetry", 0, "", ICON_NONE);
diff --git a/source/blender/modifiers/intern/MOD_displace.c b/source/blender/modifiers/intern/MOD_displace.c
index 32e36fbd09e..894a48d9f62 100644
--- a/source/blender/modifiers/intern/MOD_displace.c
+++ b/source/blender/modifiers/intern/MOD_displace.c
@@ -440,6 +440,8 @@ static void panel_draw(const bContext *C, Panel *panel)
PointerRNA ob_ptr;
modifier_panel_get_property_pointers(C, panel, &ob_ptr, &ptr);
+ PointerRNA obj_data_ptr = RNA_pointer_get(&ob_ptr, "data");
+
PointerRNA texture_ptr = RNA_pointer_get(&ptr, "texture");
bool has_texture = !RNA_pointer_is_null(&texture_ptr);
int texture_coords = RNA_enum_get(&ptr, "texture_coords");
@@ -467,7 +469,7 @@ static void panel_draw(const bContext *C, Panel *panel)
}
}
else if (texture_coords == MOD_DISP_MAP_UV && RNA_enum_get(&ob_ptr, "type") == OB_MESH) {
- uiItemR(col, &ptr, "uv_layer", 0, NULL, ICON_NONE);
+ uiItemPointerR(col, &ptr, "uv_layer", &obj_data_ptr, "uv_layers", NULL, ICON_NONE);
}
uiItemS(layout);
diff --git a/source/blender/modifiers/intern/MOD_explode.c b/source/blender/modifiers/intern/MOD_explode.c
index 00e39a7ea6c..29f7294c202 100644
--- a/source/blender/modifiers/intern/MOD_explode.c
+++ b/source/blender/modifiers/intern/MOD_explode.c
@@ -49,6 +49,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "BLO_read_write.h"
+
#include "RNA_access.h"
#include "DEG_depsgraph_query.h"
@@ -1228,6 +1230,13 @@ static void panelRegister(ARegionType *region_type)
modifier_panel_register(region_type, eModifierType_Explode, panel_draw);
}
+static void blendRead(BlendDataReader *UNUSED(reader), ModifierData *md)
+{
+ ExplodeModifierData *psmd = (ExplodeModifierData *)md;
+
+ psmd->facepa = NULL;
+}
+
ModifierTypeInfo modifierType_Explode = {
/* name */ "Explode",
/* structName */ "ExplodeModifierData",
@@ -1258,5 +1267,5 @@ ModifierTypeInfo modifierType_Explode = {
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
/* blendWrite */ NULL,
- /* blendRead */ NULL,
+ /* blendRead */ blendRead,
};
diff --git a/source/blender/modifiers/intern/MOD_hook.c b/source/blender/modifiers/intern/MOD_hook.c
index 4305c32071d..c8cfc07562f 100644
--- a/source/blender/modifiers/intern/MOD_hook.c
+++ b/source/blender/modifiers/intern/MOD_hook.c
@@ -46,6 +46,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "BLO_read_write.h"
+
#include "RNA_access.h"
#include "DEG_depsgraph_query.h"
@@ -468,6 +470,29 @@ static void panelRegister(ARegionType *region_type)
region_type, "falloff", "Falloff", NULL, falloff_panel_draw, panel_type);
}
+static void blendWrite(BlendWriter *writer, const ModifierData *md)
+{
+ const HookModifierData *hmd = (const HookModifierData *)md;
+
+ if (hmd->curfalloff) {
+ BKE_curvemapping_blend_write(writer, hmd->curfalloff);
+ }
+
+ BLO_write_int32_array(writer, hmd->totindex, hmd->indexar);
+}
+
+static void blendRead(BlendDataReader *reader, ModifierData *md)
+{
+ HookModifierData *hmd = (HookModifierData *)md;
+
+ BLO_read_data_address(reader, &hmd->curfalloff);
+ if (hmd->curfalloff) {
+ BKE_curvemapping_blend_read(reader, hmd->curfalloff);
+ }
+
+ BLO_read_int32_array(reader, hmd->totindex, &hmd->indexar);
+}
+
ModifierTypeInfo modifierType_Hook = {
/* name */ "Hook",
/* structName */ "HookModifierData",
@@ -498,6 +523,6 @@ ModifierTypeInfo modifierType_Hook = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
- /* blendWrite */ NULL,
- /* blendRead */ NULL,
+ /* blendWrite */ blendWrite,
+ /* blendRead */ blendRead,
};
diff --git a/source/blender/modifiers/intern/MOD_meshdeform.c b/source/blender/modifiers/intern/MOD_meshdeform.c
index f324a6f92b9..e53de598218 100644
--- a/source/blender/modifiers/intern/MOD_meshdeform.c
+++ b/source/blender/modifiers/intern/MOD_meshdeform.c
@@ -49,6 +49,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "BLO_read_write.h"
+
#include "RNA_access.h"
#include "MEM_guardedalloc.h"
@@ -607,6 +609,35 @@ static void panelRegister(ARegionType *region_type)
modifier_panel_register(region_type, eModifierType_MeshDeform, panel_draw);
}
+static void blendWrite(BlendWriter *writer, const ModifierData *md)
+{
+ MeshDeformModifierData *mmd = (MeshDeformModifierData *)md;
+ int size = mmd->dyngridsize;
+
+ BLO_write_struct_array(writer, MDefInfluence, mmd->totinfluence, mmd->bindinfluences);
+ BLO_write_int32_array(writer, mmd->totvert + 1, mmd->bindoffsets);
+ BLO_write_float3_array(writer, mmd->totcagevert, mmd->bindcagecos);
+ BLO_write_struct_array(writer, MDefCell, size * size * size, mmd->dyngrid);
+ BLO_write_struct_array(writer, MDefInfluence, mmd->totinfluence, mmd->dyninfluences);
+ BLO_write_int32_array(writer, mmd->totvert, mmd->dynverts);
+}
+
+static void blendRead(BlendDataReader *reader, ModifierData *md)
+{
+ MeshDeformModifierData *mmd = (MeshDeformModifierData *)md;
+
+ BLO_read_data_address(reader, &mmd->bindinfluences);
+ BLO_read_int32_array(reader, mmd->totvert + 1, &mmd->bindoffsets);
+ BLO_read_float3_array(reader, mmd->totcagevert, &mmd->bindcagecos);
+ BLO_read_data_address(reader, &mmd->dyngrid);
+ BLO_read_data_address(reader, &mmd->dyninfluences);
+ BLO_read_int32_array(reader, mmd->totvert, &mmd->dynverts);
+
+ /* Deprecated storage. */
+ BLO_read_float_array(reader, mmd->totvert, &mmd->bindweights);
+ BLO_read_float3_array(reader, mmd->totcagevert, &mmd->bindcos);
+}
+
ModifierTypeInfo modifierType_MeshDeform = {
/* name */ "MeshDeform",
/* structName */ "MeshDeformModifierData",
@@ -638,6 +669,6 @@ ModifierTypeInfo modifierType_MeshDeform = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
- /* blendWrite */ NULL,
- /* blendRead */ NULL,
+ /* blendWrite */ blendWrite,
+ /* blendRead */ blendRead,
};
diff --git a/source/blender/modifiers/intern/MOD_meshsequencecache.c b/source/blender/modifiers/intern/MOD_meshsequencecache.c
index b4269513f8e..9eae7ae941f 100644
--- a/source/blender/modifiers/intern/MOD_meshsequencecache.c
+++ b/source/blender/modifiers/intern/MOD_meshsequencecache.c
@@ -44,6 +44,8 @@
#include "RNA_access.h"
+#include "BLO_read_write.h"
+
#include "DEG_depsgraph_build.h"
#include "DEG_depsgraph_query.h"
@@ -229,6 +231,13 @@ static void panelRegister(ARegionType *region_type)
modifier_panel_register(region_type, eModifierType_MeshSequenceCache, panel_draw);
}
+static void blendRead(BlendDataReader *UNUSED(reader), ModifierData *md)
+{
+ MeshSeqCacheModifierData *msmcd = (MeshSeqCacheModifierData *)md;
+ msmcd->reader = NULL;
+ msmcd->reader_object_path[0] = '\0';
+}
+
ModifierTypeInfo modifierType_MeshSequenceCache = {
/* name */ "MeshSequenceCache",
/* structName */ "MeshSeqCacheModifierData",
@@ -260,5 +269,5 @@ ModifierTypeInfo modifierType_MeshSequenceCache = {
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
/* blendWrite */ NULL,
- /* blendRead */ NULL,
+ /* blendRead */ blendRead,
};
diff --git a/source/blender/modifiers/intern/MOD_mirror.c b/source/blender/modifiers/intern/MOD_mirror.c
index 6abea36df35..5f67d591e60 100644
--- a/source/blender/modifiers/intern/MOD_mirror.c
+++ b/source/blender/modifiers/intern/MOD_mirror.c
@@ -181,18 +181,17 @@ static void data_panel_draw(const bContext *C, Panel *panel)
uiLayoutSetPropSep(layout, true);
- col = uiLayoutColumnWithHeading(layout, false, IFACE_("Mirror U"));
- row = uiLayoutRow(col, true);
+ col = uiLayoutColumn(layout, true);
+ row = uiLayoutRowWithHeading(col, true, IFACE_("Mirror U"));
uiLayoutSetPropDecorate(row, false);
sub = uiLayoutRow(row, true);
uiItemR(sub, &ptr, "use_mirror_u", 0, "", ICON_NONE);
sub = uiLayoutRow(sub, true);
uiLayoutSetActive(sub, RNA_boolean_get(&ptr, "use_mirror_u"));
uiItemR(sub, &ptr, "mirror_offset_u", UI_ITEM_R_SLIDER, "", ICON_NONE);
- uiItemDecoratorR(row, &ptr, "mirror_offset_v", 0);
+ uiItemDecoratorR(row, &ptr, "mirror_offset_u", 0);
- col = uiLayoutColumnWithHeading(layout, false, "V");
- row = uiLayoutRow(col, true);
+ row = uiLayoutRowWithHeading(col, true, IFACE_("V"));
uiLayoutSetPropDecorate(row, false);
sub = uiLayoutRow(row, true);
uiItemR(sub, &ptr, "use_mirror_v", 0, "", ICON_NONE);
@@ -201,6 +200,10 @@ static void data_panel_draw(const bContext *C, Panel *panel)
uiItemR(sub, &ptr, "mirror_offset_v", UI_ITEM_R_SLIDER, "", ICON_NONE);
uiItemDecoratorR(row, &ptr, "mirror_offset_v", 0);
+ col = uiLayoutColumn(layout, true);
+ uiItemR(col, &ptr, "offset_u", UI_ITEM_R_SLIDER, IFACE_("Offset U"), ICON_NONE);
+ uiItemR(col, &ptr, "offset_v", UI_ITEM_R_SLIDER, IFACE_("V"), ICON_NONE);
+
uiItemR(layout, &ptr, "use_mirror_vertex_groups", 0, IFACE_("Vertex Groups"), ICON_NONE);
uiItemR(layout, &ptr, "use_mirror_udim", 0, IFACE_("Flip UDIM"), ICON_NONE);
}
diff --git a/source/blender/modifiers/intern/MOD_multires.c b/source/blender/modifiers/intern/MOD_multires.c
index e56ff991b6f..53e53315cfe 100644
--- a/source/blender/modifiers/intern/MOD_multires.c
+++ b/source/blender/modifiers/intern/MOD_multires.c
@@ -296,10 +296,10 @@ static void panel_draw(const bContext *C, Panel *panel)
/**
* Changing some of the properties can not be done once there is an
- * actual displacement stored for this multires modifier. This check
- * will disallow changes for those properties.
- * This check is a bit stupif but it should be sufficient for the usual
- * multires usage. It might become less strict and only disallow
+ * actual displacement stored for this multi-resolution modifier.
+ * This check will disallow changes for those properties.
+ * This check is a bit stupid but it should be sufficient for the usual
+ * multi-resolution usage. It might become less strict and only disallow
* modifications if there is CD_MDISPS layer, or if there is actual
* non-zero displacement, but such checks will be too slow to be done
* on every redraw.
diff --git a/source/blender/modifiers/intern/MOD_ocean.c b/source/blender/modifiers/intern/MOD_ocean.c
index 1cdba84bb27..88581464a02 100644
--- a/source/blender/modifiers/intern/MOD_ocean.c
+++ b/source/blender/modifiers/intern/MOD_ocean.c
@@ -49,6 +49,8 @@
#include "RNA_access.h"
+#include "BLO_read_write.h"
+
#include "WM_types.h" /* For UI free bake operator. */
#include "DEG_depsgraph_query.h"
@@ -674,6 +676,13 @@ static void panelRegister(ARegionType *region_type)
#endif /* WITH_OCEANSIM */
}
+static void blendRead(BlendDataReader *UNUSED(reader), ModifierData *md)
+{
+ OceanModifierData *omd = (OceanModifierData *)md;
+ omd->oceancache = NULL;
+ omd->ocean = NULL;
+}
+
ModifierTypeInfo modifierType_Ocean = {
/* name */ "Ocean",
/* structName */ "OceanModifierData",
@@ -706,5 +715,5 @@ ModifierTypeInfo modifierType_Ocean = {
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
/* blendWrite */ NULL,
- /* blendRead */ NULL,
+ /* blendRead */ blendRead,
};
diff --git a/source/blender/modifiers/intern/MOD_particlesystem.c b/source/blender/modifiers/intern/MOD_particlesystem.c
index 8c25c3eb4ba..ea0c63da1b0 100644
--- a/source/blender/modifiers/intern/MOD_particlesystem.c
+++ b/source/blender/modifiers/intern/MOD_particlesystem.c
@@ -46,6 +46,8 @@
#include "DEG_depsgraph_query.h"
+#include "BLO_read_write.h"
+
#include "MOD_ui_common.h"
#include "MOD_util.h"
@@ -295,6 +297,18 @@ static void panelRegister(ARegionType *region_type)
modifier_panel_register(region_type, eModifierType_ParticleSystem, panel_draw);
}
+static void blendRead(BlendDataReader *reader, ModifierData *md)
+{
+ ParticleSystemModifierData *psmd = (ParticleSystemModifierData *)md;
+
+ psmd->mesh_final = NULL;
+ psmd->mesh_original = NULL;
+ /* This is written as part of ob->particlesystem. */
+ BLO_read_data_address(reader, &psmd->psys);
+ psmd->flag &= ~eParticleSystemFlag_psys_updated;
+ psmd->flag |= eParticleSystemFlag_file_loaded;
+}
+
ModifierTypeInfo modifierType_ParticleSystem = {
/* name */ "ParticleSystem",
/* structName */ "ParticleSystemModifierData",
@@ -330,5 +344,5 @@ ModifierTypeInfo modifierType_ParticleSystem = {
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
/* blendWrite */ NULL,
- /* blendRead */ NULL,
+ /* blendRead */ blendRead,
};
diff --git a/source/blender/modifiers/intern/MOD_remesh.c b/source/blender/modifiers/intern/MOD_remesh.c
index b1e6e12b4b3..6fc564448c3 100644
--- a/source/blender/modifiers/intern/MOD_remesh.c
+++ b/source/blender/modifiers/intern/MOD_remesh.c
@@ -190,8 +190,8 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *UNUSED(ctx)
break;
}
/* TODO(jbakker): Dualcon crashes when run in parallel. Could be related to incorrect
- * input data or that the library isn't thread safe. This was identified when changing the task
- * isolations during T76553. */
+ * input data or that the library isn't thread safe.
+ * This was identified when changing the task isolation's during T76553. */
static ThreadMutex dualcon_mutex = BLI_MUTEX_INITIALIZER;
BLI_mutex_lock(&dualcon_mutex);
output = dualcon(&input,
diff --git a/source/blender/modifiers/intern/MOD_solidify.c b/source/blender/modifiers/intern/MOD_solidify.c
index f8d372f4d2b..82a8227c33a 100644
--- a/source/blender/modifiers/intern/MOD_solidify.c
+++ b/source/blender/modifiers/intern/MOD_solidify.c
@@ -128,7 +128,7 @@ static void panel_draw(const bContext *C, Panel *panel)
uiItemR(layout, &ptr, "use_even_offset", 0, NULL, ICON_NONE);
}
- col = uiLayoutColumnWithHeading(layout, false, "Rim");
+ col = uiLayoutColumnWithHeading(layout, false, IFACE_("Rim"));
uiItemR(col, &ptr, "use_rim", 0, IFACE_("Fill"), ICON_NONE);
sub = uiLayoutColumn(col, false);
uiLayoutSetActive(sub, RNA_boolean_get(&ptr, "use_rim"));
diff --git a/source/blender/modifiers/intern/MOD_solidify_extrude.c b/source/blender/modifiers/intern/MOD_solidify_extrude.c
index 75d2be5292e..a56194354f8 100644
--- a/source/blender/modifiers/intern/MOD_solidify_extrude.c
+++ b/source/blender/modifiers/intern/MOD_solidify_extrude.c
@@ -76,7 +76,7 @@ BLI_INLINE bool edgeref_is_init(const EdgeFaceRef *edge_ref)
}
/**
- * \param dm: Mesh to calculate normals for.
+ * \param mesh: Mesh to calculate normals for.
* \param poly_nors: Precalculated face normals.
* \param r_vert_nors: Return vert normals.
*/
diff --git a/source/blender/modifiers/intern/MOD_subsurf.c b/source/blender/modifiers/intern/MOD_subsurf.c
index aca4cad95b8..cbd2c2d27be 100644
--- a/source/blender/modifiers/intern/MOD_subsurf.c
+++ b/source/blender/modifiers/intern/MOD_subsurf.c
@@ -57,6 +57,8 @@
#include "MOD_modifiertypes.h"
#include "MOD_ui_common.h"
+#include "BLO_read_write.h"
+
#include "intern/CCGSubSurf.h"
typedef struct SubsurfRuntimeData {
@@ -414,8 +416,6 @@ static void advanced_panel_draw(const bContext *C, Panel *panel)
uiItemR(layout, &ptr, "quality", 0, NULL, ICON_NONE);
uiItemR(layout, &ptr, "uv_smooth", 0, NULL, ICON_NONE);
uiItemR(layout, &ptr, "use_creases", 0, NULL, ICON_NONE);
-
- modifier_panel_end(layout, &ptr);
}
static void panelRegister(ARegionType *region_type)
@@ -425,6 +425,13 @@ static void panelRegister(ARegionType *region_type)
region_type, "advanced", "Advanced", NULL, advanced_panel_draw, panel_type);
}
+static void blendRead(BlendDataReader *UNUSED(reader), ModifierData *md)
+{
+ SubsurfModifierData *smd = (SubsurfModifierData *)md;
+
+ smd->emCache = smd->mCache = NULL;
+}
+
ModifierTypeInfo modifierType_Subsurf = {
/* name */ "Subdivision",
/* structName */ "SubsurfModifierData",
@@ -458,5 +465,5 @@ ModifierTypeInfo modifierType_Subsurf = {
/* freeRuntimeData */ freeRuntimeData,
/* panelRegister */ panelRegister,
/* blendWrite */ NULL,
- /* blendRead */ NULL,
+ /* blendRead */ blendRead,
};
diff --git a/source/blender/modifiers/intern/MOD_surface.c b/source/blender/modifiers/intern/MOD_surface.c
index 32b342b1471..e476e01a9f2 100644
--- a/source/blender/modifiers/intern/MOD_surface.c
+++ b/source/blender/modifiers/intern/MOD_surface.c
@@ -51,6 +51,8 @@
#include "MOD_ui_common.h"
#include "MOD_util.h"
+#include "BLO_read_write.h"
+
#include "MEM_guardedalloc.h"
static void initData(ModifierData *md)
@@ -210,6 +212,17 @@ static void panelRegister(ARegionType *region_type)
modifier_panel_register(region_type, eModifierType_Surface, panel_draw);
}
+static void blendRead(BlendDataReader *UNUSED(reader), ModifierData *md)
+{
+ SurfaceModifierData *surmd = (SurfaceModifierData *)md;
+
+ surmd->mesh = NULL;
+ surmd->bvhtree = NULL;
+ surmd->x = NULL;
+ surmd->v = NULL;
+ surmd->numverts = 0;
+}
+
ModifierTypeInfo modifierType_Surface = {
/* name */ "Surface",
/* structName */ "SurfaceModifierData",
@@ -242,5 +255,5 @@ ModifierTypeInfo modifierType_Surface = {
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
/* blendWrite */ NULL,
- /* blendRead */ NULL,
+ /* blendRead */ blendRead,
};
diff --git a/source/blender/modifiers/intern/MOD_surfacedeform.c b/source/blender/modifiers/intern/MOD_surfacedeform.c
index e1bc6305b0a..fc41afb6613 100644
--- a/source/blender/modifiers/intern/MOD_surfacedeform.c
+++ b/source/blender/modifiers/intern/MOD_surfacedeform.c
@@ -47,6 +47,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "BLO_read_write.h"
+
#include "RNA_access.h"
#include "DEG_depsgraph.h"
@@ -1438,6 +1440,64 @@ static void panelRegister(ARegionType *region_type)
modifier_panel_register(region_type, eModifierType_SurfaceDeform, panel_draw);
}
+static void blendWrite(BlendWriter *writer, const ModifierData *md)
+{
+ const SurfaceDeformModifierData *smd = (const SurfaceDeformModifierData *)md;
+
+ BLO_write_struct_array(writer, SDefVert, smd->numverts, smd->verts);
+
+ if (smd->verts) {
+ for (int i = 0; i < smd->numverts; i++) {
+ BLO_write_struct_array(writer, SDefBind, smd->verts[i].numbinds, smd->verts[i].binds);
+
+ if (smd->verts[i].binds) {
+ for (int j = 0; j < smd->verts[i].numbinds; j++) {
+ BLO_write_uint32_array(
+ writer, smd->verts[i].binds[j].numverts, smd->verts[i].binds[j].vert_inds);
+
+ if (smd->verts[i].binds[j].mode == MOD_SDEF_MODE_CENTROID ||
+ smd->verts[i].binds[j].mode == MOD_SDEF_MODE_LOOPTRI) {
+ BLO_write_float3_array(writer, 1, smd->verts[i].binds[j].vert_weights);
+ }
+ else {
+ BLO_write_float_array(
+ writer, smd->verts[i].binds[j].numverts, smd->verts[i].binds[j].vert_weights);
+ }
+ }
+ }
+ }
+ }
+}
+
+static void blendRead(BlendDataReader *reader, ModifierData *md)
+{
+ SurfaceDeformModifierData *smd = (SurfaceDeformModifierData *)md;
+
+ BLO_read_data_address(reader, &smd->verts);
+
+ if (smd->verts) {
+ for (int i = 0; i < smd->numverts; i++) {
+ BLO_read_data_address(reader, &smd->verts[i].binds);
+
+ if (smd->verts[i].binds) {
+ for (int j = 0; j < smd->verts[i].numbinds; j++) {
+ BLO_read_uint32_array(
+ reader, smd->verts[i].binds[j].numverts, &smd->verts[i].binds[j].vert_inds);
+
+ if (smd->verts[i].binds[j].mode == MOD_SDEF_MODE_CENTROID ||
+ smd->verts[i].binds[j].mode == MOD_SDEF_MODE_LOOPTRI) {
+ BLO_read_float3_array(reader, 1, &smd->verts[i].binds[j].vert_weights);
+ }
+ else {
+ BLO_read_float_array(
+ reader, smd->verts[i].binds[j].numverts, &smd->verts[i].binds[j].vert_weights);
+ }
+ }
+ }
+ }
+ }
+}
+
ModifierTypeInfo modifierType_SurfaceDeform = {
/* name */ "SurfaceDeform",
/* structName */ "SurfaceDeformModifierData",
@@ -1468,6 +1528,6 @@ ModifierTypeInfo modifierType_SurfaceDeform = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
- /* blendWrite */ NULL,
- /* blendRead */ NULL,
+ /* blendWrite */ blendWrite,
+ /* blendRead */ blendRead,
};
diff --git a/source/blender/modifiers/intern/MOD_ui_common.c b/source/blender/modifiers/intern/MOD_ui_common.c
index cc0d3d8ccee..63ec9a2d929 100644
--- a/source/blender/modifiers/intern/MOD_ui_common.c
+++ b/source/blender/modifiers/intern/MOD_ui_common.c
@@ -127,7 +127,7 @@ void modifier_panel_end(uiLayout *layout, PointerRNA *ptr)
/**
* Gets RNA pointers for the active object and the panel's modifier data. Also locks
- * the layout if the modifer is from a linked object, and sets the context pointer.
+ * the layout if the modifier is from a linked object, and sets the context pointer.
*/
#define ERROR_LIBDATA_MESSAGE TIP_("External library data")
void modifier_panel_get_property_pointers(const bContext *C,
@@ -146,8 +146,7 @@ void modifier_panel_get_property_pointers(const bContext *C,
}
uiBlock *block = uiLayoutGetBlock(panel->layout);
- UI_block_lock_set(
- block, BKE_object_obdata_is_libdata(ob) || ID_IS_LINKED(ob), ERROR_LIBDATA_MESSAGE);
+ UI_block_lock_set(block, ID_IS_LINKED(ob), ERROR_LIBDATA_MESSAGE);
uiLayoutSetContextPointer(panel->layout, "modifier", r_md_ptr);
}
@@ -222,19 +221,29 @@ static bool modifier_can_delete(ModifierData *md)
return true;
}
-static void modifier_ops_extra_draw(bContext *UNUSED(C), uiLayout *layout, void *md_v)
+static void modifier_ops_extra_draw(bContext *C, uiLayout *layout, void *md_v)
{
+ PointerRNA op_ptr;
+ uiLayout *row;
ModifierData *md = (ModifierData *)md_v;
+ PointerRNA ptr;
+ Object *ob = get_modifier_object(C);
+ RNA_pointer_create(&ob->id, &RNA_Modifier, md, &ptr);
+ uiLayoutSetContextPointer(layout, "modifier", &ptr);
uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT);
+ uiLayoutSetUnitsX(layout, 4.0f);
+
+ /* Apply. */
uiItemEnumO(layout,
"OBJECT_OT_modifier_apply",
CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Apply"),
- 0,
+ ICON_CHECKMARK,
"apply_as",
MODIFIER_APPLY_DATA);
+ /* Apply as shapekey. */
if (BKE_modifier_is_same_topology(md) && !BKE_modifier_is_non_geometrical(md)) {
uiItemEnumO(layout,
"OBJECT_OT_modifier_apply",
@@ -244,6 +253,7 @@ static void modifier_ops_extra_draw(bContext *UNUSED(C), uiLayout *layout, void
MODIFIER_APPLY_SHAPE);
}
+ /* Duplicate. */
if (!ELEM(md->type,
eModifierType_Fluidsim,
eModifierType_Softbody,
@@ -256,17 +266,42 @@ static void modifier_ops_extra_draw(bContext *UNUSED(C), uiLayout *layout, void
"OBJECT_OT_modifier_copy");
}
- if (modifier_can_delete(md) && !modifier_is_simulation(md)) {
- uiItemO(layout,
- CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Delete"),
- ICON_X,
- "OBJECT_OT_modifier_remove");
+ uiItemS(layout);
+
+ /* Move to first. */
+ row = uiLayoutColumn(layout, false);
+ uiItemFullO(row,
+ "OBJECT_OT_modifier_move_to_index",
+ IFACE_("Move to First"),
+ ICON_TRIA_UP,
+ NULL,
+ WM_OP_INVOKE_DEFAULT,
+ 0,
+ &op_ptr);
+ RNA_int_set(&op_ptr, "index", 0);
+ if (!md->prev) {
+ uiLayoutSetEnabled(row, false);
+ }
+
+ /* Move to last. */
+ row = uiLayoutColumn(layout, false);
+ uiItemFullO(row,
+ "OBJECT_OT_modifier_move_to_index",
+ IFACE_("Move to Last"),
+ ICON_TRIA_DOWN,
+ NULL,
+ WM_OP_INVOKE_DEFAULT,
+ 0,
+ &op_ptr);
+ RNA_int_set(&op_ptr, "index", BLI_listbase_count(&ob->modifiers) - 1);
+ if (!md->next) {
+ uiLayoutSetEnabled(row, false);
}
}
static void modifier_panel_header(const bContext *C, Panel *panel)
{
- uiLayout *row, *sub;
+ uiLayout *row, *sub, *name_row;
uiLayout *layout = panel->layout;
PointerRNA ptr;
@@ -280,32 +315,22 @@ static void modifier_panel_header(const bContext *C, Panel *panel)
const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type);
Scene *scene = CTX_data_scene(C);
int index = panel->runtime.list_index;
- bool narrow_panel = (panel->sizex < UI_UNIT_X * 8 && panel->sizex != 0);
/* Modifier Icon. */
- row = uiLayoutRow(layout, false);
+ sub = uiLayoutRow(layout, true);
if (mti->isDisabled && mti->isDisabled(scene, md, 0)) {
- uiLayoutSetRedAlert(row, true);
+ uiLayoutSetRedAlert(sub, true);
}
- uiItemL(row, "", RNA_struct_ui_icon(ptr.type));
+ uiItemL(sub, "", RNA_struct_ui_icon(ptr.type));
- /* Modifier Name. */
- if (!narrow_panel) {
- uiItemR(layout, &ptr, "name", 0, "", ICON_NONE);
- }
+ row = uiLayoutRow(layout, true);
- /* Switch context buttons. */
- if (modifier_is_simulation(md) == 1) {
- uiItemStringO(
- layout, "", ICON_PROPERTIES, "WM_OT_properties_context_change", "context", "PHYSICS");
- }
- else if (modifier_is_simulation(md) == 2) {
- uiItemStringO(
- layout, "", ICON_PROPERTIES, "WM_OT_properties_context_change", "context", "PARTICLES");
- }
+ /* Modifier Name.
+ * Count how many buttons are added to the header to check if there is enough space. */
+ int buttons_number = 0;
+ name_row = uiLayoutRow(row, true);
- /* Mode switching buttons. */
- row = uiLayoutRow(layout, true);
+ /* Display mode switching buttons. */
if (ob->type == OB_MESH) {
int last_cage_index;
int cage_index = BKE_modifiers_get_cage_index(scene, ob, &last_cage_index, 0);
@@ -315,12 +340,14 @@ static void modifier_panel_header(const bContext *C, Panel *panel)
uiLayoutSetActive(sub, false);
}
uiItemR(sub, &ptr, "show_on_cage", 0, "", ICON_NONE);
+ buttons_number++;
}
} /* Tessellation point for curve-typed objects. */
else if (ELEM(ob->type, OB_CURVE, OB_SURF, OB_FONT)) {
if (mti->type != eModifierTypeType_Constructive) {
/* Constructive modifiers tessellates curve before applying. */
- uiItemR(layout, &ptr, "use_apply_on_spline", 0, "", ICON_NONE);
+ uiItemR(row, &ptr, "use_apply_on_spline", 0, "", ICON_NONE);
+ buttons_number++;
}
}
/* Collision and Surface are always enabled, hide buttons. */
@@ -330,15 +357,45 @@ static void modifier_panel_header(const bContext *C, Panel *panel)
sub = uiLayoutRow(row, true);
uiLayoutSetActive(sub, (md->mode & eModifierMode_Realtime));
uiItemR(sub, &ptr, "show_in_editmode", 0, "", ICON_NONE);
+ buttons_number++;
}
uiItemR(row, &ptr, "show_viewport", 0, "", ICON_NONE);
uiItemR(row, &ptr, "show_render", 0, "", ICON_NONE);
+ buttons_number += 2;
}
- row = uiLayoutRow(layout, false);
+ /* Extra operators menu. */
uiItemMenuF(row, "", ICON_DOWNARROW_HLT, modifier_ops_extra_draw, md);
- /* Some padding at the end, so the buttons aren't too close to the drag button. */
+ /* Delete button. */
+ if (modifier_can_delete(md) && !modifier_is_simulation(md)) {
+ sub = uiLayoutRow(row, false);
+ uiLayoutSetEmboss(sub, UI_EMBOSS_NONE);
+ uiItemO(sub, "", ICON_X, "OBJECT_OT_modifier_remove");
+ buttons_number++;
+ }
+
+ /* Switch context buttons. */
+ if (modifier_is_simulation(md) == 1) {
+ uiItemStringO(
+ row, "", ICON_PROPERTIES, "WM_OT_properties_context_change", "context", "PHYSICS");
+ buttons_number++;
+ }
+ else if (modifier_is_simulation(md) == 2) {
+ uiItemStringO(
+ row, "", ICON_PROPERTIES, "WM_OT_properties_context_change", "context", "PARTICLES");
+ buttons_number++;
+ }
+
+ bool display_name = (panel->sizex / UI_UNIT_X - buttons_number > 5) || (panel->sizex == 0);
+ if (display_name) {
+ uiItemR(name_row, &ptr, "name", 0, "", ICON_NONE);
+ }
+ else {
+ uiLayoutSetAlignment(row, UI_LAYOUT_ALIGN_RIGHT);
+ }
+
+ /* Extra padding for delete button. */
uiItemS(layout);
}
@@ -369,7 +426,7 @@ PanelType *modifier_panel_register(ARegionType *region_type, ModifierType type,
panel_type->poll = modifier_ui_poll;
/* Give the panel the special flag that says it was built here and corresponds to a
- * modifer rather than a PanelType. */
+ * modifier rather than a #PanelType. */
panel_type->flag = PNL_LAYOUT_HEADER_EXPAND | PNL_DRAW_BOX | PNL_INSTANCED;
panel_type->reorder = modifier_reorder;
panel_type->get_list_data_expand_flag = get_modifier_expand_flag;
@@ -381,7 +438,7 @@ PanelType *modifier_panel_register(ARegionType *region_type, ModifierType type,
}
/**
- * Add a shild panel to the parent.
+ * Add a child panel to the parent.
*
* \note To create the panel type's idname, it appends the \a name argument to the \a parent's
* idname.
diff --git a/source/blender/modifiers/intern/MOD_warp.c b/source/blender/modifiers/intern/MOD_warp.c
index b32efa3fb8f..2b67b26e620 100644
--- a/source/blender/modifiers/intern/MOD_warp.c
+++ b/source/blender/modifiers/intern/MOD_warp.c
@@ -49,6 +49,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "BLO_read_write.h"
+
#include "RNA_access.h"
#include "DEG_depsgraph.h"
@@ -510,6 +512,25 @@ static void panelRegister(ARegionType *region_type)
region_type, "texture", "Texture", NULL, texture_panel_draw, panel_type);
}
+static void blendWrite(BlendWriter *writer, const ModifierData *md)
+{
+ const WarpModifierData *tmd = (const WarpModifierData *)md;
+
+ if (tmd->curfalloff) {
+ BKE_curvemapping_blend_write(writer, tmd->curfalloff);
+ }
+}
+
+static void blendRead(BlendDataReader *reader, ModifierData *md)
+{
+ WarpModifierData *tmd = (WarpModifierData *)md;
+
+ BLO_read_data_address(reader, &tmd->curfalloff);
+ if (tmd->curfalloff) {
+ BKE_curvemapping_blend_read(reader, tmd->curfalloff);
+ }
+}
+
ModifierTypeInfo modifierType_Warp = {
/* name */ "Warp",
/* structName */ "WarpModifierData",
@@ -540,6 +561,6 @@ ModifierTypeInfo modifierType_Warp = {
/* foreachTexLink */ foreachTexLink,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
- /* blendWrite */ NULL,
- /* blendRead */ NULL,
+ /* blendWrite */ blendWrite,
+ /* blendRead */ blendRead,
};
diff --git a/source/blender/modifiers/intern/MOD_wave.c b/source/blender/modifiers/intern/MOD_wave.c
index c1fe8e1f009..789b65dbde3 100644
--- a/source/blender/modifiers/intern/MOD_wave.c
+++ b/source/blender/modifiers/intern/MOD_wave.c
@@ -388,7 +388,7 @@ static void panel_draw(const bContext *C, Panel *panel)
uiLayoutSetPropSep(layout, true);
- row = uiLayoutRowWithHeading(layout, true, "Motion");
+ row = uiLayoutRowWithHeading(layout, true, IFACE_("Motion"));
uiItemR(row, &ptr, "use_x", UI_ITEM_R_TOGGLE | UI_ITEM_R_FORCE_BLANK_DECORATE, NULL, ICON_NONE);
uiItemR(row, &ptr, "use_y", UI_ITEM_R_TOGGLE | UI_ITEM_R_FORCE_BLANK_DECORATE, NULL, ICON_NONE);
diff --git a/source/blender/modifiers/intern/MOD_weightvgedit.c b/source/blender/modifiers/intern/MOD_weightvgedit.c
index 9dfc2f653cd..8039856172a 100644
--- a/source/blender/modifiers/intern/MOD_weightvgedit.c
+++ b/source/blender/modifiers/intern/MOD_weightvgedit.c
@@ -47,6 +47,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "BLO_read_write.h"
+
#include "RNA_access.h"
#include "DEG_depsgraph_build.h"
@@ -399,6 +401,25 @@ static void panelRegister(ARegionType *region_type)
region_type, "influence", "Influence", NULL, influence_panel_draw, panel_type);
}
+static void blendWrite(BlendWriter *writer, const ModifierData *md)
+{
+ const WeightVGEditModifierData *wmd = (const WeightVGEditModifierData *)md;
+
+ if (wmd->cmap_curve) {
+ BKE_curvemapping_blend_write(writer, wmd->cmap_curve);
+ }
+}
+
+static void blendRead(BlendDataReader *reader, ModifierData *md)
+{
+ WeightVGEditModifierData *wmd = (WeightVGEditModifierData *)md;
+
+ BLO_read_data_address(reader, &wmd->cmap_curve);
+ if (wmd->cmap_curve) {
+ BKE_curvemapping_blend_read(reader, wmd->cmap_curve);
+ }
+}
+
ModifierTypeInfo modifierType_WeightVGEdit = {
/* name */ "VertexWeightEdit",
/* structName */ "WeightVGEditModifierData",
@@ -430,6 +451,6 @@ ModifierTypeInfo modifierType_WeightVGEdit = {
/* foreachTexLink */ foreachTexLink,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
- /* blendWrite */ NULL,
- /* blendRead */ NULL,
+ /* blendWrite */ blendWrite,
+ /* blendRead */ blendRead,
};
diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt
index 80bf0f7c5e2..31b5e922dab 100644
--- a/source/blender/nodes/CMakeLists.txt
+++ b/source/blender/nodes/CMakeLists.txt
@@ -273,7 +273,7 @@ set(SRC
intern/node_common.c
intern/node_exec.c
- intern/node_socket.c
+ intern/node_socket.cc
intern/node_util.c
composite/node_composite_util.h
diff --git a/source/blender/nodes/NOD_composite.h b/source/blender/nodes/NOD_composite.h
index 534e9012693..6b1dd239294 100644
--- a/source/blender/nodes/NOD_composite.h
+++ b/source/blender/nodes/NOD_composite.h
@@ -26,6 +26,10 @@
#include "BKE_node.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
extern struct bNodeTreeType *ntreeType_Composite;
/* ****************** types array for all composite nodes ****************** */
@@ -143,4 +147,8 @@ const char *node_cmp_rlayers_sock_to_pass(int sock_index);
void register_node_type_cmp_custom_group(bNodeType *ntype);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/source/blender/nodes/NOD_shader.h b/source/blender/nodes/NOD_shader.h
index a89a9e927b9..bf548aea5f4 100644
--- a/source/blender/nodes/NOD_shader.h
+++ b/source/blender/nodes/NOD_shader.h
@@ -26,6 +26,10 @@
#include "BKE_node.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
extern struct bNodeTreeType *ntreeType_Shader;
/* the type definitions array */
@@ -139,4 +143,8 @@ void register_node_type_sh_tex_white_noise(void);
void register_node_type_sh_custom_group(bNodeType *ntype);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/source/blender/nodes/NOD_socket.h b/source/blender/nodes/NOD_socket.h
index b1b1a0e40c6..ce6f0da4aee 100644
--- a/source/blender/nodes/NOD_socket.h
+++ b/source/blender/nodes/NOD_socket.h
@@ -35,6 +35,10 @@
struct bNode;
struct bNodeTree;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
struct bNodeSocket *node_add_socket_from_template(struct bNodeTree *ntree,
struct bNode *node,
struct bNodeSocketTemplate *stemp,
@@ -46,4 +50,8 @@ void node_socket_init_default_value(struct bNodeSocket *sock);
void node_socket_copy_default_value(struct bNodeSocket *to, const struct bNodeSocket *from);
void register_standard_node_socket_types(void);
+#ifdef __cplusplus
+}
#endif
+
+#endif /* __NOD_SOCKET_H__ */
diff --git a/source/blender/nodes/NOD_texture.h b/source/blender/nodes/NOD_texture.h
index eaa4952e7e6..07a05f01bc5 100644
--- a/source/blender/nodes/NOD_texture.h
+++ b/source/blender/nodes/NOD_texture.h
@@ -26,6 +26,10 @@
#include "BKE_node.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
extern struct bNodeTreeType *ntreeType_Texture;
/* ****************** types array for all texture nodes ****************** */
@@ -71,4 +75,8 @@ void register_node_type_tex_proc_noise(void);
void register_node_type_tex_proc_stucci(void);
void register_node_type_tex_proc_distnoise(void);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/source/blender/nodes/composite/node_composite_util.h b/source/blender/nodes/composite/node_composite_util.h
index 4372ef78bc0..0edc864e98f 100644
--- a/source/blender/nodes/composite/node_composite_util.h
+++ b/source/blender/nodes/composite/node_composite_util.h
@@ -49,6 +49,10 @@
/* only for forward declarations */
#include "NOD_composite.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#define CMP_SCALE_MAX 12000
bool cmp_node_poll_default(struct bNodeType *ntype, struct bNodeTree *ntree);
@@ -56,4 +60,8 @@ void cmp_node_update_default(struct bNodeTree *UNUSED(ntree), struct bNode *node
void cmp_node_type_base(
struct bNodeType *ntype, int type, const char *name, short nclass, short flag);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __NODE_COMPOSITE_UTIL_H__ */
diff --git a/source/blender/nodes/intern/node_common.c b/source/blender/nodes/intern/node_common.c
index 765fa84439f..996fb93eb76 100644
--- a/source/blender/nodes/intern/node_common.c
+++ b/source/blender/nodes/intern/node_common.c
@@ -125,7 +125,7 @@ static bNodeSocket *group_verify_socket(
bNodeSocket *sock;
for (sock = verify_lb->first; sock; sock = sock->next) {
- if (STREQ(sock->identifier, iosock->identifier)) {
+ if (sock->typeinfo == iosock->typeinfo && STREQ(sock->identifier, iosock->identifier)) {
break;
}
}
diff --git a/source/blender/nodes/intern/node_exec.h b/source/blender/nodes/intern/node_exec.h
index 07c12dc7c46..87a61f0a490 100644
--- a/source/blender/nodes/intern/node_exec.h
+++ b/source/blender/nodes/intern/node_exec.h
@@ -34,6 +34,10 @@
#include "RNA_types.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
struct bNode;
struct bNodeStack;
struct bNodeTree;
@@ -98,4 +102,8 @@ struct bNodeTreeExec *ntreeTexBeginExecTree_internal(struct bNodeExecContext *co
bNodeInstanceKey parent_key);
void ntreeTexEndExecTree_internal(struct bNodeTreeExec *exec);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/source/blender/nodes/intern/node_socket.c b/source/blender/nodes/intern/node_socket.cc
index 668dd3829cc..b23511c3bdb 100644
--- a/source/blender/nodes/intern/node_socket.c
+++ b/source/blender/nodes/intern/node_socket.cc
@@ -53,26 +53,26 @@ struct bNodeSocket *node_add_socket_from_template(struct bNodeTree *ntree,
/* initialize default_value */
switch (stemp->type) {
case SOCK_FLOAT: {
- bNodeSocketValueFloat *dval = sock->default_value;
+ bNodeSocketValueFloat *dval = (bNodeSocketValueFloat *)sock->default_value;
dval->value = stemp->val1;
dval->min = stemp->min;
dval->max = stemp->max;
break;
}
case SOCK_INT: {
- bNodeSocketValueInt *dval = sock->default_value;
+ bNodeSocketValueInt *dval = (bNodeSocketValueInt *)sock->default_value;
dval->value = (int)stemp->val1;
dval->min = (int)stemp->min;
dval->max = (int)stemp->max;
break;
}
case SOCK_BOOLEAN: {
- bNodeSocketValueBoolean *dval = sock->default_value;
+ bNodeSocketValueBoolean *dval = (bNodeSocketValueBoolean *)sock->default_value;
dval->value = (int)stemp->val1;
break;
}
case SOCK_VECTOR: {
- bNodeSocketValueVector *dval = sock->default_value;
+ bNodeSocketValueVector *dval = (bNodeSocketValueVector *)sock->default_value;
dval->value[0] = stemp->val1;
dval->value[1] = stemp->val2;
dval->value[2] = stemp->val3;
@@ -81,7 +81,7 @@ struct bNodeSocket *node_add_socket_from_template(struct bNodeTree *ntree,
break;
}
case SOCK_RGBA: {
- bNodeSocketValueRGBA *dval = sock->default_value;
+ bNodeSocketValueRGBA *dval = (bNodeSocketValueRGBA *)sock->default_value;
dval->value[0] = stemp->val1;
dval->value[1] = stemp->val2;
dval->value[2] = stemp->val3;
@@ -98,7 +98,7 @@ static bNodeSocket *verify_socket_template(
{
bNodeSocket *sock;
- for (sock = socklist->first; sock; sock = sock->next) {
+ for (sock = (bNodeSocket *)socklist->first; sock; sock = sock->next) {
if (STREQLEN(sock->name, stemp->name, NODE_MAXSTR)) {
break;
}
@@ -153,12 +153,11 @@ static void verify_socket_template_list(bNodeTree *ntree,
/* and we put back the verified sockets */
stemp = stemp_first;
if (socklist->first) {
- /* some dynamic sockets left, store the list start
- * so we can add static sockets infront of it.
- */
- sock = socklist->first;
+ /* Some dynamic sockets left, store the list start
+ * so we can add static sockets in front of it. */
+ sock = (bNodeSocket *)socklist->first;
while (stemp->type != -1) {
- /* put static sockets infront of dynamic */
+ /* Put static sockets in front of dynamic. */
BLI_insertlinkbefore(socklist, sock, stemp->sock);
stemp++;
}
@@ -201,8 +200,8 @@ void node_socket_init_default_value(bNodeSocket *sock)
switch (type) {
case SOCK_FLOAT: {
- bNodeSocketValueFloat *dval = MEM_callocN(sizeof(bNodeSocketValueFloat),
- "node socket value float");
+ bNodeSocketValueFloat *dval = (bNodeSocketValueFloat *)MEM_callocN(
+ sizeof(bNodeSocketValueFloat), "node socket value float");
dval->subtype = subtype;
dval->value = 0.0f;
dval->min = -FLT_MAX;
@@ -212,8 +211,8 @@ void node_socket_init_default_value(bNodeSocket *sock)
break;
}
case SOCK_INT: {
- bNodeSocketValueInt *dval = MEM_callocN(sizeof(bNodeSocketValueInt),
- "node socket value int");
+ bNodeSocketValueInt *dval = (bNodeSocketValueInt *)MEM_callocN(sizeof(bNodeSocketValueInt),
+ "node socket value int");
dval->subtype = subtype;
dval->value = 0;
dval->min = INT_MIN;
@@ -223,8 +222,8 @@ void node_socket_init_default_value(bNodeSocket *sock)
break;
}
case SOCK_BOOLEAN: {
- bNodeSocketValueBoolean *dval = MEM_callocN(sizeof(bNodeSocketValueBoolean),
- "node socket value bool");
+ bNodeSocketValueBoolean *dval = (bNodeSocketValueBoolean *)MEM_callocN(
+ sizeof(bNodeSocketValueBoolean), "node socket value bool");
dval->value = false;
sock->default_value = dval;
@@ -232,8 +231,8 @@ void node_socket_init_default_value(bNodeSocket *sock)
}
case SOCK_VECTOR: {
static float default_value[] = {0.0f, 0.0f, 0.0f};
- bNodeSocketValueVector *dval = MEM_callocN(sizeof(bNodeSocketValueVector),
- "node socket value vector");
+ bNodeSocketValueVector *dval = (bNodeSocketValueVector *)MEM_callocN(
+ sizeof(bNodeSocketValueVector), "node socket value vector");
dval->subtype = subtype;
copy_v3_v3(dval->value, default_value);
dval->min = -FLT_MAX;
@@ -244,16 +243,16 @@ void node_socket_init_default_value(bNodeSocket *sock)
}
case SOCK_RGBA: {
static float default_value[] = {0.0f, 0.0f, 0.0f, 1.0f};
- bNodeSocketValueRGBA *dval = MEM_callocN(sizeof(bNodeSocketValueRGBA),
- "node socket value color");
+ bNodeSocketValueRGBA *dval = (bNodeSocketValueRGBA *)MEM_callocN(
+ sizeof(bNodeSocketValueRGBA), "node socket value color");
copy_v4_v4(dval->value, default_value);
sock->default_value = dval;
break;
}
case SOCK_STRING: {
- bNodeSocketValueString *dval = MEM_callocN(sizeof(bNodeSocketValueString),
- "node socket value string");
+ bNodeSocketValueString *dval = (bNodeSocketValueString *)MEM_callocN(
+ sizeof(bNodeSocketValueString), "node socket value string");
dval->subtype = subtype;
dval->value[0] = '\0';
@@ -261,16 +260,16 @@ void node_socket_init_default_value(bNodeSocket *sock)
break;
}
case SOCK_OBJECT: {
- bNodeSocketValueObject *dval = MEM_callocN(sizeof(bNodeSocketValueObject),
- "node socket value object");
+ bNodeSocketValueObject *dval = (bNodeSocketValueObject *)MEM_callocN(
+ sizeof(bNodeSocketValueObject), "node socket value object");
dval->value = NULL;
sock->default_value = dval;
break;
}
case SOCK_IMAGE: {
- bNodeSocketValueImage *dval = MEM_callocN(sizeof(bNodeSocketValueImage),
- "node socket value image");
+ bNodeSocketValueImage *dval = (bNodeSocketValueImage *)MEM_callocN(
+ sizeof(bNodeSocketValueImage), "node socket value image");
dval->value = NULL;
sock->default_value = dval;
@@ -299,51 +298,51 @@ void node_socket_copy_default_value(bNodeSocket *to, const bNodeSocket *from)
switch (from->typeinfo->type) {
case SOCK_FLOAT: {
- bNodeSocketValueFloat *toval = to->default_value;
- bNodeSocketValueFloat *fromval = from->default_value;
+ bNodeSocketValueFloat *toval = (bNodeSocketValueFloat *)to->default_value;
+ bNodeSocketValueFloat *fromval = (bNodeSocketValueFloat *)from->default_value;
*toval = *fromval;
break;
}
case SOCK_INT: {
- bNodeSocketValueInt *toval = to->default_value;
- bNodeSocketValueInt *fromval = from->default_value;
+ bNodeSocketValueInt *toval = (bNodeSocketValueInt *)to->default_value;
+ bNodeSocketValueInt *fromval = (bNodeSocketValueInt *)from->default_value;
*toval = *fromval;
break;
}
case SOCK_BOOLEAN: {
- bNodeSocketValueBoolean *toval = to->default_value;
- bNodeSocketValueBoolean *fromval = from->default_value;
+ bNodeSocketValueBoolean *toval = (bNodeSocketValueBoolean *)to->default_value;
+ bNodeSocketValueBoolean *fromval = (bNodeSocketValueBoolean *)from->default_value;
*toval = *fromval;
break;
}
case SOCK_VECTOR: {
- bNodeSocketValueVector *toval = to->default_value;
- bNodeSocketValueVector *fromval = from->default_value;
+ bNodeSocketValueVector *toval = (bNodeSocketValueVector *)to->default_value;
+ bNodeSocketValueVector *fromval = (bNodeSocketValueVector *)from->default_value;
*toval = *fromval;
break;
}
case SOCK_RGBA: {
- bNodeSocketValueRGBA *toval = to->default_value;
- bNodeSocketValueRGBA *fromval = from->default_value;
+ bNodeSocketValueRGBA *toval = (bNodeSocketValueRGBA *)to->default_value;
+ bNodeSocketValueRGBA *fromval = (bNodeSocketValueRGBA *)from->default_value;
*toval = *fromval;
break;
}
case SOCK_STRING: {
- bNodeSocketValueString *toval = to->default_value;
- bNodeSocketValueString *fromval = from->default_value;
+ bNodeSocketValueString *toval = (bNodeSocketValueString *)to->default_value;
+ bNodeSocketValueString *fromval = (bNodeSocketValueString *)from->default_value;
*toval = *fromval;
break;
}
case SOCK_OBJECT: {
- bNodeSocketValueObject *toval = to->default_value;
- bNodeSocketValueObject *fromval = from->default_value;
+ bNodeSocketValueObject *toval = (bNodeSocketValueObject *)to->default_value;
+ bNodeSocketValueObject *fromval = (bNodeSocketValueObject *)from->default_value;
*toval = *fromval;
id_us_plus(&toval->value->id);
break;
}
case SOCK_IMAGE: {
- bNodeSocketValueImage *toval = to->default_value;
- bNodeSocketValueImage *fromval = from->default_value;
+ bNodeSocketValueImage *toval = (bNodeSocketValueImage *)to->default_value;
+ bNodeSocketValueImage *fromval = (bNodeSocketValueImage *)from->default_value;
*toval = *fromval;
id_us_plus(&toval->value->id);
break;
@@ -390,22 +389,22 @@ static void standard_node_socket_interface_verify_socket(bNodeTree *UNUSED(ntree
switch (stemp->typeinfo->type) {
case SOCK_FLOAT: {
- bNodeSocketValueFloat *toval = sock->default_value;
- bNodeSocketValueFloat *fromval = stemp->default_value;
+ bNodeSocketValueFloat *toval = (bNodeSocketValueFloat *)sock->default_value;
+ bNodeSocketValueFloat *fromval = (bNodeSocketValueFloat *)stemp->default_value;
toval->min = fromval->min;
toval->max = fromval->max;
break;
}
case SOCK_INT: {
- bNodeSocketValueInt *toval = sock->default_value;
- bNodeSocketValueInt *fromval = stemp->default_value;
+ bNodeSocketValueInt *toval = (bNodeSocketValueInt *)sock->default_value;
+ bNodeSocketValueInt *fromval = (bNodeSocketValueInt *)stemp->default_value;
toval->min = fromval->min;
toval->max = fromval->max;
break;
}
case SOCK_VECTOR: {
- bNodeSocketValueVector *toval = sock->default_value;
- bNodeSocketValueVector *fromval = stemp->default_value;
+ bNodeSocketValueVector *toval = (bNodeSocketValueVector *)sock->default_value;
+ bNodeSocketValueVector *fromval = (bNodeSocketValueVector *)stemp->default_value;
toval->min = fromval->min;
toval->max = fromval->max;
break;
@@ -423,16 +422,16 @@ static void standard_node_socket_interface_from_socket(bNodeTree *UNUSED(ntree),
node_socket_copy_default_value(stemp, sock);
}
+extern "C" void ED_init_standard_node_socket_type(bNodeSocketType *);
+
static bNodeSocketType *make_standard_socket_type(int type, int subtype)
{
- extern void ED_init_standard_node_socket_type(bNodeSocketType *);
-
const char *socket_idname = nodeStaticSocketType(type, subtype);
const char *interface_idname = nodeStaticSocketInterfaceType(type, subtype);
bNodeSocketType *stype;
StructRNA *srna;
- stype = MEM_callocN(sizeof(bNodeSocketType), "node socket C type");
+ stype = (bNodeSocketType *)MEM_callocN(sizeof(bNodeSocketType), "node socket C type");
stype->free_self = (void (*)(bNodeSocketType * stype)) MEM_freeN;
BLI_strncpy(stype->idname, socket_idname, sizeof(stype->idname));
@@ -467,15 +466,15 @@ static bNodeSocketType *make_standard_socket_type(int type, int subtype)
return stype;
}
+extern "C" void ED_init_node_socket_type_virtual(bNodeSocketType *);
+
static bNodeSocketType *make_socket_type_virtual(void)
{
- extern void ED_init_node_socket_type_virtual(bNodeSocketType *);
-
const char *socket_idname = "NodeSocketVirtual";
bNodeSocketType *stype;
StructRNA *srna;
- stype = MEM_callocN(sizeof(bNodeSocketType), "node socket C type");
+ stype = (bNodeSocketType *)MEM_callocN(sizeof(bNodeSocketType), "node socket C type");
stype->free_self = (void (*)(bNodeSocketType * stype)) MEM_freeN;
BLI_strncpy(stype->idname, socket_idname, sizeof(stype->idname));
diff --git a/source/blender/nodes/shader/node_shader_util.h b/source/blender/nodes/shader/node_shader_util.h
index 8834de0633e..fbb9979cdfa 100644
--- a/source/blender/nodes/shader/node_shader_util.h
+++ b/source/blender/nodes/shader/node_shader_util.h
@@ -69,6 +69,10 @@
#include "GPU_texture.h"
#include "GPU_uniformbuffer.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
bool sh_node_poll_default(struct bNodeType *ntype, struct bNodeTree *ntree);
void sh_node_type_base(
struct bNodeType *ntype, int type, const char *name, short nclass, short flag);
@@ -101,4 +105,8 @@ void ntreeExecGPUNodes(struct bNodeTreeExec *exec,
struct GPUMaterial *mat,
struct bNode *output_node);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_sky.c b/source/blender/nodes/shader/nodes/node_shader_tex_sky.c
index cb91e086cf4..0daa948c139 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_sky.c
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_sky.c
@@ -41,8 +41,15 @@ static void node_shader_init_tex_sky(bNodeTree *UNUSED(ntree), bNode *node)
tex->sun_direction[2] = 1.0f;
tex->turbidity = 2.2f;
tex->ground_albedo = 0.3f;
- tex->sky_model = SHD_SKY_NEW;
-
+ tex->sun_disc = true;
+ tex->sun_size = DEG2RADF(0.545);
+ tex->sun_elevation = M_PI_2;
+ tex->sun_rotation = 0.0f;
+ tex->altitude = 0;
+ tex->air_density = 1.0f;
+ tex->dust_density = 1.0f;
+ tex->ozone_density = 1.0f;
+ tex->sky_model = SHD_SKY_NISHITA;
node->storage = tex;
}
diff --git a/source/blender/nodes/texture/node_texture_util.h b/source/blender/nodes/texture/node_texture_util.h
index b3407f68857..7b8581c1f89 100644
--- a/source/blender/nodes/texture/node_texture_util.h
+++ b/source/blender/nodes/texture/node_texture_util.h
@@ -64,6 +64,10 @@
#include "RE_pipeline.h"
#include "RE_shader_ext.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
typedef struct TexCallData {
TexResult *target;
/* all float[3] */
@@ -124,4 +128,8 @@ void tex_do_preview(bNodePreview *preview,
void params_from_cdata(TexParams *out, TexCallData *in);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/source/blender/physics/intern/BPH_mass_spring.cpp b/source/blender/physics/intern/BPH_mass_spring.cpp
index 18fab5215a6..051f11aa1d9 100644
--- a/source/blender/physics/intern/BPH_mass_spring.cpp
+++ b/source/blender/physics/intern/BPH_mass_spring.cpp
@@ -137,6 +137,35 @@ static float cloth_calc_volume(ClothModifierData *clmd)
return vol;
}
+static float cloth_calc_rest_volume(ClothModifierData *clmd)
+{
+ /* Calculate the (closed) cloth volume. */
+ Cloth *cloth = clmd->clothObject;
+ const MVertTri *tri = cloth->tri;
+ const ClothVertex *v = cloth->verts;
+ float weights[3] = {1.0f, 1.0f, 1.0f};
+ float vol = 0;
+
+ /* Early exit for hair, as it never has volume. */
+ if (clmd->hairdata) {
+ return 0.0f;
+ }
+
+ for (unsigned int i = 0; i < cloth->primitive_num; i++) {
+ const MVertTri *vt = &tri[i];
+
+ if (cloth_get_pressure_weights(clmd, vt, weights)) {
+ vol += volume_tri_tetrahedron_signed_v3_6x(
+ v[vt->tri[0]].xrest, v[vt->tri[1]].xrest, v[vt->tri[2]].xrest);
+ }
+ }
+
+ /* We need to divide by 6 to get the actual volume. */
+ vol = vol / 6.0f;
+
+ return vol;
+}
+
static float cloth_calc_average_pressure(ClothModifierData *clmd, const float *vertex_pressure)
{
Cloth *cloth = clmd->clothObject;
@@ -219,7 +248,7 @@ void BKE_cloth_solver_set_volume(ClothModifierData *clmd)
{
Cloth *cloth = clmd->clothObject;
- cloth->initial_mesh_volume = cloth_calc_volume(clmd);
+ cloth->initial_mesh_volume = cloth_calc_rest_volume(clmd);
}
/* Init constraint matrix
@@ -683,23 +712,42 @@ static void cloth_calc_force(
/* handle external forces like wind */
if (effectors) {
+ bool is_not_hair = (clmd->hairdata == NULL) && (cloth->primitive_num > 0);
+ bool has_wind = false, has_force = false;
+
/* cache per-vertex forces to avoid redundant calculation */
- float(*winvec)[3] = (float(*)[3])MEM_callocN(sizeof(float[3]) * mvert_num, "effector forces");
+ float(*winvec)[3] = (float(*)[3])MEM_callocN(sizeof(float[3]) * mvert_num * 2,
+ "effector forces");
+ float(*forcevec)[3] = is_not_hair ? winvec + mvert_num : winvec;
+
for (i = 0; i < cloth->mvert_num; i++) {
float x[3], v[3];
EffectedPoint epoint;
BPH_mass_spring_get_motion_state(data, i, x, v);
pd_point_from_loc(scene, x, v, i, &epoint);
- BKE_effectors_apply(
- effectors, NULL, clmd->sim_parms->effector_weights, &epoint, winvec[i], NULL);
+ BKE_effectors_apply(effectors,
+ NULL,
+ clmd->sim_parms->effector_weights,
+ &epoint,
+ forcevec[i],
+ winvec[i],
+ NULL);
+
+ has_wind = has_wind || !is_zero_v3(winvec[i]);
+ has_force = has_force || !is_zero_v3(forcevec[i]);
}
/* Hair has only edges. */
- if ((clmd->hairdata == NULL) && (cloth->primitive_num > 0)) {
+ if (is_not_hair) {
for (i = 0; i < cloth->primitive_num; i++) {
const MVertTri *vt = &tri[i];
- BPH_mass_spring_force_face_wind(data, vt->tri[0], vt->tri[1], vt->tri[2], winvec);
+ if (has_wind) {
+ BPH_mass_spring_force_face_wind(data, vt->tri[0], vt->tri[1], vt->tri[2], winvec);
+ }
+ if (has_force) {
+ BPH_mass_spring_force_face_extern(data, vt->tri[0], vt->tri[1], vt->tri[2], forcevec);
+ }
}
}
else {
diff --git a/source/blender/physics/intern/implicit.h b/source/blender/physics/intern/implicit.h
index 69b50f7fa48..8bc09755180 100644
--- a/source/blender/physics/intern/implicit.h
+++ b/source/blender/physics/intern/implicit.h
@@ -126,9 +126,12 @@ void BPH_mass_spring_force_drag(struct Implicit_Data *data, float drag);
/* Custom external force */
void BPH_mass_spring_force_extern(
struct Implicit_Data *data, int i, const float f[3], float dfdx[3][3], float dfdv[3][3]);
-/* Wind force, acting on a face */
+/* Wind force, acting on a face (only generates pressure from the normal component) */
void BPH_mass_spring_force_face_wind(
struct Implicit_Data *data, int v1, int v2, int v3, const float (*winvec)[3]);
+/* Arbitrary per-unit-area vector force field acting on a face. */
+void BPH_mass_spring_force_face_extern(
+ struct Implicit_Data *data, int v1, int v2, int v3, const float (*forcevec)[3]);
/* Wind force, acting on an edge */
void BPH_mass_spring_force_edge_wind(struct Implicit_Data *data,
int v1,
diff --git a/source/blender/physics/intern/implicit_blender.c b/source/blender/physics/intern/implicit_blender.c
index 063c224f158..5ec4c750d5d 100644
--- a/source/blender/physics/intern/implicit_blender.c
+++ b/source/blender/physics/intern/implicit_blender.c
@@ -1469,22 +1469,71 @@ void BPH_mass_spring_force_face_wind(
Implicit_Data *data, int v1, int v2, int v3, const float (*winvec)[3])
{
const float effector_scale = 0.02f;
+ int vs[3] = {v1, v2, v3};
float win[3], nor[3], area;
- float factor;
+ float factor, base_force;
+ float force[3];
/* calculate face normal and area */
area = calc_nor_area_tri(nor, data->X[v1], data->X[v2], data->X[v3]);
/* The force is calculated and split up evenly for each of the three face verts */
factor = effector_scale * area / 3.0f;
- world_to_root_v3(data, v1, win, winvec[v1]);
- madd_v3_v3fl(data->F[v1], nor, factor * dot_v3v3(win, nor));
+ /* Calculate wind pressure at each vertex by projecting the wind field on the normal. */
+ for (int i = 0; i < 3; i++) {
+ world_to_root_v3(data, vs[i], win, winvec[vs[i]]);
- world_to_root_v3(data, v2, win, winvec[v2]);
- madd_v3_v3fl(data->F[v2], nor, factor * dot_v3v3(win, nor));
+ force[i] = dot_v3v3(win, nor);
+ }
- world_to_root_v3(data, v3, win, winvec[v3]);
- madd_v3_v3fl(data->F[v3], nor, factor * dot_v3v3(win, nor));
+ /* Compute per-vertex force values from local pressures.
+ * From integrating the pressure over the triangle and deriving
+ * equivalent vertex forces, it follows that:
+ *
+ * force[idx] = (sum(pressure) + pressure[idx]) * area / 12
+ *
+ * Effectively, 1/4 of the pressure acts just on its vertex,
+ * while 3/4 is split evenly over all three.
+ */
+ mul_v3_fl(force, factor / 4.0f);
+
+ base_force = force[0] + force[1] + force[2];
+
+ /* add pressure to each of the face verts */
+ madd_v3_v3fl(data->F[v1], nor, base_force + force[0]);
+ madd_v3_v3fl(data->F[v2], nor, base_force + force[1]);
+ madd_v3_v3fl(data->F[v3], nor, base_force + force[2]);
+}
+
+void BPH_mass_spring_force_face_extern(
+ Implicit_Data *data, int v1, int v2, int v3, const float (*forcevec)[3])
+{
+ const float effector_scale = 0.02f;
+ int vs[3] = {v1, v2, v3};
+ float nor[3], area;
+ float factor, base_force[3];
+ float force[3][3];
+
+ /* calculate face normal and area */
+ area = calc_nor_area_tri(nor, data->X[v1], data->X[v2], data->X[v3]);
+ /* The force is calculated and split up evenly for each of the three face verts */
+ factor = effector_scale * area / 3.0f;
+
+ /* Compute common and per-vertex force vectors from the original inputs. */
+ zero_v3(base_force);
+
+ for (int i = 0; i < 3; i++) {
+ world_to_root_v3(data, vs[i], force[i], forcevec[vs[i]]);
+
+ mul_v3_fl(force[i], factor / 4.0f);
+ add_v3_v3(base_force, force[i]);
+ }
+
+ /* Apply the common and vertex components to all vertices. */
+ for (int i = 0; i < 3; i++) {
+ add_v3_v3(force[i], base_force);
+ add_v3_v3(data->F[vs[i]], force[i]);
+ }
}
float BPH_tri_tetra_volume_signed_6x(Implicit_Data *data, int v1, int v2, int v3)
diff --git a/source/blender/python/bmesh/bmesh_py_types_customdata.c b/source/blender/python/bmesh/bmesh_py_types_customdata.c
index 340286191b8..8615da653ae 100644
--- a/source/blender/python/bmesh/bmesh_py_types_customdata.c
+++ b/source/blender/python/bmesh/bmesh_py_types_customdata.c
@@ -98,7 +98,8 @@ PyDoc_STRVAR(
PyDoc_STRVAR(bpy_bmlayeraccess_collection__bevel_weight_doc,
"Bevel weight float in [0 - 1].\n\n:type: :class:`BMLayerCollection`");
PyDoc_STRVAR(bpy_bmlayeraccess_collection__crease_doc,
- "Edge crease for subdivision surface - float in [0 - 1].\n\n:type: :class:`BMLayerCollection`");
+ "Edge crease for subdivision surface - float in [0 - 1].\n\n:type: "
+ ":class:`BMLayerCollection`");
PyDoc_STRVAR(
bpy_bmlayeraccess_collection__uv_doc,
"Accessor for :class:`BMLoopUV` UV (as a 2D Vector).\n\ntype: :class:`BMLayerCollection`");
diff --git a/source/blender/python/intern/bpy_library_write.c b/source/blender/python/intern/bpy_library_write.c
index fec0cef7b05..66d20dd357f 100644
--- a/source/blender/python/intern/bpy_library_write.c
+++ b/source/blender/python/intern/bpy_library_write.c
@@ -35,6 +35,8 @@
#include "BKE_main.h"
#include "BKE_report.h"
+#include "BLO_writefile.h"
+
#include "RNA_types.h"
#include "bpy_capi_utils.h"
@@ -45,8 +47,7 @@
PyDoc_STRVAR(
bpy_lib_write_doc,
- ".. method:: write(filepath, datablocks, relative_remap=False, fake_user=False, "
- "compress=False)\n"
+ ".. method:: write(filepath, datablocks, path_remap=False, fake_user=False, compress=False)\n"
"\n"
" Write data-blocks into a blend file.\n"
"\n"
@@ -58,8 +59,14 @@ PyDoc_STRVAR(
" :type filepath: string\n"
" :arg datablocks: set of data-blocks (:class:`bpy.types.ID` instances).\n"
" :type datablocks: set\n"
- " :arg relative_remap: When True, make paths relative to the current blend-file.\n"
- " :type relative_remap: bool\n"
+ " :arg path_remap: Optionally remap paths when writing the file:\n"
+ "\n"
+ " - ``NONE`` No path manipulation (default).\n"
+ " - ``RELATIVE`` Remap paths that are already relative to the new location.\n"
+ " - ``RELATIVE_ALL`` Remap all paths to be relative to the new location.\n"
+ " - ``ABSOLUTE`` Make all paths absolute on writing.\n"
+ "\n"
+ " :type path_remap: string\n"
" :arg fake_user: When True, data-blocks will be written with fake-user flag enabled.\n"
" :type fake_user: bool\n"
" :arg compress: When True, write a compressed blend file.\n"
@@ -70,13 +77,23 @@ static PyObject *bpy_lib_write(PyObject *UNUSED(self), PyObject *args, PyObject
const char *filepath;
char filepath_abs[FILE_MAX];
PyObject *datablocks = NULL;
- bool use_relative_remap = false, use_fake_user = false, use_compress = false;
+
+ const struct PyC_StringEnumItems path_remap_items[] = {
+ {BLO_WRITE_PATH_REMAP_NONE, "NONE"},
+ {BLO_WRITE_PATH_REMAP_RELATIVE, "RELATIVE"},
+ {BLO_WRITE_PATH_REMAP_RELATIVE_ALL, "RELATIVE_ALL"},
+ {BLO_WRITE_PATH_REMAP_ABSOLUTE, "ABSOLUTE"},
+ {0, NULL},
+ };
+ struct PyC_StringEnum path_remap = {path_remap_items, BLO_WRITE_PATH_REMAP_NONE};
+
+ bool use_fake_user = false, use_compress = false;
static const char *_keywords[] = {
"filepath",
"datablocks",
/* optional */
- "relative_remap",
+ "path_remap",
"fake_user",
"compress",
NULL,
@@ -88,8 +105,8 @@ static PyObject *bpy_lib_write(PyObject *UNUSED(self), PyObject *args, PyObject
&filepath,
&PySet_Type,
&datablocks,
- PyC_ParseBool,
- &use_relative_remap,
+ PyC_ParseStringEnum,
+ &path_remap,
PyC_ParseBool,
&use_fake_user,
PyC_ParseBool,
@@ -100,10 +117,6 @@ static PyObject *bpy_lib_write(PyObject *UNUSED(self), PyObject *args, PyObject
Main *bmain_src = G_MAIN;
int write_flags = 0;
- if (use_relative_remap) {
- write_flags |= G_FILE_RELATIVE_REMAP;
- }
-
if (use_compress) {
write_flags |= G_FILE_COMPRESS;
}
@@ -162,8 +175,8 @@ static PyObject *bpy_lib_write(PyObject *UNUSED(self), PyObject *args, PyObject
ReportList reports;
BKE_reports_init(&reports, RPT_STORE);
-
- retval = BKE_blendfile_write_partial(bmain_src, filepath_abs, write_flags, &reports);
+ retval = BKE_blendfile_write_partial(
+ bmain_src, filepath_abs, write_flags, path_remap.value_found, &reports);
/* cleanup state */
BKE_blendfile_write_partial_end(bmain_src);
diff --git a/source/blender/python/intern/bpy_msgbus.c b/source/blender/python/intern/bpy_msgbus.c
index c068182fe27..45c5aba1e3e 100644
--- a/source/blender/python/intern/bpy_msgbus.c
+++ b/source/blender/python/intern/bpy_msgbus.c
@@ -206,7 +206,7 @@ static void bpy_msgbus_subscribe_value_free_data(struct wmMsgSubscribeKey *UNUSE
PyDoc_STRVAR(
bpy_msgbus_subscribe_rna_doc,
- ".. function:: subscribe_rna(data, owner, args, notify, options=set())\n"
+ ".. function:: subscribe_rna(key, owner, args, notify, options=set())\n"
"\n" BPY_MSGBUS_RNA_MSGKEY_DOC
" :arg owner: Handle for this subscription (compared by identity).\n"
" :type owner: Any type.\n"
@@ -314,7 +314,7 @@ static PyObject *bpy_msgbus_subscribe_rna(PyObject *UNUSED(self), PyObject *args
PyDoc_STRVAR(
bpy_msgbus_publish_rna_doc,
- ".. function:: publish_rna(data, owner, args, notify)\n"
+ ".. function:: publish_rna(key)\n"
"\n" BPY_MSGBUS_RNA_MSGKEY_DOC
"\n"
" Notify subscribers of changes to this property\n"
diff --git a/source/blender/python/intern/bpy_props.c b/source/blender/python/intern/bpy_props.c
index ade02780210..3df0d805c5b 100644
--- a/source/blender/python/intern/bpy_props.c
+++ b/source/blender/python/intern/bpy_props.c
@@ -1366,14 +1366,14 @@ static void bpy_prop_enum_set_cb(struct PointerRNA *ptr, struct PropertyRNA *pro
}
/* utility function we need for parsing int's in an if statement */
-static int py_long_as_int(PyObject *py_long, int *r_int)
+static bool py_long_as_int(PyObject *py_long, int *r_int)
{
if (PyLong_CheckExact(py_long)) {
*r_int = (int)PyLong_AS_LONG(py_long);
- return 0;
+ return true;
}
else {
- return -1;
+ return false;
}
}
@@ -1422,7 +1422,8 @@ static const EnumPropertyItem *enum_items_from_py(PyObject *seq_fast,
Py_ssize_t totbuf = 0;
int i;
short def_used = 0;
- const char *def_cmp = NULL;
+ const char *def_string_cmp = NULL;
+ int def_int_cmp = 0;
if (is_enum_flag) {
if (seq_len > RNA_ENUM_BITFLAG_SIZE) {
@@ -1441,13 +1442,15 @@ static const EnumPropertyItem *enum_items_from_py(PyObject *seq_fast,
}
else {
if (def) {
- def_cmp = _PyUnicode_AsString(def);
- if (def_cmp == NULL) {
- PyErr_Format(PyExc_TypeError,
- "EnumProperty(...): default option must be a 'str' "
- "type when ENUM_FLAG is disabled, not a '%.200s'",
- Py_TYPE(def)->tp_name);
- return NULL;
+ if (!py_long_as_int(def, &def_int_cmp)) {
+ def_string_cmp = _PyUnicode_AsString(def);
+ if (def_string_cmp == NULL) {
+ PyErr_Format(PyExc_TypeError,
+ "EnumProperty(...): default option must be a 'str' or 'int' "
+ "type when ENUM_FLAG is disabled, not a '%.200s'",
+ Py_TYPE(def)->tp_name);
+ return NULL;
+ }
}
}
}
@@ -1474,10 +1477,10 @@ static const EnumPropertyItem *enum_items_from_py(PyObject *seq_fast,
(tmp.description = _PyUnicode_AsStringAndSize(PyTuple_GET_ITEM(item, 2),
&desc_str_size)) &&
/* TODO, number isn't ensured to be unique from the script author */
- (item_size != 4 || py_long_as_int(PyTuple_GET_ITEM(item, 3), &tmp.value) != -1) &&
- (item_size != 5 || ((py_long_as_int(PyTuple_GET_ITEM(item, 3), &tmp.icon) != -1 ||
+ (item_size != 4 || py_long_as_int(PyTuple_GET_ITEM(item, 3), &tmp.value)) &&
+ (item_size != 5 || ((py_long_as_int(PyTuple_GET_ITEM(item, 3), &tmp.icon) ||
(tmp_icon = _PyUnicode_AsString(PyTuple_GET_ITEM(item, 3)))) &&
- py_long_as_int(PyTuple_GET_ITEM(item, 4), &tmp.value) != -1))) {
+ py_long_as_int(PyTuple_GET_ITEM(item, 4), &tmp.value)))) {
if (is_enum_flag) {
if (item_size < 4) {
tmp.value = 1 << i;
@@ -1493,9 +1496,12 @@ static const EnumPropertyItem *enum_items_from_py(PyObject *seq_fast,
tmp.value = i;
}
- if (def && def_used == 0 && STREQ(def_cmp, tmp.identifier)) {
- *defvalue = tmp.value;
- def_used++; /* only ever 1 */
+ if (def && def_used == 0) {
+ if ((def_string_cmp != NULL && STREQ(def_string_cmp, tmp.identifier)) ||
+ (def_string_cmp == NULL && def_int_cmp == tmp.value)) {
+ *defvalue = tmp.value;
+ def_used++; /* only ever 1 */
+ }
}
}
@@ -1537,9 +1543,16 @@ static const EnumPropertyItem *enum_items_from_py(PyObject *seq_fast,
if (def && def_used == 0) {
MEM_freeN(items);
- PyErr_Format(PyExc_TypeError,
- "EnumProperty(..., default=\'%s\'): not found in enum members",
- def_cmp);
+ if (def_string_cmp) {
+ PyErr_Format(PyExc_TypeError,
+ "EnumProperty(..., default=\'%s\'): not found in enum members",
+ def_string_cmp);
+ }
+ else {
+ PyErr_Format(PyExc_TypeError,
+ "EnumProperty(..., default=%d): not found in enum members",
+ def_int_cmp);
+ }
return NULL;
}
}
@@ -3001,13 +3014,12 @@ PyDoc_STRVAR(
" :type items: sequence of string tuples or a function\n" BPY_PROPDEF_NAME_DOC
BPY_PROPDEF_DESC_DOC
" :arg default: The default value for this enum, a string from the identifiers used in "
- "*items*.\n"
+ "*items*, or integer matching an item number.\n"
" If the *ENUM_FLAG* option is used this must be a set of such string identifiers "
"instead.\n"
- " WARNING: It shall not be specified (or specified to its default *None* value) for "
- "dynamic enums\n"
+ " WARNING: Strings can not be specified for dynamic enums\n"
" (i.e. if a callback function is given as *items* parameter).\n"
- " :type default: string or set\n" BPY_PROPDEF_OPTIONS_ENUM_DOC BPY_PROPDEF_TAGS_DOC
+ " :type default: string, integer or set\n" BPY_PROPDEF_OPTIONS_ENUM_DOC BPY_PROPDEF_TAGS_DOC
BPY_PROPDEF_UPDATE_DOC BPY_PROPDEF_GET_DOC BPY_PROPDEF_SET_DOC);
static PyObject *BPy_EnumProperty(PyObject *self, PyObject *args, PyObject *kw)
{
@@ -3095,10 +3107,14 @@ static PyObject *BPy_EnumProperty(PyObject *self, PyObject *args, PyObject *kw)
}
if (def) {
- /* note, using type error here is odd but python does this for invalid arguments */
- PyErr_SetString(PyExc_TypeError,
- "EnumProperty(...): 'default' can't be set when 'items' is a function");
- return NULL;
+ /* Only support getting integer default values here. */
+ if (!py_long_as_int(def, &defvalue)) {
+ /* note, using type error here is odd but python does this for invalid arguments */
+ PyErr_SetString(
+ PyExc_TypeError,
+ "EnumProperty(...): 'default' can only be an integer when 'items' is a function");
+ return NULL;
+ }
}
is_itemf = true;
diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c
index ee4e5644ff2..b2622ff2d59 100644
--- a/source/blender/python/intern/bpy_rna.c
+++ b/source/blender/python/intern/bpy_rna.c
@@ -2498,7 +2498,7 @@ static int pyrna_prop_collection_subscript_str_lib_pair_ptr(BPy_PropertyRNA *sel
else if (PyUnicode_Check(keylib)) {
Main *bmain = self->ptr.data;
const char *keylib_str = _PyUnicode_AsString(keylib);
- lib = BLI_findstring(&bmain->libraries, keylib_str, offsetof(Library, name));
+ lib = BLI_findstring(&bmain->libraries, keylib_str, offsetof(Library, filepath));
if (lib == NULL) {
if (err_not_found) {
PyErr_Format(PyExc_KeyError,
diff --git a/source/blender/python/mathutils/mathutils_Matrix.c b/source/blender/python/mathutils/mathutils_Matrix.c
index 7896b939d77..63137e094b7 100644
--- a/source/blender/python/mathutils/mathutils_Matrix.c
+++ b/source/blender/python/mathutils/mathutils_Matrix.c
@@ -42,7 +42,8 @@ static PyObject *Matrix_copy_notest(MatrixObject *self, const float *matrix);
static PyObject *Matrix_copy(MatrixObject *self);
static PyObject *Matrix_deepcopy(MatrixObject *self, PyObject *args);
static int Matrix_ass_slice(MatrixObject *self, int begin, int end, PyObject *value);
-static PyObject *matrix__apply_to_copy(PyNoArgsFunction matrix_func, MatrixObject *self);
+static PyObject *matrix__apply_to_copy(PyObject *(*matrix_func)(MatrixObject *),
+ MatrixObject *self);
static PyObject *MatrixAccess_CreatePyObject(MatrixObject *matrix, const eMatrixAccess_t type);
static int matrix_row_vector_check(MatrixObject *mat, VectorObject *vec, int row)
@@ -395,14 +396,15 @@ static PyObject *Matrix_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return NULL;
}
-static PyObject *matrix__apply_to_copy(PyNoArgsFunction matrix_func, MatrixObject *self)
+static PyObject *matrix__apply_to_copy(PyObject *(*matrix_func)(MatrixObject *),
+ MatrixObject *self)
{
PyObject *ret = Matrix_copy(self);
if (ret) {
- PyObject *ret_dummy = matrix_func(ret);
+ PyObject *ret_dummy = matrix_func((MatrixObject *)ret);
if (ret_dummy) {
Py_DECREF(ret_dummy);
- return (PyObject *)ret;
+ return ret;
}
else { /* error */
Py_DECREF(ret);
@@ -1634,16 +1636,17 @@ static PyObject *Matrix_inverted_noargs(MatrixObject *self)
Py_RETURN_NONE;
}
-PyDoc_STRVAR(Matrix_invert_safe_doc,
- ".. method:: invert_safe()\n"
- "\n"
- " Set the matrix to its inverse, will never error.\n"
- " If degenerated (e.g. zero scale on an axis), add some epsilon to its diagonal, "
- "to get an invertible one.\n"
- " If tweaked matrix is still degenerated, set to the identity matrix instead.\n"
- "\n"
- " .. seealso:: `Inverse Matrix <https://en.wikipedia.org/wiki/Inverse_matrix>`__ on "
- "Wikipedia.\n");
+PyDoc_STRVAR(
+ Matrix_invert_safe_doc,
+ ".. method:: invert_safe()\n"
+ "\n"
+ " Set the matrix to its inverse, will never error.\n"
+ " If degenerated (e.g. zero scale on an axis), add some epsilon to its diagonal, "
+ "to get an invertible one.\n"
+ " If tweaked matrix is still degenerated, set to the identity matrix instead.\n"
+ "\n"
+ " .. seealso:: `Inverse Matrix <https://en.wikipedia.org/wiki/Inverse_matrix>`__ on "
+ "Wikipedia.\n");
static PyObject *Matrix_invert_safe(MatrixObject *self)
{
if (BaseMath_ReadCallback_ForWrite(self) == -1) {
@@ -1737,7 +1740,7 @@ PyDoc_STRVAR(
" .. note:: When the matrix cant be adjugated a :exc:`ValueError` exception is raised.\n");
static PyObject *Matrix_adjugated(MatrixObject *self)
{
- return matrix__apply_to_copy((PyNoArgsFunction)Matrix_adjugate, self);
+ return matrix__apply_to_copy(Matrix_adjugate, self);
}
PyDoc_STRVAR(
@@ -1945,7 +1948,7 @@ PyDoc_STRVAR(Matrix_transposed_doc,
" :rtype: :class:`Matrix`\n");
static PyObject *Matrix_transposed(MatrixObject *self)
{
- return matrix__apply_to_copy((PyNoArgsFunction)Matrix_transpose, self);
+ return matrix__apply_to_copy(Matrix_transpose, self);
}
/*---------------------------matrix.normalize() ------------------*/
@@ -1991,7 +1994,7 @@ PyDoc_STRVAR(Matrix_normalized_doc,
" :rtype: :class:`Matrix`\n");
static PyObject *Matrix_normalized(MatrixObject *self)
{
- return matrix__apply_to_copy((PyNoArgsFunction)Matrix_normalize, self);
+ return matrix__apply_to_copy(Matrix_normalize, self);
}
/*---------------------------matrix.zero() -----------------------*/
diff --git a/source/blender/python/mathutils/mathutils_Quaternion.c b/source/blender/python/mathutils/mathutils_Quaternion.c
index 39d84c1ac96..7ce0ea5f249 100644
--- a/source/blender/python/mathutils/mathutils_Quaternion.c
+++ b/source/blender/python/mathutils/mathutils_Quaternion.c
@@ -34,7 +34,8 @@
#define QUAT_SIZE 4
-static PyObject *quat__apply_to_copy(PyNoArgsFunction quat_func, QuaternionObject *self);
+static PyObject *quat__apply_to_copy(PyObject *(*quat_func)(QuaternionObject *),
+ QuaternionObject *self);
static void quat__axis_angle_sanitize(float axis[3], float *angle);
static PyObject *Quaternion_copy(QuaternionObject *self);
static PyObject *Quaternion_deepcopy(QuaternionObject *self, PyObject *args);
@@ -463,7 +464,7 @@ PyDoc_STRVAR(Quaternion_normalized_doc,
" :rtype: :class:`Quaternion`\n");
static PyObject *Quaternion_normalized(QuaternionObject *self)
{
- return quat__apply_to_copy((PyNoArgsFunction)Quaternion_normalize, self);
+ return quat__apply_to_copy(Quaternion_normalize, self);
}
PyDoc_STRVAR(Quaternion_invert_doc,
@@ -490,7 +491,7 @@ PyDoc_STRVAR(Quaternion_inverted_doc,
" :rtype: :class:`Quaternion`\n");
static PyObject *Quaternion_inverted(QuaternionObject *self)
{
- return quat__apply_to_copy((PyNoArgsFunction)Quaternion_invert, self);
+ return quat__apply_to_copy(Quaternion_invert, self);
}
PyDoc_STRVAR(Quaternion_identity_doc,
@@ -553,7 +554,7 @@ PyDoc_STRVAR(Quaternion_conjugated_doc,
" :rtype: :class:`Quaternion`\n");
static PyObject *Quaternion_conjugated(QuaternionObject *self)
{
- return quat__apply_to_copy((PyNoArgsFunction)Quaternion_conjugate, self);
+ return quat__apply_to_copy(Quaternion_conjugate, self);
}
PyDoc_STRVAR(Quaternion_copy_doc,
@@ -1385,10 +1386,11 @@ static PyObject *Quaternion_new(PyTypeObject *type, PyObject *args, PyObject *kw
return Quaternion_CreatePyObject(quat, type);
}
-static PyObject *quat__apply_to_copy(PyNoArgsFunction quat_func, QuaternionObject *self)
+static PyObject *quat__apply_to_copy(PyObject *(*quat_func)(QuaternionObject *),
+ QuaternionObject *self)
{
PyObject *ret = Quaternion_copy(self);
- PyObject *ret_dummy = quat_func(ret);
+ PyObject *ret_dummy = quat_func((QuaternionObject *)ret);
if (ret_dummy) {
Py_DECREF(ret_dummy);
return ret;
diff --git a/source/blender/python/mathutils/mathutils_Vector.c b/source/blender/python/mathutils/mathutils_Vector.c
index ace7480ee81..15ae811fd91 100644
--- a/source/blender/python/mathutils/mathutils_Vector.c
+++ b/source/blender/python/mathutils/mathutils_Vector.c
@@ -96,10 +96,10 @@ static PyObject *Vector_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return Vector_CreatePyObject_alloc(vec, size, type);
}
-static PyObject *vec__apply_to_copy(PyNoArgsFunction vec_func, VectorObject *self)
+static PyObject *vec__apply_to_copy(PyObject *(*vec_func)(VectorObject *), VectorObject *self)
{
PyObject *ret = Vector_copy(self);
- PyObject *ret_dummy = vec_func(ret);
+ PyObject *ret_dummy = vec_func((VectorObject *)ret);
if (ret_dummy) {
Py_DECREF(ret_dummy);
return (PyObject *)ret;
@@ -376,7 +376,7 @@ PyDoc_STRVAR(Vector_normalized_doc,
" :rtype: :class:`Vector`\n");
static PyObject *Vector_normalized(VectorObject *self)
{
- return vec__apply_to_copy((PyNoArgsFunction)Vector_normalize, self);
+ return vec__apply_to_copy(Vector_normalize, self);
}
PyDoc_STRVAR(Vector_resize_doc,
diff --git a/source/blender/python/mathutils/mathutils_noise.c b/source/blender/python/mathutils/mathutils_noise.c
index 8ec0c9ce44a..eece782f6d0 100644
--- a/source/blender/python/mathutils/mathutils_noise.c
+++ b/source/blender/python/mathutils/mathutils_noise.c
@@ -121,7 +121,7 @@ static void init_genrand(ulong s)
const float range = 32; /* range in both pos/neg direction */
for (j = 0; j < ARRAY_SIZE(state_offset_vector); j++, state_offset++) {
/* overflow is fine here */
- state_offset_vector[j] = (float)(int)(*state_offset) * (1.0f / (INT_MAX / range));
+ state_offset_vector[j] = (float)(int)(*state_offset) * (1.0f / ((float)INT_MAX / range));
}
}
}
diff --git a/source/blender/render/intern/source/external_engine.c b/source/blender/render/intern/source/external_engine.c
index af3a6fdd384..5391775cab8 100644
--- a/source/blender/render/intern/source/external_engine.c
+++ b/source/blender/render/intern/source/external_engine.c
@@ -838,9 +838,11 @@ int RE_engine_render(Render *re, int do_all)
engine->resolution_x = re->winx;
engine->resolution_y = re->winy;
+ BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_WRITE);
RE_parts_init(re);
engine->tile_x = re->partx;
engine->tile_y = re->party;
+ BLI_rw_mutex_unlock(&re->partsmutex);
if (re->result->do_exr_tile) {
render_result_exr_file_begin(re, engine);
diff --git a/source/blender/render/intern/source/pipeline.c b/source/blender/render/intern/source/pipeline.c
index c66c43ec467..b335862abe0 100644
--- a/source/blender/render/intern/source/pipeline.c
+++ b/source/blender/render/intern/source/pipeline.c
@@ -1844,7 +1844,7 @@ static void validate_render_settings(Render *re)
{
if (RE_engine_is_external(re)) {
/* not supported yet */
- re->r.scemode &= ~(R_FULL_SAMPLE);
+ re->r.scemode &= ~R_FULL_SAMPLE;
}
}
diff --git a/source/blender/render/intern/source/render_texture.c b/source/blender/render/intern/source/render_texture.c
index ee484924bf9..9926e08c968 100644
--- a/source/blender/render/intern/source/render_texture.c
+++ b/source/blender/render/intern/source/render_texture.c
@@ -1322,7 +1322,7 @@ static int multitex_nodes_intern(Tex *tex,
texnode_preview,
use_nodes);
- if (mtex->mapto & (MAP_COL)) {
+ if (mtex->mapto & MAP_COL) {
ImBuf *ibuf = BKE_image_pool_acquire_ibuf(tex->ima, &tex->iuser, pool);
/* don't linearize float buffers, assumed to be linear */
diff --git a/source/blender/shader_fx/CMakeLists.txt b/source/blender/shader_fx/CMakeLists.txt
index 9835c5c0588..740f7c126d1 100644
--- a/source/blender/shader_fx/CMakeLists.txt
+++ b/source/blender/shader_fx/CMakeLists.txt
@@ -24,12 +24,15 @@ set(INC
intern
../blenfont
../blenkernel
+ ../blentranslation
../blenlib
../bmesh
../depsgraph
+ ../editors/include
../makesdna
../makesrna
../render/extern/include
+ ../windowmanager
../../../intern/eigen
../../../intern/guardedalloc
)
@@ -40,6 +43,7 @@ set(INC_SYS
set(SRC
intern/FX_shader_util.h
+ intern/FX_ui_common.c
intern/FX_shader_blur.c
intern/FX_shader_colorize.c
@@ -52,6 +56,7 @@ set(SRC
intern/FX_shader_util.c
intern/FX_shader_wave.c
+ intern/FX_ui_common.h
FX_shader_types.h
)
diff --git a/source/blender/shader_fx/intern/FX_shader_blur.c b/source/blender/shader_fx/intern/FX_shader_blur.c
index 8881f147af8..8e3e7588818 100644
--- a/source/blender/shader_fx/intern/FX_shader_blur.c
+++ b/source/blender/shader_fx/intern/FX_shader_blur.c
@@ -26,7 +26,20 @@
#include "BLI_math.h"
#include "BLI_utildefines.h"
+#include "BLT_translation.h"
+
+#include "BKE_context.h"
+#include "BKE_screen.h"
+
+#include "DNA_screen_types.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "RNA_access.h"
+
#include "FX_shader_types.h"
+#include "FX_ui_common.h"
static void initData(ShaderFxData *fx)
{
@@ -41,6 +54,32 @@ static void copyData(const ShaderFxData *md, ShaderFxData *target)
BKE_shaderfx_copydata_generic(md, target);
}
+static void panel_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *col;
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ shaderfx_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ uiLayoutSetPropSep(layout, true);
+
+ uiItemR(layout, &ptr, "samples", 0, NULL, ICON_NONE);
+
+ uiItemR(layout, &ptr, "use_dof_mode", 0, IFACE_("Use Depth of Field"), ICON_NONE);
+ col = uiLayoutColumn(layout, false);
+ uiLayoutSetActive(col, !RNA_boolean_get(&ptr, "use_dof_mode"));
+ uiItemR(col, &ptr, "size", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "rotation", 0, NULL, ICON_NONE);
+
+ shaderfx_panel_end(layout, &ptr);
+}
+
+static void panelRegister(ARegionType *region_type)
+{
+ shaderfx_panel_register(region_type, eShaderFxType_Blur, panel_draw);
+}
+
ShaderFxTypeInfo shaderfx_Type_Blur = {
/* name */ "Blur",
/* structName */ "BlurShaderFxData",
@@ -57,4 +96,5 @@ ShaderFxTypeInfo shaderfx_Type_Blur = {
/* dependsOnTime */ NULL,
/* foreachObjectLink */ NULL,
/* foreachIDLink */ NULL,
+ /* panelRegister */ panelRegister,
};
diff --git a/source/blender/shader_fx/intern/FX_shader_colorize.c b/source/blender/shader_fx/intern/FX_shader_colorize.c
index 5fea2cd0ff8..969171332fa 100644
--- a/source/blender/shader_fx/intern/FX_shader_colorize.c
+++ b/source/blender/shader_fx/intern/FX_shader_colorize.c
@@ -23,11 +23,23 @@
#include <stdio.h>
-#include "DNA_shader_fx_types.h"
+#include "BKE_context.h"
+#include "BKE_screen.h"
#include "BLI_utildefines.h"
+#include "BLT_translation.h"
+
+#include "DNA_screen_types.h"
+#include "DNA_shader_fx_types.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "RNA_access.h"
+
#include "FX_shader_types.h"
+#include "FX_ui_common.h"
static void initData(ShaderFxData *fx)
{
@@ -43,6 +55,38 @@ static void copyData(const ShaderFxData *md, ShaderFxData *target)
BKE_shaderfx_copydata_generic(md, target);
}
+static void panel_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ shaderfx_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ int mode = RNA_enum_get(&ptr, "mode");
+
+ uiLayoutSetPropSep(layout, true);
+
+ uiItemR(layout, &ptr, "mode", 0, NULL, ICON_NONE);
+
+ if (ELEM(mode, eShaderFxColorizeMode_Custom, eShaderFxColorizeMode_Duotone)) {
+ const char *text = (mode == eShaderFxColorizeMode_Duotone) ? IFACE_("Low Color") :
+ IFACE_("Color");
+ uiItemR(layout, &ptr, "low_color", 0, text, ICON_NONE);
+ }
+ if (mode == eShaderFxColorizeMode_Duotone) {
+ uiItemR(layout, &ptr, "high_color", 0, NULL, ICON_NONE);
+ }
+
+ uiItemR(layout, &ptr, "factor", 0, NULL, ICON_NONE);
+
+ shaderfx_panel_end(layout, &ptr);
+}
+
+static void panelRegister(ARegionType *region_type)
+{
+ shaderfx_panel_register(region_type, eShaderFxType_Colorize, panel_draw);
+}
+
ShaderFxTypeInfo shaderfx_Type_Colorize = {
/* name */ "Colorize",
/* structName */ "ColorizeShaderFxData",
@@ -59,4 +103,5 @@ ShaderFxTypeInfo shaderfx_Type_Colorize = {
/* dependsOnTime */ NULL,
/* foreachObjectLink */ NULL,
/* foreachIDLink */ NULL,
+ /* panelRegister */ panelRegister,
};
diff --git a/source/blender/shader_fx/intern/FX_shader_flip.c b/source/blender/shader_fx/intern/FX_shader_flip.c
index b915fb8e591..325d2c2608f 100644
--- a/source/blender/shader_fx/intern/FX_shader_flip.c
+++ b/source/blender/shader_fx/intern/FX_shader_flip.c
@@ -26,10 +26,22 @@
#include "DNA_gpencil_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
#include "BLI_utildefines.h"
+#include "BLT_translation.h"
+
+#include "BKE_context.h"
+#include "BKE_screen.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "RNA_access.h"
+
#include "FX_shader_types.h"
+#include "FX_ui_common.h"
static void initData(ShaderFxData *fx)
{
@@ -42,6 +54,29 @@ static void copyData(const ShaderFxData *md, ShaderFxData *target)
BKE_shaderfx_copydata_generic(md, target);
}
+static void panel_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *row;
+ uiLayout *layout = panel->layout;
+ int toggles_flag = UI_ITEM_R_TOGGLE | UI_ITEM_R_FORCE_BLANK_DECORATE;
+
+ PointerRNA ptr;
+ shaderfx_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ uiLayoutSetPropSep(layout, true);
+
+ row = uiLayoutRowWithHeading(layout, true, IFACE_("Axis"));
+ uiItemR(row, &ptr, "flip_horizontal", toggles_flag, NULL, ICON_NONE);
+ uiItemR(row, &ptr, "flip_vertical", toggles_flag, NULL, ICON_NONE);
+
+ shaderfx_panel_end(layout, &ptr);
+}
+
+static void panelRegister(ARegionType *region_type)
+{
+ shaderfx_panel_register(region_type, eShaderFxType_Flip, panel_draw);
+}
+
ShaderFxTypeInfo shaderfx_Type_Flip = {
/* name */ "Flip",
/* structName */ "FlipShaderFxData",
@@ -58,4 +93,5 @@ ShaderFxTypeInfo shaderfx_Type_Flip = {
/* dependsOnTime */ NULL,
/* foreachObjectLink */ NULL,
/* foreachIDLink */ NULL,
+ /* panelRegister */ panelRegister,
};
diff --git a/source/blender/shader_fx/intern/FX_shader_glow.c b/source/blender/shader_fx/intern/FX_shader_glow.c
index 1194e95ce79..4398fdc13bb 100644
--- a/source/blender/shader_fx/intern/FX_shader_glow.c
+++ b/source/blender/shader_fx/intern/FX_shader_glow.c
@@ -26,14 +26,23 @@
#include "DNA_gpencil_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
#include "BLI_math.h"
#include "BLI_utildefines.h"
+#include "BKE_context.h"
#include "BKE_modifier.h"
+#include "BKE_screen.h"
#include "BKE_shader_fx.h"
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "RNA_access.h"
+
#include "FX_shader_types.h"
+#include "FX_ui_common.h"
static void initData(ShaderFxData *md)
{
@@ -50,6 +59,44 @@ static void copyData(const ShaderFxData *md, ShaderFxData *target)
BKE_shaderfx_copydata_generic(md, target);
}
+static void panel_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ shaderfx_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ int mode = RNA_enum_get(&ptr, "mode");
+
+ uiLayoutSetPropSep(layout, true);
+
+ uiItemR(layout, &ptr, "mode", 0, NULL, ICON_NONE);
+
+ if (mode == eShaderFxGlowMode_Luminance) {
+ uiItemR(layout, &ptr, "threshold", 0, NULL, ICON_NONE);
+ }
+ else {
+ uiItemR(layout, &ptr, "select_color", 0, NULL, ICON_NONE);
+ }
+ uiItemR(layout, &ptr, "glow_color", 0, NULL, ICON_NONE);
+
+ uiItemS(layout);
+
+ uiItemR(layout, &ptr, "blend_mode", 0, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "opacity", 0, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "size", 0, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "rotation", 0, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "samples", 0, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "use_glow_under", 0, NULL, ICON_NONE);
+
+ shaderfx_panel_end(layout, &ptr);
+}
+
+static void panelRegister(ARegionType *region_type)
+{
+ shaderfx_panel_register(region_type, eShaderFxType_Glow, panel_draw);
+}
+
ShaderFxTypeInfo shaderfx_Type_Glow = {
/* name */ "Glow",
/* structName */ "GlowShaderFxData",
@@ -66,4 +113,5 @@ ShaderFxTypeInfo shaderfx_Type_Glow = {
/* dependsOnTime */ NULL,
/* foreachObjectLink */ NULL,
/* foreachIDLink */ NULL,
+ /* panelRegister */ panelRegister,
};
diff --git a/source/blender/shader_fx/intern/FX_shader_light.c b/source/blender/shader_fx/intern/FX_shader_light.c
index 17d2f518d44..2fd93bff8aa 100644
--- a/source/blender/shader_fx/intern/FX_shader_light.c
+++ b/source/blender/shader_fx/intern/FX_shader_light.c
@@ -26,14 +26,23 @@
#include "DNA_gpencil_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
#include "BLI_utildefines.h"
+#include "BKE_context.h"
#include "BKE_lib_query.h"
#include "BKE_modifier.h"
+#include "BKE_screen.h"
#include "BKE_shader_fx.h"
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "RNA_access.h"
+
#include "FX_shader_types.h"
+#include "FX_ui_common.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_build.h"
@@ -93,4 +102,5 @@ ShaderFxTypeInfo shaderfx_Type_Light = {
/* dependsOnTime */ NULL,
/* foreachObjectLink */ foreachObjectLink,
/* foreachIDLink */ NULL,
+ /* panelRegister */ NULL,
};
diff --git a/source/blender/shader_fx/intern/FX_shader_pixel.c b/source/blender/shader_fx/intern/FX_shader_pixel.c
index 04bf9ae5b6d..bdc4f141017 100644
--- a/source/blender/shader_fx/intern/FX_shader_pixel.c
+++ b/source/blender/shader_fx/intern/FX_shader_pixel.c
@@ -25,7 +25,20 @@
#include "BLI_utildefines.h"
+#include "BLT_translation.h"
+
+#include "BKE_context.h"
+#include "BKE_screen.h"
+
+#include "DNA_screen_types.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "RNA_access.h"
+
#include "FX_shader_types.h"
+#include "FX_ui_common.h"
static void initData(ShaderFxData *fx)
{
@@ -39,6 +52,32 @@ static void copyData(const ShaderFxData *md, ShaderFxData *target)
BKE_shaderfx_copydata_generic(md, target);
}
+static void panel_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *col;
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ shaderfx_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ uiLayoutSetPropSep(layout, true);
+
+ /* Add the X, Y labels manually because size is a #PROP_PIXEL. */
+ col = uiLayoutColumn(layout, true);
+ PropertyRNA *prop = RNA_struct_find_property(&ptr, "size");
+ uiItemFullR(col, &ptr, prop, 0, 0, 0, IFACE_("Size X"), ICON_NONE);
+ uiItemFullR(col, &ptr, prop, 1, 0, 0, IFACE_("Y"), ICON_NONE);
+
+ uiItemR(layout, &ptr, "use_antialiasing", 0, NULL, ICON_NONE);
+
+ shaderfx_panel_end(layout, &ptr);
+}
+
+static void panelRegister(ARegionType *region_type)
+{
+ shaderfx_panel_register(region_type, eShaderFxType_Pixel, panel_draw);
+}
+
ShaderFxTypeInfo shaderfx_Type_Pixel = {
/* name */ "Pixelate",
/* structName */ "PixelShaderFxData",
@@ -55,4 +94,5 @@ ShaderFxTypeInfo shaderfx_Type_Pixel = {
/* dependsOnTime */ NULL,
/* foreachObjectLink */ NULL,
/* foreachIDLink */ NULL,
+ /* panelRegister */ panelRegister,
};
diff --git a/source/blender/shader_fx/intern/FX_shader_rim.c b/source/blender/shader_fx/intern/FX_shader_rim.c
index 16c6a6716a0..eaaa10af54c 100644
--- a/source/blender/shader_fx/intern/FX_shader_rim.c
+++ b/source/blender/shader_fx/intern/FX_shader_rim.c
@@ -23,11 +23,23 @@
#include <stdio.h>
+#include "DNA_screen_types.h"
#include "DNA_shader_fx_types.h"
#include "BLI_utildefines.h"
+#include "BLT_translation.h"
+
+#include "BKE_context.h"
+#include "BKE_screen.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "RNA_access.h"
+
#include "FX_shader_types.h"
+#include "FX_ui_common.h"
static void initData(ShaderFxData *fx)
{
@@ -46,6 +58,54 @@ static void copyData(const ShaderFxData *md, ShaderFxData *target)
BKE_shaderfx_copydata_generic(md, target);
}
+static void panel_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *col;
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ shaderfx_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ uiLayoutSetPropSep(layout, true);
+
+ uiItemR(layout, &ptr, "rim_color", 0, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "mask_color", 0, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "mode", 0, IFACE_("Blend Mode"), ICON_NONE);
+
+ /* Add the X, Z labels manually because offset is a #PROP_PIXEL. */
+ col = uiLayoutColumn(layout, true);
+ PropertyRNA *prop = RNA_struct_find_property(&ptr, "offset");
+ uiItemFullR(col, &ptr, prop, 0, 0, 0, IFACE_("Offset X"), ICON_NONE);
+ uiItemFullR(col, &ptr, prop, 1, 0, 0, IFACE_("Z"), ICON_NONE);
+
+ shaderfx_panel_end(layout, &ptr);
+}
+
+static void blur_panel_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *col;
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ shaderfx_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ uiLayoutSetPropSep(layout, true);
+
+ /* Add the X, Z labels manually because blur is a #PROP_PIXEL. */
+ col = uiLayoutColumn(layout, true);
+ PropertyRNA *prop = RNA_struct_find_property(&ptr, "blur");
+ uiItemFullR(col, &ptr, prop, 0, 0, 0, IFACE_("Blur X"), ICON_NONE);
+ uiItemFullR(col, &ptr, prop, 1, 0, 0, IFACE_("Z"), ICON_NONE);
+
+ uiItemR(layout, &ptr, "samples", 0, NULL, ICON_NONE);
+}
+
+static void panelRegister(ARegionType *region_type)
+{
+ PanelType *panel_type = shaderfx_panel_register(region_type, eShaderFxType_Rim, panel_draw);
+ shaderfx_subpanel_register(region_type, "blur", "Blur", NULL, blur_panel_draw, panel_type);
+}
+
ShaderFxTypeInfo shaderfx_Type_Rim = {
/* name */ "Rim",
/* structName */ "RimShaderFxData",
@@ -62,4 +122,5 @@ ShaderFxTypeInfo shaderfx_Type_Rim = {
/* dependsOnTime */ NULL,
/* foreachObjectLink */ NULL,
/* foreachIDLink */ NULL,
+ /* panelRegister */ panelRegister,
};
diff --git a/source/blender/shader_fx/intern/FX_shader_shadow.c b/source/blender/shader_fx/intern/FX_shader_shadow.c
index dcf66ec89e0..11690d2cca0 100644
--- a/source/blender/shader_fx/intern/FX_shader_shadow.c
+++ b/source/blender/shader_fx/intern/FX_shader_shadow.c
@@ -26,14 +26,25 @@
#include "DNA_gpencil_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
#include "BLI_utildefines.h"
+#include "BLT_translation.h"
+
+#include "BKE_context.h"
#include "BKE_lib_query.h"
#include "BKE_modifier.h"
+#include "BKE_screen.h"
#include "BKE_shader_fx.h"
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "RNA_access.h"
+
#include "FX_shader_types.h"
+#include "FX_ui_common.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_build.h"
@@ -88,6 +99,88 @@ static void foreachObjectLink(ShaderFxData *fx,
walk(userData, ob, &fxd->object, IDWALK_CB_NOP);
}
+static void panel_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *row, *col;
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ shaderfx_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ uiLayoutSetPropSep(layout, true);
+
+ uiItemR(layout, &ptr, "shadow_color", 0, NULL, ICON_NONE);
+
+ /* Add the X, Y labels manually because size is a #PROP_PIXEL. */
+ col = uiLayoutColumn(layout, true);
+ PropertyRNA *prop = RNA_struct_find_property(&ptr, "offset");
+ uiItemFullR(col, &ptr, prop, 0, 0, 0, IFACE_("Offset X"), ICON_NONE);
+ uiItemFullR(col, &ptr, prop, 1, 0, 0, IFACE_("Y"), ICON_NONE);
+
+ uiItemR(layout, &ptr, "scale", 0, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "rotation", 0, NULL, ICON_NONE);
+
+ row = uiLayoutRowWithHeading(layout, true, IFACE_("Object Pivot"));
+ uiItemR(row, &ptr, "use_object", 0, "", ICON_NONE);
+ uiItemR(row, &ptr, "object", 0, "", ICON_NONE);
+
+ shaderfx_panel_end(layout, &ptr);
+}
+
+static void blur_panel_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *col;
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ shaderfx_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ uiLayoutSetPropSep(layout, true);
+
+ /* Add the X, Y labels manually because size is a #PROP_PIXEL. */
+ col = uiLayoutColumn(layout, true);
+ PropertyRNA *prop = RNA_struct_find_property(&ptr, "blur");
+ uiItemFullR(col, &ptr, prop, 0, 0, 0, IFACE_("Blur X"), ICON_NONE);
+ uiItemFullR(col, &ptr, prop, 1, 0, 0, IFACE_("Y"), ICON_NONE);
+
+ uiItemR(layout, &ptr, "samples", 0, NULL, ICON_NONE);
+}
+
+static void wave_header_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ shaderfx_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ uiItemR(layout, &ptr, "use_wave", 0, IFACE_("Wave Effect"), ICON_NONE);
+}
+
+static void wave_panel_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ shaderfx_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ uiLayoutSetPropSep(layout, true);
+
+ uiLayoutSetActive(layout, RNA_boolean_get(&ptr, "use_wave"));
+
+ uiItemR(layout, &ptr, "orientation", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "amplitude", 0, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "period", 0, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "phase", 0, NULL, ICON_NONE);
+}
+
+static void panelRegister(ARegionType *region_type)
+{
+ PanelType *panel_type = shaderfx_panel_register(region_type, eShaderFxType_Shadow, panel_draw);
+ shaderfx_subpanel_register(region_type, "blur", "Blur", NULL, blur_panel_draw, panel_type);
+ shaderfx_subpanel_register(
+ region_type, "wave", "", wave_header_draw, wave_panel_draw, panel_type);
+}
+
ShaderFxTypeInfo shaderfx_Type_Shadow = {
/* name */ "Shadow",
/* structName */ "ShadowShaderFxData",
@@ -104,4 +197,5 @@ ShaderFxTypeInfo shaderfx_Type_Shadow = {
/* dependsOnTime */ NULL,
/* foreachObjectLink */ foreachObjectLink,
/* foreachIDLink */ NULL,
+ /* panelRegister */ panelRegister,
};
diff --git a/source/blender/shader_fx/intern/FX_shader_swirl.c b/source/blender/shader_fx/intern/FX_shader_swirl.c
index 990b43748da..65e861fa46f 100644
--- a/source/blender/shader_fx/intern/FX_shader_swirl.c
+++ b/source/blender/shader_fx/intern/FX_shader_swirl.c
@@ -26,15 +26,24 @@
#include "DNA_gpencil_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
#include "BLI_math_base.h"
#include "BLI_utildefines.h"
+#include "BKE_context.h"
#include "BKE_lib_query.h"
#include "BKE_modifier.h"
+#include "BKE_screen.h"
#include "BKE_shader_fx.h"
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "RNA_access.h"
+
#include "FX_shader_types.h"
+#include "FX_ui_common.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_build.h"
@@ -77,6 +86,28 @@ static void foreachObjectLink(ShaderFxData *fx,
walk(userData, ob, &fxd->object, IDWALK_CB_NOP);
}
+static void panel_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ PointerRNA ob_ptr;
+ shaderfx_panel_get_property_pointers(C, panel, &ob_ptr, &ptr);
+
+ uiLayoutSetPropSep(layout, true);
+
+ uiItemR(layout, &ptr, "object", 0, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "radius", 0, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "angle", 0, NULL, ICON_NONE);
+
+ shaderfx_panel_end(layout, &ptr);
+}
+
+static void panelRegister(ARegionType *region_type)
+{
+ shaderfx_panel_register(region_type, eShaderFxType_Swirl, panel_draw);
+}
+
ShaderFxTypeInfo shaderfx_Type_Swirl = {
/* name */ "Swirl",
/* structName */ "SwirlShaderFxData",
@@ -93,4 +124,5 @@ ShaderFxTypeInfo shaderfx_Type_Swirl = {
/* dependsOnTime */ NULL,
/* foreachObjectLink */ foreachObjectLink,
/* foreachIDLink */ NULL,
+ /* panelRegister */ panelRegister,
};
diff --git a/source/blender/shader_fx/intern/FX_shader_wave.c b/source/blender/shader_fx/intern/FX_shader_wave.c
index 7dc72bcb669..3b8256edae3 100644
--- a/source/blender/shader_fx/intern/FX_shader_wave.c
+++ b/source/blender/shader_fx/intern/FX_shader_wave.c
@@ -26,10 +26,20 @@
#include "DNA_gpencil_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
+
+#include "BKE_context.h"
+#include "BKE_screen.h"
#include "BLI_utildefines.h"
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "RNA_access.h"
+
#include "FX_shader_types.h"
+#include "FX_ui_common.h"
static void initData(ShaderFxData *fx)
{
@@ -45,8 +55,30 @@ static void copyData(const ShaderFxData *md, ShaderFxData *target)
BKE_shaderfx_copydata_generic(md, target);
}
+static void panel_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ shaderfx_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ uiLayoutSetPropSep(layout, true);
+
+ uiItemR(layout, &ptr, "orientation", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "amplitude", 0, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "period", 0, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "phase", 0, NULL, ICON_NONE);
+
+ shaderfx_panel_end(layout, &ptr);
+}
+
+static void panelRegister(ARegionType *region_type)
+{
+ shaderfx_panel_register(region_type, eShaderFxType_Wave, panel_draw);
+}
+
ShaderFxTypeInfo shaderfx_Type_Wave = {
- /* name */ "Wave Distortion",
+ /* name */ "WaveDistortion",
/* structName */ "WaveShaderFxData",
/* structSize */ sizeof(WaveShaderFxData),
/* type */ eShaderFxType_GpencilType,
@@ -61,4 +93,5 @@ ShaderFxTypeInfo shaderfx_Type_Wave = {
/* dependsOnTime */ NULL,
/* foreachObjectLink */ NULL,
/* foreachIDLink */ NULL,
+ /* panelRegister */ panelRegister,
};
diff --git a/source/blender/shader_fx/intern/FX_ui_common.c b/source/blender/shader_fx/intern/FX_ui_common.c
new file mode 100644
index 00000000000..3a1df937c9f
--- /dev/null
+++ b/source/blender/shader_fx/intern/FX_ui_common.c
@@ -0,0 +1,272 @@
+/* This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup modifiers
+ */
+
+#include <string.h>
+
+#include "BLI_listbase.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BKE_context.h"
+#include "BKE_object.h"
+#include "BKE_screen.h"
+#include "BKE_shader_fx.h"
+
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_shader_fx_types.h"
+#include "DNA_space_types.h"
+
+#include "ED_object.h"
+
+#include "BLT_translation.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "RNA_access.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "FX_ui_common.h" /* Self include */
+
+static Object *get_context_object(const bContext *C)
+{
+ SpaceProperties *sbuts = CTX_wm_space_properties(C);
+ if (sbuts != NULL && (sbuts->pinid != NULL) && GS(sbuts->pinid->name) == ID_OB) {
+ return (Object *)sbuts->pinid;
+ }
+ else {
+ return CTX_data_active_object(C);
+ }
+}
+
+/* -------------------------------------------------------------------- */
+/** \name Panel Drag and Drop, Expansion Saving
+ * \{ */
+
+/**
+ * Move an effect to the index it's moved to after a drag and drop.
+ */
+static void shaderfx_reorder(bContext *C, Panel *panel, int new_index)
+{
+ Object *ob = get_context_object(C);
+
+ ShaderFxData *fx = BLI_findlink(&ob->shader_fx, panel->runtime.list_index);
+ PointerRNA props_ptr;
+ wmOperatorType *ot = WM_operatortype_find("OBJECT_OT_shaderfx_move_to_index", false);
+ WM_operator_properties_create_ptr(&props_ptr, ot);
+ RNA_string_set(&props_ptr, "shaderfx", fx->name);
+ RNA_int_set(&props_ptr, "index", new_index);
+ WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr);
+}
+
+/**
+ * Get the expand flag from the active effect to use for the panel.
+ */
+static short get_shaderfx_expand_flag(const bContext *C, Panel *panel)
+{
+ Object *ob = get_context_object(C);
+ ShaderFxData *fx = BLI_findlink(&ob->shader_fx, panel->runtime.list_index);
+ return fx->ui_expand_flag;
+}
+
+/**
+ * Save the expand flag for the panel and sub-panels to the effect.
+ */
+static void set_shaderfx_expand_flag(const bContext *C, Panel *panel, short expand_flag)
+{
+ Object *ob = get_context_object(C);
+ ShaderFxData *fx = BLI_findlink(&ob->shader_fx, panel->runtime.list_index);
+ fx->ui_expand_flag = expand_flag;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name ShaderFx Panel Layouts
+ * \{ */
+
+/**
+ * Draw shaderfx error message.
+ */
+void shaderfx_panel_end(uiLayout *layout, PointerRNA *ptr)
+{
+ ShaderFxData *fx = ptr->data;
+ if (fx->error) {
+ uiLayout *row = uiLayoutRow(layout, false);
+ uiItemL(row, IFACE_(fx->error), ICON_ERROR);
+ }
+}
+
+/**
+ * Gets RNA pointers for the active object and the panel's shaderfx data.
+ */
+void shaderfx_panel_get_property_pointers(const bContext *C,
+ Panel *panel,
+ PointerRNA *r_ob_ptr,
+ PointerRNA *r_md_ptr)
+{
+ Object *ob = get_context_object(C);
+ ShaderFxData *md = BLI_findlink(&ob->shader_fx, panel->runtime.list_index);
+
+ RNA_pointer_create(&ob->id, &RNA_ShaderFx, md, r_md_ptr);
+
+ if (r_ob_ptr != NULL) {
+ RNA_pointer_create(&ob->id, &RNA_Object, ob, r_ob_ptr);
+ }
+
+ uiLayoutSetContextPointer(panel->layout, "shaderfx", r_md_ptr);
+}
+
+#define ERROR_LIBDATA_MESSAGE TIP_("External library data")
+
+static void shaderfx_panel_header(const bContext *C, Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+ bool narrow_panel = (panel->sizex < UI_UNIT_X * 7 && panel->sizex != 0);
+
+ PointerRNA ptr;
+ shaderfx_panel_get_property_pointers(C, panel, NULL, &ptr);
+ Object *ob = get_context_object(C);
+ ShaderFxData *fx = (ShaderFxData *)ptr.data;
+
+ const ShaderFxTypeInfo *fxti = BKE_shaderfx_get_info(fx->type);
+
+ UI_block_lock_set(uiLayoutGetBlock(layout), (ob && ID_IS_LINKED(ob)), ERROR_LIBDATA_MESSAGE);
+
+ /* Effect type icon. */
+ uiLayout *row = uiLayoutRow(layout, false);
+ if (fxti->isDisabled && fxti->isDisabled(fx, 0)) {
+ uiLayoutSetRedAlert(row, true);
+ }
+ uiItemL(row, "", RNA_struct_ui_icon(ptr.type));
+
+ /* Effect name. */
+ row = uiLayoutRow(layout, true);
+ if (!narrow_panel) {
+ uiItemR(row, &ptr, "name", 0, "", ICON_NONE);
+ }
+
+ /* Mode enabling buttons. */
+ if (fxti->flags & eShaderFxTypeFlag_SupportsEditmode) {
+ uiLayout *sub = uiLayoutRow(row, true);
+ uiLayoutSetActive(sub, false);
+ uiItemR(sub, &ptr, "show_in_editmode", 0, "", ICON_NONE);
+ }
+ uiItemR(row, &ptr, "show_viewport", 0, "", ICON_NONE);
+ uiItemR(row, &ptr, "show_render", 0, "", ICON_NONE);
+
+ row = uiLayoutRow(row, false);
+ uiLayoutSetEmboss(row, UI_EMBOSS_NONE);
+ uiItemO(row, "", ICON_X, "OBJECT_OT_shaderfx_remove");
+
+ /* Some padding so the X isn't too close to the drag icon. */
+ uiItemS(layout);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name ShaderFx Registration Helpers
+ * \{ */
+
+static bool shaderfx_ui_poll(const bContext *C, PanelType *UNUSED(pt))
+{
+ Object *ob = get_context_object(C);
+
+ return (ob != NULL) && (ob->type == OB_GPENCIL);
+}
+
+/**
+ * Create a panel in the context's region
+ */
+PanelType *shaderfx_panel_register(ARegionType *region_type, ShaderFxType type, PanelDrawFn draw)
+{
+
+ /* Get the name for the effect's panel. */
+ char panel_idname[BKE_ST_MAXNAME];
+ BKE_shaderfxType_panel_id(type, panel_idname);
+
+ PanelType *panel_type = MEM_callocN(sizeof(PanelType), panel_idname);
+
+ strcpy(panel_type->idname, panel_idname);
+ strcpy(panel_type->label, "");
+ strcpy(panel_type->context, "shaderfx");
+ strcpy(panel_type->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA);
+
+ panel_type->draw_header = shaderfx_panel_header;
+ panel_type->draw = draw;
+ panel_type->poll = shaderfx_ui_poll;
+
+ /* Give the panel the special flag that says it was built here and corresponds to a
+ * shader effect rather than a PanelType. */
+ panel_type->flag = PNL_LAYOUT_HEADER_EXPAND | PNL_DRAW_BOX | PNL_INSTANCED;
+ panel_type->reorder = shaderfx_reorder;
+ panel_type->get_list_data_expand_flag = get_shaderfx_expand_flag;
+ panel_type->set_list_data_expand_flag = set_shaderfx_expand_flag;
+
+ BLI_addtail(&region_type->paneltypes, panel_type);
+
+ return panel_type;
+}
+
+/**
+ * Add a child panel to the parent.
+ *
+ * \note To create the panel type's idname, it appends the \a name argument to the \a parent's
+ * idname.
+ */
+PanelType *shaderfx_subpanel_register(ARegionType *region_type,
+ const char *name,
+ const char *label,
+ PanelDrawFn draw_header,
+ PanelDrawFn draw,
+ PanelType *parent)
+{
+ /* Create the subpanel's ID name. */
+ char panel_idname[BKE_ST_MAXNAME];
+ strcpy(panel_idname, parent->idname);
+ strcat(panel_idname, "_");
+ strcat(panel_idname, name);
+
+ PanelType *panel_type = MEM_callocN(sizeof(PanelType), panel_idname);
+
+ strcpy(panel_type->idname, panel_idname);
+ strcpy(panel_type->label, label);
+ strcpy(panel_type->context, "shaderfx");
+ strcpy(panel_type->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA);
+
+ panel_type->draw_header = draw_header;
+ panel_type->draw = draw;
+ panel_type->poll = shaderfx_ui_poll;
+ panel_type->flag = (PNL_DEFAULT_CLOSED | PNL_DRAW_BOX);
+
+ BLI_assert(parent != NULL);
+ strcpy(panel_type->parent_id, parent->idname);
+ panel_type->parent = parent;
+ BLI_addtail(&parent->children, BLI_genericNodeN(panel_type));
+ BLI_addtail(&region_type->paneltypes, panel_type);
+
+ return panel_type;
+}
+
+/** \} */
diff --git a/source/blender/shader_fx/intern/FX_ui_common.h b/source/blender/shader_fx/intern/FX_ui_common.h
new file mode 100644
index 00000000000..877855b98e4
--- /dev/null
+++ b/source/blender/shader_fx/intern/FX_ui_common.h
@@ -0,0 +1,56 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup modifiers
+ */
+
+#ifndef __FX_UI_COMMON_H__
+#define __FX_UI_COMMON_H__
+
+#include "FX_shader_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ARegionType;
+struct bContext;
+struct PanelType;
+struct uiLayout;
+typedef void (*PanelDrawFn)(const bContext *, Panel *);
+
+void shaderfx_panel_end(struct uiLayout *layout, PointerRNA *ptr);
+
+void shaderfx_panel_get_property_pointers(const bContext *C,
+ struct Panel *panel,
+ struct PointerRNA *r_ob_ptr,
+ struct PointerRNA *r_ptr);
+
+PanelType *shaderfx_panel_register(ARegionType *region_type, ShaderFxType type, PanelDrawFn draw);
+
+struct PanelType *shaderfx_subpanel_register(struct ARegionType *region_type,
+ const char *name,
+ const char *label,
+ PanelDrawFn draw_header,
+ PanelDrawFn draw,
+ struct PanelType *parent);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __FX_UI_COMMON_H__ */
diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo.c
index df571e84d31..e687af15982 100644
--- a/source/blender/windowmanager/gizmo/intern/wm_gizmo.c
+++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo.c
@@ -108,7 +108,7 @@ wmGizmo *WM_gizmo_new_ptr(const wmGizmoType *gzt, wmGizmoGroup *gzgroup, Pointer
}
/**
- * \param name: Must be a valid gizmo type name,
+ * \param idname: Must be a valid gizmo type name,
* if you need to check it exists use #WM_gizmo_new_ptr
* because callers of this function don't NULL check the return value.
*/
diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c
index 0032a341610..2112477e62a 100644
--- a/source/blender/windowmanager/intern/wm.c
+++ b/source/blender/windowmanager/intern/wm.c
@@ -165,10 +165,10 @@ void WM_operator_free_all_after(wmWindowManager *wm, struct wmOperator *op)
/**
* Use with extreme care!,
- * properties, customdata etc - must be compatible.
+ * properties, custom-data etc - must be compatible.
*
* \param op: Operator to assign the type to.
- * \param ot: OperatorType to assign.
+ * \param ot: Operator type to assign.
*/
void WM_operator_type_set(wmOperator *op, wmOperatorType *ot)
{
diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c
index fa12d6cf974..3c2bc14837d 100644
--- a/source/blender/windowmanager/intern/wm_draw.c
+++ b/source/blender/windowmanager/intern/wm_draw.c
@@ -69,6 +69,8 @@
#include "wm_surface.h"
#include "wm_window.h"
+#include "UI_resources.h"
+
#ifdef WITH_OPENSUBDIV
# include "BKE_subsurf.h"
#endif
@@ -128,6 +130,28 @@ static void wm_paintcursor_draw(bContext *C, ScrArea *area, ARegion *region)
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Post Draw Region on display handlers
+ * \{ */
+
+static void wm_region_draw_overlay(bContext *C, ScrArea *area, ARegion *region)
+{
+ wmWindowManager *wm = CTX_wm_manager(C);
+ wmWindow *win = CTX_wm_window(C);
+
+ /* Don't draw overlay with locked interface. Drawing could access scene data that another thread
+ * may be modifying. */
+ if (wm->is_interface_locked) {
+ return;
+ }
+
+ wmViewport(&region->winrct);
+ UI_SetTheme(area->spacetype, region->regiontype);
+ region->type->draw_overlay(C, region);
+ wmWindowViewport(win);
+}
+
+/** \} */
+/* -------------------------------------------------------------------- */
/** \name Internal Utilities
* \{ */
@@ -749,25 +773,30 @@ static void wm_draw_window_onscreen(bContext *C, wmWindow *win, int view)
}
}
- /* Draw paint cursors. */
- if (wm->paintcursors.first) {
- ED_screen_areas_iter (win, screen, area) {
- LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
- if (region->visible && region == screen->active_region) {
- CTX_wm_area_set(C, area);
- CTX_wm_region_set(C, region);
+ /* Draw overlays and paint cursors. */
+ ED_screen_areas_iter (win, screen, area) {
+ LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
+ if (region->visible) {
+ const bool do_paint_cursor = (wm->paintcursors.first && region == screen->active_region);
+ const bool do_draw_overlay = (region->type && region->type->draw_overlay);
+ if (!(do_paint_cursor || do_draw_overlay)) {
+ continue;
+ }
- /* make region ready for draw, scissor, pixelspace */
+ CTX_wm_area_set(C, area);
+ CTX_wm_region_set(C, region);
+ if (do_draw_overlay) {
+ wm_region_draw_overlay(C, area, region);
+ }
+ if (do_paint_cursor) {
wm_paintcursor_draw(C, area, region);
-
- CTX_wm_region_set(C, NULL);
- CTX_wm_area_set(C, NULL);
}
+ CTX_wm_region_set(C, NULL);
+ CTX_wm_area_set(C, NULL);
}
}
-
- wmWindowViewport(win);
}
+ wmWindowViewport(win);
/* Blend in overlapping area regions */
ED_screen_areas_iter (win, screen, area) {
@@ -902,9 +931,9 @@ static bool wm_draw_update_test_window(Main *bmain, bContext *C, wmWindow *win)
bool do_draw = false;
for (region = screen->regionbase.first; region; region = region->next) {
- if (region->do_draw_overlay) {
+ if (region->do_draw_paintcursor) {
screen->do_draw_paintcursor = true;
- region->do_draw_overlay = false;
+ region->do_draw_paintcursor = false;
}
if (region->visible && region->do_draw) {
do_draw = true;
@@ -983,7 +1012,7 @@ void wm_draw_update(bContext *C)
wmWindowManager *wm = CTX_wm_manager(C);
wmWindow *win;
- GPU_free_unused_buffers(bmain);
+ GPU_free_unused_buffers();
for (win = wm->windows.first; win; win = win->next) {
#ifdef WIN32
diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c
index a0a2328463f..1483cf4343e 100644
--- a/source/blender/windowmanager/intern/wm_files.c
+++ b/source/blender/windowmanager/intern/wm_files.c
@@ -679,7 +679,8 @@ static void wm_file_read_post(bContext *C,
bool WM_file_read(bContext *C, const char *filepath, ReportList *reports)
{
/* assume automated tasks with background, don't write recent file list */
- const bool do_history = (G.background == false) && (CTX_wm_manager(C)->op_undo_depth == 0);
+ const bool do_history_file_update = (G.background == false) &&
+ (CTX_wm_manager(C)->op_undo_depth == 0);
bool success = false;
const bool use_data = true;
@@ -750,7 +751,7 @@ bool WM_file_read(bContext *C, const char *filepath, ReportList *reports)
WM_check(C); /* opens window(s), checks keymaps */
if (success) {
- if (do_history) {
+ if (do_history_file_update) {
wm_history_file_update();
}
}
@@ -782,7 +783,7 @@ bool WM_file_read(bContext *C, const char *filepath, ReportList *reports)
if (success == false) {
/* remove from recent files list */
- if (do_history) {
+ if (do_history_file_update) {
RecentFile *recent = wm_file_history_find(filepath);
if (recent) {
wm_history_file_free(recent);
@@ -910,6 +911,13 @@ void wm_homefile_read(bContext *C,
SET_FLAG_FROM_TEST(G.f, (U.flag & USER_SCRIPT_AUTOEXEC_DISABLE) == 0, G_FLAG_SCRIPT_AUTOEXEC);
}
+ if (use_data) {
+ if (reset_app_template) {
+ /* Always load UI when switching to another template. */
+ G.fileflags &= ~G_FILE_NO_UI;
+ }
+ }
+
if (use_userdef || reset_app_template) {
#ifdef WITH_PYTHON
/* This only runs once Blender has already started. */
@@ -1119,18 +1127,6 @@ void wm_homefile_read(bContext *C,
BLI_strncpy(U.app_template, app_template_override, sizeof(U.app_template));
}
- if (use_data) {
- /* Prevent buggy files that had G_FILE_RELATIVE_REMAP written out by mistake.
- * Screws up autosaves otherwise can remove this eventually,
- * only in a 2.53 and older, now its not written. */
- G.fileflags &= ~G_FILE_RELATIVE_REMAP;
-
- if (reset_app_template) {
- /* Always load UI when switching to another template. */
- G.fileflags &= ~G_FILE_NO_UI;
- }
- }
-
bmain = CTX_data_main(C);
if (use_userdef) {
@@ -1418,11 +1414,10 @@ static ImBuf *blend_file_thumb(const bContext *C,
bool write_crash_blend(void)
{
char path[FILE_MAX];
- int fileflags = G.fileflags & ~(G_FILE_HISTORY); /* don't do file history on crash file */
BLI_strncpy(path, BKE_main_blendfile_path_from_global(), sizeof(path));
BLI_path_extension_replace(path, sizeof(path), "_crash.blend");
- if (BLO_write_file(G_MAIN, path, fileflags, NULL, NULL)) {
+ if (BLO_write_file(G_MAIN, path, G.fileflags, &(const struct BlendFileWriteParams){0}, NULL)) {
printf("written: %s\n", path);
return 1;
}
@@ -1435,7 +1430,12 @@ bool write_crash_blend(void)
/**
* \see #wm_homefile_write_exec wraps #BLO_write_file in a similar way.
*/
-static bool wm_file_write(bContext *C, const char *filepath, int fileflags, ReportList *reports)
+static bool wm_file_write(bContext *C,
+ const char *filepath,
+ int fileflags,
+ eBLO_WritePathRemap remap_mode,
+ bool use_save_as_copy,
+ ReportList *reports)
{
Main *bmain = CTX_data_main(C);
Library *li;
@@ -1468,7 +1468,7 @@ static bool wm_file_write(bContext *C, const char *filepath, int fileflags, Repo
/* send the OnSave event */
for (li = bmain->libraries.first; li; li = li->id.next) {
- if (BLI_path_cmp(li->filepath, filepath) == 0) {
+ if (BLI_path_cmp(li->filepath_abs, filepath) == 0) {
BKE_reportf(reports, RPT_ERROR, "Cannot overwrite used library '%.240s'", filepath);
return ok;
}
@@ -1501,21 +1501,29 @@ static bool wm_file_write(bContext *C, const char *filepath, int fileflags, Repo
ED_editors_flush_edits(bmain);
- fileflags |= G_FILE_HISTORY; /* write file history */
-
/* first time saving */
/* XXX temp solution to solve bug, real fix coming (ton) */
- if ((BKE_main_blendfile_path(bmain)[0] == '\0') && !(fileflags & G_FILE_SAVE_COPY)) {
+ if ((BKE_main_blendfile_path(bmain)[0] == '\0') && (use_save_as_copy == false)) {
BLI_strncpy(bmain->name, filepath, sizeof(bmain->name));
}
/* XXX temp solution to solve bug, real fix coming (ton) */
bmain->recovered = 0;
- if (BLO_write_file(CTX_data_main(C), filepath, fileflags, reports, thumb)) {
- const bool do_history = (G.background == false) && (CTX_wm_manager(C)->op_undo_depth == 0);
-
- if (!(fileflags & G_FILE_SAVE_COPY)) {
+ if (BLO_write_file(CTX_data_main(C),
+ filepath,
+ fileflags,
+ &(const struct BlendFileWriteParams){
+ .remap_mode = remap_mode,
+ .use_save_versions = true,
+ .use_save_as_copy = use_save_as_copy,
+ .thumb = thumb,
+ },
+ reports)) {
+ const bool do_history_file_update = (G.background == false) &&
+ (CTX_wm_manager(C)->op_undo_depth == 0);
+
+ if (use_save_as_copy == false) {
G.relbase_valid = 1;
BLI_strncpy(bmain->name, filepath, sizeof(bmain->name)); /* is guaranteed current file */
@@ -1525,7 +1533,7 @@ static bool wm_file_write(bContext *C, const char *filepath, int fileflags, Repo
SET_FLAG_FROM_TEST(G.fileflags, fileflags & G_FILE_COMPRESS, G_FILE_COMPRESS);
/* prevent background mode scripts from clobbering history */
- if (do_history) {
+ if (do_history_file_update) {
wm_history_file_update();
}
@@ -1613,16 +1621,14 @@ void wm_autosave_timer(Main *bmain, wmWindowManager *wm, wmTimer *UNUSED(wt))
WM_event_remove_timer(wm, NULL, wm->autosavetimer);
- /* if a modal operator is running, don't autosave, but try again in 10 seconds */
+ /* If a modal operator is running, don't autosave because we might not be in
+ * a valid state to save. But try again in 10ms. */
LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
LISTBASE_FOREACH (wmEventHandler *, handler_base, &win->modalhandlers) {
if (handler_base->type == WM_HANDLER_TYPE_OP) {
wmEventHandler_Op *handler = (wmEventHandler_Op *)handler_base;
if (handler->op) {
- wm->autosavetimer = WM_event_add_timer(wm, NULL, TIMERAUTOSAVE, 10.0);
- if (G.debug) {
- printf("Skipping auto-save, modal operator running, retrying in ten seconds...\n");
- }
+ wm->autosavetimer = WM_event_add_timer(wm, NULL, TIMERAUTOSAVE, 0.01);
return;
}
}
@@ -1640,12 +1646,12 @@ void wm_autosave_timer(Main *bmain, wmWindowManager *wm, wmTimer *UNUSED(wt))
}
else {
/* Save as regular blend file. */
- int fileflags = G.fileflags & ~(G_FILE_COMPRESS | G_FILE_HISTORY);
+ const int fileflags = G.fileflags & ~G_FILE_COMPRESS;
ED_editors_flush_edits(bmain);
- /* Error reporting into console */
- BLO_write_file(bmain, filepath, fileflags, NULL, NULL);
+ /* Error reporting into console. */
+ BLO_write_file(bmain, filepath, fileflags, &(const struct BlendFileWriteParams){0}, NULL);
}
/* do timer after file write, just in case file write takes a long time */
wm->autosavetimer = WM_event_add_timer(wm, NULL, TIMERAUTOSAVE, U.savetime * 60.0);
@@ -1763,9 +1769,15 @@ static int wm_homefile_write_exec(bContext *C, wmOperator *op)
ED_editors_flush_edits(bmain);
/* Force save as regular blend file. */
- fileflags = G.fileflags & ~(G_FILE_COMPRESS | G_FILE_HISTORY);
-
- if (BLO_write_file(bmain, filepath, fileflags, op->reports, NULL) == 0) {
+ fileflags = G.fileflags & ~G_FILE_COMPRESS;
+
+ if (BLO_write_file(bmain,
+ filepath,
+ fileflags,
+ &(const struct BlendFileWriteParams){
+ .remap_mode = BLO_WRITE_PATH_REMAP_RELATIVE,
+ },
+ op->reports) == 0) {
printf("fail\n");
return OPERATOR_CANCELLED;
}
@@ -2680,7 +2692,15 @@ static int wm_save_as_mainfile_exec(bContext *C, wmOperator *op)
Main *bmain = CTX_data_main(C);
char path[FILE_MAX];
const bool is_save_as = (op->type->invoke == wm_save_as_mainfile_invoke);
-
+ const bool use_save_as_copy = (RNA_struct_property_is_set(op->ptr, "copy") &&
+ RNA_boolean_get(op->ptr, "copy"));
+
+ /* We could expose all options to the users however in most cases remapping
+ * existing relative paths is a good default.
+ * Users can manually make their paths relative & absolute if they wish. */
+ const eBLO_WritePathRemap remap_mode = RNA_boolean_get(op->ptr, "relative_remap") ?
+ BLO_WRITE_PATH_REMAP_RELATIVE :
+ BLO_WRITE_PATH_REMAP_NONE;
save_set_compress(op);
if (RNA_struct_property_is_set(op->ptr, "filepath")) {
@@ -2692,17 +2712,12 @@ static int wm_save_as_mainfile_exec(bContext *C, wmOperator *op)
}
const int fileflags_orig = G.fileflags;
- int fileflags = G.fileflags & ~G_FILE_USERPREFS;
+ int fileflags = G.fileflags;
/* set compression flag */
SET_FLAG_FROM_TEST(fileflags, RNA_boolean_get(op->ptr, "compress"), G_FILE_COMPRESS);
- SET_FLAG_FROM_TEST(fileflags, RNA_boolean_get(op->ptr, "relative_remap"), G_FILE_RELATIVE_REMAP);
- SET_FLAG_FROM_TEST(
- fileflags,
- (RNA_struct_property_is_set(op->ptr, "copy") && RNA_boolean_get(op->ptr, "copy")),
- G_FILE_SAVE_COPY);
- const bool ok = wm_file_write(C, path, fileflags, op->reports);
+ const bool ok = wm_file_write(C, path, fileflags, remap_mode, use_save_as_copy, op->reports);
if ((op->flag & OP_IS_INVOKE) == 0) {
/* OP_IS_INVOKE is set when the operator is called from the GUI.
diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c
index 69858f755ec..d6554dc74a5 100644
--- a/source/blender/windowmanager/intern/wm_files_link.c
+++ b/source/blender/windowmanager/intern/wm_files_link.c
@@ -952,7 +952,7 @@ static int wm_lib_relocate_invoke(bContext *C, wmOperator *op, const wmEvent *UN
BKE_reportf(op->reports,
RPT_ERROR_INVALID_INPUT,
"Cannot relocate indirectly linked library '%s'",
- lib->filepath);
+ lib->filepath_abs);
return OPERATOR_CANCELLED;
}
if (lib->flag & LIBRARY_FLAG_VIRTUAL) {
@@ -962,7 +962,7 @@ static int wm_lib_relocate_invoke(bContext *C, wmOperator *op, const wmEvent *UN
lib->id.name + 2);
return OPERATOR_CANCELLED;
}
- RNA_string_set(op->ptr, "filepath", lib->filepath);
+ RNA_string_set(op->ptr, "filepath", lib->filepath_abs);
WM_event_add_fileselect(C, op);
@@ -1199,24 +1199,24 @@ static void lib_relocate_do(Main *bmain,
void WM_lib_reload(Library *lib, bContext *C, ReportList *reports)
{
- if (!BLO_has_bfile_extension(lib->filepath)) {
- BKE_reportf(reports, RPT_ERROR, "'%s' is not a valid library filepath", lib->filepath);
+ if (!BLO_has_bfile_extension(lib->filepath_abs)) {
+ BKE_reportf(reports, RPT_ERROR, "'%s' is not a valid library filepath", lib->filepath_abs);
return;
}
- if (!BLI_exists(lib->filepath)) {
+ if (!BLI_exists(lib->filepath_abs)) {
BKE_reportf(reports,
RPT_ERROR,
"Trying to reload library '%s' from invalid path '%s'",
lib->id.name,
- lib->filepath);
+ lib->filepath_abs);
return;
}
WMLinkAppendData *lapp_data = wm_link_append_data_new(BLO_LIBLINK_USE_PLACEHOLDERS |
BLO_LIBLINK_FORCE_INDIRECT);
- wm_link_append_data_library_add(lapp_data, lib->filepath);
+ wm_link_append_data_library_add(lapp_data, lib->filepath_abs);
lib_relocate_do(CTX_data_main(C), lib, lapp_data, reports, NULL, true);
@@ -1249,7 +1249,7 @@ static int wm_lib_relocate_exec_do(bContext *C, wmOperator *op, bool do_reload)
BKE_reportf(op->reports,
RPT_ERROR_INVALID_INPUT,
"Cannot relocate indirectly linked library '%s'",
- lib->filepath);
+ lib->filepath_abs);
return OPERATOR_CANCELLED;
}
if (lib->flag & LIBRARY_FLAG_VIRTUAL) {
@@ -1279,7 +1279,7 @@ static int wm_lib_relocate_exec_do(bContext *C, wmOperator *op, bool do_reload)
return OPERATOR_CANCELLED;
}
- if (BLI_path_cmp(lib->filepath, path) == 0) {
+ if (BLI_path_cmp(lib->filepath_abs, path) == 0) {
#ifdef PRINT_DEBUG
printf("We are supposed to reload '%s' lib (%d)...\n", lib->filepath, lib->id.us);
#endif
@@ -1316,7 +1316,7 @@ static int wm_lib_relocate_exec_do(bContext *C, wmOperator *op, bool do_reload)
BLI_join_dirfile(path, sizeof(path), root, relname);
- if (BLI_path_cmp(path, lib->filepath) == 0 || !BLO_has_bfile_extension(relname)) {
+ if (BLI_path_cmp(path, lib->filepath_abs) == 0 || !BLO_has_bfile_extension(relname)) {
continue;
}
diff --git a/source/blender/windowmanager/intern/wm_gesture_ops.c b/source/blender/windowmanager/intern/wm_gesture_ops.c
index 9fb368a02b4..74a94e997e0 100644
--- a/source/blender/windowmanager/intern/wm_gesture_ops.c
+++ b/source/blender/windowmanager/intern/wm_gesture_ops.c
@@ -272,7 +272,7 @@ void WM_gesture_box_cancel(bContext *C, wmOperator *op)
/** \name Circle Gesture
*
* Currently only used for selection or modal paint stuff,
- * calls #wmOperator.exec while hold mouse, exits on release
+ * calls #wmOperatorType.exec while hold mouse, exits on release
* (with no difference between cancel and confirm).
*
* \{ */
diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c
index 80f37de021b..ad2d74b12d5 100644
--- a/source/blender/windowmanager/intern/wm_init_exit.c
+++ b/source/blender/windowmanager/intern/wm_init_exit.c
@@ -496,13 +496,15 @@ void WM_exit_ex(bContext *C, const bool do_python)
Main *bmain = CTX_data_main(C);
char filename[FILE_MAX];
bool has_edited;
- int fileflags = G.fileflags & ~(G_FILE_COMPRESS | G_FILE_HISTORY);
+ const int fileflags = G.fileflags & ~G_FILE_COMPRESS;
BLI_join_dirfile(filename, sizeof(filename), BKE_tempdir_base(), BLENDER_QUIT_FILE);
has_edited = ED_editors_flush_edits(bmain);
- if ((has_edited && BLO_write_file(bmain, filename, fileflags, NULL, NULL)) ||
+ if ((has_edited &&
+ BLO_write_file(
+ bmain, filename, fileflags, &(const struct BlendFileWriteParams){0}, NULL)) ||
(undo_memfile && BLO_memfile_write_file(undo_memfile, filename))) {
printf("Saved session recovery to '%s'\n", filename);
}
@@ -580,7 +582,7 @@ void WM_exit_ex(bContext *C, const bool do_python)
BKE_subdiv_exit();
if (opengl_is_init) {
- GPU_free_unused_buffers(G_MAIN);
+ GPU_free_unused_buffers();
}
BKE_blender_free(); /* blender.c, does entire library and spacetypes */
diff --git a/source/blender/windowmanager/intern/wm_operator_props.c b/source/blender/windowmanager/intern/wm_operator_props.c
index f26fbcae6f1..a15d9c5ac31 100644
--- a/source/blender/windowmanager/intern/wm_operator_props.c
+++ b/source/blender/windowmanager/intern/wm_operator_props.c
@@ -519,8 +519,8 @@ void WM_operator_properties_select_walk_direction(wmOperatorType *ot)
* help getting the wanted behavior to work. Most generic logic should be handled in these, so that
* the select operators only have to care for the case dependent handling.
*
- * Every select operator has slightly different requirements, e.g. VSE strip selection also needs
- * to account for handle selection. This should be the baseline behavior though.
+ * Every select operator has slightly different requirements, e.g. sequencer strip selection
+ * also needs to account for handle selection. This should be the baseline behavior though.
*/
void WM_operator_properties_generic_select(wmOperatorType *ot)
{
diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c
index 14399cf4a9a..eb9d0311247 100644
--- a/source/blender/windowmanager/intern/wm_operators.c
+++ b/source/blender/windowmanager/intern/wm_operators.c
@@ -1269,7 +1269,7 @@ ID *WM_operator_drop_load_path(struct bContext *C, wmOperator *op, const short i
if (is_relative_path) {
if (exists == false) {
if (idcode == ID_IM) {
- BLI_path_rel(((Image *)id)->name, BKE_main_blendfile_path(bmain));
+ BLI_path_rel(((Image *)id)->filepath, BKE_main_blendfile_path(bmain));
}
else {
BLI_assert(0);
diff --git a/source/blender/windowmanager/intern/wm_surface.c b/source/blender/windowmanager/intern/wm_surface.c
index e8850693d69..12e55790259 100644
--- a/source/blender/windowmanager/intern/wm_surface.c
+++ b/source/blender/windowmanager/intern/wm_surface.c
@@ -53,10 +53,17 @@ void wm_surfaces_iter(bContext *C, void (*cb)(bContext *C, wmSurface *))
void wm_surface_clear_drawable(void)
{
if (g_drawable) {
+ WM_opengl_context_release(g_drawable->ghost_ctx);
+ GPU_context_active_set(NULL);
+
BLF_batch_reset();
gpu_batch_presets_reset();
immDeactivate();
+ if (g_drawable->deactivate) {
+ g_drawable->deactivate();
+ }
+
g_drawable = NULL;
}
}
@@ -67,7 +74,10 @@ void wm_surface_set_drawable(wmSurface *surface, bool activate)
g_drawable = surface;
if (activate) {
- GHOST_ActivateOpenGLContext(surface->ghost_ctx);
+ if (surface->activate) {
+ surface->activate();
+ }
+ WM_opengl_context_activate(surface->ghost_ctx);
}
GPU_context_active_set(surface->gpu_ctx);
@@ -109,6 +119,8 @@ void wm_surface_remove(wmSurface *surface)
void wm_surfaces_free(void)
{
+ wm_surface_clear_drawable();
+
for (wmSurface *surf = global_surface_list.first, *surf_next; surf; surf = surf_next) {
surf_next = surf->next;
wm_surface_remove(surf);
diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c
index 1ba22652157..e1b2285313c 100644
--- a/source/blender/windowmanager/intern/wm_window.c
+++ b/source/blender/windowmanager/intern/wm_window.c
@@ -463,6 +463,7 @@ void wm_window_close(bContext *C, wmWindowManager *wm, wmWindow *win)
BLI_assert(BKE_workspace_layout_screen_get(layout) == screen);
BKE_workspace_layout_remove(bmain, workspace, layout);
+ WM_event_add_notifier(C, NC_SCREEN | ND_LAYOUTDELETE, NULL);
}
}
diff --git a/source/blender/windowmanager/wm_surface.h b/source/blender/windowmanager/wm_surface.h
index e1b00ae1ade..bc1cc825e4b 100644
--- a/source/blender/windowmanager/wm_surface.h
+++ b/source/blender/windowmanager/wm_surface.h
@@ -38,6 +38,11 @@ typedef struct wmSurface {
void (*draw)(struct bContext *);
/** Free customdata, not the surface itself (done by wm_surface API) */
void (*free_data)(struct wmSurface *);
+
+ /** Called when surface is activated for drawing (made drawable). */
+ void (*activate)(void);
+ /** Called when surface is deactivated for drawing (current drawable cleared). */
+ void (*deactivate)(void);
} wmSurface;
/* Create/Free */
diff --git a/source/blender/windowmanager/xr/intern/wm_xr_session.c b/source/blender/windowmanager/xr/intern/wm_xr_session.c
index e9ff38c5a92..2f72b2b25a5 100644
--- a/source/blender/windowmanager/xr/intern/wm_xr_session.c
+++ b/source/blender/windowmanager/xr/intern/wm_xr_session.c
@@ -315,12 +315,9 @@ static void wm_xr_session_surface_draw(bContext *C)
wm_xr_session_draw_data_populate(
&wm->xr, CTX_data_scene(C), CTX_data_ensure_evaluated_depsgraph(C), &draw_data);
- DRW_xr_drawing_begin();
-
GHOST_XrSessionDrawViews(wm->xr.runtime->context, &draw_data);
GPU_offscreen_unbind(surface_data->offscreen, false);
- DRW_xr_drawing_end();
}
bool wm_xr_session_surface_offscreen_ensure(wmXrSurfaceData *surface_data,
@@ -391,6 +388,9 @@ static wmSurface *wm_xr_session_surface_create(void)
surface->draw = wm_xr_session_surface_draw;
surface->free_data = wm_xr_session_surface_free_data;
+ surface->activate = DRW_xr_drawing_begin;
+ surface->deactivate = DRW_xr_drawing_end;
+
surface->ghost_ctx = DRW_xr_opengl_context_get();
surface->gpu_ctx = DRW_xr_gpu_context_get();
diff --git a/source/creator/creator.c b/source/creator/creator.c
index 67fc5fcd09c..c2333f333fb 100644
--- a/source/creator/creator.c
+++ b/source/creator/creator.c
@@ -159,7 +159,7 @@ static void callback_mem_error(const char *errorStr)
static void main_callback_setup(void)
{
- /* Error output from the alloc routines: */
+ /* Error output from the guarded allocation routines. */
MEM_set_error_callback(callback_mem_error);
}
@@ -209,7 +209,7 @@ static void callback_clg_fatal(void *fp)
static void *evil_C = NULL;
# ifdef __APPLE__
-/* environ is not available in mac shared libraries */
+/* Environment is not available in macOS shared libraries. */
# include <crt_externs.h>
char **environ = NULL;
# endif
@@ -247,20 +247,24 @@ int main(int argc,
struct CreatorAtExitData app_init_data = {NULL};
BKE_blender_atexit_register(callback_main_atexit, &app_init_data);
- /* Unbuffered stdout makes stdout and stderr better synchronized, and helps
+ /* Un-buffered `stdout` makes `stdout` and `stderr` better synchronized, and helps
* when stepping through code in a debugger (prints are immediately
- * visible). */
+ * visible). However disabling buffering causes lock contention on windows
+ * see T76767 for details, since this is a debugging aid, we do not enable
+ * the un-buffered behavior for release builds. */
+#ifndef NDEBUG
setvbuf(stdout, NULL, _IONBF, 0);
+#endif
#ifdef WIN32
- /* We delay loading of openmp so we can set the policy here. */
+ /* We delay loading of OPENMP so we can set the policy here. */
# if defined(_MSC_VER)
_putenv_s("OMP_WAIT_POLICY", "PASSIVE");
# endif
- /* Win32 Unicode Args */
+ /* Win32 Unicode Arguments. */
/* NOTE: cannot use guardedalloc malloc here, as it's not yet initialized
- * (it depends on the args passed in, which is what we're getting here!)
+ * (it depends on the arguments passed in, which is what we're getting here!)
*/
{
wchar_t **argv_16 = CommandLineToArgvW(GetCommandLineW(), &argc);
@@ -342,7 +346,7 @@ int main(int argc,
main_callback_setup();
#if defined(__APPLE__) && !defined(WITH_PYTHON_MODULE) && !defined(WITH_HEADLESS)
- /* patch to ignore argument finder gives us (pid?) */
+ /* Patch to ignore argument finder gives us (PID?) */
if (argc == 2 && STREQLEN(argv[1], "-psn_", 5)) {
extern int GHOST_HACK_getFirstFile(char buf[]);
static char firstfilebuf[512];
@@ -416,7 +420,7 @@ int main(int argc,
RE_engines_init();
init_nodesystem();
psys_init_rng();
- /* end second init */
+ /* End second initialization. */
BKE_asset_engines_init();
@@ -474,7 +478,7 @@ int main(int argc,
* #WM_init() before #BPY_python_start() crashes Blender at startup.
*/
- /* TODO - U.pythondir */
+ /* TODO: #U.pythondir */
#else
printf(
"\n* WARNING * - Blender compiled without Python!\n"
@@ -531,7 +535,7 @@ int main(int argc,
WM_main(C);
return 0;
-} /* end of int main(argc, argv) */
+} /* End of int main(...) function. */
#ifdef WITH_PYTHON_MODULE
void main_python_exit(void)
diff --git a/tests/gtests/alembic/CMakeLists.txt b/tests/gtests/alembic/CMakeLists.txt
index 6ba1c4465d9..90f1829fc77 100644
--- a/tests/gtests/alembic/CMakeLists.txt
+++ b/tests/gtests/alembic/CMakeLists.txt
@@ -24,6 +24,8 @@ set(INC
../../../source/blender/blenlib
../../../source/blender/blenkernel
../../../source/blender/io/alembic
+ ../../../source/blender/io/common
+ ../../../source/blender/io/usd/intern
../../../source/blender/makesdna
../../../source/blender/depsgraph
${ALEMBIC_INCLUDE_DIRS}
diff --git a/tests/gtests/alembic/abc_export_test.cc b/tests/gtests/alembic/abc_export_test.cc
index 238fab2f872..cdc98178550 100644
--- a/tests/gtests/alembic/abc_export_test.cc
+++ b/tests/gtests/alembic/abc_export_test.cc
@@ -1,11 +1,12 @@
#include "testing/testing.h"
// Keep first since utildefines defines AT which conflicts with STL
-#include "intern/abc_exporter.h"
+#include "exporter/abc_archive.h"
#include "intern/abc_util.h"
extern "C" {
#include "BKE_main.h"
+#include "BLI_fileops.h"
#include "BLI_math.h"
#include "BLI_utildefines.h"
#include "DNA_scene_types.h"
@@ -13,138 +14,150 @@ extern "C" {
#include "DEG_depsgraph.h"
-class TestableAbcExporter : public AbcExporter {
- public:
- TestableAbcExporter(Main *bmain, const char *filename, ExportSettings &settings)
- : AbcExporter(bmain, filename, settings)
- {
- }
-
- void getShutterSamples(unsigned int nr_of_samples,
- bool time_relative,
- std::vector<double> &samples)
- {
- AbcExporter::getShutterSamples(nr_of_samples, time_relative, samples);
- }
-
- void getFrameSet(unsigned int nr_of_samples, std::set<double> &frames)
- {
- AbcExporter::getFrameSet(nr_of_samples, frames);
- }
-};
+namespace blender {
+namespace io {
+namespace alembic {
class AlembicExportTest : public testing::Test {
protected:
- ExportSettings settings;
+ ABCArchive *abc_archive;
+
+ AlembicExportParams params;
Scene scene;
Depsgraph *depsgraph;
- TestableAbcExporter *exporter;
Main *bmain;
virtual void SetUp()
{
- settings.frame_start = 31.0;
- settings.frame_end = 223.0;
+ abc_archive = nullptr;
/* Fake a 25 FPS scene with a nonzero base (because that's sometimes forgotten) */
scene.r.frs_sec = 50;
scene.r.frs_sec_base = 2;
+ strcpy(scene.id.name, "SCTestScene");
bmain = BKE_main_new();
/* TODO(sergey): Pass scene layer somehow? */
ViewLayer *view_layer = (ViewLayer *)scene.view_layers.first;
- settings.depsgraph = depsgraph = DEG_graph_new(bmain, &scene, view_layer, DAG_EVAL_VIEWPORT);
-
- settings.scene = &scene;
- settings.view_layer = view_layer;
-
- exporter = NULL;
+ depsgraph = DEG_graph_new(bmain, &scene, view_layer, DAG_EVAL_RENDER);
}
virtual void TearDown()
{
BKE_main_free(bmain);
DEG_graph_free(depsgraph);
- delete exporter;
+ deleteArchive();
+ }
+
+ // Call after setting up the parameters.
+ void createArchive()
+ {
+ if (abc_archive != nullptr) {
+ deleteArchive();
+ }
+ abc_archive = new ABCArchive(bmain, &scene, params, "somefile.abc");
}
- // Call after setting up the settings.
- void createExporter()
+ void deleteArchive()
{
- exporter = new TestableAbcExporter(bmain, "somefile.abc", settings);
+ delete abc_archive;
+ if (BLI_exists("somefile.abc")) {
+ BLI_delete("somefile.abc", false, false);
+ }
+ abc_archive = nullptr;
}
};
-TEST_F(AlembicExportTest, TimeSamplesFullShutter)
+TEST_F(AlembicExportTest, TimeSamplesFullShutterUniform)
{
- settings.shutter_open = 0.0;
- settings.shutter_close = 1.0;
-
- createExporter();
- std::vector<double> samples;
-
- /* test 5 samples per frame */
- exporter->getShutterSamples(5, true, samples);
- EXPECT_EQ(5, samples.size());
- EXPECT_NEAR(1.240, samples[0], 1e-5f);
- EXPECT_NEAR(1.248, samples[1], 1e-5f);
- EXPECT_NEAR(1.256, samples[2], 1e-5f);
- EXPECT_NEAR(1.264, samples[3], 1e-5f);
- EXPECT_NEAR(1.272, samples[4], 1e-5f);
-
- /* test same, but using frame number offset instead of time */
- exporter->getShutterSamples(5, false, samples);
- EXPECT_EQ(5, samples.size());
- EXPECT_NEAR(0.0, samples[0], 1e-5f);
- EXPECT_NEAR(0.2, samples[1], 1e-5f);
- EXPECT_NEAR(0.4, samples[2], 1e-5f);
- EXPECT_NEAR(0.6, samples[3], 1e-5f);
- EXPECT_NEAR(0.8, samples[4], 1e-5f);
-
- /* use the same setup to test getFrameSet() */
- std::set<double> frames;
- exporter->getFrameSet(5, frames);
- EXPECT_EQ(965, frames.size());
- EXPECT_EQ(1, frames.count(31.0));
- EXPECT_EQ(1, frames.count(31.2));
- EXPECT_EQ(1, frames.count(31.4));
- EXPECT_EQ(1, frames.count(31.6));
- EXPECT_EQ(1, frames.count(31.8));
+ /* Test 5 samples per frame, for 2 frames. */
+ params.shutter_open = 0.0;
+ params.shutter_close = 1.0;
+ params.frame_start = 31.0;
+ params.frame_end = 32.0;
+ params.frame_samples_xform = params.frame_samples_shape = 5;
+ createArchive();
+ std::vector<double> frames(abc_archive->frames_begin(), abc_archive->frames_end());
+ EXPECT_EQ(10, frames.size());
+ EXPECT_NEAR(31.0, frames[0], 1e-5);
+ EXPECT_NEAR(31.2, frames[1], 1e-5);
+ EXPECT_NEAR(31.4, frames[2], 1e-5);
+ EXPECT_NEAR(31.6, frames[3], 1e-5);
+ EXPECT_NEAR(31.8, frames[4], 1e-5);
+ EXPECT_NEAR(32.0, frames[5], 1e-5);
+ EXPECT_NEAR(32.2, frames[6], 1e-5);
+ EXPECT_NEAR(32.4, frames[7], 1e-5);
+ EXPECT_NEAR(32.6, frames[8], 1e-5);
+ EXPECT_NEAR(32.8, frames[9], 1e-5);
+
+ for (double frame : frames) {
+ EXPECT_TRUE(abc_archive->is_xform_frame(frame));
+ EXPECT_TRUE(abc_archive->is_shape_frame(frame));
+ }
+}
+
+TEST_F(AlembicExportTest, TimeSamplesFullShutterDifferent)
+{
+ /* Test 3 samples per frame for transforms, and 2 per frame for shapes, for 2 frames. */
+ params.shutter_open = 0.0;
+ params.shutter_close = 1.0;
+ params.frame_start = 31.0;
+ params.frame_end = 32.0;
+ params.frame_samples_xform = 3;
+ params.frame_samples_shape = 2;
+ createArchive();
+ std::vector<double> frames(abc_archive->frames_begin(), abc_archive->frames_end());
+ EXPECT_EQ(8, frames.size());
+ EXPECT_NEAR(31.0, frames[0], 1e-5); // transform + shape
+ EXPECT_TRUE(abc_archive->is_xform_frame(frames[0]));
+ EXPECT_TRUE(abc_archive->is_shape_frame(frames[0]));
+ EXPECT_NEAR(31.33333, frames[1], 1e-5); // transform
+ EXPECT_TRUE(abc_archive->is_xform_frame(frames[1]));
+ EXPECT_FALSE(abc_archive->is_shape_frame(frames[1]));
+ EXPECT_NEAR(31.5, frames[2], 1e-5); // shape
+ EXPECT_FALSE(abc_archive->is_xform_frame(frames[2]));
+ EXPECT_TRUE(abc_archive->is_shape_frame(frames[2]));
+ EXPECT_NEAR(31.66666, frames[3], 1e-5); // transform
+ EXPECT_TRUE(abc_archive->is_xform_frame(frames[3]));
+ EXPECT_FALSE(abc_archive->is_shape_frame(frames[3]));
+ EXPECT_NEAR(32.0, frames[4], 1e-5); // transform + shape
+ EXPECT_TRUE(abc_archive->is_xform_frame(frames[4]));
+ EXPECT_TRUE(abc_archive->is_shape_frame(frames[4]));
+ EXPECT_NEAR(32.33333, frames[5], 1e-5); // transform
+ EXPECT_TRUE(abc_archive->is_xform_frame(frames[5]));
+ EXPECT_FALSE(abc_archive->is_shape_frame(frames[5]));
+ EXPECT_NEAR(32.5, frames[6], 1e-5); // shape
+ EXPECT_FALSE(abc_archive->is_xform_frame(frames[6]));
+ EXPECT_TRUE(abc_archive->is_shape_frame(frames[6]));
+ EXPECT_NEAR(32.66666, frames[7], 1e-5); // transform
+ EXPECT_TRUE(abc_archive->is_xform_frame(frames[7]));
+ EXPECT_FALSE(abc_archive->is_shape_frame(frames[7]));
}
TEST_F(AlembicExportTest, TimeSamples180degShutter)
{
- settings.shutter_open = -0.25;
- settings.shutter_close = 0.25;
-
- createExporter();
- std::vector<double> samples;
-
- /* test 5 samples per frame */
- exporter->getShutterSamples(5, true, samples);
- EXPECT_EQ(5, samples.size());
- EXPECT_NEAR(1.230, samples[0], 1e-5f);
- EXPECT_NEAR(1.234, samples[1], 1e-5f);
- EXPECT_NEAR(1.238, samples[2], 1e-5f);
- EXPECT_NEAR(1.242, samples[3], 1e-5f);
- EXPECT_NEAR(1.246, samples[4], 1e-5f);
-
- /* test same, but using frame number offset instead of time */
- exporter->getShutterSamples(5, false, samples);
- EXPECT_EQ(5, samples.size());
- EXPECT_NEAR(-0.25, samples[0], 1e-5f);
- EXPECT_NEAR(-0.15, samples[1], 1e-5f);
- EXPECT_NEAR(-0.05, samples[2], 1e-5f);
- EXPECT_NEAR(0.05, samples[3], 1e-5f);
- EXPECT_NEAR(0.15, samples[4], 1e-5f);
-
- /* Use the same setup to test getFrameSet().
- * Here only a few numbers are tested, due to rounding issues. */
- std::set<double> frames;
- exporter->getFrameSet(5, frames);
- EXPECT_EQ(965, frames.size());
- EXPECT_EQ(1, frames.count(30.75));
- EXPECT_EQ(1, frames.count(30.95));
- EXPECT_EQ(1, frames.count(31.15));
+ /* Test 5 samples per frame, for 2 frames. */
+ params.shutter_open = -0.25;
+ params.shutter_close = 0.25;
+ params.frame_start = 31.0;
+ params.frame_end = 32.0;
+ params.frame_samples_xform = params.frame_samples_shape = 5;
+ createArchive();
+ std::vector<double> frames(abc_archive->frames_begin(), abc_archive->frames_end());
+ EXPECT_EQ(10, frames.size());
+ EXPECT_NEAR(31 - 0.25, frames[0], 1e-5);
+ EXPECT_NEAR(31 - 0.15, frames[1], 1e-5);
+ EXPECT_NEAR(31 - 0.05, frames[2], 1e-5);
+ EXPECT_NEAR(31 + 0.05, frames[3], 1e-5);
+ EXPECT_NEAR(31 + 0.15, frames[4], 1e-5);
+ EXPECT_NEAR(32 - 0.25, frames[5], 1e-5);
+ EXPECT_NEAR(32 - 0.15, frames[6], 1e-5);
+ EXPECT_NEAR(32 - 0.05, frames[7], 1e-5);
+ EXPECT_NEAR(32 + 0.05, frames[8], 1e-5);
+ EXPECT_NEAR(32 + 0.15, frames[9], 1e-5);
}
+
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/tests/gtests/alembic/abc_matrix_test.cc b/tests/gtests/alembic/abc_matrix_test.cc
index 9b16eb108be..fe0635ea7ab 100644
--- a/tests/gtests/alembic/abc_matrix_test.cc
+++ b/tests/gtests/alembic/abc_matrix_test.cc
@@ -8,6 +8,10 @@ extern "C" {
#include "BLI_utildefines.h"
}
+namespace blender {
+namespace io {
+namespace alembic {
+
TEST(abc_matrix, CreateRotationMatrixY_YfromZ)
{
// Input variables
@@ -284,3 +288,7 @@ TEST(abc_matrix, CopyM44AxisSwapWithScale_gimbal_ZfromY)
EXPECT_M4_NEAR(expect, result, 1e-5f);
}
+
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/tests/gtests/blenlib/BLI_array_test.cc b/tests/gtests/blenlib/BLI_array_test.cc
index fc3c15fe813..9c77c69e296 100644
--- a/tests/gtests/blenlib/BLI_array_test.cc
+++ b/tests/gtests/blenlib/BLI_array_test.cc
@@ -2,26 +2,26 @@
#include "BLI_strict_flags.h"
#include "testing/testing.h"
-using namespace blender;
+namespace blender {
TEST(array, DefaultConstructor)
{
Array<int> array;
- EXPECT_EQ(array.size(), 0);
+ EXPECT_EQ(array.size(), 0u);
EXPECT_TRUE(array.is_empty());
}
TEST(array, SizeConstructor)
{
Array<int> array(5);
- EXPECT_EQ(array.size(), 5);
+ EXPECT_EQ(array.size(), 5u);
EXPECT_FALSE(array.is_empty());
}
TEST(array, FillConstructor)
{
Array<int> array(5, 8);
- EXPECT_EQ(array.size(), 5);
+ EXPECT_EQ(array.size(), 5u);
EXPECT_EQ(array[0], 8);
EXPECT_EQ(array[1], 8);
EXPECT_EQ(array[2], 8);
@@ -32,7 +32,7 @@ TEST(array, FillConstructor)
TEST(array, InitializerListConstructor)
{
Array<int> array = {4, 5, 6, 7};
- EXPECT_EQ(array.size(), 4);
+ EXPECT_EQ(array.size(), 4u);
EXPECT_EQ(array[0], 4);
EXPECT_EQ(array[1], 5);
EXPECT_EQ(array[2], 6);
@@ -44,7 +44,7 @@ TEST(array, SpanConstructor)
int stackarray[4] = {6, 7, 8, 9};
Span<int> span(stackarray, ARRAY_SIZE(stackarray));
Array<int> array(span);
- EXPECT_EQ(array.size(), 4);
+ EXPECT_EQ(array.size(), 4u);
EXPECT_EQ(array[0], 6);
EXPECT_EQ(array[1], 7);
EXPECT_EQ(array[2], 8);
@@ -56,8 +56,8 @@ TEST(array, CopyConstructor)
Array<int> array = {5, 6, 7, 8};
Array<int> new_array(array);
- EXPECT_EQ(array.size(), 4);
- EXPECT_EQ(new_array.size(), 4);
+ EXPECT_EQ(array.size(), 4u);
+ EXPECT_EQ(new_array.size(), 4u);
EXPECT_NE(array.data(), new_array.data());
EXPECT_EQ(new_array[0], 5);
EXPECT_EQ(new_array[1], 6);
@@ -70,8 +70,8 @@ TEST(array, MoveConstructor)
Array<int> array = {5, 6, 7, 8};
Array<int> new_array(std::move(array));
- EXPECT_EQ(array.size(), 0);
- EXPECT_EQ(new_array.size(), 4);
+ EXPECT_EQ(array.size(), 0u);
+ EXPECT_EQ(new_array.size(), 4u);
EXPECT_EQ(new_array[0], 5);
EXPECT_EQ(new_array[1], 6);
EXPECT_EQ(new_array[2], 7);
@@ -82,10 +82,10 @@ TEST(array, CopyAssignment)
{
Array<int> array = {1, 2, 3};
Array<int> new_array = {4};
- EXPECT_EQ(new_array.size(), 1);
+ EXPECT_EQ(new_array.size(), 1u);
new_array = array;
- EXPECT_EQ(new_array.size(), 3);
- EXPECT_EQ(array.size(), 3);
+ EXPECT_EQ(new_array.size(), 3u);
+ EXPECT_EQ(array.size(), 3u);
EXPECT_NE(array.data(), new_array.data());
EXPECT_EQ(new_array[0], 1);
EXPECT_EQ(new_array[1], 2);
@@ -96,10 +96,10 @@ TEST(array, MoveAssignment)
{
Array<int> array = {1, 2, 3};
Array<int> new_array = {4};
- EXPECT_EQ(new_array.size(), 1);
+ EXPECT_EQ(new_array.size(), 1u);
new_array = std::move(array);
- EXPECT_EQ(new_array.size(), 3);
- EXPECT_EQ(array.size(), 0);
+ EXPECT_EQ(new_array.size(), 3u);
+ EXPECT_EQ(array.size(), 0u);
EXPECT_EQ(new_array[0], 1);
EXPECT_EQ(new_array[1], 2);
EXPECT_EQ(new_array[2], 3);
@@ -124,3 +124,40 @@ TEST(array, TrivialTypeSizeConstructor)
EXPECT_EQ(*ptr, magic);
delete array;
}
+
+struct ConstructibleType {
+ char value;
+
+ ConstructibleType()
+ {
+ value = 42;
+ }
+};
+
+TEST(array, NoInitializationSizeConstructor)
+{
+ using MyArray = Array<ConstructibleType>;
+
+ AlignedBuffer<sizeof(MyArray), alignof(MyArray)> buffer;
+ char *buffer_ptr = (char *)buffer.ptr();
+ memset(buffer_ptr, 100, sizeof(MyArray));
+
+ /* Doing this to avoid some compiler optimization. */
+ for (uint i : IndexRange(sizeof(MyArray))) {
+ EXPECT_EQ(buffer_ptr[i], 100);
+ }
+
+ {
+ MyArray &array = *new (buffer.ptr()) MyArray(1, NoInitialization());
+ EXPECT_EQ(array[0].value, 100);
+ array.clear_without_destruct();
+ array.~Array();
+ }
+ {
+ MyArray &array = *new (buffer.ptr()) MyArray(1);
+ EXPECT_EQ(array[0].value, 42);
+ array.~Array();
+ }
+}
+
+} // namespace blender
diff --git a/tests/gtests/blenlib/BLI_edgehash_test.cc b/tests/gtests/blenlib/BLI_edgehash_test.cc
index 982f5b35565..23ad618825b 100644
--- a/tests/gtests/blenlib/BLI_edgehash_test.cc
+++ b/tests/gtests/blenlib/BLI_edgehash_test.cc
@@ -2,6 +2,7 @@
#include "testing/testing.h"
#include <algorithm>
+#include <random>
#include <vector>
extern "C" {
@@ -320,7 +321,7 @@ TEST(edgehash, StressTest)
}
std::vector<Edge> shuffled = edges;
- std::random_shuffle(shuffled.begin(), shuffled.end());
+ std::shuffle(shuffled.begin(), shuffled.end(), std::default_random_engine());
/* then remove half of them */
int remove_until = shuffled.size() / 2;
diff --git a/tests/gtests/blenlib/BLI_index_mask_test.cc b/tests/gtests/blenlib/BLI_index_mask_test.cc
index 63b528c91b1..50b32be6364 100644
--- a/tests/gtests/blenlib/BLI_index_mask_test.cc
+++ b/tests/gtests/blenlib/BLI_index_mask_test.cc
@@ -1,7 +1,7 @@
#include "BLI_index_mask.hh"
#include "testing/testing.h"
-using namespace blender;
+namespace blender {
TEST(index_mask, DefaultConstructor)
{
@@ -37,3 +37,5 @@ TEST(index_mask, RangeConstructor)
EXPECT_EQ(indices[1], 4);
EXPECT_EQ(indices[2], 5);
}
+
+} // namespace blender
diff --git a/tests/gtests/blenlib/BLI_index_range_test.cc b/tests/gtests/blenlib/BLI_index_range_test.cc
index da51b7a66c0..e484da5ef42 100644
--- a/tests/gtests/blenlib/BLI_index_range_test.cc
+++ b/tests/gtests/blenlib/BLI_index_range_test.cc
@@ -3,46 +3,46 @@
#include "BLI_vector.hh"
#include "testing/testing.h"
-using namespace blender;
+namespace blender {
TEST(index_range, DefaultConstructor)
{
IndexRange range;
- EXPECT_EQ(range.size(), 0);
+ EXPECT_EQ(range.size(), 0u);
Vector<uint> vector;
for (uint value : range) {
vector.append(value);
}
- EXPECT_EQ(vector.size(), 0);
+ EXPECT_EQ(vector.size(), 0u);
}
TEST(index_range, SingleElementRange)
{
IndexRange range(4, 1);
- EXPECT_EQ(range.size(), 1);
- EXPECT_EQ(*range.begin(), 4);
+ EXPECT_EQ(range.size(), 1u);
+ EXPECT_EQ(*range.begin(), 4u);
Vector<uint> vector;
for (uint value : range) {
vector.append(value);
}
- EXPECT_EQ(vector.size(), 1);
- EXPECT_EQ(vector[0], 4);
+ EXPECT_EQ(vector.size(), 1u);
+ EXPECT_EQ(vector[0], 4u);
}
TEST(index_range, MultipleElementRange)
{
IndexRange range(6, 4);
- EXPECT_EQ(range.size(), 4);
+ EXPECT_EQ(range.size(), 4u);
Vector<uint> vector;
for (uint value : range) {
vector.append(value);
}
- EXPECT_EQ(vector.size(), 4);
+ EXPECT_EQ(vector.size(), 4u);
for (uint i = 0; i < 4; i++) {
EXPECT_EQ(vector[i], i + 6);
}
@@ -51,28 +51,28 @@ TEST(index_range, MultipleElementRange)
TEST(index_range, SubscriptOperator)
{
IndexRange range(5, 5);
- EXPECT_EQ(range[0], 5);
- EXPECT_EQ(range[1], 6);
- EXPECT_EQ(range[2], 7);
+ EXPECT_EQ(range[0], 5u);
+ EXPECT_EQ(range[1], 6u);
+ EXPECT_EQ(range[2], 7u);
}
TEST(index_range, Before)
{
IndexRange range = IndexRange(5, 5).before(3);
- EXPECT_EQ(range[0], 2);
- EXPECT_EQ(range[1], 3);
- EXPECT_EQ(range[2], 4);
- EXPECT_EQ(range.size(), 3);
+ EXPECT_EQ(range[0], 2u);
+ EXPECT_EQ(range[1], 3u);
+ EXPECT_EQ(range[2], 4u);
+ EXPECT_EQ(range.size(), 3u);
}
TEST(index_range, After)
{
IndexRange range = IndexRange(5, 5).after(4);
- EXPECT_EQ(range[0], 10);
- EXPECT_EQ(range[1], 11);
- EXPECT_EQ(range[2], 12);
- EXPECT_EQ(range[3], 13);
- EXPECT_EQ(range.size(), 4);
+ EXPECT_EQ(range[0], 10u);
+ EXPECT_EQ(range[1], 11u);
+ EXPECT_EQ(range[2], 12u);
+ EXPECT_EQ(range[3], 13u);
+ EXPECT_EQ(range.size(), 4u);
}
TEST(index_range, Contains)
@@ -88,52 +88,54 @@ TEST(index_range, Contains)
TEST(index_range, First)
{
IndexRange range = IndexRange(5, 3);
- EXPECT_EQ(range.first(), 5);
+ EXPECT_EQ(range.first(), 5u);
}
TEST(index_range, Last)
{
IndexRange range = IndexRange(5, 3);
- EXPECT_EQ(range.last(), 7);
+ EXPECT_EQ(range.last(), 7u);
}
TEST(index_range, OneAfterEnd)
{
IndexRange range = IndexRange(5, 3);
- EXPECT_EQ(range.one_after_last(), 8);
+ EXPECT_EQ(range.one_after_last(), 8u);
}
TEST(index_range, Start)
{
IndexRange range = IndexRange(6, 2);
- EXPECT_EQ(range.start(), 6);
+ EXPECT_EQ(range.start(), 6u);
}
TEST(index_range, Slice)
{
IndexRange range = IndexRange(5, 15);
IndexRange slice = range.slice(2, 6);
- EXPECT_EQ(slice.size(), 6);
- EXPECT_EQ(slice.first(), 7);
- EXPECT_EQ(slice.last(), 12);
+ EXPECT_EQ(slice.size(), 6u);
+ EXPECT_EQ(slice.first(), 7u);
+ EXPECT_EQ(slice.last(), 12u);
}
TEST(index_range, SliceRange)
{
IndexRange range = IndexRange(5, 15);
IndexRange slice = range.slice(IndexRange(3, 5));
- EXPECT_EQ(slice.size(), 5);
- EXPECT_EQ(slice.first(), 8);
- EXPECT_EQ(slice.last(), 12);
+ EXPECT_EQ(slice.size(), 5u);
+ EXPECT_EQ(slice.first(), 8u);
+ EXPECT_EQ(slice.last(), 12u);
}
TEST(index_range, AsSpan)
{
IndexRange range = IndexRange(4, 6);
Span<uint> span = range.as_span();
- EXPECT_EQ(span.size(), 6);
- EXPECT_EQ(span[0], 4);
- EXPECT_EQ(span[1], 5);
- EXPECT_EQ(span[2], 6);
- EXPECT_EQ(span[3], 7);
+ EXPECT_EQ(span.size(), 6u);
+ EXPECT_EQ(span[0], 4u);
+ EXPECT_EQ(span[1], 5u);
+ EXPECT_EQ(span[2], 6u);
+ EXPECT_EQ(span[3], 7u);
}
+
+} // namespace blender
diff --git a/tests/gtests/blenlib/BLI_linear_allocator_test.cc b/tests/gtests/blenlib/BLI_linear_allocator_test.cc
index 0d926ca7931..bbea4c1239f 100644
--- a/tests/gtests/blenlib/BLI_linear_allocator_test.cc
+++ b/tests/gtests/blenlib/BLI_linear_allocator_test.cc
@@ -2,7 +2,7 @@
#include "BLI_strict_flags.h"
#include "testing/testing.h"
-using namespace blender;
+namespace blender {
static bool is_aligned(void *ptr, uint alignment)
{
@@ -68,7 +68,7 @@ TEST(linear_allocator, AllocateArray)
LinearAllocator<> allocator;
MutableSpan<int> span = allocator.allocate_array<int>(5);
- EXPECT_EQ(span.size(), 5);
+ EXPECT_EQ(span.size(), 5u);
}
TEST(linear_allocator, Construct)
@@ -77,7 +77,7 @@ TEST(linear_allocator, Construct)
std::array<int, 5> values = {1, 2, 3, 4, 5};
Vector<int> *vector = allocator.construct<Vector<int>>(values);
- EXPECT_EQ(vector->size(), 5);
+ EXPECT_EQ(vector->size(), 5u);
EXPECT_EQ((*vector)[3], 4);
vector->~Vector();
}
@@ -90,8 +90,8 @@ TEST(linear_allocator, ConstructElementsAndPointerArray)
Span<Vector<int> *> vectors = allocator.construct_elements_and_pointer_array<Vector<int>>(
5, values);
- EXPECT_EQ(vectors.size(), 5);
- EXPECT_EQ(vectors[3]->size(), 7);
+ EXPECT_EQ(vectors.size(), 5u);
+ EXPECT_EQ(vectors[3]->size(), 7u);
EXPECT_EQ((*vectors[2])[5], 6);
for (Vector<int> *vector : vectors) {
@@ -107,8 +107,10 @@ TEST(linear_allocator, ConstructArrayCopy)
MutableSpan<int> span1 = allocator.construct_array_copy(values.as_span());
MutableSpan<int> span2 = allocator.construct_array_copy(values.as_span());
EXPECT_NE(span1.data(), span2.data());
- EXPECT_EQ(span1.size(), 3);
- EXPECT_EQ(span2.size(), 3);
+ EXPECT_EQ(span1.size(), 3u);
+ EXPECT_EQ(span2.size(), 3u);
EXPECT_EQ(span1[1], 2);
EXPECT_EQ(span2[2], 3);
}
+
+} // namespace blender
diff --git a/tests/gtests/blenlib/BLI_map_test.cc b/tests/gtests/blenlib/BLI_map_test.cc
index 96e9879abe7..3b72795d36f 100644
--- a/tests/gtests/blenlib/BLI_map_test.cc
+++ b/tests/gtests/blenlib/BLI_map_test.cc
@@ -6,25 +6,25 @@
#include "BLI_vector.hh"
#include "testing/testing.h"
-using namespace blender;
+namespace blender {
TEST(map, DefaultConstructor)
{
Map<int, float> map;
- EXPECT_EQ(map.size(), 0);
+ EXPECT_EQ(map.size(), 0u);
EXPECT_TRUE(map.is_empty());
}
TEST(map, AddIncreasesSize)
{
Map<int, float> map;
- EXPECT_EQ(map.size(), 0);
+ EXPECT_EQ(map.size(), 0u);
EXPECT_TRUE(map.is_empty());
map.add(2, 5.0f);
- EXPECT_EQ(map.size(), 1);
+ EXPECT_EQ(map.size(), 1u);
EXPECT_FALSE(map.is_empty());
map.add(6, 2.0f);
- EXPECT_EQ(map.size(), 2);
+ EXPECT_EQ(map.size(), 2u);
EXPECT_FALSE(map.is_empty());
}
@@ -87,16 +87,16 @@ TEST(map, PopTry)
Map<int, int> map;
map.add(1, 5);
map.add(2, 7);
- EXPECT_EQ(map.size(), 2);
- Optional<int> value = map.pop_try(4);
- EXPECT_EQ(map.size(), 2);
+ EXPECT_EQ(map.size(), 2u);
+ std::optional<int> value = map.pop_try(4);
+ EXPECT_EQ(map.size(), 2u);
EXPECT_FALSE(value.has_value());
value = map.pop_try(2);
- EXPECT_EQ(map.size(), 1);
+ EXPECT_EQ(map.size(), 1u);
EXPECT_TRUE(value.has_value());
- EXPECT_EQ(value.value(), 7);
+ EXPECT_EQ(*value, 7);
EXPECT_EQ(*map.pop_try(1), 5);
- EXPECT_EQ(map.size(), 0);
+ EXPECT_EQ(map.size(), 0u);
}
TEST(map, PopDefault)
@@ -105,17 +105,17 @@ TEST(map, PopDefault)
map.add(1, 4);
map.add(2, 7);
map.add(3, 8);
- EXPECT_EQ(map.size(), 3);
+ EXPECT_EQ(map.size(), 3u);
EXPECT_EQ(map.pop_default(4, 10), 10);
- EXPECT_EQ(map.size(), 3);
+ EXPECT_EQ(map.size(), 3u);
EXPECT_EQ(map.pop_default(1, 10), 4);
- EXPECT_EQ(map.size(), 2);
+ EXPECT_EQ(map.size(), 2u);
EXPECT_EQ(map.pop_default(2, 20), 7);
- EXPECT_EQ(map.size(), 1);
+ EXPECT_EQ(map.size(), 1u);
EXPECT_EQ(map.pop_default(2, 20), 20);
- EXPECT_EQ(map.size(), 1);
+ EXPECT_EQ(map.size(), 1u);
EXPECT_EQ(map.pop_default(3, 0), 8);
- EXPECT_EQ(map.size(), 0);
+ EXPECT_EQ(map.size(), 0u);
}
TEST(map, PopItemMany)
@@ -147,7 +147,7 @@ TEST(map, ValueIterator)
iterations++;
}
- EXPECT_EQ(iterations, 3);
+ EXPECT_EQ(iterations, 3u);
EXPECT_TRUE(values.contains(5.0f));
EXPECT_TRUE(values.contains(-2.0f));
EXPECT_TRUE(values.contains(2.0f));
@@ -168,7 +168,7 @@ TEST(map, KeyIterator)
iterations++;
}
- EXPECT_EQ(iterations, 3);
+ EXPECT_EQ(iterations, 3u);
EXPECT_TRUE(keys.contains(1));
EXPECT_TRUE(keys.contains(2));
EXPECT_TRUE(keys.contains(6));
@@ -192,7 +192,7 @@ TEST(map, ItemIterator)
iterations++;
}
- EXPECT_EQ(iterations, 3);
+ EXPECT_EQ(iterations, 3u);
EXPECT_TRUE(keys.contains(5));
EXPECT_TRUE(keys.contains(2));
EXPECT_TRUE(keys.contains(1));
@@ -241,8 +241,8 @@ TEST(map, MutableItemToItemConversion)
values.append(item.value);
}
- EXPECT_EQ(keys.size(), 2);
- EXPECT_EQ(values.size(), 2);
+ EXPECT_EQ(keys.size(), 2u);
+ EXPECT_EQ(values.size(), 2u);
EXPECT_TRUE(keys.contains(3));
EXPECT_TRUE(keys.contains(2));
EXPECT_TRUE(values.contains(6));
@@ -330,10 +330,10 @@ TEST(map, MoveConstructorSmall)
map1.add(1, 2.0f);
map1.add(4, 1.0f);
Map<int, float> map2(std::move(map1));
- EXPECT_EQ(map2.size(), 2);
+ EXPECT_EQ(map2.size(), 2u);
EXPECT_EQ(map2.lookup(1), 2.0f);
EXPECT_EQ(map2.lookup(4), 1.0f);
- EXPECT_EQ(map1.size(), 0);
+ EXPECT_EQ(map1.size(), 0u);
EXPECT_EQ(map1.lookup_ptr(4), nullptr);
}
@@ -344,10 +344,10 @@ TEST(map, MoveConstructorLarge)
map1.add_new(i, i);
}
Map<int, int> map2(std::move(map1));
- EXPECT_EQ(map2.size(), 100);
+ EXPECT_EQ(map2.size(), 100u);
EXPECT_EQ(map2.lookup(1), 1);
EXPECT_EQ(map2.lookup(4), 4);
- EXPECT_EQ(map1.size(), 0);
+ EXPECT_EQ(map1.size(), 0u);
EXPECT_EQ(map1.lookup_ptr(4), nullptr);
}
@@ -358,10 +358,10 @@ TEST(map, MoveAssignment)
map1.add(4, 1.0f);
Map<int, float> map2;
map2 = std::move(map1);
- EXPECT_EQ(map2.size(), 2);
+ EXPECT_EQ(map2.size(), 2u);
EXPECT_EQ(map2.lookup(1), 2.0f);
EXPECT_EQ(map2.lookup(4), 1.0f);
- EXPECT_EQ(map1.size(), 0);
+ EXPECT_EQ(map1.size(), 0u);
EXPECT_EQ(map1.lookup_ptr(4), nullptr);
}
@@ -372,10 +372,10 @@ TEST(map, CopyAssignment)
map1.add(4, 1.0f);
Map<int, float> map2;
map2 = map1;
- EXPECT_EQ(map2.size(), 2);
+ EXPECT_EQ(map2.size(), 2u);
EXPECT_EQ(map2.lookup(1), 2.0f);
EXPECT_EQ(map2.lookup(4), 1.0f);
- EXPECT_EQ(map1.size(), 2);
+ EXPECT_EQ(map1.size(), 2u);
EXPECT_EQ(*map1.lookup_ptr(4), 1.0f);
}
@@ -385,13 +385,13 @@ TEST(map, Clear)
map.add(1, 1.0f);
map.add(2, 5.0f);
- EXPECT_EQ(map.size(), 2);
+ EXPECT_EQ(map.size(), 2u);
EXPECT_TRUE(map.contains(1));
EXPECT_TRUE(map.contains(2));
map.clear();
- EXPECT_EQ(map.size(), 0);
+ EXPECT_EQ(map.size(), 0u);
EXPECT_FALSE(map.contains(1));
EXPECT_FALSE(map.contains(2));
}
@@ -423,11 +423,11 @@ TEST(map, Remove)
{
Map<int, int> map;
map.add(2, 4);
- EXPECT_EQ(map.size(), 1);
+ EXPECT_EQ(map.size(), 1u);
EXPECT_FALSE(map.remove(3));
- EXPECT_EQ(map.size(), 1);
+ EXPECT_EQ(map.size(), 1u);
EXPECT_TRUE(map.remove(2));
- EXPECT_EQ(map.size(), 0);
+ EXPECT_EQ(map.size(), 0u);
}
TEST(map, PointerKeys)
@@ -439,7 +439,7 @@ TEST(map, PointerKeys)
EXPECT_FALSE(map.add(&a, 4));
map.add_new(&b, 1);
map.add_new(&c, 1);
- EXPECT_EQ(map.size(), 3);
+ EXPECT_EQ(map.size(), 3u);
EXPECT_TRUE(map.remove(&b));
EXPECT_TRUE(map.add(&b, 8));
EXPECT_FALSE(map.remove(&d));
@@ -458,6 +458,25 @@ TEST(map, ConstKeysAndValues)
EXPECT_FALSE(map.contains("54"));
}
+TEST(map, ForeachItem)
+{
+ Map<int, int> map;
+ map.add(3, 4);
+ map.add(1, 8);
+
+ Vector<int> keys;
+ Vector<int> values;
+ map.foreach_item([&](int key, int value) {
+ keys.append(key);
+ values.append(value);
+ });
+
+ EXPECT_EQ(keys.size(), 2u);
+ EXPECT_EQ(values.size(), 2u);
+ EXPECT_EQ(keys.first_index_of(3), values.first_index_of(4));
+ EXPECT_EQ(keys.first_index_of(1), values.first_index_of(8));
+}
+
/**
* Set this to 1 to activate the benchmark. It is disabled by default, because it prints a lot.
*/
@@ -565,3 +584,5 @@ TEST(map, Benchmark)
*/
#endif /* Benchmark */
+
+} // namespace blender
diff --git a/tests/gtests/blenlib/BLI_math_matrix_test.cc b/tests/gtests/blenlib/BLI_math_matrix_test.cc
new file mode 100644
index 00000000000..9c47c02ceaf
--- /dev/null
+++ b/tests/gtests/blenlib/BLI_math_matrix_test.cc
@@ -0,0 +1,99 @@
+/* Apache License, Version 2.0 */
+
+#include "testing/testing.h"
+
+#include "BLI_math_matrix.h"
+
+TEST(math_matrix, interp_m4_m4m4_regular)
+{
+ /* Test 4x4 matrix interpolation without singularity, i.e. without axis flip. */
+
+ /* Transposed matrix, so that the code here is written in the same way as print_m4() outputs. */
+ /* This matrix represents T=(0.1, 0.2, 0.3), R=(40, 50, 60) degrees, S=(0.7, 0.8, 0.9) */
+ float matrix_a[4][4] = {
+ {0.224976f, -0.333770f, 0.765074f, 0.100000f},
+ {0.389669f, 0.647565f, 0.168130f, 0.200000f},
+ {-0.536231f, 0.330541f, 0.443163f, 0.300000f},
+ {0.000000f, 0.000000f, 0.000000f, 1.000000f},
+ };
+ transpose_m4(matrix_a);
+
+ float matrix_i[4][4];
+ unit_m4(matrix_i);
+
+ float result[4][4];
+ const float epsilon = 1e-6;
+ interp_m4_m4m4(result, matrix_i, matrix_a, 0.0f);
+ EXPECT_M4_NEAR(result, matrix_i, epsilon);
+
+ interp_m4_m4m4(result, matrix_i, matrix_a, 1.0f);
+ EXPECT_M4_NEAR(result, matrix_a, epsilon);
+
+ /* This matrix is based on the current implementation of the code, and isn't guaranteed to be
+ * correct. It's just consistent with the current implementation. */
+ float matrix_halfway[4][4] = {
+ {0.690643f, -0.253244f, 0.484996f, 0.050000f},
+ {0.271924f, 0.852623f, 0.012348f, 0.100000f},
+ {-0.414209f, 0.137484f, 0.816778f, 0.150000f},
+ {0.000000f, 0.000000f, 0.000000f, 1.000000f},
+ };
+
+ transpose_m4(matrix_halfway);
+ interp_m4_m4m4(result, matrix_i, matrix_a, 0.5f);
+ EXPECT_M4_NEAR(result, matrix_halfway, epsilon);
+}
+
+TEST(math_matrix, interp_m3_m3m3_singularity)
+{
+ /* A singluarity means that there is an axis mirror in the rotation component of the matrix. This
+ * is reflected in its negative determinant.
+ *
+ * The interpolation of 4x4 matrices performs linear interpolation on the translation component,
+ * and then uses the 3x3 interpolation function to handle rotation and scale. As a result, this
+ * test for a singularity in the rotation matrix only needs to test the 3x3 case. */
+
+ /* Transposed matrix, so that the code here is written in the same way as print_m4() outputs. */
+ /* This matrix represents R=(4, 5, 6) degrees, S=(-1, 1, 1) */
+ float matrix_a[3][3] = {
+ {-0.990737f, -0.098227f, 0.093759f},
+ {-0.104131f, 0.992735f, -0.060286f},
+ {0.087156f, 0.069491f, 0.993768f},
+ };
+ transpose_m3(matrix_a);
+ EXPECT_NEAR(-1.0f, determinant_m3_array(matrix_a), 1e-6);
+
+ /* This matrix represents R=(0, 0, 0), S=(-1, 0, 0) */
+ float matrix_b[3][3] = {
+ {-1.0f, 0.0f, 0.0f},
+ {0.0f, 1.0f, 0.0f},
+ {0.0f, 0.0f, 1.0f},
+ };
+ transpose_m3(matrix_b);
+
+ float result[3][3];
+ interp_m3_m3m3(result, matrix_a, matrix_b, 0.0f);
+ EXPECT_M3_NEAR(result, matrix_a, 1e-5);
+
+ interp_m3_m3m3(result, matrix_a, matrix_b, 1.0f);
+ EXPECT_M3_NEAR(result, matrix_b, 1e-5);
+
+ interp_m3_m3m3(result, matrix_a, matrix_b, 0.5f);
+ float expect[3][3] = {
+ {-0.997681f, -0.049995f, 0.046186f},
+ {-0.051473f, 0.998181f, -0.031385f},
+ {0.044533f, 0.033689f, 0.998440f},
+ };
+ transpose_m3(expect);
+ EXPECT_M3_NEAR(result, expect, 1e-5);
+
+ /* Interpolating between a matrix with and without axis flip can cause it to go through a zero
+ * point. The determinant det(A) of a matrix represents the change in volume; interpolating
+ * between matrices with det(A)=-1 and det(B)=1 will have to go through a point where
+ * det(result)=0, so where the volume becomes zero. */
+ float matrix_i[3][3];
+ unit_m3(matrix_i);
+ zero_m3(expect);
+ interp_m3_m3m3(result, matrix_a, matrix_i, 0.5f);
+ EXPECT_NEAR(0.0f, determinant_m3_array(result), 1e-5);
+ EXPECT_M3_NEAR(result, expect, 1e-5);
+}
diff --git a/tests/gtests/blenlib/BLI_optional_test.cc b/tests/gtests/blenlib/BLI_optional_test.cc
deleted file mode 100644
index 2fc2188563e..00000000000
--- a/tests/gtests/blenlib/BLI_optional_test.cc
+++ /dev/null
@@ -1,61 +0,0 @@
-#include "BLI_optional.hh"
-#include "BLI_strict_flags.h"
-#include "testing/testing.h"
-#include <string>
-
-using namespace blender;
-
-TEST(optional, DefaultConstructor)
-{
- Optional<int> a;
- EXPECT_FALSE(a.has_value());
-}
-
-TEST(optional, ValueConstructor)
-{
- Optional<int> a(5);
- EXPECT_TRUE(a.has_value());
- EXPECT_EQ(a.value(), 5);
-}
-
-TEST(optional, CopyConstructor)
-{
- Optional<std::string> a("Hello");
- Optional<std::string> b = a;
- EXPECT_TRUE(a.has_value());
- EXPECT_TRUE(b.has_value());
- b.value()[0] = 'T';
- EXPECT_EQ(a.value(), "Hello");
- EXPECT_EQ(b.value(), "Tello");
-}
-
-TEST(optional, Reset)
-{
- Optional<int> a(4);
- EXPECT_TRUE(a.has_value());
- a.reset();
- EXPECT_FALSE(a.has_value());
-}
-
-TEST(optional, Extract)
-{
- Optional<int> a(32);
- EXPECT_TRUE(a.has_value());
- EXPECT_EQ(a.extract(), 32);
- EXPECT_FALSE(a.has_value());
-}
-
-TEST(optional, ArrowOperator)
-{
- Optional<std::string> value = std::string("Hello");
- EXPECT_TRUE(value.has_value());
- EXPECT_EQ(value->size(), 5);
-}
-
-TEST(optional, StarOperator)
-{
- Optional<std::string> value = std::string("Hello");
- EXPECT_TRUE(value.has_value());
- std::string &s = *value;
- EXPECT_EQ(s.size(), 5);
-}
diff --git a/tests/gtests/blenlib/BLI_set_test.cc b/tests/gtests/blenlib/BLI_set_test.cc
index 2387242c94b..ac78eb786df 100644
--- a/tests/gtests/blenlib/BLI_set_test.cc
+++ b/tests/gtests/blenlib/BLI_set_test.cc
@@ -14,7 +14,7 @@ namespace blender {
TEST(set, DefaultConstructor)
{
Set<int> set;
- EXPECT_EQ(set.size(), 0);
+ EXPECT_EQ(set.size(), 0u);
EXPECT_TRUE(set.is_empty());
}
@@ -52,7 +52,7 @@ TEST(set, AddMany)
TEST(set, InitializerListConstructor)
{
Set<int> set = {4, 5, 6};
- EXPECT_EQ(set.size(), 3);
+ EXPECT_EQ(set.size(), 3u);
EXPECT_TRUE(set.contains(4));
EXPECT_TRUE(set.contains(5));
EXPECT_TRUE(set.contains(6));
@@ -77,10 +77,10 @@ TEST(set, CopyConstructor)
TEST(set, MoveConstructor)
{
Set<int> set = {1, 2, 3};
- EXPECT_EQ(set.size(), 3);
+ EXPECT_EQ(set.size(), 3u);
Set<int> set2(std::move(set));
- EXPECT_EQ(set.size(), 0);
- EXPECT_EQ(set2.size(), 3);
+ EXPECT_EQ(set.size(), 0u);
+ EXPECT_EQ(set2.size(), 3u);
}
TEST(set, CopyAssignment)
@@ -101,11 +101,11 @@ TEST(set, CopyAssignment)
TEST(set, MoveAssignment)
{
Set<int> set = {1, 2, 3};
- EXPECT_EQ(set.size(), 3);
+ EXPECT_EQ(set.size(), 3u);
Set<int> set2;
set2 = std::move(set);
- EXPECT_EQ(set.size(), 0);
- EXPECT_EQ(set2.size(), 3);
+ EXPECT_EQ(set.size(), 0u);
+ EXPECT_EQ(set2.size(), 3u);
}
TEST(set, RemoveContained)
@@ -177,7 +177,7 @@ TEST(set, AddMultiple)
a.add_multiple({2, 4, 7});
EXPECT_TRUE(a.contains(4));
EXPECT_TRUE(a.contains(2));
- EXPECT_EQ(a.size(), 4);
+ EXPECT_EQ(a.size(), 4u);
}
TEST(set, AddMultipleNew)
@@ -195,7 +195,7 @@ TEST(set, Iterator)
for (int value : set) {
vec.append(value);
}
- EXPECT_EQ(vec.size(), 5);
+ EXPECT_EQ(vec.size(), 5u);
EXPECT_TRUE(vec.contains(1));
EXPECT_TRUE(vec.contains(3));
EXPECT_TRUE(vec.contains(2));
@@ -208,9 +208,9 @@ TEST(set, OftenAddRemoveContained)
Set<int> set;
for (int i = 0; i < 100; i++) {
set.add(42);
- EXPECT_EQ(set.size(), 1);
+ EXPECT_EQ(set.size(), 1u);
set.remove_contained(42);
- EXPECT_EQ(set.size(), 0);
+ EXPECT_EQ(set.size(), 0u);
}
}
@@ -222,15 +222,15 @@ TEST(set, UniquePtrValues)
set.add_new(std::move(value1));
set.add(std::unique_ptr<int>(new int()));
- EXPECT_EQ(set.size(), 3);
+ EXPECT_EQ(set.size(), 3u);
}
TEST(set, Clear)
{
Set<int> set = {3, 4, 6, 7};
- EXPECT_EQ(set.size(), 4);
+ EXPECT_EQ(set.size(), 4u);
set.clear();
- EXPECT_EQ(set.size(), 0);
+ EXPECT_EQ(set.size(), 0u);
}
TEST(set, StringSet)
@@ -238,7 +238,7 @@ TEST(set, StringSet)
Set<std::string> set;
set.add("hello");
set.add("world");
- EXPECT_EQ(set.size(), 2);
+ EXPECT_EQ(set.size(), 2u);
EXPECT_TRUE(set.contains("hello"));
EXPECT_TRUE(set.contains("world"));
EXPECT_FALSE(set.contains("world2"));
@@ -250,7 +250,7 @@ TEST(set, PointerSet)
Set<int *> set;
set.add(&a);
set.add(&b);
- EXPECT_EQ(set.size(), 2);
+ EXPECT_EQ(set.size(), 2u);
EXPECT_TRUE(set.contains(&a));
EXPECT_TRUE(set.contains(&b));
EXPECT_FALSE(set.contains(&c));
@@ -259,14 +259,14 @@ TEST(set, PointerSet)
TEST(set, Remove)
{
Set<int> set = {1, 2, 3, 4, 5, 6};
- EXPECT_EQ(set.size(), 6);
+ EXPECT_EQ(set.size(), 6u);
EXPECT_TRUE(set.remove(2));
- EXPECT_EQ(set.size(), 5);
+ EXPECT_EQ(set.size(), 5u);
EXPECT_FALSE(set.contains(2));
EXPECT_FALSE(set.remove(2));
- EXPECT_EQ(set.size(), 5);
+ EXPECT_EQ(set.size(), 5u);
EXPECT_TRUE(set.remove(5));
- EXPECT_EQ(set.size(), 4);
+ EXPECT_EQ(set.size(), 4u);
}
struct Type1 {
diff --git a/tests/gtests/blenlib/BLI_span_test.cc b/tests/gtests/blenlib/BLI_span_test.cc
index b122e8efdb4..375cb694b7b 100644
--- a/tests/gtests/blenlib/BLI_span_test.cc
+++ b/tests/gtests/blenlib/BLI_span_test.cc
@@ -3,13 +3,13 @@
#include "BLI_vector.hh"
#include "testing/testing.h"
-using namespace blender;
+namespace blender {
TEST(span, FromSmallVector)
{
Vector<int> a = {1, 2, 3};
Span<int> a_span = a;
- EXPECT_EQ(a_span.size(), 3);
+ EXPECT_EQ(a_span.size(), 3u);
EXPECT_EQ(a_span[0], 1);
EXPECT_EQ(a_span[1], 2);
EXPECT_EQ(a_span[2], 3);
@@ -21,14 +21,14 @@ TEST(span, AddConstToPointer)
std::vector<int *> vec = {&a};
Span<int *> span = vec;
Span<const int *> const_span = span;
- EXPECT_EQ(const_span.size(), 1);
+ EXPECT_EQ(const_span.size(), 1u);
}
TEST(span, IsReferencing)
{
int array[] = {3, 5, 8};
MutableSpan<int> span(array, ARRAY_SIZE(array));
- EXPECT_EQ(span.size(), 3);
+ EXPECT_EQ(span.size(), 3u);
EXPECT_EQ(span[1], 5);
array[1] = 10;
EXPECT_EQ(span[1], 10);
@@ -38,7 +38,7 @@ TEST(span, DropBack)
{
Vector<int> a = {4, 5, 6, 7};
auto slice = Span<int>(a).drop_back(2);
- EXPECT_EQ(slice.size(), 2);
+ EXPECT_EQ(slice.size(), 2u);
EXPECT_EQ(slice[0], 4);
EXPECT_EQ(slice[1], 5);
}
@@ -47,14 +47,14 @@ TEST(span, DropBackAll)
{
Vector<int> a = {4, 5, 6, 7};
auto slice = Span<int>(a).drop_back(a.size());
- EXPECT_EQ(slice.size(), 0);
+ EXPECT_EQ(slice.size(), 0u);
}
TEST(span, DropFront)
{
Vector<int> a = {4, 5, 6, 7};
auto slice = Span<int>(a).drop_front(1);
- EXPECT_EQ(slice.size(), 3);
+ EXPECT_EQ(slice.size(), 3u);
EXPECT_EQ(slice[0], 5);
EXPECT_EQ(slice[1], 6);
EXPECT_EQ(slice[2], 7);
@@ -64,14 +64,14 @@ TEST(span, DropFrontAll)
{
Vector<int> a = {4, 5, 6, 7};
auto slice = Span<int>(a).drop_front(a.size());
- EXPECT_EQ(slice.size(), 0);
+ EXPECT_EQ(slice.size(), 0u);
}
TEST(span, TakeFront)
{
Vector<int> a = {4, 5, 6, 7};
auto slice = Span<int>(a).take_front(2);
- EXPECT_EQ(slice.size(), 2);
+ EXPECT_EQ(slice.size(), 2u);
EXPECT_EQ(slice[0], 4);
EXPECT_EQ(slice[1], 5);
}
@@ -80,7 +80,7 @@ TEST(span, TakeBack)
{
Vector<int> a = {5, 6, 7, 8};
auto slice = Span<int>(a).take_back(2);
- EXPECT_EQ(slice.size(), 2);
+ EXPECT_EQ(slice.size(), 2u);
EXPECT_EQ(slice[0], 7);
EXPECT_EQ(slice[1], 8);
}
@@ -89,7 +89,7 @@ TEST(span, Slice)
{
Vector<int> a = {4, 5, 6, 7};
auto slice = Span<int>(a).slice(1, 2);
- EXPECT_EQ(slice.size(), 2);
+ EXPECT_EQ(slice.size(), 2u);
EXPECT_EQ(slice[0], 5);
EXPECT_EQ(slice[1], 6);
}
@@ -98,14 +98,14 @@ TEST(span, SliceEmpty)
{
Vector<int> a = {4, 5, 6, 7};
auto slice = Span<int>(a).slice(2, 0);
- EXPECT_EQ(slice.size(), 0);
+ EXPECT_EQ(slice.size(), 0u);
}
TEST(span, SliceRange)
{
Vector<int> a = {1, 2, 3, 4, 5};
auto slice = Span<int>(a).slice(IndexRange(2, 2));
- EXPECT_EQ(slice.size(), 2);
+ EXPECT_EQ(slice.size(), 2u);
EXPECT_EQ(slice[0], 3);
EXPECT_EQ(slice[1], 4);
}
@@ -126,16 +126,16 @@ TEST(span, Count)
{
Vector<int> a = {2, 3, 4, 3, 3, 2, 2, 2, 2};
Span<int> a_span = a;
- EXPECT_EQ(a_span.count(1), 0);
- EXPECT_EQ(a_span.count(2), 5);
- EXPECT_EQ(a_span.count(3), 3);
- EXPECT_EQ(a_span.count(4), 1);
- EXPECT_EQ(a_span.count(5), 0);
+ EXPECT_EQ(a_span.count(1), 0u);
+ EXPECT_EQ(a_span.count(2), 5u);
+ EXPECT_EQ(a_span.count(3), 3u);
+ EXPECT_EQ(a_span.count(4), 1u);
+ EXPECT_EQ(a_span.count(5), 0u);
}
static void test_ref_from_initializer_list(Span<int> span)
{
- EXPECT_EQ(span.size(), 4);
+ EXPECT_EQ(span.size(), 4u);
EXPECT_EQ(span[0], 3);
EXPECT_EQ(span[1], 6);
EXPECT_EQ(span[2], 8);
@@ -151,7 +151,7 @@ TEST(span, FromVector)
{
std::vector<int> a = {1, 2, 3, 4};
Span<int> a_span(a);
- EXPECT_EQ(a_span.size(), 4);
+ EXPECT_EQ(a_span.size(), 4u);
EXPECT_EQ(a_span[0], 1);
EXPECT_EQ(a_span[1], 2);
EXPECT_EQ(a_span[2], 3);
@@ -162,7 +162,7 @@ TEST(span, FromArray)
{
std::array<int, 2> a = {5, 6};
Span<int> a_span(a);
- EXPECT_EQ(a_span.size(), 2);
+ EXPECT_EQ(a_span.size(), 2u);
EXPECT_EQ(a_span[0], 5);
EXPECT_EQ(a_span[1], 6);
}
@@ -196,7 +196,7 @@ TEST(span, SizeInBytes)
std::array<int, 10> a;
Span<int> a_span(a);
EXPECT_EQ(a_span.size_in_bytes(), sizeof(a));
- EXPECT_EQ(a_span.size_in_bytes(), 40);
+ EXPECT_EQ(a_span.size_in_bytes(), 40u);
}
TEST(span, FirstLast)
@@ -244,9 +244,9 @@ TEST(span, FirstIndex)
std::array<int, 5> a = {4, 5, 4, 2, 5};
Span<int> a_span(a);
- EXPECT_EQ(a_span.first_index(4), 0);
- EXPECT_EQ(a_span.first_index(5), 1);
- EXPECT_EQ(a_span.first_index(2), 3);
+ EXPECT_EQ(a_span.first_index(4), 0u);
+ EXPECT_EQ(a_span.first_index(5), 1u);
+ EXPECT_EQ(a_span.first_index(2), 3u);
}
TEST(span, CastSameSize)
@@ -256,8 +256,8 @@ TEST(span, CastSameSize)
Span<int *> a_span = a;
Span<float *> new_a_span = a_span.cast<float *>();
- EXPECT_EQ(a_span.size(), 4);
- EXPECT_EQ(new_a_span.size(), 4);
+ EXPECT_EQ(a_span.size(), 4u);
+ EXPECT_EQ(new_a_span.size(), 4u);
EXPECT_EQ(a_span[0], &value);
EXPECT_EQ(new_a_span[0], (float *)&value);
@@ -269,8 +269,8 @@ TEST(span, CastSmallerSize)
Span<uint32_t> a_span = a;
Span<uint16_t> new_a_span = a_span.cast<uint16_t>();
- EXPECT_EQ(a_span.size(), 4);
- EXPECT_EQ(new_a_span.size(), 8);
+ EXPECT_EQ(a_span.size(), 4u);
+ EXPECT_EQ(new_a_span.size(), 8u);
}
TEST(span, CastLargerSize)
@@ -279,6 +279,8 @@ TEST(span, CastLargerSize)
Span<uint16_t> a_span = a;
Span<uint32_t> new_a_span = a_span.cast<uint32_t>();
- EXPECT_EQ(a_span.size(), 4);
- EXPECT_EQ(new_a_span.size(), 2);
+ EXPECT_EQ(a_span.size(), 4u);
+ EXPECT_EQ(new_a_span.size(), 2u);
}
+
+} // namespace blender
diff --git a/tests/gtests/blenlib/BLI_stack_cxx_test.cc b/tests/gtests/blenlib/BLI_stack_cxx_test.cc
index 2fb4c11ca83..b59ac1f7ec1 100644
--- a/tests/gtests/blenlib/BLI_stack_cxx_test.cc
+++ b/tests/gtests/blenlib/BLI_stack_cxx_test.cc
@@ -3,12 +3,12 @@
#include "BLI_vector.hh"
#include "testing/testing.h"
-using namespace blender;
+namespace blender {
TEST(stack, DefaultConstructor)
{
Stack<int> stack;
- EXPECT_EQ(stack.size(), 0);
+ EXPECT_EQ(stack.size(), 0u);
EXPECT_TRUE(stack.is_empty());
}
@@ -16,7 +16,7 @@ TEST(stack, SpanConstructor)
{
std::array<int, 3> array = {4, 7, 2};
Stack<int> stack(array);
- EXPECT_EQ(stack.size(), 3);
+ EXPECT_EQ(stack.size(), 3u);
EXPECT_EQ(stack.pop(), 2);
EXPECT_EQ(stack.pop(), 7);
EXPECT_EQ(stack.pop(), 4);
@@ -27,8 +27,8 @@ TEST(stack, CopyConstructor)
{
Stack<int> stack1 = {1, 2, 3, 4, 5, 6, 7};
Stack<int> stack2 = stack1;
- EXPECT_EQ(stack1.size(), 7);
- EXPECT_EQ(stack2.size(), 7);
+ EXPECT_EQ(stack1.size(), 7u);
+ EXPECT_EQ(stack2.size(), 7u);
for (int i = 7; i >= 1; i--) {
EXPECT_FALSE(stack1.is_empty());
EXPECT_FALSE(stack2.is_empty());
@@ -43,8 +43,8 @@ TEST(stack, MoveConstructor)
{
Stack<int> stack1 = {1, 2, 3, 4, 5, 6, 7};
Stack<int> stack2 = std::move(stack1);
- EXPECT_EQ(stack1.size(), 0);
- EXPECT_EQ(stack2.size(), 7);
+ EXPECT_EQ(stack1.size(), 0u);
+ EXPECT_EQ(stack2.size(), 7u);
for (int i = 7; i >= 1; i--) {
EXPECT_EQ(stack2.pop(), i);
}
@@ -56,8 +56,8 @@ TEST(stack, CopyAssignment)
Stack<int> stack2 = {2, 3, 4, 5, 6, 7};
stack2 = stack1;
- EXPECT_EQ(stack1.size(), 7);
- EXPECT_EQ(stack2.size(), 7);
+ EXPECT_EQ(stack1.size(), 7u);
+ EXPECT_EQ(stack2.size(), 7u);
for (int i = 7; i >= 1; i--) {
EXPECT_FALSE(stack1.is_empty());
EXPECT_FALSE(stack2.is_empty());
@@ -73,8 +73,8 @@ TEST(stack, MoveAssignment)
Stack<int> stack1 = {1, 2, 3, 4, 5, 6, 7};
Stack<int> stack2 = {5, 3, 7, 2, 2};
stack2 = std::move(stack1);
- EXPECT_EQ(stack1.size(), 0);
- EXPECT_EQ(stack2.size(), 7);
+ EXPECT_EQ(stack1.size(), 0u);
+ EXPECT_EQ(stack2.size(), 7u);
for (int i = 7; i >= 1; i--) {
EXPECT_EQ(stack2.pop(), i);
}
@@ -83,19 +83,19 @@ TEST(stack, MoveAssignment)
TEST(stack, Push)
{
Stack<int> stack;
- EXPECT_EQ(stack.size(), 0);
+ EXPECT_EQ(stack.size(), 0u);
stack.push(3);
- EXPECT_EQ(stack.size(), 1);
+ EXPECT_EQ(stack.size(), 1u);
stack.push(5);
- EXPECT_EQ(stack.size(), 2);
+ EXPECT_EQ(stack.size(), 2u);
}
TEST(stack, PushMultiple)
{
Stack<int> stack;
- EXPECT_EQ(stack.size(), 0);
+ EXPECT_EQ(stack.size(), 0u);
stack.push_multiple({1, 2, 3});
- EXPECT_EQ(stack.size(), 3);
+ EXPECT_EQ(stack.size(), 3u);
EXPECT_EQ(stack.pop(), 3);
EXPECT_EQ(stack.pop(), 2);
EXPECT_EQ(stack.pop(), 1);
@@ -106,19 +106,19 @@ TEST(stack, PushPopMany)
Stack<int> stack;
for (int i = 0; i < 1000; i++) {
stack.push(i);
- EXPECT_EQ(stack.size(), i + 1);
+ EXPECT_EQ(stack.size(), static_cast<unsigned int>(i + 1));
}
for (int i = 999; i > 50; i--) {
EXPECT_EQ(stack.pop(), i);
- EXPECT_EQ(stack.size(), i);
+ EXPECT_EQ(stack.size(), static_cast<unsigned int>(i));
}
for (int i = 51; i < 5000; i++) {
stack.push(i);
- EXPECT_EQ(stack.size(), i + 1);
+ EXPECT_EQ(stack.size(), static_cast<unsigned int>(i + 1));
}
for (int i = 4999; i >= 0; i--) {
EXPECT_EQ(stack.pop(), i);
- EXPECT_EQ(stack.size(), i);
+ EXPECT_EQ(stack.size(), static_cast<unsigned int>(i));
}
}
@@ -137,7 +137,7 @@ TEST(stack, PushMultipleAfterPop)
values.append(i);
}
stack.push_multiple(values);
- EXPECT_EQ(stack.size(), 5000);
+ EXPECT_EQ(stack.size(), 5000u);
for (int i = 4999; i >= 0; i--) {
EXPECT_EQ(stack.pop(), i);
@@ -182,3 +182,5 @@ TEST(stack, OveralignedValues)
EXPECT_EQ((uintptr_t)&stack.peek() % 512, 0);
}
}
+
+} // namespace blender
diff --git a/tests/gtests/blenlib/BLI_string_ref_test.cc b/tests/gtests/blenlib/BLI_string_ref_test.cc
index 5bb0c396288..1f84b8577b4 100644
--- a/tests/gtests/blenlib/BLI_string_ref_test.cc
+++ b/tests/gtests/blenlib/BLI_string_ref_test.cc
@@ -3,14 +3,12 @@
#include "BLI_vector.hh"
#include "testing/testing.h"
-using blender::StringRef;
-using blender::StringRefNull;
-using blender::Vector;
+namespace blender {
TEST(string_ref_null, DefaultConstructor)
{
StringRefNull ref;
- EXPECT_EQ(ref.size(), 0);
+ EXPECT_EQ(ref.size(), 0u);
EXPECT_EQ(ref[0], '\0');
}
@@ -18,7 +16,7 @@ TEST(string_ref_null, CStringConstructor)
{
const char *str = "Hello";
StringRefNull ref(str);
- EXPECT_EQ(ref.size(), 5);
+ EXPECT_EQ(ref.size(), 5u);
EXPECT_EQ(ref.data(), str);
}
@@ -26,21 +24,21 @@ TEST(string_ref_null, CStringLengthConstructor)
{
const char *str = "Hello";
StringRefNull ref(str, 5);
- EXPECT_EQ(ref.size(), 5);
+ EXPECT_EQ(ref.size(), 5u);
EXPECT_EQ(ref.data(), str);
}
TEST(string_ref, DefaultConstructor)
{
StringRef ref;
- EXPECT_EQ(ref.size(), 0);
+ EXPECT_EQ(ref.size(), 0u);
}
TEST(string_ref, StartEndConstructor)
{
const char *text = "hello world";
StringRef ref(text, text + 5);
- EXPECT_EQ(ref.size(), 5);
+ EXPECT_EQ(ref.size(), 5u);
EXPECT_TRUE(ref == "hello");
EXPECT_FALSE(ref == "hello ");
}
@@ -48,7 +46,7 @@ TEST(string_ref, StartEndConstructor)
TEST(string_ref, StartEndConstructorNullptr)
{
StringRef ref(nullptr, nullptr);
- EXPECT_EQ(ref.size(), 0);
+ EXPECT_EQ(ref.size(), 0u);
EXPECT_TRUE(ref == "");
}
@@ -56,7 +54,7 @@ TEST(string_ref, StartEndConstructorSame)
{
const char *text = "hello world";
StringRef ref(text, text);
- EXPECT_EQ(ref.size(), 0);
+ EXPECT_EQ(ref.size(), 0u);
EXPECT_TRUE(ref == "");
}
@@ -64,7 +62,7 @@ TEST(string_ref, CStringConstructor)
{
const char *str = "Test";
StringRef ref(str);
- EXPECT_EQ(ref.size(), 4);
+ EXPECT_EQ(ref.size(), 4u);
EXPECT_EQ(ref.data(), str);
}
@@ -72,7 +70,7 @@ TEST(string_ref, PointerWithLengthConstructor)
{
const char *str = "Test";
StringRef ref(str, 2);
- EXPECT_EQ(ref.size(), 2);
+ EXPECT_EQ(ref.size(), 2u);
EXPECT_EQ(ref.data(), str);
}
@@ -80,14 +78,14 @@ TEST(string_ref, StdStringConstructor)
{
std::string str = "Test";
StringRef ref(str);
- EXPECT_EQ(ref.size(), 4);
+ EXPECT_EQ(ref.size(), 4u);
EXPECT_EQ(ref.data(), str.data());
}
TEST(string_ref, SubscriptOperator)
{
StringRef ref("hello");
- EXPECT_EQ(ref.size(), 5);
+ EXPECT_EQ(ref.size(), 5u);
EXPECT_EQ(ref[0], 'h');
EXPECT_EQ(ref[1], 'e');
EXPECT_EQ(ref[2], 'l');
@@ -99,7 +97,7 @@ TEST(string_ref, ToStdString)
{
StringRef ref("test");
std::string str = ref;
- EXPECT_EQ(str.size(), 4);
+ EXPECT_EQ(str.size(), 4u);
EXPECT_EQ(str, "test");
}
@@ -204,7 +202,7 @@ TEST(string_ref, Iterate)
for (char c : ref) {
chars.append(c);
}
- EXPECT_EQ(chars.size(), 4);
+ EXPECT_EQ(chars.size(), 4u);
EXPECT_EQ(chars[0], 't');
EXPECT_EQ(chars[1], 'e');
EXPECT_EQ(chars[2], 's');
@@ -240,8 +238,8 @@ TEST(string_ref, DropPrefixN)
StringRef ref("test");
StringRef ref2 = ref.drop_prefix(2);
StringRef ref3 = ref2.drop_prefix(2);
- EXPECT_EQ(ref2.size(), 2);
- EXPECT_EQ(ref3.size(), 0);
+ EXPECT_EQ(ref2.size(), 2u);
+ EXPECT_EQ(ref3.size(), 0u);
EXPECT_EQ(ref2, "st");
EXPECT_EQ(ref3, "");
}
@@ -250,7 +248,7 @@ TEST(string_ref, DropPrefix)
{
StringRef ref("test");
StringRef ref2 = ref.drop_prefix("tes");
- EXPECT_EQ(ref2.size(), 1);
+ EXPECT_EQ(ref2.size(), 1u);
EXPECT_EQ(ref2, "t");
}
@@ -273,3 +271,5 @@ TEST(string_ref, Copy)
EXPECT_EQ(dst[6], 0xFF);
EXPECT_EQ(ref, dst);
}
+
+} // namespace blender
diff --git a/tests/gtests/blenlib/BLI_vector_set_test.cc b/tests/gtests/blenlib/BLI_vector_set_test.cc
index 1f8a830d134..982081f4b4c 100644
--- a/tests/gtests/blenlib/BLI_vector_set_test.cc
+++ b/tests/gtests/blenlib/BLI_vector_set_test.cc
@@ -2,19 +2,19 @@
#include "BLI_vector_set.hh"
#include "testing/testing.h"
-using blender::VectorSet;
+namespace blender {
TEST(vector_set, DefaultConstructor)
{
VectorSet<int> set;
- EXPECT_EQ(set.size(), 0);
+ EXPECT_EQ(set.size(), 0u);
EXPECT_TRUE(set.is_empty());
}
TEST(vector_set, InitializerListConstructor_WithoutDuplicates)
{
VectorSet<int> set = {1, 4, 5};
- EXPECT_EQ(set.size(), 3);
+ EXPECT_EQ(set.size(), 3u);
EXPECT_EQ(set[0], 1);
EXPECT_EQ(set[1], 4);
EXPECT_EQ(set[2], 5);
@@ -23,7 +23,7 @@ TEST(vector_set, InitializerListConstructor_WithoutDuplicates)
TEST(vector_set, InitializerListConstructor_WithDuplicates)
{
VectorSet<int> set = {1, 3, 3, 2, 1, 5};
- EXPECT_EQ(set.size(), 4);
+ EXPECT_EQ(set.size(), 4u);
EXPECT_EQ(set[0], 1);
EXPECT_EQ(set[1], 3);
EXPECT_EQ(set[2], 2);
@@ -34,10 +34,10 @@ TEST(vector_set, Copy)
{
VectorSet<int> set1 = {1, 2, 3};
VectorSet<int> set2 = set1;
- EXPECT_EQ(set1.size(), 3);
- EXPECT_EQ(set2.size(), 3);
- EXPECT_EQ(set1.index_of(2), 1);
- EXPECT_EQ(set2.index_of(2), 1);
+ EXPECT_EQ(set1.size(), 3u);
+ EXPECT_EQ(set2.size(), 3u);
+ EXPECT_EQ(set1.index_of(2), 1u);
+ EXPECT_EQ(set2.index_of(2), 1u);
}
TEST(vector_set, CopyAssignment)
@@ -45,18 +45,18 @@ TEST(vector_set, CopyAssignment)
VectorSet<int> set1 = {1, 2, 3};
VectorSet<int> set2 = {};
set2 = set1;
- EXPECT_EQ(set1.size(), 3);
- EXPECT_EQ(set2.size(), 3);
- EXPECT_EQ(set1.index_of(2), 1);
- EXPECT_EQ(set2.index_of(2), 1);
+ EXPECT_EQ(set1.size(), 3u);
+ EXPECT_EQ(set2.size(), 3u);
+ EXPECT_EQ(set1.index_of(2), 1u);
+ EXPECT_EQ(set2.index_of(2), 1u);
}
TEST(vector_set, Move)
{
VectorSet<int> set1 = {1, 2, 3};
VectorSet<int> set2 = std::move(set1);
- EXPECT_EQ(set1.size(), 0);
- EXPECT_EQ(set2.size(), 3);
+ EXPECT_EQ(set1.size(), 0u);
+ EXPECT_EQ(set2.size(), 3u);
}
TEST(vector_set, MoveAssignment)
@@ -64,36 +64,36 @@ TEST(vector_set, MoveAssignment)
VectorSet<int> set1 = {1, 2, 3};
VectorSet<int> set2 = {};
set2 = std::move(set1);
- EXPECT_EQ(set1.size(), 0);
- EXPECT_EQ(set2.size(), 3);
+ EXPECT_EQ(set1.size(), 0u);
+ EXPECT_EQ(set2.size(), 3u);
}
TEST(vector_set, AddNewIncreasesSize)
{
VectorSet<int> set;
EXPECT_TRUE(set.is_empty());
- EXPECT_EQ(set.size(), 0);
+ EXPECT_EQ(set.size(), 0u);
set.add(5);
EXPECT_FALSE(set.is_empty());
- EXPECT_EQ(set.size(), 1);
+ EXPECT_EQ(set.size(), 1u);
}
TEST(vector_set, AddExistingDoesNotIncreaseSize)
{
VectorSet<int> set;
- EXPECT_EQ(set.size(), 0);
+ EXPECT_EQ(set.size(), 0u);
EXPECT_TRUE(set.add(5));
- EXPECT_EQ(set.size(), 1);
+ EXPECT_EQ(set.size(), 1u);
EXPECT_FALSE(set.add(5));
- EXPECT_EQ(set.size(), 1);
+ EXPECT_EQ(set.size(), 1u);
}
TEST(vector_set, Index)
{
VectorSet<int> set = {3, 6, 4};
- EXPECT_EQ(set.index_of(6), 1);
- EXPECT_EQ(set.index_of(3), 0);
- EXPECT_EQ(set.index_of(4), 2);
+ EXPECT_EQ(set.index_of(6), 1u);
+ EXPECT_EQ(set.index_of(3), 0u);
+ EXPECT_EQ(set.index_of(4), 2u);
}
TEST(vector_set, IndexTry)
@@ -108,21 +108,21 @@ TEST(vector_set, IndexTry)
TEST(vector_set, RemoveContained)
{
VectorSet<int> set = {4, 5, 6, 7};
- EXPECT_EQ(set.size(), 4);
+ EXPECT_EQ(set.size(), 4u);
set.remove_contained(5);
- EXPECT_EQ(set.size(), 3);
+ EXPECT_EQ(set.size(), 3u);
EXPECT_EQ(set[0], 4);
EXPECT_EQ(set[1], 7);
EXPECT_EQ(set[2], 6);
set.remove_contained(6);
- EXPECT_EQ(set.size(), 2);
+ EXPECT_EQ(set.size(), 2u);
EXPECT_EQ(set[0], 4);
EXPECT_EQ(set[1], 7);
set.remove_contained(4);
- EXPECT_EQ(set.size(), 1);
+ EXPECT_EQ(set.size(), 1u);
EXPECT_EQ(set[0], 7);
set.remove_contained(7);
- EXPECT_EQ(set.size(), 0);
+ EXPECT_EQ(set.size(), 0u);
}
TEST(vector_set, AddMultipleTimes)
@@ -158,3 +158,5 @@ TEST(vector_set, Remove)
EXPECT_FALSE(set.remove(5));
EXPECT_FALSE(set.contains(5));
}
+
+} // namespace blender
diff --git a/tests/gtests/blenlib/BLI_vector_test.cc b/tests/gtests/blenlib/BLI_vector_test.cc
index 4a04d002580..ea4711ca015 100644
--- a/tests/gtests/blenlib/BLI_vector_test.cc
+++ b/tests/gtests/blenlib/BLI_vector_test.cc
@@ -3,18 +3,18 @@
#include "testing/testing.h"
#include <forward_list>
-using namespace blender;
+namespace blender {
TEST(vector, DefaultConstructor)
{
Vector<int> vec;
- EXPECT_EQ(vec.size(), 0);
+ EXPECT_EQ(vec.size(), 0u);
}
TEST(vector, SizeConstructor)
{
Vector<int> vec(3);
- EXPECT_EQ(vec.size(), 3);
+ EXPECT_EQ(vec.size(), 3u);
}
/**
@@ -40,7 +40,7 @@ TEST(vector, TrivialTypeSizeConstructor)
TEST(vector, SizeValueConstructor)
{
Vector<int> vec(4, 10);
- EXPECT_EQ(vec.size(), 4);
+ EXPECT_EQ(vec.size(), 4u);
EXPECT_EQ(vec[0], 10);
EXPECT_EQ(vec[1], 10);
EXPECT_EQ(vec[2], 10);
@@ -50,7 +50,7 @@ TEST(vector, SizeValueConstructor)
TEST(vector, InitializerListConstructor)
{
Vector<int> vec = {1, 3, 4, 6};
- EXPECT_EQ(vec.size(), 4);
+ EXPECT_EQ(vec.size(), 4u);
EXPECT_EQ(vec[0], 1);
EXPECT_EQ(vec[1], 3);
EXPECT_EQ(vec[2], 4);
@@ -74,7 +74,7 @@ TEST(vector, ListBaseConstructor)
BLI_addtail(&list, value3);
Vector<TestListValue *> vec(list);
- EXPECT_EQ(vec.size(), 3);
+ EXPECT_EQ(vec.size(), 3u);
EXPECT_EQ(vec[0]->value, 4);
EXPECT_EQ(vec[1]->value, 5);
EXPECT_EQ(vec[2]->value, 6);
@@ -92,7 +92,7 @@ TEST(vector, ContainerConstructor)
list.push_front(5);
Vector<int> vec = Vector<int>::FromContainer(list);
- EXPECT_EQ(vec.size(), 3);
+ EXPECT_EQ(vec.size(), 3u);
EXPECT_EQ(vec[0], 5);
EXPECT_EQ(vec[1], 1);
EXPECT_EQ(vec[2], 3);
@@ -102,7 +102,7 @@ TEST(vector, CopyConstructor)
{
Vector<int> vec1 = {1, 2, 3};
Vector<int> vec2(vec1);
- EXPECT_EQ(vec2.size(), 3);
+ EXPECT_EQ(vec2.size(), 3u);
EXPECT_EQ(vec2[0], 1);
EXPECT_EQ(vec2[1], 2);
EXPECT_EQ(vec2[2], 3);
@@ -117,8 +117,8 @@ TEST(vector, CopyConstructor2)
Vector<int, 2> vec1 = {1, 2, 3, 4};
Vector<int, 3> vec2(vec1);
- EXPECT_EQ(vec1.size(), 4);
- EXPECT_EQ(vec2.size(), 4);
+ EXPECT_EQ(vec1.size(), 4u);
+ EXPECT_EQ(vec2.size(), 4u);
EXPECT_NE(vec1.data(), vec2.data());
EXPECT_EQ(vec2[0], 1);
EXPECT_EQ(vec2[1], 2);
@@ -131,8 +131,8 @@ TEST(vector, CopyConstructor3)
Vector<int, 20> vec1 = {1, 2, 3, 4};
Vector<int, 1> vec2(vec1);
- EXPECT_EQ(vec1.size(), 4);
- EXPECT_EQ(vec2.size(), 4);
+ EXPECT_EQ(vec1.size(), 4u);
+ EXPECT_EQ(vec2.size(), 4u);
EXPECT_NE(vec1.data(), vec2.data());
EXPECT_EQ(vec2[2], 3);
}
@@ -142,8 +142,8 @@ TEST(vector, CopyConstructor4)
Vector<int, 5> vec1 = {1, 2, 3, 4};
Vector<int, 6> vec2(vec1);
- EXPECT_EQ(vec1.size(), 4);
- EXPECT_EQ(vec2.size(), 4);
+ EXPECT_EQ(vec1.size(), 4u);
+ EXPECT_EQ(vec2.size(), 4u);
EXPECT_NE(vec1.data(), vec2.data());
EXPECT_EQ(vec2[3], 4);
}
@@ -153,8 +153,8 @@ TEST(vector, MoveConstructor)
Vector<int> vec1 = {1, 2, 3, 4};
Vector<int> vec2(std::move(vec1));
- EXPECT_EQ(vec1.size(), 0);
- EXPECT_EQ(vec2.size(), 4);
+ EXPECT_EQ(vec1.size(), 0u);
+ EXPECT_EQ(vec2.size(), 4u);
EXPECT_EQ(vec2[0], 1);
EXPECT_EQ(vec2[1], 2);
EXPECT_EQ(vec2[2], 3);
@@ -166,8 +166,8 @@ TEST(vector, MoveConstructor2)
Vector<int, 2> vec1 = {1, 2, 3, 4};
Vector<int, 3> vec2(std::move(vec1));
- EXPECT_EQ(vec1.size(), 0);
- EXPECT_EQ(vec2.size(), 4);
+ EXPECT_EQ(vec1.size(), 0u);
+ EXPECT_EQ(vec2.size(), 4u);
EXPECT_EQ(vec2[0], 1);
EXPECT_EQ(vec2[1], 2);
EXPECT_EQ(vec2[2], 3);
@@ -179,8 +179,8 @@ TEST(vector, MoveConstructor3)
Vector<int, 20> vec1 = {1, 2, 3, 4};
Vector<int, 1> vec2(std::move(vec1));
- EXPECT_EQ(vec1.size(), 0);
- EXPECT_EQ(vec2.size(), 4);
+ EXPECT_EQ(vec1.size(), 0u);
+ EXPECT_EQ(vec2.size(), 4u);
EXPECT_EQ(vec2[2], 3);
}
@@ -189,20 +189,20 @@ TEST(vector, MoveConstructor4)
Vector<int, 5> vec1 = {1, 2, 3, 4};
Vector<int, 6> vec2(std::move(vec1));
- EXPECT_EQ(vec1.size(), 0);
- EXPECT_EQ(vec2.size(), 4);
+ EXPECT_EQ(vec1.size(), 0u);
+ EXPECT_EQ(vec2.size(), 4u);
EXPECT_EQ(vec2[3], 4);
}
TEST(vector, MoveAssignment)
{
Vector<int> vec = {1, 2};
- EXPECT_EQ(vec.size(), 2);
+ EXPECT_EQ(vec.size(), 2u);
EXPECT_EQ(vec[0], 1);
EXPECT_EQ(vec[1], 2);
vec = Vector<int>({5});
- EXPECT_EQ(vec.size(), 1);
+ EXPECT_EQ(vec.size(), 1u);
EXPECT_EQ(vec[0], 5);
}
@@ -210,11 +210,11 @@ TEST(vector, CopyAssignment)
{
Vector<int> vec1 = {1, 2, 3};
Vector<int> vec2 = {4, 5};
- EXPECT_EQ(vec1.size(), 3);
- EXPECT_EQ(vec2.size(), 2);
+ EXPECT_EQ(vec1.size(), 3u);
+ EXPECT_EQ(vec2.size(), 2u);
vec2 = vec1;
- EXPECT_EQ(vec2.size(), 3);
+ EXPECT_EQ(vec2.size(), 3u);
vec1[0] = 7;
EXPECT_EQ(vec1[0], 7);
@@ -227,7 +227,7 @@ TEST(vector, Append)
vec.append(3);
vec.append(6);
vec.append(7);
- EXPECT_EQ(vec.size(), 3);
+ EXPECT_EQ(vec.size(), 3u);
EXPECT_EQ(vec[0], 3);
EXPECT_EQ(vec[1], 6);
EXPECT_EQ(vec[2], 7);
@@ -236,40 +236,40 @@ TEST(vector, Append)
TEST(vector, AppendAndGetIndex)
{
Vector<int> vec;
- EXPECT_EQ(vec.append_and_get_index(10), 0);
- EXPECT_EQ(vec.append_and_get_index(10), 1);
- EXPECT_EQ(vec.append_and_get_index(10), 2);
+ EXPECT_EQ(vec.append_and_get_index(10), 0u);
+ EXPECT_EQ(vec.append_and_get_index(10), 1u);
+ EXPECT_EQ(vec.append_and_get_index(10), 2u);
vec.append(10);
- EXPECT_EQ(vec.append_and_get_index(10), 4);
+ EXPECT_EQ(vec.append_and_get_index(10), 4u);
}
TEST(vector, AppendNonDuplicates)
{
Vector<int> vec;
vec.append_non_duplicates(4);
- EXPECT_EQ(vec.size(), 1);
+ EXPECT_EQ(vec.size(), 1u);
vec.append_non_duplicates(5);
- EXPECT_EQ(vec.size(), 2);
+ EXPECT_EQ(vec.size(), 2u);
vec.append_non_duplicates(4);
- EXPECT_EQ(vec.size(), 2);
+ EXPECT_EQ(vec.size(), 2u);
}
TEST(vector, ExtendNonDuplicates)
{
Vector<int> vec;
vec.extend_non_duplicates({1, 2});
- EXPECT_EQ(vec.size(), 2);
+ EXPECT_EQ(vec.size(), 2u);
vec.extend_non_duplicates({3, 4});
- EXPECT_EQ(vec.size(), 4);
+ EXPECT_EQ(vec.size(), 4u);
vec.extend_non_duplicates({0, 1, 2, 3});
- EXPECT_EQ(vec.size(), 5);
+ EXPECT_EQ(vec.size(), 5u);
}
TEST(vector, Fill)
{
Vector<int> vec(5);
vec.fill(3);
- EXPECT_EQ(vec.size(), 5);
+ EXPECT_EQ(vec.size(), 5u);
EXPECT_EQ(vec[0], 3);
EXPECT_EQ(vec[1], 3);
EXPECT_EQ(vec[2], 3);
@@ -304,9 +304,9 @@ TEST(vector, BecomeLarge)
for (int i = 0; i < 100; i++) {
vec.append(i * 5);
}
- EXPECT_EQ(vec.size(), 100);
+ EXPECT_EQ(vec.size(), 100u);
for (uint i = 0; i < 100; i++) {
- EXPECT_EQ(vec[i], i * 5);
+ EXPECT_EQ(vec[i], static_cast<int>(i * 5));
}
}
@@ -318,7 +318,7 @@ static Vector<int> return_by_value_helper()
TEST(vector, ReturnByValue)
{
Vector<int> vec = return_by_value_helper();
- EXPECT_EQ(vec.size(), 3);
+ EXPECT_EQ(vec.size(), 3u);
EXPECT_EQ(vec[0], 3);
EXPECT_EQ(vec[1], 5);
EXPECT_EQ(vec[2], 1);
@@ -327,12 +327,12 @@ TEST(vector, ReturnByValue)
TEST(vector, VectorOfVectors_Append)
{
Vector<Vector<int>> vec;
- EXPECT_EQ(vec.size(), 0);
+ EXPECT_EQ(vec.size(), 0u);
Vector<int> v({1, 2});
vec.append(v);
vec.append({7, 8});
- EXPECT_EQ(vec.size(), 2);
+ EXPECT_EQ(vec.size(), 2u);
EXPECT_EQ(vec[0][0], 1);
EXPECT_EQ(vec[0][1], 2);
EXPECT_EQ(vec[1][0], 7);
@@ -355,11 +355,11 @@ TEST(vector, VectorOfVectors_Fill)
TEST(vector, RemoveLast)
{
Vector<int> vec = {5, 6};
- EXPECT_EQ(vec.size(), 2);
+ EXPECT_EQ(vec.size(), 2u);
vec.remove_last();
- EXPECT_EQ(vec.size(), 1);
+ EXPECT_EQ(vec.size(), 1u);
vec.remove_last();
- EXPECT_EQ(vec.size(), 0);
+ EXPECT_EQ(vec.size(), 0u);
}
TEST(vector, IsEmpty)
@@ -401,7 +401,7 @@ TEST(vector, RemoveFirstOccurrenceAndReorder)
vec.remove_first_occurrence_and_reorder(4);
EXPECT_EQ(vec[0], 7);
vec.remove_first_occurrence_and_reorder(7);
- EXPECT_EQ(vec.size(), 0);
+ EXPECT_EQ(vec.size(), 0u);
}
TEST(vector, Remove)
@@ -426,7 +426,7 @@ TEST(vector, ExtendSmallVector)
Vector<int> a = {2, 3, 4};
Vector<int> b = {11, 12};
b.extend(a);
- EXPECT_EQ(b.size(), 5);
+ EXPECT_EQ(b.size(), 5u);
EXPECT_EQ(b[0], 11);
EXPECT_EQ(b[1], 12);
EXPECT_EQ(b[2], 2);
@@ -441,7 +441,7 @@ TEST(vector, ExtendArray)
Vector<int> a;
a.extend(array, 2);
- EXPECT_EQ(a.size(), 2);
+ EXPECT_EQ(a.size(), 2u);
EXPECT_EQ(a[0], 3);
EXPECT_EQ(a[1], 4);
}
@@ -457,7 +457,7 @@ TEST(vector, AppendNTimes)
Vector<int> a;
a.append_n_times(5, 3);
a.append_n_times(2, 2);
- EXPECT_EQ(a.size(), 5);
+ EXPECT_EQ(a.size(), 5u);
EXPECT_EQ(a[0], 5);
EXPECT_EQ(a[1], 5);
EXPECT_EQ(a[2], 5);
@@ -472,13 +472,13 @@ TEST(vector, UniquePtrValue)
vec.append(std::unique_ptr<int>(new int()));
vec.append(std::unique_ptr<int>(new int()));
vec.append(std::unique_ptr<int>(new int()));
- EXPECT_EQ(vec.size(), 4);
+ EXPECT_EQ(vec.size(), 4u);
std::unique_ptr<int> &a = vec.last();
std::unique_ptr<int> b = vec.pop_last();
vec.remove_and_reorder(0);
vec.remove(0);
- EXPECT_EQ(vec.size(), 1);
+ EXPECT_EQ(vec.size(), 1u);
UNUSED_VARS(a, b);
}
@@ -593,29 +593,29 @@ TEST(vector, Resize)
{
std::string long_string = "012345678901234567890123456789";
Vector<std::string> vec;
- EXPECT_EQ(vec.size(), 0);
+ EXPECT_EQ(vec.size(), 0u);
vec.resize(2);
- EXPECT_EQ(vec.size(), 2);
+ EXPECT_EQ(vec.size(), 2u);
EXPECT_EQ(vec[0], "");
EXPECT_EQ(vec[1], "");
vec.resize(5, long_string);
- EXPECT_EQ(vec.size(), 5);
+ EXPECT_EQ(vec.size(), 5u);
EXPECT_EQ(vec[0], "");
EXPECT_EQ(vec[1], "");
EXPECT_EQ(vec[2], long_string);
EXPECT_EQ(vec[3], long_string);
EXPECT_EQ(vec[4], long_string);
vec.resize(1);
- EXPECT_EQ(vec.size(), 1);
+ EXPECT_EQ(vec.size(), 1u);
EXPECT_EQ(vec[0], "");
}
TEST(vector, FirstIndexOf)
{
Vector<int> vec = {2, 3, 5, 7, 5, 9};
- EXPECT_EQ(vec.first_index_of(2), 0);
- EXPECT_EQ(vec.first_index_of(5), 2);
- EXPECT_EQ(vec.first_index_of(9), 5);
+ EXPECT_EQ(vec.first_index_of(2), 0u);
+ EXPECT_EQ(vec.first_index_of(5), 2u);
+ EXPECT_EQ(vec.first_index_of(9), 5u);
}
TEST(vector, FirstIndexTryOf)
@@ -636,3 +636,5 @@ TEST(vector, OveralignedValues)
EXPECT_EQ((uintptr_t)&vec.last() % 512, 0);
}
}
+
+} // namespace blender
diff --git a/tests/gtests/blenlib/CMakeLists.txt b/tests/gtests/blenlib/CMakeLists.txt
index 8ddb2702b83..ba493b22b42 100644
--- a/tests/gtests/blenlib/CMakeLists.txt
+++ b/tests/gtests/blenlib/CMakeLists.txt
@@ -60,9 +60,9 @@ BLENDER_TEST(BLI_math_base "bf_blenlib")
BLENDER_TEST(BLI_math_bits "bf_blenlib")
BLENDER_TEST(BLI_math_color "bf_blenlib")
BLENDER_TEST(BLI_math_geom "bf_blenlib")
+BLENDER_TEST(BLI_math_matrix "bf_blenlib")
BLENDER_TEST(BLI_math_vector "bf_blenlib")
BLENDER_TEST(BLI_memiter "bf_blenlib")
-BLENDER_TEST(BLI_optional "bf_blenlib")
BLENDER_TEST(BLI_path_util "${BLI_path_util_extra_libs}")
BLENDER_TEST(BLI_polyfill_2d "bf_blenlib")
BLENDER_TEST(BLI_set "bf_blenlib")
diff --git a/tests/gtests/functions/CMakeLists.txt b/tests/gtests/functions/CMakeLists.txt
index 413761ed0c8..1246bbd5599 100644
--- a/tests/gtests/functions/CMakeLists.txt
+++ b/tests/gtests/functions/CMakeLists.txt
@@ -36,4 +36,10 @@ if(WITH_BUILDINFO)
set(BUILDINFO buildinfoobj)
endif()
+BLENDER_TEST(FN_array_spans "bf_blenlib;bf_functions;${BUILDINFO}")
+BLENDER_TEST(FN_attributes_ref "bf_blenlib;bf_functions;${BUILDINFO}")
BLENDER_TEST(FN_cpp_type "bf_blenlib;bf_functions;${BUILDINFO}")
+BLENDER_TEST(FN_generic_vector_array "bf_blenlib;bf_functions;${BUILDINFO}")
+BLENDER_TEST(FN_multi_function "bf_blenlib;bf_functions;${BUILDINFO}")
+BLENDER_TEST(FN_multi_function_network "bf_blenlib;bf_functions;${BUILDINFO}")
+BLENDER_TEST(FN_spans "bf_blenlib;bf_functions;${BUILDINFO}")
diff --git a/tests/gtests/functions/FN_array_spans_test.cc b/tests/gtests/functions/FN_array_spans_test.cc
new file mode 100644
index 00000000000..21e52a90e4b
--- /dev/null
+++ b/tests/gtests/functions/FN_array_spans_test.cc
@@ -0,0 +1,149 @@
+/*
+ * 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.
+ */
+
+#include "testing/testing.h"
+
+#include "FN_array_spans.hh"
+#include "FN_cpp_types.hh"
+#include "FN_generic_vector_array.hh"
+
+#include "BLI_array.hh"
+
+namespace blender {
+namespace fn {
+
+TEST(virtual_array_span, EmptyConstructor)
+{
+ VArraySpan<int> span;
+ EXPECT_EQ(span.size(), 0);
+ EXPECT_TRUE(span.is_empty());
+
+ GVArraySpan converted(span);
+ EXPECT_EQ(converted.type(), CPPType::get<int>());
+ EXPECT_EQ(converted.size(), 0);
+}
+
+TEST(virtual_array_span, SingleArrayConstructor)
+{
+ std::array<int, 4> values = {3, 4, 5, 6};
+ VArraySpan<int> span{Span<int>(values), 3};
+ EXPECT_EQ(span.size(), 3);
+ EXPECT_FALSE(span.is_empty());
+ EXPECT_EQ(span[0].size(), 4);
+ EXPECT_EQ(span[1].size(), 4);
+ EXPECT_EQ(span[2].size(), 4);
+ EXPECT_EQ(span[0][0], 3);
+ EXPECT_EQ(span[0][1], 4);
+ EXPECT_EQ(span[0][2], 5);
+ EXPECT_EQ(span[0][3], 6);
+ EXPECT_EQ(span[1][3], 6);
+ EXPECT_EQ(span[2][1], 4);
+
+ GVArraySpan converted(span);
+ EXPECT_EQ(converted.type(), CPPType::get<int>());
+ EXPECT_EQ(converted.size(), 3);
+ EXPECT_EQ(converted[0].size(), 4);
+ EXPECT_EQ(converted[1].size(), 4);
+ EXPECT_EQ(converted[1][2], &values[2]);
+}
+
+TEST(virtual_array_span, MultipleArrayConstructor)
+{
+ std::array<int, 4> values0 = {1, 2, 3, 4};
+ std::array<int, 2> values1 = {6, 7};
+ std::array<int, 1> values2 = {8};
+ std::array<const int *, 3> starts = {values0.data(), values1.data(), values2.data()};
+ std::array<uint, 3> sizes{values0.size(), values1.size(), values2.size()};
+
+ VArraySpan<int> span{starts, sizes};
+ EXPECT_EQ(span.size(), 3);
+ EXPECT_FALSE(span.is_empty());
+ EXPECT_EQ(span[0].size(), 4);
+ EXPECT_EQ(span[1].size(), 2);
+ EXPECT_EQ(span[2].size(), 1);
+ EXPECT_EQ(&span[0][0], values0.data());
+ EXPECT_EQ(&span[1][0], values1.data());
+ EXPECT_EQ(&span[2][0], values2.data());
+ EXPECT_EQ(span[2][0], 8);
+ EXPECT_EQ(span[1][1], 7);
+
+ GVArraySpan converted(span);
+ EXPECT_EQ(converted.type(), CPPType::get<int>());
+ EXPECT_EQ(converted.size(), 3);
+ EXPECT_EQ(converted[0].size(), 4);
+ EXPECT_EQ(converted[1].size(), 2);
+ EXPECT_EQ(converted[2].size(), 1);
+ EXPECT_EQ(converted[0][0], values0.data());
+ EXPECT_EQ(converted[1][1], values1.data() + 1);
+}
+
+TEST(generic_virtual_array_span, TypeConstructor)
+{
+ GVArraySpan span{CPPType_int32};
+ EXPECT_EQ(span.size(), 0);
+ EXPECT_TRUE(span.is_empty());
+
+ VArraySpan converted = span.typed<int>();
+ EXPECT_EQ(converted.size(), 0);
+}
+
+TEST(generic_virtual_array_span, GSpanConstructor)
+{
+ std::array<std::string, 3> values = {"hello", "world", "test"};
+ GVArraySpan span{GSpan(CPPType_string, values.data(), 3), 5};
+ EXPECT_EQ(span.size(), 5);
+ EXPECT_FALSE(span.is_empty());
+ EXPECT_EQ(span[0][0], values.data());
+ EXPECT_EQ(span[1][0], values.data());
+ EXPECT_EQ(span[4][0], values.data());
+ EXPECT_EQ(span[0].size(), 3);
+ EXPECT_EQ(span[2].size(), 3);
+ EXPECT_EQ(*(std::string *)span[3][1], "world");
+
+ VArraySpan converted = span.typed<std::string>();
+ EXPECT_EQ(converted.size(), 5);
+ EXPECT_EQ(converted[0][0], "hello");
+ EXPECT_EQ(converted[1][0], "hello");
+ EXPECT_EQ(converted[4][0], "hello");
+ EXPECT_EQ(converted[0].size(), 3);
+ EXPECT_EQ(converted[2].size(), 3);
+}
+
+TEST(generic_virtual_array_span, IsSingleArray1)
+{
+ Array<int> values = {5, 6, 7};
+ GVArraySpan span{GSpan(values.as_span()), 4};
+ EXPECT_TRUE(span.is_single_array());
+
+ VArraySpan converted = span.typed<int>();
+ EXPECT_TRUE(converted.is_single_array());
+}
+
+TEST(generic_virtual_array_span, IsSingleArray2)
+{
+ GVectorArray vectors{CPPType_int32, 3};
+ GVectorArrayRef<int> vectors_ref = vectors;
+ vectors_ref.append(1, 4);
+
+ GVArraySpan span = vectors;
+ EXPECT_FALSE(span.is_single_array());
+
+ VArraySpan converted = span.typed<int>();
+ EXPECT_FALSE(converted.is_single_array());
+}
+
+} // namespace fn
+} // namespace blender
diff --git a/tests/gtests/functions/FN_attributes_ref_test.cc b/tests/gtests/functions/FN_attributes_ref_test.cc
new file mode 100644
index 00000000000..111190564e5
--- /dev/null
+++ b/tests/gtests/functions/FN_attributes_ref_test.cc
@@ -0,0 +1,114 @@
+/*
+ * 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.
+ */
+
+#include "BLI_float3.hh"
+#include "FN_attributes_ref.hh"
+#include "FN_cpp_types.hh"
+
+#include "testing/testing.h"
+
+namespace blender {
+namespace fn {
+
+TEST(attributes_info, BuildEmpty)
+{
+ AttributesInfoBuilder info_builder;
+ AttributesInfo info{info_builder};
+
+ EXPECT_EQ(info.size(), 0);
+}
+
+TEST(attributes_info, AddSameNameTwice)
+{
+ AttributesInfoBuilder info_builder;
+ info_builder.add<int>("A", 4);
+ info_builder.add<int>("A", 5);
+ AttributesInfo info{info_builder};
+ EXPECT_EQ(info.size(), 1);
+ EXPECT_TRUE(info.has_attribute("A", CPPType::get<int>()));
+ EXPECT_FALSE(info.has_attribute("B", CPPType::get<int>()));
+ EXPECT_FALSE(info.has_attribute("A", CPPType::get<float>()));
+ EXPECT_EQ(info.default_of<int>("A"), 4);
+ EXPECT_EQ(info.name_of(0), "A");
+ EXPECT_EQ(info.index_range().start(), 0);
+ EXPECT_EQ(info.index_range().one_after_last(), 1);
+}
+
+TEST(attributes_info, BuildWithDefaultString)
+{
+ AttributesInfoBuilder info_builder;
+ info_builder.add("A", CPPType::get<std::string>());
+ AttributesInfo info{info_builder};
+ EXPECT_EQ(info.default_of<std::string>("A"), "");
+}
+
+TEST(attributes_info, BuildWithGivenDefault)
+{
+ AttributesInfoBuilder info_builder;
+ info_builder.add<std::string>("A", "hello world");
+ AttributesInfo info{info_builder};
+ const void *default_value = info.default_of("A");
+ EXPECT_EQ(*(const std::string *)default_value, "hello world");
+ EXPECT_EQ(info.type_of("A"), CPPType::get<std::string>());
+}
+
+TEST(mutable_attributes_ref, ComplexTest)
+{
+ AttributesInfoBuilder info_builder;
+ info_builder.add<float3>("Position", {0, 0, 10});
+ info_builder.add<uint>("ID", 0);
+ info_builder.add<float>("Size", 0.5f);
+ info_builder.add<std::string>("Name", "<no name>");
+ AttributesInfo info{info_builder};
+
+ uint amount = 5;
+ Array<float3> positions(amount);
+ Array<uint> ids(amount, 0);
+ Array<float> sizes(amount);
+ Array<std::string> names(amount);
+
+ Array<void *> buffers = {positions.data(), ids.data(), sizes.data(), names.data()};
+ MutableAttributesRef attributes{info, buffers, IndexRange(1, 3)};
+ EXPECT_EQ(attributes.size(), 3);
+ EXPECT_EQ(attributes.info().size(), 4);
+ EXPECT_EQ(attributes.get("Position").buffer(), positions.data() + 1);
+ EXPECT_EQ(attributes.get("ID").buffer(), ids.data() + 1);
+ EXPECT_EQ(attributes.get("Size").buffer(), sizes.data() + 1);
+ EXPECT_EQ(attributes.get("Name").buffer(), names.data() + 1);
+
+ EXPECT_EQ(attributes.get("ID").size(), 3);
+ EXPECT_EQ(attributes.get<uint>("ID").size(), 3);
+
+ EXPECT_EQ(ids[2], 0);
+ MutableSpan<uint> ids_span = attributes.get<uint>("ID");
+ ids_span[1] = 42;
+ EXPECT_EQ(ids[2], 42);
+
+ EXPECT_FALSE(attributes.try_get<int>("not existant").has_value());
+ EXPECT_FALSE(attributes.try_get<int>("Position").has_value());
+ EXPECT_TRUE(attributes.try_get<float3>("Position").has_value());
+ EXPECT_FALSE(attributes.try_get("not existant", CPPType::get<int>()).has_value());
+ EXPECT_FALSE(attributes.try_get("Position", CPPType::get<int>()).has_value());
+ EXPECT_TRUE(attributes.try_get("Position", CPPType::get<float3>()).has_value());
+
+ MutableAttributesRef sliced = attributes.slice(IndexRange(1, 2));
+ EXPECT_EQ(sliced.size(), 2);
+ sliced.get<uint>("ID")[0] = 100;
+ EXPECT_EQ(ids[2], 100);
+}
+
+} // namespace fn
+} // namespace blender
diff --git a/tests/gtests/functions/FN_cpp_type_test.cc b/tests/gtests/functions/FN_cpp_type_test.cc
index 811e1a5d783..33e6fbee7f6 100644
--- a/tests/gtests/functions/FN_cpp_type_test.cc
+++ b/tests/gtests/functions/FN_cpp_type_test.cc
@@ -12,7 +12,6 @@
* 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.
- *
*/
#include "testing/testing.h"
@@ -20,7 +19,7 @@
#include "FN_cpp_type.hh"
namespace blender {
-namespace FN {
+namespace fn {
static const int default_constructed_value = 1;
static const int copy_constructed_value = 2;
@@ -85,6 +84,12 @@ TEST(cpp_type, Alignment)
EXPECT_EQ(CPPType_TestType.alignment(), alignof(TestType));
}
+TEST(cpp_type, Is)
+{
+ EXPECT_TRUE(CPPType_TestType.is<TestType>());
+ EXPECT_FALSE(CPPType_TestType.is<int>());
+}
+
TEST(cpp_type, DefaultConstruction)
{
int buffer[10] = {0};
@@ -301,5 +306,5 @@ TEST(cpp_type, FillUninitialized)
EXPECT_EQ(buffer2[9], 0);
}
-} // namespace FN
+} // namespace fn
} // namespace blender
diff --git a/tests/gtests/functions/FN_generic_vector_array_test.cc b/tests/gtests/functions/FN_generic_vector_array_test.cc
new file mode 100644
index 00000000000..c701fb9f678
--- /dev/null
+++ b/tests/gtests/functions/FN_generic_vector_array_test.cc
@@ -0,0 +1,118 @@
+/*
+ * 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.
+ */
+
+#include "FN_cpp_types.hh"
+#include "FN_generic_vector_array.hh"
+
+#include "testing/testing.h"
+
+namespace blender {
+namespace fn {
+
+TEST(generic_vector_array, Constructor)
+{
+ GVectorArray vectors{CPPType_int32, 3};
+ EXPECT_EQ(vectors.size(), 3);
+ EXPECT_EQ(vectors.lengths().size(), 3);
+ EXPECT_EQ(vectors.starts().size(), 3);
+ EXPECT_EQ(vectors.lengths()[0], 0);
+ EXPECT_EQ(vectors.lengths()[1], 0);
+ EXPECT_EQ(vectors.lengths()[2], 0);
+ EXPECT_EQ(vectors.type(), CPPType_int32);
+}
+
+TEST(generic_vector_array, Append)
+{
+ GVectorArray vectors{CPPType_string, 3};
+ std::string value = "hello";
+ vectors.append(0, &value);
+ value = "world";
+ vectors.append(0, &value);
+ vectors.append(2, &value);
+
+ EXPECT_EQ(vectors.lengths()[0], 2);
+ EXPECT_EQ(vectors.lengths()[1], 0);
+ EXPECT_EQ(vectors.lengths()[2], 1);
+ EXPECT_EQ(vectors[0].size(), 2);
+ EXPECT_EQ(vectors[0].typed<std::string>()[0], "hello");
+ EXPECT_EQ(vectors[0].typed<std::string>()[1], "world");
+ EXPECT_EQ(vectors[2].typed<std::string>()[0], "world");
+}
+
+TEST(generic_vector_array, AsArraySpan)
+{
+ GVectorArray vectors{CPPType_int32, 3};
+ int value = 3;
+ vectors.append(0, &value);
+ vectors.append(0, &value);
+ value = 5;
+ vectors.append(2, &value);
+ vectors.append(2, &value);
+ vectors.append(2, &value);
+
+ GVArraySpan span = vectors;
+ EXPECT_EQ(span.type(), CPPType_int32);
+ EXPECT_EQ(span.size(), 3);
+ EXPECT_EQ(span[0].size(), 2);
+ EXPECT_EQ(span[1].size(), 0);
+ EXPECT_EQ(span[2].size(), 3);
+ EXPECT_EQ(span[0].typed<int>()[1], 3);
+ EXPECT_EQ(span[2].typed<int>()[0], 5);
+}
+
+TEST(generic_vector_array, TypedRef)
+{
+ GVectorArray vectors{CPPType_int32, 4};
+ GVectorArrayRef<int> ref = vectors.typed<int>();
+ ref.append(0, 2);
+ ref.append(0, 6);
+ ref.append(0, 7);
+ ref.append(2, 1);
+ ref.append(2, 1);
+ ref.append(3, 5);
+ ref.append(3, 6);
+
+ EXPECT_EQ(ref[0].size(), 3);
+ EXPECT_EQ(vectors[0].size(), 3);
+ EXPECT_EQ(ref[0][0], 2);
+ EXPECT_EQ(ref[0][1], 6);
+ EXPECT_EQ(ref[0][2], 7);
+ EXPECT_EQ(ref[1].size(), 0);
+ EXPECT_EQ(ref[2][0], 1);
+ EXPECT_EQ(ref[2][1], 1);
+ EXPECT_EQ(ref[3][0], 5);
+ EXPECT_EQ(ref[3][1], 6);
+}
+
+TEST(generic_vector_array, Extend)
+{
+ GVectorArray vectors{CPPType_int32, 3};
+ GVectorArrayRef<int> ref = vectors;
+
+ ref.extend(1, {5, 6, 7});
+ ref.extend(0, {3});
+
+ EXPECT_EQ(vectors[0].size(), 1);
+ EXPECT_EQ(vectors[1].size(), 3);
+ EXPECT_EQ(vectors[2].size(), 0);
+ EXPECT_EQ(ref[1][0], 5);
+ EXPECT_EQ(ref[1][1], 6);
+ EXPECT_EQ(ref[1][2], 7);
+ EXPECT_EQ(ref[0][0], 3);
+}
+
+} // namespace fn
+} // namespace blender
diff --git a/tests/gtests/functions/FN_multi_function_network_test.cc b/tests/gtests/functions/FN_multi_function_network_test.cc
new file mode 100644
index 00000000000..a5fe3c4fb07
--- /dev/null
+++ b/tests/gtests/functions/FN_multi_function_network_test.cc
@@ -0,0 +1,270 @@
+/*
+ * 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.
+ */
+
+#include "testing/testing.h"
+
+#include "FN_cpp_types.hh"
+#include "FN_multi_function_builder.hh"
+#include "FN_multi_function_network.hh"
+#include "FN_multi_function_network_evaluation.hh"
+
+namespace blender {
+namespace fn {
+
+TEST(multi_function_network, Test1)
+{
+ CustomMF_SI_SO<int, int> add_10_fn("add 10", [](int value) { return value + 10; });
+ CustomMF_SI_SI_SO<int, int, int> multiply_fn("multiply", [](int a, int b) { return a * b; });
+
+ MFNetwork network;
+
+ MFNode &node1 = network.add_function(add_10_fn);
+ MFNode &node2 = network.add_function(multiply_fn);
+ MFOutputSocket &input_socket = network.add_input("Input", MFDataType::ForSingle<int>());
+ MFInputSocket &output_socket = network.add_output("Output", MFDataType::ForSingle<int>());
+ network.add_link(node1.output(0), node2.input(0));
+ network.add_link(node1.output(0), node2.input(1));
+ network.add_link(node2.output(0), output_socket);
+ network.add_link(input_socket, node1.input(0));
+
+ MFNetworkEvaluator network_fn{{&input_socket}, {&output_socket}};
+
+ {
+ Array<int> values = {4, 6, 1, 2, 0};
+ Array<int> results(values.size(), 0);
+
+ MFParamsBuilder params(network_fn, values.size());
+ params.add_readonly_single_input(values.as_span());
+ params.add_uninitialized_single_output(results.as_mutable_span());
+
+ MFContextBuilder context;
+
+ network_fn.call({0, 2, 3, 4}, params, context);
+
+ EXPECT_EQ(results[0], 14 * 14);
+ EXPECT_EQ(results[1], 0);
+ EXPECT_EQ(results[2], 11 * 11);
+ EXPECT_EQ(results[3], 12 * 12);
+ EXPECT_EQ(results[4], 10 * 10);
+ }
+ {
+ int value = 3;
+ Array<int> results(5, 0);
+
+ MFParamsBuilder params(network_fn, results.size());
+ params.add_readonly_single_input(&value);
+ params.add_uninitialized_single_output(results.as_mutable_span());
+
+ MFContextBuilder context;
+
+ network_fn.call({1, 2, 4}, params, context);
+
+ EXPECT_EQ(results[0], 0);
+ EXPECT_EQ(results[1], 13 * 13);
+ EXPECT_EQ(results[2], 13 * 13);
+ EXPECT_EQ(results[3], 0);
+ EXPECT_EQ(results[4], 13 * 13);
+ }
+}
+
+class ConcatVectorsFunction : public MultiFunction {
+ public:
+ ConcatVectorsFunction()
+ {
+ MFSignatureBuilder signature = this->get_builder("Concat Vectors");
+ signature.vector_mutable<int>("A");
+ signature.vector_input<int>("B");
+ }
+
+ void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
+ {
+ GVectorArrayRef<int> a = params.vector_mutable<int>(0);
+ VArraySpan<int> b = params.readonly_vector_input<int>(1);
+
+ for (uint i : mask) {
+ a.extend(i, b[i]);
+ }
+ }
+};
+
+class AppendFunction : public MultiFunction {
+ public:
+ AppendFunction()
+ {
+ MFSignatureBuilder signature = this->get_builder("Append");
+ signature.vector_mutable<int>("Vector");
+ signature.single_input<int>("Value");
+ }
+
+ void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
+ {
+ GVectorArrayRef<int> vectors = params.vector_mutable<int>(0);
+ VSpan<int> values = params.readonly_single_input<int>(1);
+
+ for (uint i : mask) {
+ vectors.append(i, values[i]);
+ }
+ }
+};
+
+class SumVectorFunction : public MultiFunction {
+ public:
+ SumVectorFunction()
+ {
+ MFSignatureBuilder signature = this->get_builder("Sum Vector");
+ signature.vector_input<int>("Vector");
+ signature.single_output<int>("Sum");
+ }
+
+ void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
+ {
+ VArraySpan<int> vectors = params.readonly_vector_input<int>(0);
+ MutableSpan<int> sums = params.uninitialized_single_output<int>(1);
+
+ for (uint i : mask) {
+ int sum = 0;
+ VSpan<int> vector = vectors[i];
+ for (uint j = 0; j < vector.size(); j++) {
+ sum += vector[j];
+ }
+ sums[i] = sum;
+ }
+ }
+};
+
+class CreateRangeFunction : public MultiFunction {
+ public:
+ CreateRangeFunction()
+ {
+ MFSignatureBuilder builder = this->get_builder("Create Range");
+ builder.single_input<int>("Size");
+ builder.vector_output<int>("Range");
+ }
+
+ void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
+ {
+ VSpan<int> sizes = params.readonly_single_input<int>(0, "Size");
+ GVectorArrayRef<int> ranges = params.vector_output<int>(1, "Range");
+
+ for (int i : mask) {
+ int size = sizes[i];
+ for (int j : IndexRange(size)) {
+ ranges.append(i, j);
+ }
+ }
+ }
+};
+
+TEST(multi_function_network, Test2)
+{
+ CustomMF_SI_SO<int, int> add_3_fn("add 3", [](int value) { return value + 3; });
+
+ ConcatVectorsFunction concat_vectors_fn;
+ AppendFunction append_fn;
+ SumVectorFunction sum_fn;
+ CreateRangeFunction create_range_fn;
+
+ MFNetwork network;
+
+ MFOutputSocket &input1 = network.add_input("Input 1", MFDataType::ForVector<int>());
+ MFOutputSocket &input2 = network.add_input("Input 2", MFDataType::ForSingle<int>());
+ MFInputSocket &output1 = network.add_output("Output 1", MFDataType::ForVector<int>());
+ MFInputSocket &output2 = network.add_output("Output 2", MFDataType::ForSingle<int>());
+
+ MFNode &node1 = network.add_function(add_3_fn);
+ MFNode &node2 = network.add_function(create_range_fn);
+ MFNode &node3 = network.add_function(concat_vectors_fn);
+ MFNode &node4 = network.add_function(sum_fn);
+ MFNode &node5 = network.add_function(append_fn);
+ MFNode &node6 = network.add_function(sum_fn);
+
+ network.add_link(input2, node1.input(0));
+ network.add_link(node1.output(0), node2.input(0));
+ network.add_link(node2.output(0), node3.input(1));
+ network.add_link(input1, node3.input(0));
+ network.add_link(input1, node4.input(0));
+ network.add_link(node4.output(0), node5.input(1));
+ network.add_link(node3.output(0), node5.input(0));
+ network.add_link(node5.output(0), node6.input(0));
+ network.add_link(node3.output(0), output1);
+ network.add_link(node6.output(0), output2);
+
+ // std::cout << network.to_dot() << "\n\n";
+
+ MFNetworkEvaluator network_fn{{&input1, &input2}, {&output1, &output2}};
+
+ {
+ Array<int> input_value_1 = {3, 6};
+ int input_value_2 = 4;
+
+ GVectorArray output_value_1(CPPType_int32, 5);
+ Array<int> output_value_2(5, -1);
+
+ MFParamsBuilder params(network_fn, 5);
+ params.add_readonly_vector_input(GVArraySpan(input_value_1.as_span(), 5));
+ params.add_readonly_single_input(&input_value_2);
+ params.add_vector_output(output_value_1);
+ params.add_uninitialized_single_output(output_value_2.as_mutable_span());
+
+ MFContextBuilder context;
+
+ network_fn.call({1, 2, 4}, params, context);
+
+ EXPECT_EQ(output_value_1[0].size(), 0);
+ EXPECT_EQ(output_value_1[1].size(), 9);
+ EXPECT_EQ(output_value_1[2].size(), 9);
+ EXPECT_EQ(output_value_1[3].size(), 0);
+ EXPECT_EQ(output_value_1[4].size(), 9);
+
+ EXPECT_EQ(output_value_2[0], -1);
+ EXPECT_EQ(output_value_2[1], 39);
+ EXPECT_EQ(output_value_2[2], 39);
+ EXPECT_EQ(output_value_2[3], -1);
+ EXPECT_EQ(output_value_2[4], 39);
+ }
+ {
+ GVectorArray input_value_1(CPPType_int32, 3);
+ GVectorArrayRef<int> input_value_ref_1 = input_value_1;
+ input_value_ref_1.extend(0, {3, 4, 5});
+ input_value_ref_1.extend(1, {1, 2});
+
+ Array<int> input_value_2 = {4, 2, 3};
+
+ GVectorArray output_value_1(CPPType_int32, 3);
+ Array<int> output_value_2(3, -1);
+
+ MFParamsBuilder params(network_fn, 3);
+ params.add_readonly_vector_input(input_value_1);
+ params.add_readonly_single_input(input_value_2.as_span());
+ params.add_vector_output(output_value_1);
+ params.add_uninitialized_single_output(output_value_2.as_mutable_span());
+
+ MFContextBuilder context;
+
+ network_fn.call({0, 1, 2}, params, context);
+
+ EXPECT_EQ(output_value_1[0].size(), 10);
+ EXPECT_EQ(output_value_1[1].size(), 7);
+ EXPECT_EQ(output_value_1[2].size(), 6);
+
+ EXPECT_EQ(output_value_2[0], 45);
+ EXPECT_EQ(output_value_2[1], 16);
+ EXPECT_EQ(output_value_2[2], 15);
+ }
+}
+
+} // namespace fn
+} // namespace blender
diff --git a/tests/gtests/functions/FN_multi_function_test.cc b/tests/gtests/functions/FN_multi_function_test.cc
new file mode 100644
index 00000000000..903c385ea67
--- /dev/null
+++ b/tests/gtests/functions/FN_multi_function_test.cc
@@ -0,0 +1,334 @@
+/*
+ * 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.
+ */
+
+#include "testing/testing.h"
+
+#include "FN_cpp_types.hh"
+#include "FN_multi_function.hh"
+#include "FN_multi_function_builder.hh"
+
+namespace blender {
+namespace fn {
+
+class AddFunction : public MultiFunction {
+ public:
+ AddFunction()
+ {
+ MFSignatureBuilder builder = this->get_builder("Add");
+ builder.single_input<int>("A");
+ builder.single_input<int>("B");
+ builder.single_output<int>("Result");
+ }
+
+ void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
+ {
+ VSpan<int> a = params.readonly_single_input<int>(0, "A");
+ VSpan<int> b = params.readonly_single_input<int>(1, "B");
+ MutableSpan<int> result = params.uninitialized_single_output<int>(2, "Result");
+
+ for (uint i : mask) {
+ result[i] = a[i] + b[i];
+ }
+ }
+};
+
+TEST(multi_function, AddFunction)
+{
+ AddFunction fn;
+
+ Array<int> input1 = {4, 5, 6};
+ Array<int> input2 = {10, 20, 30};
+ Array<int> output(3, -1);
+
+ MFParamsBuilder params(fn, 3);
+ params.add_readonly_single_input(input1.as_span());
+ params.add_readonly_single_input(input2.as_span());
+ params.add_uninitialized_single_output(output.as_mutable_span());
+
+ MFContextBuilder context;
+
+ fn.call({0, 2}, params, context);
+
+ EXPECT_EQ(output[0], 14);
+ EXPECT_EQ(output[1], -1);
+ EXPECT_EQ(output[2], 36);
+}
+
+class AddPrefixFunction : public MultiFunction {
+ public:
+ AddPrefixFunction()
+ {
+ MFSignatureBuilder builder = this->get_builder("Add Prefix");
+ builder.single_input<std::string>("Prefix");
+ builder.single_mutable<std::string>("Strings");
+ }
+
+ void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
+ {
+ VSpan<std::string> prefixes = params.readonly_single_input<std::string>(0, "Prefix");
+ MutableSpan<std::string> strings = params.single_mutable<std::string>(1, "Strings");
+
+ for (uint i : mask) {
+ strings[i] = prefixes[i] + strings[i];
+ }
+ }
+};
+
+TEST(multi_function, AddPrefixFunction)
+{
+ AddPrefixFunction fn;
+
+ Array<std::string> strings = {
+ "Hello",
+ "World",
+ "This is a test",
+ "Another much longer string to trigger an allocation",
+ };
+
+ std::string prefix = "AB";
+
+ MFParamsBuilder params(fn, strings.size());
+ params.add_readonly_single_input(&prefix);
+ params.add_single_mutable(strings.as_mutable_span());
+
+ MFContextBuilder context;
+
+ fn.call({0, 2, 3}, params, context);
+
+ EXPECT_EQ(strings[0], "ABHello");
+ EXPECT_EQ(strings[1], "World");
+ EXPECT_EQ(strings[2], "ABThis is a test");
+ EXPECT_EQ(strings[3], "ABAnother much longer string to trigger an allocation");
+}
+
+class CreateRangeFunction : public MultiFunction {
+ public:
+ CreateRangeFunction()
+ {
+ MFSignatureBuilder builder = this->get_builder("Create Range");
+ builder.single_input<uint>("Size");
+ builder.vector_output<uint>("Range");
+ }
+
+ void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
+ {
+ VSpan<uint> sizes = params.readonly_single_input<uint>(0, "Size");
+ GVectorArrayRef<uint> ranges = params.vector_output<uint>(1, "Range");
+
+ for (uint i : mask) {
+ uint size = sizes[i];
+ for (uint j : IndexRange(size)) {
+ ranges.append(i, j);
+ }
+ }
+ }
+};
+
+TEST(multi_function, CreateRangeFunction)
+{
+ CreateRangeFunction fn;
+
+ GVectorArray ranges(CPPType_uint32, 5);
+ GVectorArrayRef<uint> ranges_ref(ranges);
+ Array<uint> sizes = {3, 0, 6, 1, 4};
+
+ MFParamsBuilder params(fn, ranges.size());
+ params.add_readonly_single_input(sizes.as_span());
+ params.add_vector_output(ranges);
+
+ MFContextBuilder context;
+
+ fn.call({0, 1, 2, 3}, params, context);
+
+ EXPECT_EQ(ranges_ref[0].size(), 3);
+ EXPECT_EQ(ranges_ref[1].size(), 0);
+ EXPECT_EQ(ranges_ref[2].size(), 6);
+ EXPECT_EQ(ranges_ref[3].size(), 1);
+ EXPECT_EQ(ranges_ref[4].size(), 0);
+
+ EXPECT_EQ(ranges_ref[0][0], 0);
+ EXPECT_EQ(ranges_ref[0][1], 1);
+ EXPECT_EQ(ranges_ref[0][2], 2);
+ EXPECT_EQ(ranges_ref[2][0], 0);
+ EXPECT_EQ(ranges_ref[2][1], 1);
+}
+
+class GenericAppendFunction : public MultiFunction {
+ public:
+ GenericAppendFunction(const CPPType &type)
+ {
+ MFSignatureBuilder builder = this->get_builder("Append");
+ builder.vector_mutable("Vector", type);
+ builder.single_input("Value", type);
+ }
+
+ void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
+ {
+ GVectorArray &vectors = params.vector_mutable(0, "Vector");
+ GVSpan values = params.readonly_single_input(1, "Value");
+
+ for (uint i : mask) {
+ vectors.append(i, values[i]);
+ }
+ }
+};
+
+TEST(multi_function, GenericAppendFunction)
+{
+ GenericAppendFunction fn(CPPType_int32);
+
+ GVectorArray vectors(CPPType_int32, 4);
+ GVectorArrayRef<int> vectors_ref(vectors);
+ vectors_ref.append(0, 1);
+ vectors_ref.append(0, 2);
+ vectors_ref.append(2, 6);
+ Array<int> values = {5, 7, 3, 1};
+
+ MFParamsBuilder params(fn, vectors.size());
+ params.add_vector_mutable(vectors);
+ params.add_readonly_single_input(values.as_span());
+
+ MFContextBuilder context;
+
+ fn.call(IndexRange(vectors.size()), params, context);
+
+ EXPECT_EQ(vectors_ref[0].size(), 3);
+ EXPECT_EQ(vectors_ref[1].size(), 1);
+ EXPECT_EQ(vectors_ref[2].size(), 2);
+ EXPECT_EQ(vectors_ref[3].size(), 1);
+
+ EXPECT_EQ(vectors_ref[0][0], 1);
+ EXPECT_EQ(vectors_ref[0][1], 2);
+ EXPECT_EQ(vectors_ref[0][2], 5);
+ EXPECT_EQ(vectors_ref[1][0], 7);
+ EXPECT_EQ(vectors_ref[2][0], 6);
+ EXPECT_EQ(vectors_ref[2][1], 3);
+ EXPECT_EQ(vectors_ref[3][0], 1);
+}
+
+TEST(multi_function, CustomMF_SI_SO)
+{
+ CustomMF_SI_SO<std::string, uint> fn("strlen",
+ [](const std::string &str) { return str.size(); });
+
+ Array<std::string> strings = {"hello", "world", "test", "another test"};
+ Array<uint> sizes(strings.size(), 0);
+
+ MFParamsBuilder params(fn, strings.size());
+ params.add_readonly_single_input(strings.as_span());
+ params.add_uninitialized_single_output(sizes.as_mutable_span());
+
+ MFContextBuilder context;
+
+ fn.call(IndexRange(strings.size()), params, context);
+
+ EXPECT_EQ(sizes[0], 5);
+ EXPECT_EQ(sizes[1], 5);
+ EXPECT_EQ(sizes[2], 4);
+ EXPECT_EQ(sizes[3], 12);
+}
+
+TEST(multi_function, CustomMF_SI_SI_SO)
+{
+ CustomMF_SI_SI_SO<int, int, int> fn("mul", [](int a, int b) { return a * b; });
+
+ Array<int> values_a = {4, 6, 8, 9};
+ int value_b = 10;
+ Array<int> outputs(values_a.size(), -1);
+
+ MFParamsBuilder params(fn, values_a.size());
+ params.add_readonly_single_input(values_a.as_span());
+ params.add_readonly_single_input(&value_b);
+ params.add_uninitialized_single_output(outputs.as_mutable_span());
+
+ MFContextBuilder context;
+
+ fn.call({0, 1, 3}, params, context);
+
+ EXPECT_EQ(outputs[0], 40);
+ EXPECT_EQ(outputs[1], 60);
+ EXPECT_EQ(outputs[2], -1);
+ EXPECT_EQ(outputs[3], 90);
+}
+
+TEST(multi_function, CustomMF_SI_SI_SI_SO)
+{
+ CustomMF_SI_SI_SI_SO<int, std::string, bool, uint> fn{
+ "custom",
+ [](int a, const std::string &b, bool c) { return (uint)((uint)a + b.size() + (uint)c); }};
+
+ Array<int> values_a = {5, 7, 3, 8};
+ Array<std::string> values_b = {"hello", "world", "another", "test"};
+ Array<bool> values_c = {true, false, false, true};
+ Array<uint> outputs(values_a.size(), 0);
+
+ MFParamsBuilder params(fn, values_a.size());
+ params.add_readonly_single_input(values_a.as_span());
+ params.add_readonly_single_input(values_b.as_span());
+ params.add_readonly_single_input(values_c.as_span());
+ params.add_uninitialized_single_output(outputs.as_mutable_span());
+
+ MFContextBuilder context;
+
+ fn.call({1, 2, 3}, params, context);
+
+ EXPECT_EQ(outputs[0], 0);
+ EXPECT_EQ(outputs[1], 12);
+ EXPECT_EQ(outputs[2], 10);
+ EXPECT_EQ(outputs[3], 13);
+}
+
+TEST(multi_function, CustomMF_SM)
+{
+ CustomMF_SM<std::string> fn("AddSuffix", [](std::string &value) { value += " test"; });
+
+ Array<std::string> values = {"a", "b", "c", "d", "e"};
+
+ MFParamsBuilder params(fn, values.size());
+ params.add_single_mutable(values.as_mutable_span());
+
+ MFContextBuilder context;
+
+ fn.call({1, 2, 3}, params, context);
+
+ EXPECT_EQ(values[0], "a");
+ EXPECT_EQ(values[1], "b test");
+ EXPECT_EQ(values[2], "c test");
+ EXPECT_EQ(values[3], "d test");
+ EXPECT_EQ(values[4], "e");
+}
+
+TEST(multi_function, CustomMF_Constant)
+{
+ CustomMF_Constant<int> fn{42};
+
+ Array<int> outputs(4, 0);
+
+ MFParamsBuilder params(fn, outputs.size());
+ params.add_uninitialized_single_output(outputs.as_mutable_span());
+
+ MFContextBuilder context;
+
+ fn.call({0, 2, 3}, params, context);
+
+ EXPECT_EQ(outputs[0], 42);
+ EXPECT_EQ(outputs[1], 0);
+ EXPECT_EQ(outputs[2], 42);
+ EXPECT_EQ(outputs[3], 42);
+}
+
+} // namespace fn
+} // namespace blender
diff --git a/tests/gtests/functions/FN_spans_test.cc b/tests/gtests/functions/FN_spans_test.cc
new file mode 100644
index 00000000000..59797f69309
--- /dev/null
+++ b/tests/gtests/functions/FN_spans_test.cc
@@ -0,0 +1,231 @@
+/*
+ * 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.
+ */
+
+#include "testing/testing.h"
+
+#include "FN_cpp_types.hh"
+#include "FN_spans.hh"
+
+namespace blender {
+namespace fn {
+
+TEST(generic_span, TypeConstructor)
+{
+ GSpan span(CPPType_float);
+ EXPECT_EQ(span.size(), 0);
+ EXPECT_EQ(span.typed<float>().size(), 0);
+ EXPECT_TRUE(span.is_empty());
+}
+
+TEST(generic_span, BufferAndSizeConstructor)
+{
+ int values[4] = {6, 7, 3, 2};
+ void *buffer = (void *)values;
+ GSpan span(CPPType_int32, buffer, 4);
+ EXPECT_EQ(span.size(), 4);
+ EXPECT_FALSE(span.is_empty());
+ EXPECT_EQ(span.typed<int>().size(), 4);
+ EXPECT_EQ(span[0], &values[0]);
+ EXPECT_EQ(span[1], &values[1]);
+ EXPECT_EQ(span[2], &values[2]);
+ EXPECT_EQ(span[3], &values[3]);
+}
+
+TEST(generic_mutable_span, TypeConstructor)
+{
+ GMutableSpan span(CPPType_int32);
+ EXPECT_EQ(span.size(), 0);
+ EXPECT_TRUE(span.is_empty());
+}
+
+TEST(generic_mutable_span, BufferAndSizeConstructor)
+{
+ int values[4] = {4, 7, 3, 5};
+ void *buffer = (void *)values;
+ GMutableSpan span(CPPType_int32, buffer, 4);
+ EXPECT_EQ(span.size(), 4);
+ EXPECT_FALSE(span.is_empty());
+ EXPECT_EQ(span.typed<int>().size(), 4);
+ EXPECT_EQ(values[2], 3);
+ *(int *)span[2] = 10;
+ EXPECT_EQ(values[2], 10);
+ span.typed<int>()[2] = 20;
+ EXPECT_EQ(values[2], 20);
+}
+
+TEST(virtual_span, EmptyConstructor)
+{
+ VSpan<int> span;
+ EXPECT_EQ(span.size(), 0);
+ EXPECT_TRUE(span.is_empty());
+ EXPECT_FALSE(span.is_single_element());
+
+ GVSpan converted(span);
+ EXPECT_EQ(converted.type(), CPPType::get<int>());
+ EXPECT_EQ(converted.size(), 0);
+}
+
+TEST(virtual_span, SpanConstructor)
+{
+ std::array<int, 5> values = {7, 3, 8, 6, 4};
+ Span<int> span = values;
+ VSpan<int> virtual_span = span;
+ EXPECT_EQ(virtual_span.size(), 5);
+ EXPECT_FALSE(virtual_span.is_empty());
+ EXPECT_EQ(virtual_span[0], 7);
+ EXPECT_EQ(virtual_span[2], 8);
+ EXPECT_EQ(virtual_span[3], 6);
+ EXPECT_FALSE(virtual_span.is_single_element());
+
+ GVSpan converted(span);
+ EXPECT_EQ(converted.type(), CPPType::get<int>());
+ EXPECT_EQ(converted.size(), 5);
+}
+
+TEST(virtual_span, PointerSpanConstructor)
+{
+ int x0 = 3;
+ int x1 = 6;
+ int x2 = 7;
+ std::array<const int *, 3> pointers = {&x0, &x2, &x1};
+ VSpan<int> span = Span<const int *>(pointers);
+ EXPECT_EQ(span.size(), 3);
+ EXPECT_FALSE(span.is_empty());
+ EXPECT_EQ(span[0], 3);
+ EXPECT_EQ(span[1], 7);
+ EXPECT_EQ(span[2], 6);
+ EXPECT_EQ(&span[1], &x2);
+ EXPECT_FALSE(span.is_single_element());
+
+ GVSpan converted(span);
+ EXPECT_EQ(converted.type(), CPPType::get<int>());
+ EXPECT_EQ(converted.size(), 3);
+ EXPECT_EQ(converted[0], &x0);
+ EXPECT_EQ(converted[1], &x2);
+ EXPECT_EQ(converted[2], &x1);
+}
+
+TEST(virtual_span, SingleConstructor)
+{
+ int value = 5;
+ VSpan<int> span = VSpan<int>::FromSingle(&value, 3);
+ EXPECT_EQ(span.size(), 3);
+ EXPECT_FALSE(span.is_empty());
+ EXPECT_EQ(span[0], 5);
+ EXPECT_EQ(span[1], 5);
+ EXPECT_EQ(span[2], 5);
+ EXPECT_EQ(&span[0], &value);
+ EXPECT_EQ(&span[1], &value);
+ EXPECT_EQ(&span[2], &value);
+ EXPECT_TRUE(span.is_single_element());
+
+ GVSpan converted(span);
+ EXPECT_EQ(converted.type(), CPPType::get<int>());
+ EXPECT_EQ(converted.size(), 3);
+ EXPECT_EQ(converted[0], &value);
+ EXPECT_EQ(converted[1], &value);
+ EXPECT_EQ(converted[2], &value);
+}
+
+TEST(generic_virtual_span, TypeConstructor)
+{
+ GVSpan span(CPPType_int32);
+ EXPECT_EQ(span.size(), 0);
+ EXPECT_TRUE(span.is_empty());
+ EXPECT_FALSE(span.is_single_element());
+
+ VSpan<int> converted = span.typed<int>();
+ EXPECT_EQ(converted.size(), 0);
+}
+
+TEST(generic_virtual_span, GenericSpanConstructor)
+{
+ int values[4] = {3, 4, 5, 6};
+ GVSpan span{GSpan(CPPType_int32, values, 4)};
+ EXPECT_EQ(span.size(), 4);
+ EXPECT_FALSE(span.is_empty());
+ EXPECT_EQ(span[0], &values[0]);
+ EXPECT_EQ(span[1], &values[1]);
+ EXPECT_EQ(span[2], &values[2]);
+ EXPECT_EQ(span[3], &values[3]);
+ EXPECT_FALSE(span.is_single_element());
+
+ int materialized[4] = {0};
+ span.materialize_to_uninitialized(materialized);
+ EXPECT_EQ(materialized[0], 3);
+ EXPECT_EQ(materialized[1], 4);
+ EXPECT_EQ(materialized[2], 5);
+ EXPECT_EQ(materialized[3], 6);
+
+ VSpan<int> converted = span.typed<int>();
+ EXPECT_EQ(converted.size(), 4);
+ EXPECT_EQ(converted[0], 3);
+ EXPECT_EQ(converted[1], 4);
+ EXPECT_EQ(converted[2], 5);
+ EXPECT_EQ(converted[3], 6);
+}
+
+TEST(generic_virtual_span, SpanConstructor)
+{
+ std::array<int, 3> values = {6, 7, 8};
+ GVSpan span{Span<int>(values)};
+ EXPECT_EQ(span.type(), CPPType_int32);
+ EXPECT_EQ(span.size(), 3);
+ EXPECT_EQ(span[0], &values[0]);
+ EXPECT_EQ(span[1], &values[1]);
+ EXPECT_EQ(span[2], &values[2]);
+ EXPECT_FALSE(span.is_single_element());
+
+ int materialized[3] = {0};
+ span.materialize_to_uninitialized(materialized);
+ EXPECT_EQ(materialized[0], 6);
+ EXPECT_EQ(materialized[1], 7);
+ EXPECT_EQ(materialized[2], 8);
+
+ VSpan<int> converted = span.typed<int>();
+ EXPECT_EQ(converted.size(), 3);
+ EXPECT_EQ(converted[0], 6);
+ EXPECT_EQ(converted[1], 7);
+ EXPECT_EQ(converted[2], 8);
+}
+
+TEST(generic_virtual_span, SingleConstructor)
+{
+ int value = 5;
+ GVSpan span = GVSpan::FromSingle(CPPType_int32, &value, 3);
+ EXPECT_EQ(span.size(), 3);
+ EXPECT_FALSE(span.is_empty());
+ EXPECT_EQ(span[0], &value);
+ EXPECT_EQ(span[1], &value);
+ EXPECT_EQ(span[2], &value);
+ EXPECT_TRUE(span.is_single_element());
+ EXPECT_EQ(span.as_single_element(), &value);
+
+ int materialized[3] = {0};
+ span.materialize_to_uninitialized({1, 2}, materialized);
+ EXPECT_EQ(materialized[0], 0);
+ EXPECT_EQ(materialized[1], 5);
+ EXPECT_EQ(materialized[2], 5);
+
+ VSpan<int> converted = span.typed<int>();
+ EXPECT_EQ(converted.size(), 3);
+ EXPECT_EQ(converted[0], 5);
+ EXPECT_EQ(converted[1], 5);
+ EXPECT_EQ(converted[2], 5);
+}
+
+} // namespace fn
+} // namespace blender
diff --git a/tests/gtests/usd/CMakeLists.txt b/tests/gtests/usd/CMakeLists.txt
index 56759f4ccea..d2bfe5e1306 100644
--- a/tests/gtests/usd/CMakeLists.txt
+++ b/tests/gtests/usd/CMakeLists.txt
@@ -35,6 +35,7 @@ set(INC
..
../../../source/blender/blenlib
../../../source/blender/blenkernel
+ ../../../source/blender/io/common
../../../source/blender/io/usd
../../../source/blender/makesdna
../../../source/blender/depsgraph
@@ -52,6 +53,7 @@ set(LIB
bf_gpu
bf_usd
+ bf_io_common
${BOOST_LIBRARIES}
${TBB_LIBRARIES}
diff --git a/tests/gtests/usd/abstract_hierarchy_iterator_test.cc b/tests/gtests/usd/abstract_hierarchy_iterator_test.cc
index 160dd52f39a..d9148a7b289 100644
--- a/tests/gtests/usd/abstract_hierarchy_iterator_test.cc
+++ b/tests/gtests/usd/abstract_hierarchy_iterator_test.cc
@@ -16,8 +16,8 @@
* The Original Code is Copyright (C) 2019 Blender Foundation.
* All rights reserved.
*/
+#include "IO_abstract_hierarchy_iterator.h"
#include "blenloader/blendfile_loading_base_test.h"
-#include "intern/abstract_hierarchy_iterator.h"
extern "C" {
#include "BLI_math.h"
@@ -30,16 +30,16 @@ extern "C" {
/* Mapping from ID.name to set of export hierarchy path. Duplicated objects can be exported
* multiple times with different export paths, hence the set. */
-typedef std::map<std::string, std::set<std::string>> created_writers;
+typedef std::map<std::string, std::set<std::string>> used_writers;
-using namespace USD;
+using namespace blender::io;
class TestHierarchyWriter : public AbstractHierarchyWriter {
public:
std::string writer_type;
- created_writers &writers_map;
+ used_writers &writers_map;
- TestHierarchyWriter(const std::string &writer_type, created_writers &writers_map)
+ TestHierarchyWriter(const std::string &writer_type, used_writers &writers_map)
: writer_type(writer_type), writers_map(writers_map)
{
}
@@ -47,7 +47,7 @@ class TestHierarchyWriter : public AbstractHierarchyWriter {
void write(HierarchyContext &context) override
{
const char *id_name = context.object->id.name;
- created_writers::mapped_type &writers = writers_map[id_name];
+ used_writers::mapped_type &writers = writers_map[id_name];
if (writers.find(context.export_path) != writers.end()) {
ADD_FAILURE() << "Unexpectedly found another " << writer_type << " writer for " << id_name
@@ -57,7 +57,7 @@ class TestHierarchyWriter : public AbstractHierarchyWriter {
}
};
-void debug_print_writers(const char *label, const created_writers &writers_map)
+void debug_print_writers(const char *label, const used_writers &writers_map)
{
printf("%s:\n", label);
for (auto idname_writers : writers_map) {
@@ -70,10 +70,10 @@ void debug_print_writers(const char *label, const created_writers &writers_map)
class TestingHierarchyIterator : public AbstractHierarchyIterator {
public: /* Public so that the test cases can directly inspect the created writers. */
- created_writers transform_writers;
- created_writers data_writers;
- created_writers hair_writers;
- created_writers particle_writers;
+ used_writers transform_writers;
+ used_writers data_writers;
+ used_writers hair_writers;
+ used_writers particle_writers;
public:
explicit TestingHierarchyIterator(Depsgraph *depsgraph) : AbstractHierarchyIterator(depsgraph)
@@ -151,7 +151,7 @@ TEST_F(USDHierarchyIteratorTest, ExportHierarchyTest)
iterator->iterate_and_write();
// Mapping from object name to set of export paths.
- created_writers expected_transforms = {
+ used_writers expected_transforms = {
{"OBCamera", {"/Camera"}},
{"OBDupli1", {"/Dupli1"}},
{"OBDupli2", {"/ParentOfDupli2/Dupli2"}},
@@ -177,7 +177,7 @@ TEST_F(USDHierarchyIteratorTest, ExportHierarchyTest)
{"OBParentOfDupli2", {"/ParentOfDupli2"}}};
EXPECT_EQ(expected_transforms, iterator->transform_writers);
- created_writers expected_data = {
+ used_writers expected_data = {
{"OBCamera", {"/Camera/Camera"}},
{"OBGEO_Ear_L",
{"/Dupli1/GEO_Head-0/GEO_Ear_L-1/Ear",
@@ -204,4 +204,124 @@ TEST_F(USDHierarchyIteratorTest, ExportHierarchyTest)
// The scene has no hair or particle systems.
EXPECT_EQ(0, iterator->hair_writers.size());
EXPECT_EQ(0, iterator->particle_writers.size());
+
+ // On the second iteration, everything should be written as well.
+ // This tests the default value of iterator->export_subset_.
+ iterator->transform_writers.clear();
+ iterator->data_writers.clear();
+ iterator->iterate_and_write();
+ EXPECT_EQ(expected_transforms, iterator->transform_writers);
+ EXPECT_EQ(expected_data, iterator->data_writers);
+}
+
+TEST_F(USDHierarchyIteratorTest, ExportSubsetTest)
+{
+ // The scene has no hair or particle systems, and this is already covered by ExportHierarchyTest,
+ // so not included here. Update this test when hair & particle systems are included.
+
+ /* Load the test blend file. */
+ if (!blendfile_load("usd/usd_hierarchy_export_test.blend")) {
+ return;
+ }
+ depsgraph_create(DAG_EVAL_RENDER);
+ iterator_create();
+
+ // Mapping from object name to set of export paths.
+ used_writers expected_transforms = {
+ {"OBCamera", {"/Camera"}},
+ {"OBDupli1", {"/Dupli1"}},
+ {"OBDupli2", {"/ParentOfDupli2/Dupli2"}},
+ {"OBGEO_Ear_L",
+ {"/Dupli1/GEO_Head-0/GEO_Ear_L-1",
+ "/Ground plane/OutsideDupliGrandParent/OutsideDupliParent/GEO_Head/GEO_Ear_L",
+ "/ParentOfDupli2/Dupli2/GEO_Head-0/GEO_Ear_L-1"}},
+ {"OBGEO_Ear_R",
+ {"/Dupli1/GEO_Head-0/GEO_Ear_R-2",
+ "/Ground plane/OutsideDupliGrandParent/OutsideDupliParent/GEO_Head/GEO_Ear_R",
+ "/ParentOfDupli2/Dupli2/GEO_Head-0/GEO_Ear_R-2"}},
+ {"OBGEO_Head",
+ {"/Dupli1/GEO_Head-0",
+ "/Ground plane/OutsideDupliGrandParent/OutsideDupliParent/GEO_Head",
+ "/ParentOfDupli2/Dupli2/GEO_Head-0"}},
+ {"OBGEO_Nose",
+ {"/Dupli1/GEO_Head-0/GEO_Nose-3",
+ "/Ground plane/OutsideDupliGrandParent/OutsideDupliParent/GEO_Head/GEO_Nose",
+ "/ParentOfDupli2/Dupli2/GEO_Head-0/GEO_Nose-3"}},
+ {"OBGround plane", {"/Ground plane"}},
+ {"OBOutsideDupliGrandParent", {"/Ground plane/OutsideDupliGrandParent"}},
+ {"OBOutsideDupliParent", {"/Ground plane/OutsideDupliGrandParent/OutsideDupliParent"}},
+ {"OBParentOfDupli2", {"/ParentOfDupli2"}}};
+
+ used_writers expected_data = {
+ {"OBCamera", {"/Camera/Camera"}},
+ {"OBGEO_Ear_L",
+ {"/Dupli1/GEO_Head-0/GEO_Ear_L-1/Ear",
+ "/Ground plane/OutsideDupliGrandParent/OutsideDupliParent/GEO_Head/GEO_Ear_L/Ear",
+ "/ParentOfDupli2/Dupli2/GEO_Head-0/GEO_Ear_L-1/Ear"}},
+ {"OBGEO_Ear_R",
+ {"/Dupli1/GEO_Head-0/GEO_Ear_R-2/Ear",
+ "/Ground plane/OutsideDupliGrandParent/OutsideDupliParent/GEO_Head/GEO_Ear_R/Ear",
+ "/ParentOfDupli2/Dupli2/GEO_Head-0/GEO_Ear_R-2/Ear"}},
+ {"OBGEO_Head",
+ {"/Dupli1/GEO_Head-0/Face",
+ "/Ground plane/OutsideDupliGrandParent/OutsideDupliParent/GEO_Head/Face",
+ "/ParentOfDupli2/Dupli2/GEO_Head-0/Face"}},
+ {"OBGEO_Nose",
+ {"/Dupli1/GEO_Head-0/GEO_Nose-3/Nose",
+ "/Ground plane/OutsideDupliGrandParent/OutsideDupliParent/GEO_Head/GEO_Nose/Nose",
+ "/ParentOfDupli2/Dupli2/GEO_Head-0/GEO_Nose-3/Nose"}},
+ {"OBGround plane", {"/Ground plane/Plane"}},
+ {"OBParentOfDupli2", {"/ParentOfDupli2/Icosphere"}},
+ };
+
+ // Even when only asking an export of transforms, on the first frame everything should be
+ // exported.
+ {
+ ExportSubset export_subset = {0};
+ export_subset.transforms = true;
+ export_subset.shapes = false;
+ iterator->set_export_subset(export_subset);
+ }
+ iterator->iterate_and_write();
+ EXPECT_EQ(expected_transforms, iterator->transform_writers);
+ EXPECT_EQ(expected_data, iterator->data_writers);
+
+ // Clear data to prepare for the next iteration.
+ iterator->transform_writers.clear();
+ iterator->data_writers.clear();
+
+ // Second iteration, should only write transforms now.
+ iterator->iterate_and_write();
+ EXPECT_EQ(expected_transforms, iterator->transform_writers);
+ EXPECT_EQ(0, iterator->data_writers.size());
+
+ // Clear data to prepare for the next iteration.
+ iterator->transform_writers.clear();
+ iterator->data_writers.clear();
+
+ // Third iteration, should only write data now.
+ {
+ ExportSubset export_subset = {0};
+ export_subset.transforms = false;
+ export_subset.shapes = true;
+ iterator->set_export_subset(export_subset);
+ }
+ iterator->iterate_and_write();
+ EXPECT_EQ(0, iterator->transform_writers.size());
+ EXPECT_EQ(expected_data, iterator->data_writers);
+
+ // Clear data to prepare for the next iteration.
+ iterator->transform_writers.clear();
+ iterator->data_writers.clear();
+
+ // Fourth iteration, should export everything now.
+ {
+ ExportSubset export_subset = {0};
+ export_subset.transforms = true;
+ export_subset.shapes = true;
+ iterator->set_export_subset(export_subset);
+ }
+ iterator->iterate_and_write();
+ EXPECT_EQ(expected_transforms, iterator->transform_writers);
+ EXPECT_EQ(expected_data, iterator->data_writers);
}
diff --git a/tests/gtests/usd/hierarchy_context_order_test.cc b/tests/gtests/usd/hierarchy_context_order_test.cc
index 4111fc7511e..25cda6d8670 100644
--- a/tests/gtests/usd/hierarchy_context_order_test.cc
+++ b/tests/gtests/usd/hierarchy_context_order_test.cc
@@ -16,7 +16,7 @@
* The Original Code is Copyright (C) 2019 Blender Foundation.
* All rights reserved.
*/
-#include "intern/abstract_hierarchy_iterator.h"
+#include "IO_abstract_hierarchy_iterator.h"
#include "testing/testing.h"
@@ -24,7 +24,7 @@ extern "C" {
#include "BLI_utildefines.h"
}
-using namespace USD;
+using namespace blender::io;
class HierarchyContextOrderTest : public testing::Test {
};
diff --git a/tests/python/alembic_tests.py b/tests/python/alembic_tests.py
index 2d477c5a6f0..5f8113729c3 100644
--- a/tests/python/alembic_tests.py
+++ b/tests/python/alembic_tests.py
@@ -194,7 +194,7 @@ class HierarchicalAndFlatExportTest(AbstractAlembicTest):
# Now check the resulting Alembic file.
xform = self.abcprop(abc, '/Cube_012/.xform')
- self.assertEqual(0, xform['.inherits'])
+ self.assertEqual(1, xform['.inherits'], "Blender transforms always inherit")
self.assertAlmostEqualFloatArray(
xform['.vals'],
@@ -214,7 +214,7 @@ class DupliGroupExportTest(AbstractAlembicTest):
self.run_blender('dupligroup-scene.blend', script)
# Now check the resulting Alembic file.
- xform = self.abcprop(abc, '/Real_Cube/Linked_Suzanne/Cylinder/Suzanne/.xform')
+ xform = self.abcprop(abc, '/Real_Cube/Linked_Suzanne/Cylinder-0/Suzanne-1/.xform')
self.assertEqual(1, xform['.inherits'])
self.assertAlmostEqualFloatArray(
xform['.vals'],
@@ -232,8 +232,8 @@ class DupliGroupExportTest(AbstractAlembicTest):
self.run_blender('dupligroup-scene.blend', script)
# Now check the resulting Alembic file.
- xform = self.abcprop(abc, '/Suzanne/.xform')
- self.assertEqual(0, xform['.inherits'])
+ xform = self.abcprop(abc, '/Suzanne-1/.xform')
+ self.assertEqual(1, xform['.inherits'])
self.assertAlmostEqualFloatArray(
xform['.vals'],
@@ -253,10 +253,10 @@ class CurveExportTest(AbstractAlembicTest):
self.run_blender('single-curve.blend', script)
# Now check the resulting Alembic file.
- abcprop = self.abcprop(abc, '/NurbsCurve/NurbsCurveShape/.geom')
+ abcprop = self.abcprop(abc, '/NurbsCurve/CurveData/.geom')
self.assertEqual(abcprop['.orders'], [4])
- abcprop = self.abcprop(abc, '/NurbsCurve/NurbsCurveShape/.geom/.userProperties')
+ abcprop = self.abcprop(abc, '/NurbsCurve/CurveData/.geom/.userProperties')
self.assertEqual(abcprop['blender:resolution'], 10)
@@ -280,49 +280,49 @@ class HairParticlesExportTest(AbstractAlembicTest):
def test_with_both(self, tempdir: pathlib.Path):
abc = self._do_test(tempdir, True, True)
- abcprop = self.abcprop(abc, '/Suzanne/Hair system/.geom')
+ abcprop = self.abcprop(abc, '/Suzanne/Hair_system/.geom')
self.assertIn('nVertices', abcprop)
- abcprop = self.abcprop(abc, '/Suzanne/Non-hair particle system/.geom')
+ abcprop = self.abcprop(abc, '/Suzanne/Non-hair_particle_system/.geom')
self.assertIn('.velocities', abcprop)
- abcprop = self.abcprop(abc, '/Suzanne/SuzanneShape/.geom')
+ abcprop = self.abcprop(abc, '/Suzanne/MonkeyMesh/.geom')
self.assertIn('.faceIndices', abcprop)
@with_tempdir
def test_with_hair_only(self, tempdir: pathlib.Path):
abc = self._do_test(tempdir, True, False)
- abcprop = self.abcprop(abc, '/Suzanne/Hair system/.geom')
+ abcprop = self.abcprop(abc, '/Suzanne/Hair_system/.geom')
self.assertIn('nVertices', abcprop)
self.assertRaises(AbcPropError, self.abcprop, abc,
- '/Suzanne/Non-hair particle system/.geom')
+ '/Suzanne/Non-hair_particle_system/.geom')
- abcprop = self.abcprop(abc, '/Suzanne/SuzanneShape/.geom')
+ abcprop = self.abcprop(abc, '/Suzanne/MonkeyMesh/.geom')
self.assertIn('.faceIndices', abcprop)
@with_tempdir
def test_with_particles_only(self, tempdir: pathlib.Path):
abc = self._do_test(tempdir, False, True)
- self.assertRaises(AbcPropError, self.abcprop, abc, '/Suzanne/Hair system/.geom')
+ self.assertRaises(AbcPropError, self.abcprop, abc, '/Suzanne/Hair_system/.geom')
- abcprop = self.abcprop(abc, '/Suzanne/Non-hair particle system/.geom')
+ abcprop = self.abcprop(abc, '/Suzanne/Non-hair_particle_system/.geom')
self.assertIn('.velocities', abcprop)
- abcprop = self.abcprop(abc, '/Suzanne/SuzanneShape/.geom')
+ abcprop = self.abcprop(abc, '/Suzanne/MonkeyMesh/.geom')
self.assertIn('.faceIndices', abcprop)
@with_tempdir
def test_with_neither(self, tempdir: pathlib.Path):
abc = self._do_test(tempdir, False, False)
- self.assertRaises(AbcPropError, self.abcprop, abc, '/Suzanne/Hair system/.geom')
+ self.assertRaises(AbcPropError, self.abcprop, abc, '/Suzanne/Hair_system/.geom')
self.assertRaises(AbcPropError, self.abcprop, abc,
- '/Suzanne/Non-hair particle system/.geom')
+ '/Suzanne/Non-hair_particle_system/.geom')
- abcprop = self.abcprop(abc, '/Suzanne/SuzanneShape/.geom')
+ abcprop = self.abcprop(abc, '/Suzanne/MonkeyMesh/.geom')
self.assertIn('.faceIndices', abcprop)
@@ -342,7 +342,7 @@ class UVMapExportTest(AbstractAlembicTest):
self.maxDiff = 1000
# The main UV map should be written to .geom
- abcprop = self.abcprop(abc, '/Cube/CubeShape/.geom/uv')
+ abcprop = self.abcprop(abc, '/Cube/Cube/.geom/uv')
self.assertEqual(abcprop['.vals'], [
[0.625, 0.75],
[0.875, 0.75],
@@ -361,7 +361,7 @@ class UVMapExportTest(AbstractAlembicTest):
])
# The second UV map should be written to .arbGeomParams
- abcprop = self.abcprop(abc, '/Cube/CubeShape/.geom/.arbGeomParams/Secondary')
+ abcprop = self.abcprop(abc, '/Cube/Cube/.geom/.arbGeomParams/Secondary')
self.assertEqual(abcprop['.vals'], [
[0.75, 0.375],
[0.75, 0.125],
@@ -465,7 +465,7 @@ class LongNamesExportTest(AbstractAlembicTest):
0.0, 3.0, 0.0, 1.0,
])
- abcprop = self.abcprop(abc, '%s/CubeShape/.geom' % name)
+ abcprop = self.abcprop(abc, '%s/Cube/.geom' % name)
self.assertIn('.faceCounts', abcprop)
diff --git a/tests/python/bl_pyapi_idprop_datablock.py b/tests/python/bl_pyapi_idprop_datablock.py
index 44fec6a9043..ca52c1b01fe 100644
--- a/tests/python/bl_pyapi_idprop_datablock.py
+++ b/tests/python/bl_pyapi_idprop_datablock.py
@@ -157,10 +157,10 @@ def check_linked_scene_copying():
extern_sce = get_scene("lib.blend", "Scene_lib")
# check node's props
- # we made full copy from linked scene, so pointers must equal each other
+ # must point to own scene camera
abort_if_false(intern_sce.node_tree.nodes['Render Layers']["prop"] and
- intern_sce.node_tree.nodes['Render Layers']["prop"] ==
- extern_sce.node_tree.nodes['Render Layers']["prop"])
+ not (intern_sce.node_tree.nodes['Render Layers']["prop"] ==
+ extern_sce.node_tree.nodes['Render Layers']["prop"]))
def check_scene_copying():